+#include "config.h"
+
+#include <sys/types.h>
+
+#include <slang.h>
#include <stdarg.h>
#include <stdlib.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#include <sys/time.h>
+
+#ifdef USE_GPM
+#include <ctype.h>
+#include <sys/time.h> /* timeval */
+#include <sys/socket.h> /* socket() */
+#include <sys/un.h> /* struct sockaddr_un */
+#include <sys/fcntl.h> /* O_RDONLY */
+#include <sys/stat.h> /* stat() */
+#include <termios.h> /* winsize */
+#include <unistd.h>
+#include <sys/kd.h> /* KDGETMODE */
+#include <signal.h>
+#include <stdio.h>
+#endif
#include "newt.h"
#include "newt_pr.h"
+#ifdef USE_GPM
+/*....................................... The connection data structure */
+
+typedef struct Gpm_Connect {
+ unsigned short eventMask, defaultMask;
+ unsigned short minMod, maxMod;
+ int pid;
+ int vc;
+} Gpm_Connect;
+
+/*....................................... Stack struct */
+typedef struct Gpm_Stst {
+ Gpm_Connect info;
+ struct Gpm_Stst *next;
+} Gpm_Stst;
+
+enum Gpm_Etype {
+ GPM_MOVE=1,
+ GPM_DRAG=2, /* exactly one of the bare ones is active at a time */
+ GPM_DOWN=4,
+ GPM_UP= 8,
+
+#define GPM_BARE_EVENTS(type) ((type)&(0x0f|GPM_ENTER|GPM_LEAVE))
+
+ GPM_SINGLE=16, /* at most one in three is set */
+ GPM_DOUBLE=32,
+ GPM_TRIPLE=64, /* WARNING: I depend on the values */
+
+ GPM_MFLAG=128, /* motion during click? */
+ GPM_HARD=256, /* if set in the defaultMask, force an already
+ used event to pass over to another handler */
+
+ GPM_ENTER=512, /* enter event, user in Roi's */
+ GPM_LEAVE=1024 /* leave event, used in Roi's */
+};
+
+/*....................................... The reported event */
+
+enum Gpm_Margin {GPM_TOP=1, GPM_BOT=2, GPM_LFT=4, GPM_RGT=8};
+
+typedef struct Gpm_Event {
+ unsigned char buttons, modifiers; /* try to be a multiple of 4 */
+ unsigned short vc;
+ short dx, dy, x, y;
+ enum Gpm_Etype type;
+ int clicks;
+ enum Gpm_Margin margin;
+} Gpm_Event;
+
+static int Gpm_Open(Gpm_Connect *conn, int flag);
+static int Gpm_Close(void);
+
+static int gpm_fd=-1;
+static int gpm_flag=0;
+static int gpm_tried=0;
+Gpm_Stst *gpm_stack=NULL;
+static char *gpm_sock_name=NULL;
+static struct sigaction gpm_saved_suspend_hook;
+static struct sigaction gpm_saved_winch_hook;
+
+#define GPM_XTERM_ON
+#define GPM_XTERM_OFF
+#define GPM_NODE_DEV "/dev/gpmctl"
+#define GPM_NODE_CTL GPM_NODE_DEV
+
+static inline int putdata(int where, Gpm_Connect *what)
+{
+ if (write(where,what,sizeof(Gpm_Connect))!=sizeof(Gpm_Connect))
+ {
+ return -1;
+ }
+ return 0;
+}
+
+static void gpm_winch_hook (int signum)
+{
+ if (SIG_IGN != gpm_saved_winch_hook.sa_handler &&
+ SIG_DFL != gpm_saved_winch_hook.sa_handler) {
+ gpm_saved_winch_hook.sa_handler(signum);
+ } /*if*/
+}
+
+static void gpm_suspend_hook (int signum)
+{
+ Gpm_Connect gpm_connect;
+ sigset_t old_sigset;
+ sigset_t new_sigset;
+ struct sigaction sa;
+ int success;
+
+ sigemptyset (&new_sigset);
+ sigaddset (&new_sigset, SIGTSTP);
+ sigprocmask (SIG_BLOCK, &new_sigset, &old_sigset);
+
+ /* Open a completely transparent gpm connection */
+ gpm_connect.eventMask = 0;
+ gpm_connect.defaultMask = ~0;
+ gpm_connect.minMod = ~0;
+ gpm_connect.maxMod = 0;
+ /* cannot do this under xterm, tough */
+ success = (Gpm_Open (&gpm_connect, 0) >= 0);
+
+ /* take the default action, whatever it is (probably a stop :) */
+ sigprocmask (SIG_SETMASK, &old_sigset, 0);
+ sigaction (SIGTSTP, &gpm_saved_suspend_hook, 0);
+ kill (getpid (), SIGTSTP);
+
+ /* in bardo here */
+
+ /* Reincarnation. Prepare for another death early. */
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = gpm_suspend_hook;
+ sa.sa_flags = SA_NOMASK;
+ sigaction (SIGTSTP, &sa, 0);
+
+ /* Pop the gpm stack by closing the useless connection */
+ /* but do it only when we know we opened one.. */
+ if (success) {
+ Gpm_Close ();
+ } /*if*/
+}
+
+static int Gpm_Open(Gpm_Connect *conn, int flag)
+{
+ char tty[32];
+ char *term;
+ int i;
+ struct sockaddr_un addr;
+ Gpm_Stst *new;
+
+ /*....................................... First of all, check xterm */
+
+ if ((term=(char *)getenv("TERM")) && !strncmp(term,"xterm",5))
+ {
+ if (gpm_tried) return gpm_fd; /* no stack */
+ gpm_fd=-2;
+ GPM_XTERM_ON;
+ gpm_flag=1;
+ return gpm_fd;
+ }
+ /*....................................... No xterm, go on */
+
+
+ /*
+ * So I chose to use the current tty, instead of /dev/console, which
+ * has permission problems. (I am fool, and my console is
+ * readable/writeable by everybody.
+ *
+ * However, making this piece of code work has been a real hassle.
+ */
+
+ if (!gpm_flag && gpm_tried) return -1;
+ gpm_tried=1; /* do or die */
+
+ new=malloc(sizeof(Gpm_Stst));
+ if (!new) return -1;
+
+ new->next=gpm_stack;
+ gpm_stack=new;
+
+ conn->pid=getpid(); /* fill obvious values */
+
+ if (new->next)
+ conn->vc=new->next->info.vc; /* inherit */
+ else
+ {
+ conn->vc=0; /* default handler */
+ if (flag>0)
+ { /* forced vc number */
+ conn->vc=flag;
+ sprintf(tty,"/dev/tty%i",flag);
+ }
+ else if (flag==0) /* use your current vc */
+ {
+ char *t = ttyname(0); /* stdin */
+ if (!t) t = ttyname(1); /* stdout */
+ if (!t) goto err;
+ strcpy(tty,t);
+ if (strncmp(tty,"/dev/tty",8) || !isdigit(tty[8]))
+ goto err;
+ conn->vc=atoi(tty+8);
+ }
+ else /* a default handler -- use console */
+ sprintf(tty,"/dev/tty0");
+
+ }
+
+ new->info=*conn;
+
+ /*....................................... Connect to the control socket */
+
+ if (!(gpm_flag++))
+ {
+
+ if ( (gpm_fd=socket(AF_UNIX,SOCK_STREAM,0))<0 )
+ {
+ goto err;
+ }
+
+ bzero((char *)&addr,sizeof(addr));
+ addr.sun_family=AF_UNIX;
+ if (!(gpm_sock_name = tempnam (0, "gpm"))) {
+ goto err;
+ } /*if*/
+ strncpy (addr.sun_path, gpm_sock_name, sizeof (addr.sun_path));
+ if (bind (gpm_fd, (struct sockaddr*)&addr,
+ sizeof (addr.sun_family) + strlen (addr.sun_path))==-1) {
+ goto err;
+ } /*if*/
+
+ bzero((char *)&addr,sizeof(addr));
+ addr.sun_family=AF_UNIX;
+ strcpy(addr.sun_path, GPM_NODE_CTL);
+ i=sizeof(addr.sun_family)+strlen(GPM_NODE_CTL);
+
+ if ( connect(gpm_fd,(struct sockaddr *)(&addr),i)<0 )
+ {
+ struct stat stbuf;
+
+ /*
+ * Well, try to open a chr device called /dev/gpmctl. This should
+ * be forward-compatible with a kernel server
+ */
+ close(gpm_fd); /* the socket */
+ if ((gpm_fd=open(GPM_NODE_DEV,O_RDWR))==-1) {
+ goto err;
+ } /*if*/
+ if (fstat(gpm_fd,&stbuf)==-1 || (stbuf.st_mode&S_IFMT)!=S_IFCHR)
+ goto err;
+ }
+ }
+ /*....................................... Put your data */
+
+ if (putdata(gpm_fd,conn)!=-1)
+ {
+ /* itz Wed Dec 16 23:22:16 PST 1998 use sigaction, the old
+ code caused a signal loop under XEmacs */
+ struct sigaction sa;
+ sigemptyset(&sa.sa_mask);
+
+#if (defined(SIGWINCH))
+ /* And the winch hook .. */
+ sa.sa_handler = gpm_winch_hook;
+ sa.sa_flags = 0;
+ sigaction(SIGWINCH, &sa, &gpm_saved_winch_hook);
+#endif
+
+#if (defined(SIGTSTP))
+ if (gpm_flag == 1) {
+ /* Install suspend hook */
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGTSTP, &sa, &gpm_saved_suspend_hook);
+
+ /* if signal was originally ignored, job control is not supported */
+ if (gpm_saved_suspend_hook.sa_handler != SIG_IGN) {
+ sa.sa_flags = SA_NOMASK;
+ sa.sa_handler = gpm_suspend_hook;
+ sigaction(SIGTSTP, &sa, 0);
+ } /*if*/
+ } /*if*/
+#endif
+
+ } /*if*/
+ return gpm_fd;
+
+ /*....................................... Error: free all memory */
+ err:
+ do
+ {
+ new=gpm_stack->next;
+ free(gpm_stack);
+ gpm_stack=new;
+ }
+ while(gpm_stack);
+ if (gpm_fd>=0) close(gpm_fd);
+ if (gpm_sock_name) {
+ unlink(gpm_sock_name);
+ free(gpm_sock_name);
+ gpm_sock_name = NULL;
+ } /*if*/
+ gpm_flag=0;
+ gpm_fd=-1;
+ return -1;
+}
+
+/*-------------------------------------------------------------------*/
+static int Gpm_Close(void)
+{
+ Gpm_Stst *next;
+
+ gpm_tried=0; /* reset the error flag for next time */
+ if (gpm_fd==-2) /* xterm */
+ GPM_XTERM_OFF;
+ else /* linux */
+ {
+ if (!gpm_flag) return 0;
+ next=gpm_stack->next;
+ free(gpm_stack);
+ gpm_stack=next;
+ if (next)
+ putdata(gpm_fd,&(next->info));
+
+ if (--gpm_flag) return -1;
+ }
+
+ if (gpm_fd>=0) close(gpm_fd);
+ gpm_fd=-1;
+ if (gpm_sock_name) {
+ unlink(gpm_sock_name);
+ free(gpm_sock_name);
+ gpm_sock_name = NULL;
+ }
+#ifdef SIGTSTP
+ sigaction(SIGTSTP, &gpm_saved_suspend_hook, 0);
+#endif
+#ifdef SIGWINCH
+ sigaction(SIGWINCH, &gpm_saved_winch_hook, 0);
+#endif
+ return 0;
+}
+
+/*-------------------------------------------------------------------*/
+static int Gpm_GetEvent(Gpm_Event *event)
+{
+ int count;
+
+ if (!gpm_flag) return 0;
+
+ if ((count=read(gpm_fd,event,sizeof(Gpm_Event)))!=sizeof(Gpm_Event))
+ {
+ if (count==0)
+ {
+ Gpm_Close();
+ return 0;
+ }
+ return -1;
+ }
+ return 1;
+}
+#endif
+
+/****************************************************************************
+ These forms handle vertical scrolling of components with a height of 1
+
+ Horizontal scrolling won't work, and scrolling large widgets will fail
+ miserably. It shouldn't be too hard to fix either of those if anyone
+ cares to. I only use scrolling for listboxes and text boxes though so
+ I didn't bother.
+*****************************************************************************/
+
+struct element {
+ newtComponent co;
+};
+
+struct fdInfo {
+ int fd;
+ int flags;
+};
+
struct form {
int numCompsAlloced;
- newtComponent * comps;
+ struct element * elements;
int numComps;
int currComp;
+ int fixedHeight;
+ int flags;
+ int vertOffset;
+ newtComponent vertBar, exitComp;
+ const char * help;
+ int numRows;
+ int * hotKeys;
+ int numHotKeys;
+ int background;
+ int numFds;
+ struct fdInfo * fds;
+ int maxFd;
+ int timer; /* in milliseconds */
+ struct timeval lastTimeout;
+ void * helpTag;
+ newtCallback helpCb;
};
-static void formDraw(newtComponent co);
-static void gotoComponent(struct form * form, int newComp);
+static void gotoComponent(newtComponent co, int newComp);
static struct eventResult formEvent(newtComponent co, struct event ev);
+static struct eventResult sendEvent(newtComponent comp, struct event ev);
+static void formPlace(newtComponent co, int left, int top);
-static struct componentOps formOps = {
- formDraw,
+/* Global, ick */
+static newtCallback helpCallback;
+
+/* this isn't static as grid.c tests against it to find forms */
+struct componentOps formOps = {
+ newtDrawForm,
formEvent,
newtFormDestroy,
+ formPlace,
+ newtDefaultMappedHandler,
} ;
-newtComponent newtForm(void) {
+int needResize = 0;
+
+static inline int componentFits(newtComponent co, int compNum) {
+ struct form * form = co->data;
+ struct element * el = form->elements + compNum;
+
+ if (co->top > el->co->top)
+ return 0;
+ if (co->top + co->height < el->co->top + el->co->height)
+ return 0;
+
+ return 1;
+}
+
+newtComponent newtForm(newtComponent vertBar, void * help, int flags) {
newtComponent co;
struct form * form;
co->height = 0;
co->top = -1;
co->left = -1;
+ co->isMapped = 0;
- co->takesFocus = 0;
+ co->takesFocus = 0; /* we may have 0 components */
co->ops = &formOps;
+ co->callback = NULL;
+ co->destroyCallback = NULL;
- form = malloc(sizeof(*form));
+ form->help = help;
+ form->flags = flags;
form->numCompsAlloced = 5;
form->numComps = 0;
form->currComp = -1;
- form->comps = malloc(sizeof(*(form->comps)) * form->numCompsAlloced);
+ form->vertOffset = 0;
+ form->fixedHeight = 0;
+ form->numRows = 0;
+ form->numFds = 0;
+ form->maxFd = 0;
+ form->fds = NULL;
+ form->elements = malloc(sizeof(*(form->elements)) * form->numCompsAlloced);
+
+ form->background = COLORSET_WINDOW;
+ form->hotKeys = malloc(sizeof(int));
+ form->numHotKeys = 0;
+ form->timer = 0;
+ form->lastTimeout.tv_sec = form->lastTimeout.tv_usec = 0;
+ if (!(form->flags & NEWT_FLAG_NOF12)) {
+ newtFormAddHotKey(co, NEWT_KEY_F12);
+ }
+
+ if (vertBar)
+ form->vertBar = vertBar;
+ else
+ form->vertBar = NULL;
+
+ form->helpTag = help;
+ form->helpCb = helpCallback;
return co;
}
-void newtFormAddComponent(newtComponent co, newtComponent newco) {
+newtComponent newtFormGetCurrent(newtComponent co) {
struct form * form = co->data;
- int delta;
- if (form->numCompsAlloced == form->numComps) {
- form->numCompsAlloced += 5;
- form->comps = realloc(form->comps,
- sizeof(*(form->comps)) * form->numCompsAlloced);
+ if (form->currComp == -1) return 0;
+ return form->elements[form->currComp].co;
+}
+
+static void formScroll(newtComponent co, int delta) {
+ struct form * form = co->data;
+ struct element * el;
+ int i, newVertOffset = form->vertOffset + delta;
+
+ if (newVertOffset < 0)
+ newVertOffset = 0;
+ if (newVertOffset > form->numRows - co->height)
+ newVertOffset = form->numRows - co->height;
+
+ delta = newVertOffset - form->vertOffset;
+ form->vertOffset = newVertOffset;
+
+ for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
+ if (el->co == form->vertBar)
+ continue;
+ el->co->ops->place(el->co, el->co->left, el->co->top - delta);
}
+}
- form->comps[form->numComps++] = newco;
+int newtFormGetScrollPosition(newtComponent co) {
+ struct form * form = co->data;
- if (co->left == -1) {
- co->left = newco->left;
- co->top = newco->top;
- co->width = newco->width;
- co->height = newco->height;
- } else {
- if (co->left > newco->left) {
- delta = co->left - newco->left;
- co->left -= delta;
- co->width += delta;
- }
+ return form->vertOffset;
+}
- if (co->top > newco->top) {
- delta = co->top - newco->top;
- co->top -= delta;
- co->height += delta;
- }
+void newtFormSetScrollPosition(newtComponent co, int position) {
+ struct form * form = co->data;
+
+ if (form->numRows == 0)
+ newtFormSetSize(co);
+ formScroll(co, position - form->vertOffset);
+}
+
+void newtFormSetCurrent(newtComponent co, newtComponent subco) {
+ struct form * form = co->data;
+ int i, new;
+
+ for (i = 0; i < form->numComps; i++) {
+ if (form->elements[i].co == subco) break;
+ }
- if ((co->left + co->width) < (newco->left + newco->width))
- co->left = (newco->left + newco->width) - co->width;
+ if (form->elements[i].co != subco) return;
+ new = i;
- if ((co->top + co->height) < (newco->top + newco->height))
- co->top = (newco->top + newco->height) - co->height;
+ if (co->isMapped && !componentFits(co, new)) {
+ gotoComponent(co, -1);
+ formScroll(co, form->elements[new].co->top - co->top - 1);
}
+
+ gotoComponent(co, new);
+}
+
+void newtFormSetTimer(newtComponent co, int millisecs) {
+ struct form * form = co->data;
+
+ form->timer = millisecs;
+ form->lastTimeout.tv_usec = 0;
+ form->lastTimeout.tv_sec = 0;
+}
+
+void newtFormSetHeight(newtComponent co, int height) {
+ struct form * form = co->data;
+
+ form->fixedHeight = 1;
+ co->height = height;
+}
+
+void newtFormSetWidth(newtComponent co, int width) {
+ co->width = width;
+}
+
+void newtFormAddComponent(newtComponent co, newtComponent newco) {
+ struct form * form = co->data;
+
+ co->takesFocus = 1;
+
+ if (form->numCompsAlloced == form->numComps) {
+ form->numCompsAlloced += 5;
+ form->elements = realloc(form->elements,
+ sizeof(*(form->elements)) * form->numCompsAlloced);
+ }
+
+ form->elements[form->numComps].co = newco;
+
+ if (newco->takesFocus && form->currComp == -1)
+ form->currComp = form->numComps;
+
+ form->numComps++;
}
void newtFormAddComponents(newtComponent co, ...) {
while ((subco = va_arg(ap, newtComponent)))
newtFormAddComponent(co, subco);
-
+
va_end(ap);
}
-static void formDraw(newtComponent co) {
+static void formPlace(newtComponent co, int left, int top) {
struct form * form = co->data;
- newtComponent subco;
+ int vertDelta, horizDelta;
+ struct element * el;
int i;
- for (i = 0; i < form->numComps; i++) {
- subco = form->comps[i];
- subco->ops->draw(subco);
+ vertDelta = top - co->top;
+ horizDelta = left - co->left;
+ co->top = top;
+ co->left = left;
+
+ for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
+ el->co->ops->place(el->co, el->co->left + horizDelta,
+ el->co->top + vertDelta);
}
}
+void newtDrawForm(newtComponent co) {
+ struct form * form = co->data;
+ struct element * el;
+ int i;
+
+ newtFormSetSize(co);
+
+ SLsmg_set_color(form->background);
+ newtClearBox(co->left, co->top, co->width, co->height);
+
+ for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
+ /* only draw it if it'll fit on the screen vertically
+ (the scrollbar *always* fits somewhere) */
+ if (el->co == form->vertBar || componentFits(co, i)) {
+ el->co->ops->mapped(el->co, 1);
+ el->co->ops->draw(el->co);
+ } else {
+ el->co->ops->mapped(el->co, 0);
+ }
+ }
+
+ if (form->vertBar)
+ newtScrollbarSet(form->vertBar, form->vertOffset,
+ form->numRows - co->height);
+}
+
static struct eventResult formEvent(newtComponent co, struct event ev) {
struct form * form = co->data;
- newtComponent subco = form->comps[form->currComp];
- int new;
+ newtComponent subco = form->elements[form->currComp].co;
+ int new, wrap = 0;
struct eventResult er;
+ int dir = 0, page = 0;
+ int i, num, found;
+ struct element * el;
er.result = ER_IGNORED;
+ if (!form->numComps) return er;
+
+ if (form->currComp == -1) return er;
+
+ switch (ev.when) {
+ case EV_EARLY:
+ if (ev.event == EV_KEYPRESS) {
+ if (ev.u.key == NEWT_KEY_TAB) {
+ er.result = ER_SWALLOWED;
+ dir = 1;
+ wrap = 1;
+ } else if (ev.u.key == NEWT_KEY_UNTAB) {
+ er.result = ER_SWALLOWED;
+ dir = -1;
+ wrap = 1;
+ }
+ }
+
+ if (form->numComps) {
+ i = form->currComp;
+ num = 0;
+ while (er.result == ER_IGNORED && num != form->numComps ) {
+ er = form->elements[i].co->ops->event(form->elements[i].co, ev);
+
+ num++;
+ i++;
+ if (i == form->numComps) i = 0;
+ }
+ }
- switch (ev.event) {
- case EV_FOCUS:
- case EV_UNFOCUS:
- er = subco->ops->event(subco, ev);
break;
-
- case EV_KEYPRESS:
- if (ev.u.key == NEWT_KEY_TAB) {
- er.result = ER_NEXTCOMP;
- }
- if (er.result == ER_IGNORED) {
- /* let the current component handle the event */
- er = subco->ops->event(subco, ev);
+ case EV_NORMAL:
+ if (ev.event == EV_MOUSE) {
+ found = 0;
+ for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
+ if ((el->co->top <= ev.u.mouse.y) &&
+ (el->co->top + el->co->height > ev.u.mouse.y) &&
+ (el->co->left <= ev.u.mouse.x) &&
+ (el->co->left + el->co->width > ev.u.mouse.x)) {
+ found = 1;
+ if (el->co->takesFocus) {
+ gotoComponent(co, i);
+ subco = form->elements[form->currComp].co;
+ }
+ }
+ /* If we did not find a co to send this event to, we
+ should just swallow the event here. */
+ }
+ if (!found) {
+ er.result = ER_SWALLOWED;
+
+ return er;
+ }
+ }
+ er = subco->ops->event(subco, ev);
+ switch (er.result) {
+ case ER_NEXTCOMP:
+ er.result = ER_SWALLOWED;
+ dir = 1;
+ break;
+
+ case ER_EXITFORM:
+ form->exitComp = subco;
+ break;
+
+ default:
+ break;
}
+ break;
+
+ case EV_LATE:
+ er = subco->ops->event(subco, ev);
if (er.result == ER_IGNORED) {
- /* handle default events */
- if (er.result == ER_IGNORED) {
- switch (ev.u.key) {
- case NEWT_KEY_UP:
- case NEWT_KEY_LEFT:
- case NEWT_KEY_BKSPC:
- er.result = ER_PREVCOMP;
- break;
+ switch (ev.u.key) {
+ case NEWT_KEY_UP:
+ case NEWT_KEY_LEFT:
+ case NEWT_KEY_BKSPC:
+ er.result = ER_SWALLOWED;
+ dir = -1;
+ break;
- case NEWT_KEY_DOWN:
- case NEWT_KEY_RIGHT:
- case NEWT_KEY_ENTER:
- er.result = ER_NEXTCOMP;
- break;
- }
+ case NEWT_KEY_DOWN:
+ case NEWT_KEY_RIGHT:
+ er.result = ER_SWALLOWED;
+ dir = 1;
+ break;
+
+ case NEWT_KEY_PGUP:
+ er.result = ER_SWALLOWED;
+ dir = -1;
+ page = 1;
+ break;
+
+ case NEWT_KEY_PGDN:
+ er.result = ER_SWALLOWED;
+ dir = 1;
+ page = 1;
+ break;
}
}
}
- /* we try and do previous/next actions ourselves if possible */
- if (er.result == ER_PREVCOMP) {
- if (form->currComp > 0) {
- new = form->currComp - 1;
- gotoComponent(form, new);
- er.result = ER_SWALLOWED;
- }
- } else if (er.result == ER_NEXTCOMP) {
- new = form->currComp + 1;
- if (new < form->numComps) {
- gotoComponent(form, new);
- er.result = ER_SWALLOWED;
+ if (dir) {
+ new = form->currComp;
+
+ if (page) {
+ new += dir * co->height;
+ if (new < 0)
+ new = 0;
+ else if (new >= form->numComps)
+ new = (form->numComps - 1);
+
+ while (!form->elements[new].co->takesFocus &&
+ new - dir >= 0 && new - dir < form->numComps)
+ new -= dir;
+ } else {
+ do {
+ new += dir;
+
+ if (wrap) {
+ if (new < 0)
+ new = form->numComps - 1;
+ else if (new >= form->numComps)
+ new = 0;
+ if (new == form->currComp)
+ /* back where we started */
+ return er;
+ } else if (new < 0 || new >= form->numComps)
+ return er;
+ } while (!form->elements[new].co->takesFocus);
}
+
+ /* make sure this component is visible */
+ if (!componentFits(co, new)) {
+ int vertDelta;
+
+ gotoComponent(co, -1);
+
+ if (dir < 0) {
+ /* make the new component the first one */
+ vertDelta = form->elements[new].co->top - co->top;
+ } else {
+ /* make the new component the last one */
+ vertDelta = (form->elements[new].co->top +
+ form->elements[new].co->height) -
+ (co->top + co->height);
+ }
+
+ formScroll(co, vertDelta);
+ newtDrawForm(co);
+ }
+
+ gotoComponent(co, new);
+ er.result = ER_SWALLOWED;
}
return er;
}
+/* Destroy a component. Components which have been added to a form
+ * are destroyed when the form is destroyed; this is just for the
+ * (rare) case of components which for whatever reason weren't added
+ * to a form.
+ */
+void newtComponentDestroy(newtComponent co) {
+ /* If the user registered a destroy callback for this component,
+ * now is a good time to call it.
+ */
+ if (co->destroyCallback)
+ co->destroyCallback(co, co->destroyCallbackData);
+
+ if (co->ops->destroy) {
+ co->ops->destroy(co);
+ } else {
+ if (co->data) free(co->data);
+ free(co);
+ }
+}
+
/* this also destroys all of the components on the form */
void newtFormDestroy(newtComponent co) {
- int i;
newtComponent subco;
struct form * form = co->data;
+ int i;
/* first, destroy all of the components */
for (i = 0; i < form->numComps; i++) {
- subco = form->comps[i];
- if (subco->ops->destroy) {
- subco->ops->destroy(subco);
- } else {
- if (subco->data) free(subco->data);
- free(subco);
- }
+ subco = form->elements[i].co;
+ newtComponentDestroy(subco);
}
- free(form->comps);
+ if (form->hotKeys) free(form->hotKeys);
+
+ free(form->elements);
free(form);
free(co);
}
newtComponent newtRunForm(newtComponent co) {
+ struct newtExitStruct es;
+
+ newtFormRun(co, &es);
+ if (es.reason == NEWT_EXIT_HOTKEY) {
+ if (es.u.key == NEWT_KEY_F12) {
+ es.reason = NEWT_EXIT_COMPONENT;
+ es.u.co = co;
+ } else {
+ return NULL;
+ }
+ } else if (es.reason == NEWT_EXIT_ERROR)
+ return NULL;
+
+ return es.u.co;
+}
+
+void newtFormAddHotKey(newtComponent co, int key) {
+ struct form * form = co->data;
+
+ form->numHotKeys++;
+ form->hotKeys = realloc(form->hotKeys, sizeof(int) * form->numHotKeys);
+ form->hotKeys[form->numHotKeys - 1] = key;
+}
+
+void newtFormSetSize(newtComponent co) {
+ struct form * form = co->data;
+ int delta, i, first;
+ struct element * el;
+
+ form->numRows = 0;
+
+ co->width = 0;
+ if (!form->fixedHeight) co->height = 0;
+
+ co->top = -1;
+ co->left = -1;
+ first = 1;
+
+ for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
+ if (el->co->ops == &formOps)
+ newtFormSetSize(el->co);
+ else if (el->co == form->vertBar)
+ continue;
+
+ if (first) {
+ co->top = el->co->top;
+ co->left = el->co->left;
+ first = 0;
+ }
+
+ if (co->left > el->co->left) {
+ delta = co->left - el->co->left;
+ co->left -= delta;
+ co->width += delta;
+ }
+
+ if (co->top > el->co->top) {
+ delta = co->top - el->co->top;
+ co->top -= delta;
+ form->numRows += delta;
+ if (!form->fixedHeight)
+ co->height += delta;
+ }
+
+ if ((co->left + co->width) < (el->co->left + el->co->width))
+ co->width = (el->co->left + el->co->width) - co->left;
+
+ if (!form->fixedHeight) {
+ if ((co->top + co->height) < (el->co->top + el->co->height))
+ co->height = (el->co->top + el->co->height) - co->top;
+ }
+
+ if ((el->co->top + el->co->height - co->top) > form->numRows) {
+ form->numRows = el->co->top + el->co->height - co->top;
+ }
+ }
+
+ co->top += form->vertOffset;
+}
+
+void newtFormRun(newtComponent co, struct newtExitStruct * es) {
struct form * form = co->data;
struct event ev;
struct eventResult er;
- int key;
+ int key, i, max;
+ int done = 0;
+ fd_set readSet, writeSet, exceptSet;
+ struct timeval nextTimeout, now, timeout;
+#ifdef USE_GPM
+ int x, y;
+ Gpm_Connect conn;
+ Gpm_Event event;
- /* first, draw all of the components */
- formDraw(co);
+ /* Set up GPM interface */
+ conn.eventMask = ~GPM_MOVE;
+ conn.defaultMask = GPM_MOVE;
+ conn.minMod = 0;
+ conn.maxMod = 0;
- if (form->currComp == -1)
- gotoComponent(form, 0);
- else
- gotoComponent(form, form->currComp);
-
- do {
+ Gpm_Open(&conn, 0);
+#endif
+
+ /* draw all of the components */
+ newtDrawForm(co);
+
+ if (form->currComp == -1) {
+ if (form->numComps)
+ gotoComponent(co, 0);
+ } else
+ gotoComponent(co, form->currComp);
+
+ while (!done) {
newtRefresh();
- key = newtGetKey();
- ev.event = EV_KEYPRESS;
- ev.u.key = key;
- er = formEvent(co, ev);
+ FD_ZERO(&readSet);
+ FD_ZERO(&writeSet);
+ FD_ZERO(&exceptSet);
+ FD_SET(0, &readSet);
+#ifdef USE_GPM
+ if (gpm_fd > 0) {
+ FD_SET(gpm_fd, &readSet);
+ }
+ max = form->maxFd > gpm_fd ? form->maxFd : gpm_fd;
+#else
+ max = form->maxFd;
+#endif
+
+ for (i = 0; i < form->numFds; i++) {
+ if (form->fds[i].flags & NEWT_FD_READ)
+ FD_SET(form->fds[i].fd, &readSet);
+ if (form->fds[i].flags & NEWT_FD_WRITE)
+ FD_SET(form->fds[i].fd, &writeSet);
+ if (form->fds[i].flags & NEWT_FD_EXCEPT)
+ FD_SET(form->fds[i].fd, &exceptSet);
+ }
+
+ if (form->timer) {
+ /* Calculate when we next need to return with a timeout. Do
+ this inside the loop in case a callback resets the timer. */
+ gettimeofday(&now, 0);
+
+ if ((!form->lastTimeout.tv_sec && !form->lastTimeout.tv_usec) ||
+ now.tv_sec < form->lastTimeout.tv_sec ||
+ (now.tv_sec == form->lastTimeout.tv_sec &&
+ now.tv_usec < form->lastTimeout.tv_usec))
+ form->lastTimeout = now;
+
+ nextTimeout.tv_sec = form->lastTimeout.tv_sec +
+ (form->timer / 1000);
+ nextTimeout.tv_usec = form->lastTimeout.tv_usec +
+ (form->timer % 1000) * 1000;
+
+ if (now.tv_sec > nextTimeout.tv_sec) {
+ timeout.tv_sec = timeout.tv_usec = 0;
+ } else if (now.tv_sec == nextTimeout.tv_sec) {
+ timeout.tv_sec = 0;
+ if (now.tv_usec > nextTimeout.tv_usec)
+ timeout.tv_usec = 0;
+ else
+ timeout.tv_usec = nextTimeout.tv_usec - now.tv_usec;
+ } else if (now.tv_sec < nextTimeout.tv_sec) {
+ timeout.tv_sec = nextTimeout.tv_sec - now.tv_sec;
+ if (now.tv_usec > nextTimeout.tv_usec)
+ timeout.tv_sec--,
+ timeout.tv_usec = nextTimeout.tv_usec + 1000000 -
+ now.tv_usec;
+ else
+ timeout.tv_usec = nextTimeout.tv_usec - now.tv_usec;
+ }
+ } else {
+ timeout.tv_sec = timeout.tv_usec = 0;
+ }
+
+ if (needResize) {
+ needResize = 0;
+ newtResizeScreen(1);
+
+ /* The application may want to handle the resize */
+ for (i = 0; i < form->numHotKeys; i++) {
+ if (form->hotKeys[i] == NEWT_KEY_RESIZE) {
+ es->reason = NEWT_EXIT_HOTKEY;
+ es->u.key = NEWT_KEY_RESIZE;
+ done = 1;
+ break;
+ }
+ }
+ if (done)
+ break;
+ }
+
+ i = select(max + 1, &readSet, &writeSet, &exceptSet,
+ form->timer ? &timeout : NULL);
+ if (i < 0) continue; /* ?? What should we do here? */
+
+ if (i == 0) {
+ done = 1;
+ es->reason = NEWT_EXIT_TIMER;
+ gettimeofday(&form->lastTimeout, NULL);
+ } else
+#ifdef USE_GPM
+ if (gpm_fd > 0 && FD_ISSET(gpm_fd, &readSet)) {
+ Gpm_GetEvent(&event);
+
+ if (event.type & GPM_DOWN) {
+ /* Transform coordinates to current window */
+ newtGetWindowPos(&x, &y);
+
+ ev.event = EV_MOUSE;
+ ev.u.mouse.type = MOUSE_BUTTON_DOWN;
+ ev.u.mouse.x = event.x - x - 1;
+ ev.u.mouse.y = event.y - y - 1;
+
+ /* Send the form the event */
+ er = sendEvent(co, ev);
+
+ if (er.result == ER_EXITFORM) {
+ done = 1;
+ es->reason = NEWT_EXIT_COMPONENT;
+ es->u.co = form->exitComp;
+ }
+
+ }
+ } else
+#endif
+ {
+ if (FD_ISSET(0, &readSet)) {
+
+ key = newtGetKey();
+
+ for (i = 0; i < form->numHotKeys; i++) {
+ if (form->hotKeys[i] == key) {
+ es->reason = NEWT_EXIT_HOTKEY;
+ es->u.key = key;
+ done = 1;
+ break;
+ }
+ }
+
+ if (key == NEWT_KEY_F1 && form->helpTag && form->helpCb) {
+ if (form->currComp != -1) {
+ ev.event = EV_UNFOCUS;
+ sendEvent(form->elements[form->currComp].co, ev);
+ }
+ form->helpCb(co, form->helpTag);
+ if (form->currComp != -1) {
+ ev.event = EV_FOCUS;
+ sendEvent(form->elements[form->currComp].co, ev);
+ }
+ }
+
+ if (key == NEWT_KEY_ERROR) {
+ es->u.watch = -1;
+ es->reason = NEWT_EXIT_ERROR;
+ done = 1;
+ }
+
+ if (!done) {
+ ev.event = EV_KEYPRESS;
+ ev.u.key = key;
+
+ er = sendEvent(co, ev);
- /* EV_NEXTCOMP and EV_PREVCOMP should cause wrapping */
- } while (er.result != ER_EXITFORM);
+ if (er.result == ER_EXITFORM) {
+ done = 1;
+ es->reason = NEWT_EXIT_COMPONENT;
+ es->u.co = form->exitComp;
+ }
+ }
+ } else {
+ for (i = 0; i < form->numFds; i++) {
+ if (((form->fds[i].flags & NEWT_FD_READ)
+ && FD_ISSET(form->fds[i].fd, &readSet))
+ || ((form->fds[i].flags & NEWT_FD_WRITE)
+ && FD_ISSET(form->fds[i].fd, &writeSet))
+ || ((form->fds[i].flags & NEWT_FD_EXCEPT)
+ && FD_ISSET(form->fds[i].fd, &exceptSet))) break;
+ }
+ if(i < form->numFds)
+ es->u.watch = form->fds[i].fd;
+ else
+ es->u.watch = -1;
+ es->reason = NEWT_EXIT_FDREADY;
+ done = 1;
+ }
+ }
+ }
newtRefresh();
+#ifdef USE_GPM
+ Gpm_Close();
+#endif
+}
+
+static struct eventResult sendEvent(newtComponent co, struct event ev) {
+ struct eventResult er;
+
+ ev.when = EV_EARLY;
+ er = co->ops->event(co, ev);
+
+ if (er.result == ER_IGNORED) {
+ ev.when = EV_NORMAL;
+ er = co->ops->event(co, ev);
+ }
- return form->comps[form->currComp];
+ if (er.result == ER_IGNORED) {
+ ev.when = EV_LATE;
+ er = co->ops->event(co, ev);
+ }
+ return er;
}
-static void gotoComponent(struct form * form, int newComp) {
- newtComponent co;
+static void gotoComponent(newtComponent co, int newComp) {
+ struct form * form = co->data;
struct event ev;
if (form->currComp != -1) {
ev.event = EV_UNFOCUS;
- co = form->comps[form->currComp];
- co->ops->event(co, ev);
+ sendEvent(form->elements[form->currComp].co, ev);
}
form->currComp = newComp;
-
- ev.event = EV_FOCUS;
- co = form->comps[form->currComp];
- co->ops->event(co, ev);
+
+ if (form->currComp != -1) {
+ ev.event = EV_FOCUS;
+ ev.when = EV_NORMAL;
+ sendEvent(form->elements[form->currComp].co, ev);
+ }
+
+ if (co->callback)
+ co->callback(co, co->callbackData);
+}
+
+void newtComponentAddCallback(newtComponent co, newtCallback f, void * data) {
+ co->callback = f;
+ co->callbackData = data;
+}
+
+/* Add a callback which is called when the component is destroyed. */
+void newtComponentAddDestroyCallback(newtComponent co,
+ newtCallback f, void * data) {
+ co->destroyCallback = f;
+ co->destroyCallbackData = data;
+}
+
+void newtComponentTakesFocus(newtComponent co, int val) {
+ co->takesFocus = val;
+}
+
+void newtFormSetBackground(newtComponent co, int color) {
+ struct form * form = co->data;
+
+ form->background = color;
+}
+
+void newtFormWatchFd(newtComponent co, int fd, int fdFlags) {
+ struct form * form = co->data;
+ int i;
+
+ for (i = 0; i < form->numFds; i++)
+ if (form->fds[i].fd == fd)
+ break;
+
+ if(i >= form->numFds)
+ form->fds = realloc(form->fds, (++form->numFds) * sizeof(*form->fds));
+
+ form->fds[i].fd = fd;
+ form->fds[i].flags = fdFlags;
+ if (form->maxFd < fd) form->maxFd = fd;
+}
+
+void newtSetHelpCallback(newtCallback cb) {
+ helpCallback = cb;
}