]> git.ipfire.org Git - thirdparty/newt.git/blame - form.c
0.52.24
[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
ad2dca95 412static void gotoComponent(newtComponent co, int newComp);
c4827b34 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;
ad2dca95 458 co->callback = NULL;
c101e99e 459 co->destroyCallback = NULL;
6fb96a3f 460
02b180e8 461 form->help = help;
462 form->flags = flags;
6fb96a3f 463 form->numCompsAlloced = 5;
464 form->numComps = 0;
465 form->currComp = -1;
f5daa14e 466 form->vertOffset = 0;
8c4e1047 467 form->fixedHeight = 0;
f1f3611f 468 form->numRows = 0;
d02ffb49 469 form->numFds = 0;
470 form->maxFd = 0;
471 form->fds = NULL;
f5daa14e 472 form->elements = malloc(sizeof(*(form->elements)) * form->numCompsAlloced);
6fb96a3f 473
8f4e87fc 474 form->background = COLORSET_WINDOW;
da151ca8 475 form->hotKeys = malloc(sizeof(int));
476 form->numHotKeys = 0;
73390860 477 form->timer = 0;
478 form->lastTimeout.tv_sec = form->lastTimeout.tv_usec = 0;
4a93351d 479 if (!(form->flags & NEWT_FLAG_NOF12)) {
6f481af2 480 newtFormAddHotKey(co, NEWT_KEY_F12);
da151ca8 481 }
482
02b180e8 483 if (vertBar)
6f481af2 484 form->vertBar = vertBar;
f1f3611f 485 else
6f481af2 486 form->vertBar = NULL;
f1f3611f 487
72b71fa6 488 form->helpTag = help;
489 form->helpCb = helpCallback;
490
c4827b34 491 return co;
6fb96a3f 492}
493
2a2998a0 494newtComponent newtFormGetCurrent(newtComponent co) {
495 struct form * form = co->data;
496
d78245ab 497 if (form->currComp == -1) return 0;
2a2998a0 498 return form->elements[form->currComp].co;
499}
500
5c72fb03
ML
501static void formScroll(newtComponent co, int delta) {
502 struct form * form = co->data;
503 struct element * el;
504 int i, newVertOffset = form->vertOffset + delta;
505
506 if (newVertOffset < 0)
507 newVertOffset = 0;
508 if (newVertOffset > form->numRows - co->height)
509 newVertOffset = form->numRows - co->height;
510
511 delta = newVertOffset - form->vertOffset;
512 form->vertOffset = newVertOffset;
513
514 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
515 if (el->co == form->vertBar)
516 continue;
517 el->co->ops->place(el->co, el->co->left, el->co->top - delta);
518 }
519}
520
7f72e4ef
DW
521int newtFormGetScrollPosition(newtComponent co) {
522 struct form * form = co->data;
523
524 return form->vertOffset;
525}
526
527void newtFormSetScrollPosition(newtComponent co, int position) {
528 struct form * form = co->data;
529
530 if (form->numRows == 0)
531 newtFormSetSize(co);
532 formScroll(co, position - form->vertOffset);
533}
534
7ea3dccd 535void newtFormSetCurrent(newtComponent co, newtComponent subco) {
536 struct form * form = co->data;
537 int i, new;
538
539 for (i = 0; i < form->numComps; i++) {
6f481af2 540 if (form->elements[i].co == subco) break;
7ea3dccd 541 }
542
543 if (form->elements[i].co != subco) return;
544 new = i;
545
3da9c9a2 546 if (co->isMapped && !componentFits(co, new)) {
ad2dca95 547 gotoComponent(co, -1);
5c72fb03 548 formScroll(co, form->elements[new].co->top - co->top - 1);
7ea3dccd 549 }
550
ad2dca95 551 gotoComponent(co, new);
7ea3dccd 552}
553
e67a6cab 554void newtFormSetTimer(newtComponent co, int millisecs) {
555 struct form * form = co->data;
556
557 form->timer = millisecs;
73390860 558 form->lastTimeout.tv_usec = 0;
559 form->lastTimeout.tv_sec = 0;
e67a6cab 560}
561
8c4e1047 562void newtFormSetHeight(newtComponent co, int height) {
f5daa14e 563 struct form * form = co->data;
564
8c4e1047 565 form->fixedHeight = 1;
f5daa14e 566 co->height = height;
567}
568
ea947804 569void newtFormSetWidth(newtComponent co, int width) {
570 co->width = width;
571}
572
c4827b34 573void newtFormAddComponent(newtComponent co, newtComponent newco) {
574 struct form * form = co->data;
6fb96a3f 575
8f52cd47 576 co->takesFocus = 1;
577
6fb96a3f 578 if (form->numCompsAlloced == form->numComps) {
6f481af2 579 form->numCompsAlloced += 5;
580 form->elements = realloc(form->elements,
581 sizeof(*(form->elements)) * form->numCompsAlloced);
6fb96a3f 582 }
583
f5daa14e 584 form->elements[form->numComps].co = newco;
f1f3611f 585
586 if (newco->takesFocus && form->currComp == -1)
6f481af2 587 form->currComp = form->numComps;
f1f3611f 588
f5daa14e 589 form->numComps++;
6fb96a3f 590}
591
c4827b34 592void newtFormAddComponents(newtComponent co, ...) {
6fb96a3f 593 va_list ap;
c4827b34 594 newtComponent subco;
6fb96a3f 595
c4827b34 596 va_start(ap, co);
6fb96a3f 597
c4827b34 598 while ((subco = va_arg(ap, newtComponent)))
6f481af2 599 newtFormAddComponent(co, subco);
45f6c4fd 600
6fb96a3f 601 va_end(ap);
602}
603
8f52cd47 604static void formPlace(newtComponent co, int left, int top) {
605 struct form * form = co->data;
606 int vertDelta, horizDelta;
607 struct element * el;
608 int i;
609
8f52cd47 610 vertDelta = top - co->top;
611 horizDelta = left - co->left;
612 co->top = top;
613 co->left = left;
614
615 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
5c72fb03
ML
616 el->co->ops->place(el->co, el->co->left + horizDelta,
617 el->co->top + vertDelta);
8f52cd47 618 }
619}
620
8b68158d 621void newtDrawForm(newtComponent co) {
c4827b34 622 struct form * form = co->data;
f5daa14e 623 struct element * el;
6fb96a3f 624 int i;
6fb96a3f 625
3da9c9a2 626 newtFormSetSize(co);
627
8f4e87fc 628 SLsmg_set_color(form->background);
a1331450 629 newtClearBox(co->left, co->top, co->width, co->height);
7ff5fd71 630
f5daa14e 631 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
5c72fb03
ML
632 /* only draw it if it'll fit on the screen vertically
633 (the scrollbar *always* fits somewhere) */
634 if (el->co == form->vertBar || componentFits(co, i)) {
6f481af2 635 el->co->ops->mapped(el->co, 1);
636 el->co->ops->draw(el->co);
637 } else {
5c72fb03 638 el->co->ops->mapped(el->co, 0);
6f481af2 639 }
6fb96a3f 640 }
f1f3611f 641
642 if (form->vertBar)
6f481af2 643 newtScrollbarSet(form->vertBar, form->vertOffset,
644 form->numRows - co->height);
c4827b34 645}
6fb96a3f 646
c4827b34 647static struct eventResult formEvent(newtComponent co, struct event ev) {
648 struct form * form = co->data;
f5daa14e 649 newtComponent subco = form->elements[form->currComp].co;
d1a682d1 650 int new, wrap = 0;
c4827b34 651 struct eventResult er;
d1a682d1 652 int dir = 0, page = 0;
45f6c4fd 653 int i, num, found;
654 struct element * el;
6fb96a3f 655
c4827b34 656 er.result = ER_IGNORED;
8f52cd47 657 if (!form->numComps) return er;
658
d78245ab 659 if (form->currComp == -1) return er;
6fb96a3f 660
434c766b 661 switch (ev.when) {
662 case EV_EARLY:
6f481af2 663 if (ev.event == EV_KEYPRESS) {
664 if (ev.u.key == NEWT_KEY_TAB) {
665 er.result = ER_SWALLOWED;
666 dir = 1;
667 wrap = 1;
668 } else if (ev.u.key == NEWT_KEY_UNTAB) {
669 er.result = ER_SWALLOWED;
670 dir = -1;
671 wrap = 1;
672 }
673 }
674
675 if (form->numComps) {
676 i = form->currComp;
677 num = 0;
678 while (er.result == ER_IGNORED && num != form->numComps ) {
679 er = form->elements[i].co->ops->event(form->elements[i].co, ev);
680
681 num++;
682 i++;
683 if (i == form->numComps) i = 0;
684 }
685 }
686
687 break;
45f6c4fd 688
434c766b 689 case EV_NORMAL:
6f481af2 690 if (ev.event == EV_MOUSE) {
691 found = 0;
692 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
693 if ((el->co->top <= ev.u.mouse.y) &&
694 (el->co->top + el->co->height > ev.u.mouse.y) &&
695 (el->co->left <= ev.u.mouse.x) &&
696 (el->co->left + el->co->width > ev.u.mouse.x)) {
697 found = 1;
698 if (el->co->takesFocus) {
ad2dca95 699 gotoComponent(co, i);
6f481af2 700 subco = form->elements[form->currComp].co;
701 }
702 }
703 /* If we did not find a co to send this event to, we
704 should just swallow the event here. */
705 }
706 if (!found) {
707 er.result = ER_SWALLOWED;
708
709 return er;
710 }
711 }
712 er = subco->ops->event(subco, ev);
713 switch (er.result) {
714 case ER_NEXTCOMP:
715 er.result = ER_SWALLOWED;
716 dir = 1;
717 break;
718
719 case ER_EXITFORM:
720 form->exitComp = subco;
721 break;
722
723 default:
724 break;
725 }
726 break;
434c766b 727
728 case EV_LATE:
6f481af2 729 er = subco->ops->event(subco, ev);
730
731 if (er.result == ER_IGNORED) {
732 switch (ev.u.key) {
733 case NEWT_KEY_UP:
734 case NEWT_KEY_LEFT:
735 case NEWT_KEY_BKSPC:
736 er.result = ER_SWALLOWED;
737 dir = -1;
738 break;
739
740 case NEWT_KEY_DOWN:
741 case NEWT_KEY_RIGHT:
742 er.result = ER_SWALLOWED;
743 dir = 1;
744 break;
745
746 case NEWT_KEY_PGUP:
747 er.result = ER_SWALLOWED;
748 dir = -1;
749 page = 1;
750 break;
751
752 case NEWT_KEY_PGDN:
753 er.result = ER_SWALLOWED;
754 dir = 1;
755 page = 1;
756 break;
757 }
758 }
c4827b34 759 }
6fb96a3f 760
f5daa14e 761 if (dir) {
6f481af2 762 new = form->currComp;
763
764 if (page) {
765 new += dir * co->height;
766 if (new < 0)
767 new = 0;
768 else if (new >= form->numComps)
769 new = (form->numComps - 1);
770
cf69773e
ML
771 while (!form->elements[new].co->takesFocus &&
772 new - dir >= 0 && new - dir < form->numComps)
773 new -= dir;
6f481af2 774 } else {
775 do {
776 new += dir;
777
778 if (wrap) {
779 if (new < 0)
780 new = form->numComps - 1;
781 else if (new >= form->numComps)
782 new = 0;
cf69773e
ML
783 if (new == form->currComp)
784 /* back where we started */
785 return er;
6f481af2 786 } else if (new < 0 || new >= form->numComps)
787 return er;
788 } while (!form->elements[new].co->takesFocus);
789 }
790
791 /* make sure this component is visible */
792 if (!componentFits(co, new)) {
5c72fb03
ML
793 int vertDelta;
794
ad2dca95 795 gotoComponent(co, -1);
6f481af2 796
797 if (dir < 0) {
798 /* make the new component the first one */
5c72fb03 799 vertDelta = form->elements[new].co->top - co->top;
6f481af2 800 } else {
801 /* make the new component the last one */
5c72fb03 802 vertDelta = (form->elements[new].co->top +
6f481af2 803 form->elements[new].co->height) -
804 (co->top + co->height);
805 }
806
5c72fb03 807 formScroll(co, vertDelta);
6f481af2 808 newtDrawForm(co);
809 }
810
ad2dca95 811 gotoComponent(co, new);
6f481af2 812 er.result = ER_SWALLOWED;
c4827b34 813 }
6fb96a3f 814
c4827b34 815 return er;
6fb96a3f 816}
817
c101e99e
RJ
818/* Destroy a component. Components which have been added to a form
819 * are destroyed when the form is destroyed; this is just for the
820 * (rare) case of components which for whatever reason weren't added
821 * to a form.
822 */
823void newtComponentDestroy(newtComponent co) {
824 /* If the user registered a destroy callback for this component,
825 * now is a good time to call it.
826 */
827 if (co->destroyCallback)
828 co->destroyCallback(co, co->destroyCallbackData);
829
830 if (co->ops->destroy) {
831 co->ops->destroy(co);
832 } else {
833 if (co->data) free(co->data);
834 free(co);
835 }
836}
837
6fb96a3f 838/* this also destroys all of the components on the form */
c4827b34 839void newtFormDestroy(newtComponent co) {
c4827b34 840 newtComponent subco;
841 struct form * form = co->data;
f1f3611f 842 int i;
6fb96a3f 843
844 /* first, destroy all of the components */
845 for (i = 0; i < form->numComps; i++) {
6f481af2 846 subco = form->elements[i].co;
c101e99e 847 newtComponentDestroy(subco);
6fb96a3f 848 }
849
4e61f5db 850 if (form->hotKeys) free(form->hotKeys);
851
f5daa14e 852 free(form->elements);
6fb96a3f 853 free(form);
c4827b34 854 free(co);
855}
856
857newtComponent newtRunForm(newtComponent co) {
da151ca8 858 struct newtExitStruct es;
859
860 newtFormRun(co, &es);
861 if (es.reason == NEWT_EXIT_HOTKEY) {
6f481af2 862 if (es.u.key == NEWT_KEY_F12) {
863 es.reason = NEWT_EXIT_COMPONENT;
864 es.u.co = co;
865 } else {
866 return NULL;
867 }
b8b8a86f
ML
868 } else if (es.reason == NEWT_EXIT_ERROR)
869 return NULL;
da151ca8 870
871 return es.u.co;
872}
873
874void newtFormAddHotKey(newtComponent co, int key) {
875 struct form * form = co->data;
876
877 form->numHotKeys++;
878 form->hotKeys = realloc(form->hotKeys, sizeof(int) * form->numHotKeys);
879 form->hotKeys[form->numHotKeys - 1] = key;
880}
881
3da9c9a2 882void newtFormSetSize(newtComponent co) {
f7540d34 883 struct form * form = co->data;
21150df5 884 int delta, i, first;
f7540d34 885 struct element * el;
886
21150df5 887 form->numRows = 0;
8f52cd47 888
f7540d34 889 co->width = 0;
3da9c9a2 890 if (!form->fixedHeight) co->height = 0;
891
35d6a6ab
ML
892 co->top = -1;
893 co->left = -1;
21150df5 894 first = 1;
35d6a6ab 895
f7540d34 896 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
6f481af2 897 if (el->co->ops == &formOps)
898 newtFormSetSize(el->co);
35d6a6ab
ML
899 else if (el->co == form->vertBar)
900 continue;
901
21150df5 902 if (first) {
35d6a6ab
ML
903 co->top = el->co->top;
904 co->left = el->co->left;
21150df5 905 first = 0;
35d6a6ab 906 }
6f481af2 907
6f481af2 908 if (co->left > el->co->left) {
909 delta = co->left - el->co->left;
910 co->left -= delta;
911 co->width += delta;
912 }
913
914 if (co->top > el->co->top) {
915 delta = co->top - el->co->top;
916 co->top -= delta;
35d6a6ab 917 form->numRows += delta;
6f481af2 918 if (!form->fixedHeight)
919 co->height += delta;
920 }
921
922 if ((co->left + co->width) < (el->co->left + el->co->width))
923 co->width = (el->co->left + el->co->width) - co->left;
924
925 if (!form->fixedHeight) {
926 if ((co->top + co->height) < (el->co->top + el->co->height))
927 co->height = (el->co->top + el->co->height) - co->top;
928 }
929
930 if ((el->co->top + el->co->height - co->top) > form->numRows) {
931 form->numRows = el->co->top + el->co->height - co->top;
932 }
f7540d34 933 }
5c72fb03
ML
934
935 co->top += form->vertOffset;
f7540d34 936}
937
da151ca8 938void newtFormRun(newtComponent co, struct newtExitStruct * es) {
c4827b34 939 struct form * form = co->data;
940 struct event ev;
941 struct eventResult er;
45f6c4fd 942 int key, i, max;
da151ca8 943 int done = 0;
d7e202d6 944 fd_set readSet, writeSet, exceptSet;
e67a6cab 945 struct timeval nextTimeout, now, timeout;
33b9b6eb 946#ifdef USE_GPM
45f6c4fd 947 int x, y;
948 Gpm_Connect conn;
949 Gpm_Event event;
950
951 /* Set up GPM interface */
952 conn.eventMask = ~GPM_MOVE;
953 conn.defaultMask = GPM_MOVE;
954 conn.minMod = 0;
955 conn.maxMod = 0;
956
957 Gpm_Open(&conn, 0);
958#endif
c4827b34 959
f7540d34 960 /* draw all of the components */
8b68158d 961 newtDrawForm(co);
c4827b34 962
f1f3611f 963 if (form->currComp == -1) {
d3cfe899 964 if (form->numComps)
ad2dca95 965 gotoComponent(co, 0);
f1f3611f 966 } else
ad2dca95 967 gotoComponent(co, form->currComp);
45f6c4fd 968
da151ca8 969 while (!done) {
6f481af2 970 newtRefresh();
c4827b34 971
6f481af2 972 FD_ZERO(&readSet);
973 FD_ZERO(&writeSet);
974 FD_ZERO(&exceptSet);
975 FD_SET(0, &readSet);
33b9b6eb 976#ifdef USE_GPM
6f481af2 977 if (gpm_fd > 0) {
978 FD_SET(gpm_fd, &readSet);
979 }
980 max = form->maxFd > gpm_fd ? form->maxFd : gpm_fd;
45f6c4fd 981#else
6f481af2 982 max = form->maxFd;
45f6c4fd 983#endif
984
6f481af2 985 for (i = 0; i < form->numFds; i++) {
986 if (form->fds[i].flags & NEWT_FD_READ)
987 FD_SET(form->fds[i].fd, &readSet);
988 if (form->fds[i].flags & NEWT_FD_WRITE)
989 FD_SET(form->fds[i].fd, &writeSet);
990 if (form->fds[i].flags & NEWT_FD_EXCEPT)
991 FD_SET(form->fds[i].fd, &exceptSet);
992 }
993
994 if (form->timer) {
995 /* Calculate when we next need to return with a timeout. Do
996 this inside the loop in case a callback resets the timer. */
57660927 997 gettimeofday(&now, 0);
998
999 if ((!form->lastTimeout.tv_sec && !form->lastTimeout.tv_usec) ||
1000 now.tv_sec < form->lastTimeout.tv_sec ||
1001 (now.tv_sec == form->lastTimeout.tv_sec &&
1002 now.tv_usec < form->lastTimeout.tv_usec))
1003 form->lastTimeout = now;
6f481af2 1004
1005 nextTimeout.tv_sec = form->lastTimeout.tv_sec +
1006 (form->timer / 1000);
1007 nextTimeout.tv_usec = form->lastTimeout.tv_usec +
1008 (form->timer % 1000) * 1000;
1009
6f481af2 1010 if (now.tv_sec > nextTimeout.tv_sec) {
1011 timeout.tv_sec = timeout.tv_usec = 0;
1012 } else if (now.tv_sec == nextTimeout.tv_sec) {
1013 timeout.tv_sec = 0;
1014 if (now.tv_usec > nextTimeout.tv_usec)
1015 timeout.tv_usec = 0;
1016 else
1017 timeout.tv_usec = nextTimeout.tv_usec - now.tv_usec;
1018 } else if (now.tv_sec < nextTimeout.tv_sec) {
1019 timeout.tv_sec = nextTimeout.tv_sec - now.tv_sec;
1020 if (now.tv_usec > nextTimeout.tv_usec)
1021 timeout.tv_sec--,
1022 timeout.tv_usec = nextTimeout.tv_usec + 1000000 -
1023 now.tv_usec;
1024 else
1025 timeout.tv_usec = nextTimeout.tv_usec - now.tv_usec;
1026 }
1027 } else {
1028 timeout.tv_sec = timeout.tv_usec = 0;
1029 }
1030
0593cb33
ML
1031 if (needResize) {
1032 needResize = 0;
1033 newtResizeScreen(1);
d861e991
ML
1034
1035 /* The application may want to handle the resize */
1036 for (i = 0; i < form->numHotKeys; i++) {
1037 if (form->hotKeys[i] == NEWT_KEY_RESIZE) {
1038 es->reason = NEWT_EXIT_HOTKEY;
1039 es->u.key = NEWT_KEY_RESIZE;
1040 done = 1;
1041 break;
1042 }
1043 }
1044 if (done)
1045 break;
0593cb33
ML
1046 }
1047
6f481af2 1048 i = select(max + 1, &readSet, &writeSet, &exceptSet,
1049 form->timer ? &timeout : NULL);
1050 if (i < 0) continue; /* ?? What should we do here? */
1051
1052 if (i == 0) {
1053 done = 1;
1054 es->reason = NEWT_EXIT_TIMER;
1055 gettimeofday(&form->lastTimeout, NULL);
1056 } else
33b9b6eb 1057#ifdef USE_GPM
6f481af2 1058 if (gpm_fd > 0 && FD_ISSET(gpm_fd, &readSet)) {
1059 Gpm_GetEvent(&event);
d02ffb49 1060
6f481af2 1061 if (event.type & GPM_DOWN) {
1062 /* Transform coordinates to current window */
1063 newtGetWindowPos(&x, &y);
c4827b34 1064
6f481af2 1065 ev.event = EV_MOUSE;
1066 ev.u.mouse.type = MOUSE_BUTTON_DOWN;
1067 ev.u.mouse.x = event.x - x - 1;
1068 ev.u.mouse.y = event.y - y - 1;
d02ffb49 1069
6f481af2 1070 /* Send the form the event */
1071 er = sendEvent(co, ev);
45f6c4fd 1072
6f481af2 1073 if (er.result == ER_EXITFORM) {
1074 done = 1;
1075 es->reason = NEWT_EXIT_COMPONENT;
1076 es->u.co = form->exitComp;
1077 }
45f6c4fd 1078
6f481af2 1079 }
1080 } else
45f6c4fd 1081#endif
6f481af2 1082 {
1083 if (FD_ISSET(0, &readSet)) {
1084
1085 key = newtGetKey();
1086
6f481af2 1087 for (i = 0; i < form->numHotKeys; i++) {
1088 if (form->hotKeys[i] == key) {
1089 es->reason = NEWT_EXIT_HOTKEY;
1090 es->u.key = key;
1091 done = 1;
1092 break;
1093 }
1094 }
1095
9fc6a6b0 1096 if (key == NEWT_KEY_F1 && form->helpTag && form->helpCb) {
1097 if (form->currComp != -1) {
1098 ev.event = EV_UNFOCUS;
1099 sendEvent(form->elements[form->currComp].co, ev);
1100 }
6f481af2 1101 form->helpCb(co, form->helpTag);
9fc6a6b0 1102 if (form->currComp != -1) {
1103 ev.event = EV_FOCUS;
1104 sendEvent(form->elements[form->currComp].co, ev);
1105 }
1106 }
6f481af2 1107
b8b8a86f
ML
1108 if (key == NEWT_KEY_ERROR) {
1109 es->u.watch = -1;
1110 es->reason = NEWT_EXIT_ERROR;
1111 done = 1;
1112 }
1113
6f481af2 1114 if (!done) {
1115 ev.event = EV_KEYPRESS;
1116 ev.u.key = key;
1117
1118 er = sendEvent(co, ev);
1119
1120 if (er.result == ER_EXITFORM) {
1121 done = 1;
1122 es->reason = NEWT_EXIT_COMPONENT;
1123 es->u.co = form->exitComp;
1124 }
1125 }
1126 } else {
1127 for (i = 0; i < form->numFds; i++) {
1128 if (((form->fds[i].flags & NEWT_FD_READ)
1129 && FD_ISSET(form->fds[i].fd, &readSet))
1130 || ((form->fds[i].flags & NEWT_FD_WRITE)
1131 && FD_ISSET(form->fds[i].fd, &writeSet))
1132 || ((form->fds[i].flags & NEWT_FD_EXCEPT)
1133 && FD_ISSET(form->fds[i].fd, &exceptSet))) break;
1134 }
1135 if(i < form->numFds)
1136 es->u.watch = form->fds[i].fd;
1137 else
1138 es->u.watch = -1;
1139
1140 es->reason = NEWT_EXIT_FDREADY;
1141 done = 1;
1142 }
1143 }
45f6c4fd 1144 }
c4827b34 1145 newtRefresh();
33b9b6eb 1146#ifdef USE_GPM
45f6c4fd 1147 Gpm_Close();
1148#endif
6fb96a3f 1149}
1150
434c766b 1151static struct eventResult sendEvent(newtComponent co, struct event ev) {
1152 struct eventResult er;
1153
1154 ev.when = EV_EARLY;
1155 er = co->ops->event(co, ev);
1156
1157 if (er.result == ER_IGNORED) {
6f481af2 1158 ev.when = EV_NORMAL;
1159 er = co->ops->event(co, ev);
434c766b 1160 }
1161
1162 if (er.result == ER_IGNORED) {
6f481af2 1163 ev.when = EV_LATE;
1164 er = co->ops->event(co, ev);
434c766b 1165 }
1166
1167 return er;
1168}
1169
ad2dca95
DW
1170static void gotoComponent(newtComponent co, int newComp) {
1171 struct form * form = co->data;
6fb96a3f 1172 struct event ev;
1173
1174 if (form->currComp != -1) {
6f481af2 1175 ev.event = EV_UNFOCUS;
1176 sendEvent(form->elements[form->currComp].co, ev);
6fb96a3f 1177 }
1178
1179 form->currComp = newComp;
45f6c4fd 1180
f5daa14e 1181 if (form->currComp != -1) {
6f481af2 1182 ev.event = EV_FOCUS;
1183 ev.when = EV_NORMAL;
1184 sendEvent(form->elements[form->currComp].co, ev);
f5daa14e 1185 }
ad2dca95
DW
1186
1187 if (co->callback)
1188 co->callback(co, co->callbackData);
6fb96a3f 1189}
da151ca8 1190
1191void newtComponentAddCallback(newtComponent co, newtCallback f, void * data) {
1192 co->callback = f;
1193 co->callbackData = data;
1194}
1195
c101e99e
RJ
1196/* Add a callback which is called when the component is destroyed. */
1197void newtComponentAddDestroyCallback(newtComponent co,
1198 newtCallback f, void * data) {
1199 co->destroyCallback = f;
1200 co->destroyCallbackData = data;
1201}
1202
41d86a3c 1203void newtComponentTakesFocus(newtComponent co, int val) {
1204 co->takesFocus = val;
1205}
1206
8f4e87fc 1207void newtFormSetBackground(newtComponent co, int color) {
1208 struct form * form = co->data;
1209
1210 form->background = color;
1211}
d02ffb49 1212
1213void newtFormWatchFd(newtComponent co, int fd, int fdFlags) {
1214 struct form * form = co->data;
005d8237 1215 int i;
1216
1217 for (i = 0; i < form->numFds; i++)
1218 if (form->fds[i].fd == fd)
6f481af2 1219 break;
005d8237 1220
1221 if(i >= form->numFds)
1222 form->fds = realloc(form->fds, (++form->numFds) * sizeof(*form->fds));
d02ffb49 1223
005d8237 1224 form->fds[i].fd = fd;
1225 form->fds[i].flags = fdFlags;
d02ffb49 1226 if (form->maxFd < fd) form->maxFd = fd;
1227}
72b71fa6 1228
1229void newtSetHelpCallback(newtCallback cb) {
1230 helpCallback = cb;
1231}