📄 tkmenudraw.c
字号:
* Results: * None. * * Side effects: * Fields of menu entries are changed to reflect their * current positions, and the size of the menu window * itself may be changed. * *-------------------------------------------------------------- */static voidComputeMenuGeometry(clientData) ClientData clientData; /* Structure describing menu. */{ TkMenu *menuPtr = (TkMenu *) clientData; if (menuPtr->tkwin == NULL) { return; } if (menuPtr->menuType == MENUBAR) { TkpComputeMenubarGeometry(menuPtr); } else { TkpComputeStandardMenuGeometry(menuPtr); } if ((menuPtr->totalWidth != Tk_ReqWidth(menuPtr->tkwin)) || (menuPtr->totalHeight != Tk_ReqHeight(menuPtr->tkwin))) { Tk_GeometryRequest(menuPtr->tkwin, menuPtr->totalWidth, menuPtr->totalHeight); } /* * Must always force a redisplay here if the window is mapped * (even if the size didn't change, something else might have * changed in the menu, such as a label or accelerator). The * resize will force a redisplay above. */ TkEventuallyRedrawMenu(menuPtr, (TkMenuEntry *) NULL); menuPtr->menuFlags &= ~RESIZE_PENDING;}/* *---------------------------------------------------------------------- * * TkMenuSelectImageProc -- * * This procedure is invoked by the image code whenever the manager * for an image does something that affects the size of contents * of an image displayed in a menu entry when it is selected. * * Results: * None. * * Side effects: * Arranges for the menu to get redisplayed. * *---------------------------------------------------------------------- */voidTkMenuSelectImageProc(clientData, x, y, width, height, imgWidth, imgHeight) ClientData clientData; /* Pointer to widget record. */ int x, y; /* Upper left pixel (within image) * that must be redisplayed. */ int width, height; /* Dimensions of area to redisplay * (may be <= 0). */ int imgWidth, imgHeight; /* New dimensions of image. */{ register TkMenuEntry *mePtr = (TkMenuEntry *) clientData; if ((mePtr->entryFlags & ENTRY_SELECTED) && !(mePtr->menuPtr->menuFlags & REDRAW_PENDING)) { mePtr->menuPtr->menuFlags |= REDRAW_PENDING; Tcl_DoWhenIdle(DisplayMenu, (ClientData) mePtr->menuPtr); }}/* *---------------------------------------------------------------------- * * DisplayMenu -- * * This procedure is invoked to display a menu widget. * * Results: * None. * * Side effects: * Commands are output to X to display the menu in its * current mode. * *---------------------------------------------------------------------- */static voidDisplayMenu(clientData) ClientData clientData; /* Information about widget. */{ register TkMenu *menuPtr = (TkMenu *) clientData; register TkMenuEntry *mePtr; register Tk_Window tkwin = menuPtr->tkwin; int index, strictMotif; Tk_Font tkfont = menuPtr->tkfont; Tk_FontMetrics menuMetrics; int width; menuPtr->menuFlags &= ~REDRAW_PENDING; if ((menuPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { return; } if (menuPtr->menuType == MENUBAR) { Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), menuPtr->border, menuPtr->borderWidth, menuPtr->borderWidth, Tk_Width(tkwin) - 2 * menuPtr->borderWidth, Tk_Height(tkwin) - 2 * menuPtr->borderWidth, 0, TK_RELIEF_FLAT); } strictMotif = Tk_StrictMotif(menuPtr->tkwin); /* * See note in ComputeMenuGeometry. We don't want to be doing font metrics * all of the time. */ Tk_GetFontMetrics(menuPtr->tkfont, &menuMetrics); /* * Loop through all of the entries, drawing them one at a time. */ for (index = 0; index < menuPtr->numEntries; index++) { mePtr = menuPtr->entries[index]; if (menuPtr->menuType != MENUBAR) { if (!(mePtr->entryFlags & ENTRY_NEEDS_REDISPLAY)) { continue; } } mePtr->entryFlags &= ~ENTRY_NEEDS_REDISPLAY; if (menuPtr->menuType == MENUBAR) { width = mePtr->width; } else { if (mePtr->entryFlags & ENTRY_LAST_COLUMN) { width = Tk_Width(menuPtr->tkwin) - mePtr->x - menuPtr->activeBorderWidth; } else { width = mePtr->width + menuPtr->borderWidth; } } TkpDrawMenuEntry(mePtr, Tk_WindowId(menuPtr->tkwin), tkfont, &menuMetrics, mePtr->x, mePtr->y, width, mePtr->height, strictMotif, 1); if ((index > 0) && (menuPtr->menuType != MENUBAR) && mePtr->columnBreak) { mePtr = menuPtr->entries[index - 1]; Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), menuPtr->border, mePtr->x, mePtr->y + mePtr->height, mePtr->width, Tk_Height(tkwin) - mePtr->y - mePtr->height - menuPtr->activeBorderWidth, 0, TK_RELIEF_FLAT); } } if (menuPtr->menuType != MENUBAR) { int x, y, height; if (menuPtr->numEntries == 0) { x = y = menuPtr->borderWidth; width = Tk_Width(tkwin) - 2 * menuPtr->activeBorderWidth; height = Tk_Height(tkwin) - 2 * menuPtr->activeBorderWidth; } else { mePtr = menuPtr->entries[menuPtr->numEntries - 1]; Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), menuPtr->border, mePtr->x, mePtr->y + mePtr->height, mePtr->width, Tk_Height(tkwin) - mePtr->y - mePtr->height - menuPtr->activeBorderWidth, 0, TK_RELIEF_FLAT); x = mePtr->x + mePtr->width; y = mePtr->y + mePtr->height; width = Tk_Width(tkwin) - x - menuPtr->activeBorderWidth; height = Tk_Height(tkwin) - y - menuPtr->activeBorderWidth; } Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), menuPtr->border, x, y, width, height, 0, TK_RELIEF_FLAT); } Tk_Draw3DRectangle(menuPtr->tkwin, Tk_WindowId(tkwin), menuPtr->border, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), menuPtr->borderWidth, menuPtr->relief);}/* *-------------------------------------------------------------- * * TkMenuEventProc -- * * This procedure is invoked by the Tk dispatcher for various * events on menus. * * Results: * None. * * Side effects: * When the window gets deleted, internal structures get * cleaned up. When it gets exposed, it is redisplayed. * *-------------------------------------------------------------- */voidTkMenuEventProc(clientData, eventPtr) ClientData clientData; /* Information about window. */ XEvent *eventPtr; /* Information about event. */{ TkMenu *menuPtr = (TkMenu *) clientData; if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) { TkEventuallyRedrawMenu(menuPtr, (TkMenuEntry *) NULL); } else if (eventPtr->type == ConfigureNotify) { TkEventuallyRecomputeMenu(menuPtr); TkEventuallyRedrawMenu(menuPtr, (TkMenuEntry *) NULL); } else if (eventPtr->type == ActivateNotify) { if (menuPtr->menuType == TEAROFF_MENU) { TkpSetMainMenubar(menuPtr->interp, menuPtr->tkwin, NULL); } } else if (eventPtr->type == DestroyNotify) { if (menuPtr->tkwin != NULL) { menuPtr->tkwin = NULL; Tcl_DeleteCommandFromToken(menuPtr->interp, menuPtr->widgetCmd); } if (menuPtr->menuFlags & REDRAW_PENDING) { Tcl_CancelIdleCall(DisplayMenu, (ClientData) menuPtr); } if (menuPtr->menuFlags & RESIZE_PENDING) { Tcl_CancelIdleCall(ComputeMenuGeometry, (ClientData) menuPtr); } TkDestroyMenu(menuPtr); }}/* *---------------------------------------------------------------------- * * TkMenuImageProc -- * * This procedure is invoked by the image code whenever the manager * for an image does something that affects the size of contents * of an image displayed in a menu entry. * * Results: * None. * * Side effects: * Arranges for the menu to get redisplayed. * *---------------------------------------------------------------------- */voidTkMenuImageProc(clientData, x, y, width, height, imgWidth, imgHeight) ClientData clientData; /* Pointer to widget record. */ int x, y; /* Upper left pixel (within image) * that must be redisplayed. */ int width, height; /* Dimensions of area to redisplay * (may be <= 0). */ int imgWidth, imgHeight; /* New dimensions of image. */{ register TkMenu *menuPtr = ((TkMenuEntry *)clientData)->menuPtr; if ((menuPtr->tkwin != NULL) && !(menuPtr->menuFlags & RESIZE_PENDING)) { menuPtr->menuFlags |= RESIZE_PENDING; Tcl_DoWhenIdle(ComputeMenuGeometry, (ClientData) menuPtr); }}/* *---------------------------------------------------------------------- * * TkPostTearoffMenu -- * * Posts a menu on the screen. Used to post tearoff menus. On Unix, * all menus are posted this way. Adjusts the menu's position * so that it fits on the screen, and maps and raises the menu. * * Results: * Returns a standard Tcl Error. * * Side effects: * The menu is posted. * *---------------------------------------------------------------------- */intTkPostTearoffMenu(interp, menuPtr, x, y) Tcl_Interp *interp; /* The interpreter of the menu */ TkMenu *menuPtr; /* The menu we are posting */ int x; /* The root X coordinate where we * are posting */ int y; /* The root Y coordinate where we * are posting */{ int vRootX, vRootY, vRootWidth, vRootHeight; int tmp, result; TkActivateMenuEntry(menuPtr, -1); TkRecomputeMenu(menuPtr); result = TkPostCommand(menuPtr); if (result != TCL_OK) { return result; } /* * The post commands could have deleted the menu, which means * we are dead and should go away. */ if (menuPtr->tkwin == NULL) { return TCL_OK; } /* * Adjust the position of the menu if necessary to keep it * visible on the screen. There are two special tricks to * make this work right: * * 1. If a virtual root window manager is being used then * the coordinates are in the virtual root window of * menuPtr's parent; since the menu uses override-redirect * mode it will be in the *real* root window for the screen, * so we have to map the coordinates from the virtual root * (if any) to the real root. Can't get the virtual root * from the menu itself (it will never be seen by the wm) * so use its parent instead (it would be better to have an * an option that names a window to use for this...). * 2. The menu may not have been mapped yet, so its current size * might be the default 1x1. To compute how much space it * needs, use its requested size, not its actual size. * * Note that this code assumes square screen regions and all * positive coordinates. This does not work on a Mac with * multiple monitors. But then again, Tk has other problems * with this. */ Tk_GetVRootGeometry(Tk_Parent(menuPtr->tkwin), &vRootX, &vRootY, &vRootWidth, &vRootHeight); x += vRootX; y += vRootY; tmp = WidthOfScreen(Tk_Screen(menuPtr->tkwin)) - Tk_ReqWidth(menuPtr->tkwin); if (x > tmp) { x = tmp; } if (x < 0) { x = 0; } tmp = HeightOfScreen(Tk_Screen(menuPtr->tkwin)) - Tk_ReqHeight(menuPtr->tkwin); if (y > tmp) { y = tmp; } if (y < 0) { y = 0; } Tk_MoveToplevelWindow(menuPtr->tkwin, x, y); if (!Tk_IsMapped(menuPtr->tkwin)) { Tk_MapWindow(menuPtr->tkwin); } TkWmRestackToplevel((TkWindow *) menuPtr->tkwin, Above, NULL); return TCL_OK;}/* *-------------------------------------------------------------- * * TkPostSubmenu -- * * This procedure arranges for a particular submenu (i.e. the * menu corresponding to a given cascade entry) to be * posted. * * Results: * A standard Tcl return result. Errors may occur in the * Tcl commands generated to post and unpost submenus. * * Side effects: * If there is already a submenu posted, it is unposted. * The new submenu is then posted. * *-------------------------------------------------------------- */intTkPostSubmenu(interp, menuPtr, mePtr) Tcl_Interp *interp; /* Used for invoking sub-commands and * reporting errors. */ register TkMenu *menuPtr; /* Information about menu as a whole. */ register TkMenuEntry *mePtr; /* Info about submenu that is to be * posted. NULL means make sure that * no submenu is posted. */{ char string[30]; int result, x, y; if (mePtr == menuPtr->postedCascade) { return TCL_OK; } if (menuPtr->postedCascade != NULL) { /* * Note: when unposting a submenu, we have to redraw the entire * parent menu. This is because of a combination of the following * things: * (a) the submenu partially overlaps the parent. * (b) the submenu specifies "save under", which causes the X * server to make a copy of the information under it when it * is posted. When the submenu is unposted, the X server * copies this data back and doesn't generate any Expose * events for the parent. * (c) the parent may have redisplayed itself after the submenu * was posted, in which case the saved information is no * longer correct. * The simplest solution is just force a complete redisplay of * the parent. */ TkEventuallyRedrawMenu(menuPtr, (TkMenuEntry *) NULL); result = Tcl_VarEval(interp, menuPtr->postedCascade->name, " unpost", (char *) NULL); menuPtr->postedCascade = NULL; if (result != TCL_OK) { return result; } } if ((mePtr != NULL) && (mePtr->name != NULL) && Tk_IsMapped(menuPtr->tkwin)) { /* * Position the cascade with its upper left corner slightly * below and to the left of the upper right corner of the * menu entry (this is an attempt to match Motif behavior). * * The menu has to redrawn so that the entry can change relief. */ Tk_GetRootCoords(menuPtr->tkwin, &x, &y); AdjustMenuCoords(menuPtr, mePtr, &x, &y, string); result = Tcl_VarEval(interp, mePtr->name, " post ", string, (char *) NULL); if (result != TCL_OK) { return result; } menuPtr->postedCascade = mePtr; TkEventuallyRedrawMenu(menuPtr, mePtr); } return TCL_OK;}/* *---------------------------------------------------------------------- * * AdjustMenuCoords -- * * Adjusts the given coordinates down and the left to give a Motif * look. * * Results: * None. * * Side effects: * The menu is eventually redrawn if necessary. * *---------------------------------------------------------------------- */static voidAdjustMenuCoords(menuPtr, mePtr, xPtr, yPtr, string) TkMenu *menuPtr; TkMenuEntry *mePtr; int *xPtr; int *yPtr; char *string;{ if (menuPtr->menuType == MENUBAR) { *xPtr += mePtr->x; *yPtr += mePtr->y + mePtr->height; } else { *xPtr += Tk_Width(menuPtr->tkwin) - menuPtr->borderWidth - menuPtr->activeBorderWidth - 2; *yPtr += mePtr->y + menuPtr->activeBorderWidth + 2; } sprintf(string, "%d %d", *xPtr, *yPtr);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -