1 |
/* $Header$ */ |
2 |
|
3 |
/* |
4 |
* tkWinButton.c -- |
5 |
* |
6 |
* This file implements the Windows specific portion of the button |
7 |
* widgets. |
8 |
* |
9 |
* Copyright (c) 1996-1998 by Sun Microsystems, Inc. |
10 |
* |
11 |
* See the file "license.terms" for information on usage and redistribution |
12 |
* of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
13 |
* |
14 |
* RCS: @(#) $Id: tkwinbutton.c,v 1.1.1.1 2001/06/13 05:11:56 dtashley Exp $ |
15 |
*/ |
16 |
|
17 |
#define OEMRESOURCE |
18 |
#include "tkWinInt.h" |
19 |
#include "tkButton.h" |
20 |
|
21 |
#include "resource.h" |
22 |
/* Necessary to load the TK_BUTTONS. |
23 |
*/ |
24 |
|
25 |
/* |
26 |
* These macros define the base style flags for the different button types. |
27 |
*/ |
28 |
|
29 |
#define LABEL_STYLE (BS_OWNERDRAW | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS) |
30 |
#define PUSH_STYLE (BS_OWNERDRAW | BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS) |
31 |
#define CHECK_STYLE (BS_OWNERDRAW | BS_CHECKBOX | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS) |
32 |
#define RADIO_STYLE (BS_OWNERDRAW | BS_RADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS) |
33 |
|
34 |
static DWORD buttonStyles[] = { |
35 |
LABEL_STYLE, PUSH_STYLE, CHECK_STYLE, RADIO_STYLE |
36 |
}; |
37 |
|
38 |
/* |
39 |
* Declaration of Windows specific button structure. |
40 |
*/ |
41 |
|
42 |
typedef struct WinButton { |
43 |
TkButton info; /* Generic button info. */ |
44 |
WNDPROC oldProc; /* Old window procedure. */ |
45 |
HWND hwnd; /* Current window handle. */ |
46 |
Pixmap pixmap; /* Bitmap for rendering the button. */ |
47 |
DWORD style; /* Window style flags. */ |
48 |
} WinButton; |
49 |
|
50 |
|
51 |
/* |
52 |
* The following macro reverses the order of RGB bytes to convert |
53 |
* between RGBQUAD and COLORREF values. |
54 |
*/ |
55 |
|
56 |
#define FlipColor(rgb) (RGB(GetBValue(rgb),GetGValue(rgb),GetRValue(rgb))) |
57 |
|
58 |
/* |
59 |
* The following enumeration defines the meaning of the palette entries |
60 |
* in the "buttons" image used to draw checkbox and radiobutton indicators. |
61 |
*/ |
62 |
|
63 |
enum { |
64 |
PAL_CHECK = 0, |
65 |
PAL_TOP_OUTER = 1, |
66 |
PAL_BOTTOM_OUTER = 2, |
67 |
PAL_BOTTOM_INNER = 3, |
68 |
PAL_INTERIOR = 4, |
69 |
PAL_TOP_INNER = 5, |
70 |
PAL_BACKGROUND = 6 |
71 |
}; |
72 |
|
73 |
/* |
74 |
* Cached information about the boxes bitmap, and the default border |
75 |
* width for a button in string form for use in Tk_OptionSpec for |
76 |
* the various button widget classes. |
77 |
*/ |
78 |
|
79 |
typedef struct ThreadSpecificData { |
80 |
BITMAPINFOHEADER *boxesPtr; /* Information about the bitmap. */ |
81 |
DWORD *boxesPalette; /* Pointer to color palette. */ |
82 |
LPSTR boxesBits; /* Pointer to bitmap data. */ |
83 |
DWORD boxHeight; /* Height of each sub-image. */ |
84 |
DWORD boxWidth ; /* Width of each sub-image. */ |
85 |
char defWidth[TCL_INTEGER_SPACE]; |
86 |
} ThreadSpecificData; |
87 |
static Tcl_ThreadDataKey dataKey; |
88 |
|
89 |
/* |
90 |
* Declarations for functions defined in this file. |
91 |
*/ |
92 |
|
93 |
static int ButtonBindProc _ANSI_ARGS_((ClientData clientData, |
94 |
Tcl_Interp *interp, XEvent *eventPtr, |
95 |
Tk_Window tkwin, KeySym keySym)); |
96 |
static LRESULT CALLBACK ButtonProc _ANSI_ARGS_((HWND hwnd, UINT message, |
97 |
WPARAM wParam, LPARAM lParam)); |
98 |
static DWORD ComputeStyle _ANSI_ARGS_((WinButton* butPtr)); |
99 |
static Window CreateProc _ANSI_ARGS_((Tk_Window tkwin, |
100 |
Window parent, ClientData instanceData)); |
101 |
static void InitBoxes _ANSI_ARGS_((void)); |
102 |
|
103 |
/* |
104 |
* The class procedure table for the button widgets. |
105 |
*/ |
106 |
|
107 |
TkClassProcs tkpButtonProcs = { |
108 |
CreateProc, /* createProc. */ |
109 |
TkButtonWorldChanged, /* geometryProc. */ |
110 |
NULL /* modalProc. */ |
111 |
}; |
112 |
|
113 |
|
114 |
/* |
115 |
*---------------------------------------------------------------------- |
116 |
* |
117 |
* InitBoxes -- |
118 |
* |
119 |
* This function load the Tk 3d button bitmap. "buttons" is a 16 |
120 |
* color bitmap that is laid out such that the top row contains |
121 |
* the 4 checkbox images, and the bottom row contains the radio |
122 |
* button images. Note that the bitmap is stored in bottom-up |
123 |
* format. Also, the first seven palette entries are used to |
124 |
* identify the different parts of the bitmaps so we can do the |
125 |
* appropriate color mappings based on the current button colors. |
126 |
* |
127 |
* Results: |
128 |
* None. |
129 |
* |
130 |
* Side effects: |
131 |
* Loads the "buttons" resource. |
132 |
* |
133 |
*---------------------------------------------------------------------- |
134 |
*/ |
135 |
|
136 |
static void |
137 |
InitBoxes() |
138 |
{ |
139 |
/* |
140 |
* For DLLs like Tk, the HINSTANCE is the same as the HMODULE. |
141 |
*/ |
142 |
|
143 |
HMODULE module = (HINSTANCE) Tk_GetHINSTANCE(); |
144 |
HRSRC hrsrc; |
145 |
HGLOBAL hblk; |
146 |
LPBITMAPINFOHEADER newBitmap; |
147 |
DWORD size; |
148 |
ThreadSpecificData *tsdPtr = (ThreadSpecificData *) |
149 |
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); |
150 |
|
151 |
hrsrc = FindResource(module, (const char *)TK_BUTTONS, RT_BITMAP); |
152 |
/* Found experimentally that using the constant defined in |
153 |
** RESOURCE.H in the call above works as per Microsoft |
154 |
** documentation. Old line was: |
155 |
** hrsrc = FindResource(module, "buttons", RT_BITMAP); |
156 |
** Didn't work reliably with GUI build after code ported. |
157 |
** Believe the call above accesses the resource correctly. |
158 |
*/ |
159 |
|
160 |
if (hrsrc) { |
161 |
hblk = LoadResource(module, hrsrc); |
162 |
tsdPtr->boxesPtr = (LPBITMAPINFOHEADER)LockResource(hblk); |
163 |
} |
164 |
|
165 |
/* |
166 |
* Copy the DIBitmap into writable memory. |
167 |
*/ |
168 |
|
169 |
if (tsdPtr->boxesPtr != NULL && !(tsdPtr->boxesPtr->biWidth % 4) |
170 |
&& !(tsdPtr->boxesPtr->biHeight % 2)) { |
171 |
size = tsdPtr->boxesPtr->biSize + (1 << tsdPtr->boxesPtr->biBitCount) |
172 |
* sizeof(RGBQUAD) + tsdPtr->boxesPtr->biSizeImage; |
173 |
newBitmap = (LPBITMAPINFOHEADER) ckalloc(size); |
174 |
memcpy(newBitmap, tsdPtr->boxesPtr, size); |
175 |
tsdPtr->boxesPtr = newBitmap; |
176 |
tsdPtr->boxWidth = tsdPtr->boxesPtr->biWidth / 4; |
177 |
tsdPtr->boxHeight = tsdPtr->boxesPtr->biHeight / 2; |
178 |
tsdPtr->boxesPalette = (DWORD*) (((LPSTR) tsdPtr->boxesPtr) |
179 |
+ tsdPtr->boxesPtr->biSize); |
180 |
tsdPtr->boxesBits = ((LPSTR) tsdPtr->boxesPalette) |
181 |
+ ((1 << tsdPtr->boxesPtr->biBitCount) * sizeof(RGBQUAD)); |
182 |
} else { |
183 |
tsdPtr->boxesPtr = NULL; |
184 |
} |
185 |
} |
186 |
|
187 |
/* |
188 |
*---------------------------------------------------------------------- |
189 |
* |
190 |
* TkpButtonSetDefaults -- |
191 |
* |
192 |
* This procedure is invoked before option tables are created for |
193 |
* buttons. It modifies some of the default values to match the |
194 |
* current values defined for this platform. |
195 |
* |
196 |
* Results: |
197 |
* Some of the default values in *specPtr are modified. |
198 |
* |
199 |
* Side effects: |
200 |
* Updates some of. |
201 |
* |
202 |
*---------------------------------------------------------------------- |
203 |
*/ |
204 |
|
205 |
void |
206 |
TkpButtonSetDefaults(specPtr) |
207 |
Tk_OptionSpec *specPtr; /* Points to an array of option specs, |
208 |
* terminated by one with type |
209 |
* TK_OPTION_END. */ |
210 |
{ |
211 |
int width; |
212 |
ThreadSpecificData *tsdPtr = (ThreadSpecificData *) |
213 |
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); |
214 |
|
215 |
if (tsdPtr->defWidth[0] == 0) { |
216 |
width = GetSystemMetrics(SM_CXEDGE); |
217 |
if (width == 0) { |
218 |
width = 1; |
219 |
} |
220 |
sprintf(tsdPtr->defWidth, "%d", width); |
221 |
} |
222 |
for ( ; specPtr->type != TK_OPTION_END; specPtr++) { |
223 |
if (specPtr->internalOffset == Tk_Offset(TkButton, borderWidth)) { |
224 |
specPtr->defValue = tsdPtr->defWidth; |
225 |
} |
226 |
} |
227 |
} |
228 |
|
229 |
/* |
230 |
*---------------------------------------------------------------------- |
231 |
* |
232 |
* TkpCreateButton -- |
233 |
* |
234 |
* Allocate a new TkButton structure. |
235 |
* |
236 |
* Results: |
237 |
* Returns a newly allocated TkButton structure. |
238 |
* |
239 |
* Side effects: |
240 |
* Registers an event handler for the widget. |
241 |
* |
242 |
*---------------------------------------------------------------------- |
243 |
*/ |
244 |
|
245 |
TkButton * |
246 |
TkpCreateButton(tkwin) |
247 |
Tk_Window tkwin; |
248 |
{ |
249 |
WinButton *butPtr; |
250 |
|
251 |
butPtr = (WinButton *)ckalloc(sizeof(WinButton)); |
252 |
butPtr->hwnd = NULL; |
253 |
return (TkButton *) butPtr; |
254 |
} |
255 |
|
256 |
/* |
257 |
*---------------------------------------------------------------------- |
258 |
* |
259 |
* CreateProc -- |
260 |
* |
261 |
* This function creates a new Button control, subclasses |
262 |
* the instance, and generates a new Window object. |
263 |
* |
264 |
* Results: |
265 |
* Returns the newly allocated Window object, or None on failure. |
266 |
* |
267 |
* Side effects: |
268 |
* Causes a new Button control to come into existence. |
269 |
* |
270 |
*---------------------------------------------------------------------- |
271 |
*/ |
272 |
|
273 |
static Window |
274 |
CreateProc(tkwin, parentWin, instanceData) |
275 |
Tk_Window tkwin; /* Token for window. */ |
276 |
Window parentWin; /* Parent of new window. */ |
277 |
ClientData instanceData; /* Button instance data. */ |
278 |
{ |
279 |
Window window; |
280 |
HWND parent; |
281 |
char *class; |
282 |
WinButton *butPtr = (WinButton *)instanceData; |
283 |
|
284 |
parent = Tk_GetHWND(parentWin); |
285 |
if (butPtr->info.type == TYPE_LABEL) { |
286 |
class = "STATIC"; |
287 |
butPtr->style = SS_OWNERDRAW | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS; |
288 |
} else { |
289 |
class = "BUTTON"; |
290 |
butPtr->style = BS_OWNERDRAW | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS; |
291 |
} |
292 |
butPtr->hwnd = CreateWindow(class, NULL, butPtr->style, |
293 |
Tk_X(tkwin), Tk_Y(tkwin), Tk_Width(tkwin), Tk_Height(tkwin), |
294 |
parent, NULL, Tk_GetHINSTANCE(), NULL); |
295 |
SetWindowPos(butPtr->hwnd, HWND_TOP, 0, 0, 0, 0, |
296 |
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); |
297 |
butPtr->oldProc = (WNDPROC)SetWindowLong(butPtr->hwnd, GWL_WNDPROC, |
298 |
(DWORD) ButtonProc); |
299 |
|
300 |
window = Tk_AttachHWND(tkwin, butPtr->hwnd); |
301 |
return window; |
302 |
} |
303 |
|
304 |
/* |
305 |
*---------------------------------------------------------------------- |
306 |
* |
307 |
* TkpDestroyButton -- |
308 |
* |
309 |
* Free data structures associated with the button control. |
310 |
* |
311 |
* Results: |
312 |
* None. |
313 |
* |
314 |
* Side effects: |
315 |
* Restores the default control state. |
316 |
* |
317 |
*---------------------------------------------------------------------- |
318 |
*/ |
319 |
|
320 |
void |
321 |
TkpDestroyButton(butPtr) |
322 |
TkButton *butPtr; |
323 |
{ |
324 |
WinButton *winButPtr = (WinButton *)butPtr; |
325 |
HWND hwnd = winButPtr->hwnd; |
326 |
if (hwnd) { |
327 |
SetWindowLong(hwnd, GWL_WNDPROC, (DWORD) winButPtr->oldProc); |
328 |
} |
329 |
} |
330 |
|
331 |
/* |
332 |
*---------------------------------------------------------------------- |
333 |
* |
334 |
* TkpDisplayButton -- |
335 |
* |
336 |
* This procedure is invoked to display a button widget. It is |
337 |
* normally invoked as an idle handler. |
338 |
* |
339 |
* Results: |
340 |
* None. |
341 |
* |
342 |
* Side effects: |
343 |
* Information appears on the screen. The REDRAW_PENDING flag |
344 |
* is cleared. |
345 |
* |
346 |
*---------------------------------------------------------------------- |
347 |
*/ |
348 |
|
349 |
void |
350 |
TkpDisplayButton(clientData) |
351 |
ClientData clientData; /* Information about widget. */ |
352 |
{ |
353 |
TkWinDCState state; |
354 |
HDC dc; |
355 |
register TkButton *butPtr = (TkButton *) clientData; |
356 |
GC gc; |
357 |
Tk_3DBorder border; |
358 |
Pixmap pixmap; |
359 |
int x = 0; /* Initialization only needed to stop |
360 |
* compiler warning. */ |
361 |
int y, relief; |
362 |
register Tk_Window tkwin = butPtr->tkwin; |
363 |
int width, height; |
364 |
int defaultWidth; /* Width of default ring. */ |
365 |
int offset; /* 0 means this is a label widget. 1 means |
366 |
* it is a flavor of button, so we offset |
367 |
* the text to make the button appear to |
368 |
* move up and down as the relief changes. */ |
369 |
DWORD *boxesPalette; |
370 |
|
371 |
ThreadSpecificData *tsdPtr = (ThreadSpecificData *) |
372 |
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); |
373 |
|
374 |
boxesPalette= tsdPtr->boxesPalette; |
375 |
butPtr->flags &= ~REDRAW_PENDING; |
376 |
if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { |
377 |
return; |
378 |
} |
379 |
|
380 |
border = butPtr->normalBorder; |
381 |
if ((butPtr->state == STATE_DISABLED) && (butPtr->disabledFg != NULL)) { |
382 |
gc = butPtr->disabledGC; |
383 |
} else if ((butPtr->state == STATE_ACTIVE) |
384 |
&& !Tk_StrictMotif(butPtr->tkwin)) { |
385 |
gc = butPtr->activeTextGC; |
386 |
border = butPtr->activeBorder; |
387 |
} else { |
388 |
gc = butPtr->normalTextGC; |
389 |
} |
390 |
if ((butPtr->flags & SELECTED) && (butPtr->state != STATE_ACTIVE) |
391 |
&& (butPtr->selectBorder != NULL) && !butPtr->indicatorOn) { |
392 |
border = butPtr->selectBorder; |
393 |
} |
394 |
|
395 |
/* |
396 |
* Override the relief specified for the button if this is a |
397 |
* checkbutton or radiobutton and there's no indicator. |
398 |
*/ |
399 |
|
400 |
relief = butPtr->relief; |
401 |
if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) { |
402 |
relief = (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN |
403 |
: TK_RELIEF_RAISED; |
404 |
} |
405 |
|
406 |
/* |
407 |
* Compute width of default ring and offset for pushed buttons. |
408 |
*/ |
409 |
|
410 |
if (butPtr->type == TYPE_BUTTON) { |
411 |
defaultWidth = ((butPtr->defaultState == DEFAULT_ACTIVE) |
412 |
? butPtr->highlightWidth : 0); |
413 |
offset = 1; |
414 |
} else { |
415 |
defaultWidth = 0; |
416 |
if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) { |
417 |
offset = 1; |
418 |
} else { |
419 |
offset = 0; |
420 |
} |
421 |
} |
422 |
|
423 |
/* |
424 |
* In order to avoid screen flashes, this procedure redraws |
425 |
* the button in a pixmap, then copies the pixmap to the |
426 |
* screen in a single operation. This means that there's no |
427 |
* point in time where the on-sreen image has been cleared. |
428 |
*/ |
429 |
|
430 |
pixmap = Tk_GetPixmap(butPtr->display, Tk_WindowId(tkwin), |
431 |
Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin)); |
432 |
Tk_Fill3DRectangle(tkwin, pixmap, border, 0, 0, Tk_Width(tkwin), |
433 |
Tk_Height(tkwin), 0, TK_RELIEF_FLAT); |
434 |
|
435 |
/* |
436 |
* Display image or bitmap or text for button. |
437 |
*/ |
438 |
|
439 |
if (butPtr->image != None) { |
440 |
Tk_SizeOfImage(butPtr->image, &width, &height); |
441 |
|
442 |
imageOrBitmap: |
443 |
TkComputeAnchor(butPtr->anchor, tkwin, 0, 0, |
444 |
butPtr->indicatorSpace + width, height, &x, &y); |
445 |
x += butPtr->indicatorSpace; |
446 |
|
447 |
if (relief == TK_RELIEF_SUNKEN) { |
448 |
x += offset; |
449 |
y += offset; |
450 |
} |
451 |
if (butPtr->image != NULL) { |
452 |
if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) { |
453 |
Tk_RedrawImage(butPtr->selectImage, 0, 0, width, height, |
454 |
pixmap, x, y); |
455 |
} else { |
456 |
Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap, |
457 |
x, y); |
458 |
} |
459 |
} else { |
460 |
XSetClipOrigin(butPtr->display, gc, x, y); |
461 |
XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0, |
462 |
(unsigned int) width, (unsigned int) height, x, y, 1); |
463 |
XSetClipOrigin(butPtr->display, gc, 0, 0); |
464 |
} |
465 |
y += height/2; |
466 |
} else if (butPtr->bitmap != None) { |
467 |
Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height); |
468 |
goto imageOrBitmap; |
469 |
} else { |
470 |
RECT rect; |
471 |
TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY, |
472 |
butPtr->indicatorSpace + butPtr->textWidth, butPtr->textHeight, |
473 |
&x, &y); |
474 |
|
475 |
x += butPtr->indicatorSpace; |
476 |
|
477 |
if (relief == TK_RELIEF_SUNKEN) { |
478 |
x += offset; |
479 |
y += offset; |
480 |
} |
481 |
Tk_DrawTextLayout(butPtr->display, pixmap, gc, butPtr->textLayout, |
482 |
x, y, 0, -1); |
483 |
Tk_UnderlineTextLayout(butPtr->display, pixmap, gc, |
484 |
butPtr->textLayout, x, y, butPtr->underline); |
485 |
|
486 |
/* |
487 |
* Draw the focus ring. If this is a push button then we need to put |
488 |
* it around the inner edge of the border, otherwise we put it around |
489 |
* the text. |
490 |
*/ |
491 |
|
492 |
if (butPtr->flags & GOT_FOCUS && butPtr->type != TYPE_LABEL) { |
493 |
dc = TkWinGetDrawableDC(butPtr->display, pixmap, &state); |
494 |
if (butPtr->type == TYPE_BUTTON || !butPtr->indicatorOn) { |
495 |
rect.top = butPtr->borderWidth + 1 + defaultWidth; |
496 |
rect.left = rect.top; |
497 |
rect.right = Tk_Width(tkwin) - rect.left; |
498 |
rect.bottom = Tk_Height(tkwin) - rect.top; |
499 |
} else { |
500 |
rect.top = y-2; |
501 |
rect.left = x-2; |
502 |
rect.right = x+butPtr->textWidth + 1; |
503 |
rect.bottom = y+butPtr->textHeight + 1; |
504 |
} |
505 |
SetTextColor(dc, gc->foreground); |
506 |
SetBkColor(dc, gc->background); |
507 |
DrawFocusRect(dc, &rect); |
508 |
TkWinReleaseDrawableDC(pixmap, dc, &state); |
509 |
} |
510 |
y += butPtr->textHeight/2; |
511 |
} |
512 |
|
513 |
/* |
514 |
* Draw the indicator for check buttons and radio buttons. At this |
515 |
* point x and y refer to the top-left corner of the text or image |
516 |
* or bitmap. |
517 |
*/ |
518 |
|
519 |
if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn |
520 |
&& tsdPtr->boxesPtr) { |
521 |
int xSrc, ySrc; |
522 |
|
523 |
x -= butPtr->indicatorSpace; |
524 |
y -= butPtr->indicatorDiameter / 2; |
525 |
|
526 |
xSrc = (butPtr->flags & SELECTED) ? tsdPtr->boxWidth : 0; |
527 |
if (butPtr->state == STATE_ACTIVE) { |
528 |
xSrc += tsdPtr->boxWidth*2; |
529 |
} |
530 |
ySrc = (butPtr->type == TYPE_RADIO_BUTTON) ? 0 : tsdPtr->boxHeight; |
531 |
|
532 |
/* |
533 |
* Update the palette in the boxes bitmap to reflect the current |
534 |
* button colors. Note that this code relies on the layout of the |
535 |
* bitmap's palette. Also, all of the colors used to draw the |
536 |
* bitmap must be in the palette that is selected into the DC of |
537 |
* the offscreen pixmap. This requires that the static colors |
538 |
* be placed into the palette. |
539 |
*/ |
540 |
|
541 |
boxesPalette[PAL_CHECK] = FlipColor(gc->foreground); |
542 |
boxesPalette[PAL_TOP_OUTER] = FlipColor(TkWinGetBorderPixels(tkwin, |
543 |
border, TK_3D_DARK_GC)); |
544 |
boxesPalette[PAL_TOP_INNER] = FlipColor(TkWinGetBorderPixels(tkwin, |
545 |
border, TK_3D_DARK2)); |
546 |
boxesPalette[PAL_BOTTOM_INNER] = FlipColor(TkWinGetBorderPixels(tkwin, |
547 |
border, TK_3D_LIGHT2)); |
548 |
boxesPalette[PAL_BOTTOM_OUTER] = FlipColor(TkWinGetBorderPixels(tkwin, |
549 |
border, TK_3D_LIGHT_GC)); |
550 |
if (butPtr->state == STATE_DISABLED) { |
551 |
boxesPalette[PAL_INTERIOR] = FlipColor(TkWinGetBorderPixels(tkwin, |
552 |
border, TK_3D_LIGHT2)); |
553 |
} else if (butPtr->selectBorder != NULL) { |
554 |
boxesPalette[PAL_INTERIOR] = FlipColor(TkWinGetBorderPixels(tkwin, |
555 |
butPtr->selectBorder, TK_3D_FLAT_GC)); |
556 |
} else { |
557 |
boxesPalette[PAL_INTERIOR] = FlipColor(GetSysColor(COLOR_WINDOW)); |
558 |
} |
559 |
boxesPalette[PAL_BACKGROUND] = FlipColor(TkWinGetBorderPixels(tkwin, |
560 |
border, TK_3D_FLAT_GC)); |
561 |
|
562 |
dc = TkWinGetDrawableDC(butPtr->display, pixmap, &state); |
563 |
StretchDIBits(dc, x, y, tsdPtr->boxWidth, tsdPtr->boxHeight, |
564 |
xSrc, ySrc, tsdPtr->boxWidth, tsdPtr->boxHeight, |
565 |
tsdPtr->boxesBits, (LPBITMAPINFO) tsdPtr->boxesPtr, |
566 |
DIB_RGB_COLORS, SRCCOPY); |
567 |
TkWinReleaseDrawableDC(pixmap, dc, &state); |
568 |
} |
569 |
|
570 |
/* |
571 |
* If the button is disabled with a stipple rather than a special |
572 |
* foreground color, generate the stippled effect. If the widget |
573 |
* is selected and we use a different background color when selected, |
574 |
* must temporarily modify the GC. |
575 |
*/ |
576 |
|
577 |
if ((butPtr->state == STATE_DISABLED) |
578 |
&& ((butPtr->disabledFg == NULL) || (butPtr->image != NULL))) { |
579 |
if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn |
580 |
&& (butPtr->selectBorder != NULL)) { |
581 |
XSetForeground(butPtr->display, butPtr->disabledGC, |
582 |
Tk_3DBorderColor(butPtr->selectBorder)->pixel); |
583 |
} |
584 |
XFillRectangle(butPtr->display, pixmap, butPtr->disabledGC, |
585 |
butPtr->inset, butPtr->inset, |
586 |
(unsigned) (Tk_Width(tkwin) - 2*butPtr->inset), |
587 |
(unsigned) (Tk_Height(tkwin) - 2*butPtr->inset)); |
588 |
if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn |
589 |
&& (butPtr->selectBorder != NULL)) { |
590 |
XSetForeground(butPtr->display, butPtr->disabledGC, |
591 |
Tk_3DBorderColor(butPtr->normalBorder)->pixel); |
592 |
} |
593 |
} |
594 |
|
595 |
/* |
596 |
* Draw the border and traversal highlight last. This way, if the |
597 |
* button's contents overflow they'll be covered up by the border. |
598 |
*/ |
599 |
|
600 |
if (relief != TK_RELIEF_FLAT) { |
601 |
Tk_Draw3DRectangle(tkwin, pixmap, border, |
602 |
defaultWidth, defaultWidth, |
603 |
Tk_Width(tkwin) - 2*defaultWidth, |
604 |
Tk_Height(tkwin) - 2*defaultWidth, |
605 |
butPtr->borderWidth, relief); |
606 |
} |
607 |
if (defaultWidth != 0) { |
608 |
dc = TkWinGetDrawableDC(butPtr->display, pixmap, &state); |
609 |
TkWinFillRect(dc, 0, 0, Tk_Width(tkwin), defaultWidth, |
610 |
butPtr->highlightColorPtr->pixel); |
611 |
TkWinFillRect(dc, 0, 0, defaultWidth, Tk_Height(tkwin), |
612 |
butPtr->highlightColorPtr->pixel); |
613 |
TkWinFillRect(dc, 0, Tk_Height(tkwin) - defaultWidth, |
614 |
Tk_Width(tkwin), defaultWidth, |
615 |
butPtr->highlightColorPtr->pixel); |
616 |
TkWinFillRect(dc, Tk_Width(tkwin) - defaultWidth, 0, |
617 |
defaultWidth, Tk_Height(tkwin), |
618 |
butPtr->highlightColorPtr->pixel); |
619 |
TkWinReleaseDrawableDC(pixmap, dc, &state); |
620 |
} |
621 |
|
622 |
/* |
623 |
* Copy the information from the off-screen pixmap onto the screen, |
624 |
* then delete the pixmap. |
625 |
*/ |
626 |
|
627 |
XCopyArea(butPtr->display, pixmap, Tk_WindowId(tkwin), |
628 |
butPtr->copyGC, 0, 0, (unsigned) Tk_Width(tkwin), |
629 |
(unsigned) Tk_Height(tkwin), 0, 0); |
630 |
Tk_FreePixmap(butPtr->display, pixmap); |
631 |
} |
632 |
|
633 |
/* |
634 |
*---------------------------------------------------------------------- |
635 |
* |
636 |
* TkpComputeButtonGeometry -- |
637 |
* |
638 |
* After changes in a button's text or bitmap, this procedure |
639 |
* recomputes the button's geometry and passes this information |
640 |
* along to the geometry manager for the window. |
641 |
* |
642 |
* Results: |
643 |
* None. |
644 |
* |
645 |
* Side effects: |
646 |
* The button's window may change size. |
647 |
* |
648 |
*---------------------------------------------------------------------- |
649 |
*/ |
650 |
|
651 |
void |
652 |
TkpComputeButtonGeometry(butPtr) |
653 |
register TkButton *butPtr; /* Button whose geometry may have changed. */ |
654 |
{ |
655 |
int width, height, avgWidth; |
656 |
Tk_FontMetrics fm; |
657 |
ThreadSpecificData *tsdPtr = (ThreadSpecificData *) |
658 |
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); |
659 |
|
660 |
if (butPtr->highlightWidth < 0) { |
661 |
butPtr->highlightWidth = 0; |
662 |
} |
663 |
butPtr->inset = butPtr->highlightWidth + butPtr->borderWidth; |
664 |
butPtr->indicatorSpace = 0; |
665 |
|
666 |
if (!tsdPtr->boxesPtr) { |
667 |
InitBoxes(); |
668 |
} |
669 |
|
670 |
if (butPtr->image != NULL) { |
671 |
Tk_SizeOfImage(butPtr->image, &width, &height); |
672 |
imageOrBitmap: |
673 |
if (butPtr->width > 0) { |
674 |
width = butPtr->width; |
675 |
} |
676 |
if (butPtr->height > 0) { |
677 |
height = butPtr->height; |
678 |
} |
679 |
if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) { |
680 |
butPtr->indicatorSpace = tsdPtr->boxWidth * 2; |
681 |
butPtr->indicatorDiameter = tsdPtr->boxHeight; |
682 |
} |
683 |
} else if (butPtr->bitmap != None) { |
684 |
Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height); |
685 |
goto imageOrBitmap; |
686 |
} else { |
687 |
Tk_FreeTextLayout(butPtr->textLayout); |
688 |
butPtr->textLayout = Tk_ComputeTextLayout(butPtr->tkfont, |
689 |
Tcl_GetString(butPtr->textPtr), -1, butPtr->wrapLength, |
690 |
butPtr->justify, 0, &butPtr->textWidth, &butPtr->textHeight); |
691 |
|
692 |
width = butPtr->textWidth; |
693 |
height = butPtr->textHeight; |
694 |
avgWidth = Tk_TextWidth(butPtr->tkfont, "0", 1); |
695 |
Tk_GetFontMetrics(butPtr->tkfont, &fm); |
696 |
|
697 |
if (butPtr->width > 0) { |
698 |
width = butPtr->width * avgWidth; |
699 |
} |
700 |
if (butPtr->height > 0) { |
701 |
height = butPtr->height * fm.linespace; |
702 |
} |
703 |
|
704 |
if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) { |
705 |
butPtr->indicatorDiameter = tsdPtr->boxHeight; |
706 |
butPtr->indicatorSpace = butPtr->indicatorDiameter + avgWidth; |
707 |
} |
708 |
|
709 |
/* |
710 |
* Increase the inset to allow for the focus ring. |
711 |
*/ |
712 |
|
713 |
if (butPtr->type != TYPE_LABEL) { |
714 |
butPtr->inset += 3; |
715 |
} |
716 |
} |
717 |
|
718 |
/* |
719 |
* When issuing the geometry request, add extra space for the indicator, |
720 |
* if any, and for the border and padding, plus an extra pixel so the |
721 |
* display can be offset by 1 pixel in either direction for the raised |
722 |
* or lowered effect. |
723 |
*/ |
724 |
|
725 |
if ((butPtr->image == NULL) && (butPtr->bitmap == None)) { |
726 |
width += 2*butPtr->padX; |
727 |
height += 2*butPtr->padY; |
728 |
} |
729 |
if ((butPtr->type == TYPE_BUTTON) |
730 |
|| ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn)) { |
731 |
width += 1; |
732 |
height += 1; |
733 |
} |
734 |
Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->indicatorSpace |
735 |
+ 2*butPtr->inset), (int) (height + 2*butPtr->inset)); |
736 |
Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset); |
737 |
} |
738 |
|
739 |
/* |
740 |
*---------------------------------------------------------------------- |
741 |
* |
742 |
* ButtonProc -- |
743 |
* |
744 |
* This function is call by Windows whenever an event occurs on |
745 |
* a button control created by Tk. |
746 |
* |
747 |
* Results: |
748 |
* Standard Windows return value. |
749 |
* |
750 |
* Side effects: |
751 |
* May generate events. |
752 |
* |
753 |
*---------------------------------------------------------------------- |
754 |
*/ |
755 |
|
756 |
static LRESULT CALLBACK |
757 |
ButtonProc(hwnd, message, wParam, lParam) |
758 |
HWND hwnd; |
759 |
UINT message; |
760 |
WPARAM wParam; |
761 |
LPARAM lParam; |
762 |
{ |
763 |
LRESULT result; |
764 |
WinButton *butPtr; |
765 |
Tk_Window tkwin = Tk_HWNDToWindow(hwnd); |
766 |
|
767 |
if (tkwin == NULL) { |
768 |
panic("ButtonProc called on an invalid HWND"); |
769 |
} |
770 |
butPtr = (WinButton *)((TkWindow*)tkwin)->instanceData; |
771 |
|
772 |
switch(message) { |
773 |
case WM_ERASEBKGND: |
774 |
return 0; |
775 |
|
776 |
case BM_GETCHECK: |
777 |
if (((butPtr->info.type == TYPE_CHECK_BUTTON) |
778 |
|| (butPtr->info.type == TYPE_RADIO_BUTTON)) |
779 |
&& butPtr->info.indicatorOn) { |
780 |
return (butPtr->info.flags & SELECTED) |
781 |
? BST_CHECKED : BST_UNCHECKED; |
782 |
} |
783 |
return 0; |
784 |
|
785 |
case BM_GETSTATE: { |
786 |
DWORD state = 0; |
787 |
if (((butPtr->info.type == TYPE_CHECK_BUTTON) |
788 |
|| (butPtr->info.type == TYPE_RADIO_BUTTON)) |
789 |
&& butPtr->info.indicatorOn) { |
790 |
state = (butPtr->info.flags & SELECTED) |
791 |
? BST_CHECKED : BST_UNCHECKED; |
792 |
} |
793 |
if (butPtr->info.flags & GOT_FOCUS) { |
794 |
state |= BST_FOCUS; |
795 |
} |
796 |
return state; |
797 |
} |
798 |
case WM_ENABLE: |
799 |
break; |
800 |
|
801 |
case WM_PAINT: { |
802 |
PAINTSTRUCT ps; |
803 |
BeginPaint(hwnd, &ps); |
804 |
EndPaint(hwnd, &ps); |
805 |
TkpDisplayButton((ClientData)butPtr); |
806 |
|
807 |
/* |
808 |
* Special note: must cancel any existing idle handler |
809 |
* for TkpDisplayButton; it's no longer needed, and |
810 |
* TkpDisplayButton cleared the REDRAW_PENDING flag. |
811 |
*/ |
812 |
|
813 |
Tcl_CancelIdleCall(TkpDisplayButton, (ClientData)butPtr); |
814 |
return 0; |
815 |
} |
816 |
case BN_CLICKED: { |
817 |
int code; |
818 |
Tcl_Interp *interp = butPtr->info.interp; |
819 |
if (butPtr->info.state != STATE_DISABLED) { |
820 |
Tcl_Preserve((ClientData)interp); |
821 |
code = TkInvokeButton((TkButton*)butPtr); |
822 |
if (code != TCL_OK && code != TCL_CONTINUE |
823 |
&& code != TCL_BREAK) { |
824 |
Tcl_AddErrorInfo(interp, "\n (button invoke)"); |
825 |
Tcl_BackgroundError(interp); |
826 |
} |
827 |
Tcl_Release((ClientData)interp); |
828 |
} |
829 |
Tcl_ServiceAll(); |
830 |
return 0; |
831 |
} |
832 |
|
833 |
default: |
834 |
if (Tk_TranslateWinEvent(hwnd, message, wParam, lParam, &result)) { |
835 |
return result; |
836 |
} |
837 |
} |
838 |
return DefWindowProc(hwnd, message, wParam, lParam); |
839 |
} |
840 |
|
841 |
/* End of tkwinbutton.c */ |