📄 menus.c
字号:
/*****************************************************************************//** Copyright 1988 by Evans & Sutherland Computer Corporation, **//** Salt Lake City, Utah **//** Portions Copyright 1989 by the Massachusetts Institute of Technology **//** Cambridge, Massachusetts **//** **//** All Rights Reserved **//** **//** Permission to use, copy, modify, and distribute this software and **//** its documentation for any purpose and without fee is hereby **//** granted, provided that the above copyright notice appear in all **//** copies and that both that copyright notice and this permis- **//** sion notice appear in supporting documentation, and that the **//** names of Evans & Sutherland and M.I.T. not be used in advertising **//** in publicity pertaining to distribution of the software without **//** specific, written prior permission. **//** **//** EVANS & SUTHERLAND AND M.I.T. DISCLAIM ALL WARRANTIES WITH REGARD **//** TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- **//** ABILITY AND FITNESS, IN NO EVENT SHALL EVANS & SUTHERLAND OR **//** M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAM- **//** AGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA **//** OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER **//** TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE **//** OR PERFORMANCE OF THIS SOFTWARE. **//*****************************************************************************//**************************************************************************** * This module is based on Twm, but has been significantly modified * by Rob Nation ****************************************************************************//*********************************************************************** * The rest of it is all my fault -- MLM * mwm - "LessTif Window Manager" ***********************************************************************/#include <LTconfig.h>#include <stdio.h>#include <string.h>#include <ctype.h>#include <Xm/Xm.h>#include <Xm/MwmUtil.h>#include <X11/keysym.h>#include "mwm.h"static int menu_on = 0;static MenuRoot *ActiveMenu = NULL; /* the active menu */static MenuItem *ActiveItem = NULL; /* the active menu item */int menuFromFrameOrWindowOrTitlebar = False;int Stashed_X, Stashed_Y;static int MenuY = 0;static MenuRoot *PrevMenu = NULL;static MenuItem *PrevItem = NULL;static int PrevY = 0;/* * Calculate the pixel offsets to the start of the character position we * want to underline and to the next character in the string. Shrink by * one pixel from each end and the draw a line that long two pixels below * the character... */static voiddraw_underline(ScreenInfo *scr, Window w, GC gc, int x, int y, char *txt, int posn){ int off1 = XTextWidth(scr->components[MWM_MENU].font, txt, posn); int off2 = XTextWidth(scr->components[MWM_MENU].font, txt, posn + 1) - 1; XDrawLine(dpy, w, gc, x + off1, y + 2, x + off2, y + 2);}/* * Draws two horizontal lines to form a separator */static voiddraw_separator(Window w, GC TopGC, GC BottomGC, int x1, int y1, int x2, int y2, int extra_off){ XDrawLine(dpy, w, TopGC, x1, y1, x2, y2); XDrawLine(dpy, w, BottomGC, x1 - extra_off, y1 + 1, x2 + extra_off, y2 + 1);}/* * Draws a little Triangle pattern within a window */static voiddraw_arrow(Window w, GC GC1, GC GC2, GC GC3, int l, int u, int r, int b){ int m; m = (u + b) / 2; XDrawLine(dpy, w, GC1, l, u, l, b); XDrawLine(dpy, w, GC2, l, b, r, m); XDrawLine(dpy, w, GC3, r, m, l, u);}/* * add relief lines to a rectangular window */static voidrelieve_rectangle(Window win, int x, int y, int w, int h, GC Hilite, GC Shadow){ XDrawLine(dpy, win, Hilite, x, y, w + x - 1, y); XDrawLine(dpy, win, Hilite, x, y, x, h + y - 1); XDrawLine(dpy, win, Shadow, x, h + y - 1, w + x - 1, h + y - 1); XDrawLine(dpy, win, Shadow, w + x - 1, y, w + x - 1, h + y - 1);}/* * add relief lines to the sides only of a rectangular window */static voidrelieve_half_rectangle(Window win, int x, int y, int w, int h, GC Hilite, GC Shadow){ XDrawLine(dpy, win, Hilite, x, y - 1, x, h + y); XDrawLine(dpy, win, Hilite, x + 1, y, x + 1, h + y - 1); XDrawLine(dpy, win, Shadow, w + x - 1, y - 1, w + x - 1, h + y); XDrawLine(dpy, win, Shadow, w + x - 2, y, w + x - 2, h + y - 1);}/* * Checks the function described in menuItem mi, and sees if it * is an allowed function for window Tmp_Win, * according to the motif way of life. * * This routine is used to determine whether or not to grey out menu items. * FIXME: This needs to be beefed up to handle other functions (like * f.send_msg) -- MLM */static Booleanfunction_allowed(MwmWindow *tmp, MenuItem *mi){ /* Complex functions are a little tricky... ignore them for now */ /* Move is a funny hint. Keeps it out of the menu, but you're still * allowed * to move. */ if ((mi->func == F_MOVE) && tmp && !(tmp->functions & MWM_FUNC_MOVE)) return False; if (mi->func == F_RESIZE && tmp && !(tmp->functions & MWM_FUNC_RESIZE)) return False; /* Cannot iconify if the window is already iconified or the window does not allow minimize */ if ((mi->func == F_ICONIFY && tmp) && ((tmp->flags & ICONIFIED) || !(tmp->functions & MWM_FUNC_MINIMIZE))) return False; /* Cannot resize if iconified */ if (mi->func == F_RESIZE && tmp && (tmp->flags & ICONIFIED)) return False; /* Can only normalize if iconified or maximized */ if (mi->func == F_NORMALIZE && tmp && !(tmp->flags & ICONIFIED) && !(tmp->flags & MAXIMIZED)) return False; if (mi->func == F_MAXIMIZE && tmp && !(tmp->functions & MWM_FUNC_MAXIMIZE)) return False; if (mi->func == F_CLOSE && tmp && !(tmp->functions & MWM_FUNC_CLOSE)) return False; return True;}/* * draws a single entry in a poped up menu */static voidpaint_entry(ScreenInfo *scr, MwmWindow *tmp, MenuRoot * mr, MenuItem *mi){ int y_offset, text_y, d, y_height; GC ShadowGC, ReliefGC, currentGC; y_offset = mi->y_offset; y_height = mi->y_height; text_y = y_offset + scr->components[MWM_MENU].f_y; ShadowGC = scr->components[MWM_MENU].bot_GC; ReliefGC = scr->components[MWM_MENU].top_GC; if ((!mi->prev) || (!mi->prev->state)) XClearArea(dpy, mr->w, 0, y_offset - 1, mr->width, y_height + 2, 0); else XClearArea(dpy, mr->w, 0, y_offset + 1, mr->width, y_height - 1, 0); if ((mi->state) && (mi->func != F_TITLE) && (mi->func != F_NOP) && *mi->item) { relieve_rectangle(mr->w, 3, y_offset, mr->width - 5, mi->y_height, ReliefGC, ShadowGC); relieve_rectangle(mr->w, 2, y_offset - 1, mr->width - 3, mi->y_height + 2, ReliefGC, ShadowGC); } relieve_half_rectangle(mr->w, 0, y_offset - 1, mr->width, y_height + 2, ReliefGC, ShadowGC); if (mi->func == F_TITLE) { text_y += HEIGHT_EXTRA >> 1; XDrawLine(dpy, mr->w, ShadowGC, 2, y_offset + y_height - 2, mr->width - 3, y_offset + y_height - 2); XDrawLine(dpy, mr->w, ShadowGC, 2, y_offset + y_height - 4, mr->width - 3, y_offset + y_height - 4); } else text_y += HEIGHT_EXTRA >> 1; if (mi->func == F_NOP && *mi->item == 0) { draw_separator(mr->w, ShadowGC, ReliefGC, 2, y_offset - 1 + HEIGHT_SEPARATOR / 2, mr->width - 3, y_offset - 1 + HEIGHT_SEPARATOR / 2, 0); } if (mi->next == NULL) draw_separator(mr->w, ShadowGC, ShadowGC, 1, mr->height - 2, mr->width - 2, mr->height - 2, 1); if (mi == mr->first) draw_separator(mr->w, ReliefGC, ReliefGC, 0, 0, mr->width - 1, 0, -1); if (function_allowed(tmp, mi)) currentGC = scr->components[MWM_MENU].normal_GC; else { /* MLM: FIXME: SHOULD GRAY OUT ITEM */ currentGC = scr->components[MWM_MENU].grayed_GC; } if (*mi->item) XDrawString(dpy, mr->w, currentGC, mi->x, text_y, mi->item, mi->strlen); if (mi->strlen2 > 0 && mi->item2 && *mi->item2) XDrawString(dpy, mr->w, currentGC, mi->x2, text_y, mi->item2, mi->strlen2); /* pete@tecc.co.uk: If the item has a hot key, underline it */ if (mi->hotkey > 0) draw_underline(scr, mr->w, currentGC, mi->x, text_y, mi->item, mi->hotkey - 1); if (mi->hotkey < 0) draw_underline(scr, mr->w, currentGC, mi->x2, text_y, mi->item2, -1 - mi->hotkey); d = (scr->components[MWM_MENU].f_height + HEIGHT_EXTRA - 7) / 2; if (mi->func == F_POPUP) { if (mi->state) draw_arrow(mr->w, ShadowGC, ReliefGC, ShadowGC, mr->width - d - 8, y_offset + d - 1, mr->width - d - 1, y_offset + d + 7); else draw_arrow(mr->w, ReliefGC, ShadowGC, ReliefGC, mr->width - d - 8, y_offset + d - 1, mr->width - d - 1, y_offset + d + 7); }}/* * draws the entire menu */static voidpaint_menu(ScreenInfo *scr, MwmWindow *tmp, MenuRoot * mr, XEvent *e){ MenuItem *mi; for (mi = mr->first; mi != NULL; mi = mi->next) { /* be smart about handling the expose, redraw only the entries * that we need to */ if (e->xexpose.y < (mi->y_offset + mi->y_height) && (e->xexpose.y + e->xexpose.height) > mi->y_offset) { paint_entry(scr, tmp, mr, mi); } } XSync(dpy, 0);}/* * Updates menu display to reflect the highlighted item */static intfind_entry(ScreenInfo *scr, MwmWindow *tmp){ MenuItem *mi; MenuRoot *actual_mr; int retval = MENU_NOP; MenuRoot *PrevPrevMenu; MenuItem *PrevPrevItem; int PrevPrevY; int x, y, ChildY; Window Child; XQueryPointer(dpy, scr->root_win, &JunkRoot, &Child, &JunkX, &ChildY, &x, &y, &JunkMask); XQueryPointer(dpy, ActiveMenu->w, &JunkRoot, &JunkChild, &JunkX, &ChildY, &x, &y, &JunkMask); /* look for the entry that the mouse is in */ for (mi = ActiveMenu->first; mi; mi = mi->next) if (y >= mi->y_offset && y < mi->y_offset + mi->y_height) break; if (x < 0 || x > ActiveMenu->width) mi = NULL; /* if we weren't on the active entry, let's turn the old active one off */ if ((ActiveItem) && (mi != ActiveItem)) { ActiveItem->state = 0; paint_entry(scr, tmp, ActiveMenu, ActiveItem); } /* if we weren't on the active item, change the active item and turn it * on */ if ((mi != ActiveItem) && (mi != NULL) && function_allowed(tmp, mi)) { mi->state = 1; paint_entry(scr, tmp, ActiveMenu, mi); } ActiveItem = mi; if (ActiveItem) { /* create a new sub-menu */ if (ActiveItem->func == F_POPUP) { PrevPrevMenu = PrevMenu; PrevPrevItem = PrevItem; PrevPrevY = PrevY; PrevY = MenuY; PrevMenu = ActiveMenu; PrevItem = ActiveItem; retval = MENU_PopupMenu(scr, ActiveItem->menu); /* Unfortunately, this is needed (why?) for multi-screen * operation */ MISC_FlushExpose(ActiveMenu->w); for (mi = ActiveMenu->first; mi != NULL; mi = mi->next) paint_entry(scr, tmp, ActiveMenu, mi); XSync(dpy, 0); MenuY = PrevY; PrevMenu = PrevPrevMenu; PrevItem = PrevPrevItem; PrevY = PrevPrevY; } } /* end a sub-menu */ if (XFindContext(dpy, Child, MenuContext, (XPointer *)&actual_mr) == XCNOENT) { return retval; } if (actual_mr != ActiveMenu) { if (actual_mr == PrevMenu) { if ((PrevItem->y_offset + PrevY > ChildY) || ((PrevItem->y_offset + PrevItem->y_height + PrevY) < ChildY)) { return SUBMENU_DONE; } } else return SUBMENU_DONE; } return retval;}/* * Function called from update_menu instead of Keyboard_Shortcuts() * when a KeyPress event is received. If the key is alphanumeric, * then the menu is scanned for a matching hot key. Otherwise if * it was the escape key then the menu processing is aborted. * If none of these conditions are true, then the default processing * routine is called. */static voidmenu_shortcuts(ScreenInfo *scr, XEvent *ev){ MenuItem *mi; KeySym keysym = XLookupKeysym(&ev->xkey, 0); /* Try to match hot keys */ if (((keysym >= XK_a) && (keysym <= XK_z)) || /* consider alphabetic */ ((keysym >= XK_0) && (keysym <= XK_9))) { /* ...or numeric keys */ /* Search menu for matching hotkey */ for (mi = ActiveMenu->first; mi; mi = mi->next) { char key; if (mi->hotkey == 0) continue; /* Item has no hotkey */ key = (mi->hotkey > 0) ? /* Extract hot character */ mi->item[mi->hotkey - 1] : mi->item2[-1 - mi->hotkey]; /* Convert to lower case to match the keysym */ if (isupper(key)) key = tolower(key); if (keysym == key) { /* Force a menu exit */ ActiveItem = mi; ev->type = ButtonRelease; return; } } } switch (keysym) { case XK_Escape: /* escape exits menu */ ActiveItem = NULL; ev->type = ButtonRelease; break; /* Nothing special --- Allow other shortcuts (cursor movement) */ default: MISC_KeyboardShortcut(scr, ev, ButtonRelease); break; }}/* * pop up a pull down menu */static Booleanpop_up_menu(ScreenInfo *scr, MenuRoot * menu, int x, int y){ if ((!menu) || (menu->w == None) || (menu->items == 0) || (menu->in_use)) return False; menu_on++; COLOR_PushRootColorMap(scr); Stashed_X = x; Stashed_Y = y; /* pop up the menu */ ActiveMenu = menu; ActiveItem = NULL; /* clip to screen */ if (x + menu->width > scr->d_width - 2) x = scr->d_width - menu->width - 2; if (x < 0) x = 0; if (y + menu->height > scr->d_height - 2) y = scr->d_height - menu->height - 2; if (y < 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -