]> git.ipfire.org Git - thirdparty/newt.git/blame - form.c
add newtEntryGet/SetCursorPosition
[thirdparty/newt.git] / form.c
CommitLineData
45f6c4fd 1#include "config.h"
2
4bb1be99 3#include <sys/types.h>
4
139f06bc 5#include <slang.h>
6fb96a3f 6#include <stdarg.h>
7#include <stdlib.h>
4bb1be99 8#ifdef HAVE_SYS_SELECT_H
d02ffb49 9#include <sys/select.h>
4bb1be99 10#endif
e67a6cab 11#include <sys/time.h>
6fb96a3f 12
33b9b6eb 13#ifdef USE_GPM
14#include <ctype.h>
15#include <sys/time.h> /* timeval */
33b9b6eb 16#include <sys/socket.h> /* socket() */
17#include <sys/un.h> /* struct sockaddr_un */
18#include <sys/fcntl.h> /* O_RDONLY */
19#include <sys/stat.h> /* stat() */
20#include <termios.h> /* winsize */
21#include <unistd.h>
22#include <sys/kd.h> /* KDGETMODE */
23#include <signal.h>
24#include <stdio.h>
45f6c4fd 25#endif
26
6fb96a3f 27#include "newt.h"
28#include "newt_pr.h"
29
33b9b6eb 30#ifdef USE_GPM
31/*....................................... The connection data structure */
32
33typedef struct Gpm_Connect {
34 unsigned short eventMask, defaultMask;
35 unsigned short minMod, maxMod;
36 int pid;
37 int vc;
38} Gpm_Connect;
39
40/*....................................... Stack struct */
41typedef struct Gpm_Stst {
42 Gpm_Connect info;
43 struct Gpm_Stst *next;
44} Gpm_Stst;
45
46enum Gpm_Etype {
47 GPM_MOVE=1,
48 GPM_DRAG=2, /* exactly one of the bare ones is active at a time */
49 GPM_DOWN=4,
50 GPM_UP= 8,
51
52#define GPM_BARE_EVENTS(type) ((type)&(0x0f|GPM_ENTER|GPM_LEAVE))
53
54 GPM_SINGLE=16, /* at most one in three is set */
55 GPM_DOUBLE=32,
56 GPM_TRIPLE=64, /* WARNING: I depend on the values */
57
58 GPM_MFLAG=128, /* motion during click? */
59 GPM_HARD=256, /* if set in the defaultMask, force an already
60 used event to pass over to another handler */
61
62 GPM_ENTER=512, /* enter event, user in Roi's */
63 GPM_LEAVE=1024 /* leave event, used in Roi's */
64};
65
66/*....................................... The reported event */
67
68enum Gpm_Margin {GPM_TOP=1, GPM_BOT=2, GPM_LFT=4, GPM_RGT=8};
69
70typedef struct Gpm_Event {
71 unsigned char buttons, modifiers; /* try to be a multiple of 4 */
72 unsigned short vc;
73 short dx, dy, x, y;
74 enum Gpm_Etype type;
75 int clicks;
76 enum Gpm_Margin margin;
77} Gpm_Event;
78
79static int Gpm_Open(Gpm_Connect *conn, int flag);
80static int Gpm_Close(void);
81
82static int gpm_fd=-1;
83static int gpm_flag=0;
84static int gpm_tried=0;
85Gpm_Stst *gpm_stack=NULL;
36ccb508 86static char *gpm_sock_name=NULL;
33b9b6eb 87static struct sigaction gpm_saved_suspend_hook;
88static struct sigaction gpm_saved_winch_hook;
89
90#define GPM_XTERM_ON
91#define GPM_XTERM_OFF
92#define GPM_NODE_DEV "/dev/gpmctl"
93#define GPM_NODE_CTL GPM_NODE_DEV
94
95static inline int putdata(int where, Gpm_Connect *what)
96{
97 if (write(where,what,sizeof(Gpm_Connect))!=sizeof(Gpm_Connect))
98 {
99 return -1;
100 }
101 return 0;
102}
103
104static void gpm_winch_hook (int signum)
105{
106 if (SIG_IGN != gpm_saved_winch_hook.sa_handler &&
107 SIG_DFL != gpm_saved_winch_hook.sa_handler) {
108 gpm_saved_winch_hook.sa_handler(signum);
109 } /*if*/
110}
111
112static void gpm_suspend_hook (int signum)
113{
114 Gpm_Connect gpm_connect;
115 sigset_t old_sigset;
116 sigset_t new_sigset;
117 struct sigaction sa;
118 int success;
119
120 sigemptyset (&new_sigset);
121 sigaddset (&new_sigset, SIGTSTP);
122 sigprocmask (SIG_BLOCK, &new_sigset, &old_sigset);
123
124 /* Open a completely transparent gpm connection */
125 gpm_connect.eventMask = 0;
126 gpm_connect.defaultMask = ~0;
127 gpm_connect.minMod = ~0;
128 gpm_connect.maxMod = 0;
129 /* cannot do this under xterm, tough */
130 success = (Gpm_Open (&gpm_connect, 0) >= 0);
131
132 /* take the default action, whatever it is (probably a stop :) */
133 sigprocmask (SIG_SETMASK, &old_sigset, 0);
134 sigaction (SIGTSTP, &gpm_saved_suspend_hook, 0);
135 kill (getpid (), SIGTSTP);
136
137 /* in bardo here */
138
139 /* Reincarnation. Prepare for another death early. */
140 sigemptyset(&sa.sa_mask);
141 sa.sa_handler = gpm_suspend_hook;
142 sa.sa_flags = SA_NOMASK;
143 sigaction (SIGTSTP, &sa, 0);
144
145 /* Pop the gpm stack by closing the useless connection */
146 /* but do it only when we know we opened one.. */
147 if (success) {
148 Gpm_Close ();
149 } /*if*/
150}
151
152static int Gpm_Open(Gpm_Connect *conn, int flag)
153{
154 char tty[32];
155 char *term;
156 int i;
157 struct sockaddr_un addr;
158 Gpm_Stst *new;
33b9b6eb 159
160 /*....................................... First of all, check xterm */
161
162 if ((term=(char *)getenv("TERM")) && !strncmp(term,"xterm",5))
163 {
164 if (gpm_tried) return gpm_fd; /* no stack */
165 gpm_fd=-2;
166 GPM_XTERM_ON;
167 gpm_flag=1;
168 return gpm_fd;
169 }
170 /*....................................... No xterm, go on */
171
172
173 /*
174 * So I chose to use the current tty, instead of /dev/console, which
175 * has permission problems. (I am fool, and my console is
176 * readable/writeable by everybody.
177 *
178 * However, making this piece of code work has been a real hassle.
179 */
180
181 if (!gpm_flag && gpm_tried) return -1;
182 gpm_tried=1; /* do or die */
183
184 new=malloc(sizeof(Gpm_Stst));
185 if (!new) return -1;
186
187 new->next=gpm_stack;
188 gpm_stack=new;
189
190 conn->pid=getpid(); /* fill obvious values */
191
192 if (new->next)
193 conn->vc=new->next->info.vc; /* inherit */
194 else
195 {
196 conn->vc=0; /* default handler */
197 if (flag>0)
198 { /* forced vc number */
199 conn->vc=flag;
200 sprintf(tty,"/dev/tty%i",flag);
201 }
202 else if (flag==0) /* use your current vc */
203 {
204 char *t = ttyname(0); /* stdin */
205 if (!t) t = ttyname(1); /* stdout */
206 if (!t) goto err;
207 strcpy(tty,t);
208 if (strncmp(tty,"/dev/tty",8) || !isdigit(tty[8]))
209 goto err;
210 conn->vc=atoi(tty+8);
211 }
212 else /* a default handler -- use console */
213 sprintf(tty,"/dev/tty0");
214
215 }
216
217 new->info=*conn;
218
219 /*....................................... Connect to the control socket */
220
221 if (!(gpm_flag++))
222 {
223
224 if ( (gpm_fd=socket(AF_UNIX,SOCK_STREAM,0))<0 )
225 {
226 goto err;
227 }
228
229 bzero((char *)&addr,sizeof(addr));
230 addr.sun_family=AF_UNIX;
36ccb508 231 if (!(gpm_sock_name = tempnam (0, "gpm"))) {
33b9b6eb 232 goto err;
233 } /*if*/
36ccb508 234 strncpy (addr.sun_path, gpm_sock_name, sizeof (addr.sun_path));
33b9b6eb 235 if (bind (gpm_fd, (struct sockaddr*)&addr,
236 sizeof (addr.sun_family) + strlen (addr.sun_path))==-1) {
237 goto err;
238 } /*if*/
239
240 bzero((char *)&addr,sizeof(addr));
241 addr.sun_family=AF_UNIX;
242 strcpy(addr.sun_path, GPM_NODE_CTL);
243 i=sizeof(addr.sun_family)+strlen(GPM_NODE_CTL);
244
245 if ( connect(gpm_fd,(struct sockaddr *)(&addr),i)<0 )
246 {
247 struct stat stbuf;
248
249 /*
250 * Well, try to open a chr device called /dev/gpmctl. This should
251 * be forward-compatible with a kernel server
252 */
253 close(gpm_fd); /* the socket */
254 if ((gpm_fd=open(GPM_NODE_DEV,O_RDWR))==-1) {
255 goto err;
256 } /*if*/
257 if (fstat(gpm_fd,&stbuf)==-1 || (stbuf.st_mode&S_IFMT)!=S_IFCHR)
258 goto err;
259 }
260 }
261 /*....................................... Put your data */
262
263 if (putdata(gpm_fd,conn)!=-1)
264 {
265 /* itz Wed Dec 16 23:22:16 PST 1998 use sigaction, the old
266 code caused a signal loop under XEmacs */
267 struct sigaction sa;
268 sigemptyset(&sa.sa_mask);
269
270#if (defined(SIGWINCH))
271 /* And the winch hook .. */
272 sa.sa_handler = gpm_winch_hook;
273 sa.sa_flags = 0;
274 sigaction(SIGWINCH, &sa, &gpm_saved_winch_hook);
275#endif
276
277#if (defined(SIGTSTP))
278 if (gpm_flag == 1) {
279 /* Install suspend hook */
280 sa.sa_handler = SIG_IGN;
281 sigaction(SIGTSTP, &sa, &gpm_saved_suspend_hook);
282
283 /* if signal was originally ignored, job control is not supported */
284 if (gpm_saved_suspend_hook.sa_handler != SIG_IGN) {
285 sa.sa_flags = SA_NOMASK;
286 sa.sa_handler = gpm_suspend_hook;
287 sigaction(SIGTSTP, &sa, 0);
288 } /*if*/
289 } /*if*/
290#endif
291
292 } /*if*/
293 return gpm_fd;
294
295 /*....................................... Error: free all memory */
296 err:
297 do
298 {
299 new=gpm_stack->next;
300 free(gpm_stack);
301 gpm_stack=new;
302 }
303 while(gpm_stack);
304 if (gpm_fd>=0) close(gpm_fd);
36ccb508
ML
305 if (gpm_sock_name) {
306 unlink(gpm_sock_name);
307 free(gpm_sock_name);
308 gpm_sock_name = NULL;
33b9b6eb 309 } /*if*/
310 gpm_flag=0;
9a3aad92 311 gpm_fd=-1;
33b9b6eb 312 return -1;
313}
314
315/*-------------------------------------------------------------------*/
316static int Gpm_Close(void)
317{
318 Gpm_Stst *next;
319
320 gpm_tried=0; /* reset the error flag for next time */
321 if (gpm_fd==-2) /* xterm */
322 GPM_XTERM_OFF;
323 else /* linux */
324 {
325 if (!gpm_flag) return 0;
326 next=gpm_stack->next;
327 free(gpm_stack);
328 gpm_stack=next;
329 if (next)
330 putdata(gpm_fd,&(next->info));
331
332 if (--gpm_flag) return -1;
333 }
334
335 if (gpm_fd>=0) close(gpm_fd);
336 gpm_fd=-1;
36ccb508
ML
337 if (gpm_sock_name) {
338 unlink(gpm_sock_name);
339 free(gpm_sock_name);
340 gpm_sock_name = NULL;
341 }
33b9b6eb 342#ifdef SIGTSTP
343 sigaction(SIGTSTP, &gpm_saved_suspend_hook, 0);
344#endif
345#ifdef SIGWINCH
346 sigaction(SIGWINCH, &gpm_saved_winch_hook, 0);
347#endif
348 return 0;
349}
350
351/*-------------------------------------------------------------------*/
352static int Gpm_GetEvent(Gpm_Event *event)
353{
354 int count;
355
356 if (!gpm_flag) return 0;
357
358 if ((count=read(gpm_fd,event,sizeof(Gpm_Event)))!=sizeof(Gpm_Event))
359 {
360 if (count==0)
361 {
362 Gpm_Close();
363 return 0;
364 }
365 return -1;
366 }
367 return 1;
368}
369#endif
370
f5daa14e 371/****************************************************************************
45f6c4fd 372 These forms handle vertical scrolling of components with a height of 1
373
f5daa14e 374 Horizontal scrolling won't work, and scrolling large widgets will fail
375 miserably. It shouldn't be too hard to fix either of those if anyone
376 cares to. I only use scrolling for listboxes and text boxes though so
377 I didn't bother.
378*****************************************************************************/
379
380struct element {
5c72fb03 381 newtComponent co;
f5daa14e 382};
383
d02ffb49 384struct fdInfo {
385 int fd;
386 int flags;
387};
388
c4827b34 389struct form {
6fb96a3f 390 int numCompsAlloced;
f5daa14e 391 struct element * elements;
6fb96a3f 392 int numComps;
393 int currComp;
8c4e1047 394 int fixedHeight;
02b180e8 395 int flags;
f5daa14e 396 int vertOffset;
02b180e8 397 newtComponent vertBar, exitComp;
d4109c37 398 const char * help;
f1f3611f 399 int numRows;
da151ca8 400 int * hotKeys;
401 int numHotKeys;
8f4e87fc 402 int background;
d02ffb49 403 int numFds;
404 struct fdInfo * fds;
405 int maxFd;
e67a6cab 406 int timer; /* in milliseconds */
407 struct timeval lastTimeout;
72b71fa6 408 void * helpTag;
409 newtCallback helpCb;
6fb96a3f 410};
411
c4827b34 412static void gotoComponent(struct form * form, int newComp);
413static struct eventResult formEvent(newtComponent co, struct event ev);
434c766b 414static struct eventResult sendEvent(newtComponent comp, struct event ev);
8f52cd47 415static void formPlace(newtComponent co, int left, int top);
c4827b34 416
72b71fa6 417/* Global, ick */
418static newtCallback helpCallback;
419
8f52cd47 420/* this isn't static as grid.c tests against it to find forms */
421struct componentOps formOps = {
8b68158d 422 newtDrawForm,
c4827b34 423 formEvent,
424 newtFormDestroy,
8f52cd47 425 formPlace,
426 newtDefaultMappedHandler,
c4827b34 427} ;
428
0593cb33
ML
429int needResize = 0;
430
f5daa14e 431static inline int componentFits(newtComponent co, int compNum) {
432 struct form * form = co->data;
433 struct element * el = form->elements + compNum;
434
5c72fb03
ML
435 if (co->top > el->co->top)
436 return 0;
437 if (co->top + co->height < el->co->top + el->co->height)
438 return 0;
f5daa14e 439
440 return 1;
441}
442
72b71fa6 443newtComponent newtForm(newtComponent vertBar, void * help, int flags) {
c4827b34 444 newtComponent co;
445 struct form * form;
446
447 co = malloc(sizeof(*co));
448 form = malloc(sizeof(*form));
449 co->data = form;
450 co->width = 0;
451 co->height = 0;
452 co->top = -1;
453 co->left = -1;
3da9c9a2 454 co->isMapped = 0;
c4827b34 455
6f481af2 456 co->takesFocus = 0; /* we may have 0 components */
c4827b34 457 co->ops = &formOps;
c101e99e 458 co->destroyCallback = NULL;
6fb96a3f 459
02b180e8 460 form->help = help;
461 form->flags = flags;
6fb96a3f 462 form->numCompsAlloced = 5;
463 form->numComps = 0;
464 form->currComp = -1;
f5daa14e 465 form->vertOffset = 0;
8c4e1047 466 form->fixedHeight = 0;
f1f3611f 467 form->numRows = 0;
d02ffb49 468 form->numFds = 0;
469 form->maxFd = 0;
470 form->fds = NULL;
f5daa14e 471 form->elements = malloc(sizeof(*(form->elements)) * form->numCompsAlloced);
6fb96a3f 472
8f4e87fc 473 form->background = COLORSET_WINDOW;
da151ca8 474 form->hotKeys = malloc(sizeof(int));
475 form->numHotKeys = 0;
73390860 476 form->timer = 0;
477 form->lastTimeout.tv_sec = form->lastTimeout.tv_usec = 0;
4a93351d 478 if (!(form->flags & NEWT_FLAG_NOF12)) {
6f481af2 479 newtFormAddHotKey(co, NEWT_KEY_F12);
da151ca8 480 }
481
02b180e8 482 if (vertBar)
6f481af2 483 form->vertBar = vertBar;
f1f3611f 484 else
6f481af2 485 form->vertBar = NULL;
f1f3611f 486
72b71fa6 487 form->helpTag = help;
488 form->helpCb = helpCallback;
489
c4827b34 490 return co;
6fb96a3f 491}
492
2a2998a0 493newtComponent newtFormGetCurrent(newtComponent co) {
494 struct form * form = co->data;
495
d78245ab 496 if (form->currComp == -1) return 0;
2a2998a0 497 return form->elements[form->currComp].co;
498}
499
5c72fb03
ML
500static void formScroll(newtComponent co, int delta) {
501 struct form * form = co->data;
502 struct element * el;
503 int i, newVertOffset = form->vertOffset + delta;
504
505 if (newVertOffset < 0)
506 newVertOffset = 0;
507 if (newVertOffset > form->numRows - co->height)
508 newVertOffset = form->numRows - co->height;
509
510 delta = newVertOffset - form->vertOffset;
511 form->vertOffset = newVertOffset;
512
513 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
514 if (el->co == form->vertBar)
515 continue;
516 el->co->ops->place(el->co, el->co->left, el->co->top - delta);
517 }
518}
519
7f72e4ef
DW
520int newtFormGetScrollPosition(newtComponent co) {
521 struct form * form = co->data;
522
523 return form->vertOffset;
524}
525
526void newtFormSetScrollPosition(newtComponent co, int position) {
527 struct form * form = co->data;
528
529 if (form->numRows == 0)
530 newtFormSetSize(co);
531 formScroll(co, position - form->vertOffset);
532}
533
7ea3dccd 534void newtFormSetCurrent(newtComponent co, newtComponent subco) {
535 struct form * form = co->data;
536 int i, new;
537
538 for (i = 0; i < form->numComps; i++) {
6f481af2 539 if (form->elements[i].co == subco) break;
7ea3dccd 540 }
541
542 if (form->elements[i].co != subco) return;
543 new = i;
544
3da9c9a2 545 if (co->isMapped && !componentFits(co, new)) {
6f481af2 546 gotoComponent(form, -1);
5c72fb03 547 formScroll(co, form->elements[new].co->top - co->top - 1);
7ea3dccd 548 }
549
550 gotoComponent(form, new);
551}
552
e67a6cab 553void newtFormSetTimer(newtComponent co, int millisecs) {
554 struct form * form = co->data;
555
556 form->timer = millisecs;
73390860 557 form->lastTimeout.tv_usec = 0;
558 form->lastTimeout.tv_sec = 0;
e67a6cab 559}
560
8c4e1047 561void newtFormSetHeight(newtComponent co, int height) {
f5daa14e 562 struct form * form = co->data;
563
8c4e1047 564 form->fixedHeight = 1;
f5daa14e 565 co->height = height;
566}
567
ea947804 568void newtFormSetWidth(newtComponent co, int width) {
569 co->width = width;
570}
571
c4827b34 572void newtFormAddComponent(newtComponent co, newtComponent newco) {
573 struct form * form = co->data;
6fb96a3f 574
8f52cd47 575 co->takesFocus = 1;
576
6fb96a3f 577 if (form->numCompsAlloced == form->numComps) {
6f481af2 578 form->numCompsAlloced += 5;
579 form->elements = realloc(form->elements,
580 sizeof(*(form->elements)) * form->numCompsAlloced);
6fb96a3f 581 }
582
f5daa14e 583 form->elements[form->numComps].co = newco;
f1f3611f 584
585 if (newco->takesFocus && form->currComp == -1)
6f481af2 586 form->currComp = form->numComps;
f1f3611f 587
f5daa14e 588 form->numComps++;
6fb96a3f 589}
590
c4827b34 591void newtFormAddComponents(newtComponent co, ...) {
6fb96a3f 592 va_list ap;
c4827b34 593 newtComponent subco;
6fb96a3f 594
c4827b34 595 va_start(ap, co);
6fb96a3f 596
c4827b34 597 while ((subco = va_arg(ap, newtComponent)))
6f481af2 598 newtFormAddComponent(co, subco);
45f6c4fd 599
6fb96a3f 600 va_end(ap);
601}
602
8f52cd47 603static void formPlace(newtComponent co, int left, int top) {
604 struct form * form = co->data;
605 int vertDelta, horizDelta;
606 struct element * el;
607 int i;
608
8f52cd47 609 vertDelta = top - co->top;
610 horizDelta = left - co->left;
611 co->top = top;
612 co->left = left;
613
614 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
5c72fb03
ML
615 el->co->ops->place(el->co, el->co->left + horizDelta,
616 el->co->top + vertDelta);
8f52cd47 617 }
618}
619
8b68158d 620void newtDrawForm(newtComponent co) {
c4827b34 621 struct form * form = co->data;
f5daa14e 622 struct element * el;
6fb96a3f 623 int i;
6fb96a3f 624
3da9c9a2 625 newtFormSetSize(co);
626
8f4e87fc 627 SLsmg_set_color(form->background);
a1331450 628 newtClearBox(co->left, co->top, co->width, co->height);
7ff5fd71 629
f5daa14e 630 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
5c72fb03
ML
631 /* only draw it if it'll fit on the screen vertically
632 (the scrollbar *always* fits somewhere) */
633 if (el->co == form->vertBar || componentFits(co, i)) {
6f481af2 634 el->co->ops->mapped(el->co, 1);
635 el->co->ops->draw(el->co);
636 } else {
5c72fb03 637 el->co->ops->mapped(el->co, 0);
6f481af2 638 }
6fb96a3f 639 }
f1f3611f 640
641 if (form->vertBar)
6f481af2 642 newtScrollbarSet(form->vertBar, form->vertOffset,
643 form->numRows - co->height);
c4827b34 644}
6fb96a3f 645
c4827b34 646static struct eventResult formEvent(newtComponent co, struct event ev) {
647 struct form * form = co->data;
f5daa14e 648 newtComponent subco = form->elements[form->currComp].co;
d1a682d1 649 int new, wrap = 0;
c4827b34 650 struct eventResult er;
d1a682d1 651 int dir = 0, page = 0;
45f6c4fd 652 int i, num, found;
653 struct element * el;
6fb96a3f 654
c4827b34 655 er.result = ER_IGNORED;
8f52cd47 656 if (!form->numComps) return er;
657
d78245ab 658 if (form->currComp == -1) return er;
6fb96a3f 659
434c766b 660 switch (ev.when) {
661 case EV_EARLY:
6f481af2 662 if (ev.event == EV_KEYPRESS) {
663 if (ev.u.key == NEWT_KEY_TAB) {
664 er.result = ER_SWALLOWED;
665 dir = 1;
666 wrap = 1;
667 } else if (ev.u.key == NEWT_KEY_UNTAB) {
668 er.result = ER_SWALLOWED;
669 dir = -1;
670 wrap = 1;
671 }
672 }
673
674 if (form->numComps) {
675 i = form->currComp;
676 num = 0;
677 while (er.result == ER_IGNORED && num != form->numComps ) {
678 er = form->elements[i].co->ops->event(form->elements[i].co, ev);
679
680 num++;
681 i++;
682 if (i == form->numComps) i = 0;
683 }
684 }
685
686 break;
45f6c4fd 687
434c766b 688 case EV_NORMAL:
6f481af2 689 if (ev.event == EV_MOUSE) {
690 found = 0;
691 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
692 if ((el->co->top <= ev.u.mouse.y) &&
693 (el->co->top + el->co->height > ev.u.mouse.y) &&
694 (el->co->left <= ev.u.mouse.x) &&
695 (el->co->left + el->co->width > ev.u.mouse.x)) {
696 found = 1;
697 if (el->co->takesFocus) {
698 gotoComponent(form, i);
699 subco = form->elements[form->currComp].co;
700 }
701 }
702 /* If we did not find a co to send this event to, we
703 should just swallow the event here. */
704 }
705 if (!found) {
706 er.result = ER_SWALLOWED;
707
708 return er;
709 }
710 }
711 er = subco->ops->event(subco, ev);
712 switch (er.result) {
713 case ER_NEXTCOMP:
714 er.result = ER_SWALLOWED;
715 dir = 1;
716 break;
717
718 case ER_EXITFORM:
719 form->exitComp = subco;
720 break;
721
722 default:
723 break;
724 }
725 break;
434c766b 726
727 case EV_LATE:
6f481af2 728 er = subco->ops->event(subco, ev);
729
730 if (er.result == ER_IGNORED) {
731 switch (ev.u.key) {
732 case NEWT_KEY_UP:
733 case NEWT_KEY_LEFT:
734 case NEWT_KEY_BKSPC:
735 er.result = ER_SWALLOWED;
736 dir = -1;
737 break;
738
739 case NEWT_KEY_DOWN:
740 case NEWT_KEY_RIGHT:
741 er.result = ER_SWALLOWED;
742 dir = 1;
743 break;
744
745 case NEWT_KEY_PGUP:
746 er.result = ER_SWALLOWED;
747 dir = -1;
748 page = 1;
749 break;
750
751 case NEWT_KEY_PGDN:
752 er.result = ER_SWALLOWED;
753 dir = 1;
754 page = 1;
755 break;
756 }
757 }
c4827b34 758 }
6fb96a3f 759
f5daa14e 760 if (dir) {
6f481af2 761 new = form->currComp;
762
763 if (page) {
764 new += dir * co->height;
765 if (new < 0)
766 new = 0;
767 else if (new >= form->numComps)
768 new = (form->numComps - 1);
769
cf69773e
ML
770 while (!form->elements[new].co->takesFocus &&
771 new - dir >= 0 && new - dir < form->numComps)
772 new -= dir;
6f481af2 773 } else {
774 do {
775 new += dir;
776
777 if (wrap) {
778 if (new < 0)
779 new = form->numComps - 1;
780 else if (new >= form->numComps)
781 new = 0;
cf69773e
ML
782 if (new == form->currComp)
783 /* back where we started */
784 return er;
6f481af2 785 } else if (new < 0 || new >= form->numComps)
786 return er;
787 } while (!form->elements[new].co->takesFocus);
788 }
789
790 /* make sure this component is visible */
791 if (!componentFits(co, new)) {
5c72fb03
ML
792 int vertDelta;
793
6f481af2 794 gotoComponent(form, -1);
795
796 if (dir < 0) {
797 /* make the new component the first one */
5c72fb03 798 vertDelta = form->elements[new].co->top - co->top;
6f481af2 799 } else {
800 /* make the new component the last one */
5c72fb03 801 vertDelta = (form->elements[new].co->top +
6f481af2 802 form->elements[new].co->height) -
803 (co->top + co->height);
804 }
805
5c72fb03 806 formScroll(co, vertDelta);
6f481af2 807 newtDrawForm(co);
808 }
809
810 gotoComponent(form, new);
811 er.result = ER_SWALLOWED;
c4827b34 812 }
6fb96a3f 813
c4827b34 814 return er;
6fb96a3f 815}
816
c101e99e
RJ
817/* Destroy a component. Components which have been added to a form
818 * are destroyed when the form is destroyed; this is just for the
819 * (rare) case of components which for whatever reason weren't added
820 * to a form.
821 */
822void newtComponentDestroy(newtComponent co) {
823 /* If the user registered a destroy callback for this component,
824 * now is a good time to call it.
825 */
826 if (co->destroyCallback)
827 co->destroyCallback(co, co->destroyCallbackData);
828
829 if (co->ops->destroy) {
830 co->ops->destroy(co);
831 } else {
832 if (co->data) free(co->data);
833 free(co);
834 }
835}
836
6fb96a3f 837/* this also destroys all of the components on the form */
c4827b34 838void newtFormDestroy(newtComponent co) {
c4827b34 839 newtComponent subco;
840 struct form * form = co->data;
f1f3611f 841 int i;
6fb96a3f 842
843 /* first, destroy all of the components */
844 for (i = 0; i < form->numComps; i++) {
6f481af2 845 subco = form->elements[i].co;
c101e99e 846 newtComponentDestroy(subco);
6fb96a3f 847 }
848
4e61f5db 849 if (form->hotKeys) free(form->hotKeys);
850
f5daa14e 851 free(form->elements);
6fb96a3f 852 free(form);
c4827b34 853 free(co);
854}
855
856newtComponent newtRunForm(newtComponent co) {
da151ca8 857 struct newtExitStruct es;
858
859 newtFormRun(co, &es);
860 if (es.reason == NEWT_EXIT_HOTKEY) {
6f481af2 861 if (es.u.key == NEWT_KEY_F12) {
862 es.reason = NEWT_EXIT_COMPONENT;
863 es.u.co = co;
864 } else {
865 return NULL;
866 }
b8b8a86f
ML
867 } else if (es.reason == NEWT_EXIT_ERROR)
868 return NULL;
da151ca8 869
870 return es.u.co;
871}
872
873void newtFormAddHotKey(newtComponent co, int key) {
874 struct form * form = co->data;
875
876 form->numHotKeys++;
877 form->hotKeys = realloc(form->hotKeys, sizeof(int) * form->numHotKeys);
878 form->hotKeys[form->numHotKeys - 1] = key;
879}
880
3da9c9a2 881void newtFormSetSize(newtComponent co) {
f7540d34 882 struct form * form = co->data;
21150df5 883 int delta, i, first;
f7540d34 884 struct element * el;
885
21150df5 886 form->numRows = 0;
8f52cd47 887
f7540d34 888 co->width = 0;
3da9c9a2 889 if (!form->fixedHeight) co->height = 0;
890
35d6a6ab
ML
891 co->top = -1;
892 co->left = -1;
21150df5 893 first = 1;
35d6a6ab 894
f7540d34 895 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
6f481af2 896 if (el->co->ops == &formOps)
897 newtFormSetSize(el->co);
35d6a6ab
ML
898 else if (el->co == form->vertBar)
899 continue;
900
21150df5 901 if (first) {
35d6a6ab
ML
902 co->top = el->co->top;
903 co->left = el->co->left;
21150df5 904 first = 0;
35d6a6ab 905 }
6f481af2 906
6f481af2 907 if (co->left > el->co->left) {
908 delta = co->left - el->co->left;
909 co->left -= delta;
910 co->width += delta;
911 }
912
913 if (co->top > el->co->top) {
914 delta = co->top - el->co->top;
915 co->top -= delta;
35d6a6ab 916 form->numRows += delta;
6f481af2 917 if (!form->fixedHeight)
918 co->height += delta;
919 }
920
921 if ((co->left + co->width) < (el->co->left + el->co->width))
922 co->width = (el->co->left + el->co->width) - co->left;
923
924 if (!form->fixedHeight) {
925 if ((co->top + co->height) < (el->co->top + el->co->height))
926 co->height = (el->co->top + el->co->height) - co->top;
927 }
928
929 if ((el->co->top + el->co->height - co->top) > form->numRows) {
930 form->numRows = el->co->top + el->co->height - co->top;
931 }
f7540d34 932 }
5c72fb03
ML
933
934 co->top += form->vertOffset;
f7540d34 935}
936
da151ca8 937void newtFormRun(newtComponent co, struct newtExitStruct * es) {
c4827b34 938 struct form * form = co->data;
939 struct event ev;
940 struct eventResult er;
45f6c4fd 941 int key, i, max;
da151ca8 942 int done = 0;
d7e202d6 943 fd_set readSet, writeSet, exceptSet;
e67a6cab 944 struct timeval nextTimeout, now, timeout;
33b9b6eb 945#ifdef USE_GPM
45f6c4fd 946 int x, y;
947 Gpm_Connect conn;
948 Gpm_Event event;
949
950 /* Set up GPM interface */
951 conn.eventMask = ~GPM_MOVE;
952 conn.defaultMask = GPM_MOVE;
953 conn.minMod = 0;
954 conn.maxMod = 0;
955
956 Gpm_Open(&conn, 0);
957#endif
c4827b34 958
f7540d34 959 /* draw all of the components */
8b68158d 960 newtDrawForm(co);
c4827b34 961
f1f3611f 962 if (form->currComp == -1) {
d3cfe899
ML
963 if (form->numComps)
964 gotoComponent(form, 0);
f1f3611f 965 } else
6f481af2 966 gotoComponent(form, form->currComp);
45f6c4fd 967
da151ca8 968 while (!done) {
6f481af2 969 newtRefresh();
c4827b34 970
6f481af2 971 FD_ZERO(&readSet);
972 FD_ZERO(&writeSet);
973 FD_ZERO(&exceptSet);
974 FD_SET(0, &readSet);
33b9b6eb 975#ifdef USE_GPM
6f481af2 976 if (gpm_fd > 0) {
977 FD_SET(gpm_fd, &readSet);
978 }
979 max = form->maxFd > gpm_fd ? form->maxFd : gpm_fd;
45f6c4fd 980#else
6f481af2 981 max = form->maxFd;
45f6c4fd 982#endif
983
6f481af2 984 for (i = 0; i < form->numFds; i++) {
985 if (form->fds[i].flags & NEWT_FD_READ)
986 FD_SET(form->fds[i].fd, &readSet);
987 if (form->fds[i].flags & NEWT_FD_WRITE)
988 FD_SET(form->fds[i].fd, &writeSet);
989 if (form->fds[i].flags & NEWT_FD_EXCEPT)
990 FD_SET(form->fds[i].fd, &exceptSet);
991 }
992
993 if (form->timer) {
994 /* Calculate when we next need to return with a timeout. Do
995 this inside the loop in case a callback resets the timer. */
57660927 996 gettimeofday(&now, 0);
997
998 if ((!form->lastTimeout.tv_sec && !form->lastTimeout.tv_usec) ||
999 now.tv_sec < form->lastTimeout.tv_sec ||
1000 (now.tv_sec == form->lastTimeout.tv_sec &&
1001 now.tv_usec < form->lastTimeout.tv_usec))
1002 form->lastTimeout = now;
6f481af2 1003
1004 nextTimeout.tv_sec = form->lastTimeout.tv_sec +
1005 (form->timer / 1000);
1006 nextTimeout.tv_usec = form->lastTimeout.tv_usec +
1007 (form->timer % 1000) * 1000;
1008
6f481af2 1009 if (now.tv_sec > nextTimeout.tv_sec) {
1010 timeout.tv_sec = timeout.tv_usec = 0;
1011 } else if (now.tv_sec == nextTimeout.tv_sec) {
1012 timeout.tv_sec = 0;
1013 if (now.tv_usec > nextTimeout.tv_usec)
1014 timeout.tv_usec = 0;
1015 else
1016 timeout.tv_usec = nextTimeout.tv_usec - now.tv_usec;
1017 } else if (now.tv_sec < nextTimeout.tv_sec) {
1018 timeout.tv_sec = nextTimeout.tv_sec - now.tv_sec;
1019 if (now.tv_usec > nextTimeout.tv_usec)
1020 timeout.tv_sec--,
1021 timeout.tv_usec = nextTimeout.tv_usec + 1000000 -
1022 now.tv_usec;
1023 else
1024 timeout.tv_usec = nextTimeout.tv_usec - now.tv_usec;
1025 }
1026 } else {
1027 timeout.tv_sec = timeout.tv_usec = 0;
1028 }
1029
0593cb33
ML
1030 if (needResize) {
1031 needResize = 0;
1032 newtResizeScreen(1);
1033 }
1034
6f481af2 1035 i = select(max + 1, &readSet, &writeSet, &exceptSet,
1036 form->timer ? &timeout : NULL);
1037 if (i < 0) continue; /* ?? What should we do here? */
1038
1039 if (i == 0) {
1040 done = 1;
1041 es->reason = NEWT_EXIT_TIMER;
1042 gettimeofday(&form->lastTimeout, NULL);
1043 } else
33b9b6eb 1044#ifdef USE_GPM
6f481af2 1045 if (gpm_fd > 0 && FD_ISSET(gpm_fd, &readSet)) {
1046 Gpm_GetEvent(&event);
d02ffb49 1047
6f481af2 1048 if (event.type & GPM_DOWN) {
1049 /* Transform coordinates to current window */
1050 newtGetWindowPos(&x, &y);
c4827b34 1051
6f481af2 1052 ev.event = EV_MOUSE;
1053 ev.u.mouse.type = MOUSE_BUTTON_DOWN;
1054 ev.u.mouse.x = event.x - x - 1;
1055 ev.u.mouse.y = event.y - y - 1;
d02ffb49 1056
6f481af2 1057 /* Send the form the event */
1058 er = sendEvent(co, ev);
45f6c4fd 1059
6f481af2 1060 if (er.result == ER_EXITFORM) {
1061 done = 1;
1062 es->reason = NEWT_EXIT_COMPONENT;
1063 es->u.co = form->exitComp;
1064 }
45f6c4fd 1065
6f481af2 1066 }
1067 } else
45f6c4fd 1068#endif
6f481af2 1069 {
1070 if (FD_ISSET(0, &readSet)) {
1071
1072 key = newtGetKey();
1073
6f481af2 1074 for (i = 0; i < form->numHotKeys; i++) {
1075 if (form->hotKeys[i] == key) {
1076 es->reason = NEWT_EXIT_HOTKEY;
1077 es->u.key = key;
1078 done = 1;
1079 break;
1080 }
1081 }
1082
9fc6a6b0 1083 if (key == NEWT_KEY_F1 && form->helpTag && form->helpCb) {
1084 if (form->currComp != -1) {
1085 ev.event = EV_UNFOCUS;
1086 sendEvent(form->elements[form->currComp].co, ev);
1087 }
6f481af2 1088 form->helpCb(co, form->helpTag);
9fc6a6b0 1089 if (form->currComp != -1) {
1090 ev.event = EV_FOCUS;
1091 sendEvent(form->elements[form->currComp].co, ev);
1092 }
1093 }
6f481af2 1094
b8b8a86f
ML
1095 if (key == NEWT_KEY_ERROR) {
1096 es->u.watch = -1;
1097 es->reason = NEWT_EXIT_ERROR;
1098 done = 1;
1099 }
1100
6f481af2 1101 if (!done) {
1102 ev.event = EV_KEYPRESS;
1103 ev.u.key = key;
1104
1105 er = sendEvent(co, ev);
1106
1107 if (er.result == ER_EXITFORM) {
1108 done = 1;
1109 es->reason = NEWT_EXIT_COMPONENT;
1110 es->u.co = form->exitComp;
1111 }
1112 }
1113 } else {
1114 for (i = 0; i < form->numFds; i++) {
1115 if (((form->fds[i].flags & NEWT_FD_READ)
1116 && FD_ISSET(form->fds[i].fd, &readSet))
1117 || ((form->fds[i].flags & NEWT_FD_WRITE)
1118 && FD_ISSET(form->fds[i].fd, &writeSet))
1119 || ((form->fds[i].flags & NEWT_FD_EXCEPT)
1120 && FD_ISSET(form->fds[i].fd, &exceptSet))) break;
1121 }
1122 if(i < form->numFds)
1123 es->u.watch = form->fds[i].fd;
1124 else
1125 es->u.watch = -1;
1126
1127 es->reason = NEWT_EXIT_FDREADY;
1128 done = 1;
1129 }
1130 }
45f6c4fd 1131 }
c4827b34 1132 newtRefresh();
33b9b6eb 1133#ifdef USE_GPM
45f6c4fd 1134 Gpm_Close();
1135#endif
6fb96a3f 1136}
1137
434c766b 1138static struct eventResult sendEvent(newtComponent co, struct event ev) {
1139 struct eventResult er;
1140
1141 ev.when = EV_EARLY;
1142 er = co->ops->event(co, ev);
1143
1144 if (er.result == ER_IGNORED) {
6f481af2 1145 ev.when = EV_NORMAL;
1146 er = co->ops->event(co, ev);
434c766b 1147 }
1148
1149 if (er.result == ER_IGNORED) {
6f481af2 1150 ev.when = EV_LATE;
1151 er = co->ops->event(co, ev);
434c766b 1152 }
1153
1154 return er;
1155}
1156
c4827b34 1157static void gotoComponent(struct form * form, int newComp) {
6fb96a3f 1158 struct event ev;
1159
1160 if (form->currComp != -1) {
6f481af2 1161 ev.event = EV_UNFOCUS;
1162 sendEvent(form->elements[form->currComp].co, ev);
6fb96a3f 1163 }
1164
1165 form->currComp = newComp;
45f6c4fd 1166
f5daa14e 1167 if (form->currComp != -1) {
6f481af2 1168 ev.event = EV_FOCUS;
1169 ev.when = EV_NORMAL;
1170 sendEvent(form->elements[form->currComp].co, ev);
f5daa14e 1171 }
6fb96a3f 1172}
da151ca8 1173
1174void newtComponentAddCallback(newtComponent co, newtCallback f, void * data) {
1175 co->callback = f;
1176 co->callbackData = data;
1177}
1178
c101e99e
RJ
1179/* Add a callback which is called when the component is destroyed. */
1180void newtComponentAddDestroyCallback(newtComponent co,
1181 newtCallback f, void * data) {
1182 co->destroyCallback = f;
1183 co->destroyCallbackData = data;
1184}
1185
41d86a3c 1186void newtComponentTakesFocus(newtComponent co, int val) {
1187 co->takesFocus = val;
1188}
1189
8f4e87fc 1190void newtFormSetBackground(newtComponent co, int color) {
1191 struct form * form = co->data;
1192
1193 form->background = color;
1194}
d02ffb49 1195
1196void newtFormWatchFd(newtComponent co, int fd, int fdFlags) {
1197 struct form * form = co->data;
005d8237 1198 int i;
1199
1200 for (i = 0; i < form->numFds; i++)
1201 if (form->fds[i].fd == fd)
6f481af2 1202 break;
005d8237 1203
1204 if(i >= form->numFds)
1205 form->fds = realloc(form->fds, (++form->numFds) * sizeof(*form->fds));
d02ffb49 1206
005d8237 1207 form->fds[i].fd = fd;
1208 form->fds[i].flags = fdFlags;
d02ffb49 1209 if (form->maxFd < fd) form->maxFd = fd;
1210}
72b71fa6 1211
1212void newtSetHelpCallback(newtCallback cb) {
1213 helpCallback = cb;
1214}