]> git.ipfire.org Git - thirdparty/newt.git/blame - form.c
before checkboxtree drawing return first item in GetCurrent()
[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;
86static struct sigaction gpm_saved_suspend_hook;
87static struct sigaction gpm_saved_winch_hook;
88
89#define GPM_XTERM_ON
90#define GPM_XTERM_OFF
91#define GPM_NODE_DEV "/dev/gpmctl"
92#define GPM_NODE_CTL GPM_NODE_DEV
93
94static inline int putdata(int where, Gpm_Connect *what)
95{
96 if (write(where,what,sizeof(Gpm_Connect))!=sizeof(Gpm_Connect))
97 {
98 return -1;
99 }
100 return 0;
101}
102
103static void gpm_winch_hook (int signum)
104{
105 if (SIG_IGN != gpm_saved_winch_hook.sa_handler &&
106 SIG_DFL != gpm_saved_winch_hook.sa_handler) {
107 gpm_saved_winch_hook.sa_handler(signum);
108 } /*if*/
109}
110
111static void gpm_suspend_hook (int signum)
112{
113 Gpm_Connect gpm_connect;
114 sigset_t old_sigset;
115 sigset_t new_sigset;
116 struct sigaction sa;
117 int success;
118
119 sigemptyset (&new_sigset);
120 sigaddset (&new_sigset, SIGTSTP);
121 sigprocmask (SIG_BLOCK, &new_sigset, &old_sigset);
122
123 /* Open a completely transparent gpm connection */
124 gpm_connect.eventMask = 0;
125 gpm_connect.defaultMask = ~0;
126 gpm_connect.minMod = ~0;
127 gpm_connect.maxMod = 0;
128 /* cannot do this under xterm, tough */
129 success = (Gpm_Open (&gpm_connect, 0) >= 0);
130
131 /* take the default action, whatever it is (probably a stop :) */
132 sigprocmask (SIG_SETMASK, &old_sigset, 0);
133 sigaction (SIGTSTP, &gpm_saved_suspend_hook, 0);
134 kill (getpid (), SIGTSTP);
135
136 /* in bardo here */
137
138 /* Reincarnation. Prepare for another death early. */
139 sigemptyset(&sa.sa_mask);
140 sa.sa_handler = gpm_suspend_hook;
141 sa.sa_flags = SA_NOMASK;
142 sigaction (SIGTSTP, &sa, 0);
143
144 /* Pop the gpm stack by closing the useless connection */
145 /* but do it only when we know we opened one.. */
146 if (success) {
147 Gpm_Close ();
148 } /*if*/
149}
150
151static int Gpm_Open(Gpm_Connect *conn, int flag)
152{
153 char tty[32];
154 char *term;
155 int i;
156 struct sockaddr_un addr;
157 Gpm_Stst *new;
158 char* sock_name = 0;
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;
231 if (!(sock_name = tempnam (0, "gpm"))) {
232 goto err;
233 } /*if*/
234 strncpy (addr.sun_path, sock_name, sizeof (addr.sun_path));
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);
305 if (sock_name) {
306 unlink(sock_name);
307 free(sock_name);
308 sock_name = 0;
309 } /*if*/
310 gpm_flag=0;
311 return -1;
312}
313
314/*-------------------------------------------------------------------*/
315static int Gpm_Close(void)
316{
317 Gpm_Stst *next;
318
319 gpm_tried=0; /* reset the error flag for next time */
320 if (gpm_fd==-2) /* xterm */
321 GPM_XTERM_OFF;
322 else /* linux */
323 {
324 if (!gpm_flag) return 0;
325 next=gpm_stack->next;
326 free(gpm_stack);
327 gpm_stack=next;
328 if (next)
329 putdata(gpm_fd,&(next->info));
330
331 if (--gpm_flag) return -1;
332 }
333
334 if (gpm_fd>=0) close(gpm_fd);
335 gpm_fd=-1;
336#ifdef SIGTSTP
337 sigaction(SIGTSTP, &gpm_saved_suspend_hook, 0);
338#endif
339#ifdef SIGWINCH
340 sigaction(SIGWINCH, &gpm_saved_winch_hook, 0);
341#endif
342 return 0;
343}
344
345/*-------------------------------------------------------------------*/
346static int Gpm_GetEvent(Gpm_Event *event)
347{
348 int count;
349
350 if (!gpm_flag) return 0;
351
352 if ((count=read(gpm_fd,event,sizeof(Gpm_Event)))!=sizeof(Gpm_Event))
353 {
354 if (count==0)
355 {
356 Gpm_Close();
357 return 0;
358 }
359 return -1;
360 }
361 return 1;
362}
363#endif
364
f5daa14e 365/****************************************************************************
45f6c4fd 366 These forms handle vertical scrolling of components with a height of 1
367
f5daa14e 368 Horizontal scrolling won't work, and scrolling large widgets will fail
369 miserably. It shouldn't be too hard to fix either of those if anyone
370 cares to. I only use scrolling for listboxes and text boxes though so
371 I didn't bother.
372*****************************************************************************/
373
374struct element {
6f481af2 375 int top, left; /* Actual, not virtual. These are translated */
376 newtComponent co; /* into actual through vertOffset */
f5daa14e 377};
378
d02ffb49 379struct fdInfo {
380 int fd;
381 int flags;
382};
383
c4827b34 384struct form {
6fb96a3f 385 int numCompsAlloced;
f5daa14e 386 struct element * elements;
6fb96a3f 387 int numComps;
388 int currComp;
8c4e1047 389 int fixedHeight;
02b180e8 390 int flags;
f5daa14e 391 int vertOffset;
02b180e8 392 newtComponent vertBar, exitComp;
d4109c37 393 const char * help;
f1f3611f 394 int numRows;
da151ca8 395 int * hotKeys;
396 int numHotKeys;
8f4e87fc 397 int background;
3da9c9a2 398 int beenSet;
d02ffb49 399 int numFds;
400 struct fdInfo * fds;
401 int maxFd;
e67a6cab 402 int timer; /* in milliseconds */
403 struct timeval lastTimeout;
72b71fa6 404 void * helpTag;
405 newtCallback helpCb;
6fb96a3f 406};
407
c4827b34 408static void gotoComponent(struct form * form, int newComp);
409static struct eventResult formEvent(newtComponent co, struct event ev);
434c766b 410static struct eventResult sendEvent(newtComponent comp, struct event ev);
8f52cd47 411static void formPlace(newtComponent co, int left, int top);
c4827b34 412
72b71fa6 413/* Global, ick */
414static newtCallback helpCallback;
415
8f52cd47 416/* this isn't static as grid.c tests against it to find forms */
417struct componentOps formOps = {
8b68158d 418 newtDrawForm,
c4827b34 419 formEvent,
420 newtFormDestroy,
8f52cd47 421 formPlace,
422 newtDefaultMappedHandler,
c4827b34 423} ;
424
0593cb33
ML
425int needResize = 0;
426
f5daa14e 427static inline int componentFits(newtComponent co, int compNum) {
428 struct form * form = co->data;
429 struct element * el = form->elements + compNum;
430
431 if ((co->top + form->vertOffset) > el->top) return 0;
432 if ((co->top + form->vertOffset + co->height) <
6f481af2 433 (el->top + el->co->height)) return 0;
f5daa14e 434
435 return 1;
436}
437
72b71fa6 438newtComponent newtForm(newtComponent vertBar, void * help, int flags) {
c4827b34 439 newtComponent co;
440 struct form * form;
441
442 co = malloc(sizeof(*co));
443 form = malloc(sizeof(*form));
444 co->data = form;
445 co->width = 0;
446 co->height = 0;
447 co->top = -1;
448 co->left = -1;
3da9c9a2 449 co->isMapped = 0;
c4827b34 450
6f481af2 451 co->takesFocus = 0; /* we may have 0 components */
c4827b34 452 co->ops = &formOps;
c101e99e 453 co->destroyCallback = NULL;
6fb96a3f 454
02b180e8 455 form->help = help;
456 form->flags = flags;
6fb96a3f 457 form->numCompsAlloced = 5;
458 form->numComps = 0;
459 form->currComp = -1;
f5daa14e 460 form->vertOffset = 0;
8c4e1047 461 form->fixedHeight = 0;
f1f3611f 462 form->numRows = 0;
d02ffb49 463 form->numFds = 0;
464 form->maxFd = 0;
465 form->fds = NULL;
3da9c9a2 466 form->beenSet = 0;
f5daa14e 467 form->elements = malloc(sizeof(*(form->elements)) * form->numCompsAlloced);
6fb96a3f 468
8f4e87fc 469 form->background = COLORSET_WINDOW;
da151ca8 470 form->hotKeys = malloc(sizeof(int));
471 form->numHotKeys = 0;
73390860 472 form->timer = 0;
473 form->lastTimeout.tv_sec = form->lastTimeout.tv_usec = 0;
4a93351d 474 if (!(form->flags & NEWT_FLAG_NOF12)) {
6f481af2 475 newtFormAddHotKey(co, NEWT_KEY_F12);
da151ca8 476 }
477
02b180e8 478 if (vertBar)
6f481af2 479 form->vertBar = vertBar;
f1f3611f 480 else
6f481af2 481 form->vertBar = NULL;
f1f3611f 482
72b71fa6 483 form->helpTag = help;
484 form->helpCb = helpCallback;
485
c4827b34 486 return co;
6fb96a3f 487}
488
2a2998a0 489newtComponent newtFormGetCurrent(newtComponent co) {
490 struct form * form = co->data;
491
d78245ab 492 if (form->currComp == -1) return 0;
2a2998a0 493 return form->elements[form->currComp].co;
494}
495
7ea3dccd 496void newtFormSetCurrent(newtComponent co, newtComponent subco) {
497 struct form * form = co->data;
498 int i, new;
499
500 for (i = 0; i < form->numComps; i++) {
6f481af2 501 if (form->elements[i].co == subco) break;
7ea3dccd 502 }
503
504 if (form->elements[i].co != subco) return;
505 new = i;
506
3da9c9a2 507 if (co->isMapped && !componentFits(co, new)) {
6f481af2 508 gotoComponent(form, -1);
509 form->vertOffset = form->elements[new].top - co->top - 1;
510 if (form->vertOffset > (form->numRows - co->height))
511 form->vertOffset = form->numRows - co->height;
7ea3dccd 512 }
513
514 gotoComponent(form, new);
515}
516
e67a6cab 517void newtFormSetTimer(newtComponent co, int millisecs) {
518 struct form * form = co->data;
519
520 form->timer = millisecs;
73390860 521 form->lastTimeout.tv_usec = 0;
522 form->lastTimeout.tv_sec = 0;
e67a6cab 523}
524
8c4e1047 525void newtFormSetHeight(newtComponent co, int height) {
f5daa14e 526 struct form * form = co->data;
527
8c4e1047 528 form->fixedHeight = 1;
f5daa14e 529 co->height = height;
530}
531
ea947804 532void newtFormSetWidth(newtComponent co, int width) {
533 co->width = width;
534}
535
c4827b34 536void newtFormAddComponent(newtComponent co, newtComponent newco) {
537 struct form * form = co->data;
6fb96a3f 538
8f52cd47 539 co->takesFocus = 1;
540
6fb96a3f 541 if (form->numCompsAlloced == form->numComps) {
6f481af2 542 form->numCompsAlloced += 5;
543 form->elements = realloc(form->elements,
544 sizeof(*(form->elements)) * form->numCompsAlloced);
6fb96a3f 545 }
546
f7540d34 547 /* we grab real values for these a bit later */
548 form->elements[form->numComps].left = -2;
549 form->elements[form->numComps].top = -2;
f5daa14e 550 form->elements[form->numComps].co = newco;
f1f3611f 551
552 if (newco->takesFocus && form->currComp == -1)
6f481af2 553 form->currComp = form->numComps;
f1f3611f 554
f5daa14e 555 form->numComps++;
6fb96a3f 556}
557
c4827b34 558void newtFormAddComponents(newtComponent co, ...) {
6fb96a3f 559 va_list ap;
c4827b34 560 newtComponent subco;
6fb96a3f 561
c4827b34 562 va_start(ap, co);
6fb96a3f 563
c4827b34 564 while ((subco = va_arg(ap, newtComponent)))
6f481af2 565 newtFormAddComponent(co, subco);
45f6c4fd 566
6fb96a3f 567 va_end(ap);
568}
569
8f52cd47 570static void formPlace(newtComponent co, int left, int top) {
571 struct form * form = co->data;
572 int vertDelta, horizDelta;
573 struct element * el;
574 int i;
575
576 newtFormSetSize(co);
577
578 vertDelta = top - co->top;
579 horizDelta = left - co->left;
580 co->top = top;
581 co->left = left;
582
583 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
6f481af2 584 el->co->top += vertDelta;
585 el->top += vertDelta;
586 el->co->left += horizDelta;
587 el->left += horizDelta;
8f52cd47 588 }
589}
590
8b68158d 591void newtDrawForm(newtComponent co) {
c4827b34 592 struct form * form = co->data;
f5daa14e 593 struct element * el;
6fb96a3f 594 int i;
6fb96a3f 595
3da9c9a2 596 newtFormSetSize(co);
597
8f4e87fc 598 SLsmg_set_color(form->background);
a1331450 599 newtClearBox(co->left, co->top, co->width, co->height);
7ff5fd71 600
f5daa14e 601 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
6f481af2 602 /* the scrollbar *always* fits somewhere */
603 if (el->co == form->vertBar) {
604 el->co->ops->mapped(el->co, 1);
605 el->co->ops->draw(el->co);
606 } else {
607 /* only draw it if it'll fit on the screen vertically */
608 if (componentFits(co, i)) {
609 el->co->top = el->top - form->vertOffset;
610 el->co->ops->mapped(el->co, 1);
611 el->co->ops->draw(el->co);
612 } else {
613 el->co->ops->mapped(el->co, 0);
614 }
615 }
6fb96a3f 616 }
f1f3611f 617
618 if (form->vertBar)
6f481af2 619 newtScrollbarSet(form->vertBar, form->vertOffset,
620 form->numRows - co->height);
c4827b34 621}
6fb96a3f 622
c4827b34 623static struct eventResult formEvent(newtComponent co, struct event ev) {
624 struct form * form = co->data;
f5daa14e 625 newtComponent subco = form->elements[form->currComp].co;
d1a682d1 626 int new, wrap = 0;
c4827b34 627 struct eventResult er;
d1a682d1 628 int dir = 0, page = 0;
45f6c4fd 629 int i, num, found;
630 struct element * el;
6fb96a3f 631
c4827b34 632 er.result = ER_IGNORED;
8f52cd47 633 if (!form->numComps) return er;
634
d78245ab 635 if (form->currComp == -1) return er;
6fb96a3f 636
434c766b 637 switch (ev.when) {
638 case EV_EARLY:
6f481af2 639 if (ev.event == EV_KEYPRESS) {
640 if (ev.u.key == NEWT_KEY_TAB) {
641 er.result = ER_SWALLOWED;
642 dir = 1;
643 wrap = 1;
644 } else if (ev.u.key == NEWT_KEY_UNTAB) {
645 er.result = ER_SWALLOWED;
646 dir = -1;
647 wrap = 1;
648 }
649 }
650
651 if (form->numComps) {
652 i = form->currComp;
653 num = 0;
654 while (er.result == ER_IGNORED && num != form->numComps ) {
655 er = form->elements[i].co->ops->event(form->elements[i].co, ev);
656
657 num++;
658 i++;
659 if (i == form->numComps) i = 0;
660 }
661 }
662
663 break;
45f6c4fd 664
434c766b 665 case EV_NORMAL:
6f481af2 666 if (ev.event == EV_MOUSE) {
667 found = 0;
668 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
669 if ((el->co->top <= ev.u.mouse.y) &&
670 (el->co->top + el->co->height > ev.u.mouse.y) &&
671 (el->co->left <= ev.u.mouse.x) &&
672 (el->co->left + el->co->width > ev.u.mouse.x)) {
673 found = 1;
674 if (el->co->takesFocus) {
675 gotoComponent(form, i);
676 subco = form->elements[form->currComp].co;
677 }
678 }
679 /* If we did not find a co to send this event to, we
680 should just swallow the event here. */
681 }
682 if (!found) {
683 er.result = ER_SWALLOWED;
684
685 return er;
686 }
687 }
688 er = subco->ops->event(subco, ev);
689 switch (er.result) {
690 case ER_NEXTCOMP:
691 er.result = ER_SWALLOWED;
692 dir = 1;
693 break;
694
695 case ER_EXITFORM:
696 form->exitComp = subco;
697 break;
698
699 default:
700 break;
701 }
702 break;
434c766b 703
704 case EV_LATE:
6f481af2 705 er = subco->ops->event(subco, ev);
706
707 if (er.result == ER_IGNORED) {
708 switch (ev.u.key) {
709 case NEWT_KEY_UP:
710 case NEWT_KEY_LEFT:
711 case NEWT_KEY_BKSPC:
712 er.result = ER_SWALLOWED;
713 dir = -1;
714 break;
715
716 case NEWT_KEY_DOWN:
717 case NEWT_KEY_RIGHT:
718 er.result = ER_SWALLOWED;
719 dir = 1;
720 break;
721
722 case NEWT_KEY_PGUP:
723 er.result = ER_SWALLOWED;
724 dir = -1;
725 page = 1;
726 break;
727
728 case NEWT_KEY_PGDN:
729 er.result = ER_SWALLOWED;
730 dir = 1;
731 page = 1;
732 break;
733 }
734 }
c4827b34 735 }
6fb96a3f 736
f5daa14e 737 if (dir) {
6f481af2 738 new = form->currComp;
739
740 if (page) {
741 new += dir * co->height;
742 if (new < 0)
743 new = 0;
744 else if (new >= form->numComps)
745 new = (form->numComps - 1);
746
747 while (!form->elements[new].co->takesFocus)
748 new = new - dir;
749 } else {
750 do {
751 new += dir;
752
753 if (wrap) {
754 if (new < 0)
755 new = form->numComps - 1;
756 else if (new >= form->numComps)
757 new = 0;
758 } else if (new < 0 || new >= form->numComps)
759 return er;
760 } while (!form->elements[new].co->takesFocus);
761 }
762
763 /* make sure this component is visible */
764 if (!componentFits(co, new)) {
765 gotoComponent(form, -1);
766
767 if (dir < 0) {
768 /* make the new component the first one */
769 form->vertOffset = form->elements[new].top - co->top;
770 } else {
771 /* make the new component the last one */
772 form->vertOffset = (form->elements[new].top +
773 form->elements[new].co->height) -
774 (co->top + co->height);
775 }
776
777 if (form->vertOffset < 0) form->vertOffset = 0;
778 if (form->vertOffset > (form->numRows - co->height))
779 form->vertOffset = form->numRows - co->height;
780
781 newtDrawForm(co);
782 }
783
784 gotoComponent(form, new);
785 er.result = ER_SWALLOWED;
c4827b34 786 }
6fb96a3f 787
c4827b34 788 return er;
6fb96a3f 789}
790
c101e99e
RJ
791/* Destroy a component. Components which have been added to a form
792 * are destroyed when the form is destroyed; this is just for the
793 * (rare) case of components which for whatever reason weren't added
794 * to a form.
795 */
796void newtComponentDestroy(newtComponent co) {
797 /* If the user registered a destroy callback for this component,
798 * now is a good time to call it.
799 */
800 if (co->destroyCallback)
801 co->destroyCallback(co, co->destroyCallbackData);
802
803 if (co->ops->destroy) {
804 co->ops->destroy(co);
805 } else {
806 if (co->data) free(co->data);
807 free(co);
808 }
809}
810
6fb96a3f 811/* this also destroys all of the components on the form */
c4827b34 812void newtFormDestroy(newtComponent co) {
c4827b34 813 newtComponent subco;
814 struct form * form = co->data;
f1f3611f 815 int i;
6fb96a3f 816
817 /* first, destroy all of the components */
818 for (i = 0; i < form->numComps; i++) {
6f481af2 819 subco = form->elements[i].co;
c101e99e 820 newtComponentDestroy(subco);
6fb96a3f 821 }
822
4e61f5db 823 if (form->hotKeys) free(form->hotKeys);
824
f5daa14e 825 free(form->elements);
6fb96a3f 826 free(form);
c4827b34 827 free(co);
828}
829
830newtComponent newtRunForm(newtComponent co) {
da151ca8 831 struct newtExitStruct es;
832
833 newtFormRun(co, &es);
834 if (es.reason == NEWT_EXIT_HOTKEY) {
6f481af2 835 if (es.u.key == NEWT_KEY_F12) {
836 es.reason = NEWT_EXIT_COMPONENT;
837 es.u.co = co;
838 } else {
839 return NULL;
840 }
da151ca8 841 }
842
843 return es.u.co;
844}
845
846void newtFormAddHotKey(newtComponent co, int key) {
847 struct form * form = co->data;
848
849 form->numHotKeys++;
850 form->hotKeys = realloc(form->hotKeys, sizeof(int) * form->numHotKeys);
851 form->hotKeys[form->numHotKeys - 1] = key;
852}
853
3da9c9a2 854void newtFormSetSize(newtComponent co) {
f7540d34 855 struct form * form = co->data;
856 int delta, i;
857 struct element * el;
858
3da9c9a2 859 if (form->beenSet) return;
860
861 form->beenSet = 1;
862
8f52cd47 863 if (!form->numComps) return;
864
f7540d34 865 co->width = 0;
3da9c9a2 866 if (!form->fixedHeight) co->height = 0;
867
f7540d34 868 co->top = form->elements[0].co->top;
869 co->left = form->elements[0].co->left;
870 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
6f481af2 871 if (el->co->ops == &formOps)
872 newtFormSetSize(el->co);
873
874 el->left = el->co->left;
875 el->top = el->co->top;
876
877 if (co->left > el->co->left) {
878 delta = co->left - el->co->left;
879 co->left -= delta;
880 co->width += delta;
881 }
882
883 if (co->top > el->co->top) {
884 delta = co->top - el->co->top;
885 co->top -= delta;
886 if (!form->fixedHeight)
887 co->height += delta;
888 }
889
890 if ((co->left + co->width) < (el->co->left + el->co->width))
891 co->width = (el->co->left + el->co->width) - co->left;
892
893 if (!form->fixedHeight) {
894 if ((co->top + co->height) < (el->co->top + el->co->height))
895 co->height = (el->co->top + el->co->height) - co->top;
896 }
897
898 if ((el->co->top + el->co->height - co->top) > form->numRows) {
899 form->numRows = el->co->top + el->co->height - co->top;
900 }
f7540d34 901 }
902}
903
da151ca8 904void newtFormRun(newtComponent co, struct newtExitStruct * es) {
c4827b34 905 struct form * form = co->data;
906 struct event ev;
907 struct eventResult er;
45f6c4fd 908 int key, i, max;
da151ca8 909 int done = 0;
d7e202d6 910 fd_set readSet, writeSet, exceptSet;
e67a6cab 911 struct timeval nextTimeout, now, timeout;
33b9b6eb 912#ifdef USE_GPM
45f6c4fd 913 int x, y;
914 Gpm_Connect conn;
915 Gpm_Event event;
916
917 /* Set up GPM interface */
918 conn.eventMask = ~GPM_MOVE;
919 conn.defaultMask = GPM_MOVE;
920 conn.minMod = 0;
921 conn.maxMod = 0;
922
923 Gpm_Open(&conn, 0);
924#endif
c4827b34 925
f7540d34 926 newtFormSetSize(co);
927 /* draw all of the components */
8b68158d 928 newtDrawForm(co);
c4827b34 929
f1f3611f 930 if (form->currComp == -1) {
6f481af2 931 gotoComponent(form, 0);
f1f3611f 932 } else
6f481af2 933 gotoComponent(form, form->currComp);
45f6c4fd 934
da151ca8 935 while (!done) {
6f481af2 936 newtRefresh();
c4827b34 937
6f481af2 938 FD_ZERO(&readSet);
939 FD_ZERO(&writeSet);
940 FD_ZERO(&exceptSet);
941 FD_SET(0, &readSet);
33b9b6eb 942#ifdef USE_GPM
6f481af2 943 if (gpm_fd > 0) {
944 FD_SET(gpm_fd, &readSet);
945 }
946 max = form->maxFd > gpm_fd ? form->maxFd : gpm_fd;
45f6c4fd 947#else
6f481af2 948 max = form->maxFd;
45f6c4fd 949#endif
950
6f481af2 951 for (i = 0; i < form->numFds; i++) {
952 if (form->fds[i].flags & NEWT_FD_READ)
953 FD_SET(form->fds[i].fd, &readSet);
954 if (form->fds[i].flags & NEWT_FD_WRITE)
955 FD_SET(form->fds[i].fd, &writeSet);
956 if (form->fds[i].flags & NEWT_FD_EXCEPT)
957 FD_SET(form->fds[i].fd, &exceptSet);
958 }
959
960 if (form->timer) {
961 /* Calculate when we next need to return with a timeout. Do
962 this inside the loop in case a callback resets the timer. */
57660927 963 gettimeofday(&now, 0);
964
965 if ((!form->lastTimeout.tv_sec && !form->lastTimeout.tv_usec) ||
966 now.tv_sec < form->lastTimeout.tv_sec ||
967 (now.tv_sec == form->lastTimeout.tv_sec &&
968 now.tv_usec < form->lastTimeout.tv_usec))
969 form->lastTimeout = now;
6f481af2 970
971 nextTimeout.tv_sec = form->lastTimeout.tv_sec +
972 (form->timer / 1000);
973 nextTimeout.tv_usec = form->lastTimeout.tv_usec +
974 (form->timer % 1000) * 1000;
975
6f481af2 976 if (now.tv_sec > nextTimeout.tv_sec) {
977 timeout.tv_sec = timeout.tv_usec = 0;
978 } else if (now.tv_sec == nextTimeout.tv_sec) {
979 timeout.tv_sec = 0;
980 if (now.tv_usec > nextTimeout.tv_usec)
981 timeout.tv_usec = 0;
982 else
983 timeout.tv_usec = nextTimeout.tv_usec - now.tv_usec;
984 } else if (now.tv_sec < nextTimeout.tv_sec) {
985 timeout.tv_sec = nextTimeout.tv_sec - now.tv_sec;
986 if (now.tv_usec > nextTimeout.tv_usec)
987 timeout.tv_sec--,
988 timeout.tv_usec = nextTimeout.tv_usec + 1000000 -
989 now.tv_usec;
990 else
991 timeout.tv_usec = nextTimeout.tv_usec - now.tv_usec;
992 }
993 } else {
994 timeout.tv_sec = timeout.tv_usec = 0;
995 }
996
0593cb33
ML
997 if (needResize) {
998 needResize = 0;
999 newtResizeScreen(1);
1000 }
1001
6f481af2 1002 i = select(max + 1, &readSet, &writeSet, &exceptSet,
1003 form->timer ? &timeout : NULL);
1004 if (i < 0) continue; /* ?? What should we do here? */
1005
1006 if (i == 0) {
1007 done = 1;
1008 es->reason = NEWT_EXIT_TIMER;
1009 gettimeofday(&form->lastTimeout, NULL);
1010 } else
33b9b6eb 1011#ifdef USE_GPM
6f481af2 1012 if (gpm_fd > 0 && FD_ISSET(gpm_fd, &readSet)) {
1013 Gpm_GetEvent(&event);
d02ffb49 1014
6f481af2 1015 if (event.type & GPM_DOWN) {
1016 /* Transform coordinates to current window */
1017 newtGetWindowPos(&x, &y);
c4827b34 1018
6f481af2 1019 ev.event = EV_MOUSE;
1020 ev.u.mouse.type = MOUSE_BUTTON_DOWN;
1021 ev.u.mouse.x = event.x - x - 1;
1022 ev.u.mouse.y = event.y - y - 1;
d02ffb49 1023
6f481af2 1024 /* Send the form the event */
1025 er = sendEvent(co, ev);
45f6c4fd 1026
6f481af2 1027 if (er.result == ER_EXITFORM) {
1028 done = 1;
1029 es->reason = NEWT_EXIT_COMPONENT;
1030 es->u.co = form->exitComp;
1031 }
45f6c4fd 1032
6f481af2 1033 }
1034 } else
45f6c4fd 1035#endif
6f481af2 1036 {
1037 if (FD_ISSET(0, &readSet)) {
1038
1039 key = newtGetKey();
1040
6f481af2 1041 for (i = 0; i < form->numHotKeys; i++) {
1042 if (form->hotKeys[i] == key) {
1043 es->reason = NEWT_EXIT_HOTKEY;
1044 es->u.key = key;
1045 done = 1;
1046 break;
1047 }
1048 }
1049
9fc6a6b0 1050 if (key == NEWT_KEY_F1 && form->helpTag && form->helpCb) {
1051 if (form->currComp != -1) {
1052 ev.event = EV_UNFOCUS;
1053 sendEvent(form->elements[form->currComp].co, ev);
1054 }
6f481af2 1055 form->helpCb(co, form->helpTag);
9fc6a6b0 1056 if (form->currComp != -1) {
1057 ev.event = EV_FOCUS;
1058 sendEvent(form->elements[form->currComp].co, ev);
1059 }
1060 }
6f481af2 1061
1062 if (!done) {
1063 ev.event = EV_KEYPRESS;
1064 ev.u.key = key;
1065
1066 er = sendEvent(co, ev);
1067
1068 if (er.result == ER_EXITFORM) {
1069 done = 1;
1070 es->reason = NEWT_EXIT_COMPONENT;
1071 es->u.co = form->exitComp;
1072 }
1073 }
1074 } else {
1075 for (i = 0; i < form->numFds; i++) {
1076 if (((form->fds[i].flags & NEWT_FD_READ)
1077 && FD_ISSET(form->fds[i].fd, &readSet))
1078 || ((form->fds[i].flags & NEWT_FD_WRITE)
1079 && FD_ISSET(form->fds[i].fd, &writeSet))
1080 || ((form->fds[i].flags & NEWT_FD_EXCEPT)
1081 && FD_ISSET(form->fds[i].fd, &exceptSet))) break;
1082 }
1083 if(i < form->numFds)
1084 es->u.watch = form->fds[i].fd;
1085 else
1086 es->u.watch = -1;
1087
1088 es->reason = NEWT_EXIT_FDREADY;
1089 done = 1;
1090 }
1091 }
45f6c4fd 1092 }
c4827b34 1093 newtRefresh();
33b9b6eb 1094#ifdef USE_GPM
45f6c4fd 1095 Gpm_Close();
1096#endif
6fb96a3f 1097}
1098
434c766b 1099static struct eventResult sendEvent(newtComponent co, struct event ev) {
1100 struct eventResult er;
1101
1102 ev.when = EV_EARLY;
1103 er = co->ops->event(co, ev);
1104
1105 if (er.result == ER_IGNORED) {
6f481af2 1106 ev.when = EV_NORMAL;
1107 er = co->ops->event(co, ev);
434c766b 1108 }
1109
1110 if (er.result == ER_IGNORED) {
6f481af2 1111 ev.when = EV_LATE;
1112 er = co->ops->event(co, ev);
434c766b 1113 }
1114
1115 return er;
1116}
1117
c4827b34 1118static void gotoComponent(struct form * form, int newComp) {
6fb96a3f 1119 struct event ev;
1120
1121 if (form->currComp != -1) {
6f481af2 1122 ev.event = EV_UNFOCUS;
1123 sendEvent(form->elements[form->currComp].co, ev);
6fb96a3f 1124 }
1125
1126 form->currComp = newComp;
45f6c4fd 1127
f5daa14e 1128 if (form->currComp != -1) {
6f481af2 1129 ev.event = EV_FOCUS;
1130 ev.when = EV_NORMAL;
1131 sendEvent(form->elements[form->currComp].co, ev);
f5daa14e 1132 }
6fb96a3f 1133}
da151ca8 1134
1135void newtComponentAddCallback(newtComponent co, newtCallback f, void * data) {
1136 co->callback = f;
1137 co->callbackData = data;
1138}
1139
c101e99e
RJ
1140/* Add a callback which is called when the component is destroyed. */
1141void newtComponentAddDestroyCallback(newtComponent co,
1142 newtCallback f, void * data) {
1143 co->destroyCallback = f;
1144 co->destroyCallbackData = data;
1145}
1146
41d86a3c 1147void newtComponentTakesFocus(newtComponent co, int val) {
1148 co->takesFocus = val;
1149}
1150
8f4e87fc 1151void newtFormSetBackground(newtComponent co, int color) {
1152 struct form * form = co->data;
1153
1154 form->background = color;
1155}
d02ffb49 1156
1157void newtFormWatchFd(newtComponent co, int fd, int fdFlags) {
1158 struct form * form = co->data;
005d8237 1159 int i;
1160
1161 for (i = 0; i < form->numFds; i++)
1162 if (form->fds[i].fd == fd)
6f481af2 1163 break;
005d8237 1164
1165 if(i >= form->numFds)
1166 form->fds = realloc(form->fds, (++form->numFds) * sizeof(*form->fds));
d02ffb49 1167
005d8237 1168 form->fds[i].fd = fd;
1169 form->fds[i].flags = fdFlags;
d02ffb49 1170 if (form->maxFd < fd) form->maxFd = fd;
1171}
72b71fa6 1172
1173void newtSetHelpCallback(newtCallback cb) {
1174 helpCallback = cb;
1175}