/* * $Id: Menu.c,v 1.4 2000-07-17 21:12:02 ghudson Exp $ * * Copyright 1990, 1991 by the Massachusetts Institute of Technology. * * For copying and distribution information, please see the file * . * */ #if (!defined(lint)) && (!defined(SABER)) static char *rcsid = "$Id: Menu.c,v 1.4 2000-07-17 21:12:02 ghudson Exp $"; #endif #include "mit-copyright.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "Jets.h" #include "Window.h" #include "Menu.h" #include "Button.h" #include "warn.h" #include #include extern int DEBUG; /* #define DEBUGPARSING */ /* #define DEBUGMEM */ extern XrmDatabase rdb; /* oops! broken abstraction! but it's just a string. :-) */ #define NOMENU "\ menu deadroot: \"empty file\" {none} [NULL];\ item quit: \"Quit Dash\" [none] -verify quit();" /* come to think of it, should this be a class variable? */ static XContext context; static int gotContextType = 0; int me_moving = 0; #define offset(field) XjOffset(MenuJet,field) static XjResource resources[] = { { XjNfont, XjCFont, XjRFontStruct, sizeof(XFontStruct *), offset(menu.font), XjRString, XjDefaultFont }, { XjNx, XjCX, XjRInt, sizeof(int), offset(core.x), XjRString, XjInheritValue }, { XjNy, XjCY, XjRInt, sizeof(int), offset(core.y), XjRString, XjInheritValue }, { XjNwidth, XjCWidth, XjRInt, sizeof(int), offset(core.width), XjRString, XjInheritValue }, { XjNheight, XjCHeight, XjRInt, sizeof(int), offset(core.height), XjRString, XjInheritValue }, { XjNhMenuPadding, XjCMenuPadding, XjRInt, sizeof(int), offset(menu.hMenuPadding), XjRString, "10" }, { XjNvMenuPadding, XjCMenuPadding, XjRInt, sizeof(int), offset(menu.vMenuPadding), XjRString, "4" }, { XjNitems, XjCItems, XjRString, sizeof(char *), offset(menu.items), XjRString, "" }, { XjNfile, XjCFile, XjRString, sizeof(char *), offset(menu.file), XjRString, "" }, { XjNfallback, XjCFallback, XjRString, sizeof(char *), offset(menu.fallback), XjRString, "" }, { XjNuserItems, XjCItems, XjRString, sizeof(char *), offset(menu.userItems), XjRString, "" }, { XjNuserFile, XjCFile, XjRString, sizeof(char *), offset(menu.userFile), XjRString, "" }, { XjNforeground, XjCForeground, XjRColor, sizeof(int), offset(menu.foreground), XjRString, XjDefaultForeground }, { XjNbackground, XjCBackground, XjRColor, sizeof(int), offset(menu.background), XjRString, XjDefaultBackground }, { XjNreverseVideo, XjCReverseVideo, XjRBoolean, sizeof(Boolean), offset(menu.reverseVideo), XjRBoolean, (caddr_t)False }, { XjNscreenWidth, XjCScreenWidth, XjRBoolean, sizeof(Boolean), offset(menu.screenWidth), XjRBoolean, (caddr_t) False }, { XjNhelpPixmap, XjCPixmap, XjRPixmap, sizeof(XjPixmap *), offset(menu.helpPixmap), XjRString, "help.bits" }, { XjNsubmenuPixmap, XjCPixmap, XjRPixmap, sizeof(XjPixmap *), offset(menu.submenuPixmap), XjRString, "submenu.bits" }, { XjNshowHelp, XjCShowHelp, XjRBoolean, sizeof(Boolean), offset(menu.showHelp), XjRBoolean, (caddr_t)True }, { XjNgrey, XjCGrey, XjRPixmap, sizeof(XjPixmap *), offset(menu.grey), XjRString, NULL }, { XjNrude, XjCRude, XjRBoolean, sizeof(Boolean), offset(menu.rude), XjRBoolean, (caddr_t)True }, { XjNverifyProc, XjCVerifyProc, XjRCallback, sizeof(XjCallback *), offset(menu.verifyProc), XjRString, NULL }, { XjNverify, XjCVerify, XjRBoolean, sizeof(Boolean), offset(menu.verify), XjRBoolean, (caddr_t)True }, { XjNautoRaise, XjCAutoRaise, XjRBoolean, sizeof(Boolean), offset(menu.autoRaise), XjRBoolean, (caddr_t)False }, { XjNmoveMenus, XjCMoveMenus, XjRBoolean, sizeof(Boolean), offset(menu.moveMenus), XjRBoolean, (caddr_t)False }, { XjNrightJet, XjCJet, XjRString, sizeof(Jet), offset(menu.rightJet), XjRString, NULL }, }; #undef offset #define BORDER 1 #define SHADOW 1 #define SAME (Menu *)-1 static void initialize(), realize(); static Boolean event_handler(); void computeMenuSize(); static void closeMenuAndAncestorsToLevel(); MenuClassRec menuClassRec = { { /* class name */ "Menu", /* jet size */ sizeof(MenuRec), /* classInitialize */ NULL, /* classInitialized? */ 1, /* initialize */ initialize, /* prerealize */ NULL, /* realize */ realize, /* event */ event_handler, /* expose */ NULL, /* querySize */ NULL, /* move */ NULL, /* resize */ NULL, /* destroy */ NULL, /* resources */ resources, /* number of 'em */ XjNumber(resources) } }; JetClass menuJetClass = (JetClass)&menuClassRec; /************************************************************************ * * drawHelp -- draws text in a help box when it is popped up. * ************************************************************************/ static void drawHelp(me, menu) MenuJet me; Menu *menu; { char *ptr, *end; int y = 0; ptr = menu->child->title; while (*ptr != '\0') { end = ptr; while (*end != '\n' && *end != '\0') end++; if (end > ptr) XDrawString(me->core.display, menu->menuPane, me->menu.gc, me->menu.hMenuPadding / 2, y + me->menu.vMenuPadding / 2 + me->menu.font->ascent, ptr, (int)(end - ptr)); y += me->menu.font->ascent + me->menu.font->descent; if (*end != '\0') ptr = end + 1; else ptr = end; } } /************************************************************************ * * computeHelpSize -- figures out how big a help box is, based on the * text that it contains. * ************************************************************************/ static void computeHelpSize(me, menu) MenuJet me; Menu *menu; { char *ptr, *end; menu->pane_width = 0; menu->pane_height = me->menu.vMenuPadding; if (menu->child->title_width != 0) { menu->pane_width += me->menu.hMenuPadding + menu->child->title_width; menu->pane_height += menu->child->title_height; } else { ptr = menu->child->title; while (*ptr != '\0') { end = ptr; while (*end != '\n' && *end != '\0') end++; if (end > ptr) menu->pane_width = MAX(menu->pane_width, me->menu.hMenuPadding + XTextWidth(me->menu.font, ptr, (int)(end - ptr))); menu->pane_height += me->menu.font->ascent + me->menu.font->descent; if (*end != '\0') ptr = end + 1; else ptr = end; } } menu->child->label_x = menu->child->label_y = BORDER; menu->child->label_width = menu->pane_width; menu->child->label_height = menu->pane_height; menu->pane_width += 2 * BORDER + SHADOW; menu->pane_height += 2 * BORDER + SHADOW; } /************************************************************************ * * computeMenuSize -- figures out how big a menu is, based on the * items that it contains. * ************************************************************************/ void computeMenuSize(me, menu) MenuJet me; Menu *menu; { int x, y; static int height=0; int maxPixmap = 0; Menu *m; if (height==0) height = me->menu.font->ascent + me->menu.font->descent + me->menu.vMenuPadding; if (menu->parent && menu->parent->paneType == HELP) { computeHelpSize(me, menu->parent); return; } if (menu->title_width == 0) { menu->title_width = XTextWidth(me->menu.font, menu->title, strlen(menu->title)); } menu->label_width = me->menu.hMenuPadding + menu->title_width; menu->label_height = height; x = y = BORDER; if (menu->orientation == VERTICAL) x += 10; menu->pane_width = 0; menu->pane_height = 0; for (m = menu->child; m != NULL; m = m->sibling) { m->label_x = x; m->label_y = y; switch(menu->orientation) { case HORIZONTAL: menu->pane_width += m->label_width; if (m->label_height > menu->pane_height) menu->pane_height = m->label_height; x += m->label_width; break; case VERTICAL: if (m->child != NULL) { if (m->paneType != HELP) { int tmp; tmp = me->menu.submenuPixmap->width + me->menu.hMenuPadding; if (tmp > maxPixmap) maxPixmap = tmp; tmp = me->menu.submenuPixmap->height + me->menu.vMenuPadding; if (tmp > m->label_height) m->label_height = tmp; } else if (me->menu.showHelp) { int tmp; tmp = me->menu.helpPixmap->width + me->menu.hMenuPadding; if (tmp > maxPixmap) maxPixmap = tmp; tmp = me->menu.helpPixmap->height + me->menu.vMenuPadding; if (tmp > m->label_height) m->label_height = tmp; } } if (m->label_width > menu->pane_width) menu->pane_width = m->label_width; /* menu->pane_width = MAX(menu->pane_width, m->label_width + m->label_x); */ menu->pane_height += m->label_height; y += m->label_height; break; } } if (menu->orientation == VERTICAL) menu->pane_width += x; menu->pane_open_x = menu->pane_width + BORDER; menu->pane_width += 2 * BORDER + SHADOW + maxPixmap; menu->pane_height += 2 * BORDER + SHADOW; } /************************************************************************ * * computeAllMenuSizes -- * Iteratively traverse the menu tree (bottom up) and * recompute it. This function is usefully called * when help mode is toggled. * ************************************************************************/ void computeAllMenuSizes(me, menu) MenuJet me; Menu *menu; { Menu *m; struct timeval start, end; if (DEBUG) { gettimeofday(&start, NULL); printf("computeAllMenuSizes: - %d.%d + \n", start.tv_sec, start.tv_usec); } m = menu; while (m != NULL) { while (m->child != NULL) m = m->child; computeMenuSize(me, m); while (m != NULL && m->sibling == NULL) { m = m->parent; if (m != NULL) computeMenuSize(me, m); } if (m != NULL /* && m->sibling != NULL */) m = m->sibling; } if (DEBUG) { gettimeofday(&end, NULL); printf("computeAllMenuSizes: %d.%d = %d.%06.6d\n", end.tv_sec, end.tv_usec, (end.tv_usec > start.tv_usec) ? end.tv_sec - start.tv_sec : end.tv_sec - start.tv_sec - 1, (end.tv_usec > start.tv_usec) ? end.tv_usec - start.tv_usec : end.tv_usec + 1000000 - start.tv_usec ); } } /************************************************************************ * * computeRootMenuSize -- figure out how big the root menu is. * ************************************************************************/ void computeRootMenuSize(me, size) MenuJet me; XjSize *size; { computeMenuSize(me, me->menu.rootMenu); size->width = me->menu.rootMenu->pane_width; size->height = me->menu.rootMenu->pane_height; if (me->menu.screenWidth == 1 && me->menu.rootMenu->orientation == HORIZONTAL) size->width = MAX(DisplayWidth(me->core.display, DefaultScreen(me->core.display)), me->menu.rootMenu->pane_width); #ifdef notdefined me->core.width = me->menu.rootMenu->pane_width; me->core.height = me->menu.rootMenu->pane_height; if (me->menu.screenWidth == 1 && me->menu.rootMenu->orientation == HORIZONTAL) me->core.width = MAX(DisplayWidth(me->core.display, DefaultScreen(me->core.display)), me->menu.rootMenu->pane_width); #endif } /************************************************************************ * * stringToQuark -- quarkifies first string on comma-seperated list * and returns it, leaving string pointing to next item. * ************************************************************************/ static XrmQuark stringToQuark(string) char **string; { char *ptr, tmp; XrmQuark q; ptr = *string; while (isalnum(*ptr)) ptr++; if (ptr == *string) return (XrmQuark) NULL; tmp = *ptr; *ptr = '\0'; q = XrmStringToQuark(*string); #ifdef DEBUGPARSING fprintf(stdout, "%s ", *string); fflush(stdout); #endif *ptr = tmp; if (*ptr == ',') ptr++; while (isspace(*ptr)) ptr++; *string = ptr; return q; } /************************************************************************ * * skippast -- skip past all text until first occurrence of 'what', * except if 'inquotes' is true, then just find the end of the quote. * ************************************************************************/ static void skippast(ptr, inquotes, what) char **ptr; int inquotes; char what; { while (1) { if (inquotes) { while (**ptr != '\0' && **ptr != '"') (*ptr)++; if (**ptr == '\0') break; (*ptr)++; inquotes = 0; } else { while (**ptr != '\0' && **ptr != '"' && **ptr != what) (*ptr)++; if (**ptr == '\0') break; if (**ptr == what) { (*ptr)++; break; } (*ptr)++; inquotes = 1; } } } /************************************************************************ * * countMenuEntries -- count the number of menu entries in a string. * ************************************************************************/ static int countMenuEntries(me, string) MenuJet me; char *string; { int count = 0; struct timeval start, end; if (DEBUG) { gettimeofday(&start, NULL); printf("countMenuEntries: - %d.%d + \n", start.tv_sec, start.tv_usec); } while (*string != '\0') { if ((!strncmp(string, "item", 4)) || (!strncmp(string, "menu", 4)) || (!strncmp(string, "title", 5)) || (!strncmp(string, "separator", 9))) count++; skippast(&string, False, ';'); while (isspace(*string)) string++; } if (DEBUG) { gettimeofday(&end, NULL); printf("countMenuEntries: %d.%d = %d.%06.6d\n", end.tv_sec, end.tv_usec, (end.tv_usec > start.tv_usec) ? end.tv_sec - start.tv_sec : end.tv_sec - start.tv_sec - 1, (end.tv_usec > start.tv_usec) ? end.tv_usec - start.tv_usec : end.tv_usec + 1000000 - start.tv_usec ); } return count; } /************************************************************************ * * parseMenuEntry -- parse entry in 'string' and shove it into 'info'. * ************************************************************************/ /* * Some of this code is *sooooo* gross */ static Boolean parseMenuEntry(me, string, info) MenuJet me; char **string; Item *info; { int qnum, done, got_one; int inquotes = 0; char *ptr, *end, *beginning, *hosttype; Item *lookup; Boolean unfinished = True; char errtext[100]; beginning = ptr = *string; /* remember beginning for better diagnostics */ /* * Zeroes flags for us, too. */ memset(info, 0, sizeof(Item)); /* * Parse menu/item field */ if (*ptr == '!') goto skipit; if (!strncmp(ptr, "item", 4)) { info->type = ItemITEM; info->u.i.supported = 1; ptr += 5; } else if (!strncmp(ptr, "menu", 4)) { info->type = MenuITEM; ptr += 5; } else if (!strncmp(ptr, "help", 4)) { info->type = HelpITEM; ptr += 5; } else if (!strncmp(ptr, "title", 5)) { info->type = TitleITEM; ptr += 6; } else if (!strncmp(ptr, "separator", 9)) { info->type = SeparatorITEM; ptr += 10; } else { sprintf(errtext, "menu line -\n %.70s\n- %s%s", beginning, "does not begin with menu, item, title, separator, ", "or help; ignoring"); XjWarning(errtext); goto youlose; } while (isspace(*ptr)) ptr++; /* * Parse label field */ if (0 == (end = strchr(ptr, ':'))) { sprintf(errtext, "menu line -\n %.70s\n- %s", beginning, "does not contain ':' terminated label; ignoring"); XjWarning(errtext); goto youlose; } *end = '\0'; info->name = XrmStringToQuark(ptr); *end = ':'; ptr = end + 1; /* The rest may come in any order */ while (unfinished) { while (isspace(*ptr)) ptr++; switch(*ptr) { /* * Parse hosttype or menu orientation */ case '+': /* should do this right someday... */ case '-': if (info->type == ItemITEM) { hosttype = getenv("HOSTTYPE"); if (!hosttype) hosttype = HOSTTYPE; if (strncasecmp(ptr + 1, hosttype, strlen(hosttype)) == 0) info->u.i.supported = (*ptr == '+'); if (strncasecmp(ptr + 1, "verify", 6) == 0) { info->flags |= verifyFLAG; info->u.i.verify = (*ptr == '+'); } ptr++; while (*ptr && !isspace(*ptr)) ptr++; break; } /* * Menu orientation flags */ if ((info->type == MenuITEM) && (*(ptr + 1) == 'h')) { info->u.m.orientation = HORIZONTAL; info->flags |= orientationFLAG; ptr += 2; break; } if ((info->type == MenuITEM) && (*(ptr + 1) == 'v')) { info->u.m.orientation = VERTICAL; info->flags |= orientationFLAG; ptr += 2; break; } if (DEBUG) { sprintf(errtext, "'%s' contains unknown flag; ignored", XrmQuarkToString(info->name)); XjWarning(errtext); } while (*ptr != '\0' && !isspace(*ptr) && *ptr != ';') ptr++; break; /* * Parse title field */ case '"': ptr++; /* Deal with star BS... */ if (ptr[0] == ' ' && ptr[1] == ' ' && ptr[2] == ' ') ptr += 3; if (ptr[0] == '*' && ptr[1] == ' ') ptr += 2; /* Rip this code out in a future */ /* release... */ inquotes = 1; if (0 == (end = strchr(ptr, '"'))) { if (info->type == HelpITEM) { sprintf(errtext, "'%s' help does not have closing quote; ignoring", XrmQuarkToString(info->name)); XjWarning(errtext); } else { sprintf(errtext, "'%s' menu definition title does not have closing quote; ignoring", XrmQuarkToString(info->name)); XjWarning(errtext); } goto youlose; } *end = '\0'; info->title = ptr; ptr = end + 1; inquotes = 0; info->flags |= titleFLAG; if (info->type == HelpITEM) { /* * This help is to be appended to an * existing menu item */ lookup = (Item *)hash_lookup(me->menu.Names, info->name); if (lookup == NULL) { sprintf(errtext, "'%s' help entry references nonexistent item", XrmQuarkToString(info->name)); XjWarning(errtext); goto youlose; } if (lookup->type == MenuITEM) { sprintf(errtext, "'%s' help entry cannot be added to a menu", XrmQuarkToString(info->name)); XjWarning(errtext); goto youlose; } lookup->u.i.help = info->title; goto skipit; } break; /* * Parse (possibly) child field */ case '{': ptr++; if (info->type == MenuITEM) { qnum = 0; done = 0; while ((qnum < MAXCHILDREN) && !done) { info->u.m.children[qnum++] = stringToQuark(&ptr); if (info->u.m.children[qnum - 1] == (XrmQuark) NULL) done = 1; } if (!done) { if ((XrmQuark) NULL != stringToQuark(&ptr)) { while (stringToQuark(&ptr) != (XrmQuark) NULL); sprintf(errtext, "'%s' - more than %d child types; truncated", XrmQuarkToString(info->name), MAXCHILDREN); XjWarning(errtext); } } if (info->u.m.children[0] != (XrmQuark) NULL) info->flags |= childrenFLAG; } else /* type != MenuITEM */ { sprintf(errtext, "'%s' non-menu specifies children", XrmQuarkToString(info->name)); XjWarning(errtext); } if (0 == (end = strchr(ptr, '}'))) { sprintf(errtext, "%s: child specifier does not have '}'; trying to be smart", XrmQuarkToString(info->name)); } else ptr = end + 1; break; /* * Parse parent field * qnum always points to an empty slot... */ case '[': qnum = 0; done = 0; got_one = 0; while (!done) { if (*ptr != '[') { done = 1; if (!got_one) { sprintf(errtext, "'%s' missing '[' in menu definition; ignoring", XrmQuarkToString(info->name)); XjWarning(errtext); goto youlose; } else info->weight[qnum - 1] = 0; /* true termination */ } else { ptr++; while ((qnum < MAXPARENTS) && !done) { info->parents[qnum++] = stringToQuark(&ptr); if (info->parents[qnum - 1] == (XrmQuark) NULL) done = 1; else { /* if weight not specified, use 0 */ if (*ptr == '/') { ptr++; info->weight[qnum - 1] = atoi(ptr); while (isdigit(*ptr) || *ptr == '-') ptr++; while (isspace(*ptr)) ptr++; } else info->weight[qnum - 1] = 0; if (*ptr == ',') ptr++; } } if (!done) /* out of space... */ { if (isalnum(*ptr)) /* we weren't done yet... */ { sprintf(errtext, "'%s' more than %d parent types; truncated", XrmQuarkToString(info->name), MAXCHILDREN); XjWarning(errtext); if (0 == (end = strchr(ptr, ']'))) { XjWarning( "and mismatched brackets, too! ignoring"); goto youlose; } done = 1; if (0 != (end = strchr(ptr, ';'))) ptr = end + 1; else { sprintf(errtext, "'%s' missing semicolon; ignoring", XrmQuarkToString(info->name)); XjWarning(errtext); goto youlose; } } } else /* we finished the group; do next */ { done = 0; got_one = 1; info->parents[qnum - 1] = (XrmQuark) NULL; info->weight[qnum - 1] = 1; /* short termination */ if (*ptr == ']') ptr++; else { sprintf(errtext, "'%s' missing ']'; ignoring", XrmQuarkToString(info->name)); XjWarning(errtext); goto youlose; } } } } /* if (info->parents[0] != NULL) */ info->flags |= parentsFLAG; break; /* * End of line? */ case ';': ptr++; unfinished = False; break; /* * Default case */ default: if (info->type == ItemITEM) { if (NULL == (info->u.i.activateProc = XjConvertStringToCallback(&ptr))) { sprintf(errtext, "'%s' - couldn't grok callback; ignoring entry", XrmQuarkToString(info->name)); XjWarning(errtext); goto youlose; } *(ptr-1) = '\0'; info->flags |= activateFLAG; } else { sprintf(errtext, "'%s' - garbage at end of line; ignoring entry", XrmQuarkToString(info->name)); XjWarning(errtext); goto youlose; } break; } /* switch */ } /* while */ while (isspace(*ptr)) ptr++; *string = ptr; /* fprintf(stdout, "%s: ", XrmQuarkToString(info->name)); if (info->flags & titleFLAG) fprintf(stdout, "title "); if (info->flags & activateFLAG) fprintf(stdout, "callback "); if (info->flags & orientationFLAG) fprintf(stdout, "orientation "); if (info->flags & parentsFLAG) fprintf(stdout, "parents "); if (info->flags & childrenFLAG) fprintf(stdout, "children"); fprintf(stdout, "\n"); */ return True; youlose: skipit: skippast(&ptr, inquotes, ';'); while (isspace(*ptr)) ptr++; *string = ptr; return False; } /************************************************************************ * * addMenuEntry -- add a menu entry to the menu struct. * ************************************************************************/ static Boolean addMenuEntry(me, info, i) MenuJet me; Item *info, *i; /* location to put it if new */ { Item *j; TypeDef *t; int m, n; j = (Item *)hash_lookup(me->menu.Names, info->name); if (j != NULL) i = j; if (j != NULL && j->type != info->type) { char errtext[100]; sprintf(errtext, "'%s' - type %s can't override type %s", XrmQuarkToString(info->name), &"item\000menu"[info->type*5], &"item\000menu"[j->type*5]); XjWarning(errtext); return False; } /* * Register the item's name and zero its structure if not an override */ if (j == NULL) { (void)hash_store(me->menu.Names, info->name, i); memset(i, 0, sizeof(Item)); /* if new, init to zeroes */ i->u.i.supported = 1; i->u.i.verify = True; } /* * Put into list if not override... */ if (j == NULL) switch(info->type) { case ItemITEM: i->next = me->menu.firstItem; me->menu.firstItem = i; break; case MenuITEM: i->next = me->menu.firstMenu; me->menu.firstMenu = i; break; } /* * Now merge in information */ i->name = info->name; i->type = info->type; i->flags |= info->flags; /* hey, what the heck! */ if (info->flags & titleFLAG) i->title = info->title; if (info->flags & activateFLAG) i->u.i.activateProc = info->u.i.activateProc; if (info->flags & orientationFLAG) i->u.m.orientation = info->u.m.orientation; if (info->flags & verifyFLAG) i->u.i.verify = info->u.i.verify; i->u.i.supported = i->u.i.supported && info->u.i.supported; if (info->flags & parentsFLAG) { memcpy(i->parents, info->parents, MAXPARENTS * sizeof(XrmQuark)); memcpy(i->weight, info->weight, MAXPARENTS * sizeof(int)); } if (info->flags & childrenFLAG) { /* * First, remove all the old ones. Then add all the new ones. * There may be wasted work here, but at least the computer * will be doing it and not me. */ for (n = 0; i->u.m.children[n] != 0 && n < MAXCHILDREN; n++) { t = (TypeDef *)hash_lookup(me->menu.Types, i->u.m.children[n]); if (t != NULL) /* better not be NULL! */ { for (m = 0; m < MAXMENUSPERTYPE && t->menus[m] != 0 && /* shouldn't happen */ t->menus[m] != i; m++); if (m < MAXMENUSPERTYPE && t->menus[m] == i) { for (; m < MAXMENUSPERTYPE - 1; m++) t->menus[m] = t->menus[m + 1]; t->menus[m] = 0; } } } memcpy(i->u.m.children, info->u.m.children, MAXCHILDREN * sizeof(XrmQuark)); /* * Add the new ones... */ for (n = 0; i->u.m.children[n] != 0 && n < MAXCHILDREN; n++) { t = (TypeDef *)hash_lookup(me->menu.Types, i->u.m.children[n]); if (t == NULL) { t = (TypeDef *)XjMalloc(sizeof(TypeDef)); memset(t, 0, sizeof(TypeDef)); (void)hash_store(me->menu.Types, i->u.m.children[n], t); t->type = i->u.m.children[n]; t->menus[0] = i; } else { for (m = 0; m < MAXMENUSPERTYPE && t->menus[m] != 0; m++); if (m == MAXMENUSPERTYPE) { char errtext[100]; sprintf(errtext, "'%s' not typed as %s due to overflow", XrmQuarkToString(i->name), XrmQuarkToString(t->type)); XjWarning(errtext); } else t->menus[m] = i; } } } if (j == NULL) return True; else return False; } #ifdef notdefined /************************************************************************ * * printTable -- prints out the menu table - for debugging... * ************************************************************************/ static void printTable(t) Item *t; { int i, done; while (t != NULL) { fprintf(stdout, "%d %s %d %d %d %s\n", t->type, XrmQuarkToString(t->name), t->flags, (t->type == MenuITEM) ? t->u.m.orientation : t->u.i.supported, (t->type == ItemITEM) ? t->u.i.verify : 0, t->title); if (t->type == MenuITEM) { for (i = 0; i < MAXCHILDREN && t->u.m.children[i] != 0; i++) { fprintf(stdout, "%s", XrmQuarkToString(t->u.m.children[i])); if ((i < MAXCHILDREN - 1) && t->u.m.children[i+1] != 0) fprintf(stdout, " "); } fprintf(stdout, "\n"); } done = 0; i = 0; while (!done) { if (t->parents[i] == 0) done = 1; while (!done) { if (t->parents[i] != 0) { fprintf(stdout, "%s", XrmQuarkToString(t->parents[i])); fprintf(stdout, " %d", t->weight[i]); if ((i < MAXPARENTS - 1) && t->parents[i+1] != 0) fprintf(stdout, " "); else { done = 1; fprintf(stdout, "\n"); } i++; } } if ((i < MAXPARENTS - 1) && t->weight[i] == 0) done = 1; else { i++; done = 0; } } fprintf(stdout, "!\n"); if (t->type == ItemITEM) { printf("%s\n", "activateStringHere"); printf("%s\n",t->u.i.help); printf("!\n"); } t = t->next; } } #endif /************************************************************************ * * createTablesFromString -- create menu tables from a menu string. * ************************************************************************/ static void createTablesFromString(me, string) MenuJet me; char *string; { int counter = 0; char *ptr; Item info, *location; int num; struct timeval start, end; if (DEBUG) { gettimeofday(&start, NULL); printf("createTablesFromString: - %d.%d + \n", start.tv_sec, start.tv_usec); } ptr = string; num = countMenuEntries(me, ptr); location = (Item *)XjMalloc(num * sizeof(Item)); while (*ptr != '\0') { if (parseMenuEntry(me, &ptr, &info)) { if (addMenuEntry(me, &info, location)) location++; counter++; } } if (DEBUG) { gettimeofday(&end, NULL); printf("createTablesFromString: %d.%d = %d.%06.6d\n", end.tv_sec, end.tv_usec, (end.tv_usec > start.tv_usec) ? end.tv_sec - start.tv_sec : end.tv_sec - start.tv_sec - 1, (end.tv_usec > start.tv_usec) ? end.tv_usec - start.tv_usec : end.tv_usec + 1000000 - start.tv_usec ); } } /************************************************************************ * * createItem -- stick an item into the menu struct... * ************************************************************************/ static Boolean createItem(me, what) MenuJet me; Item *what; { Menu *menuParent = NULL, *item, *here, **hereptr; Item *parent; TypeDef *list; int created = 0, qnum = 0, pnum; while (1) /* this loops through the list of possible parents */ { /* skip past any breaks... */ if (what->parents[qnum] == 0) { if (what->weight[qnum] == 0) /* end of possibilities... we're done */ break; qnum++; /* soft break... possibly create more */ if (qnum >= MAXPARENTS) break; } /* see if there are any possible parents of this type */ list = (TypeDef *)hash_lookup(me->menu.Types, what->parents[qnum]); if (list == NULL && (0 != strcmp("NULL", XrmQuarkToString(what->parents[qnum])))) qnum++; /* type is not NULL and is otherwise undefined */ else { /* there are possible parents */ if (list != NULL) { pnum = 0; while (1) /* on exit from this loop, menuParent points to */ { /* parent, NULL if none */ if ((parent = list->menus[pnum]) == 0) break; /* couldn't create *any* menus of this type */ if ((menuParent = parent->u.m.m) != NULL) break; else { /* this menu doesn't exist... try to create it */ if (createItem(me, parent)) { /* yay! we created our parent! */ menuParent = parent->u.m.m; break; } } pnum++; if (pnum >= MAXMENUSPERTYPE) break; } } if (menuParent == NULL && list != NULL) qnum++; /* no possible parent of this type */ else /* menuParent == NULL && list == NULL */ { /* * We win! Create this item! */ created++; /* fprintf(stdout, "Created %s\n", what->title); */ item = (Menu *)XjMalloc(sizeof(Menu)); if (what->type == MenuITEM) what->u.m.m = item; memset(item, 0, sizeof(Menu)); item->parent = menuParent; if (menuParent == NULL) me->menu.rootMenu = item; else { here = menuParent->child; hereptr = &(menuParent->child); while (here != NULL && ((what->weight[qnum] > here->weight) || ((what->weight[qnum] == here->weight) && strcasecmp(what->title, here->title) > 0))) { hereptr = &(here->sibling); here = here->sibling; } item->sibling = here; *hereptr = item; } item->state = CLOSED; item->paneType = NORMAL; item->title = what->title; item->title_width = what->title_width; item->weight = what->weight[qnum]; if (what->type == MenuITEM) { item->orientation = what->u.m.orientation; item->child = NULL; item->supported = 1; } else { item->activateProc = what->u.i.activateProc; item->supported = what->u.i.supported; item->verify = what->u.i.verify; if (NULL != what->u.i.help) { item->paneType = HELP; item->child = (Menu *)XjMalloc(sizeof(Menu)); memset(item->child, 0, sizeof(Menu)); item->child->title = what->u.i.help; item->child->title_width = what->u.i.help_width; item->child->title_height = what->u.i.help_height; item->child->parent = item; item->child->state = CLOSED; item->child->activateProc = item->activateProc; } } if (what->type == MenuITEM) return True; /* can currently create only one instance of a menu... just break here? */ while (qnum < MAXPARENTS && what->parents[qnum] != 0) qnum++; /* skip past other same-set candidates */ } if (qnum >= MAXPARENTS) break; } } if (created != 0) return True; else return False; } /************************************************************************ * * createMenuFromTables -- build the menu struct. * ************************************************************************/ static void createMenuFromTables(me) MenuJet me; { Item *i; struct timeval start, end; if (DEBUG) { gettimeofday(&start, NULL); printf("createMenuFromTables: - %d.%d + \n", start.tv_sec, start.tv_usec); } for (i = me->menu.firstItem; i != NULL; i = i->next) (void)createItem(me, i); if (DEBUG) { gettimeofday(&end, NULL); printf("createMenuFromTables: %d.%d = %d.%06.6d\n", end.tv_sec, end.tv_usec, (end.tv_usec > start.tv_usec) ? end.tv_sec - start.tv_sec : end.tv_sec - start.tv_sec - 1, (end.tv_usec > start.tv_usec) ? end.tv_usec - start.tv_usec : end.tv_usec + 1000000 - start.tv_usec ); } } /************************************************************************ * * loadFile -- read in a file and return it as one big ol' string. * ************************************************************************/ static char *loadFile(filename) char *filename; { int fd, size; struct stat buf; char *info; char errtext[100]; if (-1 == (fd = open(filename, O_RDONLY, 0))) { sprintf(errtext, "opening '%s': %s", filename, strerror(errno)); XjWarning(errtext); return NULL; } if (-1 == fstat(fd, &buf)) /* could only return EIO */ { sprintf(errtext, "fstat '%s': %s", filename, strerror(errno)); XjWarning(errtext); close(fd); return NULL; } size = (int)buf.st_size; info = (char *)XjMalloc(size + 1); /* one extra for the NULL */ if (-1 == read(fd, info, size)) { sprintf(errtext, "reading '%s': %s", filename, strerror(errno)); XjWarning(errtext); close(fd); XjFree(info); return NULL; } close(fd); info[size] = '\0'; return info; } /************************************************************************ * * destroyChildren -- mass murder... * ************************************************************************/ static void destroyChildren(reap) Menu *reap; { Menu *anal; while (reap != NULL) { if (reap->child != NULL) destroyChildren(reap->child); anal = reap; reap = reap->sibling; XjFree(anal); } } /************************************************************************ * * destroyMenu -- destroy yourself! and your little dog too! * ************************************************************************/ static void destroyMenu(me) MenuJet me; { destroyChildren(me->menu.rootMenu); XDeleteContext(me->core.display, me->menu.rootMenu->menuPane, context); XjFree(me->menu.rootMenu); me->menu.rootMenu = NULL; } /************************************************************************ * * loadNewMenus -- load some new menus into the menu struct. * ************************************************************************/ /* * A lot of this procedure is duplicated from other places - * these things ought to be done in common routines. Prime opportunity * to introduce bugs by changing the other stuff and forgetting to * do it here. */ int loadNewMenus(me, file) MenuJet me; char *file; { XjSize size; char *newItems; Item *i; newItems = loadFile(file); if (newItems != NULL && newItems[0] != '\0') { closeMenuAndAncestorsToLevel(me, me->menu.deepestOpened, me->menu.rootMenu, False); me->menu.deepestOpened = me->menu.rootMenu; if (me->menu.rude == True) { if (me->menu.grabbed == True) XUngrabPointer(me->core.display, CurrentTime); me->menu.grabbed = False; } if (me->menu.buttonDown == 1) me->menu.buttonDown = 0; createTablesFromString(me, newItems); destroyMenu(me); for (i = me->menu.firstMenu; i != NULL; i = i->next) i->u.m.m = NULL; createMenuFromTables(me); me->menu.rootMenu->state = OPENED; me->menu.rootMenu->menuPane = me->core.window; me->menu.rootMenu->pane_x = 0; me->menu.rootMenu->pane_y = 0; me->menu.same = False; me->menu.inside = False; me->menu.buttonDown = 0; if (XCNOMEM == XSaveContext(me->core.display, me->menu.rootMenu->menuPane, context, (caddr_t)me->menu.rootMenu)) XjFatalError("out of memory in XSaveContext"); me->menu.rootMenu->paneType = NORMAL; computeAllMenuSizes(me, me->menu.rootMenu); computeRootMenuSize(me, &size); me->core.width = size.width; me->core.height = size.height; #ifdef notdefined size.width = me->core.width; /* this is an official hack */ size.height = me->core.height; #endif XjResize(XjParent(me), &size); return 0; } else return 1; } /************************************************************************ * * initialize -- the mother of all routines... * ************************************************************************/ static void initialize(me) MenuJet me; { struct timeval start, end; unsigned long foo; char *fontname; XjSize size; Bool font_prop; if (DEBUG) { gettimeofday(&start, NULL); printf("initialize: - %d.%d + \n", start.tv_sec, start.tv_usec); } font_prop = XGetFontProperty(me->menu.font, XA_FONT, &foo); if (font_prop) fontname = XGetAtomName(me->core.display, foo); me->menu.Names = create_hash(HASHSIZE); me->menu.Types = create_hash(HASHSIZE); me->menu.firstItem = NULL; me->menu.firstMenu = NULL; me->menu.items = loadFile(me->menu.file); if (me->menu.items == NULL && me->menu.fallback[0] != '\0') { Jet root=(Jet)me; XjWarning("trying fallback file"); while (XjParent(root)) root=XjParent(root); XjUserWarning(root, NULL, False, "Unable to load menu file, trying fallback file.", "Try restarting dash later to get up-to-date menus."); me->menu.items = loadFile(me->menu.fallback); } if (me->menu.items != NULL && me->menu.items[0] != '\0') /* a bit of a lose, but... */ createTablesFromString(me, me->menu.items); else createTablesFromString(me, NOMENU); if (font_prop) XjFree(fontname); /* * load up user-defined stuff... */ if (me->menu.userFile[0] != '\0') me->menu.userItems = loadFile(me->menu.userFile); if (me->menu.userItems != NULL && me->menu.userItems[0] != '\0') /* a bit of a lose, but... */ createTablesFromString(me, me->menu.userItems); /* * create menus from everything... */ createMenuFromTables(me); #ifdef notdefined if (print) { struct timeval start, end; gettimeofday(&start, NULL); printf("initialize(printTable): - %d.%d + \n", start.tv_sec, start.tv_usec); printTable(me->menu.firstMenu); printTable(me->menu.firstItem); print=0; gettimeofday(&end, NULL); printf("initialize(printTable): %d.%d = %d.%06.6d\n", end.tv_sec, end.tv_usec, (end.tv_usec > start.tv_usec) ? end.tv_sec - start.tv_sec : end.tv_sec - start.tv_sec - 1, (end.tv_usec > start.tv_usec) ? end.tv_usec - start.tv_usec : end.tv_usec + 1000000 - start.tv_usec ); } #endif /* * Here all of the menu tree has been created and filled in * with titles, callbacks, etc. Now we go through and compute the * necessary sizes of all of the menu panes. */ me->menu.rootMenu->paneType = NORMAL; computeAllMenuSizes(me, me->menu.rootMenu); computeRootMenuSize(me, &size); (XjParent(me))->core.width = me->core.width = size.width; (XjParent(me))->core.height = me->core.height = size.height; me->menu.grabbed = False; me->menu.scrollx = 0; if (DEBUG) { gettimeofday(&end, NULL); printf("initialize: %d.%d = %d.%06.6d\n", end.tv_sec, end.tv_usec, (end.tv_usec > start.tv_usec) ? end.tv_sec - start.tv_sec : end.tv_sec - start.tv_sec - 1, (end.tv_usec > start.tv_usec) ? end.tv_usec - start.tv_usec : end.tv_usec + 1000000 - start.tv_usec ); } } /************************************************************************ * * realize -- think about it... * ************************************************************************/ static void realize(me) MenuJet me; { unsigned long valuemask; XGCValues values; int temp; /* * Initialize GC's */ if (me->menu.reverseVideo) { temp = me->menu.foreground; me->menu.foreground = me->menu.background; me->menu.background = temp; } values.function = GXcopy; values.line_width = 0; values.foreground = me->menu.foreground; values.background = me->menu.background; values.font = me->menu.font->fid; values.graphics_exposures = False; valuemask = ( GCForeground | GCBackground | GCFunction | GCFont | GCLineWidth | GCGraphicsExposures ); me->menu.gc = XjCreateGC(me->core.display, me->core.window, valuemask, &values); values.line_width = 0; values.line_style = LineOnOffDash; values.dashes = (char)1; valuemask |= GCLineStyle | GCDashList; me->menu.dash_gc = XjCreateGC(me->core.display, me->core.window, valuemask, &values); me->menu.dot_gc = XjCreateGC(me->core.display, me->core.window, valuemask, &values); XSetDashes(me->core.display, me->menu.dash_gc, 1, "\001\002", 2); XSetDashes(me->core.display, me->menu.dot_gc, 1, "\001\002", 2); valuemask &= ~(GCLineStyle | GCDashList); me->menu.dim_gc = XjCreateGC(me->core.display, me->core.window, valuemask, &values); if (me->menu.grey != NULL) { /* Turn into a toolkit routine... */ Pixmap p; p = XjCreatePixmap(me->core.display, me->core.window, me->menu.grey->width, me->menu.grey->height, DefaultDepth(me->core.display, /* wrong... sigh. */ DefaultScreen(me->core.display))); XCopyPlane(me->core.display, me->menu.grey->pixmap, p, me->menu.gc, 0, 0, me->menu.grey->width, me->menu.grey->height, 0, 0, 1); valuemask = GCFillStyle | GCTile; values.fill_style = FillTiled; values.tile = p; XChangeGC(me->core.display, me->menu.dim_gc, valuemask, &values); } values.line_width = 0; values.foreground = me->menu.background; valuemask = ( GCForeground | GCBackground | GCFunction | GCFont | GCLineWidth | GCGraphicsExposures ); me->menu.background_gc = XjCreateGC(me->core.display, me->core.window, valuemask, &values); values.function = GXxor; values.foreground = (me->menu.foreground ^ me->menu.background); me->menu.invert_gc = XjCreateGC(me->core.display, me->core.window, valuemask, &values); /* * Do some context initialization */ if (!gotContextType) { context = XUniqueContext(); gotContextType ++; } /* rootMenu is always opened */ me->menu.deepestOpened = me->menu.rootMenu; me->menu.rootMenu->state = OPENED; me->menu.rootMenu->menuPane = me->core.window; me->menu.rootMenu->pane_x = 0; me->menu.rootMenu->pane_y = 0; me->menu.same = False; me->menu.inside = False; me->menu.buttonDown = 0; XjSelectInput(me->core.display, me->core.window, ExposureMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | EnterWindowMask /* | LeaveWindowMask */ /*| StructureNotifyMask */); /* * Send the events for this window to us */ XjRegisterWindow(me->core.window, me); /* * We also want to remember what part of the tree * this menu belongs to */ if (XCNOMEM == XSaveContext(me->core.display, me->menu.rootMenu->menuPane, context, (caddr_t)me->menu.rootMenu)) XjFatalError("out of memory in XSaveContext"); /* * futz around with "rightJet"... */ if (me->menu.rightJet) { me->menu.right_jet = XjFindJet(me->menu.rightJet, XjParent(me)); if (me->menu.right_jet) { XjSize size; int x = me->menu.rootMenu->pane_width + me->menu.rootMenu->pane_x; size.width = me->core.width - x; size.height = me->core.height; XjMove(me->menu.right_jet, x, me->menu.rootMenu->pane_y); XjResize(me->menu.right_jet, &size); } } } static int (*def_handler)(); static int menu_error=0; static int handler(display, error) Display *display; XErrorEvent *error; { if (error->error_code == BadWindow || error->error_code == BadAtom) { menu_error=1; return 0; } def_handler(display, error); return 0; /* it'll never get this far anyway... */ } /************************************************************************ * * findMenu -- figure out where the user clicked... * ************************************************************************/ void findMenu(me, who, what, start, window, rx, ry) MenuJet me; Menu **who; /* what menu we found the mouse to be in */ int *what; /* what state that menu ought to be in */ Menu *start; /* where to start scanning the menu tree */ Window window; /* window mouse location reported relative to */ int rx, ry; /* where the mouse is */ { int x, y; Menu *level, *ptr; Window scratch; menu_error=0; def_handler = XSetErrorHandler(handler); if (window != me->core.window) (void)XTranslateCoordinates(me->core.display, window, me->core.window, rx, ry, &rx, &ry, &scratch); (void) XSetErrorHandler(def_handler); if (menu_error) { *who=NULL; menu_error=0; return; } menu_error=0; #ifdef DEBUGEVENTS fprintf(stderr, "find: %d, %d\n",rx, ry); #endif for (level = start; level != NULL; level = level->parent) { x = rx - level->pane_x; y = ry - level->pane_y; if (level->state == OPENED) { switch(level->orientation) { case HORIZONTAL: for (ptr = level->child; ptr != NULL; ptr = ptr->sibling) if (x >= ptr->label_x && y >= BORDER && x < ptr->label_x + ptr->label_width && y < level->pane_height - BORDER - SHADOW) { *who = ptr; if (ptr->paneType == HELP || ptr->child == NULL) *what = SELECTED; else *what = OPENED; return; } break; case VERTICAL: for (ptr = level->child; ptr != NULL; ptr = ptr->sibling) if (x >= BORDER && y >= ptr->label_y && x < level->pane_width - BORDER - SHADOW && y < ptr->label_y + ptr->label_height) { *who = ptr; *what = CLOSED; /* if (ptr->paneType == HELP && me->menu.showHelp == 0) *what = SELECTED; else */ if (x >= level->pane_open_x) { if (ptr->child != NULL && (ptr->paneType != HELP || (ptr->paneType == HELP && me->menu.showHelp != 0))) *what = OPENED; } else *what = SELECTED; /* if (*what == SELECTED && ptr->activateProc == NULL) *what = CLOSED; */ return; } break; } if (x >= 0 && x < level->pane_width && y >= 0 && y < level->pane_height) { *who = SAME; return; } } } *who = NULL; return; } /************************************************************************ * * highlightMenu -- highlight the current item. * ************************************************************************/ static void highlightMenu(me, menu, state, gc) MenuJet me; Menu *menu; int state; GC gc; { #ifdef DEBUGSETTINGS fprintf(stderr, "Invert %s\n", menu->title); #endif if (menu->parent->paneType == HELP) return; switch(menu->parent->orientation) { case HORIZONTAL: XDrawRectangle(me->core.display, menu->parent->menuPane, gc, menu->label_x + me->menu.hMenuPadding / 4, BORDER + me->menu.vMenuPadding / 4, menu->label_width - me->menu.hMenuPadding / 2 - 1, menu->parent->pane_height - 2 * BORDER - SHADOW - 1 - me->menu.vMenuPadding / 2); break; case VERTICAL: /* make sure pane_open_x gets inited */ if (state == OPENED) XDrawRectangle(me->core.display, menu->parent->menuPane, gc, menu->parent->pane_open_x + me->menu.hMenuPadding / 4, menu->label_y + me->menu.vMenuPadding / 4, menu->parent->pane_width - BORDER - SHADOW - menu->parent->pane_open_x - 1 - me->menu.hMenuPadding / 2, menu->label_height - 1 - me->menu.vMenuPadding / 2); else if (state == SELECTED) XDrawRectangle(me->core.display, menu->parent->menuPane, gc, BORDER + me->menu.hMenuPadding / 4, menu->label_y + me->menu.vMenuPadding / 4, menu->parent->pane_open_x - BORDER - 1 - me->menu.hMenuPadding / 2, menu->label_height - 1 - me->menu.vMenuPadding / 2); break; } } /************************************************************************ * * offset -- move the menu bar... there be beasties here... * ************************************************************************/ static void offset(me, menu, delta) MenuJet me; Menu *menu; int delta; { if (menu->parent != NULL) offset(me, menu->parent, delta); else me->menu.scrollx += delta; /* Not quite right... close... if (menu->menuPane == me->core.window) { me_moving = 1; XMoveWindow(me->core.display, menu->menuPane, me->core.x + delta, me->core.y); } else */ menu->x += delta; me_moving = 1; XMoveWindow(me->core.display, menu->menuPane, menu->x, menu->y); } /************************************************************************ * * woffset -- offset and Warp! (more beasties...) * ************************************************************************/ static void woffset(me, menu, delta, warp) MenuJet me; Menu *menu; int delta; Boolean warp; { offset(me, menu, delta); if (warp) XWarpPointer(me->core.display, None, None, 0, 0, 0, 0, delta, 0); } /************************************************************************ * * openMenu -- user clicked on another menu... open up! * ************************************************************************/ /* * Add window caching code? */ static void openMenu(bar, menu) MenuJet bar; Menu *menu; { Menu *checkMenu; int x, y, delta; Window parentwindow, scratch; int screen; WindowJet wj; #ifdef DEBUGSETTINGS fprintf(stderr, "Open %s\n", menu->title); #endif if (menu->child && !menu->menuPane /* twm problems... */) { XSetWindowAttributes attributes; int attribsMask; attributes.override_redirect = True; attribsMask = CWOverrideRedirect; attributes.background_pixel = bar->menu.background; attribsMask |= CWBackPixel; screen = DefaultScreen(bar->core.display); /* * Create the menu pane */ parentwindow = RootWindow(bar->core.display, screen); /* * This is a hack to figure out where to put the window - * it's definitely the wrong way to go, but I get anxious * sometimes. I'll fix it later. Honest. * (He's lying.) */ XTranslateCoordinates(bar->core.display, menu->parent->menuPane, parentwindow, 0, 0, &x, &y, &scratch); switch(menu->parent->orientation) { case HORIZONTAL: x += menu->label_x; y += menu->parent->pane_height/* - BORDER*/; menu->pane_x = menu->parent->pane_x + menu->label_x; menu->pane_y = menu->parent->pane_y + menu->parent->pane_height; break; case VERTICAL: x += menu->parent->pane_width/* - BORDER */; y += menu->label_y; menu->pane_x = menu->parent->pane_x + menu->parent->pane_width; menu->pane_y = menu->parent->pane_y + menu->label_y; break; } delta = 0; if (bar->menu.moveMenus) { for (checkMenu = menu->parent->child; checkMenu != NULL; checkMenu = checkMenu->sibling) delta = MAX(delta, checkMenu->pane_width); if ((delta = (DisplayWidth(bar->core.display, DefaultScreen(bar->core.display)) - (x + delta))) < 0) { woffset(bar, menu->parent, delta, True); bar->menu.responsibleParent = menu->parent; } } menu->x = x + (delta < 0 ? delta : 0); menu->y = y; menu->menuPane = XCreateWindow(bar->core.display, parentwindow, menu->x, menu->y, menu->pane_width, menu->pane_height, 0, DefaultDepth(bar->core.display, DefaultScreen(bar->core.display)), InputOutput, CopyFromParent, attribsMask, &attributes); wj = (WindowJet) XjParent(bar); if (wj->window.cursor != (Cursor) NULL) XDefineCursor(bar->core.display, menu->menuPane, wj->window.cursor); XSelectInput(bar->core.display, menu->menuPane, ExposureMask | StructureNotifyMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask); XMapWindow(bar->core.display, menu->menuPane); /* * Send the events for this window to us */ XjRegisterWindow(menu->menuPane, bar); /* * We also want to remember what part of the tree * this menu belongs to */ if (XCNOMEM == XSaveContext(bar->core.display, menu->menuPane, context, (caddr_t)menu)) XjFatalError("out of memory in XSaveContext"); } } /************************************************************************ * * closeMenu -- user closed a menu... * ************************************************************************/ static void closeMenu(bar, menu, warp) MenuJet bar; Menu *menu; Boolean warp; { int freespace; #ifdef DEBUGSETTINGS fprintf(stderr, "Close %s\n", menu->title); #endif if (menu->menuPane != (Window) NULL) { XDestroyWindow(bar->core.display, menu->menuPane); menu->menuPane = (Window) NULL; if (bar->menu.scrollx != 0 && menu->parent != NULL && bar->menu.responsibleParent == menu) { menu = menu->parent; bar->menu.responsibleParent = menu; freespace = DisplayWidth(bar->core.display, DefaultScreen(bar->core.display)) - (menu->x + menu->pane_width); woffset(bar, menu, MIN(freespace, -bar->menu.scrollx), warp); } else if (bar->menu.scrollx != 0 && menu->parent == bar->menu.rootMenu) woffset(bar, menu->parent, -bar->menu.scrollx, warp); } } /************************************************************************ * * setMenu -- I have no idea... * ************************************************************************/ static void setMenu(me, menu, newState, warp) MenuJet me; Menu *menu; int newState; Boolean warp; { switch(newState) { case CLOSED: switch(menu->state) { case CLOSED: break; case OPENED: closeMenu(me, menu, warp); highlightMenu(me, menu, OPENED, me->menu.background_gc); break; case SELECTED: highlightMenu(me, menu, SELECTED, me->menu.background_gc); break; } menu->state = CLOSED; break; case SELECTED: switch(menu->state) { case CLOSED: highlightMenu(me, menu, SELECTED, (menu->activateProc && menu->supported) ? me->menu.gc : me->menu.dot_gc); break; case OPENED: closeMenu(me, menu, warp); highlightMenu(me, menu, OPENED, me->menu.background_gc); highlightMenu(me, menu, SELECTED, (menu->activateProc && menu->supported) ? me->menu.gc : me->menu.dot_gc); break; case SELECTED: break; } menu->state = SELECTED; break; case OPENED: switch(menu->state) { case CLOSED: highlightMenu(me, menu, OPENED, me->menu.dot_gc); openMenu(me, menu); break; case OPENED: break; case SELECTED: highlightMenu(me, menu, SELECTED, me->menu.background_gc); highlightMenu(me, menu, OPENED, me->menu.dot_gc); openMenu(me, menu); break; } menu->state = OPENED; break; } } /************************************************************************ * * closeMenuAndAncestorsToLevel -- close menu and ancestors up to * level specified... * ************************************************************************/ static void closeMenuAndAncestorsToLevel(bar, start, end, warp) MenuJet bar; Menu *start, *end; Boolean warp; { Menu *ptr; for (ptr = start; ptr != end && ptr != end->parent; ptr = ptr->parent) setMenu(bar, ptr, CLOSED, warp); XFlush(bar->core.display); } /************************************************************************ * * drawBackground -- draw background of menu window... * ************************************************************************/ static void drawBackground(me, menu) MenuJet me; Menu *menu; { int width = menu->pane_width; int height = menu->pane_height; if (me->core.window == menu->menuPane) width = me->core.width; /* XFillRectangle(me->core.display, menu->menuPane, me->menu.background_gc, BORDER, BORDER, width - 2 * BORDER - SHADOW, height - 2 * BORDER - SHADOW); */ XDrawRectangle(me->core.display, menu->menuPane, me->menu.gc, 0, 0, width - SHADOW - 1, height - SHADOW - 1); XDrawLine(me->core.display, menu->menuPane, me->menu.gc, BORDER, height - 1, width - 1, height - 1); XDrawLine(me->core.display, menu->menuPane, me->menu.gc, width - 1, BORDER, width - 1, height - 1); } /************************************************************************ * * drawPane -- draw the items in a menu pane. * ************************************************************************/ static void drawPane(me, eventMenu) MenuJet me; Menu *eventMenu; { Menu *m; drawBackground(me, eventMenu); if (eventMenu->paneType == HELP) drawHelp(me, eventMenu); else { if (eventMenu->orientation == VERTICAL) XDrawLine(me->core.display, eventMenu->menuPane, me->menu.dash_gc, eventMenu->pane_open_x, BORDER, eventMenu->pane_open_x, eventMenu->pane_height - BORDER); for (m = eventMenu->child; m != NULL; m = m->sibling) { if (m->activateProc != NULL) XDrawString(me->core.display, eventMenu->menuPane, m->supported ? me->menu.gc : me->menu.dim_gc, m->label_x + me->menu.hMenuPadding / 2 - 10, m->label_y + me->menu.vMenuPadding / 2 + me->menu.font->ascent, "*", 1); XDrawString(me->core.display, eventMenu->menuPane, m->supported ? me->menu.gc : me->menu.dim_gc, m->label_x + me->menu.hMenuPadding / 2, m->label_y + me->menu.vMenuPadding / 2 + me->menu.font->ascent, m->title, strlen(m->title)); switch(m->state) { case OPENED: highlightMenu(me, m, OPENED, me->menu.dot_gc); break; case SELECTED: highlightMenu(me, m, SELECTED, (m->activateProc && m->supported) ? me->menu.gc : me->menu.dot_gc); break; case CLOSED: break; } if (eventMenu->orientation == VERTICAL) { if (m->paneType == HELP) { if (me->menu.showHelp) XCopyPlane(me->core.display, me->menu.helpPixmap->pixmap, eventMenu->menuPane, me->menu.gc, 0, 0, me->menu.helpPixmap->width, me->menu.helpPixmap->height, eventMenu->pane_open_x + me->menu.hMenuPadding / 2, m->label_y + me->menu.vMenuPadding / 2, 1); } else if (m->child != NULL) XCopyPlane(me->core.display, me->menu.submenuPixmap->pixmap, eventMenu->menuPane, me->menu.gc, 0, 0, me->menu.submenuPixmap->width, me->menu.submenuPixmap->height, eventMenu->pane_open_x + me->menu.hMenuPadding / 2, m->label_y + me->menu.vMenuPadding / 2, 1); } } } } /************************************************************************ * * event_handler -- deal with all the x events that get thrown at us. * ************************************************************************/ static Boolean event_handler(me, event) MenuJet me; XEvent *event; { MenuInfo info; Menu *eventMenu; Menu *m; int state, newState; if (XCNOENT == XFindContext(me->core.display, event->xany.window, context, (caddr_t *)&eventMenu)) return False; switch(event->type) { case GraphicsExpose: case Expose: if (DEBUG) fprintf(stderr, "Exposure\n"); if (event->xexpose.count != 0) break; if (eventMenu->menuPane != (Window) NULL) { drawPane(me, eventMenu); } /* hack to support clock in menu bar */ if (me->menu.right_jet) XjExpose(me->menu.right_jet, event); #ifdef notdefined if (me->core.sibling != NULL && me->core.sibling->core.window == event->xany.window) XjExpose(me->core.sibling, event); #endif break; case ButtonRelease: #ifdef DEBUGEVENTS fprintf(stderr, "ButtonRelease to %d (%d) %d\n", event->xbutton.button, me->menu.buttonDown - 1, event->xbutton.state); #endif findMenu(me, &m, &newState, me->menu.deepestOpened, event->xbutton.window, event->xbutton.x, event->xbutton.y); if (me->menu.buttonDown == 1 && (me->menu.deepestOpened->state == SELECTED || m == NULL)) { state = me->menu.deepestOpened->state; closeMenuAndAncestorsToLevel(me, me->menu.deepestOpened, me->menu.rootMenu, False); if (me->menu.inside == True || me->menu.same == True) { info.menubar = me; /* there's potentially a bug here, but I don't want to think about it right now. */ if ((me->menu.deepestOpened->parent != NULL && me->menu.deepestOpened->parent->paneType == HELP) || state != SELECTED) info.menu = me->menu.deepestOpened->parent; /* NOP */ else { info.null = NULL; info.menu = me->menu.deepestOpened; if (me->menu.deepestOpened->supported && me->menu.deepestOpened->activateProc) if (me->menu.verifyProc != NULL && me->menu.deepestOpened->verify) XjCallCallbacks(&info, me->menu.verifyProc, NULL); else XjCallCallbacks(&info, me->menu.deepestOpened->activateProc, event); } } me->menu.deepestOpened = me->menu.rootMenu; me->menu.inside = False; me->menu.buttonDown = 0; if (me->menu.rude == True) { if (me->menu.grabbed == True) XUngrabPointer(me->core.display, CurrentTime); me->menu.grabbed = False; } } else if (me->menu.buttonDown == 1) me->menu.buttonDown = 0; break; case ButtonPress: if (me->core.window == event->xany.window) { if (WindowVisibility(XjParent(me)) != VisibilityUnobscured) XRaiseWindow(me->core.display, me->core.window); else { Jet rj = me->menu.right_jet; if (rj && ((event->xbutton.x > rj->core.x) && (event->xbutton.x < rj->core.x + rj->core.width)) && ((event->xbutton.y > rj->core.y) && (event->xbutton.y < rj->core.y + rj->core.height)) && rj->core.classRec->core_class.event) { if (rj->core.classRec->core_class.event(rj, event)) break; } } } #ifdef DEBUGEVENTS fprintf(stderr, "ButtonPress to %d (%d) %d\n", event->xbutton.button, me->menu.buttonDown + 1, event->xbutton.state); #endif if (me->menu.buttonDown == 0) { if (me->menu.rude == True && me->menu.grabbed == False) { XGrabPointer(me->core.display, me->core.window, False, ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | EnterWindowMask /* | LeaveWindowMask */ , GrabModeAsync, GrabModeAsync, None, None, CurrentTime); me->menu.grabbed = True; } me->menu.buttonDown = 1; } /* break; */ case MotionNotify: /* #ifdef DEBUGERVENTS fprintf(stderr, "MotionNotify: %d, %d; %d\n", event->xmotion.x, event->xmotion.y, event->xmotion.state); #endif */ if (me->menu.buttonDown == 0) break; findMenu(me, &m, &newState, me->menu.deepestOpened, event->xmotion.window, event->xmotion.x, event->xmotion.y); if (m == SAME) { me->menu.same = True; break; } me->menu.same = False; /* We're outside of all menus */ if (m == NULL) { if (me->menu.inside == False) break; /* just moving around outside menus */ /* Here we've just moved outside of all menus */ /* if (m == NULL && me->menu.inside == True) */ setMenu(me, me->menu.deepestOpened, CLOSED, True); me->menu.deepestOpened = me->menu.deepestOpened->parent; me->menu.inside = False; break; } /* m != NULL ... */ if (me->menu.inside == False) /* just moved into something */ { if (m == me->menu.deepestOpened) /* merge? */ { if (m->state != newState) setMenu(me, m, newState, True); me->menu.inside = True; /* this is an unlikely case */ break; } /* so we just moved into something that's not what's already opened */ me->menu.inside = True; } if (m == me->menu.deepestOpened) /* merge? */ { if (m->state != newState) setMenu(me, m, newState, True); break; } if (m->parent == me->menu.deepestOpened) /* opened a deeper child */ { me->menu.deepestOpened = m; setMenu(me, me->menu.deepestOpened, newState, True); break; } /* * Here we have moved into a sibling or parent of the deepest * child. We must close everything from the level of that * parent/sibling on down. */ closeMenuAndAncestorsToLevel(me, me->menu.deepestOpened, m, True); if (m->menuPane == (Window) NULL) setMenu(me, m, newState, True); me->menu.deepestOpened = m; break; case CirculateNotify: if (DEBUG) printf("Menu - circ: x=%d, y=%d\n", event->xconfigure.x, event->xconfigure.y); break; case CreateNotify: if (DEBUG) printf("Menu - create: x=%d, y=%d\n", event->xconfigure.x, event->xconfigure.y); break; case ConfigureNotify: if (me->core.window == event->xany.window) { if (DEBUG) printf("Menu - config: x=%d, y=%d\n", event->xconfigure.x, event->xconfigure.y); me->menu.rootMenu->x = event->xconfigure.x; me->menu.rootMenu->y = event->xconfigure.y; } break; case DestroyNotify: XDeleteContext(me->core.display, event->xany.window, context); XjUnregisterWindow(event->xany.window, me); break; case GravityNotify: case MapNotify: case ReparentNotify: case UnmapNotify: break; case EnterNotify: if (me->menu.autoRaise && me->core.window == event->xany.window && WindowVisibility(XjParent(me)) != VisibilityUnobscured) XRaiseWindow(me->core.display, me->core.window); break; case LeaveNotify: if (me->menu.buttonDown == 0) { Cursor cursor = XjCreateFontCursor(me->core.display, XC_ul_angle); XDefineCursor(me->core.display, me->core.window, cursor); } default: return False; } return True; } /************************************************************************ * * tab -- space out... * ************************************************************************/ static void tab(n) int n; { while (n--) fprintf(stdout, " "); } /************************************************************************ * * printmenu -- print out menu struct in a human-readable form. * ************************************************************************/ static void printmenu(first, tabbing) Menu *first; int tabbing; { Menu *ptr; char *start, *end; for (ptr = first; ptr != NULL; ptr = ptr->sibling) { tab(tabbing); if (ptr->activateProc != NULL) fprintf(stdout, "*"); if (!strncmp(" ", ptr->title, 3)) fprintf(stdout, "%s", ptr->title + 3); else if (!strncmp("* ", ptr->title, 2)) fprintf(stdout, "%s", ptr->title + 2); else fprintf(stdout, "%s", ptr->title); if (ptr->child != NULL) fprintf(stdout, ":\n"); else fprintf(stdout, "\n"); if (ptr->paneType == HELP && ptr->child != NULL) { start = ptr->child->title; while (*start != '\0') { end = strchr(start, '\n'); if (end == NULL) end = start + strlen(start); tab(tabbing + 1); fprintf(stdout, "%.*s\n", end - start, start); start = end; if (*start != '\0') start++; } } else if (ptr->child != NULL) printmenu(ptr->child, tabbing + 1); } } /************************************************************************ * * PrintMenu -- call the printmenu proc above... this proc is * suitable for use in a procedure table, the other is not... * ************************************************************************/ void PrintMenu(me) MenuJet me; { int tabbing; Menu *ptr; tabbing = 0; ptr = me->menu.rootMenu; printmenu(ptr, tabbing); }