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

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tkWinButton.c --
  3.  *
  4.  * This file implements the Windows specific portion of the button
  5.  * widgets.
  6.  *
  7.  * Copyright (c) 1996-1998 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: tkWinButton.c,v 1.20.2.4 2006/12/19 19:50:55 hobbs Exp $
  13.  */
  14. #define OEMRESOURCE
  15. #include "tkWinInt.h"
  16. #include "tkButton.h"
  17. /*
  18.  * These macros define the base style flags for the different button types.
  19.  */
  20. #define LABEL_STYLE (BS_OWNERDRAW | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS)
  21. #define PUSH_STYLE (BS_OWNERDRAW | BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS)
  22. #define CHECK_STYLE (BS_OWNERDRAW | BS_CHECKBOX | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS)
  23. #define RADIO_STYLE (BS_OWNERDRAW | BS_RADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS)
  24. /*
  25.  * Declaration of Windows specific button structure.
  26.  */
  27. typedef struct WinButton {
  28.     TkButton info; /* Generic button info. */
  29.     WNDPROC oldProc; /* Old window procedure. */
  30.     HWND hwnd; /* Current window handle. */
  31.     Pixmap pixmap; /* Bitmap for rendering the button. */
  32.     DWORD style; /* Window style flags. */
  33. } WinButton;
  34. /*
  35.  * The following macro reverses the order of RGB bytes to convert
  36.  * between RGBQUAD and COLORREF values.
  37.  */
  38. #define FlipColor(rgb) (RGB(GetBValue(rgb),GetGValue(rgb),GetRValue(rgb)))
  39. /*
  40.  * The following enumeration defines the meaning of the palette entries
  41.  * in the "buttons" image used to draw checkbox and radiobutton indicators.
  42.  */
  43. enum {
  44.     PAL_CHECK = 0,
  45.     PAL_TOP_OUTER = 1,
  46.     PAL_BOTTOM_OUTER = 2,
  47.     PAL_BOTTOM_INNER = 3,
  48.     PAL_INTERIOR = 4,
  49.     PAL_TOP_INNER = 5,
  50.     PAL_BACKGROUND = 6
  51. };
  52. /*
  53.  * Cached information about the boxes bitmap, and the default border 
  54.  * width for a button in string form for use in Tk_OptionSpec for 
  55.  * the various button widget classes.
  56.  */
  57. typedef struct ThreadSpecificData { 
  58.     BITMAPINFOHEADER *boxesPtr;   /* Information about the bitmap. */
  59.     DWORD *boxesPalette;   /* Pointer to color palette. */
  60.     LPSTR boxesBits;   /* Pointer to bitmap data. */
  61.     DWORD boxHeight;              /* Height of each sub-image. */
  62.     DWORD boxWidth ;              /* Width of each sub-image. */
  63.     char defWidth[TCL_INTEGER_SPACE];
  64. } ThreadSpecificData;
  65. static Tcl_ThreadDataKey dataKey;
  66. /*
  67.  * Declarations for functions defined in this file.
  68.  */
  69. static LRESULT CALLBACK ButtonProc _ANSI_ARGS_((HWND hwnd, UINT message,
  70.     WPARAM wParam, LPARAM lParam));
  71. static Window CreateProc _ANSI_ARGS_((Tk_Window tkwin,
  72.     Window parent, ClientData instanceData));
  73. static void InitBoxes _ANSI_ARGS_((void));
  74. /*
  75.  * The class procedure table for the button widgets.
  76.  */
  77. Tk_ClassProcs tkpButtonProcs = { 
  78.     sizeof(Tk_ClassProcs), /* size */
  79.     TkButtonWorldChanged, /* worldChangedProc */
  80.     CreateProc, /* createProc */
  81. };
  82. /*
  83.  *----------------------------------------------------------------------
  84.  *
  85.  * InitBoxes --
  86.  *
  87.  * This function load the Tk 3d button bitmap.  "buttons" is a 16 
  88.  * color bitmap that is laid out such that the top row contains 
  89.  * the 4 checkbox images, and the bottom row contains the radio 
  90.  * button images. Note that the bitmap is stored in bottom-up 
  91.  * format.  Also, the first seven palette entries are used to 
  92.  * identify the different parts of the bitmaps so we can do the 
  93.  * appropriate color mappings based on the current button colors.
  94.  *
  95.  * Results:
  96.  * None.
  97.  *
  98.  * Side effects:
  99.  * Loads the "buttons" resource.
  100.  *
  101.  *----------------------------------------------------------------------
  102.  */
  103. static void
  104. InitBoxes()
  105. {
  106.     /*
  107.      * For DLLs like Tk, the HINSTANCE is the same as the HMODULE.
  108.      */
  109.     HMODULE module = (HINSTANCE) Tk_GetHINSTANCE();
  110.     HRSRC hrsrc;
  111.     HGLOBAL hblk;
  112.     LPBITMAPINFOHEADER newBitmap;
  113.     DWORD size;
  114.     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 
  115.             Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
  116.     hrsrc = FindResource(module, "buttons", RT_BITMAP);
  117.     if (hrsrc) {
  118. hblk = LoadResource(module, hrsrc);
  119. tsdPtr->boxesPtr = (LPBITMAPINFOHEADER)LockResource(hblk);
  120.     }
  121.     /*
  122.      * Copy the DIBitmap into writable memory.
  123.      */
  124.     if (tsdPtr->boxesPtr != NULL && !(tsdPtr->boxesPtr->biWidth % 4)
  125.     && !(tsdPtr->boxesPtr->biHeight % 2)) {
  126. size = tsdPtr->boxesPtr->biSize + (1 << tsdPtr->boxesPtr->biBitCount) 
  127.                 * sizeof(RGBQUAD) + tsdPtr->boxesPtr->biSizeImage;
  128. newBitmap = (LPBITMAPINFOHEADER) ckalloc(size);
  129. memcpy(newBitmap, tsdPtr->boxesPtr, size);
  130. tsdPtr->boxesPtr = newBitmap;
  131. tsdPtr->boxWidth = tsdPtr->boxesPtr->biWidth / 4;
  132. tsdPtr->boxHeight = tsdPtr->boxesPtr->biHeight / 2;
  133. tsdPtr->boxesPalette = (DWORD*) (((LPSTR) tsdPtr->boxesPtr) 
  134.                 + tsdPtr->boxesPtr->biSize);
  135. tsdPtr->boxesBits = ((LPSTR) tsdPtr->boxesPalette)
  136.     + ((1 << tsdPtr->boxesPtr->biBitCount) * sizeof(RGBQUAD));
  137.     } else {
  138. tsdPtr->boxesPtr = NULL;
  139.     }
  140. }
  141. /*
  142.  *----------------------------------------------------------------------
  143.  *
  144.  * TkpButtonSetDefaults --
  145.  *
  146.  * This procedure is invoked before option tables are created for
  147.  * buttons.  It modifies some of the default values to match the
  148.  * current values defined for this platform.
  149.  *
  150.  * Results:
  151.  * Some of the default values in *specPtr are modified.
  152.  *
  153.  * Side effects:
  154.  * Updates some of.
  155.  *
  156.  *----------------------------------------------------------------------
  157.  */
  158. void
  159. TkpButtonSetDefaults(specPtr)
  160.     Tk_OptionSpec *specPtr; /* Points to an array of option specs,
  161.  * terminated by one with type
  162.  * TK_OPTION_END. */
  163. {
  164.     int width;
  165.     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 
  166.             Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
  167.     if (tsdPtr->defWidth[0] == 0) {
  168. width = GetSystemMetrics(SM_CXEDGE);
  169. if (width == 0) {
  170.     width = 1;
  171. }
  172. sprintf(tsdPtr->defWidth, "%d", width);
  173.     }
  174.     for ( ; specPtr->type != TK_OPTION_END; specPtr++) {
  175. if (specPtr->internalOffset == Tk_Offset(TkButton, borderWidth)) {
  176.     specPtr->defValue = tsdPtr->defWidth;
  177. }
  178.     }
  179. }
  180. /*
  181.  *----------------------------------------------------------------------
  182.  *
  183.  * TkpCreateButton --
  184.  *
  185.  * Allocate a new TkButton structure.
  186.  *
  187.  * Results:
  188.  * Returns a newly allocated TkButton structure.
  189.  *
  190.  * Side effects:
  191.  * Registers an event handler for the widget.
  192.  *
  193.  *----------------------------------------------------------------------
  194.  */
  195. TkButton *
  196. TkpCreateButton(tkwin)
  197.     Tk_Window tkwin;
  198. {
  199.     WinButton *butPtr;
  200.     butPtr = (WinButton *)ckalloc(sizeof(WinButton));
  201.     butPtr->hwnd = NULL;
  202.     return (TkButton *) butPtr;
  203. }
  204. /*
  205.  *----------------------------------------------------------------------
  206.  *
  207.  * CreateProc --
  208.  *
  209.  * This function creates a new Button control, subclasses
  210.  * the instance, and generates a new Window object.
  211.  *
  212.  * Results:
  213.  * Returns the newly allocated Window object, or None on failure.
  214.  *
  215.  * Side effects:
  216.  * Causes a new Button control to come into existence.
  217.  *
  218.  *----------------------------------------------------------------------
  219.  */
  220. static Window
  221. CreateProc(tkwin, parentWin, instanceData)
  222.     Tk_Window tkwin; /* Token for window. */
  223.     Window parentWin; /* Parent of new window. */
  224.     ClientData instanceData; /* Button instance data. */
  225. {
  226.     Window window;
  227.     HWND parent;
  228.     char *class;
  229.     WinButton *butPtr = (WinButton *)instanceData;
  230.     parent = Tk_GetHWND(parentWin);
  231.     if (butPtr->info.type == TYPE_LABEL) {
  232. class = "STATIC";
  233. butPtr->style = SS_OWNERDRAW | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS;
  234.     } else {
  235. class = "BUTTON";
  236. butPtr->style = BS_OWNERDRAW | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS;
  237.     }
  238.     butPtr->hwnd = CreateWindow(class, NULL, butPtr->style,
  239.     Tk_X(tkwin), Tk_Y(tkwin), Tk_Width(tkwin), Tk_Height(tkwin),
  240.     parent, NULL, Tk_GetHINSTANCE(), NULL);
  241.     SetWindowPos(butPtr->hwnd, HWND_TOP, 0, 0, 0, 0,
  242.     SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
  243. #ifdef _WIN64
  244.     butPtr->oldProc = (WNDPROC)SetWindowLongPtr(butPtr->hwnd, GWLP_WNDPROC,
  245.     (LONG_PTR) ButtonProc);
  246. #else
  247.     butPtr->oldProc = (WNDPROC)SetWindowLong(butPtr->hwnd, GWL_WNDPROC,
  248.     (DWORD) ButtonProc);
  249. #endif
  250.     window = Tk_AttachHWND(tkwin, butPtr->hwnd);
  251.     return window;
  252. }
  253. /*
  254.  *----------------------------------------------------------------------
  255.  *
  256.  * TkpDestroyButton --
  257.  *
  258.  * Free data structures associated with the button control.
  259.  *
  260.  * Results:
  261.  * None.
  262.  *
  263.  * Side effects:
  264.  * Restores the default control state.
  265.  *
  266.  *----------------------------------------------------------------------
  267.  */
  268. void
  269. TkpDestroyButton(butPtr)
  270.     TkButton *butPtr;
  271. {
  272.     WinButton *winButPtr = (WinButton *)butPtr;
  273.     HWND hwnd = winButPtr->hwnd;
  274.     if (hwnd) {
  275. #ifdef _WIN64
  276. SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) winButPtr->oldProc);
  277. #else
  278. SetWindowLong(hwnd, GWL_WNDPROC, (DWORD) winButPtr->oldProc);
  279. #endif
  280.     }
  281. }
  282. /*
  283.  *----------------------------------------------------------------------
  284.  *
  285.  * TkpDisplayButton --
  286.  *
  287.  * This procedure is invoked to display a button widget.  It is
  288.  * normally invoked as an idle handler.
  289.  *
  290.  * Results:
  291.  * None.
  292.  *
  293.  * Side effects:
  294.  * Information appears on the screen.  The REDRAW_PENDING flag
  295.  * is cleared.
  296.  *
  297.  *----------------------------------------------------------------------
  298.  */
  299. void
  300. TkpDisplayButton(clientData)
  301.     ClientData clientData; /* Information about widget. */
  302. {
  303.     TkWinDCState state;
  304.     HDC dc;
  305.     register TkButton *butPtr = (TkButton *) clientData;
  306.     GC gc;
  307.     Tk_3DBorder border;
  308.     Pixmap pixmap;
  309.     int x = 0; /* Initialization only needed to stop
  310.  * compiler warning. */
  311.     int y, relief;
  312.     register Tk_Window tkwin = butPtr->tkwin;
  313.     int width = 0, height = 0, haveImage = 0, haveText = 0, drawRing = 0;
  314.     RECT rect;
  315.     int defaultWidth; /* Width of default ring. */
  316.     int offset; /* 0 means this is a label widget.  1 means
  317.  * it is a flavor of button, so we offset
  318.  * the text to make the button appear to
  319.  * move up and down as the relief changes. */
  320.     int textXOffset = 0, textYOffset = 0; /* text offsets for use with
  321.    * compound buttons and focus ring */
  322.     int imageWidth, imageHeight;
  323.     int imageXOffset = 0, imageYOffset = 0; /* image information that will
  324.      * be used to restrict disabled
  325.      * pixmap as well */
  326.     DWORD *boxesPalette;
  327.     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 
  328.             Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
  329.     boxesPalette= tsdPtr->boxesPalette;
  330.     butPtr->flags &= ~REDRAW_PENDING;
  331.     if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  332. return;
  333.     }
  334.     border = butPtr->normalBorder;
  335.     if ((butPtr->state == STATE_DISABLED) && (butPtr->disabledFg != NULL)) {
  336. gc = butPtr->disabledGC;
  337.     } else if ((butPtr->state == STATE_ACTIVE)
  338.     && !Tk_StrictMotif(butPtr->tkwin)) {
  339. gc = butPtr->activeTextGC;
  340. border = butPtr->activeBorder;
  341.     } else {
  342. gc = butPtr->normalTextGC;
  343.     }
  344.     if ((butPtr->flags & SELECTED) && (butPtr->state != STATE_ACTIVE)
  345.     && (butPtr->selectBorder != NULL) && !butPtr->indicatorOn) {
  346. border = butPtr->selectBorder;
  347.     }
  348.     /*
  349.      * Override the relief specified for the button if this is a
  350.      * checkbutton or radiobutton and there's no indicator.  The new
  351.      * relief is as follows:
  352.      *      If the button is select  --> "sunken"
  353.      *      If relief==overrelief    --> relief
  354.      *      Otherwise                --> overrelief
  355.      *
  356.      * The effect we are trying to achieve is as follows:
  357.      *
  358.      *      value    mouse-over?   -->   relief
  359.      *     -------  ------------        --------
  360.      *       off        no               flat
  361.      *       off        yes              raised
  362.      *       on         no               sunken
  363.      *       on         yes              sunken
  364.      *
  365.      * This is accomplished by configuring the checkbutton or radiobutton
  366.      * like this:
  367.      *
  368.      *     -indicatoron 0 -overrelief raised -offrelief flat
  369.      *
  370.      * Bindings (see library/button.tcl) will copy the -overrelief into
  371.      * -relief on mouseover.  Hence, we can tell if we are in mouse-over by
  372.      * comparing relief against overRelief.  This is an aweful kludge, but
  373.      * it gives use the desired behavior while keeping the code backwards
  374.      * compatible.
  375.      */
  376.     relief = butPtr->relief;
  377.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) {
  378. if (butPtr->flags & SELECTED) {
  379.     relief = TK_RELIEF_SUNKEN;
  380. } else if (butPtr->overRelief != relief) {
  381.     relief = butPtr->offRelief;
  382. }
  383.     }
  384.     /*
  385.      * Compute width of default ring and offset for pushed buttons.
  386.      */
  387.     if (butPtr->type == TYPE_BUTTON) {
  388. defaultWidth = ((butPtr->defaultState == DEFAULT_ACTIVE)
  389. ? butPtr->highlightWidth : 0);
  390. offset = 1;
  391.     } else {
  392. defaultWidth = 0;
  393. if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) {
  394.     offset = 1;
  395. } else {
  396.     offset = 0;
  397. }
  398.     }
  399.     /*
  400.      * In order to avoid screen flashes, this procedure redraws
  401.      * the button in a pixmap, then copies the pixmap to the
  402.      * screen in a single operation.  This means that there's no
  403.      * point in time where the on-sreen image has been cleared.
  404.      */
  405.     pixmap = Tk_GetPixmap(butPtr->display, Tk_WindowId(tkwin),
  406.     Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
  407.     Tk_Fill3DRectangle(tkwin, pixmap, border, 0, 0, Tk_Width(tkwin),
  408.     Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
  409.     /*
  410.      * Display image or bitmap or text for button.
  411.      */
  412.     if (butPtr->image != None) {
  413. Tk_SizeOfImage(butPtr->image, &width, &height);
  414. haveImage = 1;
  415.     } else if (butPtr->bitmap != None) {
  416. Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
  417. haveImage = 1;
  418.     }
  419.     imageWidth  = width;
  420.     imageHeight = height;
  421.     haveText = (butPtr->textWidth != 0 && butPtr->textHeight != 0);
  422.     if (butPtr->compound != COMPOUND_NONE && haveImage && haveText) {
  423. int fullWidth = 0, fullHeight = 0;
  424. switch ((enum compound) butPtr->compound) {
  425.     case COMPOUND_TOP: 
  426.     case COMPOUND_BOTTOM: {
  427. /* Image is above or below text */
  428. if (butPtr->compound == COMPOUND_TOP) {
  429.     textYOffset = height + butPtr->padY;
  430. } else {
  431.     imageYOffset = butPtr->textHeight + butPtr->padY;
  432. }
  433. fullHeight = height + butPtr->textHeight + butPtr->padY;
  434. fullWidth = (width > butPtr->textWidth ? width :
  435. butPtr->textWidth);
  436. textXOffset = (fullWidth - butPtr->textWidth)/2;
  437. imageXOffset = (fullWidth - width)/2;
  438. break;
  439.     }
  440.     case COMPOUND_LEFT:
  441.     case COMPOUND_RIGHT: {
  442. /* Image is left or right of text */
  443. if (butPtr->compound == COMPOUND_LEFT) {
  444.     textXOffset = width + butPtr->padX;
  445. } else {
  446.     imageXOffset = butPtr->textWidth + butPtr->padX;
  447. }
  448. fullWidth = butPtr->textWidth + butPtr->padX + width;
  449. fullHeight = (height > butPtr->textHeight ? height :
  450. butPtr->textHeight);
  451. textYOffset = (fullHeight - butPtr->textHeight)/2;
  452. imageYOffset = (fullHeight - height)/2;
  453. break;
  454.     }
  455.     case COMPOUND_CENTER: {
  456. /* Image and text are superimposed */
  457. fullWidth = (width > butPtr->textWidth ? width :
  458. butPtr->textWidth);
  459. fullHeight = (height > butPtr->textHeight ? height :
  460. butPtr->textHeight);
  461. textXOffset = (fullWidth - butPtr->textWidth)/2;
  462. imageXOffset = (fullWidth - width)/2;
  463. textYOffset = (fullHeight - butPtr->textHeight)/2;
  464. imageYOffset = (fullHeight - height)/2;
  465. break;
  466.     }
  467.     case COMPOUND_NONE: {break;}
  468. }
  469. TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY,
  470. butPtr->indicatorSpace + fullWidth, fullHeight, &x, &y);
  471. x += butPtr->indicatorSpace;
  472. if (relief == TK_RELIEF_SUNKEN) {
  473.     x += offset;
  474.     y += offset;
  475. }
  476. imageXOffset += x;
  477. imageYOffset += y;
  478. if (butPtr->image != NULL) {
  479.     if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) {
  480. Tk_RedrawImage(butPtr->selectImage, 0, 0,
  481. width, height, pixmap, imageXOffset, imageYOffset);
  482.     } else {
  483. Tk_RedrawImage(butPtr->image, 0, 0,
  484. width, height, pixmap, imageXOffset, imageYOffset);
  485.     }
  486. } else {
  487.     XSetClipOrigin(butPtr->display, gc, imageXOffset, imageYOffset);
  488.     XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc,
  489.     0, 0, (unsigned int) width, (unsigned int) height,
  490.     imageXOffset, imageYOffset, 1);
  491.     XSetClipOrigin(butPtr->display, gc, 0, 0);
  492. }
  493. Tk_DrawTextLayout(butPtr->display, pixmap, gc,
  494. butPtr->textLayout, x + textXOffset, y + textYOffset, 0, -1);
  495. Tk_UnderlineTextLayout(butPtr->display, pixmap, gc,
  496. butPtr->textLayout, x + textXOffset, y + textYOffset,
  497. butPtr->underline);
  498. height = fullHeight;
  499. drawRing = 1;
  500.     } else {
  501. if (haveImage) {
  502.     TkComputeAnchor(butPtr->anchor, tkwin, 0, 0,
  503.     butPtr->indicatorSpace + width, height, &x, &y);
  504.     x += butPtr->indicatorSpace;
  505.     
  506.     if (relief == TK_RELIEF_SUNKEN) {
  507. x += offset;
  508. y += offset;
  509.     }
  510.     imageXOffset += x;
  511.     imageYOffset += y;
  512.     if (butPtr->image != NULL) {
  513. if ((butPtr->selectImage != NULL) &&
  514. (butPtr->flags & SELECTED)) {
  515.     Tk_RedrawImage(butPtr->selectImage, 0, 0, width, height,
  516.     pixmap, imageXOffset, imageYOffset);
  517. } else {
  518.     Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap,
  519.     imageXOffset, imageYOffset);
  520. }
  521.     } else {
  522. XSetClipOrigin(butPtr->display, gc, x, y);
  523. XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0,
  524. (unsigned int) width, (unsigned int) height, x, y, 1);
  525. XSetClipOrigin(butPtr->display, gc, 0, 0);
  526.     }
  527. } else {
  528.     TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY,
  529.     butPtr->indicatorSpace + butPtr->textWidth,
  530.     butPtr->textHeight, &x, &y);
  531.     x += butPtr->indicatorSpace;
  532.     
  533.     if (relief == TK_RELIEF_SUNKEN) {
  534. x += offset;
  535. y += offset;
  536.     }
  537.     Tk_DrawTextLayout(butPtr->display, pixmap, gc, butPtr->textLayout,
  538.     x, y, 0, -1);
  539.     Tk_UnderlineTextLayout(butPtr->display, pixmap, gc,
  540.     butPtr->textLayout, x, y, butPtr->underline);
  541.     height = butPtr->textHeight;
  542.     drawRing = 1;
  543. }
  544.     }
  545.     /*
  546.      * Draw the focus ring.  If this is a push button then we need to
  547.      * put it around the inner edge of the border, otherwise we put it
  548.      * around the text.  The text offsets are only non-zero when this
  549.      * is a compound button.
  550.      */
  551.     
  552.     if (drawRing && butPtr->flags & GOT_FOCUS && butPtr->type != TYPE_LABEL) {
  553. dc = TkWinGetDrawableDC(butPtr->display, pixmap, &state);
  554. if (butPtr->type == TYPE_BUTTON || !butPtr->indicatorOn) {
  555.     rect.top = butPtr->borderWidth + 1 + defaultWidth;
  556.     rect.left = rect.top;
  557.     rect.right = Tk_Width(tkwin) - rect.left;
  558.     rect.bottom = Tk_Height(tkwin) - rect.top;
  559. } else {
  560.     rect.top = y-1 + textYOffset;
  561.     rect.left = x-1 + textXOffset;
  562.     rect.right = x+butPtr->textWidth + 1 + textXOffset;
  563.     rect.bottom = y+butPtr->textHeight + 2 + textYOffset;
  564. }
  565. SetTextColor(dc, gc->foreground);
  566. SetBkColor(dc, gc->background);
  567. DrawFocusRect(dc, &rect);
  568. TkWinReleaseDrawableDC(pixmap, dc, &state);
  569.     }
  570.     y += height/2;
  571.     
  572.     /*
  573.      * Draw the indicator for check buttons and radio buttons.  At this
  574.      * point x and y refer to the top-left corner of the text or image
  575.      * or bitmap.
  576.      */
  577.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn
  578.     && tsdPtr->boxesPtr) {
  579. int xSrc, ySrc;
  580. x -= butPtr->indicatorSpace;
  581. y -= butPtr->indicatorDiameter / 2;
  582. xSrc = (butPtr->flags & SELECTED) ? tsdPtr->boxWidth : 0;
  583. if (butPtr->state == STATE_ACTIVE) {
  584.     xSrc += tsdPtr->boxWidth*2;
  585. }
  586. ySrc = (butPtr->type == TYPE_RADIO_BUTTON) ? 0 : tsdPtr->boxHeight;
  587. /*
  588.  * Update the palette in the boxes bitmap to reflect the current
  589.  * button colors.  Note that this code relies on the layout of the
  590.  * bitmap's palette.  Also, all of the colors used to draw the
  591.  * bitmap must be in the palette that is selected into the DC of
  592.  * the offscreen pixmap.  This requires that the static colors
  593.  * be placed into the palette.
  594.  */
  595. if ((butPtr->state == STATE_DISABLED)
  596. && (butPtr->disabledFg == NULL)) {
  597.     boxesPalette[PAL_CHECK] = FlipColor(TkWinGetBorderPixels(tkwin,
  598.     border, TK_3D_DARK_GC));
  599. } else {
  600.     boxesPalette[PAL_CHECK] = FlipColor(gc->foreground);
  601. }
  602. boxesPalette[PAL_TOP_OUTER] = FlipColor(TkWinGetBorderPixels(tkwin,
  603. border, TK_3D_DARK_GC));
  604. boxesPalette[PAL_TOP_INNER] = FlipColor(TkWinGetBorderPixels(tkwin,
  605. border, TK_3D_DARK2));
  606. boxesPalette[PAL_BOTTOM_INNER] = FlipColor(TkWinGetBorderPixels(tkwin,
  607. border, TK_3D_LIGHT2));
  608. boxesPalette[PAL_BOTTOM_OUTER] = FlipColor(TkWinGetBorderPixels(tkwin,
  609. border, TK_3D_LIGHT_GC));
  610. if (butPtr->state == STATE_DISABLED) {
  611.     boxesPalette[PAL_INTERIOR] = FlipColor(TkWinGetBorderPixels(tkwin,
  612. border, TK_3D_LIGHT2));
  613. } else if (butPtr->selectBorder != NULL) {
  614.     boxesPalette[PAL_INTERIOR] = FlipColor(TkWinGetBorderPixels(tkwin,
  615.     butPtr->selectBorder, TK_3D_FLAT_GC));
  616. } else {
  617.     boxesPalette[PAL_INTERIOR] = FlipColor(GetSysColor(COLOR_WINDOW));
  618. }
  619. boxesPalette[PAL_BACKGROUND] = FlipColor(TkWinGetBorderPixels(tkwin,
  620. border, TK_3D_FLAT_GC));
  621. dc = TkWinGetDrawableDC(butPtr->display, pixmap, &state);
  622. StretchDIBits(dc, x, y, tsdPtr->boxWidth, tsdPtr->boxHeight, 
  623.                 xSrc, ySrc, tsdPtr->boxWidth, tsdPtr->boxHeight, 
  624.                 tsdPtr->boxesBits, (LPBITMAPINFO) tsdPtr->boxesPtr, 
  625.                 DIB_RGB_COLORS, SRCCOPY);
  626. TkWinReleaseDrawableDC(pixmap, dc, &state);
  627.     }
  628.     /*
  629.      * If the button is disabled with a stipple rather than a special
  630.      * foreground color, generate the stippled effect.  If the widget
  631.      * is selected and we use a different background color when selected,
  632.      * must temporarily modify the GC so the stippling is the right color.
  633.      */
  634.     if ((butPtr->state == STATE_DISABLED)
  635.     && ((butPtr->disabledFg == NULL) || (butPtr->image != NULL))) {
  636. if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
  637. && (butPtr->selectBorder != NULL)) {
  638.     XSetForeground(butPtr->display, butPtr->stippleGC,
  639.     Tk_3DBorderColor(butPtr->selectBorder)->pixel);
  640. }
  641. /*
  642.  * Stipple the whole button if no disabledFg was specified,
  643.  * otherwise restrict stippling only to displayed image
  644.  */
  645. if (butPtr->disabledFg == NULL) {
  646.     XFillRectangle(butPtr->display, pixmap, butPtr->stippleGC, 0, 0,
  647.     (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin));
  648. } else {
  649.     XFillRectangle(butPtr->display, pixmap, butPtr->stippleGC,
  650.     imageXOffset, imageYOffset,
  651.     (unsigned) imageWidth, (unsigned) imageHeight);
  652. }
  653. if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
  654. && (butPtr->selectBorder != NULL)) {
  655.     XSetForeground(butPtr->display, butPtr->stippleGC,
  656.     Tk_3DBorderColor(butPtr->normalBorder)->pixel);
  657. }
  658.     }
  659.     /*
  660.      * Draw the border and traversal highlight last.  This way, if the
  661.      * button's contents overflow they'll be covered up by the border.
  662.      */
  663.     if (relief != TK_RELIEF_FLAT) {
  664. Tk_Draw3DRectangle(tkwin, pixmap, border,
  665. defaultWidth, defaultWidth,
  666. Tk_Width(tkwin) - 2*defaultWidth,
  667. Tk_Height(tkwin) - 2*defaultWidth,
  668. butPtr->borderWidth, relief);
  669.     }
  670.     if (defaultWidth != 0) {
  671. dc = TkWinGetDrawableDC(butPtr->display, pixmap, &state);
  672. TkWinFillRect(dc, 0, 0, Tk_Width(tkwin), defaultWidth,
  673. butPtr->highlightColorPtr->pixel);
  674. TkWinFillRect(dc, 0, 0, defaultWidth, Tk_Height(tkwin),
  675. butPtr->highlightColorPtr->pixel);
  676. TkWinFillRect(dc, 0, Tk_Height(tkwin) - defaultWidth,
  677. Tk_Width(tkwin), defaultWidth,
  678. butPtr->highlightColorPtr->pixel);
  679. TkWinFillRect(dc, Tk_Width(tkwin) - defaultWidth, 0,
  680. defaultWidth, Tk_Height(tkwin),
  681. butPtr->highlightColorPtr->pixel);
  682. TkWinReleaseDrawableDC(pixmap, dc, &state);
  683.     }
  684.     if (butPtr->flags & GOT_FOCUS) {
  685. Tk_SetCaretPos(tkwin, x, y, 0 /* not used */);
  686.     }
  687.     /*
  688.      * Copy the information from the off-screen pixmap onto the screen,
  689.      * then delete the pixmap.
  690.      */
  691.     XCopyArea(butPtr->display, pixmap, Tk_WindowId(tkwin),
  692.     butPtr->copyGC, 0, 0, (unsigned) Tk_Width(tkwin),
  693.     (unsigned) Tk_Height(tkwin), 0, 0);
  694.     Tk_FreePixmap(butPtr->display, pixmap);
  695. }
  696. /*
  697.  *----------------------------------------------------------------------
  698.  *
  699.  * TkpComputeButtonGeometry --
  700.  *
  701.  * After changes in a button's text or bitmap, this procedure
  702.  * recomputes the button's geometry and passes this information
  703.  * along to the geometry manager for the window.
  704.  *
  705.  * Results:
  706.  * None.
  707.  *
  708.  * Side effects:
  709.  * The button's window may change size.
  710.  *
  711.  *----------------------------------------------------------------------
  712.  */
  713. void
  714. TkpComputeButtonGeometry(butPtr)
  715.     register TkButton *butPtr; /* Button whose geometry may have changed. */
  716. {
  717.     int txtWidth, txtHeight; /* Width and height of text */
  718.     int imgWidth, imgHeight; /* Width and height of image */
  719.     int width = 0, height = 0; /* Width and height of button */
  720.     int haveImage, haveText;
  721.     int avgWidth;
  722.     int minWidth;
  723.     /* Vertical and horizontal dialog units size in pixels. */
  724.     double vDLU, hDLU;
  725.     Tk_FontMetrics fm;
  726.     
  727.     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 
  728. Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
  729.     if (butPtr->highlightWidth < 0) {
  730. butPtr->highlightWidth = 0;
  731.     }
  732.     butPtr->inset = butPtr->highlightWidth + butPtr->borderWidth;
  733.     butPtr->indicatorSpace = 0;
  734.     if (!tsdPtr->boxesPtr) {
  735. InitBoxes();
  736.     }
  737.     /* Figure out image metrics */
  738.     if (butPtr->image != NULL) {
  739. Tk_SizeOfImage(butPtr->image, &imgWidth, &imgHeight);
  740. haveImage = 1;
  741.     } else if (butPtr->bitmap != None) {
  742. Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap,
  743. &imgWidth, &imgHeight);
  744. haveImage = 1;
  745.     } else {
  746. imgWidth = 0;
  747. imgHeight = 0;
  748. haveImage = 0;
  749.     }
  750.     /* 
  751.      * Figure out font metrics (even if we don't have text because we need
  752.      * DLUs (based on font, not text) for some spacing calculations below).
  753.      */
  754.     Tk_FreeTextLayout(butPtr->textLayout);
  755.     butPtr->textLayout = Tk_ComputeTextLayout(butPtr->tkfont,
  756.     Tcl_GetString(butPtr->textPtr), -1, butPtr->wrapLength,
  757.     butPtr->justify, 0, &butPtr->textWidth, &butPtr->textHeight);
  758.     txtWidth = butPtr->textWidth;
  759.     txtHeight = butPtr->textHeight;
  760.     haveText = (*(Tcl_GetString(butPtr->textPtr)) != '');
  761.     avgWidth = (Tk_TextWidth(butPtr->tkfont,
  762.     "abcdefghijklmnopqurstuvwzyABCDEFGHIJKLMNOPQURSTUVWZY",
  763.     52) + 26) / 52;
  764.     Tk_GetFontMetrics(butPtr->tkfont, &fm);
  765.     /* Compute dialog units for layout calculations. */
  766.     hDLU = avgWidth / 4.0;
  767.     vDLU = fm.linespace / 8.0;
  768.     /*
  769.      * First, let's try to compute button size "by the book" (See "Microsoft
  770.      * Windows User Experience" (ISBN 0-7356-0566-1), Chapter 14 - Visual
  771.      * Design, Section 4 - Layout (page 448)).
  772.      *
  773.      * Note, that Tk "buttons" are Microsoft "Command buttons", Tk
  774.      * "checkbuttons" are Microsoft "check boxes", Tk "radiobuttons" are
  775.      * Microsoft "option buttons", and Tk "labels" are Microsoft "text
  776.      * labels".
  777.      */
  778.     /*
  779.      * Set width and height by button type; See User Experience table, p449.
  780.      * These are text-based measurements, even if the text is "".
  781.      * If there is an image, height will get set again later.
  782.      */
  783.     switch (butPtr->type) {
  784.         case TYPE_BUTTON: {
  785.     /*
  786.      * First compute the minimum width of the button in 
  787.      * characters. MWUE says that the button should be
  788.      * 50 DLUs.  We allow 6 DLUs padding left and right.
  789.      * (There is no rule but this is consistent with the
  790.      * fact that button text is 8 DLUs high and buttons
  791.      * are 14 DLUs high.)
  792.      * 
  793.      * The width is specified in characters.  A character
  794.      * is, by definition, 4 DLUs wide.  11 char * 4 DLU
  795.      * is 44 DLU + 6 DLU padding = 50 DLU. Therefore,
  796.      * width = -11 -> MWUE compliant buttons.
  797.      */
  798.     if (butPtr->width < 0) {
  799. /* Min width in characters */
  800. minWidth = -(butPtr->width);
  801. /* Allow for characters */
  802. width = avgWidth * minWidth;
  803. /* Add for padding */
  804. width += (int)(0.5 + (6 * hDLU));
  805.     } 
  806.     /*
  807.      * If shrink-wrapping was requested (width = 0) or
  808.      * if the text is wider than the default button width,
  809.      * adjust the button width up to suit.
  810.      */
  811.     if (butPtr->width == 0 
  812.     || (txtWidth + (int)(0.5 + (6 * hDLU)) > width)) {
  813. width = txtWidth + (int)(0.5 + (6 * hDLU));
  814.     }
  815.     /*
  816.      * The User Experience says 14 DLUs.  Since text is, by
  817.      * definition, 8 DLU/line, this allows for multi-line text
  818.      * while working perfectly for single-line text.
  819.      */
  820.     height = txtHeight + (int)(0.5 + (6 * vDLU));
  821.     /*
  822.      * The above includes 6 DLUs of padding which should include
  823.      * defaults of 1 pixel of highlightwidth, 2 pixels of 
  824.      * borderwidth, 1 pixel of padding and 1 pixel of extra inset 
  825.      * on each side.  Those will be added later so reduce width 
  826.      * and height now to compensate.
  827.      */
  828.     width  -= 10;
  829.     height -= 10;
  830.     if (!haveImage) {
  831. /*
  832.  * Extra inset for the focus ring.
  833.  */
  834. butPtr->inset += 1;
  835.     }
  836.     break;
  837. }
  838.         case TYPE_LABEL: {
  839.             /*
  840.              * The User Experience says, "as wide as needed".
  841.              */
  842.             width = txtWidth;
  843.             /*
  844.              * The User Experience says, "8 (DLUs) per line of text."
  845.              * Since text is, by definition, 8 DLU/line, this allows
  846.              * for multi-line text while working perfectly for single-line
  847.              * text.
  848.              */
  849.             if (txtHeight) {
  850.                 height = txtHeight;
  851.             } else {
  852. /*
  853.  * If there's no text, we want the height to be one linespace.
  854.  */
  855.                 height = fm.linespace;
  856.             }
  857.             break;
  858.         }
  859.         case TYPE_RADIO_BUTTON:
  860.         case TYPE_CHECK_BUTTON: {
  861.             /* See note for TYPE_LABEL */
  862.             width = txtWidth;
  863.             /*
  864.              * The User Experience says 10 DLUs.  (Is that one DLU above
  865.              * and below for the focus ring?)  See note above about
  866.              * multi-line text and 8 DLU/line.
  867.              */
  868.             height = txtHeight + (int)(0.5 + (2.0 * vDLU));
  869.             
  870.             /*
  871.              * The above includes 2 DLUs of padding which should include
  872.              * defaults of 1 pixel of highlightwidth, 0 pixels of 
  873.              * borderwidth, and 1 pixel of padding on each side.  Those
  874.              * will be added later so reduce height now to compensate.
  875.              */
  876.             height -= 4;
  877.             
  878.             /*
  879.              * Extra inset for the focus ring.
  880.              */
  881.             butPtr->inset += 1;
  882.             break;
  883.         }
  884.     }/* switch */
  885.     /*
  886.      * At this point, the width and height are correct for a Tk text
  887.      * button, excluding padding and inset, but we have to allow for
  888.      * compound buttons.  The image may be above, below, left, or right
  889.      * of the text.
  890.      */
  891.     /*
  892.      * If the button is compound (i.e., it shows both an image and text),
  893.      * the new geometry is a combination of the image and text geometry.
  894.      * We only honor the compound bit if the button has both text and an
  895.      * image, because otherwise it is not really a compound button.
  896.      */
  897.     if (butPtr->compound != COMPOUND_NONE && haveImage && haveText) {
  898. switch ((enum compound) butPtr->compound) {
  899.     case COMPOUND_TOP:
  900.     case COMPOUND_BOTTOM: {
  901. /* Image is above or below text */
  902. if (imgWidth > width) {
  903.     width = imgWidth;
  904. }
  905. height += imgHeight + butPtr->padY;
  906. break;
  907.     }
  908.     case COMPOUND_LEFT:
  909.     case COMPOUND_RIGHT: {
  910. /* Image is left or right of text */
  911. /*
  912.  * Only increase width of button if image doesn't fit in
  913.  * slack space of default button width
  914.  */
  915. if ((imgWidth + txtWidth + butPtr->padX) > width) {
  916.     width = imgWidth + txtWidth + butPtr->padX;
  917. }
  918. if (imgHeight > height) {
  919.     height = imgHeight;
  920. }
  921. break;
  922.     }
  923.     case COMPOUND_CENTER: {
  924. /* Image and text are superimposed */
  925. if (imgWidth > width) {
  926.     width = imgWidth;
  927. }
  928. if (imgHeight > height) {
  929.     height = imgHeight;
  930. }
  931. break;
  932.     }
  933. } /* switch */
  934.         /* Fix up for minimum width */
  935.         if (butPtr->width < 0) {
  936.             /* minWidth in pixels (because there's an image */
  937.             minWidth = -(butPtr->width);
  938.             if (width < minWidth) {
  939.                 width =  minWidth;
  940.             }
  941.         } else if (butPtr->width > 0) {
  942.     width = butPtr->width;
  943. }
  944. if (butPtr->height > 0) {
  945.     height = butPtr->height;
  946. }
  947. width += 2*butPtr->padX;
  948. height += 2*butPtr->padY;
  949.     } else if (haveImage) {
  950. if (butPtr->width > 0) {
  951.     width = butPtr->width;
  952. } else {
  953.     width = imgWidth;
  954. }
  955. if (butPtr->height > 0) {
  956.     height = butPtr->height;
  957. } else {
  958.     height = imgHeight;
  959. }
  960.     } else {
  961.         /* No image.  May or may not be text.  May or may not be compound. */
  962.         /*
  963.  * butPtr->width is in characters.  We need to allow for that
  964.  * many characters on the face, not in the over-all button width
  965.  */
  966.         if (butPtr->width > 0) {
  967.     width = butPtr->width * avgWidth;
  968. }
  969. /*
  970.  * butPtr->height is in lines of text. We need to allow for
  971.  * that many lines on the face, not in the over-all button
  972.  * height.
  973.  */
  974. if (butPtr->height > 0) {
  975.     height = butPtr->height * fm.linespace;
  976.     /*
  977.      * Make the same adjustments as above to get same height for
  978.      * e.g. a one line text with -height 0 or 1.  [Bug #565485]
  979.      */
  980.     switch (butPtr->type) {
  981. case TYPE_BUTTON: {
  982.     height += (int)(0.5 + (6 * vDLU)) - 10;
  983.     break;
  984. }
  985. case TYPE_RADIO_BUTTON:
  986. case TYPE_CHECK_BUTTON: {
  987.     height += (int)(0.5 + (2.0 * vDLU)) - 4;
  988.     break;
  989. }
  990.     }
  991. }
  992.     
  993. width  += 2 * butPtr->padX;
  994. height += 2 * butPtr->padY;
  995.     }
  996.     /* Fix up width and height for indicator sizing and spacing */
  997.     if (butPtr->type == TYPE_RADIO_BUTTON
  998.     || butPtr->type == TYPE_CHECK_BUTTON) {
  999. if (butPtr->indicatorOn) {
  1000.     butPtr->indicatorDiameter = tsdPtr->boxHeight;
  1001.             /* 
  1002.              * Make sure we can see the whole indicator, even if the text
  1003.              * or image is very small.
  1004.              */
  1005.             if (height < butPtr->indicatorDiameter) {
  1006.                 height = butPtr->indicatorDiameter;
  1007.             }
  1008.     /*
  1009.      * There is no rule for space between the indicator and
  1010.      * the text (the two are atomic on 'Windows) but the User
  1011.      * Experience page 451 says leave 3 hDLUs between "text
  1012.      * labels and their associated controls".
  1013.      */
  1014.     butPtr->indicatorSpace = butPtr->indicatorDiameter +
  1015. (int)(0.5 + (3.0 * hDLU));
  1016.     width += butPtr->indicatorSpace;
  1017. }
  1018.     }
  1019.     /*
  1020.      * Inset is always added to the size.
  1021.      */
  1022.     width  += 2 * butPtr->inset;
  1023.     height += 2 * butPtr->inset;
  1024.     Tk_GeometryRequest(butPtr->tkwin, width, height);
  1025.     Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset);
  1026. }
  1027. /*
  1028.  *----------------------------------------------------------------------
  1029.  *
  1030.  * ButtonProc --
  1031.  *
  1032.  * This function is call by Windows whenever an event occurs on
  1033.  * a button control created by Tk.
  1034.  *
  1035.  * Results:
  1036.  * Standard Windows return value.
  1037.  *
  1038.  * Side effects:
  1039.  * May generate events.
  1040.  *
  1041.  *----------------------------------------------------------------------
  1042.  */
  1043. static LRESULT CALLBACK
  1044. ButtonProc(hwnd, message, wParam, lParam)
  1045.     HWND hwnd;
  1046.     UINT message;
  1047.     WPARAM wParam;
  1048.     LPARAM lParam;
  1049. {
  1050.     LRESULT result;
  1051.     WinButton *butPtr;
  1052.     Tk_Window tkwin = Tk_HWNDToWindow(hwnd);
  1053.     if (tkwin == NULL) {
  1054. panic("ButtonProc called on an invalid HWND");
  1055.     }
  1056.     butPtr = (WinButton *)((TkWindow*)tkwin)->instanceData;
  1057.     switch(message) {
  1058. case WM_ERASEBKGND:
  1059.     return 0;
  1060. case BM_GETCHECK:
  1061.     if (((butPtr->info.type == TYPE_CHECK_BUTTON)
  1062.     || (butPtr->info.type == TYPE_RADIO_BUTTON))
  1063.     && butPtr->info.indicatorOn) {
  1064. return (butPtr->info.flags & SELECTED)
  1065.     ? BST_CHECKED : BST_UNCHECKED;
  1066.     }
  1067.     return 0;
  1068. case BM_GETSTATE: {
  1069.     DWORD state = 0;
  1070.     if (((butPtr->info.type == TYPE_CHECK_BUTTON)
  1071.     || (butPtr->info.type == TYPE_RADIO_BUTTON))
  1072.     && butPtr->info.indicatorOn) {
  1073. state = (butPtr->info.flags & SELECTED)
  1074.     ? BST_CHECKED : BST_UNCHECKED;
  1075.     }
  1076.     if (butPtr->info.flags & GOT_FOCUS) {
  1077. state |= BST_FOCUS;
  1078.     }
  1079.     return state;
  1080. }
  1081. case WM_ENABLE:
  1082.     break;
  1083. case WM_PAINT: {
  1084.     PAINTSTRUCT ps;
  1085.     BeginPaint(hwnd, &ps);
  1086.     EndPaint(hwnd, &ps);
  1087.     TkpDisplayButton((ClientData)butPtr);
  1088.     /*
  1089.      * Special note: must cancel any existing idle handler
  1090.      * for TkpDisplayButton;  it's no longer needed, and
  1091.      * TkpDisplayButton cleared the REDRAW_PENDING flag.
  1092.      */
  1093.            
  1094.     Tcl_CancelIdleCall(TkpDisplayButton, (ClientData)butPtr);
  1095.     return 0;
  1096. }
  1097. case BN_CLICKED: {
  1098.     int code;
  1099.     Tcl_Interp *interp = butPtr->info.interp;
  1100.     if (butPtr->info.state != STATE_DISABLED) {
  1101. Tcl_Preserve((ClientData)interp);
  1102. code = TkInvokeButton((TkButton*)butPtr);
  1103. if (code != TCL_OK && code != TCL_CONTINUE
  1104. && code != TCL_BREAK) {
  1105.     Tcl_AddErrorInfo(interp, "n    (button invoke)");
  1106.     Tcl_BackgroundError(interp);
  1107. }
  1108. Tcl_Release((ClientData)interp);
  1109.     }
  1110.     Tcl_ServiceAll();
  1111.     return 0;
  1112. }
  1113. default:
  1114.     if (Tk_TranslateWinEvent(hwnd, message, wParam, lParam, &result)) {
  1115. return result;
  1116.     }
  1117.     }
  1118.     return DefWindowProc(hwnd, message, wParam, lParam);
  1119. }