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