|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T s
Length: 44161 (0xac81) Types: TextFile Names: »sxMenu.c«
└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki └─⟦526ad3590⟧ »EUUGD11/gnu-31mar87/X.V10.R4.tar.Z« └─⟦2109abc41⟧ └─⟦this⟧ »./X.V10R4/Toolkit/Sx/code/sxMenu.c«
/* * $Source: /u1/Sx.new/code/RCS/sxMenu.c,v $ * $Header: sxMenu.c,v 1.1 86/12/03 16:10:13 swick Exp $ */ #ifndef lint static char *rcsid_sxMenu_c = "$Header: sxMenu.c,v 1.1 86/12/03 16:10:13 swick Exp $"; #endif lint /* * sxMenu.c -- * * This file implements pull-down menus using the facilities of the * X window package. * * Copyright (C) 1986 Regents of the University of California. * 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. The University of California * makes no representations about the suitability of this * software for any purpose. It is provided "as is" without * express or implied warranty. */ #ifndef lint static char rcsid[] = "$Header: sxMenu.c,v 1.1 86/12/03 16:10:13 swick Exp $ SPRITE (Berkeley)"; #endif not lint #include <sys/types.h> #include <X/Xlib.h> #include "sprite.h" #include "mem.h" #include "string.h" #include "sx.h" #include "sxInt.h" /* * The structure below is used internally to this file to describe * a menu entry. */ typedef struct { char *leftText; /* Text to display left-justified in * menu entry, with an extra space character * added at each end. NULL means no text. */ int leftSize; /* # of characters in leftText. */ char *centerText; /* Text to display in center of menu * entry (space-padded as for leftText). * NULL means no centered text. */ int centerSize; /* Number of characters in centerText. */ int centerPos; /* X position at which to draw text in menu. */ char *rightText; /* Text to display right-justified in * menu entry (space-padded as for leftText). * NULL means no text. */ int rightSize; /* Number of characters in rightText. */ int rightPos; /* X position at which to draw text in menu. */ int background; /* Background color for menu entry. */ void (*proc)(); /* Procedure to call when menu entry * is selected. */ ClientData clientData; /* Client-supplied information to pass * to proc. */ } MenuEntry; /* * The structure below defines a pull-down menu. It consists of a * number of menu entries plus a mask indicating which entries are * enabled: disabled entries are displayed but do not respond to * button presses. Two windows are used to display the menu: one * for the menu title, and one for the menu itself (which is a child * of the root window and is only displayed when the menu is active). */ typedef struct Menu { FontInfo *fontPtr; /* Font to be used for this menu. */ int foreground; /* Foreground color for title and * menu. */ int background; /* Background color for title. */ char *name; /* Name of menu (displayed in * titleWindow). */ int nameWidth; /* Size of menu name, in pixels. */ Window titleWindow; /* Window that is used to display menu * name, and which user clicks on to * pull menu down. */ Window menuWindow; /* Window used to display menu * entries. */ struct MenuGroup *groupPtr; /* Structure describing group of * pulldown menus, all created in * the same containing window. */ int numEntries; /* Number of entries in menu. */ MenuEntry *entryPtrs[SX_MAX_MENU_ENTRIES]; /* Array of entries for this menu. * Entry 0 is displayed at the top. * NULL means entry doesn't exist. * Only first numEntries are valid. */ int mask; /* Indicates which entries are enabled. * 0 means disabled, 1 means enabled. * The lowest-order bit corresponds to * menu entry 0. */ int titleWidth, titleHeight; /* Dimensions of titleWindow. */ int menuWidth; /* Width of menuWindow in pixels, * including border and shadow. */ int menuHeight; /* Height of menuWindow in pixels, * including border and shadow. */ } Menu; /* * The structure below defines a menu group, which consists of a bunch * of pulldown menus all lined up in a row in some containing window. */ typedef struct MenuGroup { Window w; /* X window that contains the title * windows for all the menus in * this group. */ Menu *menuPtrs[SX_MAX_MENUS]; /* Array of menus. NULL means menu * not defined. All defined menus * are together at beginning of * array. */ } MenuGroup; /* * Constants for displaying menus: * * MARGIN Vertical spacing to leave around entries in menus. */ #define MARGIN 1 /* * Hash table used to find group and menu information from window ids. */ static XAssocTable *menuTable; static XAssocTable *groupTable; extern XAssocTable *XCreateAssocTable(); /* * The include's below are used to define the cursors used for * menu titles and menu windows. */ #include "cursors/dot" #include "cursors/dotMask" #include "cursors/star" #include "cursors/starMask" static Cursor cursorDot, cursorStar; /* * If a menu is actually pulled down, the information below is used * to keep track of information about it. */ static Pixmap savedPixmap; /* Used to save pixels underneath menu * so they don't have to be redrawn. 0 * means nothing is saved. */ static int savedWidth, savedHeight; /* Dimensions of savedPixmap. */ static int menuX, menuY; /* Location of origin of pulled-down * menu, in coordinates of RootWindow. */ static int groupX, groupY; /* Location of UL corner of leftmost * menu in group of pulled-down menu, * in coordinates of RootWindow. Used * to determine when the pulled-down * menu should be changed. */ static Menu *selectedMenuPtr = NULL; /* Selected menu (the one that's * pulled down or that the mouse is * over), or NULL if none selected. */ static int selectedEntry = -1; /* Index of selected entry, or -1 if * no entry is selected. */ static Boolean init = FALSE; /* TRUE means initialized OK. */ static int displayWidth; /* Dimensions of display. */ static int displayHeight; /* * Forward references: */ extern void ComputeMenuLayout(); extern void EraseMenu(); extern void MenuDeleteEntries(); extern void MenuDestroyProc(); extern void MenuEntryDisplay(); extern void MenuExposeProc(); extern void MenuInit(); extern void MenuMouseProc(); extern void MenuRedisplayProc(); extern void MenuTitleAdjust(); extern void MenuTitleMouseProc(); extern void MenuTitleRedisplay(); extern void PadAndCopy(); extern void PullMenuDown(); \f /* *---------------------------------------------------------------------- * * Sx_MenuCreate -- * * Create a new pulldown menu. * * Results: * The return value is an X id for the window containing the * pulldown menu's title. This can be used to manipulate the * menu (e.g. call XDestroyWindow to delete the menu). * * Side effects: * A new pulldown menu is created and displayed. It will have * numEntries entries, as described by the entries in the entries[] * array. If there was already a menu by the given name in the * given parent window, then the existing menu is replaced. * * Later, when buttons are pressed over the entries in the pulldown * menu, the client procedures indicated in entries[] will be called * as follows: * * void * proc(clientData, entry, menuWindow) * ClientData clientData; * int entry; * Window menuWindow; * { * } * * The clientData parameter is the one passed in the entries[] * element that was buttoned, entry is the index of the entry * that was invoked, and menuWindow indicates the menu (it's * the same as the value returned by this procedure). * *---------------------------------------------------------------------- */ Window Sx_MenuCreate(parent, name, numEntries, entries, fontPtr, foreground, background) Window parent; /* Containing window in which to * create menu. If many menus are * created in this window, their * titles will be ordered from left * to right in order of creation. */ char *name; /* Name of menu. */ int numEntries; /* Number of entries in menu. Must be * less than or equal to * SX_MAX_MENU_ENTRIES. */ Sx_MenuEntry entries[]; /* Entries: must have numEntries * values. */ FontInfo *fontPtr; /* Font to use for displaying menu. * NULL means use default font. */ int foreground; /* Foreground color to use to display * menu title and menu entries. */ int background; /* Background color to use for title. * Each menu entry indicates its own * background color. */ { register MenuGroup *groupPtr; register Menu *menuPtr; register MenuEntry *entryPtr; register Sx_MenuEntry *paramPtr; int i, index; if (!init) { MenuInit(); } if (fontPtr == NULL) { fontPtr = Sx_GetDefaultFont(); } /* * See if there's already a MenuGroup set up for the parent window. * If not then create one. */ groupPtr = (MenuGroup *) XLookUpAssoc(groupTable, parent); if (groupPtr == NULL) { groupPtr = (MenuGroup *) Mem_Alloc(sizeof(MenuGroup)); groupPtr->w = parent; for (i = 0; i < SX_MAX_MENUS; i++) { groupPtr->menuPtrs[i] = NULL; } XMakeAssoc(groupTable, parent, (caddr_t) groupPtr); } /* * Figure out which entry to use (or replace). */ for (index = 0; index < SX_MAX_MENUS; index++) { menuPtr = groupPtr->menuPtrs[index]; if (menuPtr == NULL) { break; } if (String_Compare(menuPtr->name, name) == 0) { break; } } if (index >= SX_MAX_MENUS) { Sx_Panic("Sx_MenuCreate: tried to create too many menus."); } /* * If replacing, clean up old stuff that will be redone. If * creating from scratch, do once-only initialization. */ if (menuPtr != NULL) { MenuDeleteEntries(menuPtr); } else { menuPtr = (Menu *) Mem_Alloc(sizeof(Menu)); groupPtr->menuPtrs[index] = menuPtr; menuPtr->name = (char *) Mem_Alloc(String_Length(name) + 1); String_Copy(name, menuPtr->name); menuPtr->nameWidth = XStringWidth(name, fontPtr, 0, 0); menuPtr->titleWindow = XCreateWindow(groupPtr->w, 0, 0, 1, 1, 0, BlackPixmap, WhitePixmap); (void) Sx_HandlerCreate(menuPtr->titleWindow, ButtonPressed|ButtonReleased|EnterWindow|LeaveWindow, MenuTitleMouseProc, (ClientData) menuPtr); (void) Sx_HandlerCreate(menuPtr->titleWindow, ExposeWindow, MenuExposeProc, (ClientData) menuPtr); (void) Sx_HandlerCreate(menuPtr->titleWindow, SX_DESTROYED, MenuDestroyProc, (ClientData) menuPtr); (void) Sx_HandlerCreate(menuPtr->titleWindow, KeyPressed|KeyReleased, Sx_NullProc, (ClientData) NULL); Sx_Pack(menuPtr->titleWindow, parent, SX_LEFT, menuPtr->nameWidth + XStringWidth(" ", fontPtr, 0, 0), 0, (Window) 0, (Window) 0); XMakeAssoc(menuTable, menuPtr->titleWindow, (caddr_t) menuPtr); menuPtr->menuWindow = XCreateWindow(RootWindow, 0, 0, 1, 1, 0, BlackPixmap, WhitePixmap); (void) Sx_HandlerCreate(menuPtr->menuWindow, ExposeWindow, MenuRedisplayProc, (ClientData) menuPtr); (void) Sx_HandlerCreate(menuPtr->menuWindow, ButtonReleased|MouseMoved, MenuMouseProc, (ClientData) menuPtr); menuPtr->groupPtr = groupPtr; } /* * Do the initialization that's independent of whether this is a * replacement or creation. */ menuPtr->fontPtr = fontPtr; menuPtr->foreground = foreground; menuPtr->background = background; if (numEntries > SX_MAX_MENU_ENTRIES) { numEntries = SX_MAX_MENU_ENTRIES; } menuPtr->numEntries = numEntries; for (i = 0, paramPtr = entries; i < numEntries; i++, paramPtr++) { entryPtr = (MenuEntry *) Mem_Alloc(sizeof(MenuEntry)); menuPtr->entryPtrs[i] = entryPtr; if (paramPtr->leftText == NULL) { entryPtr->leftText = NULL; entryPtr->leftSize = 0; } else { PadAndCopy(paramPtr->leftText, &entryPtr->leftText, &entryPtr->leftSize); } if (paramPtr->centerText == NULL) { entryPtr->centerText = NULL; entryPtr->centerSize = 0; } else { PadAndCopy(paramPtr->centerText, &entryPtr->centerText, &entryPtr->centerSize); } if (paramPtr->rightText == NULL) { entryPtr->rightText = NULL; entryPtr->rightSize = 0; } else { PadAndCopy(paramPtr->rightText, &entryPtr->rightText, &entryPtr->rightSize); } if (paramPtr->background != -1) { entryPtr->background = paramPtr->background; } else { entryPtr->background = menuPtr->background; } entryPtr->proc = paramPtr->proc; entryPtr->clientData = paramPtr->clientData; } menuPtr->mask = (1<<numEntries) - 1; menuPtr->menuHeight = numEntries*(menuPtr->fontPtr->height + 2*MARGIN) + SHADOW_TOP + SHADOW_BOTTOM; ComputeMenuLayout(menuPtr); return menuPtr->titleWindow; } \f /* *---------------------------------------------------------------------- * * Sx_MenuSetMask -- * * Select which entries in a menu are enabled. * * Results: * The return value is the previous value of the enabled mask * for the menu. * * Side effects: * After this call, only the entries given by ones in mask will be * enabled in the menu given by window. The low-order bit of mask * corresponds to the first entry in the menu, etc. Disabled menu * entries are displayed differently, and are not sensitive to * button pushes (i.e. the client procedure isn't invoked). If * every entry in a menu is disabled, then the menu title is * displayed differently to indicate this fact. * *---------------------------------------------------------------------- */ int Sx_MenuSetMask(window, mask) Window window; /* Window containing menu title (i.e. value * returned by Sx_MenuCreate). */ int mask; /* New value for mask. Low-order bit * corresponds to first menu entry. */ { Menu *menuPtr; int oldMask; if (!init) { MenuInit(); } menuPtr = (Menu *) XLookUpAssoc(menuTable, window); if (menuPtr == NULL) { Sx_Panic("Sx_MenuSetMask: window parameter isn't a menu."); } oldMask = menuPtr->mask; menuPtr->mask = mask & ((1<<menuPtr->numEntries) - 1); if (menuPtr->mask == 0) { if (oldMask != 0) { MenuTitleRedisplay(menuPtr); } } else if (oldMask == 0) { MenuTitleRedisplay(menuPtr); } return oldMask; } \f /* *---------------------------------------------------------------------- * * Sx_MenuGetMask -- * * Find out which entries in a menu are enabled. * * Results: * The return value is the current value of the enabled mask for * the menu associated with window. * * Side effects: * None. * *---------------------------------------------------------------------- */ int Sx_MenuGetMask(window) Window window; /* Window containing menu's title (i.e. return * value from Sx_MenuCreate). */ { Menu *menuPtr; if (!init) { MenuInit(); } menuPtr = (Menu *) XLookUpAssoc(menuTable, window); if (menuPtr == NULL) { Sx_Panic("Sx_MenuGetMask: window parameter isn't a menu."); } return menuPtr->mask; } \f /* *---------------------------------------------------------------------- * * Sx_MenuReplaceEntry -- * * This procedure replaces a single entry in a pulldown menu. * * Results: * None. * * Side effects: * The entryIndex'th entry in the menu associated with window * is replaced with newEntry. * *---------------------------------------------------------------------- */ void Sx_MenuReplaceEntry(window, entryIndex, newEntryPtr) Window window; /* Window that's a pulldown menu * title (i.e. window was a return * value from Sx_MenuCreate). */ int entryIndex; /* Index of desired entry. This * entry must already exist. */ register Sx_MenuEntry *newEntryPtr; /* Pointer to new menu entry. */ { Menu *menuPtr; register MenuEntry *entryPtr; if (!init) { MenuInit(); } menuPtr = (Menu *) XLookUpAssoc(menuTable, window); if (menuPtr == NULL) { Sx_Panic("Sx_MenuReplaceEntry: window parameter isn't a menu."); } entryPtr = menuPtr->entryPtrs[entryIndex]; if (entryPtr->leftText != NULL) { Mem_Free(entryPtr->leftText); } if (entryPtr->centerText != NULL) { Mem_Free(entryPtr->centerText); } if (entryPtr->rightText != NULL) { Mem_Free(entryPtr->rightText); } if (newEntryPtr->leftText == NULL) { entryPtr->leftText = NULL; entryPtr->leftSize = 0; } else { PadAndCopy(newEntryPtr->leftText, &entryPtr->leftText, &entryPtr->leftSize); } if (newEntryPtr->centerText == NULL) { entryPtr->centerText = NULL; entryPtr->centerSize = 0; } else { PadAndCopy(newEntryPtr->centerText, &entryPtr->centerText, &entryPtr->centerSize); } if (newEntryPtr->rightText == NULL) { entryPtr->rightText = NULL; entryPtr->rightSize = 0; } else { PadAndCopy(newEntryPtr->rightText, &entryPtr->rightText, &entryPtr->rightSize); } if (newEntryPtr->background != -1) { entryPtr->background = newEntryPtr->background; } else { entryPtr->background = menuPtr->background; } entryPtr->proc = newEntryPtr->proc; entryPtr->clientData = newEntryPtr->clientData; ComputeMenuLayout(menuPtr); } \f /* *---------------------------------------------------------------------- * * Sx_MenuGetInfo -- * * This procedure returns a complete description of a particular * menu. * * Results: * The return value is a count of the number of entries in the * menu, or -1 if window isn't a menu window. Entries, * *fontPtrPtr, *foregroundPtr, and *backgroundPtr all get * filled in with the corresponding information as it was * passed to Sx_MenuCreate or Sx_MenuReplaceEntry. * * Side effects: * None. * *---------------------------------------------------------------------- */ int Sx_MenuGetInfo(window, entries, fontPtrPtr, foregroundPtr, backgroundPtr) Window window; /* Window corresponding to menu. */ Sx_MenuEntry entries[]; /* Array of menu entries; gets filled * in by this procedure. The string * pointers will all refer to statics * in the menu manager; they will * become invalid whenever the menu * is modified or deleted. If entries * is NULL then no entry information * is returned. */ FontInfo **fontPtrPtr; /* Gets filled in with font info * pointer (if NULL, then ignored). */ int *foregroundPtr; /* Gets filled in with menu's * foreground color (if not NULL). */ int *backgroundPtr; /* Gets filled in with menu's * background color (if not NULL). */ { register MenuEntry *srcPtr; register Sx_MenuEntry *dstPtr; register Menu *menuPtr; int i; if (!init) { MenuInit(); } menuPtr = (Menu *) XLookUpAssoc(menuTable, window); if (menuPtr == NULL) { return -1; } if (fontPtrPtr != NULL) { *fontPtrPtr = menuPtr->fontPtr; } if (foregroundPtr != NULL) { *foregroundPtr = menuPtr->foreground; } if (backgroundPtr != NULL) { *backgroundPtr = menuPtr->background; } if (entries == NULL) { return menuPtr->numEntries; } for (i = 0, dstPtr = entries; i < menuPtr->numEntries; i++, dstPtr++) { srcPtr = menuPtr->entryPtrs[i]; dstPtr->leftText = srcPtr->leftText; dstPtr->centerText = srcPtr->centerText; dstPtr->rightText = srcPtr->rightText; dstPtr->background = srcPtr->background; dstPtr->proc = srcPtr->proc; dstPtr->clientData = srcPtr->clientData; } return menuPtr->numEntries; } \f /* *---------------------------------------------------------------------- * * Sx_MenuGetNames -- * * Given a parent window containing 0 or more menu children, return * the names of all the children menus (and also their X window ids). * * Results: * The return value is a count of the number of menus in parent, * which may be zero. The arrays "names" and "windows" get filled * in with information about the menus in parent. * * Side effects: * None. * *---------------------------------------------------------------------- */ int Sx_MenuGetNames(parent, names, windows) Window parent; /* Parent window, which ostensibly contains * one or menus as subwindows. */ char *(names[]); /* Entries in this array get filled in with * pointers to statically-allocated strings * giving names of menus, unless names is NULL. * Array must have SX_MAX_MENUS entries. * Strings are statically allocated and are * part of the menu manager; they will become * invalid when the corresponding menu is * modified or deleted. */ Window windows[]; /* Entries in this array get filled in with * X window ids corresponding to names, unless * windows is NULL. */ { register Menu *menuPtr; register MenuGroup *groupPtr; int i; if (!init) { MenuInit(); } groupPtr = (MenuGroup *) XLookUpAssoc(groupTable, parent); if (groupPtr == NULL) { return 0; } for (i = 0; i < SX_MAX_MENUS; i++) { menuPtr = groupPtr->menuPtrs[i]; if (menuPtr == NULL) { break; } if (names != NULL) { names[i] = menuPtr->name; } if (windows != NULL) { windows[i] = menuPtr->titleWindow; } } return i; } \f /* *---------------------------------------------------------------------- * * Sx_MenuGetWindow -- * * Given the parent window containing a menu, and the name of the * menu, get the X window id corresponding to the menu. * * Results: * The return value is the X window id of a menu subwindow of * parent whose name (title) is as given. If there isn't any * such menu, NULL is returned. * * Side effects: * None. * *---------------------------------------------------------------------- */ Window Sx_MenuGetWindow(parent, name) Window parent; /* Parent window, which ostensibly has one * or more menu subwindows as children. */ char *name; /* Title of desired menu. */ { register Menu *menuPtr; register MenuGroup *groupPtr; int i; if (!init) { MenuInit(); } groupPtr = (MenuGroup *) XLookUpAssoc(groupTable, parent); if (groupPtr == NULL) { return NULL; } for (i = 0; i < SX_MAX_MENUS; i++) { menuPtr = groupPtr->menuPtrs[i]; if (menuPtr == NULL) { break; } if (String_Compare(name, menuPtr->name) == 0) { return menuPtr->titleWindow; } } return NULL; } \f /* *---------------------------------------------------------------------- * * MenuInit -- * * Performs various once-only initialization functions. * * Results: * None. * * Side effects: * Random stuff gets initialized. See code for details. * *---------------------------------------------------------------------- */ static void MenuInit() { displayWidth = DisplayWidth(); displayHeight = DisplayHeight(); cursorDot = XCreateCursor(dot_width, dot_height, dot_bits, dotMask_bits, dot_x_hot, dot_y_hot, BlackPixel, WhitePixel, GXcopy); cursorStar = XCreateCursor(star_width, star_height, star_bits, starMask_bits, star_x_hot, star_y_hot, BlackPixel, WhitePixel, GXcopy); groupTable = XCreateAssocTable(4); menuTable = XCreateAssocTable(8); if ((cursorDot == 0) || (cursorStar == 0) || (menuTable == NULL) || (groupTable == NULL)) { Sx_Panic("Sx_MenuCreate: couldn't initialize cursor \ and/or hash tables."); } init = TRUE; } \f /* *---------------------------------------------------------------------- * * MenuTitleRedisplay-- * * Redisplay the title window for a menu. * * Results: * None. * * Side effects: * The menu title for menu is redisplayed. * *---------------------------------------------------------------------- */ static void MenuTitleRedisplay(menuPtr) register Menu *menuPtr; /* Menu to be redisplayed. */ { int background, foreground, x, y; /* * If this menu is currently selected, display it in reverse video. */ if (menuPtr == selectedMenuPtr) { background = menuPtr->foreground; foreground = menuPtr->background; } else { background = menuPtr->background; foreground = menuPtr->foreground; } XPixSet(menuPtr->titleWindow, 0, 0, menuPtr->titleWidth, menuPtr->titleHeight, background); x = (menuPtr->titleWidth - menuPtr->nameWidth)/2; y = (menuPtr->titleHeight - menuPtr->fontPtr->height)/2; XText(menuPtr->titleWindow, x, y, menuPtr->name, String_Length(menuPtr->name), menuPtr->fontPtr->id, foreground, background); /* * If the entire menu is disabled, display a bar through the title. */ if (menuPtr->mask == 0) { XPixSet(menuPtr->titleWindow, 2, menuPtr->titleHeight/2 - 1, menuPtr->titleWidth-4, 2, foreground); } } \f /* *---------------------------------------------------------------------- * * MenuExposeProc -- * * This procedure is called for each expose event on a menu title. * * Results: * None. * * Side effects: * The title gets redisplayed, and its size information gets * reset. * *---------------------------------------------------------------------- */ static void MenuExposeProc(menuPtr, eventPtr) Menu *menuPtr; /* Menu whose title was exposed. */ XExposeEvent *eventPtr; /* Gives new dimensions of title. */ { if (eventPtr->subwindow != NULL) { return; } menuPtr->titleWidth = eventPtr->width; menuPtr->titleHeight = eventPtr->height; MenuTitleRedisplay(menuPtr); } \f /* *---------------------------------------------------------------------- * * MenuTitleMouseProc -- * * This procedure is called by the dispatcher when mouse-related * events (entry, exit, or button press/release) occur in the * title for a menu. * * Results: * None. * * Side effects: * The menu title gets highlighted when the cursor is in the * title window. When a button is pressed, the pull-down menu * gets displayed and it grabs the mouse. * *---------------------------------------------------------------------- */ static void MenuTitleMouseProc(menuPtr, eventPtr) register Menu *menuPtr; /* Menu title for which event * occurred. */ register XButtonEvent *eventPtr; /* Describes what happened. */ { if (eventPtr->subwindow != 0) { return; } if (eventPtr->type == EnterWindow) { selectedMenuPtr = menuPtr; MenuTitleRedisplay(menuPtr); } else if (eventPtr->type == LeaveWindow) { if (selectedMenuPtr == menuPtr) { selectedMenuPtr = NULL; } MenuTitleRedisplay(menuPtr); } else if (eventPtr->type == ButtonPressed) { register Menu **menuPtrPtr; /* * Time to pull a menu down. Save the origin of the menu group * window, for use later in mouse tracking. Compute where the * menu should go on the screen, then display it. */ groupX = ((eventPtr->location >> 16) & 0xffff) - eventPtr->x; groupY = (eventPtr->location & 0xffff) - eventPtr->y; PullMenuDown(menuPtr, groupX, groupY + menuPtr->titleHeight); Sx_GrabMouse(menuPtr->menuWindow, cursorStar, ButtonReleased|MouseMoved); selectedEntry = -1; for (menuPtrPtr = menuPtr->groupPtr->menuPtrs; (*menuPtrPtr != menuPtr) && (*menuPtrPtr != NULL); menuPtrPtr++) { groupX -=(*menuPtrPtr)->titleWidth; } } } \f /* *---------------------------------------------------------------------- * * MenuDeleteEntries -- * * Recycle all of the memory associated with entries for a given * menu. * * Results: * None. * * Side effects: * Memory gets reallocated. * *---------------------------------------------------------------------- */ static void MenuDeleteEntries(menuPtr) register Menu *menuPtr; /* Menu whose entries are to be deleted. */ { register MenuEntry *entryPtr; int i; for (i = 0; i < menuPtr->numEntries; i++) { entryPtr = menuPtr->entryPtrs[i]; if (entryPtr->leftText != NULL) { Mem_Free((Address) entryPtr->leftText); } if (entryPtr->centerText != NULL) { Mem_Free((Address) entryPtr->centerText); } if (entryPtr->rightText != NULL) { Mem_Free((Address) entryPtr->rightText); } Mem_Free((Address) entryPtr); } menuPtr->numEntries = 0; } \f /* *---------------------------------------------------------------------- * * MenuDestroyProc -- * * This procedure destroys a menu. It's called by Sx when the * window containing the menu is deleted. * * Results: * None. * * Side effects: * Internal storage associated with the pulldown menu is released. * *---------------------------------------------------------------------- */ void MenuDestroyProc(menuPtr) register Menu *menuPtr; /* Menu to be deleted. */ { register MenuGroup *groupPtr; int i; /* * Free up everything associated with the menu itself. */ XDeleteAssoc(menuTable, menuPtr->titleWindow); XDestroyWindow(menuPtr->menuWindow); MenuDeleteEntries(menuPtr); Mem_Free((Address) menuPtr->name); Mem_Free((Address) menuPtr); /* * Remove this menu from its group. If the group is * now empty, delete it also. */ groupPtr = menuPtr->groupPtr; for (i = 0; i < SX_MAX_MENUS; i++) { if (groupPtr->menuPtrs[i] == menuPtr) { int j; groupPtr->menuPtrs[i] = NULL; for (j = i+1; (j < SX_MAX_MENUS) && (groupPtr->menuPtrs[j] != NULL); j++) { groupPtr->menuPtrs[j-1] = groupPtr->menuPtrs[j]; groupPtr->menuPtrs[j] = NULL; } } } if (groupPtr->menuPtrs[0] == NULL) { XDeleteAssoc(groupTable, groupPtr->w); Mem_Free((Address) groupPtr); } } \f /* *---------------------------------------------------------------------- * * ComputeMenuLayout -- * * This procedure scans all the entries in a menu and computes * the overall size of the menu as well as where to position * each text field of each entry. * * Results: * None. * * Side effects: * Entries in the menu get modified to reflect the new layout. * Menu->menuWindow also gets modified in size to reflect * the new layout. * *---------------------------------------------------------------------- */ static void ComputeMenuLayout(menuPtr) register Menu *menuPtr; /* Menu to resize. */ { register MenuEntry *entryPtr; int leftMax, centerMax, rightMax; int i, length; /* * Compute the maximum width of any entry for each column. */ leftMax = centerMax = rightMax = 0; for (i = 0; i < menuPtr->numEntries; i++) { entryPtr = menuPtr->entryPtrs[i]; if (entryPtr->leftText != NULL) { length = XStringWidth(entryPtr->leftText, menuPtr->fontPtr, 0, 0); if (length > leftMax) { leftMax = length; } } if (entryPtr->centerText != NULL) { length = XStringWidth(entryPtr->centerText, menuPtr->fontPtr, 0, 0); if (length > centerMax) { centerMax = length; } entryPtr->centerPos = length; /* Save to avoid recomputation. */ } if (entryPtr->rightText != NULL) { length = XStringWidth(entryPtr->rightText, menuPtr->fontPtr, 0, 0); if (length > rightMax) { rightMax = length; } entryPtr->rightPos = length; /* Save to avoid recomputation. */ } } /* * Size the window so that none of the columns overlap (be sure * to leave space for the fancy border). Then go back and fill * in the exact drawing position for each string. */ menuPtr->menuWidth = leftMax + centerMax + rightMax + SHADOW_LEFT + SHADOW_RIGHT; XChangeWindow(menuPtr->menuWindow, menuPtr->menuWidth, menuPtr->menuHeight); for (i = 0; i < menuPtr->numEntries; i++) { entryPtr = menuPtr->entryPtrs[i]; entryPtr->centerPos = SHADOW_LEFT + leftMax + (centerMax - entryPtr->centerPos)/2; entryPtr->rightPos = menuPtr->menuWidth - entryPtr->rightPos - SHADOW_RIGHT; } } \f /* *---------------------------------------------------------------------- * * PadAndCopy -- * * This procedure makes a Mem_Alloc'ed copy of a string, * adding a space onto each end of the string in the process. * * Results: * *newPtr is filled in with a pointer to an area of memory (allocated * with Mem_Alloc) that contains a copy of string with a space * added onto each end. If lengthPtr is non-NULL, *lengthPtr is filled * in with the length of the new string (not including the terminator). * * Side effects: * None. * *---------------------------------------------------------------------- */ static void PadAndCopy(string, newPtr, lengthPtr) char *string; /* String to copy. */ char **newPtr; /* Tells where to store pointer to copy. */ int *lengthPtr; /* Where to store length of copy, or NULL. */ { register char *new; int length; length = String_Length(string); new = (char *) Mem_Alloc(length + 3); new[0] = ' '; (void) String_Copy(string, &new[1]); new[length+1] = ' '; new[length+2] = 0; *newPtr = new; if (lengthPtr != NULL) { *lengthPtr = length+2; } } \f /* *---------------------------------------------------------------------- * * MenuEntryDisplay -- * * This procedure is called to display one entry of a menu. * * Results: * None. * * Side effects: * The entry is displayed in its menu. * *---------------------------------------------------------------------- */ static void MenuEntryDisplay(menuPtr, index) register Menu *menuPtr; /* Menu containing entry to display. */ int index; /* Index of entry within menu. */ { register MenuEntry *entryPtr; register FontInfo *font = menuPtr->fontPtr; int y, background, foreground; entryPtr = menuPtr->entryPtrs[index]; if (index == selectedEntry) { background = menuPtr->foreground; foreground = entryPtr->background; } else { background = entryPtr->background; foreground = menuPtr->foreground; } y = index*(font->height + 2*MARGIN) + SHADOW_LEFT; XPixSet(menuPtr->menuWindow, SHADOW_LEFT, y, menuPtr->menuWidth - (SHADOW_LEFT + SHADOW_RIGHT), font->height + 2*MARGIN, background); y += MARGIN; if (entryPtr->leftText != NULL) { XText(menuPtr->menuWindow, SHADOW_LEFT, y, entryPtr->leftText, entryPtr->leftSize, font->id, foreground, background); } if (entryPtr->centerText != NULL) { XText(menuPtr->menuWindow, entryPtr->centerPos, y, entryPtr->centerText, entryPtr->centerSize, font->id, foreground, background); } if (entryPtr->rightText != NULL) { XText(menuPtr->menuWindow, entryPtr->rightPos, y, entryPtr->rightText, entryPtr->rightSize, font->id, foreground, background); } /* * If the entry is disabled, display a bar through it. */ if (((1<<index) & menuPtr->mask) == 0) { y += (font->height)/2 - 1; XPixSet(menuPtr->menuWindow, SHADOW_LEFT + 2, y, menuPtr->menuWidth - (SHADOW_LEFT + SHADOW_RIGHT + 4), 2, foreground); } } \f /* *---------------------------------------------------------------------- * * MenuRedisplayProc -- * * This procedure is invoked by the Sx_ dispatcher to redisplay * menus. * * Results: * None. * * Side effects: * The menu gets redisplayed. * *---------------------------------------------------------------------- */ static void MenuRedisplayProc(menuPtr) register Menu *menuPtr; /* Menu to be redisplayed. */ { int i; SxDrawShadow(menuPtr->menuWindow, 0, 0, menuPtr->menuWidth - SHADOW_LEFT - SHADOW_RIGHT, menuPtr->menuHeight - SHADOW_TOP - SHADOW_BOTTOM, menuPtr->foreground, savedPixmap); for (i = 0; i < menuPtr->numEntries; i++) { MenuEntryDisplay(menuPtr, i); } } \f /* *---------------------------------------------------------------------- * * MenuFindTitle -- * * When tracking the mouse, this procedure determines if the * mouse is in the title area of a menu. * * Results: * The return value is a pointer to the menu over whose title * the cursor is currently position, or NULL if the cursor * isn't over any menu title in the caption containing menuPtr. * If xPtr isn't NULL, *xPtr is filled in with the x-location * (in RootWindow coords.) of the left edge of the returned * title. * * Side effects: * None. * *---------------------------------------------------------------------- */ static Menu * MenuFindTitle(menuPtr, eventPtr, xPtr) register Menu *menuPtr; /* Menu entry. The idea is to find * out if some other menu in this * one's group is selected. */ register XMouseEvent *eventPtr; /* X event giving current mouse * location. */ int *xPtr; { register MenuGroup *groupPtr; int x, curX, y, i; /* * Compute mouse location relative to upper-left corner of the menu group. */ x = ((eventPtr->location >> 16) & 0xffff); y = (eventPtr->location & 0xffff) - groupY; /* * Make sure that y is within the range of this group. */ if ((y < 0) || (y > menuPtr->titleHeight)) { return NULL; } /* * See which title's area it's in, if any. */ if (x < groupX) { return NULL; } for (i = 0, curX = groupX, groupPtr = menuPtr->groupPtr; i < SX_MAX_MENUS; i++, curX += menuPtr->titleWidth) { menuPtr = groupPtr->menuPtrs[i]; if (menuPtr == NULL) { return NULL; } if (x < curX + menuPtr->titleWidth) { if (xPtr != NULL) { *xPtr = curX; } return menuPtr; } } return NULL; } \f /* *---------------------------------------------------------------------- * * MenuMouseProc -- * * This procedure is called by the dispatcher when mouse-related * events occur for a menu window. This can only happen when * the menu is being displayed, which means that a mouse button * was pressed over a menu title and hasn't been released yet. * When this procedure is called, the mouse has been grabbed * for the menu window. * * Results: * None. * * Side effects: * This procedure tracks the motion of the mouse and button * releases. It highlights the menu entry underneath the mouse * (if the entry is enabled) and calls a client-supplied * procedure if the mouse button is released over an entry. * When the buttons are released, the menu is unmapped and * mouse-grabbing stops. * *---------------------------------------------------------------------- */ static void MenuMouseProc(menuPtr, eventPtr) register Menu *menuPtr; /* Menu that is currently active. */ XEvent *eventPtr; /* Describes what just happened. */ { Menu *newPtr; if (eventPtr->type == MouseMoved) { register XMouseMovedEvent *mouseEventPtr; int index; mouseEventPtr = (XMouseMovedEvent *) eventPtr; /* * Find the entry under the mouse (if any). If it's enabled, * then select it and unselect the previous one, if any. */ index = mouseEventPtr->y/(menuPtr->fontPtr->height + 2*MARGIN); if ((mouseEventPtr->y < 0) || (index >= menuPtr->numEntries) || (mouseEventPtr->x < SHADOW_LEFT) || (mouseEventPtr->x > (menuPtr->menuWidth - SHADOW_RIGHT)) || (((1<<index) & menuPtr->mask) == 0)) { index = -1; } if (selectedEntry != index) { int prev = selectedEntry; selectedEntry = index; if (index >= 0) { MenuEntryDisplay(menuPtr, index); } if (prev >= 0) { MenuEntryDisplay(menuPtr, prev); } } /* * If no entry is currently selected, see if the mouse has * moved over the title of a different menu. If so, then * deselect the current menu and select the one whose title * is under the cursor. */ if (index == -1) { int x; newPtr = MenuFindTitle(menuPtr, mouseEventPtr, &x); if ((newPtr != NULL) && (newPtr != menuPtr)) { selectedMenuPtr = newPtr; selectedEntry = -1; EraseMenu(menuPtr); MenuTitleRedisplay(menuPtr); PullMenuDown(newPtr, x, groupY + menuPtr->titleHeight); MenuTitleRedisplay(newPtr); Sx_GrabMouse(newPtr->menuWindow, cursorStar, ButtonReleased|MouseMoved); } } } else if (eventPtr->type == ButtonReleased) { register XButtonEvent *butEventPtr = (XButtonEvent *) eventPtr; static int masks[] = { LeftMask|MiddleMask, LeftMask|RightMask, MiddleMask|RightMask }; /* * When all of the buttons have been released, then unmap * the menu. If an entry was selected at the time, flash it * before erasing the menu, then call its action procedure * after erasing the menu. The computation of whether all * buttons have been released is a little tricky because the * "detail" value doesn't take into account the current event. */ if ((butEventPtr->detail & masks[butEventPtr->detail&0377]) == 0) { if (selectedEntry >= 0) { int i, index; index = selectedEntry; for (i = 0; i < 2; i++) { selectedEntry = -1; MenuEntryDisplay(menuPtr, index); SxFlashWait(); selectedEntry = index; MenuEntryDisplay(menuPtr, index); SxFlashWait(); } } selectedMenuPtr = NULL; EraseMenu(menuPtr); MenuTitleRedisplay(menuPtr); Sx_UngrabMouse(); XFlush(); if (selectedEntry >= 0) { MenuEntry *entry = menuPtr->entryPtrs[selectedEntry]; (*entry->proc)(entry->clientData, selectedEntry, menuPtr->titleWindow); /* * Be VERY CAREFUL after returning from the client procedure. * It's possible that the client deleted the menu, leaving * us with a dangling pointer. The best thing here is to * do nothing but return. */ } else { newPtr = MenuFindTitle(menuPtr, butEventPtr, (int *) NULL); if (newPtr != NULL) { selectedMenuPtr = newPtr; MenuTitleRedisplay(newPtr); } } } } } \f /* *---------------------------------------------------------------------- * * PullMenuDown -- * * This procedure does all the work of "pulling a menu down", * i.e. making the menu visible on the screen. * * Results: * None. * * Side effects: * Information on the screen changes, and the previous pixels under * the menu area get saved away. MenuX and menuY get set, and the * X server is grabbed to make sure that the saved pixels stay * up-to-date. * *---------------------------------------------------------------------- */ static void PullMenuDown(menuPtr, x, y) register Menu *menuPtr; /* Menu to pull down. There must not * currently be a menu pulled down. */ int x, y; /* Screen coordinates at which to pull * menu down. */ { /* * Compute the location and dimensions of the new menu to be * displayed. Float the menu up or to the left if that is * necessary to get it all on-screen. */ savedWidth = menuPtr->menuWidth; if (savedWidth > displayWidth) { savedWidth = displayWidth; } savedHeight = menuPtr->menuHeight; if (savedHeight > displayHeight) { savedHeight = displayHeight; } if ((x+savedWidth) > displayWidth) { x = displayWidth - savedWidth; } if ((y+savedHeight) > displayHeight) { y = displayHeight - savedHeight; } if (x < 0) { x = 0; } if (y < 0) { y = 0; } menuX = x; menuY = y; /* * Save the previous pixels under the menu, then display the menu. */ XGrabServer(); savedPixmap = XPixmapSave(RootWindow, x, y, savedWidth, savedHeight); XMoveWindow(menuPtr->menuWindow, x, y); XMapWindow(menuPtr->menuWindow); } \f /* *---------------------------------------------------------------------- * * EraseMenu -- * * This procedure does all the work of erasing a pulled-down * menu from the screen and restoring the screen contents. * * Results: * None. * * Side effects: * Information changes on the screen. * *---------------------------------------------------------------------- */ static void EraseMenu(menuPtr) Menu *menuPtr; /* Menu to erase. Must currently be * pulled down. */ { /* * If pixels were successfully saved, then restore from them. * Otherwise, just let the are of the menuPtr be redisplayed in * the normal fashion. */ if (savedPixmap == 0) { XUnmapWindow(menuPtr->menuWindow); } else { XPixmapPut(menuPtr->menuWindow, 0, 0, 0, 0, savedWidth, savedHeight, savedPixmap, GXcopy, AllPlanes); XUnmapTransparent(menuPtr->menuWindow); XFreePixmap(savedPixmap); } savedPixmap = 0; XUngrabServer(); }