summaryrefslogtreecommitdiff
path: root/src/menus.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/menus.c')
-rw-r--r--src/menus.c3035
1 files changed, 3035 insertions, 0 deletions
diff --git a/src/menus.c b/src/menus.c
new file mode 100644
index 0000000..83fe792
--- /dev/null
+++ b/src/menus.c
@@ -0,0 +1,3035 @@
+/*****************************************************************************/
+/*
+
+Copyright 1989, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+
+*/
+/** Copyright 1988 by Evans & Sutherland Computer Corporation, **/
+/** Salt Lake City, Utah **/
+/** Cambridge, Massachusetts **/
+/** **/
+/** All Rights Reserved **/
+/** **/
+/** Permission to use, copy, modify, and distribute this software and **/
+/** its documentation for any purpose and without fee is hereby **/
+/** granted, provided that the above copyright notice appear in all **/
+/** copies and that both that copyright notice and this permis- **/
+/** sion notice appear in supporting documentation, and that the **/
+/** name of Evans & Sutherland not be used in advertising **/
+/** in publicity pertaining to distribution of the software without **/
+/** specific, written prior permission. **/
+/** **/
+/** EVANS & SUTHERLAND DISCLAIMs ALL WARRANTIES WITH REGARD **/
+/** TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- **/
+/** ABILITY AND FITNESS, IN NO EVENT SHALL EVANS & SUTHERLAND **/
+/** BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAM- **/
+/** AGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA **/
+/** OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER **/
+/** TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE **/
+/** OR PERFORMANCE OF THIS SOFTWARE. **/
+/*****************************************************************************/
+
+
+/***********************************************************************
+ *
+ * $Xorg: menus.c,v 1.6 2001/02/09 02:05:36 xorgcvs Exp $
+ *
+ * twm menu code
+ *
+ * 17-Nov-87 Thomas E. LaStrange File created
+ *
+ ***********************************************************************/
+
+#include <stdio.h>
+#include <X11/Xos.h>
+#include "twm.h"
+#include "gc.h"
+#include "menus.h"
+#include "resize.h"
+#include "events.h"
+#include "util.h"
+#include "parse.h"
+#include "gram.h"
+#include "screen.h"
+#include <X11/Xmu/CharSet.h>
+#include <X11/bitmaps/menu12>
+#include "version.h"
+#include <X11/extensions/sync.h>
+#include <X11/SM/SMlib.h>
+
+extern XEvent Event;
+
+int RootFunction = 0;
+MenuRoot *ActiveMenu = NULL; /* the active menu */
+MenuItem *ActiveItem = NULL; /* the active menu item */
+int MoveFunction; /* either F_MOVE or F_FORCEMOVE */
+int WindowMoved = FALSE;
+int menuFromFrameOrWindowOrTitlebar = FALSE;
+
+int ConstMove = FALSE; /* constrained move variables */
+int ConstMoveDir;
+int ConstMoveX;
+int ConstMoveY;
+int ConstMoveXL;
+int ConstMoveXR;
+int ConstMoveYT;
+int ConstMoveYB;
+
+/* Globals used to keep track of whether the mouse has moved during
+ a resize function. */
+int ResizeOrigX;
+int ResizeOrigY;
+
+int MenuDepth = 0; /* number of menus up */
+static struct {
+ int x;
+ int y;
+} MenuOrigins[MAXMENUDEPTH];
+static Cursor LastCursor;
+
+void WarpAlongRing(), WarpToWindow();
+
+extern char *Action;
+extern int Context;
+extern TwmWindow *ButtonWindow, *Tmp_win;
+extern XEvent Event, ButtonEvent;
+extern char *InitFile;
+static void Identify();
+
+#define SHADOWWIDTH 5 /* in pixels */
+
+
+
+/***********************************************************************
+ *
+ * Procedure:
+ * InitMenus - initialize menu roots
+ *
+ ***********************************************************************
+ */
+
+void
+InitMenus()
+{
+ int i, j, k;
+ FuncKey *key, *tmp;
+
+ for (i = 0; i < MAX_BUTTONS+1; i++)
+ for (j = 0; j < NUM_CONTEXTS; j++)
+ for (k = 0; k < MOD_SIZE; k++)
+ {
+ Scr->Mouse[i][j][k].func = 0;
+ Scr->Mouse[i][j][k].item = NULL;
+ }
+
+ Scr->DefaultFunction.func = 0;
+ Scr->WindowFunction.func = 0;
+
+ if (FirstScreen)
+ {
+ for (key = Scr->FuncKeyRoot.next; key != NULL;)
+ {
+ free(key->name);
+ tmp = key;
+ key = key->next;
+ free((char *) tmp);
+ }
+ Scr->FuncKeyRoot.next = NULL;
+ }
+
+}
+
+
+
+/***********************************************************************
+ *
+ * Procedure:
+ * AddFuncKey - add a function key to the list
+ *
+ * Inputs:
+ * name - the name of the key
+ * cont - the context to look for the key press in
+ * mods - modifier keys that need to be pressed
+ * func - the function to perform
+ * win_name- the window name (if any)
+ * action - the action string associated with the function (if any)
+ *
+ ***********************************************************************
+ */
+
+Bool AddFuncKey (name, cont, mods, func, win_name, action)
+ char *name;
+ int cont, mods, func;
+ char *win_name;
+ char *action;
+{
+ FuncKey *tmp;
+ KeySym keysym;
+ KeyCode keycode;
+
+ /*
+ * Don't let a 0 keycode go through, since that means AnyKey to the
+ * XGrabKey call in GrabKeys().
+ */
+ if ((keysym = XStringToKeysym(name)) == NoSymbol ||
+ (keycode = XKeysymToKeycode(dpy, keysym)) == 0)
+ {
+ return False;
+ }
+
+ /* see if there already is a key defined for this context */
+ for (tmp = Scr->FuncKeyRoot.next; tmp != NULL; tmp = tmp->next)
+ {
+ if (tmp->keysym == keysym &&
+ tmp->cont == cont &&
+ tmp->mods == mods)
+ break;
+ }
+
+ if (tmp == NULL)
+ {
+ tmp = (FuncKey *) malloc(sizeof(FuncKey));
+ tmp->next = Scr->FuncKeyRoot.next;
+ Scr->FuncKeyRoot.next = tmp;
+ }
+
+ tmp->name = name;
+ tmp->keysym = keysym;
+ tmp->keycode = keycode;
+ tmp->cont = cont;
+ tmp->mods = mods;
+ tmp->func = func;
+ tmp->win_name = win_name;
+ tmp->action = action;
+
+ return True;
+}
+
+
+
+int CreateTitleButton (name, func, action, menuroot, rightside, append)
+ char *name;
+ int func;
+ char *action;
+ MenuRoot *menuroot;
+ Bool rightside;
+ Bool append;
+{
+ TitleButton *tb = (TitleButton *) malloc (sizeof(TitleButton));
+
+ if (!tb) {
+ fprintf (stderr,
+ "%s: unable to allocate %d bytes for title button\n",
+ ProgramName, sizeof(TitleButton));
+ return 0;
+ }
+
+ tb->next = NULL;
+ tb->name = name; /* note that we are not copying */
+ tb->bitmap = None; /* WARNING, values not set yet */
+ tb->width = 0; /* see InitTitlebarButtons */
+ tb->height = 0; /* ditto */
+ tb->func = func;
+ tb->action = action;
+ tb->menuroot = menuroot;
+ tb->rightside = rightside;
+ if (rightside) {
+ Scr->TBInfo.nright++;
+ } else {
+ Scr->TBInfo.nleft++;
+ }
+
+ /*
+ * Cases for list:
+ *
+ * 1. empty list, prepend left put at head of list
+ * 2. append left, prepend right put in between left and right
+ * 3. append right put at tail of list
+ *
+ * Do not refer to widths and heights yet since buttons not created
+ * (since fonts not loaded and heights not known).
+ */
+ if ((!Scr->TBInfo.head) || ((!append) && (!rightside))) { /* 1 */
+ tb->next = Scr->TBInfo.head;
+ Scr->TBInfo.head = tb;
+ } else if (append && rightside) { /* 3 */
+ register TitleButton *t;
+ for /* SUPPRESS 530 */
+ (t = Scr->TBInfo.head; t->next; t = t->next);
+ t->next = tb;
+ tb->next = NULL;
+ } else { /* 2 */
+ register TitleButton *t, *prev = NULL;
+ for (t = Scr->TBInfo.head; t && !t->rightside; t = t->next) {
+ prev = t;
+ }
+ if (prev) {
+ tb->next = prev->next;
+ prev->next = tb;
+ } else {
+ tb->next = Scr->TBInfo.head;
+ Scr->TBInfo.head = tb;
+ }
+ }
+
+ return 1;
+}
+
+
+
+/*
+ * InitTitlebarButtons - Do all the necessary stuff to load in a titlebar
+ * button. If we can't find the button, then put in a question; if we can't
+ * find the question mark, something is wrong and we are probably going to be
+ * in trouble later on.
+ */
+void InitTitlebarButtons ()
+{
+ TitleButton *tb;
+ int h;
+
+ /*
+ * initialize dimensions
+ */
+ Scr->TBInfo.width = (Scr->TitleHeight -
+ 2 * (Scr->FramePadding + Scr->ButtonIndent));
+ Scr->TBInfo.pad = ((Scr->TitlePadding > 1)
+ ? ((Scr->TitlePadding + 1) / 2) : 1);
+ h = Scr->TBInfo.width - 2 * Scr->TBInfo.border;
+
+ /*
+ * add in some useful buttons and bindings so that novices can still
+ * use the system.
+ */
+ if (!Scr->NoDefaults) {
+ /* insert extra buttons */
+ if (!CreateTitleButton (TBPM_ICONIFY, F_ICONIFY, "", (MenuRoot *) NULL,
+ False, False)) {
+ fprintf (stderr, "%s: unable to add iconify button\n",
+ ProgramName);
+ }
+ if (!CreateTitleButton (TBPM_RESIZE, F_RESIZE, "", (MenuRoot *) NULL,
+ True, True)) {
+ fprintf (stderr, "%s: unable to add resize button\n",
+ ProgramName);
+ }
+ AddDefaultBindings ();
+ }
+ ComputeCommonTitleOffsets ();
+
+ /*
+ * load in images and do appropriate centering
+ */
+
+ for (tb = Scr->TBInfo.head; tb; tb = tb->next) {
+ tb->bitmap = FindBitmap (tb->name, &tb->width, &tb->height);
+ if (!tb->bitmap) {
+ tb->bitmap = FindBitmap (TBPM_QUESTION, &tb->width, &tb->height);
+ if (!tb->bitmap) { /* cannot happen (see util.c) */
+ fprintf (stderr,
+ "%s: unable to add titlebar button \"%s\"\n",
+ ProgramName, tb->name);
+ }
+ }
+
+ tb->dstx = (h - tb->width + 1) / 2;
+ if (tb->dstx < 0) { /* clip to minimize copying */
+ tb->srcx = -(tb->dstx);
+ tb->width = h;
+ tb->dstx = 0;
+ } else {
+ tb->srcx = 0;
+ }
+ tb->dsty = (h - tb->height + 1) / 2;
+ if (tb->dsty < 0) {
+ tb->srcy = -(tb->dsty);
+ tb->height = h;
+ tb->dsty = 0;
+ } else {
+ tb->srcy = 0;
+ }
+ }
+}
+
+
+
+PaintEntry(mr, mi, exposure)
+MenuRoot *mr;
+MenuItem *mi;
+int exposure;
+{
+ int y_offset;
+ int text_y;
+ GC gc;
+
+#ifdef DEBUG_MENUS
+ fprintf(stderr, "Paint entry\n");
+#endif
+ y_offset = mi->item_num * Scr->EntryHeight;
+ text_y = y_offset + Scr->MenuFont.y;
+
+ if (mi->func != F_TITLE)
+ {
+ int x, y;
+
+ if (mi->state)
+ {
+ XSetForeground(dpy, Scr->NormalGC, mi->hi_back);
+
+ XFillRectangle(dpy, mr->w, Scr->NormalGC, 0, y_offset,
+ mr->width, Scr->EntryHeight);
+
+ FBF(mi->hi_fore, mi->hi_back, Scr->MenuFont.font->fid);
+
+ XDrawString(dpy, mr->w, Scr->NormalGC, mi->x,
+ text_y, mi->item, mi->strlen);
+
+ gc = Scr->NormalGC;
+ }
+ else
+ {
+ if (mi->user_colors || !exposure)
+ {
+ XSetForeground(dpy, Scr->NormalGC, mi->back);
+
+ XFillRectangle(dpy, mr->w, Scr->NormalGC, 0, y_offset,
+ mr->width, Scr->EntryHeight);
+
+ FBF(mi->fore, mi->back, Scr->MenuFont.font->fid);
+ gc = Scr->NormalGC;
+ }
+ else
+ gc = Scr->MenuGC;
+
+ XDrawString(dpy, mr->w, gc, mi->x,
+ text_y, mi->item, mi->strlen);
+ }
+
+ if (mi->func == F_MENU)
+ {
+ /* create the pull right pixmap if needed */
+ if (Scr->pullPm == None)
+ {
+ Scr->pullPm = CreateMenuIcon (Scr->MenuFont.height,
+ &Scr->pullW, &Scr->pullH);
+ }
+ x = mr->width - Scr->pullW - 5;
+ y = y_offset + ((Scr->MenuFont.height - Scr->pullH) / 2);
+ XCopyPlane(dpy, Scr->pullPm, mr->w, gc, 0, 0,
+ Scr->pullW, Scr->pullH, x, y, 1);
+ }
+ }
+ else
+ {
+ int y;
+
+ XSetForeground(dpy, Scr->NormalGC, mi->back);
+
+ /* fill the rectangle with the title background color */
+ XFillRectangle(dpy, mr->w, Scr->NormalGC, 0, y_offset,
+ mr->width, Scr->EntryHeight);
+
+ {
+ XSetForeground(dpy, Scr->NormalGC, mi->fore);
+ /* now draw the dividing lines */
+ if (y_offset)
+ XDrawLine (dpy, mr->w, Scr->NormalGC, 0, y_offset,
+ mr->width, y_offset);
+ y = ((mi->item_num+1) * Scr->EntryHeight)-1;
+ XDrawLine(dpy, mr->w, Scr->NormalGC, 0, y, mr->width, y);
+ }
+
+ FBF(mi->fore, mi->back, Scr->MenuFont.font->fid);
+ /* finally render the title */
+ XDrawString(dpy, mr->w, Scr->NormalGC, mi->x,
+ text_y, mi->item, mi->strlen);
+ }
+}
+
+
+
+PaintMenu(mr, e)
+MenuRoot *mr;
+XEvent *e;
+{
+ MenuItem *mi;
+
+ for (mi = mr->first; mi != NULL; mi = mi->next)
+ {
+ int y_offset = mi->item_num * Scr->EntryHeight;
+
+ /* be smart about handling the expose, redraw only the entries
+ * that we need to
+ */
+ if (e->xexpose.y < (y_offset + Scr->EntryHeight) &&
+ (e->xexpose.y + e->xexpose.height) > y_offset)
+ {
+ PaintEntry(mr, mi, True);
+ }
+ }
+ XSync(dpy, 0);
+}
+
+
+
+static Bool fromMenu;
+
+UpdateMenu()
+{
+ MenuItem *mi;
+ int i, x, y, x_root, y_root, entry;
+ int done;
+ MenuItem *badItem = NULL;
+
+ fromMenu = TRUE;
+
+ while (TRUE)
+ {
+ /* block until there is an event */
+ if (!menuFromFrameOrWindowOrTitlebar) {
+ XMaskEvent(dpy,
+ ButtonPressMask | ButtonReleaseMask |
+ EnterWindowMask | ExposureMask |
+ VisibilityChangeMask | LeaveWindowMask |
+ ButtonMotionMask, &Event);
+ }
+ if (Event.type == MotionNotify) {
+ /* discard any extra motion events before a release */
+ while(XCheckMaskEvent(dpy,
+ ButtonMotionMask | ButtonReleaseMask, &Event))
+ if (Event.type == ButtonRelease)
+ break;
+ }
+
+ if (!DispatchEvent ())
+ continue;
+
+ if (Event.type == ButtonRelease || Cancel) {
+ menuFromFrameOrWindowOrTitlebar = FALSE;
+ fromMenu = FALSE;
+ return;
+ }
+
+ if (Event.type != MotionNotify)
+ continue;
+
+ done = FALSE;
+ XQueryPointer( dpy, ActiveMenu->w, &JunkRoot, &JunkChild,
+ &x_root, &y_root, &x, &y, &JunkMask);
+
+ /* if we haven't recieved the enter notify yet, wait */
+ if (ActiveMenu && !ActiveMenu->entered)
+ continue;
+
+ XFindContext(dpy, ActiveMenu->w, ScreenContext, (caddr_t *)&Scr);
+
+ if (x < 0 || y < 0 ||
+ x >= ActiveMenu->width || y >= ActiveMenu->height)
+ {
+ if (ActiveItem && ActiveItem->func != F_TITLE)
+ {
+ ActiveItem->state = 0;
+ PaintEntry(ActiveMenu, ActiveItem, False);
+ }
+ ActiveItem = NULL;
+ continue;
+ }
+
+ /* look for the entry that the mouse is in */
+ entry = y / Scr->EntryHeight;
+ for (i = 0, mi = ActiveMenu->first; mi != NULL; i++, mi=mi->next)
+ {
+ if (i == entry)
+ break;
+ }
+
+ /* if there is an active item, we might have to turn it off */
+ if (ActiveItem)
+ {
+ /* is the active item the one we are on ? */
+ if (ActiveItem->item_num == entry && ActiveItem->state)
+ done = TRUE;
+
+ /* if we weren't on the active entry, let's turn the old
+ * active one off
+ */
+ if (!done && ActiveItem->func != F_TITLE)
+ {
+ ActiveItem->state = 0;
+ PaintEntry(ActiveMenu, ActiveItem, False);
+ }
+ }
+
+ /* if we weren't on the active item, change the active item and turn
+ * it on
+ */
+ if (!done)
+ {
+ ActiveItem = mi;
+ if (ActiveItem->func != F_TITLE && !ActiveItem->state)
+ {
+ ActiveItem->state = 1;
+ PaintEntry(ActiveMenu, ActiveItem, False);
+ }
+ }
+
+ /* now check to see if we were over the arrow of a pull right entry */
+ if (ActiveItem->func == F_MENU &&
+ ((ActiveMenu->width - x) < (ActiveMenu->width >> 1)))
+ {
+ MenuRoot *save = ActiveMenu;
+ int savex = MenuOrigins[MenuDepth - 1].x;
+ int savey = MenuOrigins[MenuDepth - 1].y;
+
+ if (MenuDepth < MAXMENUDEPTH) {
+ PopUpMenu (ActiveItem->sub,
+ (savex + (ActiveMenu->width >> 1)),
+ (savey + ActiveItem->item_num * Scr->EntryHeight)
+ /*(savey + ActiveItem->item_num * Scr->EntryHeight +
+ (Scr->EntryHeight >> 1))*/, False);
+ } else if (!badItem) {
+ Bell(XkbBI_MinorError,0,None);
+ badItem = ActiveItem;
+ }
+
+ /* if the menu did get popped up, unhighlight the active item */
+ if (save != ActiveMenu && ActiveItem->state)
+ {
+ ActiveItem->state = 0;
+ PaintEntry(save, ActiveItem, False);
+ ActiveItem = NULL;
+ }
+ }
+ if (badItem != ActiveItem) badItem = NULL;
+ XFlush(dpy);
+ }
+
+}
+
+
+
+/***********************************************************************
+ *
+ * Procedure:
+ * NewMenuRoot - create a new menu root
+ *
+ * Returned Value:
+ * (MenuRoot *)
+ *
+ * Inputs:
+ * name - the name of the menu root
+ *
+ ***********************************************************************
+ */
+
+MenuRoot *
+NewMenuRoot(name)
+ char *name;
+{
+ MenuRoot *tmp;
+
+#define UNUSED_PIXEL ((unsigned long) (~0)) /* more than 24 bits */
+
+ tmp = (MenuRoot *) malloc(sizeof(MenuRoot));
+ tmp->hi_fore = UNUSED_PIXEL;
+ tmp->hi_back = UNUSED_PIXEL;
+ tmp->name = name;
+ tmp->prev = NULL;
+ tmp->first = NULL;
+ tmp->last = NULL;
+ tmp->items = 0;
+ tmp->width = 0;
+ tmp->mapped = NEVER_MAPPED;
+ tmp->pull = FALSE;
+ tmp->w = None;
+ tmp->shadow = None;
+ tmp->real_menu = FALSE;
+
+ if (Scr->MenuList == NULL)
+ {
+ Scr->MenuList = tmp;
+ Scr->MenuList->next = NULL;
+ }
+
+ if (Scr->LastMenu == NULL)
+ {
+ Scr->LastMenu = tmp;
+ Scr->LastMenu->next = NULL;
+ }
+ else
+ {
+ Scr->LastMenu->next = tmp;
+ Scr->LastMenu = tmp;
+ Scr->LastMenu->next = NULL;
+ }
+
+ if (strcmp(name, TWM_WINDOWS) == 0)
+ Scr->Windows = tmp;
+
+ return (tmp);
+}
+
+
+
+/***********************************************************************
+ *
+ * Procedure:
+ * AddToMenu - add an item to a root menu
+ *
+ * Returned Value:
+ * (MenuItem *)
+ *
+ * Inputs:
+ * menu - pointer to the root menu to add the item
+ * item - the text to appear in the menu
+ * action - the string to possibly execute
+ * sub - the menu root if it is a pull-right entry
+ * func - the numeric function
+ * fore - foreground color string
+ * back - background color string
+ *
+ ***********************************************************************
+ */
+
+MenuItem *
+AddToMenu(menu, item, action, sub, func, fore, back)
+ MenuRoot *menu;
+ char *item, *action;
+ MenuRoot *sub;
+ int func;
+ char *fore, *back;
+{
+ MenuItem *tmp;
+ int width;
+
+#ifdef DEBUG_MENUS
+ fprintf(stderr, "adding menu item=\"%s\", action=%s, sub=%d, f=%d\n",
+ item, action, sub, func);
+#endif
+
+ tmp = (MenuItem *) malloc(sizeof(MenuItem));
+ tmp->root = menu;
+
+ if (menu->first == NULL)
+ {
+ menu->first = tmp;
+ tmp->prev = NULL;
+ }
+ else
+ {
+ menu->last->next = tmp;
+ tmp->prev = menu->last;
+ }
+ menu->last = tmp;
+
+ tmp->item = item;
+ tmp->strlen = strlen(item);
+ tmp->action = action;
+ tmp->next = NULL;
+ tmp->sub = NULL;
+ tmp->state = 0;
+ tmp->func = func;
+
+ if (!Scr->HaveFonts) CreateFonts();
+ width = XTextWidth(Scr->MenuFont.font, item, tmp->strlen);
+ if (width <= 0)
+ width = 1;
+ if (width > menu->width)
+ menu->width = width;
+
+ tmp->user_colors = FALSE;
+ if (Scr->Monochrome == COLOR && fore != NULL)
+ {
+ int save;
+
+ save = Scr->FirstTime;
+ Scr->FirstTime = TRUE;
+ GetColor(COLOR, &tmp->fore, fore);
+ GetColor(COLOR, &tmp->back, back);
+ Scr->FirstTime = save;
+ tmp->user_colors = TRUE;
+ }
+ if (sub != NULL)
+ {
+ tmp->sub = sub;
+ menu->pull = TRUE;
+ }
+ tmp->item_num = menu->items++;
+
+ return (tmp);
+}
+
+
+
+MakeMenus()
+{
+ MenuRoot *mr;
+
+ for (mr = Scr->MenuList; mr != NULL; mr = mr->next)
+ {
+ if (mr->real_menu == FALSE)
+ continue;
+
+ MakeMenu(mr);
+ }
+}
+
+
+
+MakeMenu(mr)
+MenuRoot *mr;
+{
+ MenuItem *start, *end, *cur, *tmp;
+ XColor f1, f2, f3;
+ XColor b1, b2, b3;
+ XColor save_fore, save_back;
+ int num, i;
+ int fred, fgreen, fblue;
+ int bred, bgreen, bblue;
+ int width;
+ unsigned long valuemask;
+ XSetWindowAttributes attributes;
+ Colormap cmap = Scr->TwmRoot.cmaps.cwins[0]->colormap->c;
+
+ Scr->EntryHeight = Scr->MenuFont.height + 4;
+
+ /* lets first size the window accordingly */
+ if (mr->mapped == NEVER_MAPPED)
+ {
+ if (mr->pull == TRUE)
+ {
+ mr->width += 16 + 10;
+ }
+
+ width = mr->width + 10;
+
+ for (cur = mr->first; cur != NULL; cur = cur->next)
+ {
+ if (cur->func != F_TITLE)
+ cur->x = 5;
+ else
+ {
+ cur->x = width - XTextWidth(Scr->MenuFont.font, cur->item,
+ cur->strlen);
+ cur->x /= 2;
+ }
+ }
+ mr->height = mr->items * Scr->EntryHeight;
+ mr->width += 10;
+
+ if (Scr->Shadow)
+ {
+ /*
+ * Make sure that you don't draw into the shadow window or else
+ * the background bits there will get saved
+ */
+ valuemask = (CWBackPixel | CWBorderPixel);
+ attributes.background_pixel = Scr->MenuShadowColor;
+ attributes.border_pixel = Scr->MenuShadowColor;
+ if (Scr->SaveUnder) {
+ valuemask |= CWSaveUnder;
+ attributes.save_under = True;
+ }
+ mr->shadow = XCreateWindow (dpy, Scr->Root, 0, 0,
+ (unsigned int) mr->width,
+ (unsigned int) mr->height,
+ (unsigned int)0,
+ CopyFromParent,
+ (unsigned int) CopyFromParent,
+ (Visual *) CopyFromParent,
+ valuemask, &attributes);
+ }
+
+ valuemask = (CWBackPixel | CWBorderPixel | CWEventMask);
+ attributes.background_pixel = Scr->MenuC.back;
+ attributes.border_pixel = Scr->MenuC.fore;
+ attributes.event_mask = (ExposureMask | EnterWindowMask);
+ if (Scr->SaveUnder) {
+ valuemask |= CWSaveUnder;
+ attributes.save_under = True;
+ }
+ if (Scr->BackingStore) {
+ valuemask |= CWBackingStore;
+ attributes.backing_store = Always;
+ }
+ mr->w = XCreateWindow (dpy, Scr->Root, 0, 0, (unsigned int) mr->width,
+ (unsigned int) mr->height, (unsigned int) 1,
+ CopyFromParent, (unsigned int) CopyFromParent,
+ (Visual *) CopyFromParent,
+ valuemask, &attributes);
+
+
+ XSaveContext(dpy, mr->w, MenuContext, (caddr_t)mr);
+ XSaveContext(dpy, mr->w, ScreenContext, (caddr_t)Scr);
+
+ mr->mapped = UNMAPPED;
+ }
+
+ /* get the default colors into the menus */
+ for (tmp = mr->first; tmp != NULL; tmp = tmp->next)
+ {
+ if (!tmp->user_colors) {
+ if (tmp->func != F_TITLE) {
+ tmp->fore = Scr->MenuC.fore;
+ tmp->back = Scr->MenuC.back;
+ } else {
+ tmp->fore = Scr->MenuTitleC.fore;
+ tmp->back = Scr->MenuTitleC.back;
+ }
+ }
+
+ if (mr->hi_fore != UNUSED_PIXEL)
+ {
+ tmp->hi_fore = mr->hi_fore;
+ tmp->hi_back = mr->hi_back;
+ }
+ else
+ {
+ tmp->hi_fore = tmp->back;
+ tmp->hi_back = tmp->fore;
+ }
+ }
+
+ if (Scr->Monochrome == MONOCHROME || !Scr->InterpolateMenuColors)
+ return;
+
+ start = mr->first;
+ while (TRUE)
+ {
+ for (; start != NULL; start = start->next)
+ {
+ if (start->user_colors)
+ break;
+ }
+ if (start == NULL)
+ break;
+
+ for (end = start->next; end != NULL; end = end->next)
+ {
+ if (end->user_colors)
+ break;
+ }
+ if (end == NULL)
+ break;
+
+ /* we have a start and end to interpolate between */
+ num = end->item_num - start->item_num;
+
+ f1.pixel = start->fore;
+ XQueryColor(dpy, cmap, &f1);
+ f2.pixel = end->fore;
+ XQueryColor(dpy, cmap, &f2);
+
+ b1.pixel = start->back;
+ XQueryColor(dpy, cmap, &b1);
+ b2.pixel = end->back;
+ XQueryColor(dpy, cmap, &b2);
+
+ fred = ((int)f2.red - (int)f1.red) / num;
+ fgreen = ((int)f2.green - (int)f1.green) / num;
+ fblue = ((int)f2.blue - (int)f1.blue) / num;
+
+ bred = ((int)b2.red - (int)b1.red) / num;
+ bgreen = ((int)b2.green - (int)b1.green) / num;
+ bblue = ((int)b2.blue - (int)b1.blue) / num;
+
+ f3 = f1;
+ f3.flags = DoRed | DoGreen | DoBlue;
+
+ b3 = b1;
+ b3.flags = DoRed | DoGreen | DoBlue;
+
+ num -= 1;
+ for (i = 0, cur = start->next; i < num; i++, cur = cur->next)
+ {
+ f3.red += fred;
+ f3.green += fgreen;
+ f3.blue += fblue;
+ save_fore = f3;
+
+ b3.red += bred;
+ b3.green += bgreen;
+ b3.blue += bblue;
+ save_back = b3;
+
+ XAllocColor(dpy, cmap, &f3);
+ XAllocColor(dpy, cmap, &b3);
+ cur->hi_back = cur->fore = f3.pixel;
+ cur->hi_fore = cur->back = b3.pixel;
+ cur->user_colors = True;
+
+ f3 = save_fore;
+ b3 = save_back;
+ }
+ start = end;
+ }
+}
+
+
+
+/***********************************************************************
+ *
+ * Procedure:
+ * PopUpMenu - pop up a pull down menu
+ *
+ * Inputs:
+ * menu - the root pointer of the menu to pop up
+ * x, y - location of upper left of menu
+ * center - whether or not to center horizontally over position
+ *
+ ***********************************************************************
+ */
+
+Bool PopUpMenu (menu, x, y, center)
+ MenuRoot *menu;
+ int x, y;
+ Bool center;
+{
+ int WindowNameOffset, WindowNameCount;
+ TwmWindow **WindowNames;
+ TwmWindow *tmp_win2,*tmp_win3;
+ int i;
+ int (*compar)() =
+ (Scr->CaseSensitive ? strcmp : XmuCompareISOLatin1);
+
+ if (!menu) return False;
+
+ InstallRootColormap();
+
+ if (menu == Scr->Windows)
+ {
+ TwmWindow *tmp_win;
+
+ /* this is the twm windows menu, let's go ahead and build it */
+
+ DestroyMenu (menu);
+
+ menu->first = NULL;
+ menu->last = NULL;
+ menu->items = 0;
+ menu->width = 0;
+ menu->mapped = NEVER_MAPPED;
+ AddToMenu(menu, "TWM Windows", NULLSTR, NULL, F_TITLE,NULLSTR,NULLSTR);
+
+ WindowNameOffset=(char *)Scr->TwmRoot.next->name -
+ (char *)Scr->TwmRoot.next;
+ for(tmp_win = Scr->TwmRoot.next , WindowNameCount=0;
+ tmp_win != NULL;
+ tmp_win = tmp_win->next)
+ WindowNameCount++;
+ WindowNames =
+ (TwmWindow **)malloc(sizeof(TwmWindow *)*WindowNameCount);
+ WindowNames[0] = Scr->TwmRoot.next;
+ for(tmp_win = Scr->TwmRoot.next->next , WindowNameCount=1;
+ tmp_win != NULL;
+ tmp_win = tmp_win->next,WindowNameCount++)
+ {
+ tmp_win2 = tmp_win;
+ for (i=0;i<WindowNameCount;i++)
+ {
+ if ((*compar)(tmp_win2->name,WindowNames[i]->name) < 0)
+ {
+ tmp_win3 = tmp_win2;
+ tmp_win2 = WindowNames[i];
+ WindowNames[i] = tmp_win3;
+ }
+ }
+ WindowNames[WindowNameCount] = tmp_win2;
+ }
+ for (i=0; i<WindowNameCount; i++)
+ {
+ AddToMenu(menu, WindowNames[i]->name, (char *)WindowNames[i],
+ NULL, F_POPUP,NULL,NULL);
+ }
+ free(WindowNames);
+
+ MakeMenu(menu);
+ }
+
+ if (menu->w == None || menu->items == 0) return False;
+
+ /* Prevent recursively bringing up menus. */
+ if (menu->mapped == MAPPED) return False;
+
+ /*
+ * Dynamically set the parent; this allows pull-ups to also be main
+ * menus, or to be brought up from more than one place.
+ */
+ menu->prev = ActiveMenu;
+
+ XGrabPointer(dpy, Scr->Root, True,
+ ButtonPressMask | ButtonReleaseMask |
+ ButtonMotionMask | PointerMotionHintMask,
+ GrabModeAsync, GrabModeAsync,
+ Scr->Root, Scr->MenuCursor, CurrentTime);
+
+ ActiveMenu = menu;
+ menu->mapped = MAPPED;
+ menu->entered = FALSE;
+
+ if (center) {
+ x -= (menu->width / 2);
+ y -= (Scr->EntryHeight / 2); /* sticky menus would be nice here */
+ }
+
+ /*
+ * clip to screen
+ */
+ if (x + menu->width > Scr->MyDisplayWidth) {
+ x = Scr->MyDisplayWidth - menu->width;
+ }
+ if (x < 0) x = 0;
+ if (y + menu->height > Scr->MyDisplayHeight) {
+ y = Scr->MyDisplayHeight - menu->height;
+ }
+ if (y < 0) y = 0;
+
+ MenuOrigins[MenuDepth].x = x;
+ MenuOrigins[MenuDepth].y = y;
+ MenuDepth++;
+
+ XMoveWindow(dpy, menu->w, x, y);
+ if (Scr->Shadow) {
+ XMoveWindow (dpy, menu->shadow, x + SHADOWWIDTH, y + SHADOWWIDTH);
+ }
+ if (Scr->Shadow) {
+ XRaiseWindow (dpy, menu->shadow);
+ }
+ XMapRaised(dpy, menu->w);
+ if (Scr->Shadow) {
+ XMapWindow (dpy, menu->shadow);
+ }
+ XSync(dpy, 0);
+ return True;
+}
+
+
+
+/***********************************************************************
+ *
+ * Procedure:
+ * PopDownMenu - unhighlight the current menu selection and
+ * take down the menus
+ *
+ ***********************************************************************
+ */
+
+PopDownMenu()
+{
+ MenuRoot *tmp;
+
+ if (ActiveMenu == NULL)
+ return;
+
+ if (ActiveItem)
+ {
+ ActiveItem->state = 0;
+ PaintEntry(ActiveMenu, ActiveItem, False);
+ }
+
+ for (tmp = ActiveMenu; tmp != NULL; tmp = tmp->prev)
+ {
+ if (Scr->Shadow) {
+ XUnmapWindow (dpy, tmp->shadow);
+ }
+ XUnmapWindow(dpy, tmp->w);
+ tmp->mapped = UNMAPPED;
+ UninstallRootColormap();
+ }
+
+ XFlush(dpy);
+ ActiveMenu = NULL;
+ ActiveItem = NULL;
+ MenuDepth = 0;
+ if (Context == C_WINDOW || Context == C_FRAME || Context == C_TITLE)
+ menuFromFrameOrWindowOrTitlebar = TRUE;
+}
+
+
+
+/***********************************************************************
+ *
+ * Procedure:
+ * FindMenuRoot - look for a menu root
+ *
+ * Returned Value:
+ * (MenuRoot *) - a pointer to the menu root structure
+ *
+ * Inputs:
+ * name - the name of the menu root
+ *
+ ***********************************************************************
+ */
+
+MenuRoot *
+FindMenuRoot(name)
+ char *name;
+{
+ MenuRoot *tmp;
+
+ for (tmp = Scr->MenuList; tmp != NULL; tmp = tmp->next)
+ {
+ if (strcmp(name, tmp->name) == 0)
+ return (tmp);
+ }
+ return NULL;
+}
+
+
+
+static Bool belongs_to_twm_window (t, w)
+ register TwmWindow *t;
+ register Window w;
+{
+ if (!t) return False;
+
+ if (w == t->frame || w == t->title_w || w == t->hilite_w ||
+ w == t->icon_w || w == t->icon_bm_w) return True;
+
+ if (t && t->titlebuttons) {
+ register TBWindow *tbw;
+ register int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright;
+ for (tbw = t->titlebuttons; nb > 0; tbw++, nb--) {
+ if (tbw->window == w) return True;
+ }
+ }
+ return False;
+}
+
+
+
+
+/***********************************************************************
+ *
+ * Procedure:
+ * resizeFromCenter -
+ *
+ ***********************************************************************
+ */
+
+
+extern int AddingX;
+extern int AddingY;
+extern int AddingW;
+extern int AddingH;
+
+void resizeFromCenter(w, tmp_win)
+ Window w;
+ TwmWindow *tmp_win;
+{
+ int lastx, lasty, width, height, bw2;
+ int namelen;
+ XEvent event;
+
+ namelen = strlen (tmp_win->name);
+ bw2 = tmp_win->frame_bw * 2;
+ AddingW = tmp_win->attr.width + bw2;
+ AddingH = tmp_win->attr.height + tmp_win->title_height + bw2;
+ width = (SIZE_HINDENT + XTextWidth (Scr->SizeFont.font,
+ tmp_win->name, namelen));
+ height = Scr->SizeFont.height + SIZE_VINDENT * 2;
+ XGetGeometry(dpy, w, &JunkRoot, &origDragX, &origDragY,
+ (unsigned int *)&DragWidth, (unsigned int *)&DragHeight,
+ &JunkBW, &JunkDepth);
+ XWarpPointer(dpy, None, w,
+ 0, 0, 0, 0, DragWidth/2, DragHeight/2);
+ XQueryPointer (dpy, Scr->Root, &JunkRoot,
+ &JunkChild, &JunkX, &JunkY,
+ &AddingX, &AddingY, &JunkMask);
+/*****
+ Scr->SizeStringOffset = width +
+ XTextWidth(Scr->SizeFont.font, ": ", 2);
+ XResizeWindow (dpy, Scr->SizeWindow, Scr->SizeStringOffset +
+ Scr->SizeStringWidth, height);
+ XDrawImageString (dpy, Scr->SizeWindow, Scr->NormalGC, width,
+ SIZE_VINDENT + Scr->SizeFont.font->ascent,
+ ": ", 2);
+*****/
+ lastx = -10000;
+ lasty = -10000;
+/*****
+ MoveOutline(Scr->Root,
+ origDragX - JunkBW, origDragY - JunkBW,
+ DragWidth * JunkBW, DragHeight * JunkBW,
+ tmp_win->frame_bw,
+ tmp_win->title_height);
+*****/
+ MenuStartResize(tmp_win, origDragX, origDragY, DragWidth, DragHeight);
+ while (TRUE)
+ {
+ XMaskEvent(dpy,
+ ButtonPressMask | PointerMotionMask, &event);
+
+ if (event.type == MotionNotify) {
+ /* discard any extra motion events before a release */
+ while(XCheckMaskEvent(dpy,
+ ButtonMotionMask | ButtonPressMask, &event))
+ if (event.type == ButtonPress)
+ break;
+ }
+
+ if (event.type == ButtonPress)
+ {
+ MenuEndResize(tmp_win);
+ XMoveResizeWindow(dpy, w, AddingX, AddingY, AddingW, AddingH);
+ break;
+ }
+
+/* if (!DispatchEvent ()) continue; */
+
+ if (event.type != MotionNotify) {
+ continue;
+ }
+
+ /*
+ * XXX - if we are going to do a loop, we ought to consider
+ * using multiple GXxor lines so that we don't need to
+ * grab the server.
+ */
+ XQueryPointer(dpy, Scr->Root, &JunkRoot, &JunkChild,
+ &JunkX, &JunkY, &AddingX, &AddingY, &JunkMask);
+
+ if (lastx != AddingX || lasty != AddingY)
+ {
+ MenuDoResize(AddingX, AddingY, tmp_win);
+
+ lastx = AddingX;
+ lasty = AddingY;
+ }
+
+ }
+}
+
+
+
+/***********************************************************************
+ *
+ * Procedure:
+ * ExecuteFunction - execute a twm root function
+ *
+ * Inputs:
+ * func - the function to execute
+ * action - the menu action to execute
+ * w - the window to execute this function on
+ * tmp_win - the twm window structure
+ * event - the event that caused the function
+ * context - the context in which the button was pressed
+ * pulldown- flag indicating execution from pull down menu
+ *
+ * Returns:
+ * TRUE if should continue with remaining actions else FALSE to abort
+ *
+ ***********************************************************************
+ */
+
+/* for F_WARPTO */
+#define true 1
+#define false 0
+int
+WarpThere(t)
+ TwmWindow* t;
+{
+ if (Scr->WarpUnmapped || t->mapped) {
+ if (!t->mapped) DeIconify (t);
+ if (!Scr->NoRaiseWarp) XRaiseWindow (dpy, t->frame);
+ WarpToWindow (t);
+ return true;
+ }
+ return false;
+}
+
+extern int MovedFromKeyPress;
+
+int
+ExecuteFunction(func, action, w, tmp_win, eventp, context, pulldown)
+ int func;
+ char *action;
+ Window w;
+ TwmWindow *tmp_win;
+ XEvent *eventp;
+ int context;
+ int pulldown;
+{
+ static Time last_time = 0;
+ char tmp[200];
+ char *ptr;
+ char buff[MAX_FILE_SIZE];
+ int count, fd;
+ Window rootw;
+ int origX, origY;
+ int do_next_action = TRUE;
+ int moving_icon = FALSE;
+ Bool fromtitlebar = False;
+ extern int ConstrainedMoveTime;
+
+ RootFunction = 0;
+ if (Cancel)
+ return TRUE; /* XXX should this be FALSE? */
+
+ switch (func)
+ {
+ case F_UPICONMGR:
+ case F_LEFTICONMGR:
+ case F_RIGHTICONMGR:
+ case F_DOWNICONMGR:
+ case F_FORWICONMGR:
+ case F_BACKICONMGR:
+ case F_NEXTICONMGR:
+ case F_PREVICONMGR:
+ case F_NOP:
+ case F_TITLE:
+ case F_DELTASTOP:
+ case F_RAISELOWER:
+ case F_WARPTOSCREEN:
+ case F_WARPTO:
+ case F_WARPRING:
+ case F_WARPTOICONMGR:
+ case F_COLORMAP:
+ break;
+ default:
+ XGrabPointer(dpy, Scr->Root, True,
+ ButtonPressMask | ButtonReleaseMask,
+ GrabModeAsync, GrabModeAsync,
+ Scr->Root, Scr->WaitCursor, CurrentTime);
+ break;
+ }
+
+ switch (func)
+ {
+ case F_NOP:
+ case F_TITLE:
+ break;
+
+ case F_DELTASTOP:
+ if (WindowMoved) do_next_action = FALSE;
+ break;
+
+ case F_RESTART:
+ {
+ extern SmcConn smcConn;
+
+ XSync (dpy, 0);
+ Reborder (eventp->xbutton.time);
+ XSync (dpy, 0);
+ if (smcConn)
+ SmcCloseConnection (smcConn, 0, NULL);
+ execvp(*Argv, Argv);
+ fprintf (stderr, "%s: unable to restart: %s\n", ProgramName, *Argv);
+ break;
+ }
+
+ case F_UPICONMGR:
+ case F_DOWNICONMGR:
+ case F_LEFTICONMGR:
+ case F_RIGHTICONMGR:
+ case F_FORWICONMGR:
+ case F_BACKICONMGR:
+ MoveIconManager(func);
+ break;
+
+ case F_NEXTICONMGR:
+ case F_PREVICONMGR:
+ JumpIconManager(func);
+ break;
+
+ case F_SHOWLIST:
+ if (Scr->NoIconManagers)
+ break;
+ DeIconify(Scr->iconmgr.twm_win);
+ XRaiseWindow(dpy, Scr->iconmgr.twm_win->frame);
+ break;
+
+ case F_HIDELIST:
+ if (Scr->NoIconManagers)
+ break;
+ HideIconManager ();
+ break;
+
+ case F_SORTICONMGR:
+ if (DeferExecution(context, func, Scr->SelectCursor))
+ return TRUE;
+
+ {
+ int save_sort;
+
+ save_sort = Scr->SortIconMgr;
+ Scr->SortIconMgr = TRUE;
+
+ if (context == C_ICONMGR)
+ SortIconManager((IconMgr *) NULL);
+ else if (tmp_win->iconmgr)
+ SortIconManager(tmp_win->iconmgrp);
+ else
+ Bell(XkbBI_Info,0,tmp_win->w);
+
+ Scr->SortIconMgr = save_sort;
+ }
+ break;
+
+ case F_IDENTIFY:
+ if (DeferExecution(context, func, Scr->SelectCursor))
+ return TRUE;
+
+ Identify(tmp_win);
+ break;
+
+ case F_VERSION:
+ Identify ((TwmWindow *) NULL);
+ break;
+
+ case F_AUTORAISE:
+ if (DeferExecution(context, func, Scr->SelectCursor))
+ return TRUE;
+
+ tmp_win->auto_raise = !tmp_win->auto_raise;
+ if (tmp_win->auto_raise) ++(Scr->NumAutoRaises);
+ else --(Scr->NumAutoRaises);
+ break;
+
+ case F_BEEP:
+ Bell(XkbBI_Info,0,None);
+ break;
+
+ case F_POPUP:
+ tmp_win = (TwmWindow *)action;
+ if (Scr->WindowFunction.func != 0)
+ {
+ ExecuteFunction(Scr->WindowFunction.func,
+ Scr->WindowFunction.item->action,
+ w, tmp_win, eventp, C_FRAME, FALSE);
+ }
+ else
+ {
+ DeIconify(tmp_win);
+ XRaiseWindow (dpy, tmp_win->frame);
+ }
+ break;
+
+ case F_RESIZE:
+ EventHandler[EnterNotify] = HandleUnknown;
+ EventHandler[LeaveNotify] = HandleUnknown;
+ if (DeferExecution(context, func, Scr->MoveCursor))
+ return TRUE;
+
+ PopDownMenu();
+
+ if (pulldown)
+ XWarpPointer(dpy, None, Scr->Root,
+ 0, 0, 0, 0, eventp->xbutton.x_root, eventp->xbutton.y_root);
+
+ if (w != tmp_win->icon_w) { /* can't resize icons */
+
+ if ((Context == C_FRAME || Context == C_WINDOW || Context == C_TITLE)
+ && fromMenu)
+ resizeFromCenter(w, tmp_win);
+ else {
+ /*
+ * see if this is being done from the titlebar
+ */
+ fromtitlebar =
+ belongs_to_twm_window (tmp_win, eventp->xbutton.window);
+
+ /* Save pointer position so we can tell if it was moved or
+ not during the resize. */
+ ResizeOrigX = eventp->xbutton.x_root;
+ ResizeOrigY = eventp->xbutton.y_root;
+
+ StartResize (eventp, tmp_win, fromtitlebar);
+
+ do {
+ XMaskEvent(dpy,
+ ButtonPressMask | ButtonReleaseMask |
+ EnterWindowMask | LeaveWindowMask |
+ ButtonMotionMask, &Event);
+
+ if (fromtitlebar && Event.type == ButtonPress) {
+ fromtitlebar = False;
+ continue;
+ }
+
+ if (Event.type == MotionNotify) {
+ /* discard any extra motion events before a release */
+ while
+ (XCheckMaskEvent
+ (dpy, ButtonMotionMask | ButtonReleaseMask, &Event))
+ if (Event.type == ButtonRelease)
+ break;
+ }
+
+ if (!DispatchEvent ()) continue;
+
+ } while (!(Event.type == ButtonRelease || Cancel));
+ return TRUE;
+ }
+ }
+ break;
+
+
+ case F_ZOOM:
+ case F_HORIZOOM:
+ case F_FULLZOOM:
+ case F_LEFTZOOM:
+ case F_RIGHTZOOM:
+ case F_TOPZOOM:
+ case F_BOTTOMZOOM:
+ if (DeferExecution(context, func, Scr->SelectCursor))
+ return TRUE;
+ fullzoom(tmp_win, func);
+ break;
+
+ case F_MOVE:
+ case F_FORCEMOVE:
+ if (DeferExecution(context, func, Scr->MoveCursor))
+ return TRUE;
+
+ PopDownMenu();
+ rootw = eventp->xbutton.root;
+ MoveFunction = func;
+
+ if (pulldown)
+ XWarpPointer(dpy, None, Scr->Root,
+ 0, 0, 0, 0, eventp->xbutton.x_root, eventp->xbutton.y_root);
+
+ EventHandler[EnterNotify] = HandleUnknown;
+ EventHandler[LeaveNotify] = HandleUnknown;
+
+ if (!Scr->NoGrabServer || !Scr->OpaqueMove) {
+ XGrabServer(dpy);
+ }
+ XGrabPointer(dpy, eventp->xbutton.root, True,
+ ButtonPressMask | ButtonReleaseMask |
+ ButtonMotionMask | PointerMotionMask, /* PointerMotionHintMask */
+ GrabModeAsync, GrabModeAsync,
+ Scr->Root, Scr->MoveCursor, CurrentTime);
+
+ if (context == C_ICON && tmp_win->icon_w)
+ {
+ w = tmp_win->icon_w;
+ DragX = eventp->xbutton.x;
+ DragY = eventp->xbutton.y;
+ moving_icon = TRUE;
+ }
+
+ else if (w != tmp_win->icon_w)
+ {
+ XTranslateCoordinates(dpy, w, tmp_win->frame,
+ eventp->xbutton.x,
+ eventp->xbutton.y,
+ &DragX, &DragY, &JunkChild);
+
+ w = tmp_win->frame;
+ }
+
+ DragWindow = None;
+
+ XGetGeometry(dpy, w, &JunkRoot, &origDragX, &origDragY,
+ (unsigned int *)&DragWidth, (unsigned int *)&DragHeight, &JunkBW,
+ &JunkDepth);
+
+ origX = eventp->xbutton.x_root;
+ origY = eventp->xbutton.y_root;
+ CurrentDragX = origDragX;
+ CurrentDragY = origDragY;
+
+ /*
+ * only do the constrained move if timer is set; need to check it
+ * in case of stupid or wicked fast servers
+ */
+ if (ConstrainedMoveTime &&
+ (eventp->xbutton.time - last_time) < ConstrainedMoveTime)
+ {
+ int width, height;
+
+ ConstMove = TRUE;
+ ConstMoveDir = MOVE_NONE;
+ ConstMoveX = eventp->xbutton.x_root - DragX - JunkBW;
+ ConstMoveY = eventp->xbutton.y_root - DragY - JunkBW;
+ width = DragWidth + 2 * JunkBW;
+ height = DragHeight + 2 * JunkBW;
+ ConstMoveXL = ConstMoveX + width/3;
+ ConstMoveXR = ConstMoveX + 2*(width/3);
+ ConstMoveYT = ConstMoveY + height/3;
+ ConstMoveYB = ConstMoveY + 2*(height/3);
+
+ XWarpPointer(dpy, None, w,
+ 0, 0, 0, 0, DragWidth/2, DragHeight/2);
+
+ XQueryPointer(dpy, w, &JunkRoot, &JunkChild,
+ &JunkX, &JunkY, &DragX, &DragY, &JunkMask);
+ }
+ last_time = eventp->xbutton.time;
+
+ if (!Scr->OpaqueMove)
+ {
+ InstallRootColormap();
+ if (!Scr->MoveDelta)
+ {
+ /*
+ * Draw initial outline. This was previously done the
+ * first time though the outer loop by dropping out of
+ * the XCheckMaskEvent inner loop down to one of the
+ * MoveOutline's below.
+ */
+ MoveOutline(rootw,
+ origDragX - JunkBW, origDragY - JunkBW,
+ DragWidth + 2 * JunkBW, DragHeight + 2 * JunkBW,
+ tmp_win->frame_bw,
+ moving_icon ? 0 : tmp_win->title_height);
+ /*
+ * This next line causes HandleReleaseNotify to call
+ * XRaiseWindow(). This is solely to preserve the
+ * previous behaviour that raises a window being moved
+ * on button release even if you never actually moved
+ * any distance (unless you move less than MoveDelta or
+ * NoRaiseMove is set or OpaqueMove is set).
+ */
+ DragWindow = w;
+ }
+ }
+
+ /*
+ * see if this is being done from the titlebar
+ */
+ fromtitlebar = belongs_to_twm_window (tmp_win, eventp->xbutton.window);
+
+ if (menuFromFrameOrWindowOrTitlebar) {
+ /* warp the pointer to the middle of the window */
+ XWarpPointer(dpy, None, Scr->Root, 0, 0, 0, 0,
+ origDragX + DragWidth / 2,
+ origDragY + DragHeight / 2);
+ XFlush(dpy);
+ }
+
+ while (TRUE)
+ {
+ long releaseEvent = menuFromFrameOrWindowOrTitlebar ?
+ ButtonPress : ButtonRelease;
+ long movementMask = menuFromFrameOrWindowOrTitlebar ?
+ PointerMotionMask : ButtonMotionMask;
+
+ /* block until there is an interesting event */
+ XMaskEvent(dpy, ButtonPressMask | ButtonReleaseMask |
+ EnterWindowMask | LeaveWindowMask |
+ ExposureMask | movementMask |
+ VisibilityChangeMask, &Event);
+
+ /* throw away enter and leave events until release */
+ if (Event.xany.type == EnterNotify ||
+ Event.xany.type == LeaveNotify) continue;
+
+ if (Event.type == MotionNotify) {
+ /* discard any extra motion events before a logical release */
+ while(XCheckMaskEvent(dpy,
+ movementMask | releaseEvent, &Event))
+ if (Event.type == releaseEvent)
+ break;
+ }
+
+ /* test to see if we have a second button press to abort move */
+ if (!menuFromFrameOrWindowOrTitlebar && !MovedFromKeyPress) {
+ if (Event.type == ButtonPress && DragWindow != None) {
+ if (Scr->OpaqueMove)
+ XMoveWindow (dpy, DragWindow, origDragX, origDragY);
+ else
+ MoveOutline(Scr->Root, 0, 0, 0, 0, 0, 0);
+ DragWindow = None;
+ }
+ }
+ if (fromtitlebar && Event.type == ButtonPress) {
+ fromtitlebar = False;
+ CurrentDragX = origX = Event.xbutton.x_root;
+ CurrentDragY = origY = Event.xbutton.y_root;
+ XTranslateCoordinates (dpy, rootw, tmp_win->frame,
+ origX, origY,
+ &DragX, &DragY, &JunkChild);
+ continue;
+ }
+
+ if (!DispatchEvent2 ()) continue;
+
+ if (Cancel)
+ {
+ WindowMoved = FALSE;
+ if (!Scr->OpaqueMove)
+ UninstallRootColormap();
+ return TRUE; /* XXX should this be FALSE? */
+ }
+ if (Event.type == releaseEvent)
+ {
+ MoveOutline(rootw, 0, 0, 0, 0, 0, 0);
+ if (moving_icon &&
+ ((CurrentDragX != origDragX ||
+ CurrentDragY != origDragY)))
+ tmp_win->icon_moved = TRUE;
+ if (!Scr->OpaqueMove && menuFromFrameOrWindowOrTitlebar)
+ XMoveWindow(dpy, DragWindow,
+ Event.xbutton.x_root - DragWidth / 2,
+ Event.xbutton.y_root - DragHeight / 2);
+ break;
+ }
+
+ /* something left to do only if the pointer moved */
+ if (Event.type != MotionNotify)
+ continue;
+
+ XQueryPointer(dpy, rootw, &(eventp->xmotion.root), &JunkChild,
+ &(eventp->xmotion.x_root), &(eventp->xmotion.y_root),
+ &JunkX, &JunkY, &JunkMask);
+
+ if (DragWindow == None &&
+ abs(eventp->xmotion.x_root - origX) < Scr->MoveDelta &&
+ abs(eventp->xmotion.y_root - origY) < Scr->MoveDelta)
+ continue;
+
+ WindowMoved = TRUE;
+ DragWindow = w;
+
+ if (!Scr->NoRaiseMove && Scr->OpaqueMove) /* can't restore... */
+ XRaiseWindow(dpy, DragWindow);
+
+ if (ConstMove)
+ {
+ switch (ConstMoveDir)
+ {
+ case MOVE_NONE:
+ if (eventp->xmotion.x_root < ConstMoveXL ||
+ eventp->xmotion.x_root > ConstMoveXR)
+ ConstMoveDir = MOVE_HORIZ;
+
+ if (eventp->xmotion.y_root < ConstMoveYT ||
+ eventp->xmotion.y_root > ConstMoveYB)
+ ConstMoveDir = MOVE_VERT;
+
+ XQueryPointer(dpy, DragWindow, &JunkRoot, &JunkChild,
+ &JunkX, &JunkY, &DragX, &DragY, &JunkMask);
+ break;
+
+ case MOVE_VERT:
+ ConstMoveY = eventp->xmotion.y_root - DragY - JunkBW;
+ break;
+
+ case MOVE_HORIZ:
+ ConstMoveX= eventp->xmotion.x_root - DragX - JunkBW;
+ break;
+ }
+
+ if (ConstMoveDir != MOVE_NONE)
+ {
+ int xl, yt, xr, yb, w, h;
+
+ xl = ConstMoveX;
+ yt = ConstMoveY;
+ w = DragWidth + 2 * JunkBW;
+ h = DragHeight + 2 * JunkBW;
+
+ if (Scr->DontMoveOff && MoveFunction != F_FORCEMOVE)
+ {
+ xr = xl + w;
+ yb = yt + h;
+
+ if (xl < 0)
+ xl = 0;
+ if (xr > Scr->MyDisplayWidth)
+ xl = Scr->MyDisplayWidth - w;
+
+ if (yt < 0)
+ yt = 0;
+ if (yb > Scr->MyDisplayHeight)
+ yt = Scr->MyDisplayHeight - h;
+ }
+ CurrentDragX = xl;
+ CurrentDragY = yt;
+ if (Scr->OpaqueMove)
+ XMoveWindow(dpy, DragWindow, xl, yt);
+ else
+ MoveOutline(eventp->xmotion.root, xl, yt, w, h,
+ tmp_win->frame_bw,
+ moving_icon ? 0 : tmp_win->title_height);
+ }
+ }
+ else if (DragWindow != None)
+ {
+ int xl, yt, xr, yb, w, h;
+ if (!menuFromFrameOrWindowOrTitlebar) {
+ xl = eventp->xmotion.x_root - DragX - JunkBW;
+ yt = eventp->xmotion.y_root - DragY - JunkBW;
+ }
+ else {
+ xl = eventp->xmotion.x_root - (DragWidth / 2);
+ yt = eventp->xmotion.y_root - (DragHeight / 2);
+ }
+ w = DragWidth + 2 * JunkBW;
+ h = DragHeight + 2 * JunkBW;
+
+ if (Scr->DontMoveOff && MoveFunction != F_FORCEMOVE)
+ {
+ xr = xl + w;
+ yb = yt + h;
+
+ if (xl < 0)
+ xl = 0;
+ if (xr > Scr->MyDisplayWidth)
+ xl = Scr->MyDisplayWidth - w;
+
+ if (yt < 0)
+ yt = 0;
+ if (yb > Scr->MyDisplayHeight)
+ yt = Scr->MyDisplayHeight - h;
+ }
+
+ CurrentDragX = xl;
+ CurrentDragY = yt;
+ if (Scr->OpaqueMove)
+ XMoveWindow(dpy, DragWindow, xl, yt);
+ else
+ MoveOutline(eventp->xmotion.root, xl, yt, w, h,
+ tmp_win->frame_bw,
+ moving_icon ? 0 : tmp_win->title_height);
+ }
+
+ }
+ MovedFromKeyPress = False;
+
+
+ if (!Scr->OpaqueMove && DragWindow == None)
+ UninstallRootColormap();
+
+ break;
+
+ case F_FUNCTION:
+ {
+ MenuRoot *mroot;
+ MenuItem *mitem;
+
+ if ((mroot = FindMenuRoot(action)) == NULL)
+ {
+ fprintf (stderr, "%s: couldn't find function \"%s\"\n",
+ ProgramName, action);
+ return TRUE;
+ }
+
+ if (NeedToDefer(mroot) && DeferExecution(context, func, Scr->SelectCursor))
+ return TRUE;
+ else
+ {
+ for (mitem = mroot->first; mitem != NULL; mitem = mitem->next)
+ {
+ if (!ExecuteFunction (mitem->func, mitem->action, w,
+ tmp_win, eventp, context, pulldown))
+ break;
+ }
+ }
+ }
+ break;
+
+ case F_DEICONIFY:
+ case F_ICONIFY:
+ if (DeferExecution(context, func, Scr->SelectCursor))
+ return TRUE;
+
+ if (tmp_win->icon)
+ {
+ DeIconify(tmp_win);
+ }
+ else if (func == F_ICONIFY)
+ {
+ Iconify (tmp_win, eventp->xbutton.x_root - 5,
+ eventp->xbutton.y_root - 5);
+ }
+ break;
+
+ case F_RAISELOWER:
+ if (DeferExecution(context, func, Scr->SelectCursor))
+ return TRUE;
+
+ if (!WindowMoved) {
+ XWindowChanges xwc;
+
+ xwc.stack_mode = Opposite;
+ if (w != tmp_win->icon_w)
+ w = tmp_win->frame;
+ XConfigureWindow (dpy, w, CWStackMode, &xwc);
+ }
+ break;
+
+ case F_RAISE:
+ if (DeferExecution(context, func, Scr->SelectCursor))
+ return TRUE;
+
+ /* check to make sure raise is not from the WindowFunction */
+ if (w == tmp_win->icon_w && Context != C_ROOT)
+ XRaiseWindow(dpy, tmp_win->icon_w);
+ else
+ XRaiseWindow(dpy, tmp_win->frame);
+
+ break;
+
+ case F_LOWER:
+ if (DeferExecution(context, func, Scr->SelectCursor))
+ return TRUE;
+
+ if (w == tmp_win->icon_w)
+ XLowerWindow(dpy, tmp_win->icon_w);
+ else
+ XLowerWindow(dpy, tmp_win->frame);
+
+ break;
+
+ case F_FOCUS:
+ if (DeferExecution(context, func, Scr->SelectCursor))
+ return TRUE;
+
+ if (tmp_win->icon == FALSE)
+ {
+ if (!Scr->FocusRoot && Scr->Focus == tmp_win)
+ {
+ FocusOnRoot();
+ }
+ else
+ {
+ if (Scr->Focus != NULL) {
+ SetBorder (Scr->Focus, False);
+ if (Scr->Focus->hilite_w)
+ XUnmapWindow (dpy, Scr->Focus->hilite_w);
+ }
+
+ InstallWindowColormaps (0, tmp_win);
+ if (tmp_win->hilite_w) XMapWindow (dpy, tmp_win->hilite_w);
+ SetBorder (tmp_win, True);
+ SetFocus (tmp_win, eventp->xbutton.time);
+ Scr->FocusRoot = FALSE;
+ Scr->Focus = tmp_win;
+ }
+ }
+ break;
+
+ case F_DESTROY:
+ if (DeferExecution(context, func, Scr->DestroyCursor))
+ return TRUE;
+
+ if (tmp_win->iconmgr)
+ Bell(XkbBI_MinorError,0,tmp_win->w);
+ else
+ XKillClient(dpy, tmp_win->w);
+ break;
+
+ case F_DELETE:
+ if (DeferExecution(context, func, Scr->DestroyCursor))
+ return TRUE;
+
+ if (tmp_win->iconmgr) /* don't send ourself a message */
+ HideIconManager ();
+ else if (tmp_win->protocols & DoesWmDeleteWindow)
+ SendDeleteWindowMessage (tmp_win, LastTimestamp());
+ else
+ Bell(XkbBI_MinorError,0,tmp_win->w);
+ break;
+
+ case F_SAVEYOURSELF:
+ if (DeferExecution (context, func, Scr->SelectCursor))
+ return TRUE;
+
+ if (tmp_win->protocols & DoesWmSaveYourself)
+ SendSaveYourselfMessage (tmp_win, LastTimestamp());
+ else
+ Bell(XkbBI_MinorError,0,tmp_win->w);
+ break;
+
+ case F_CIRCLEUP:
+ XCirculateSubwindowsUp(dpy, Scr->Root);
+ break;
+
+ case F_CIRCLEDOWN:
+ XCirculateSubwindowsDown(dpy, Scr->Root);
+ break;
+
+ case F_EXEC:
+ PopDownMenu();
+ if (!Scr->NoGrabServer) {
+ XUngrabServer (dpy);
+ XSync (dpy, 0);
+ }
+ Execute(action);
+ break;
+
+ case F_UNFOCUS:
+ FocusOnRoot();
+ break;
+
+ case F_CUT:
+ strcpy(tmp, action);
+ strcat(tmp, "\n");
+ XStoreBytes(dpy, tmp, strlen(tmp));
+ break;
+
+ case F_CUTFILE:
+ ptr = XFetchBytes(dpy, &count);
+ if (ptr) {
+ if (sscanf (ptr, "%s", tmp) == 1) {
+ XFree (ptr);
+ ptr = ExpandFilename(tmp);
+ if (ptr) {
+ fd = open (ptr, 0);
+ if (fd >= 0) {
+ count = read (fd, buff, MAX_FILE_SIZE - 1);
+ if (count > 0) XStoreBytes (dpy, buff, count);
+ close(fd);
+ } else {
+ fprintf (stderr,
+ "%s: unable to open cut file \"%s\"\n",
+ ProgramName, tmp);
+ }
+ if (ptr != tmp) free (ptr);
+ }
+ } else {
+ XFree(ptr);
+ }
+ } else {
+ fprintf(stderr, "%s: cut buffer is empty\n", ProgramName);
+ }
+ break;
+
+ case F_WARPTOSCREEN:
+ {
+ if (strcmp (action, WARPSCREEN_NEXT) == 0) {
+ WarpToScreen (Scr->screen + 1, 1);
+ } else if (strcmp (action, WARPSCREEN_PREV) == 0) {
+ WarpToScreen (Scr->screen - 1, -1);
+ } else if (strcmp (action, WARPSCREEN_BACK) == 0) {
+ WarpToScreen (PreviousScreen, 0);
+ } else {
+ WarpToScreen (atoi (action), 0);
+ }
+ }
+ break;
+
+ case F_COLORMAP:
+ {
+ if (strcmp (action, COLORMAP_NEXT) == 0) {
+ BumpWindowColormap (tmp_win, 1);
+ } else if (strcmp (action, COLORMAP_PREV) == 0) {
+ BumpWindowColormap (tmp_win, -1);
+ } else {
+ BumpWindowColormap (tmp_win, 0);
+ }
+ }
+ break;
+
+ case F_WARPTO:
+ {
+ register TwmWindow *t;
+ int len;
+
+ len = strlen(action);
+
+ for (t = Scr->TwmRoot.next; t != NULL; t = t->next) {
+ if (!strncmp(action, t->name, len))
+ if (WarpThere(t)) break;
+ }
+ if (!t) {
+ for (t = Scr->TwmRoot.next; t != NULL; t = t->next) {
+ if (!strncmp(action, t->class.res_name, len))
+ if (WarpThere(t)) break;
+ }
+ if (!t) {
+ for (t = Scr->TwmRoot.next; t != NULL; t = t->next) {
+ if (!strncmp(action, t->class.res_class, len))
+ if (WarpThere(t)) break;
+ }
+ }
+ }
+
+ if (!t)
+ Bell(XkbBI_MinorError,0,None);
+ }
+ break;
+
+ case F_WARPTOICONMGR:
+ {
+ TwmWindow *t;
+ int len;
+ Window raisewin = None, iconwin = None;
+
+ len = strlen(action);
+ if (len == 0) {
+ if (tmp_win && tmp_win->list) {
+ raisewin = tmp_win->list->iconmgr->twm_win->frame;
+ iconwin = tmp_win->list->icon;
+ } else if (Scr->iconmgr.active) {
+ raisewin = Scr->iconmgr.twm_win->frame;
+ iconwin = Scr->iconmgr.active->w;
+ }
+ } else {
+ for (t = Scr->TwmRoot.next; t != NULL; t = t->next) {
+ if (strncmp (action, t->icon_name, len) == 0) {
+ if (t->list && t->list->iconmgr->twm_win->mapped) {
+ raisewin = t->list->iconmgr->twm_win->frame;
+ iconwin = t->list->icon;
+ break;
+ }
+ }
+ }
+ }
+
+ if (raisewin) {
+ XRaiseWindow (dpy, raisewin);
+ XWarpPointer (dpy, None, iconwin, 0,0,0,0, 5, 5);
+ } else {
+ Bell(XkbBI_MinorError,0,None);
+ }
+ }
+ break;
+
+ case F_WARPRING:
+ switch (action[0]) {
+ case 'n':
+ WarpAlongRing (&eventp->xbutton, True);
+ break;
+ case 'p':
+ WarpAlongRing (&eventp->xbutton, False);
+ break;
+ default:
+ Bell(XkbBI_MinorError,0,None);
+ break;
+ }
+ break;
+
+ case F_FILE:
+ action = ExpandFilename(action);
+ fd = open(action, 0);
+ if (fd >= 0)
+ {
+ count = read(fd, buff, MAX_FILE_SIZE - 1);
+ if (count > 0)
+ XStoreBytes(dpy, buff, count);
+
+ close(fd);
+ }
+ else
+ {
+ fprintf (stderr, "%s: unable to open file \"%s\"\n",
+ ProgramName, action);
+ }
+ break;
+
+ case F_REFRESH:
+ {
+ XSetWindowAttributes attributes;
+ unsigned long valuemask;
+
+ valuemask = (CWBackPixel | CWBackingStore | CWSaveUnder);
+ attributes.background_pixel = Scr->Black;
+ attributes.backing_store = NotUseful;
+ attributes.save_under = False;
+ w = XCreateWindow (dpy, Scr->Root, 0, 0,
+ (unsigned int) Scr->MyDisplayWidth,
+ (unsigned int) Scr->MyDisplayHeight,
+ (unsigned int) 0,
+ CopyFromParent, (unsigned int) CopyFromParent,
+ (Visual *) CopyFromParent, valuemask,
+ &attributes);
+ XMapWindow (dpy, w);
+ XDestroyWindow (dpy, w);
+ XFlush (dpy);
+ }
+ break;
+
+ case F_WINREFRESH:
+ if (DeferExecution(context, func, Scr->SelectCursor))
+ return TRUE;
+
+ if (context == C_ICON && tmp_win->icon_w)
+ w = XCreateSimpleWindow(dpy, tmp_win->icon_w,
+ 0, 0, 9999, 9999, 0, Scr->Black, Scr->Black);
+ else
+ w = XCreateSimpleWindow(dpy, tmp_win->frame,
+ 0, 0, 9999, 9999, 0, Scr->Black, Scr->Black);
+
+ XMapWindow(dpy, w);
+ XDestroyWindow(dpy, w);
+ XFlush(dpy);
+ break;
+
+ case F_QUIT:
+ Done();
+ break;
+
+ case F_PRIORITY:
+ if (HasSync)
+ {
+ if (DeferExecution (context, func, Scr->SelectCursor))
+ return TRUE;
+ (void)XSyncSetPriority(dpy, tmp_win->w, atoi(action));
+ }
+ break;
+ }
+
+ if (ButtonPressed == -1) XUngrabPointer(dpy, CurrentTime);
+ return do_next_action;
+}
+
+
+
+/***********************************************************************
+ *
+ * Procedure:
+ * DeferExecution - defer the execution of a function to the
+ * next button press if the context is C_ROOT
+ *
+ * Inputs:
+ * context - the context in which the mouse button was pressed
+ * func - the function to defer
+ * cursor - the cursor to display while waiting
+ *
+ ***********************************************************************
+ */
+
+int
+DeferExecution(context, func, cursor)
+int context, func;
+Cursor cursor;
+{
+ if (context == C_ROOT)
+ {
+ LastCursor = cursor;
+ XGrabPointer(dpy, Scr->Root, True,
+ ButtonPressMask | ButtonReleaseMask,
+ GrabModeAsync, GrabModeAsync,
+ Scr->Root, cursor, CurrentTime);
+
+ RootFunction = func;
+
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+
+
+/***********************************************************************
+ *
+ * Procedure:
+ * ReGrab - regrab the pointer with the LastCursor;
+ *
+ ***********************************************************************
+ */
+
+ReGrab()
+{
+ XGrabPointer(dpy, Scr->Root, True,
+ ButtonPressMask | ButtonReleaseMask,
+ GrabModeAsync, GrabModeAsync,
+ Scr->Root, LastCursor, CurrentTime);
+}
+
+
+
+/***********************************************************************
+ *
+ * Procedure:
+ * NeedToDefer - checks each function in the list to see if it
+ * is one that needs to be defered.
+ *
+ * Inputs:
+ * root - the menu root to check
+ *
+ ***********************************************************************
+ */
+
+NeedToDefer(root)
+MenuRoot *root;
+{
+ MenuItem *mitem;
+
+ for (mitem = root->first; mitem != NULL; mitem = mitem->next)
+ {
+ switch (mitem->func)
+ {
+ case F_IDENTIFY:
+ case F_RESIZE:
+ case F_MOVE:
+ case F_FORCEMOVE:
+ case F_DEICONIFY:
+ case F_ICONIFY:
+ case F_RAISELOWER:
+ case F_RAISE:
+ case F_LOWER:
+ case F_FOCUS:
+ case F_DESTROY:
+ case F_WINREFRESH:
+ case F_ZOOM:
+ case F_FULLZOOM:
+ case F_HORIZOOM:
+ case F_RIGHTZOOM:
+ case F_LEFTZOOM:
+ case F_TOPZOOM:
+ case F_BOTTOMZOOM:
+ case F_AUTORAISE:
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+
+/***********************************************************************
+ *
+ * Procedure:
+ * Execute - execute the string by /bin/sh
+ *
+ * Inputs:
+ * s - the string containing the command
+ *
+ ***********************************************************************
+ */
+
+#if defined(sun) && defined(SVR4)
+static int System (s)
+ char *s;
+{
+ int pid, status;
+ if ((pid = fork ()) == 0) {
+ (void) setpgrp();
+ execl ("/bin/sh", "sh", "-c", s, 0);
+ } else
+ waitpid (pid, &status, 0);
+ return status;
+}
+#define system(s) System(s)
+#endif
+
+void
+Execute(s)
+ char *s;
+{
+ static char buf[256];
+ char *ds = DisplayString (dpy);
+ char *colon, *dot1;
+ char oldDisplay[256];
+ char *doisplay;
+ int restorevar = 0;
+
+ oldDisplay[0] = '\0';
+ doisplay=getenv("DISPLAY");
+ if (doisplay)
+ strcpy (oldDisplay, doisplay);
+
+ /*
+ * Build a display string using the current screen number, so that
+ * X programs which get fired up from a menu come up on the screen
+ * that they were invoked from, unless specifically overridden on
+ * their command line.
+ */
+ colon = strrchr (ds, ':');
+ if (colon) { /* if host[:]:dpy */
+ strcpy (buf, "DISPLAY=");
+ strcat (buf, ds);
+ colon = buf + 8 + (colon - ds); /* use version in buf */
+ dot1 = strchr (colon, '.'); /* first period after colon */
+ if (!dot1) dot1 = colon + strlen (colon); /* if not there, append */
+ (void) sprintf (dot1, ".%d", Scr->screen);
+ putenv (buf);
+ restorevar = 1;
+ }
+
+ (void) system (s);
+
+ if (restorevar) { /* why bother? */
+ (void) sprintf (buf, "DISPLAY=%s", oldDisplay);
+ putenv (buf);
+ }
+}
+
+
+
+/***********************************************************************
+ *
+ * Procedure:
+ * FocusOnRoot - put input focus on the root window
+ *
+ ***********************************************************************
+ */
+
+void
+FocusOnRoot()
+{
+ SetFocus ((TwmWindow *) NULL, LastTimestamp());
+ if (Scr->Focus != NULL)
+ {
+ SetBorder (Scr->Focus, False);
+ if (Scr->Focus->hilite_w) XUnmapWindow (dpy, Scr->Focus->hilite_w);
+ }
+ InstallWindowColormaps(0, &Scr->TwmRoot);
+ Scr->Focus = NULL;
+ Scr->FocusRoot = TRUE;
+}
+
+DeIconify(tmp_win)
+TwmWindow *tmp_win;
+{
+ TwmWindow *t;
+
+ /* de-iconify the main window */
+ if (tmp_win->icon)
+ {
+ if (tmp_win->icon_on)
+ Zoom(tmp_win->icon_w, tmp_win->frame);
+ else if (tmp_win->group != (Window) 0)
+ {
+ for (t = Scr->TwmRoot.next; t != NULL; t = t->next)
+ {
+ if (tmp_win->group == t->w && t->icon_on)
+ {
+ Zoom(t->icon_w, tmp_win->frame);
+ break;
+ }
+ }
+ }
+ }
+
+ XMapWindow(dpy, tmp_win->w);
+ tmp_win->mapped = TRUE;
+ if (Scr->NoRaiseDeicon)
+ XMapWindow(dpy, tmp_win->frame);
+ else
+ XMapRaised(dpy, tmp_win->frame);
+ SetMapStateProp(tmp_win, NormalState);
+
+ if (tmp_win->icon_w) {
+ XUnmapWindow(dpy, tmp_win->icon_w);
+ IconDown (tmp_win);
+ }
+ if (tmp_win->list)
+ XUnmapWindow(dpy, tmp_win->list->icon);
+ if ((Scr->WarpCursor ||
+ LookInList(Scr->WarpCursorL, tmp_win->full_name, &tmp_win->class)) &&
+ tmp_win->icon)
+ WarpToWindow (tmp_win);
+ tmp_win->icon = FALSE;
+ tmp_win->icon_on = FALSE;
+
+
+ /* now de-iconify transients */
+ for (t = Scr->TwmRoot.next; t != NULL; t = t->next)
+ {
+ if (t->transient && t->transientfor == tmp_win->w)
+ {
+ if (t->icon_on)
+ Zoom(t->icon_w, t->frame);
+ else
+ Zoom(tmp_win->icon_w, t->frame);
+
+ XMapWindow(dpy, t->w);
+ t->mapped = TRUE;
+ if (Scr->NoRaiseDeicon)
+ XMapWindow(dpy, t->frame);
+ else
+ XMapRaised(dpy, t->frame);
+ SetMapStateProp(t, NormalState);
+
+ if (t->icon_w) {
+ XUnmapWindow(dpy, t->icon_w);
+ IconDown (t);
+ }
+ if (t->list) XUnmapWindow(dpy, t->list->icon);
+ t->icon = FALSE;
+ t->icon_on = FALSE;
+ }
+ }
+
+ XSync (dpy, 0);
+}
+
+
+
+Iconify(tmp_win, def_x, def_y)
+TwmWindow *tmp_win;
+int def_x, def_y;
+{
+ TwmWindow *t;
+ int iconify;
+ XWindowAttributes winattrs;
+ unsigned long eventMask;
+
+ iconify = ((!tmp_win->iconify_by_unmapping) || tmp_win->transient);
+ if (iconify)
+ {
+ if (tmp_win->icon_w == (Window) 0)
+ CreateIconWindow(tmp_win, def_x, def_y);
+ else
+ IconUp(tmp_win);
+ XMapRaised(dpy, tmp_win->icon_w);
+ }
+ if (tmp_win->list)
+ XMapWindow(dpy, tmp_win->list->icon);
+
+ XGetWindowAttributes(dpy, tmp_win->w, &winattrs);
+ eventMask = winattrs.your_event_mask;
+
+ /* iconify transients first */
+ for (t = Scr->TwmRoot.next; t != NULL; t = t->next)
+ {
+ if (t->transient && t->transientfor == tmp_win->w)
+ {
+ if (iconify)
+ {
+ if (t->icon_on)
+ Zoom(t->icon_w, tmp_win->icon_w);
+ else
+ Zoom(t->frame, tmp_win->icon_w);
+ }
+
+ /*
+ * Prevent the receipt of an UnmapNotify, since that would
+ * cause a transition to the Withdrawn state.
+ */
+ t->mapped = FALSE;
+ XSelectInput(dpy, t->w, eventMask & ~StructureNotifyMask);
+ XUnmapWindow(dpy, t->w);
+ XSelectInput(dpy, t->w, eventMask);
+ XUnmapWindow(dpy, t->frame);
+ if (t->icon_w)
+ XUnmapWindow(dpy, t->icon_w);
+ SetMapStateProp(t, IconicState);
+ SetBorder (t, False);
+ if (t == Scr->Focus)
+ {
+ SetFocus ((TwmWindow *) NULL, LastTimestamp());
+ Scr->Focus = NULL;
+ Scr->FocusRoot = TRUE;
+ }
+ if (t->list) XMapWindow(dpy, t->list->icon);
+ t->icon = TRUE;
+ t->icon_on = FALSE;
+ }
+ }
+
+ if (iconify)
+ Zoom(tmp_win->frame, tmp_win->icon_w);
+
+ /*
+ * Prevent the receipt of an UnmapNotify, since that would
+ * cause a transition to the Withdrawn state.
+ */
+ tmp_win->mapped = FALSE;
+ XSelectInput(dpy, tmp_win->w, eventMask & ~StructureNotifyMask);
+ XUnmapWindow(dpy, tmp_win->w);
+ XSelectInput(dpy, tmp_win->w, eventMask);
+ XUnmapWindow(dpy, tmp_win->frame);
+ SetMapStateProp(tmp_win, IconicState);
+
+ SetBorder (tmp_win, False);
+ if (tmp_win == Scr->Focus)
+ {
+ SetFocus ((TwmWindow *) NULL, LastTimestamp());
+ Scr->Focus = NULL;
+ Scr->FocusRoot = TRUE;
+ }
+ tmp_win->icon = TRUE;
+ if (iconify)
+ tmp_win->icon_on = TRUE;
+ else
+ tmp_win->icon_on = FALSE;
+ XSync (dpy, 0);
+}
+
+
+
+static void Identify (t)
+TwmWindow *t;
+{
+ int i, n, twidth, width, height;
+ int x, y;
+ unsigned int wwidth, wheight, bw, depth;
+ Window junk;
+ int px, py, dummy;
+ unsigned udummy;
+
+ n = 0;
+ (void) sprintf(Info[n++], "Twm version: %s", Version);
+ Info[n++][0] = '\0';
+
+ if (t) {
+ XGetGeometry (dpy, t->w, &JunkRoot, &JunkX, &JunkY,
+ &wwidth, &wheight, &bw, &depth);
+ (void) XTranslateCoordinates (dpy, t->w, Scr->Root, 0, 0,
+ &x, &y, &junk);
+ (void) sprintf(Info[n++], "Name = \"%s\"", t->full_name);
+ (void) sprintf(Info[n++], "Class.res_name = \"%s\"", t->class.res_name);
+ (void) sprintf(Info[n++], "Class.res_class = \"%s\"", t->class.res_class);
+ Info[n++][0] = '\0';
+ (void) sprintf(Info[n++], "Geometry/root = %dx%d+%d+%d", wwidth, wheight,
+ x, y);
+ (void) sprintf(Info[n++], "Border width = %d", bw);
+ (void) sprintf(Info[n++], "Depth = %d", depth);
+ if (HasSync)
+ {
+ int priority;
+ (void)XSyncGetPriority(dpy, t->w, &priority);
+ (void) sprintf(Info[n++], "Priority = %d", priority);
+ }
+ }
+
+ Info[n++][0] = '\0';
+ (void) sprintf(Info[n++], "Click to dismiss....");
+
+ /* figure out the width and height of the info window */
+ height = n * (Scr->DefaultFont.height+2);
+ width = 1;
+ for (i = 0; i < n; i++)
+ {
+ twidth = XTextWidth(Scr->DefaultFont.font, Info[i],
+ strlen(Info[i]));
+ if (twidth > width)
+ width = twidth;
+ }
+ if (InfoLines) XUnmapWindow(dpy, Scr->InfoWindow);
+
+ width += 10; /* some padding */
+ if (XQueryPointer (dpy, Scr->Root, &JunkRoot, &JunkChild, &px, &py,
+ &dummy, &dummy, &udummy)) {
+ px -= (width / 2);
+ py -= (height / 3);
+ if (px + width + BW2 >= Scr->MyDisplayWidth)
+ px = Scr->MyDisplayWidth - width - BW2;
+ if (py + height + BW2 >= Scr->MyDisplayHeight)
+ py = Scr->MyDisplayHeight - height - BW2;
+ if (px < 0) px = 0;
+ if (py < 0) py = 0;
+ } else {
+ px = py = 0;
+ }
+ XMoveResizeWindow(dpy, Scr->InfoWindow, px, py, width, height);
+ XMapRaised(dpy, Scr->InfoWindow);
+ InfoLines = n;
+}
+
+
+
+SetMapStateProp(tmp_win, state)
+TwmWindow *tmp_win;
+int state;
+{
+ unsigned long data[2]; /* "suggested" by ICCCM version 1 */
+
+ data[0] = (unsigned long) state;
+ data[1] = (unsigned long) (tmp_win->iconify_by_unmapping ? None :
+ tmp_win->icon_w);
+
+ XChangeProperty (dpy, tmp_win->w, _XA_WM_STATE, _XA_WM_STATE, 32,
+ PropModeReplace, (unsigned char *) data, 2);
+}
+
+
+
+Bool GetWMState (w, statep, iwp)
+ Window w;
+ int *statep;
+ Window *iwp;
+{
+ Atom actual_type;
+ int actual_format;
+ unsigned long nitems, bytesafter;
+ unsigned long *datap = NULL;
+ Bool retval = False;
+
+ if (XGetWindowProperty (dpy, w, _XA_WM_STATE, 0L, 2L, False, _XA_WM_STATE,
+ &actual_type, &actual_format, &nitems, &bytesafter,
+ (unsigned char **) &datap) != Success || !datap)
+ return False;
+
+ if (nitems <= 2) { /* "suggested" by ICCCM version 1 */
+ *statep = (int) datap[0];
+ *iwp = (Window) datap[1];
+ retval = True;
+ }
+
+ XFree ((char *) datap);
+ return retval;
+}
+
+
+
+WarpToScreen (n, inc)
+ int n, inc;
+{
+ Window dumwin;
+ int x, y, dumint;
+ unsigned int dummask;
+ ScreenInfo *newscr = NULL;
+
+ while (!newscr) {
+ /* wrap around */
+ if (n < 0)
+ n = NumScreens - 1;
+ else if (n >= NumScreens)
+ n = 0;
+
+ newscr = ScreenList[n];
+ if (!newscr) { /* make sure screen is managed */
+ if (inc) { /* walk around the list */
+ n += inc;
+ continue;
+ }
+ fprintf (stderr, "%s: unable to warp to unmanaged screen %d\n",
+ ProgramName, n);
+ Bell(XkbBI_MinorError,0,None);
+ return;
+ }
+ }
+
+ if (Scr->screen == n) return; /* already on that screen */
+
+ PreviousScreen = Scr->screen;
+ XQueryPointer (dpy, Scr->Root, &dumwin, &dumwin, &x, &y,
+ &dumint, &dumint, &dummask);
+
+ XWarpPointer (dpy, None, newscr->Root, 0, 0, 0, 0, x, y);
+ return;
+}
+
+
+
+
+/*
+ * BumpWindowColormap - rotate our internal copy of WM_COLORMAP_WINDOWS
+ */
+
+BumpWindowColormap (tmp, inc)
+ TwmWindow *tmp;
+ int inc;
+{
+ int i, j, previously_installed;
+ ColormapWindow **cwins;
+
+ if (!tmp) return;
+
+ if (inc && tmp->cmaps.number_cwins > 0) {
+ cwins = (ColormapWindow **) malloc(sizeof(ColormapWindow *)*
+ tmp->cmaps.number_cwins);
+ if (cwins) {
+ if ((previously_installed =
+ /* SUPPRESS 560 */(Scr->cmapInfo.cmaps == &tmp->cmaps &&
+ tmp->cmaps.number_cwins))) {
+ for (i = tmp->cmaps.number_cwins; i-- > 0; )
+ tmp->cmaps.cwins[i]->colormap->state = 0;
+ }
+
+ for (i = 0; i < tmp->cmaps.number_cwins; i++) {
+ j = i - inc;
+ if (j >= tmp->cmaps.number_cwins)
+ j -= tmp->cmaps.number_cwins;
+ else if (j < 0)
+ j += tmp->cmaps.number_cwins;
+ cwins[j] = tmp->cmaps.cwins[i];
+ }
+
+ free((char *) tmp->cmaps.cwins);
+
+ tmp->cmaps.cwins = cwins;
+
+ if (tmp->cmaps.number_cwins > 1)
+ bzero (tmp->cmaps.scoreboard,
+ ColormapsScoreboardLength(&tmp->cmaps));
+
+ if (previously_installed)
+ InstallWindowColormaps(PropertyNotify, (TwmWindow *) NULL);
+ }
+ } else
+ FetchWmColormapWindows (tmp);
+}
+
+
+
+HideIconManager ()
+{
+ SetMapStateProp (Scr->iconmgr.twm_win, WithdrawnState);
+ XUnmapWindow(dpy, Scr->iconmgr.twm_win->frame);
+ if (Scr->iconmgr.twm_win->icon_w)
+ XUnmapWindow (dpy, Scr->iconmgr.twm_win->icon_w);
+ Scr->iconmgr.twm_win->mapped = FALSE;
+ Scr->iconmgr.twm_win->icon = TRUE;
+}
+
+
+
+
+SetBorder (tmp, onoroff)
+ TwmWindow *tmp;
+ Bool onoroff;
+{
+ if (tmp->highlight) {
+ if (onoroff) {
+ XSetWindowBorder (dpy, tmp->frame, tmp->border);
+ if (tmp->title_w)
+ XSetWindowBorder (dpy, tmp->title_w, tmp->border);
+ } else {
+ XSetWindowBorderPixmap (dpy, tmp->frame, tmp->gray);
+ if (tmp->title_w)
+ XSetWindowBorderPixmap (dpy, tmp->title_w, tmp->gray);
+ }
+ }
+}
+
+
+
+DestroyMenu (menu)
+ MenuRoot *menu;
+{
+ MenuItem *item;
+
+ if (menu->w) {
+ XDeleteContext (dpy, menu->w, MenuContext);
+ XDeleteContext (dpy, menu->w, ScreenContext);
+ if (Scr->Shadow) XDestroyWindow (dpy, menu->shadow);
+ XDestroyWindow(dpy, menu->w);
+ }
+
+ for (item = menu->first; item; ) {
+ MenuItem *tmp = item;
+ item = item->next;
+ free ((char *) tmp);
+ }
+}
+
+
+
+/*
+ * warping routines
+ */
+void WarpAlongRing (ev, forward)
+ XButtonEvent *ev;
+ Bool forward;
+{
+ TwmWindow *r, *head;
+
+ if (Scr->RingLeader)
+ head = Scr->RingLeader;
+ else if (!(head = Scr->Ring))
+ return;
+
+ if (forward) {
+ for (r = head->ring.next; r != head; r = r->ring.next) {
+ if (!r || r->mapped) break;
+ }
+ } else {
+ for (r = head->ring.prev; r != head; r = r->ring.prev) {
+ if (!r || r->mapped) break;
+ }
+ }
+
+ if (r && r != head) {
+ TwmWindow *p = Scr->RingLeader, *t;
+
+ Scr->RingLeader = r;
+ WarpToWindow (r);
+
+ if (p && p->mapped &&
+ XFindContext (dpy, ev->window, TwmContext, (caddr_t *)&t) == XCSUCCESS &&
+ p == t) {
+ p->ring.cursor_valid = True;
+ p->ring.curs_x = ev->x_root - t->frame_x;
+ p->ring.curs_y = ev->y_root - t->frame_y;
+ if (p->ring.curs_x < -p->frame_bw ||
+ p->ring.curs_x >= p->frame_width + p->frame_bw ||
+ p->ring.curs_y < -p->frame_bw ||
+ p->ring.curs_y >= p->frame_height + p->frame_bw) {
+ /* somehow out of window */
+ p->ring.curs_x = p->frame_width / 2;
+ p->ring.curs_y = p->frame_height / 2;
+ }
+ }
+ }
+}
+
+
+
+void WarpToWindow (t)
+ TwmWindow *t;
+{
+ int x, y;
+
+ if (t->auto_raise || !Scr->NoRaiseWarp) AutoRaiseWindow (t);
+ if (t->ring.cursor_valid) {
+ x = t->ring.curs_x;
+ y = t->ring.curs_y;
+ } else {
+ x = t->frame_width / 2;
+ y = t->frame_height / 2;
+ }
+ XWarpPointer (dpy, None, t->frame, 0, 0, 0, 0, x, y);
+}
+
+
+
+
+/*
+ * ICCCM Client Messages - Section 4.2.8 of the ICCCM dictates that all
+ * client messages will have the following form:
+ *
+ * event type ClientMessage
+ * message type _XA_WM_PROTOCOLS
+ * window tmp->w
+ * format 32
+ * data[0] message atom
+ * data[1] time stamp
+ */
+static void send_clientmessage (w, a, timestamp)
+ Window w;
+ Atom a;
+ Time timestamp;
+{
+ XClientMessageEvent ev;
+
+ ev.type = ClientMessage;
+ ev.window = w;
+ ev.message_type = _XA_WM_PROTOCOLS;
+ ev.format = 32;
+ ev.data.l[0] = a;
+ ev.data.l[1] = timestamp;
+ XSendEvent (dpy, w, False, 0L, (XEvent *) &ev);
+}
+
+SendDeleteWindowMessage (tmp, timestamp)
+ TwmWindow *tmp;
+ Time timestamp;
+{
+ send_clientmessage (tmp->w, _XA_WM_DELETE_WINDOW, timestamp);
+}
+
+SendSaveYourselfMessage (tmp, timestamp)
+ TwmWindow *tmp;
+ Time timestamp;
+{
+ send_clientmessage (tmp->w, _XA_WM_SAVE_YOURSELF, timestamp);
+}
+
+
+SendTakeFocusMessage (tmp, timestamp)
+ TwmWindow *tmp;
+ Time timestamp;
+{
+ send_clientmessage (tmp->w, _XA_WM_TAKE_FOCUS, timestamp);
+}