#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>
-#include <string.h>
#ifdef USE_GPM
#include <ctype.h>
#include <sys/time.h> /* timeval */
-#include <sys/types.h> /* socket() */
#include <sys/socket.h> /* socket() */
#include <sys/un.h> /* struct sockaddr_un */
#include <sys/fcntl.h> /* O_RDONLY */
#include "newt.h"
#include "newt_pr.h"
-#include "eawidth.h"
-
-struct label {
- char *text;
- int length;
-};
#ifdef USE_GPM
/*....................................... The connection data structure */
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;
int i;
struct sockaddr_un addr;
Gpm_Stst *new;
- char* sock_name = 0;
/*....................................... First of all, check xterm */
bzero((char *)&addr,sizeof(addr));
addr.sun_family=AF_UNIX;
- if (!(sock_name = tempnam (0, "gpm"))) {
+ if (!(gpm_sock_name = tempnam (0, "gpm"))) {
goto err;
} /*if*/
- strncpy (addr.sun_path, sock_name, sizeof (addr.sun_path));
+ 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;
}
while(gpm_stack);
if (gpm_fd>=0) close(gpm_fd);
- if (sock_name) {
- unlink(sock_name);
- free(sock_name);
- sock_name = 0;
+ 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;
}
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
*****************************************************************************/
struct element {
- int top, left; /* Actual, not virtual. These are translated */
- newtComponent co; /* into actual through vertOffset */
+ newtComponent co;
};
struct fdInfo {
int * hotKeys;
int numHotKeys;
int background;
- int beenSet;
int numFds;
struct fdInfo * fds;
int maxFd;
newtCallback helpCb;
};
-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);
newtDefaultMappedHandler,
} ;
+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 + form->vertOffset) > el->top) return 0;
- if ((co->top + form->vertOffset + co->height) <
- (el->top + el->co->height)) return 0;
+ if (co->top > el->co->top)
+ return 0;
+ if (co->top + co->height < el->co->top + el->co->height)
+ return 0;
return 1;
}
co->top = -1;
co->left = -1;
co->isMapped = 0;
- co->isLabel = 0;
co->takesFocus = 0; /* we may have 0 components */
co->ops = &formOps;
+ co->callback = NULL;
+ co->destroyCallback = NULL;
form->help = help;
form->flags = flags;
form->numFds = 0;
form->maxFd = 0;
form->fds = NULL;
- form->beenSet = 0;
form->elements = malloc(sizeof(*(form->elements)) * form->numCompsAlloced);
form->background = COLORSET_WINDOW;
newtComponent newtFormGetCurrent(newtComponent co) {
struct form * form = co->data;
+ 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);
+ }
+}
+
+int newtFormGetScrollPosition(newtComponent co) {
+ struct form * form = co->data;
+
+ return form->vertOffset;
+}
+
+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;
new = i;
if (co->isMapped && !componentFits(co, new)) {
- gotoComponent(form, -1);
- form->vertOffset = form->elements[new].top - co->top - 1;
- if (form->vertOffset > (form->numRows - co->height))
- form->vertOffset = form->numRows - co->height;
+ gotoComponent(co, -1);
+ formScroll(co, form->elements[new].co->top - co->top - 1);
}
- gotoComponent(form, new);
+ gotoComponent(co, new);
}
void newtFormSetTimer(newtComponent co, int millisecs) {
void newtFormAddComponent(newtComponent co, newtComponent newco) {
struct form * form = co->data;
- int i, j, k, l, xmap, ymap, x, y;
- char *map, *s = NULL;
co->takesFocus = 1;
sizeof(*(form->elements)) * form->numCompsAlloced);
}
- /* we grab real values for these a bit later */
- form->elements[form->numComps].left = -2;
- form->elements[form->numComps].top = -2;
form->elements[form->numComps].co = newco;
if (newco->takesFocus && form->currComp == -1)
form->currComp = form->numComps;
form->numComps++;
-
- /* check multibyte char broken */
- /* create text map */
- xmap = 0;
- ymap = 0;
- for ( i = 0; i < form->numComps; i++ ) {
- if ((form->elements[i].co->top + form->elements[i].co->height) > ymap )
- ymap = form->elements[i].co->top + form->elements[i].co->height;
- if ((form->elements[i].co->left + form->elements[i].co->width) > xmap )
- xmap = form->elements[i].co->left + form->elements[i].co->width;
- }
- map = (char *)calloc ((xmap+1) * (ymap+1), sizeof (char));
-
-#define MAP(x,y) *(map + ((x)*ymap + (y)))
-
- /* create non-label components map */
- for ( i = 0; i < form->numComps; i++ ) {
- if ( form->elements[i].co->isLabel == 0 && form->elements[i].co->left >= 0 && form->elements[i].co->top >= 0 ) {
- for ( x = 0; x < form->elements[i].co->width; x++ ) {
- for ( y = 0; y < form->elements[i].co->height; y++ )
- MAP (form->elements[i].co->left+x, form->elements[i].co->top+y) = 1;
- }
- }
- }
-
- /* check label overlap */
- for ( i = 0; i < form->numComps; i++ ) {
- if ( form->elements[i].co->isLabel != 0 ) {
- y = form->elements[i].co->top;
- l = form->elements[i].co->left;
- if ( y >= 0 && l >= 0 ) {
- for ( j = 0; j < form->elements[i].co->width; j++ ) {
- if ( MAP (j+l, y) != 0 ) {
- if ( MAP (j+l, y) != 1 ) {
- /* if label is here, move label */
- while ( MAP (form->elements[i].co->left, y) != 0 )
- form->elements[i].co->left++;
- } else {
- /* if label is overlapping, cut label */
- if ((j+l) == form->elements[i].co->left ) {
- free (((struct label *)form->elements[i].co->data)->text);
- ((struct label *)form->elements[i].co->data)->text = malloc (1);
- ((struct label *)form->elements[i].co->data)->text[0] = 0;
- ((struct label *)form->elements[i].co->data)->length = 0;
- form->elements[i].co->width = 0;
- } else {
- x = 0;
- while (x < j) {
- s = &((struct label *)form->elements[i].co->data)->text[x];
- k = east_asia_mblen (NULL, s, strlen (s), 0);
- if ( k <= 0 ) break;
- else {
- if ((x+k) < j ) x += k;
- else break;
- }
- }
- s = ((struct label *)form->elements[i].co->data)->text;
- ((struct label *)form->elements[i].co->data)->text = realloc(s, x+1);
- ((struct label *)form->elements[i].co->data)->text[x] = 0;
- ((struct label *)form->elements[i].co->data)->length = strlen (s);
- form->elements[i].co->width = get_east_asia_str_width (NULL, s, 0);
- }
- }
- } else
- MAP (j+l, y) = 2;
- }
- }
- }
- }
- free (map);
}
void newtFormAddComponents(newtComponent co, ...) {
struct element * el;
int i;
- newtFormSetSize(co);
-
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->top += vertDelta;
- el->top += vertDelta;
- el->co->left += horizDelta;
- el->left += horizDelta;
+ el->co->ops->place(el->co, el->co->left + horizDelta,
+ el->co->top + vertDelta);
}
}
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++) {
- /* the scrollbar *always* fits somewhere */
- if (el->co == form->vertBar) {
+ /* 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 {
- /* only draw it if it'll fit on the screen vertically */
- if (componentFits(co, i)) {
- el->co->top = el->top - form->vertOffset;
- el->co->ops->mapped(el->co, 1);
- el->co->ops->draw(el->co);
- } else {
- el->co->ops->mapped(el->co, 0);
- }
+ el->co->ops->mapped(el->co, 0);
}
}
er.result = ER_IGNORED;
if (!form->numComps) return er;
- subco = form->elements[form->currComp].co;
+ if (form->currComp == -1) return er;
switch (ev.when) {
case EV_EARLY:
(el->co->left + el->co->width > ev.u.mouse.x)) {
found = 1;
if (el->co->takesFocus) {
- gotoComponent(form, i);
+ gotoComponent(co, i);
subco = form->elements[form->currComp].co;
}
}
else if (new >= form->numComps)
new = (form->numComps - 1);
- while (!form->elements[new].co->takesFocus)
- new = new - dir;
+ while (!form->elements[new].co->takesFocus &&
+ new - dir >= 0 && new - dir < form->numComps)
+ new -= dir;
} else {
do {
new += dir;
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)) {
- gotoComponent(form, -1);
+ int vertDelta;
+
+ gotoComponent(co, -1);
if (dir < 0) {
/* make the new component the first one */
- form->vertOffset = form->elements[new].top - co->top;
+ vertDelta = form->elements[new].co->top - co->top;
} else {
/* make the new component the last one */
- form->vertOffset = (form->elements[new].top +
+ vertDelta = (form->elements[new].co->top +
form->elements[new].co->height) -
(co->top + co->height);
}
- if (form->vertOffset < 0) form->vertOffset = 0;
- if (form->vertOffset > (form->numRows - co->height))
- form->vertOffset = form->numRows - co->height;
-
+ formScroll(co, vertDelta);
newtDrawForm(co);
}
- gotoComponent(form, new);
+ 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) {
newtComponent subco;
/* first, destroy all of the components */
for (i = 0; i < form->numComps; i++) {
subco = form->elements[i].co;
- if (subco->ops->destroy) {
- subco->ops->destroy(subco);
- } else {
- if (subco->data) free(subco->data);
- free(subco);
- }
+ newtComponentDestroy(subco);
}
if (form->hotKeys) free(form->hotKeys);
free(form->elements);
free(form);
free(co);
- newtResizeScreen(1);
}
newtComponent newtRunForm(newtComponent co) {
} else {
return NULL;
}
- }
+ } else if (es.reason == NEWT_EXIT_ERROR)
+ return NULL;
return es.u.co;
}
void newtFormSetSize(newtComponent co) {
struct form * form = co->data;
- int delta, i;
+ int delta, i, first;
struct element * el;
- if (form->beenSet) return;
-
- form->beenSet = 1;
-
- if (!form->numComps) return;
+ form->numRows = 0;
co->width = 0;
if (!form->fixedHeight) co->height = 0;
- co->top = form->elements[0].co->top;
- co->left = form->elements[0].co->left;
+ 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;
- el->left = el->co->left;
- el->top = el->co->top;
+ 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;
if (co->top > el->co->top) {
delta = co->top - el->co->top;
co->top -= delta;
+ form->numRows += delta;
if (!form->fixedHeight)
co->height += delta;
}
form->numRows = el->co->top + el->co->height - co->top;
}
}
+
+ co->top += form->vertOffset;
}
void newtFormRun(newtComponent co, struct newtExitStruct * es) {
struct eventResult er;
int key, i, max;
int done = 0;
- fd_set readSet, writeSet;
+ fd_set readSet, writeSet, exceptSet;
struct timeval nextTimeout, now, timeout;
#ifdef USE_GPM
int x, y;
Gpm_Open(&conn, 0);
#endif
- newtFormSetSize(co);
/* draw all of the components */
newtDrawForm(co);
if (form->currComp == -1) {
- gotoComponent(form, 0);
+ if (form->numComps)
+ gotoComponent(co, 0);
} else
- gotoComponent(form, form->currComp);
+ gotoComponent(co, form->currComp);
while (!done) {
newtRefresh();
FD_ZERO(&readSet);
FD_ZERO(&writeSet);
+ FD_ZERO(&exceptSet);
FD_SET(0, &readSet);
#ifdef USE_GPM
if (gpm_fd > 0) {
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. */
- if (!form->lastTimeout.tv_sec && !form->lastTimeout.tv_usec)
- gettimeofday(&form->lastTimeout, NULL);
+ 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;
- gettimeofday(&now, 0);
-
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 = timeout.tv_usec = 0;
}
- i = select(max + 1, &readSet, &writeSet, NULL,
+ 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? */
key = newtGetKey();
- if (key == NEWT_KEY_RESIZE) {
- /* newtResizeScreen(1); */
- continue;
- }
-
for (i = 0; i < form->numHotKeys; i++) {
if (form->hotKeys[i] == key) {
es->reason = NEWT_EXIT_HOTKEY;
}
}
- if (key == NEWT_KEY_F1 && form->helpTag && form->helpCb)
+ 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;
}
}
} 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;
}
return er;
}
-static void gotoComponent(struct form * form, int newComp) {
+static void gotoComponent(newtComponent co, int newComp) {
+ struct form * form = co->data;
struct event ev;
if (form->currComp != -1) {
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->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 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 = realloc(form->fds, (form->numFds + 1) * sizeof(*form->fds));
- form->fds[form->numFds].fd = fd;
- form->fds[form->numFds++].flags = fdFlags;
+ form->fds[i].fd = fd;
+ form->fds[i].flags = fdFlags;
if (form->maxFd < fd) form->maxFd = fd;
}