tkMenuDraw.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:29k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tkMenuDraw.c --
  3.  *
  4.  * This module implements the platform-independent drawing and
  5.  * geometry calculations of menu widgets.
  6.  *
  7.  * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  *
  12.  * RCS: @(#) $Id: tkMenuDraw.c,v 1.3.20.2 2003/11/12 00:04:53 hobbs Exp $
  13.  */
  14. #include "tkMenu.h"
  15. /*
  16.  * Forward declarations for procedures defined later in this file:
  17.  */
  18. static void AdjustMenuCoords _ANSI_ARGS_ ((TkMenu *menuPtr,
  19.     TkMenuEntry *mePtr, int *xPtr, int *yPtr,
  20.     char *string));
  21. static void ComputeMenuGeometry _ANSI_ARGS_((
  22.     ClientData clientData));
  23. static void DisplayMenu _ANSI_ARGS_((ClientData clientData));
  24. /*
  25.  *----------------------------------------------------------------------
  26.  *
  27.  * TkMenuInitializeDrawingFields --
  28.  *
  29.  * Fills in drawing fields of a new menu. Called when new menu is
  30.  * created by MenuCmd.
  31.  *
  32.  * Results:
  33.  * None.
  34.  *
  35.  * Side effects:
  36.  * menuPtr fields are initialized.
  37.  *
  38.  *----------------------------------------------------------------------
  39.  */
  40. void
  41. TkMenuInitializeDrawingFields(menuPtr)
  42.     TkMenu *menuPtr; /* The menu we are initializing. */
  43. {
  44.     menuPtr->textGC = None;
  45.     menuPtr->gray = None;
  46.     menuPtr->disabledGC = None;
  47.     menuPtr->activeGC = None;
  48.     menuPtr->indicatorGC = None;
  49.     menuPtr->disabledImageGC = None;
  50.     menuPtr->totalWidth = menuPtr->totalHeight = 0;
  51. }
  52. /*
  53.  *----------------------------------------------------------------------
  54.  *
  55.  * TkMenuInitializeEntryDrawingFields --
  56.  *
  57.  * Fills in drawing fields of a new menu entry. Called when an
  58.  * entry is created.
  59.  *
  60.  * Results:
  61.  * None.
  62.  *
  63.  * Side effects:
  64.  * None.
  65.  *
  66.  *----------------------------------------------------------------------
  67.  */
  68. void
  69. TkMenuInitializeEntryDrawingFields(mePtr)
  70.     TkMenuEntry *mePtr; /* The menu we are initializing. */
  71. {
  72.     mePtr->width = 0;
  73.     mePtr->height = 0;
  74.     mePtr->x = 0;
  75.     mePtr->y = 0;
  76.     mePtr->indicatorSpace = 0;
  77.     mePtr->labelWidth = 0;
  78.     mePtr->textGC = None;
  79.     mePtr->activeGC = None;
  80.     mePtr->disabledGC = None;
  81.     mePtr->indicatorGC = None;
  82. }
  83. /*
  84.  *----------------------------------------------------------------------
  85.  *
  86.  * TkMenuFreeDrawOptions --
  87.  *
  88.  * Frees up any structures allocated for the drawing of a menu.
  89.  * Called when menu is deleted.
  90.  *
  91.  * Results:
  92.  * None.
  93.  *
  94.  * Side effects:
  95.  * Storage is released.
  96.  *
  97.  *----------------------------------------------------------------------
  98.  */
  99. void
  100. TkMenuFreeDrawOptions(menuPtr)
  101.     TkMenu *menuPtr;
  102. {
  103.     if (menuPtr->textGC != None) {
  104. Tk_FreeGC(menuPtr->display, menuPtr->textGC);
  105.     }
  106.     if (menuPtr->disabledImageGC != None) {
  107. Tk_FreeGC(menuPtr->display, menuPtr->disabledImageGC);
  108.     }
  109.     if (menuPtr->gray != None) {
  110. Tk_FreeBitmap(menuPtr->display, menuPtr->gray);
  111.     }
  112.     if (menuPtr->disabledGC != None) {
  113. Tk_FreeGC(menuPtr->display, menuPtr->disabledGC);
  114.     }
  115.     if (menuPtr->activeGC != None) {
  116. Tk_FreeGC(menuPtr->display, menuPtr->activeGC);
  117.     }
  118.     if (menuPtr->indicatorGC != None) {
  119. Tk_FreeGC(menuPtr->display, menuPtr->indicatorGC);
  120.     }
  121. }
  122. /*
  123.  *----------------------------------------------------------------------
  124.  *
  125.  * TkMenuEntryFreeDrawOptions --
  126.  *
  127.  * Frees up drawing structures for a menu entry. Called when
  128.  * menu entry is freed.
  129.  *
  130.  * RESULTS:
  131.  * None.
  132.  *
  133.  * Side effects:
  134.  * Storage is freed.
  135.  *
  136.  *----------------------------------------------------------------------
  137.  */
  138. void
  139. TkMenuEntryFreeDrawOptions(mePtr)
  140.     TkMenuEntry *mePtr;
  141. {
  142.     if (mePtr->textGC != None) {
  143. Tk_FreeGC(mePtr->menuPtr->display, mePtr->textGC);
  144.     }
  145.     if (mePtr->disabledGC != None) {
  146. Tk_FreeGC(mePtr->menuPtr->display, mePtr->disabledGC);
  147.     }
  148.     if (mePtr->activeGC != None) {
  149. Tk_FreeGC(mePtr->menuPtr->display, mePtr->activeGC);
  150.     }
  151.     if (mePtr->indicatorGC != None) {
  152. Tk_FreeGC(mePtr->menuPtr->display, mePtr->indicatorGC);
  153.     }
  154. }
  155. /*
  156.  *----------------------------------------------------------------------
  157.  *
  158.  * TkMenuConfigureDrawOptions --
  159.  *
  160.  * Sets the menu's drawing attributes in preparation for drawing
  161.  * the menu.
  162.  *
  163.  * RESULTS:
  164.  * None.
  165.  *
  166.  * Side effects:
  167.  * Storage is allocated.
  168.  *
  169.  *----------------------------------------------------------------------
  170.  */
  171. void
  172. TkMenuConfigureDrawOptions(menuPtr)
  173.     TkMenu *menuPtr; /* The menu we are configuring. */
  174. {
  175.     XGCValues gcValues;
  176.     GC newGC;
  177.     unsigned long mask;
  178.     Tk_3DBorder border, activeBorder;
  179.     Tk_Font tkfont;
  180.     XColor *fg, *activeFg, *indicatorFg;
  181.  
  182.     /*
  183.      * A few options need special processing, such as setting the
  184.      * background from a 3-D border, or filling in complicated
  185.      * defaults that couldn't be specified to Tk_ConfigureWidget.
  186.      */
  187.     border = Tk_Get3DBorderFromObj(menuPtr->tkwin, menuPtr->borderPtr);
  188.     Tk_SetBackgroundFromBorder(menuPtr->tkwin, border);
  189.     tkfont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr);
  190.     gcValues.font = Tk_FontId(tkfont);
  191.     fg = Tk_GetColorFromObj(menuPtr->tkwin, menuPtr->fgPtr);
  192.     gcValues.foreground = fg->pixel;
  193.     gcValues.background = Tk_3DBorderColor(border)->pixel;
  194.     newGC = Tk_GetGC(menuPtr->tkwin, GCForeground|GCBackground|GCFont,
  195.     &gcValues);
  196.     if (menuPtr->textGC != None) {
  197. Tk_FreeGC(menuPtr->display, menuPtr->textGC);
  198.     }
  199.     menuPtr->textGC = newGC;
  200.     gcValues.font = Tk_FontId(tkfont);
  201.     gcValues.background = Tk_3DBorderColor(border)->pixel;
  202.     if (menuPtr->disabledFgPtr != NULL) {
  203. XColor *disabledFg;
  204. disabledFg = Tk_GetColorFromObj(menuPtr->tkwin, 
  205. menuPtr->disabledFgPtr);
  206. gcValues.foreground = disabledFg->pixel;
  207. mask = GCForeground|GCBackground|GCFont;
  208.     } else {
  209. gcValues.foreground = gcValues.background;
  210. mask = GCForeground;
  211. if (menuPtr->gray == None) {
  212.     menuPtr->gray = Tk_GetBitmap(menuPtr->interp, menuPtr->tkwin,
  213.     "gray50");
  214. }
  215. if (menuPtr->gray != None) {
  216.     gcValues.fill_style = FillStippled;
  217.     gcValues.stipple = menuPtr->gray;
  218.     mask = GCForeground|GCFillStyle|GCStipple;
  219. }
  220.     }
  221.     newGC = Tk_GetGC(menuPtr->tkwin, mask, &gcValues);
  222.     if (menuPtr->disabledGC != None) {
  223. Tk_FreeGC(menuPtr->display, menuPtr->disabledGC);
  224.     }
  225.     menuPtr->disabledGC = newGC;
  226.     gcValues.foreground = Tk_3DBorderColor(border)->pixel;
  227.     if (menuPtr->gray == None) {
  228. menuPtr->gray = Tk_GetBitmap(menuPtr->interp, menuPtr->tkwin,
  229. "gray50");
  230.     }
  231.     if (menuPtr->gray != None) {
  232. gcValues.fill_style = FillStippled;
  233. gcValues.stipple = menuPtr->gray;
  234. newGC = Tk_GetGC(menuPtr->tkwin, 
  235.     GCForeground|GCFillStyle|GCStipple, &gcValues);
  236.     }
  237.     if (menuPtr->disabledImageGC != None) {
  238. Tk_FreeGC(menuPtr->display, menuPtr->disabledImageGC);
  239.     }
  240.     menuPtr->disabledImageGC = newGC;
  241.     gcValues.font = Tk_FontId(tkfont);
  242.     activeFg = Tk_GetColorFromObj(menuPtr->tkwin, menuPtr->activeFgPtr);
  243.     gcValues.foreground = activeFg->pixel;
  244.     activeBorder = Tk_Get3DBorderFromObj(menuPtr->tkwin, 
  245.     menuPtr->activeBorderPtr);
  246.     gcValues.background = Tk_3DBorderColor(activeBorder)->pixel;
  247.     newGC = Tk_GetGC(menuPtr->tkwin, GCForeground|GCBackground|GCFont,
  248.     &gcValues);
  249.     if (menuPtr->activeGC != None) {
  250. Tk_FreeGC(menuPtr->display, menuPtr->activeGC);
  251.     }
  252.     menuPtr->activeGC = newGC;
  253.     indicatorFg = Tk_GetColorFromObj(menuPtr->tkwin, 
  254.     menuPtr->indicatorFgPtr);
  255.     gcValues.foreground = indicatorFg->pixel;
  256.     gcValues.background = Tk_3DBorderColor(border)->pixel;
  257.     newGC = Tk_GetGC(menuPtr->tkwin, GCForeground|GCBackground|GCFont,
  258.     &gcValues);
  259.     if (menuPtr->indicatorGC != None) {
  260. Tk_FreeGC(menuPtr->display, menuPtr->indicatorGC);
  261.     }
  262.     menuPtr->indicatorGC = newGC;
  263. }
  264. /*
  265.  *----------------------------------------------------------------------
  266.  *
  267.  * TkMenuConfigureEntryDrawOptions --
  268.  *
  269.  * Calculates any entry-specific draw options for the given menu
  270.  * entry.
  271.  *
  272.  * Results:
  273.  * Returns a standard Tcl error.
  274.  *
  275.  * Side effects:
  276.  * Storage may be allocated.
  277.  *
  278.  *----------------------------------------------------------------------
  279.  */
  280. int
  281. TkMenuConfigureEntryDrawOptions(mePtr, index)
  282.     TkMenuEntry *mePtr;
  283.     int index;
  284. {
  285.     XGCValues gcValues;
  286.     GC newGC, newActiveGC, newDisabledGC, newIndicatorGC;
  287.     unsigned long mask;
  288.     Tk_Font tkfont;
  289.     TkMenu *menuPtr = mePtr->menuPtr;
  290.     tkfont = Tk_GetFontFromObj(menuPtr->tkwin,
  291.     (mePtr->fontPtr != NULL) ? mePtr->fontPtr : menuPtr->fontPtr);
  292.     
  293.     if (mePtr->state == ENTRY_ACTIVE) {
  294. if (index != menuPtr->active) {
  295.     TkActivateMenuEntry(menuPtr, index);
  296. }
  297.     } else {
  298. if (index == menuPtr->active) {
  299.     TkActivateMenuEntry(menuPtr, -1);
  300. }
  301.     }
  302.     if ((mePtr->fontPtr != NULL)
  303.     || (mePtr->borderPtr != NULL)
  304.     || (mePtr->fgPtr != NULL)
  305.     || (mePtr->activeBorderPtr != NULL)
  306.     || (mePtr->activeFgPtr != NULL)
  307.     || (mePtr->indicatorFgPtr != NULL)) {
  308. XColor *fg, *indicatorFg, *activeFg;
  309. Tk_3DBorder border, activeBorder;
  310.     
  311. fg = Tk_GetColorFromObj(menuPtr->tkwin, (mePtr->fgPtr != NULL)
  312. ? mePtr->fgPtr : menuPtr->fgPtr);
  313. gcValues.foreground = fg->pixel;
  314. border = Tk_Get3DBorderFromObj(menuPtr->tkwin, 
  315. (mePtr->borderPtr != NULL) ? mePtr->borderPtr 
  316. : menuPtr->borderPtr);
  317. gcValues.background = Tk_3DBorderColor(border)->pixel;
  318. gcValues.font = Tk_FontId(tkfont);
  319. /*
  320.  * Note: disable GraphicsExpose events;  we know there won't be
  321.  * obscured areas when copying from an off-screen pixmap to the
  322.  * screen and this gets rid of unnecessary events.
  323.  */
  324. gcValues.graphics_exposures = False;
  325. newGC = Tk_GetGC(menuPtr->tkwin,
  326. GCForeground|GCBackground|GCFont|GCGraphicsExposures,
  327. &gcValues);
  328. indicatorFg = Tk_GetColorFromObj(menuPtr->tkwin, 
  329. (mePtr->indicatorFgPtr != NULL) ? mePtr->indicatorFgPtr
  330. : menuPtr->indicatorFgPtr);
  331. gcValues.foreground = indicatorFg->pixel;
  332. newIndicatorGC = Tk_GetGC(menuPtr->tkwin,
  333. GCForeground|GCBackground|GCGraphicsExposures,
  334. &gcValues);
  335. if ((menuPtr->disabledFgPtr != NULL) || (mePtr->image != NULL)) {
  336.     XColor *disabledFg;
  337.     disabledFg = Tk_GetColorFromObj(menuPtr->tkwin, 
  338.     menuPtr->disabledFgPtr);
  339.     gcValues.foreground = disabledFg->pixel;
  340.     mask = GCForeground|GCBackground|GCFont|GCGraphicsExposures;
  341. } else {
  342.     gcValues.foreground = gcValues.background;
  343.     gcValues.fill_style = FillStippled;
  344.     gcValues.stipple = menuPtr->gray;
  345.     mask = GCForeground|GCFillStyle|GCStipple;
  346. }
  347. newDisabledGC = Tk_GetGC(menuPtr->tkwin, mask, &gcValues);
  348. activeFg = Tk_GetColorFromObj(menuPtr->tkwin, 
  349. (mePtr->activeFgPtr != NULL) ? mePtr->activeFgPtr
  350. : menuPtr->activeFgPtr);
  351. activeBorder = Tk_Get3DBorderFromObj(menuPtr->tkwin, 
  352. (mePtr->activeBorderPtr != NULL) ? mePtr->activeBorderPtr 
  353. : menuPtr->activeBorderPtr);
  354. gcValues.foreground = activeFg->pixel;
  355. gcValues.background = Tk_3DBorderColor(activeBorder)->pixel;
  356. newActiveGC = Tk_GetGC(menuPtr->tkwin,
  357. GCForeground|GCBackground|GCFont|GCGraphicsExposures,
  358. &gcValues);
  359.     } else {
  360. newGC = None;
  361. newActiveGC = None;
  362. newDisabledGC = None;
  363. newIndicatorGC = None;
  364.     }
  365.     if (mePtr->textGC != None) {
  366.     Tk_FreeGC(menuPtr->display, mePtr->textGC);
  367.     }
  368.     mePtr->textGC = newGC;
  369.     if (mePtr->activeGC != None) {
  370.     Tk_FreeGC(menuPtr->display, mePtr->activeGC);
  371.     }
  372.     mePtr->activeGC = newActiveGC;
  373.     if (mePtr->disabledGC != None) {
  374.     Tk_FreeGC(menuPtr->display, mePtr->disabledGC);
  375.     }
  376.     mePtr->disabledGC = newDisabledGC;
  377.     if (mePtr->indicatorGC != None) {
  378. Tk_FreeGC(menuPtr->display, mePtr->indicatorGC);
  379.     }
  380.     mePtr->indicatorGC = newIndicatorGC;
  381.     return TCL_OK;
  382. }
  383. /*
  384.  *----------------------------------------------------------------------
  385.  *
  386.  * TkEventuallyRecomputeMenu --
  387.  *
  388.  * Tells Tcl to redo the geometry because this menu has changed.
  389.  *
  390.  * Results:
  391.  * None.
  392.  *
  393.  * Side effects:
  394.  * Menu geometry is recomputed at idle time, and the menu will be
  395.  * redisplayed.
  396.  *
  397.  *----------------------------------------------------------------------
  398.  */
  399. void
  400. TkEventuallyRecomputeMenu(menuPtr)
  401.     TkMenu *menuPtr;
  402. {
  403.     if (!(menuPtr->menuFlags & RESIZE_PENDING)) {
  404. menuPtr->menuFlags |= RESIZE_PENDING;
  405. Tcl_DoWhenIdle(ComputeMenuGeometry, (ClientData) menuPtr);
  406.     }
  407. }
  408. /*
  409.  *----------------------------------------------------------------------
  410.  *
  411.  * TkRecomputeMenu --
  412.  *
  413.  * Tells Tcl to redo the geometry because this menu has changed.
  414.  * Does it now; removes any ComputeMenuGeometries from the idler.
  415.  *
  416.  * Results:
  417.  * None.
  418.  *
  419.  * Side effects:
  420.  * Menu geometry is immediately reconfigured.
  421.  *
  422.  *----------------------------------------------------------------------
  423.  */
  424. void
  425. TkRecomputeMenu(menuPtr)
  426.     TkMenu *menuPtr;
  427. {    
  428.     if (menuPtr->menuFlags & RESIZE_PENDING) {
  429. Tcl_CancelIdleCall(ComputeMenuGeometry, (ClientData) menuPtr);
  430. ComputeMenuGeometry((ClientData) menuPtr);
  431.     }
  432. }
  433. /*
  434.  *----------------------------------------------------------------------
  435.  *
  436.  * TkEventuallyRedrawMenu --
  437.  *
  438.  * Arrange for an entry of a menu, or the whole menu, to be
  439.  * redisplayed at some point in the future.
  440.  *
  441.  * Results:
  442.  * None.
  443.  *
  444.  * Side effects:
  445.  * A when-idle hander is scheduled to do the redisplay, if there
  446.  * isn't one already scheduled.
  447.  *
  448.  *----------------------------------------------------------------------
  449.  */
  450. void
  451. TkEventuallyRedrawMenu(menuPtr, mePtr)
  452.     register TkMenu *menuPtr; /* Information about menu to redraw. */
  453.     register TkMenuEntry *mePtr;/* Entry to redraw.  NULL means redraw
  454.  * all the entries in the menu. */
  455. {
  456.     int i;
  457.     
  458.     if (menuPtr->tkwin == NULL) {
  459. return;
  460.     }
  461.     if (mePtr != NULL) {
  462. mePtr->entryFlags |= ENTRY_NEEDS_REDISPLAY;
  463.     } else {
  464. for (i = 0; i < menuPtr->numEntries; i++) {
  465.     menuPtr->entries[i]->entryFlags |= ENTRY_NEEDS_REDISPLAY;
  466. }
  467.     }
  468.     if (!Tk_IsMapped(menuPtr->tkwin)
  469.     || (menuPtr->menuFlags & REDRAW_PENDING)) {
  470. return;
  471.     }
  472.     Tcl_DoWhenIdle(DisplayMenu, (ClientData) menuPtr);
  473.     menuPtr->menuFlags |= REDRAW_PENDING;
  474. }
  475. /*
  476.  *--------------------------------------------------------------
  477.  *
  478.  * ComputeMenuGeometry --
  479.  *
  480.  * This procedure is invoked to recompute the size and
  481.  * layout of a menu.  It is called as a when-idle handler so
  482.  * that it only gets done once, even if a group of changes is
  483.  * made to the menu.
  484.  *
  485.  * Results:
  486.  * None.
  487.  *
  488.  * Side effects:
  489.  * Fields of menu entries are changed to reflect their
  490.  * current positions, and the size of the menu window
  491.  * itself may be changed.
  492.  *
  493.  *--------------------------------------------------------------
  494.  */
  495. static void
  496. ComputeMenuGeometry(clientData)
  497.     ClientData clientData; /* Structure describing menu. */
  498. {
  499.     TkMenu *menuPtr = (TkMenu *) clientData;
  500.     if (menuPtr->tkwin == NULL) {
  501. return;
  502.     }
  503.     if (menuPtr->menuType == MENUBAR) {
  504. TkpComputeMenubarGeometry(menuPtr);
  505.     } else {
  506. TkpComputeStandardMenuGeometry(menuPtr);
  507.     }
  508.     if ((menuPtr->totalWidth != Tk_ReqWidth(menuPtr->tkwin)) ||
  509.     (menuPtr->totalHeight != Tk_ReqHeight(menuPtr->tkwin))) {
  510. Tk_GeometryRequest(menuPtr->tkwin, menuPtr->totalWidth,
  511. menuPtr->totalHeight);
  512.     }
  513.     /*
  514.      * Must always force a redisplay here if the window is mapped
  515.      * (even if the size didn't change, something else might have
  516.      * changed in the menu, such as a label or accelerator).  The
  517.      * resize will force a redisplay above.
  518.      */
  519.     
  520.     TkEventuallyRedrawMenu(menuPtr, (TkMenuEntry *) NULL);
  521.     
  522.     menuPtr->menuFlags &= ~RESIZE_PENDING;
  523. }
  524. /*
  525.  *----------------------------------------------------------------------
  526.  *
  527.  * TkMenuSelectImageProc --
  528.  *
  529.  * This procedure is invoked by the image code whenever the manager
  530.  * for an image does something that affects the size of contents
  531.  * of an image displayed in a menu entry when it is selected.
  532.  *
  533.  * Results:
  534.  * None.
  535.  *
  536.  * Side effects:
  537.  * Arranges for the menu to get redisplayed.
  538.  *
  539.  *----------------------------------------------------------------------
  540.  */
  541. void
  542. TkMenuSelectImageProc(clientData, x, y, width, height, imgWidth,
  543. imgHeight)
  544.     ClientData clientData; /* Pointer to widget record. */
  545.     int x, y; /* Upper left pixel (within image)
  546.  * that must be redisplayed. */
  547.     int width, height; /* Dimensions of area to redisplay
  548.  * (may be <= 0). */
  549.     int imgWidth, imgHeight; /* New dimensions of image. */
  550. {
  551.     register TkMenuEntry *mePtr = (TkMenuEntry *) clientData;
  552.     if ((mePtr->entryFlags & ENTRY_SELECTED)
  553.     && !(mePtr->menuPtr->menuFlags &
  554.     REDRAW_PENDING)) {
  555. mePtr->menuPtr->menuFlags |= REDRAW_PENDING;
  556. Tcl_DoWhenIdle(DisplayMenu, (ClientData) mePtr->menuPtr);
  557.     }
  558. }
  559. /*
  560.  *----------------------------------------------------------------------
  561.  *
  562.  * DisplayMenu --
  563.  *
  564.  * This procedure is invoked to display a menu widget.
  565.  *
  566.  * Results:
  567.  * None.
  568.  *
  569.  * Side effects:
  570.  * Commands are output to X to display the menu in its
  571.  * current mode.
  572.  *
  573.  *----------------------------------------------------------------------
  574.  */
  575. static void
  576. DisplayMenu(clientData)
  577.     ClientData clientData; /* Information about widget. */
  578. {
  579.     register TkMenu *menuPtr = (TkMenu *) clientData;
  580.     register TkMenuEntry *mePtr;
  581.     register Tk_Window tkwin = menuPtr->tkwin;
  582.     int index, strictMotif;
  583.     Tk_Font tkfont;
  584.     Tk_FontMetrics menuMetrics;
  585.     int width;
  586.     int borderWidth;
  587.     Tk_3DBorder border;
  588.     int activeBorderWidth;
  589.     int relief;
  590.     menuPtr->menuFlags &= ~REDRAW_PENDING;
  591.     if ((menuPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  592. return;
  593.     }
  594.     Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr,
  595.     &borderWidth);
  596.     border = Tk_Get3DBorderFromObj(menuPtr->tkwin, menuPtr->borderPtr);
  597.     Tk_GetPixelsFromObj(NULL, menuPtr->tkwin,
  598.     menuPtr->activeBorderWidthPtr, &activeBorderWidth);
  599.     if (menuPtr->menuType == MENUBAR) {
  600. Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), border, borderWidth, 
  601. borderWidth, Tk_Width(tkwin) - 2 * borderWidth,
  602. Tk_Height(tkwin) - 2 * borderWidth, 0, TK_RELIEF_FLAT);
  603.     }
  604.     strictMotif = Tk_StrictMotif(menuPtr->tkwin);
  605.     /*
  606.      * See note in ComputeMenuGeometry. We don't want to be doing font metrics
  607.      * all of the time.
  608.      */
  609.     tkfont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr);
  610.     Tk_GetFontMetrics(tkfont, &menuMetrics);
  611.     /*
  612.      * Loop through all of the entries, drawing them one at a time.
  613.      */
  614.     for (index = 0; index < menuPtr->numEntries; index++) {
  615. mePtr = menuPtr->entries[index];
  616. if (menuPtr->menuType != MENUBAR) {
  617.     if (!(mePtr->entryFlags & ENTRY_NEEDS_REDISPLAY)) {
  618. continue;
  619.     }
  620. }
  621. mePtr->entryFlags &= ~ENTRY_NEEDS_REDISPLAY;
  622. if (menuPtr->menuType == MENUBAR) {
  623.     width = mePtr->width;
  624. } else {
  625.     if (mePtr->entryFlags & ENTRY_LAST_COLUMN) {
  626. width = Tk_Width(menuPtr->tkwin) - mePtr->x
  627. - activeBorderWidth;
  628.     } else {
  629. width = mePtr->width + borderWidth;
  630.     }
  631. }
  632. TkpDrawMenuEntry(mePtr, Tk_WindowId(menuPtr->tkwin), tkfont,
  633. &menuMetrics, mePtr->x, mePtr->y, width, 
  634. mePtr->height, strictMotif, 1);
  635. if ((index > 0) && (menuPtr->menuType != MENUBAR)
  636. && mePtr->columnBreak) {
  637.     mePtr = menuPtr->entries[index - 1];
  638.     Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), border,
  639. mePtr->x, mePtr->y + mePtr->height, 
  640. mePtr->width,
  641. Tk_Height(tkwin) - mePtr->y - mePtr->height - 
  642. activeBorderWidth, 0,
  643. TK_RELIEF_FLAT);
  644. }
  645.     }
  646.     if (menuPtr->menuType != MENUBAR) {
  647. int x, y, height;
  648. if (menuPtr->numEntries == 0) {
  649.     x = y = borderWidth;
  650.     width = Tk_Width(tkwin) - 2 * activeBorderWidth;
  651.     height = Tk_Height(tkwin) - 2 * activeBorderWidth;
  652. } else {
  653.     mePtr = menuPtr->entries[menuPtr->numEntries - 1];
  654.     Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin),
  655. border, mePtr->x, mePtr->y + mePtr->height, mePtr->width, 
  656. Tk_Height(tkwin) - mePtr->y - mePtr->height
  657. - activeBorderWidth, 0,
  658. TK_RELIEF_FLAT);
  659.     x = mePtr->x + mePtr->width;
  660.     y = mePtr->y + mePtr->height;
  661.     width = Tk_Width(tkwin) - x - activeBorderWidth;
  662.     height = Tk_Height(tkwin) - y - activeBorderWidth;
  663. }
  664. Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), border, x, y, 
  665. width, height, 0, TK_RELIEF_FLAT);
  666.     }
  667.     Tk_GetReliefFromObj(NULL, menuPtr->reliefPtr, &relief);
  668.     Tk_Draw3DRectangle(menuPtr->tkwin, Tk_WindowId(tkwin),
  669.     border, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), borderWidth, 
  670.     relief);
  671. }
  672. /*
  673.  *--------------------------------------------------------------
  674.  *
  675.  * TkMenuEventProc --
  676.  *
  677.  * This procedure is invoked by the Tk dispatcher for various
  678.  * events on menus.
  679.  *
  680.  * Results:
  681.  * None.
  682.  *
  683.  * Side effects:
  684.  * When the window gets deleted, internal structures get
  685.  * cleaned up.  When it gets exposed, it is redisplayed.
  686.  *
  687.  *--------------------------------------------------------------
  688.  */
  689. void
  690. TkMenuEventProc(clientData, eventPtr)
  691.     ClientData clientData; /* Information about window. */
  692.     XEvent *eventPtr; /* Information about event. */
  693. {
  694.     TkMenu *menuPtr = (TkMenu *) clientData;
  695.     
  696.     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
  697. TkEventuallyRedrawMenu(menuPtr, (TkMenuEntry *) NULL);
  698.     } else if (eventPtr->type == ConfigureNotify) {
  699. TkEventuallyRecomputeMenu(menuPtr);
  700. TkEventuallyRedrawMenu(menuPtr, (TkMenuEntry *) NULL);
  701.     } else if (eventPtr->type == ActivateNotify) {
  702. if (menuPtr->menuType == TEAROFF_MENU) {
  703.     TkpSetMainMenubar(menuPtr->interp, menuPtr->tkwin, NULL);
  704. }
  705.     } else if (eventPtr->type == DestroyNotify) {
  706. if (menuPtr->tkwin != NULL) {
  707.     if (!(menuPtr->menuFlags & MENU_DELETION_PENDING)) {
  708. TkDestroyMenu(menuPtr);
  709.     }
  710.     menuPtr->tkwin = NULL;
  711. }
  712. if (menuPtr->menuFlags & MENU_WIN_DESTRUCTION_PENDING) {
  713.     return;
  714. }
  715. menuPtr->menuFlags |= MENU_WIN_DESTRUCTION_PENDING;
  716. if (menuPtr->widgetCmd != NULL) {
  717.     Tcl_DeleteCommandFromToken(menuPtr->interp, menuPtr->widgetCmd);
  718.     menuPtr->widgetCmd = NULL;
  719. }
  720. if (menuPtr->menuFlags & REDRAW_PENDING) {
  721.     Tcl_CancelIdleCall(DisplayMenu, (ClientData) menuPtr);
  722.     menuPtr->menuFlags &= ~REDRAW_PENDING;
  723. }
  724. if (menuPtr->menuFlags & RESIZE_PENDING) {
  725.     Tcl_CancelIdleCall(ComputeMenuGeometry, (ClientData) menuPtr);
  726.     menuPtr->menuFlags &= ~RESIZE_PENDING;
  727. }
  728. Tcl_EventuallyFree((ClientData) menuPtr, TCL_DYNAMIC);
  729.     }
  730. }
  731. /*
  732.  *----------------------------------------------------------------------
  733.  *
  734.  * TkMenuImageProc --
  735.  *
  736.  * This procedure is invoked by the image code whenever the manager
  737.  * for an image does something that affects the size of contents
  738.  * of an image displayed in a menu entry.
  739.  *
  740.  * Results:
  741.  * None.
  742.  *
  743.  * Side effects:
  744.  * Arranges for the menu to get redisplayed.
  745.  *
  746.  *----------------------------------------------------------------------
  747.  */
  748. void
  749. TkMenuImageProc(clientData, x, y, width, height, imgWidth,
  750. imgHeight)
  751.     ClientData clientData; /* Pointer to widget record. */
  752.     int x, y; /* Upper left pixel (within image)
  753.  * that must be redisplayed. */
  754.     int width, height; /* Dimensions of area to redisplay
  755.  * (may be <= 0). */
  756.     int imgWidth, imgHeight; /* New dimensions of image. */
  757. {
  758.     register TkMenu *menuPtr = ((TkMenuEntry *)clientData)->menuPtr;
  759.     if ((menuPtr->tkwin != NULL) && !(menuPtr->menuFlags
  760.     & RESIZE_PENDING)) {
  761. menuPtr->menuFlags |= RESIZE_PENDING;
  762. Tcl_DoWhenIdle(ComputeMenuGeometry, (ClientData) menuPtr);
  763.     }
  764. }
  765. /*
  766.  *----------------------------------------------------------------------
  767.  *
  768.  * TkPostTearoffMenu --
  769.  *
  770.  * Posts a menu on the screen. Used to post tearoff menus. On Unix,
  771.  * all menus are posted this way. Adjusts the menu's position
  772.  * so that it fits on the screen, and maps and raises the menu.
  773.  *
  774.  * Results:
  775.  * Returns a standard Tcl Error.
  776.  *
  777.  * Side effects:
  778.  * The menu is posted.
  779.  *
  780.  *----------------------------------------------------------------------
  781.  */
  782. int
  783. TkPostTearoffMenu(interp, menuPtr, x, y)
  784.     Tcl_Interp *interp; /* The interpreter of the menu */
  785.     TkMenu *menuPtr; /* The menu we are posting */
  786.     int x; /* The root X coordinate where we
  787.  * are posting */
  788.     int y; /* The root Y coordinate where we
  789.  * are posting */
  790. {
  791.     int vRootX, vRootY, vRootWidth, vRootHeight;
  792.     int tmp, result;
  793.     TkActivateMenuEntry(menuPtr, -1);
  794.     TkRecomputeMenu(menuPtr);
  795.     result = TkPostCommand(menuPtr);
  796.     if (result != TCL_OK) {
  797.      return result;
  798.     }
  799.     /*
  800.      * The post commands could have deleted the menu, which means
  801.      * we are dead and should go away.
  802.      */
  803.     if (menuPtr->tkwin == NULL) {
  804.      return TCL_OK;
  805.     }
  806.     /*
  807.      * Adjust the position of the menu if necessary to keep it
  808.      * visible on the screen.  There are two special tricks to
  809.      * make this work right:
  810.      *
  811.      * 1. If a virtual root window manager is being used then
  812.      *    the coordinates are in the virtual root window of
  813.      *    menuPtr's parent;  since the menu uses override-redirect
  814.      *    mode it will be in the *real* root window for the screen,
  815.      *    so we have to map the coordinates from the virtual root
  816.      *    (if any) to the real root.  Can't get the virtual root
  817.      *    from the menu itself (it will never be seen by the wm)
  818.      *    so use its parent instead (it would be better to have an
  819.      *    an option that names a window to use for this...).
  820.      * 2. The menu may not have been mapped yet, so its current size
  821.      *    might be the default 1x1.  To compute how much space it
  822.      *    needs, use its requested size, not its actual size.
  823.      *
  824.      * Note that this code assumes square screen regions and all
  825.      * positive coordinates. This does not work on a Mac with
  826.      * multiple monitors. But then again, Tk has other problems
  827.      * with this.
  828.      */
  829.     Tk_GetVRootGeometry(Tk_Parent(menuPtr->tkwin), &vRootX, &vRootY,
  830. &vRootWidth, &vRootHeight);
  831.     x += vRootX;
  832.     y += vRootY;
  833.     tmp = WidthOfScreen(Tk_Screen(menuPtr->tkwin))
  834. - Tk_ReqWidth(menuPtr->tkwin);
  835.     if (x > tmp) {
  836. x = tmp;
  837.     }
  838.     if (x < 0) {
  839. x = 0;
  840.     }
  841.     tmp = HeightOfScreen(Tk_Screen(menuPtr->tkwin))
  842. - Tk_ReqHeight(menuPtr->tkwin);
  843.     if (y > tmp) {
  844. y = tmp;
  845.     }
  846.     if (y < 0) {
  847. y = 0;
  848.     }
  849.     Tk_MoveToplevelWindow(menuPtr->tkwin, x, y);
  850.     if (!Tk_IsMapped(menuPtr->tkwin)) {
  851. Tk_MapWindow(menuPtr->tkwin);
  852.     }
  853.     TkWmRestackToplevel((TkWindow *) menuPtr->tkwin, Above, NULL);
  854.     return TCL_OK;
  855. }
  856. /*
  857.  *--------------------------------------------------------------
  858.  *
  859.  * TkPostSubmenu --
  860.  *
  861.  * This procedure arranges for a particular submenu (i.e. the
  862.  * menu corresponding to a given cascade entry) to be
  863.  * posted.
  864.  *
  865.  * Results:
  866.  * A standard Tcl return result.  Errors may occur in the
  867.  * Tcl commands generated to post and unpost submenus.
  868.  *
  869.  * Side effects:
  870.  * If there is already a submenu posted, it is unposted.
  871.  * The new submenu is then posted.
  872.  *
  873.  *--------------------------------------------------------------
  874.  */
  875. int
  876. TkPostSubmenu(interp, menuPtr, mePtr)
  877.     Tcl_Interp *interp; /* Used for invoking sub-commands and
  878.  * reporting errors. */
  879.     register TkMenu *menuPtr; /* Information about menu as a whole. */
  880.     register TkMenuEntry *mePtr; /* Info about submenu that is to be
  881.  * posted.  NULL means make sure that
  882.  * no submenu is posted. */
  883. {
  884.     int result, x, y;
  885.     if (mePtr == menuPtr->postedCascade) {
  886. return TCL_OK;
  887.     }
  888.     if (menuPtr->postedCascade != NULL) {
  889. char *name = Tcl_GetStringFromObj(menuPtr->postedCascade->namePtr,
  890. NULL);
  891. /*
  892.  * Note: when unposting a submenu, we have to redraw the entire
  893.  * parent menu.  This is because of a combination of the following
  894.  * things:
  895.  * (a) the submenu partially overlaps the parent.
  896.  * (b) the submenu specifies "save under", which causes the X
  897.  *     server to make a copy of the information under it when it
  898.  *     is posted.  When the submenu is unposted, the X server
  899.  *     copies this data back and doesn't generate any Expose
  900.  *     events for the parent.
  901.  * (c) the parent may have redisplayed itself after the submenu
  902.  *     was posted, in which case the saved information is no
  903.  *     longer correct.
  904.  * The simplest solution is just force a complete redisplay of
  905.  * the parent.
  906.  */
  907. TkEventuallyRedrawMenu(menuPtr, (TkMenuEntry *) NULL);
  908. result = Tcl_VarEval(interp, "{", name, "} unpost", (char *) NULL);
  909. menuPtr->postedCascade = NULL;
  910. if (result != TCL_OK) {
  911.     return result;
  912. }
  913.     }
  914.     if ((mePtr != NULL) && (mePtr->namePtr != NULL)
  915.     && Tk_IsMapped(menuPtr->tkwin)) {
  916. /*
  917.  * Position the cascade with its upper left corner slightly
  918.  * below and to the left of the upper right corner of the
  919.  * menu entry (this is an attempt to match Motif behavior).
  920.  *
  921.  * The menu has to redrawn so that the entry can change relief.
  922.  */
  923. char string[TCL_INTEGER_SPACE * 2];
  924. char *name;
  925. name = Tcl_GetStringFromObj(mePtr->namePtr, NULL);
  926. Tk_GetRootCoords(menuPtr->tkwin, &x, &y);
  927. AdjustMenuCoords(menuPtr, mePtr, &x, &y, string);
  928. result = Tcl_VarEval(interp, "{", name, "} post ", string, (char *) NULL);
  929. if (result != TCL_OK) {
  930.     return result;
  931. }
  932. menuPtr->postedCascade = mePtr;
  933. TkEventuallyRedrawMenu(menuPtr, mePtr);
  934.     }
  935.     return TCL_OK;
  936. }
  937. /*
  938.  *----------------------------------------------------------------------
  939.  *
  940.  * AdjustMenuCoords --
  941.  *
  942.  * Adjusts the given coordinates down and the left to give a Motif
  943.  * look.
  944.  *
  945.  * Results:
  946.  * None.
  947.  *
  948.  * Side effects:
  949.  * The menu is eventually redrawn if necessary.
  950.  *
  951.  *----------------------------------------------------------------------
  952.  */
  953. static void
  954. AdjustMenuCoords(menuPtr, mePtr, xPtr, yPtr, string)
  955.     TkMenu *menuPtr;
  956.     TkMenuEntry *mePtr;
  957.     int *xPtr;
  958.     int *yPtr;
  959.     char *string;
  960. {
  961.     if (menuPtr->menuType == MENUBAR) {
  962. *xPtr += mePtr->x;
  963. *yPtr += mePtr->y + mePtr->height;
  964.     } else {
  965. int borderWidth, activeBorderWidth;
  966. Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr,
  967. &borderWidth);
  968. Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, 
  969. menuPtr->activeBorderWidthPtr, &activeBorderWidth);
  970. *xPtr += Tk_Width(menuPtr->tkwin) - borderWidth - activeBorderWidth 
  971. - 2;
  972. *yPtr += mePtr->y + activeBorderWidth + 2;
  973.     }
  974.     sprintf(string, "%d %d", *xPtr, *yPtr);
  975. }