~ubuntu-branches/ubuntu/dapper/tk8.0/dapper-updates

« back to all changes in this revision

Viewing changes to generic/tkButton.c

  • Committer: Bazaar Package Importer
  • Author(s): Mike Markley
  • Date: 2001-07-24 21:57:40 UTC
  • Revision ID: james.westby@ubuntu.com-20010724215740-r70t25rtmbqjil2h
Tags: upstream-8.0.5
ImportĀ upstreamĀ versionĀ 8.0.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* 
 
2
 * tkButton.c --
 
3
 *
 
4
 *      This module implements a collection of button-like
 
5
 *      widgets for the Tk toolkit.  The widgets implemented
 
6
 *      include labels, buttons, check buttons, and radio
 
7
 *      buttons.
 
8
 *
 
9
 * Copyright (c) 1990-1994 The Regents of the University of California.
 
10
 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
 
11
 *
 
12
 * See the file "license.terms" for information on usage and redistribution
 
13
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 
14
 *
 
15
 * RCS: @(#) $Id: tkButton.c,v 1.2 1998/09/14 18:23:04 stanton Exp $
 
16
 */
 
17
 
 
18
#include "tkButton.h"
 
19
#include "default.h"
 
20
 
 
21
/*
 
22
 * Class names for buttons, indexed by one of the type values above.
 
23
 */
 
24
 
 
25
static char *classNames[] = {"Label", "Button", "Checkbutton", "Radiobutton"};
 
26
 
 
27
/*
 
28
 * The class procedure table for the button widget.
 
29
 */
 
30
 
 
31
static int configFlags[] = {LABEL_MASK, BUTTON_MASK,
 
32
        CHECK_BUTTON_MASK, RADIO_BUTTON_MASK};
 
33
 
 
34
/*
 
35
 * Information used for parsing configuration specs:
 
36
 */
 
37
 
 
38
Tk_ConfigSpec tkpButtonConfigSpecs[] = {
 
39
    {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
 
40
        DEF_BUTTON_ACTIVE_BG_COLOR, Tk_Offset(TkButton, activeBorder),
 
41
        BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
 
42
        |TK_CONFIG_COLOR_ONLY},
 
43
    {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
 
44
        DEF_BUTTON_ACTIVE_BG_MONO, Tk_Offset(TkButton, activeBorder),
 
45
        BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
 
46
        |TK_CONFIG_MONO_ONLY},
 
47
    {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
 
48
        DEF_BUTTON_ACTIVE_FG_COLOR, Tk_Offset(TkButton, activeFg), 
 
49
        BUTTON_MASK|TK_CONFIG_COLOR_ONLY},
 
50
    {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
 
51
        DEF_CHKRAD_ACTIVE_FG_COLOR, Tk_Offset(TkButton, activeFg), 
 
52
        CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY},
 
53
    {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
 
54
        DEF_BUTTON_ACTIVE_FG_MONO, Tk_Offset(TkButton, activeFg), 
 
55
        BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
 
56
        |TK_CONFIG_MONO_ONLY},
 
57
    {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
 
58
        DEF_BUTTON_ANCHOR, Tk_Offset(TkButton, anchor), ALL_MASK},
 
59
    {TK_CONFIG_BORDER, "-background", "background", "Background",
 
60
        DEF_BUTTON_BG_COLOR, Tk_Offset(TkButton, normalBorder),
 
61
        ALL_MASK | TK_CONFIG_COLOR_ONLY},
 
62
    {TK_CONFIG_BORDER, "-background", "background", "Background",
 
63
        DEF_BUTTON_BG_MONO, Tk_Offset(TkButton, normalBorder),
 
64
        ALL_MASK | TK_CONFIG_MONO_ONLY},
 
65
    {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
 
66
        (char *) NULL, 0, ALL_MASK},
 
67
    {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
 
68
        (char *) NULL, 0, ALL_MASK},
 
69
    {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap",
 
70
        DEF_BUTTON_BITMAP, Tk_Offset(TkButton, bitmap),
 
71
        ALL_MASK|TK_CONFIG_NULL_OK},
 
72
    {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
 
73
        DEF_BUTTON_BORDER_WIDTH, Tk_Offset(TkButton, borderWidth), ALL_MASK},
 
74
    {TK_CONFIG_STRING, "-command", "command", "Command",
 
75
        DEF_BUTTON_COMMAND, Tk_Offset(TkButton, command),
 
76
        BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_NULL_OK},
 
77
    {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
 
78
        DEF_BUTTON_CURSOR, Tk_Offset(TkButton, cursor),
 
79
        ALL_MASK|TK_CONFIG_NULL_OK},
 
80
    {TK_CONFIG_UID, "-default", "default", "Default",
 
81
        DEF_BUTTON_DEFAULT, Tk_Offset(TkButton, defaultState), BUTTON_MASK},
 
82
    {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
 
83
        "DisabledForeground", DEF_BUTTON_DISABLED_FG_COLOR,
 
84
        Tk_Offset(TkButton, disabledFg), BUTTON_MASK|CHECK_BUTTON_MASK
 
85
        |RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK},
 
86
    {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
 
87
        "DisabledForeground", DEF_BUTTON_DISABLED_FG_MONO,
 
88
        Tk_Offset(TkButton, disabledFg), BUTTON_MASK|CHECK_BUTTON_MASK
 
89
        |RADIO_BUTTON_MASK|TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK},
 
90
    {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
 
91
        (char *) NULL, 0, ALL_MASK},
 
92
    {TK_CONFIG_FONT, "-font", "font", "Font",
 
93
        DEF_BUTTON_FONT, Tk_Offset(TkButton, tkfont),
 
94
        ALL_MASK},
 
95
    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
 
96
        DEF_BUTTON_FG, Tk_Offset(TkButton, normalFg), LABEL_MASK|BUTTON_MASK},
 
97
    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
 
98
        DEF_CHKRAD_FG, Tk_Offset(TkButton, normalFg), CHECK_BUTTON_MASK
 
99
        |RADIO_BUTTON_MASK},
 
100
    {TK_CONFIG_STRING, "-height", "height", "Height",
 
101
        DEF_BUTTON_HEIGHT, Tk_Offset(TkButton, heightString), ALL_MASK},
 
102
    {TK_CONFIG_BORDER, "-highlightbackground", "highlightBackground",
 
103
        "HighlightBackground", DEF_BUTTON_HIGHLIGHT_BG,
 
104
        Tk_Offset(TkButton, highlightBorder), ALL_MASK},
 
105
    {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
 
106
        DEF_BUTTON_HIGHLIGHT, Tk_Offset(TkButton, highlightColorPtr),
 
107
        ALL_MASK},
 
108
    {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
 
109
        "HighlightThickness",
 
110
        DEF_LABEL_HIGHLIGHT_WIDTH, Tk_Offset(TkButton, highlightWidth),
 
111
        LABEL_MASK},
 
112
    {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
 
113
        "HighlightThickness",
 
114
        DEF_BUTTON_HIGHLIGHT_WIDTH, Tk_Offset(TkButton, highlightWidth),
 
115
        BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
 
116
    {TK_CONFIG_STRING, "-image", "image", "Image",
 
117
        DEF_BUTTON_IMAGE, Tk_Offset(TkButton, imageString),
 
118
        ALL_MASK|TK_CONFIG_NULL_OK},
 
119
    {TK_CONFIG_BOOLEAN, "-indicatoron", "indicatorOn", "IndicatorOn",
 
120
        DEF_BUTTON_INDICATOR, Tk_Offset(TkButton, indicatorOn),
 
121
        CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
 
122
    {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
 
123
        DEF_BUTTON_JUSTIFY, Tk_Offset(TkButton, justify), ALL_MASK},
 
124
    {TK_CONFIG_STRING, "-offvalue", "offValue", "Value",
 
125
        DEF_BUTTON_OFF_VALUE, Tk_Offset(TkButton, offValue),
 
126
        CHECK_BUTTON_MASK},
 
127
    {TK_CONFIG_STRING, "-onvalue", "onValue", "Value",
 
128
        DEF_BUTTON_ON_VALUE, Tk_Offset(TkButton, onValue),
 
129
        CHECK_BUTTON_MASK},
 
130
    {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
 
131
        DEF_BUTTON_PADX, Tk_Offset(TkButton, padX), BUTTON_MASK},
 
132
    {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
 
133
        DEF_LABCHKRAD_PADX, Tk_Offset(TkButton, padX),
 
134
        LABEL_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
 
135
    {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
 
136
        DEF_BUTTON_PADY, Tk_Offset(TkButton, padY), BUTTON_MASK},
 
137
    {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
 
138
        DEF_LABCHKRAD_PADY, Tk_Offset(TkButton, padY),
 
139
        LABEL_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
 
140
    {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
 
141
        DEF_BUTTON_RELIEF, Tk_Offset(TkButton, relief), BUTTON_MASK},
 
142
    {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
 
143
        DEF_LABCHKRAD_RELIEF, Tk_Offset(TkButton, relief),
 
144
        LABEL_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
 
145
    {TK_CONFIG_BORDER, "-selectcolor", "selectColor", "Background",
 
146
        DEF_BUTTON_SELECT_COLOR, Tk_Offset(TkButton, selectBorder),
 
147
        CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY
 
148
        |TK_CONFIG_NULL_OK},
 
149
    {TK_CONFIG_BORDER, "-selectcolor", "selectColor", "Background",
 
150
        DEF_BUTTON_SELECT_MONO, Tk_Offset(TkButton, selectBorder),
 
151
        CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_MONO_ONLY
 
152
        |TK_CONFIG_NULL_OK},
 
153
    {TK_CONFIG_STRING, "-selectimage", "selectImage", "SelectImage",
 
154
        DEF_BUTTON_SELECT_IMAGE, Tk_Offset(TkButton, selectImageString),
 
155
        CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_NULL_OK},
 
156
    {TK_CONFIG_UID, "-state", "state", "State",
 
157
        DEF_BUTTON_STATE, Tk_Offset(TkButton, state),
 
158
        BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
 
159
    {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
 
160
        DEF_LABEL_TAKE_FOCUS, Tk_Offset(TkButton, takeFocus),
 
161
        LABEL_MASK|TK_CONFIG_NULL_OK},
 
162
    {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
 
163
        DEF_BUTTON_TAKE_FOCUS, Tk_Offset(TkButton, takeFocus),
 
164
        BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_NULL_OK},
 
165
    {TK_CONFIG_STRING, "-text", "text", "Text",
 
166
        DEF_BUTTON_TEXT, Tk_Offset(TkButton, text), ALL_MASK},
 
167
    {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
 
168
        DEF_BUTTON_TEXT_VARIABLE, Tk_Offset(TkButton, textVarName),
 
169
        ALL_MASK|TK_CONFIG_NULL_OK},
 
170
    {TK_CONFIG_INT, "-underline", "underline", "Underline",
 
171
        DEF_BUTTON_UNDERLINE, Tk_Offset(TkButton, underline), ALL_MASK},
 
172
    {TK_CONFIG_STRING, "-value", "value", "Value",
 
173
        DEF_BUTTON_VALUE, Tk_Offset(TkButton, onValue),
 
174
        RADIO_BUTTON_MASK},
 
175
    {TK_CONFIG_STRING, "-variable", "variable", "Variable",
 
176
        DEF_RADIOBUTTON_VARIABLE, Tk_Offset(TkButton, selVarName),
 
177
        RADIO_BUTTON_MASK},
 
178
    {TK_CONFIG_STRING, "-variable", "variable", "Variable",
 
179
        DEF_CHECKBUTTON_VARIABLE, Tk_Offset(TkButton, selVarName),
 
180
        CHECK_BUTTON_MASK|TK_CONFIG_NULL_OK},
 
181
    {TK_CONFIG_STRING, "-width", "width", "Width",
 
182
        DEF_BUTTON_WIDTH, Tk_Offset(TkButton, widthString), ALL_MASK},
 
183
    {TK_CONFIG_PIXELS, "-wraplength", "wrapLength", "WrapLength",
 
184
        DEF_BUTTON_WRAP_LENGTH, Tk_Offset(TkButton, wrapLength), ALL_MASK},
 
185
    {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
 
186
        (char *) NULL, 0, 0}
 
187
};
 
188
 
 
189
/*
 
190
 * String to print out in error messages, identifying options for
 
191
 * widget commands for different types of labels or buttons:
 
192
 */
 
193
 
 
194
static char *optionStrings[] = {
 
195
    "cget or configure",
 
196
    "cget, configure, flash, or invoke",
 
197
    "cget, configure, deselect, flash, invoke, select, or toggle",
 
198
    "cget, configure, deselect, flash, invoke, or select"
 
199
};
 
200
 
 
201
/*
 
202
 * Forward declarations for procedures defined later in this file:
 
203
 */
 
204
 
 
205
static void             ButtonCmdDeletedProc _ANSI_ARGS_((
 
206
                            ClientData clientData));
 
207
static int              ButtonCreate _ANSI_ARGS_((ClientData clientData,
 
208
                            Tcl_Interp *interp, int argc, char **argv,
 
209
                            int type));
 
210
static void             ButtonEventProc _ANSI_ARGS_((ClientData clientData,
 
211
                            XEvent *eventPtr));
 
212
static void             ButtonImageProc _ANSI_ARGS_((ClientData clientData,
 
213
                            int x, int y, int width, int height,
 
214
                            int imgWidth, int imgHeight));
 
215
static void             ButtonSelectImageProc _ANSI_ARGS_((
 
216
                            ClientData clientData, int x, int y, int width,
 
217
                            int height, int imgWidth, int imgHeight));
 
218
static char *           ButtonTextVarProc _ANSI_ARGS_((ClientData clientData,
 
219
                            Tcl_Interp *interp, char *name1, char *name2,
 
220
                            int flags));
 
221
static char *           ButtonVarProc _ANSI_ARGS_((ClientData clientData,
 
222
                            Tcl_Interp *interp, char *name1, char *name2,
 
223
                            int flags));
 
224
static int              ButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
 
225
                            Tcl_Interp *interp, int argc, char **argv));
 
226
static int              ConfigureButton _ANSI_ARGS_((Tcl_Interp *interp,
 
227
                            TkButton *butPtr, int argc, char **argv,
 
228
                            int flags));
 
229
static void             DestroyButton _ANSI_ARGS_((TkButton *butPtr));
 
230
 
 
231
 
 
232
/*
 
233
 *--------------------------------------------------------------
 
234
 *
 
235
 * Tk_ButtonCmd, Tk_CheckbuttonCmd, Tk_LabelCmd, Tk_RadiobuttonCmd --
 
236
 *
 
237
 *      These procedures are invoked to process the "button", "label",
 
238
 *      "radiobutton", and "checkbutton" Tcl commands.  See the
 
239
 *      user documentation for details on what they do.
 
240
 *
 
241
 * Results:
 
242
 *      A standard Tcl result.
 
243
 *
 
244
 * Side effects:
 
245
 *      See the user documentation.  These procedures are just wrappers;
 
246
 *      they call ButtonCreate to do all of the real work.
 
247
 *
 
248
 *--------------------------------------------------------------
 
249
 */
 
250
 
 
251
int
 
252
Tk_ButtonCmd(clientData, interp, argc, argv)
 
253
    ClientData clientData;      /* Main window associated with
 
254
                                 * interpreter. */
 
255
    Tcl_Interp *interp;         /* Current interpreter. */
 
256
    int argc;                   /* Number of arguments. */
 
257
    char **argv;                /* Argument strings. */
 
258
{
 
259
    return ButtonCreate(clientData, interp, argc, argv, TYPE_BUTTON);
 
260
}
 
261
 
 
262
int
 
263
Tk_CheckbuttonCmd(clientData, interp, argc, argv)
 
264
    ClientData clientData;      /* Main window associated with
 
265
                                 * interpreter. */
 
266
    Tcl_Interp *interp;         /* Current interpreter. */
 
267
    int argc;                   /* Number of arguments. */
 
268
    char **argv;                /* Argument strings. */
 
269
{
 
270
    return ButtonCreate(clientData, interp, argc, argv, TYPE_CHECK_BUTTON);
 
271
}
 
272
 
 
273
int
 
274
Tk_LabelCmd(clientData, interp, argc, argv)
 
275
    ClientData clientData;      /* Main window associated with
 
276
                                 * interpreter. */
 
277
    Tcl_Interp *interp;         /* Current interpreter. */
 
278
    int argc;                   /* Number of arguments. */
 
279
    char **argv;                /* Argument strings. */
 
280
{
 
281
    return ButtonCreate(clientData, interp, argc, argv, TYPE_LABEL);
 
282
}
 
283
 
 
284
int
 
285
Tk_RadiobuttonCmd(clientData, interp, argc, argv)
 
286
    ClientData clientData;      /* Main window associated with
 
287
                                 * interpreter. */
 
288
    Tcl_Interp *interp;         /* Current interpreter. */
 
289
    int argc;                   /* Number of arguments. */
 
290
    char **argv;                /* Argument strings. */
 
291
{
 
292
    return ButtonCreate(clientData, interp, argc, argv, TYPE_RADIO_BUTTON);
 
293
}
 
294
 
 
295
/*
 
296
 *--------------------------------------------------------------
 
297
 *
 
298
 * ButtonCreate --
 
299
 *
 
300
 *      This procedure does all the real work of implementing the
 
301
 *      "button", "label", "radiobutton", and "checkbutton" Tcl
 
302
 *      commands.  See the user documentation for details on what it does.
 
303
 *
 
304
 * Results:
 
305
 *      A standard Tcl result.
 
306
 *
 
307
 * Side effects:
 
308
 *      See the user documentation.
 
309
 *
 
310
 *--------------------------------------------------------------
 
311
 */
 
312
 
 
313
static int
 
314
ButtonCreate(clientData, interp, argc, argv, type)
 
315
    ClientData clientData;      /* Main window associated with
 
316
                                 * interpreter. */
 
317
    Tcl_Interp *interp;         /* Current interpreter. */
 
318
    int argc;                   /* Number of arguments. */
 
319
    char **argv;                /* Argument strings. */
 
320
    int type;                   /* Type of button to create: TYPE_LABEL,
 
321
                                 * TYPE_BUTTON, TYPE_CHECK_BUTTON, or
 
322
                                 * TYPE_RADIO_BUTTON. */
 
323
{
 
324
    register TkButton *butPtr;
 
325
    Tk_Window tkwin = (Tk_Window) clientData;
 
326
    Tk_Window new;
 
327
 
 
328
    if (argc < 2) {
 
329
        Tcl_AppendResult(interp, "wrong # args: should be \"",
 
330
                argv[0], " pathName ?options?\"", (char *) NULL);
 
331
        return TCL_ERROR;
 
332
    }
 
333
 
 
334
    /*
 
335
     * Create the new window.
 
336
     */
 
337
 
 
338
    new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
 
339
    if (new == NULL) {
 
340
        return TCL_ERROR;
 
341
    }
 
342
 
 
343
    Tk_SetClass(new, classNames[type]);
 
344
    butPtr = TkpCreateButton(new);
 
345
 
 
346
    TkSetClassProcs(new, &tkpButtonProcs, (ClientData) butPtr);
 
347
 
 
348
    /*
 
349
     * Initialize the data structure for the button.
 
350
     */
 
351
 
 
352
    butPtr->tkwin = new;
 
353
    butPtr->display = Tk_Display(new);
 
354
    butPtr->widgetCmd = Tcl_CreateCommand(interp, Tk_PathName(butPtr->tkwin),
 
355
            ButtonWidgetCmd, (ClientData) butPtr, ButtonCmdDeletedProc);
 
356
    butPtr->interp = interp;
 
357
    butPtr->type = type;
 
358
    butPtr->text = NULL;
 
359
    butPtr->underline = -1;
 
360
    butPtr->textVarName = NULL;
 
361
    butPtr->bitmap = None;
 
362
    butPtr->imageString = NULL;
 
363
    butPtr->image = NULL;
 
364
    butPtr->selectImageString = NULL;
 
365
    butPtr->selectImage = NULL;
 
366
    butPtr->state = tkNormalUid;
 
367
    butPtr->normalBorder = NULL;
 
368
    butPtr->activeBorder = NULL;
 
369
    butPtr->borderWidth = 0;
 
370
    butPtr->relief = TK_RELIEF_FLAT;
 
371
    butPtr->highlightWidth = 0;
 
372
    butPtr->highlightBorder = NULL;
 
373
    butPtr->highlightColorPtr = NULL;
 
374
    butPtr->inset = 0;
 
375
    butPtr->tkfont = NULL;
 
376
    butPtr->normalFg = NULL;
 
377
    butPtr->activeFg = NULL;
 
378
    butPtr->disabledFg = NULL;
 
379
    butPtr->normalTextGC = None;
 
380
    butPtr->activeTextGC = None;
 
381
    butPtr->gray = None;
 
382
    butPtr->disabledGC = None;
 
383
    butPtr->copyGC = None;
 
384
    butPtr->widthString = NULL;
 
385
    butPtr->heightString = NULL;
 
386
    butPtr->width = 0;
 
387
    butPtr->height = 0;
 
388
    butPtr->wrapLength = 0;
 
389
    butPtr->padX = 0;
 
390
    butPtr->padY = 0;
 
391
    butPtr->anchor = TK_ANCHOR_CENTER;
 
392
    butPtr->justify = TK_JUSTIFY_CENTER;
 
393
    butPtr->textLayout = NULL;
 
394
    butPtr->indicatorOn = 0;
 
395
    butPtr->selectBorder = NULL;
 
396
    butPtr->indicatorSpace = 0;
 
397
    butPtr->indicatorDiameter = 0;
 
398
    butPtr->defaultState = tkDisabledUid;
 
399
    butPtr->selVarName = NULL;
 
400
    butPtr->onValue = NULL;
 
401
    butPtr->offValue = NULL;
 
402
    butPtr->cursor = None;
 
403
    butPtr->command = NULL;
 
404
    butPtr->takeFocus = NULL;
 
405
    butPtr->flags = 0;
 
406
 
 
407
    Tk_CreateEventHandler(butPtr->tkwin,
 
408
            ExposureMask|StructureNotifyMask|FocusChangeMask,
 
409
            ButtonEventProc, (ClientData) butPtr);
 
410
 
 
411
    if (ConfigureButton(interp, butPtr, argc - 2, argv + 2,
 
412
            configFlags[type]) != TCL_OK) {
 
413
        Tk_DestroyWindow(butPtr->tkwin);
 
414
        return TCL_ERROR;
 
415
    }
 
416
 
 
417
    interp->result = Tk_PathName(butPtr->tkwin);
 
418
    return TCL_OK;
 
419
}
 
420
 
 
421
/*
 
422
 *--------------------------------------------------------------
 
423
 *
 
424
 * ButtonWidgetCmd --
 
425
 *
 
426
 *      This procedure is invoked to process the Tcl command
 
427
 *      that corresponds to a widget managed by this module.
 
428
 *      See the user documentation for details on what it does.
 
429
 *
 
430
 * Results:
 
431
 *      A standard Tcl result.
 
432
 *
 
433
 * Side effects:
 
434
 *      See the user documentation.
 
435
 *
 
436
 *--------------------------------------------------------------
 
437
 */
 
438
 
 
439
static int
 
440
ButtonWidgetCmd(clientData, interp, argc, argv)
 
441
    ClientData clientData;      /* Information about button widget. */
 
442
    Tcl_Interp *interp;         /* Current interpreter. */
 
443
    int argc;                   /* Number of arguments. */
 
444
    char **argv;                /* Argument strings. */
 
445
{
 
446
    register TkButton *butPtr = (TkButton *) clientData;
 
447
    int result = TCL_OK;
 
448
    size_t length;
 
449
    int c;
 
450
 
 
451
    if (argc < 2) {
 
452
        sprintf(interp->result,
 
453
                "wrong # args: should be \"%.50s option ?arg arg ...?\"",
 
454
                argv[0]);
 
455
        return TCL_ERROR;
 
456
    }
 
457
    Tcl_Preserve((ClientData) butPtr);
 
458
    c = argv[1][0];
 
459
    length = strlen(argv[1]);
 
460
 
 
461
    if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
 
462
            && (length >= 2)) {
 
463
        if (argc != 3) {
 
464
            Tcl_AppendResult(interp, "wrong # args: should be \"",
 
465
                    argv[0], " cget option\"",
 
466
                    (char *) NULL);
 
467
            goto error;
 
468
        }
 
469
        result = Tk_ConfigureValue(interp, butPtr->tkwin, tkpButtonConfigSpecs,
 
470
                (char *) butPtr, argv[2], configFlags[butPtr->type]);
 
471
    } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
 
472
            && (length >= 2)) {
 
473
        if (argc == 2) {
 
474
            result = Tk_ConfigureInfo(interp, butPtr->tkwin,
 
475
                    tkpButtonConfigSpecs, (char *) butPtr, (char *) NULL,
 
476
                    configFlags[butPtr->type]);
 
477
        } else if (argc == 3) {
 
478
            result = Tk_ConfigureInfo(interp, butPtr->tkwin,
 
479
                    tkpButtonConfigSpecs, (char *) butPtr, argv[2],
 
480
                    configFlags[butPtr->type]);
 
481
        } else {
 
482
            result = ConfigureButton(interp, butPtr, argc-2, argv+2,
 
483
                    configFlags[butPtr->type] | TK_CONFIG_ARGV_ONLY);
 
484
        }
 
485
    } else if ((c == 'd') && (strncmp(argv[1], "deselect", length) == 0)
 
486
            && (butPtr->type >= TYPE_CHECK_BUTTON)) {
 
487
        if (argc > 2) {
 
488
            sprintf(interp->result,
 
489
                    "wrong # args: should be \"%.50s deselect\"",
 
490
                    argv[0]);
 
491
            goto error;
 
492
        }
 
493
        if (butPtr->type == TYPE_CHECK_BUTTON) {
 
494
            if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue,
 
495
                    TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
 
496
                result = TCL_ERROR;
 
497
            }
 
498
        } else if (butPtr->flags & SELECTED) {
 
499
            if (Tcl_SetVar(interp, butPtr->selVarName, "",
 
500
                    TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
 
501
                result = TCL_ERROR;
 
502
            };
 
503
        }
 
504
    } else if ((c == 'f') && (strncmp(argv[1], "flash", length) == 0)
 
505
            && (butPtr->type != TYPE_LABEL)) {
 
506
        int i;
 
507
 
 
508
        if (argc > 2) {
 
509
            sprintf(interp->result,
 
510
                    "wrong # args: should be \"%.50s flash\"",
 
511
                    argv[0]);
 
512
            goto error;
 
513
        }
 
514
        if (butPtr->state != tkDisabledUid) {
 
515
            for (i = 0; i < 4; i++) {
 
516
                butPtr->state = (butPtr->state == tkNormalUid)
 
517
                        ? tkActiveUid : tkNormalUid;
 
518
                Tk_SetBackgroundFromBorder(butPtr->tkwin,
 
519
                        (butPtr->state == tkActiveUid) ? butPtr->activeBorder
 
520
                        : butPtr->normalBorder);
 
521
                TkpDisplayButton((ClientData) butPtr);
 
522
 
 
523
                /*
 
524
                 * Special note: must cancel any existing idle handler
 
525
                 * for TkpDisplayButton;  it's no longer needed, and TkpDisplayButton
 
526
                 * cleared the REDRAW_PENDING flag.
 
527
                 */
 
528
 
 
529
                Tcl_CancelIdleCall(TkpDisplayButton, (ClientData) butPtr);
 
530
                XFlush(butPtr->display);
 
531
                Tcl_Sleep(50);
 
532
            }
 
533
        }
 
534
    } else if ((c == 'i') && (strncmp(argv[1], "invoke", length) == 0)
 
535
            && (butPtr->type > TYPE_LABEL)) {
 
536
        if (argc > 2) {
 
537
            sprintf(interp->result,
 
538
                    "wrong # args: should be \"%.50s invoke\"",
 
539
                    argv[0]);
 
540
            goto error;
 
541
        }
 
542
        if (butPtr->state != tkDisabledUid) {
 
543
            result = TkInvokeButton(butPtr);
 
544
        }
 
545
    } else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
 
546
            && (butPtr->type >= TYPE_CHECK_BUTTON)) {
 
547
        if (argc > 2) {
 
548
            sprintf(interp->result,
 
549
                    "wrong # args: should be \"%.50s select\"",
 
550
                    argv[0]);
 
551
            goto error;
 
552
        }
 
553
        if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue,
 
554
                TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
 
555
            result = TCL_ERROR;
 
556
        }
 
557
    } else if ((c == 't') && (strncmp(argv[1], "toggle", length) == 0)
 
558
            && (length >= 2) && (butPtr->type == TYPE_CHECK_BUTTON)) {
 
559
        if (argc > 2) {
 
560
            sprintf(interp->result,
 
561
                    "wrong # args: should be \"%.50s toggle\"",
 
562
                    argv[0]);
 
563
            goto error;
 
564
        }
 
565
        if (butPtr->flags & SELECTED) {
 
566
            if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue,
 
567
                    TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
 
568
                result = TCL_ERROR;
 
569
            }
 
570
        } else {
 
571
            if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue,
 
572
                    TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
 
573
                result = TCL_ERROR;
 
574
            }
 
575
        }
 
576
    } else {
 
577
        sprintf(interp->result,
 
578
                "bad option \"%.50s\": must be %s", argv[1],
 
579
                optionStrings[butPtr->type]);
 
580
        goto error;
 
581
    }
 
582
    Tcl_Release((ClientData) butPtr);
 
583
    return result;
 
584
 
 
585
    error:
 
586
    Tcl_Release((ClientData) butPtr);
 
587
    return TCL_ERROR;
 
588
}
 
589
 
 
590
/*
 
591
 *----------------------------------------------------------------------
 
592
 *
 
593
 * DestroyButton --
 
594
 *
 
595
 *      This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
 
596
 *      to clean up the internal structure of a button at a safe time
 
597
 *      (when no-one is using it anymore).
 
598
 *
 
599
 * Results:
 
600
 *      None.
 
601
 *
 
602
 * Side effects:
 
603
 *      Everything associated with the widget is freed up.
 
604
 *
 
605
 *----------------------------------------------------------------------
 
606
 */
 
607
 
 
608
static void
 
609
DestroyButton(butPtr)
 
610
    TkButton *butPtr;           /* Info about button widget. */
 
611
{
 
612
    /*
 
613
     * Free up all the stuff that requires special handling, then
 
614
     * let Tk_FreeOptions handle all the standard option-related
 
615
     * stuff.
 
616
     */
 
617
 
 
618
    if (butPtr->textVarName != NULL) {
 
619
        Tcl_UntraceVar(butPtr->interp, butPtr->textVarName,
 
620
                TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
 
621
                ButtonTextVarProc, (ClientData) butPtr);
 
622
    }
 
623
    if (butPtr->image != NULL) {
 
624
        Tk_FreeImage(butPtr->image);
 
625
    }
 
626
    if (butPtr->selectImage != NULL) {
 
627
        Tk_FreeImage(butPtr->selectImage);
 
628
    }
 
629
    if (butPtr->normalTextGC != None) {
 
630
        Tk_FreeGC(butPtr->display, butPtr->normalTextGC);
 
631
    }
 
632
    if (butPtr->activeTextGC != None) {
 
633
        Tk_FreeGC(butPtr->display, butPtr->activeTextGC);
 
634
    }
 
635
    if (butPtr->gray != None) {
 
636
        Tk_FreeBitmap(butPtr->display, butPtr->gray);
 
637
    }
 
638
    if (butPtr->disabledGC != None) {
 
639
        Tk_FreeGC(butPtr->display, butPtr->disabledGC);
 
640
    }
 
641
    if (butPtr->copyGC != None) {
 
642
        Tk_FreeGC(butPtr->display, butPtr->copyGC);
 
643
    }
 
644
    if (butPtr->selVarName != NULL) {
 
645
        Tcl_UntraceVar(butPtr->interp, butPtr->selVarName,
 
646
                TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
 
647
                ButtonVarProc, (ClientData) butPtr);
 
648
    }
 
649
    Tk_FreeTextLayout(butPtr->textLayout);
 
650
    Tk_FreeOptions(tkpButtonConfigSpecs, (char *) butPtr, butPtr->display,
 
651
            configFlags[butPtr->type]);
 
652
    Tcl_EventuallyFree((ClientData)butPtr, TCL_DYNAMIC);
 
653
}
 
654
 
 
655
/*
 
656
 *----------------------------------------------------------------------
 
657
 *
 
658
 * ConfigureButton --
 
659
 *
 
660
 *      This procedure is called to process an argv/argc list, plus
 
661
 *      the Tk option database, in order to configure (or
 
662
 *      reconfigure) a button widget.
 
663
 *
 
664
 * Results:
 
665
 *      The return value is a standard Tcl result.  If TCL_ERROR is
 
666
 *      returned, then interp->result contains an error message.
 
667
 *
 
668
 * Side effects:
 
669
 *      Configuration information, such as text string, colors, font,
 
670
 *      etc. get set for butPtr;  old resources get freed, if there
 
671
 *      were any.  The button is redisplayed.
 
672
 *
 
673
 *----------------------------------------------------------------------
 
674
 */
 
675
 
 
676
static int
 
677
ConfigureButton(interp, butPtr, argc, argv, flags)
 
678
    Tcl_Interp *interp;         /* Used for error reporting. */
 
679
    register TkButton *butPtr;  /* Information about widget;  may or may
 
680
                                 * not already have values for some fields. */
 
681
    int argc;                   /* Number of valid entries in argv. */
 
682
    char **argv;                /* Arguments. */
 
683
    int flags;                  /* Flags to pass to Tk_ConfigureWidget. */
 
684
{
 
685
    Tk_Image image;
 
686
 
 
687
    /*
 
688
     * Eliminate any existing trace on variables monitored by the button.
 
689
     */
 
690
 
 
691
    if (butPtr->textVarName != NULL) {
 
692
        Tcl_UntraceVar(interp, butPtr->textVarName, 
 
693
                TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
 
694
                ButtonTextVarProc, (ClientData) butPtr);
 
695
    }
 
696
    if (butPtr->selVarName != NULL) {
 
697
        Tcl_UntraceVar(interp, butPtr->selVarName, 
 
698
                TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
 
699
                ButtonVarProc, (ClientData) butPtr);
 
700
    }
 
701
 
 
702
    
 
703
 
 
704
    if (Tk_ConfigureWidget(interp, butPtr->tkwin, tkpButtonConfigSpecs,
 
705
            argc, argv, (char *) butPtr, flags) != TCL_OK) {
 
706
        return TCL_ERROR;
 
707
    }
 
708
 
 
709
    /*
 
710
     * A few options need special processing, such as setting the
 
711
     * background from a 3-D border, or filling in complicated
 
712
     * defaults that couldn't be specified to Tk_ConfigureWidget.
 
713
     */
 
714
 
 
715
    if ((butPtr->state == tkActiveUid) && !Tk_StrictMotif(butPtr->tkwin)) {
 
716
        Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->activeBorder);
 
717
    } else {
 
718
        Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->normalBorder);
 
719
        if ((butPtr->state != tkNormalUid) && (butPtr->state != tkActiveUid)
 
720
                && (butPtr->state != tkDisabledUid)) {
 
721
            Tcl_AppendResult(interp, "bad state value \"", butPtr->state,
 
722
                    "\": must be normal, active, or disabled", (char *) NULL);
 
723
            butPtr->state = tkNormalUid;
 
724
            return TCL_ERROR;
 
725
        }
 
726
    }
 
727
 
 
728
    if ((butPtr->defaultState != tkActiveUid)
 
729
            && (butPtr->defaultState != tkDisabledUid)
 
730
            && (butPtr->defaultState != tkNormalUid)) {
 
731
        Tcl_AppendResult(interp, "bad -default value \"", butPtr->defaultState,
 
732
                "\": must be normal, active, or disabled", (char *) NULL);
 
733
        butPtr->defaultState = tkDisabledUid;
 
734
        return TCL_ERROR;
 
735
    }
 
736
 
 
737
    if (butPtr->highlightWidth < 0) {
 
738
        butPtr->highlightWidth = 0;
 
739
    }
 
740
 
 
741
    if (butPtr->padX < 0) {
 
742
        butPtr->padX = 0;
 
743
    }
 
744
    if (butPtr->padY < 0) {
 
745
        butPtr->padY = 0;
 
746
    }
 
747
 
 
748
    if (butPtr->type >= TYPE_CHECK_BUTTON) {
 
749
        char *value;
 
750
 
 
751
        if (butPtr->selVarName == NULL) {
 
752
            butPtr->selVarName = (char *) ckalloc((unsigned)
 
753
                    (strlen(Tk_Name(butPtr->tkwin)) + 1));
 
754
            strcpy(butPtr->selVarName, Tk_Name(butPtr->tkwin));
 
755
        }
 
756
 
 
757
        /*
 
758
         * Select the button if the associated variable has the
 
759
         * appropriate value, initialize the variable if it doesn't
 
760
         * exist, then set a trace on the variable to monitor future
 
761
         * changes to its value.
 
762
         */
 
763
 
 
764
        value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
 
765
        butPtr->flags &= ~SELECTED;
 
766
        if (value != NULL) {
 
767
            if (strcmp(value, butPtr->onValue) == 0) {
 
768
                butPtr->flags |= SELECTED;
 
769
            }
 
770
        } else {
 
771
            if (Tcl_SetVar(interp, butPtr->selVarName,
 
772
                    (butPtr->type == TYPE_CHECK_BUTTON) ? butPtr->offValue : "",
 
773
                    TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
 
774
                return TCL_ERROR;
 
775
            }
 
776
        }
 
777
        Tcl_TraceVar(interp, butPtr->selVarName,
 
778
                TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
 
779
                ButtonVarProc, (ClientData) butPtr);
 
780
    }
 
781
 
 
782
    /*
 
783
     * Get the images for the widget, if there are any.  Allocate the
 
784
     * new images before freeing the old ones, so that the reference
 
785
     * counts don't go to zero and cause image data to be discarded.
 
786
     */
 
787
 
 
788
    if (butPtr->imageString != NULL) {
 
789
        image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
 
790
                butPtr->imageString, ButtonImageProc, (ClientData) butPtr);
 
791
        if (image == NULL) {
 
792
            return TCL_ERROR;
 
793
        }
 
794
    } else {
 
795
        image = NULL;
 
796
    }
 
797
    if (butPtr->image != NULL) {
 
798
        Tk_FreeImage(butPtr->image);
 
799
    }
 
800
    butPtr->image = image;
 
801
    if (butPtr->selectImageString != NULL) {
 
802
        image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
 
803
                butPtr->selectImageString, ButtonSelectImageProc,
 
804
                (ClientData) butPtr);
 
805
        if (image == NULL) {
 
806
            return TCL_ERROR;
 
807
        }
 
808
    } else {
 
809
        image = NULL;
 
810
    }
 
811
    if (butPtr->selectImage != NULL) {
 
812
        Tk_FreeImage(butPtr->selectImage);
 
813
    }
 
814
    butPtr->selectImage = image;
 
815
 
 
816
    if ((butPtr->image == NULL) && (butPtr->bitmap == None)
 
817
            && (butPtr->textVarName != NULL)) {
 
818
        /*
 
819
         * The button must display the value of a variable: set up a trace
 
820
         * on the variable's value, create the variable if it doesn't
 
821
         * exist, and fetch its current value.
 
822
         */
 
823
 
 
824
        char *value;
 
825
 
 
826
        value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
 
827
        if (value == NULL) {
 
828
            if (Tcl_SetVar(interp, butPtr->textVarName, butPtr->text,
 
829
                    TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
 
830
                return TCL_ERROR;
 
831
            }
 
832
        } else {
 
833
            if (butPtr->text != NULL) {
 
834
                ckfree(butPtr->text);
 
835
            }
 
836
            butPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
 
837
            strcpy(butPtr->text, value);
 
838
        }
 
839
        Tcl_TraceVar(interp, butPtr->textVarName,
 
840
                TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
 
841
                ButtonTextVarProc, (ClientData) butPtr);
 
842
    }
 
843
 
 
844
    if ((butPtr->bitmap != None) || (butPtr->image != NULL)) {
 
845
        if (Tk_GetPixels(interp, butPtr->tkwin, butPtr->widthString,
 
846
                &butPtr->width) != TCL_OK) {
 
847
            widthError:
 
848
            Tcl_AddErrorInfo(interp, "\n    (processing -width option)");
 
849
            return TCL_ERROR;
 
850
        }
 
851
        if (Tk_GetPixels(interp, butPtr->tkwin, butPtr->heightString,
 
852
                &butPtr->height) != TCL_OK) {
 
853
            heightError:
 
854
            Tcl_AddErrorInfo(interp, "\n    (processing -height option)");
 
855
            return TCL_ERROR;
 
856
        }
 
857
    } else {
 
858
        if (Tcl_GetInt(interp, butPtr->widthString, &butPtr->width)
 
859
                != TCL_OK) {
 
860
            goto widthError;
 
861
        }
 
862
        if (Tcl_GetInt(interp, butPtr->heightString, &butPtr->height)
 
863
                != TCL_OK) {
 
864
            goto heightError;
 
865
        }
 
866
    }
 
867
    
 
868
    TkButtonWorldChanged((ClientData) butPtr);
 
869
    return TCL_OK;
 
870
}
 
871
 
 
872
/*
 
873
 *---------------------------------------------------------------------------
 
874
 *
 
875
 * TkButtonWorldChanged --
 
876
 *
 
877
 *      This procedure is called when the world has changed in some
 
878
 *      way and the widget needs to recompute all its graphics contexts
 
879
 *      and determine its new geometry.
 
880
 *
 
881
 * Results:
 
882
 *      None.
 
883
 *
 
884
 * Side effects:
 
885
 *      Button will be relayed out and redisplayed.
 
886
 *
 
887
 *---------------------------------------------------------------------------
 
888
 */
 
889
 
 
890
void
 
891
TkButtonWorldChanged(instanceData)
 
892
    ClientData instanceData;    /* Information about widget. */
 
893
{
 
894
    XGCValues gcValues;
 
895
    GC newGC;
 
896
    unsigned long mask;
 
897
    TkButton *butPtr;
 
898
 
 
899
    butPtr = (TkButton *) instanceData;
 
900
 
 
901
    /*
 
902
     * Recompute GCs.
 
903
     */
 
904
 
 
905
    gcValues.font = Tk_FontId(butPtr->tkfont);
 
906
    gcValues.foreground = butPtr->normalFg->pixel;
 
907
    gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
 
908
    
 
909
    /*
 
910
     * Note: GraphicsExpose events are disabled in normalTextGC because it's
 
911
     * used to copy stuff from an off-screen pixmap onto the screen (we know
 
912
     * that there's no problem with obscured areas).
 
913
     */
 
914
 
 
915
    gcValues.graphics_exposures = False;
 
916
    mask = GCForeground | GCBackground | GCFont | GCGraphicsExposures;
 
917
    newGC = Tk_GetGC(butPtr->tkwin, mask, &gcValues);
 
918
    if (butPtr->normalTextGC != None) {
 
919
        Tk_FreeGC(butPtr->display, butPtr->normalTextGC);
 
920
    }
 
921
    butPtr->normalTextGC = newGC;
 
922
 
 
923
    if (butPtr->activeFg != NULL) {
 
924
        gcValues.font = Tk_FontId(butPtr->tkfont);
 
925
        gcValues.foreground = butPtr->activeFg->pixel;
 
926
        gcValues.background = Tk_3DBorderColor(butPtr->activeBorder)->pixel;
 
927
        mask = GCForeground | GCBackground | GCFont;
 
928
        newGC = Tk_GetGC(butPtr->tkwin, mask, &gcValues);
 
929
        if (butPtr->activeTextGC != None) {
 
930
            Tk_FreeGC(butPtr->display, butPtr->activeTextGC);
 
931
        }
 
932
        butPtr->activeTextGC = newGC;
 
933
    }
 
934
 
 
935
    if (butPtr->type != TYPE_LABEL) {
 
936
        gcValues.font = Tk_FontId(butPtr->tkfont);
 
937
        gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
 
938
        if ((butPtr->disabledFg != NULL) && (butPtr->imageString == NULL)) {
 
939
            gcValues.foreground = butPtr->disabledFg->pixel;
 
940
            mask = GCForeground | GCBackground | GCFont;
 
941
        } else {
 
942
            gcValues.foreground = gcValues.background;
 
943
            mask = GCForeground;
 
944
            if (butPtr->gray == None) {
 
945
                butPtr->gray = Tk_GetBitmap(NULL, butPtr->tkwin, 
 
946
                        Tk_GetUid("gray50"));
 
947
            }
 
948
            if (butPtr->gray != None) {
 
949
                gcValues.fill_style = FillStippled;
 
950
                gcValues.stipple = butPtr->gray;
 
951
                mask |= GCFillStyle | GCStipple;
 
952
            }
 
953
        }
 
954
        newGC = Tk_GetGC(butPtr->tkwin, mask, &gcValues);
 
955
        if (butPtr->disabledGC != None) {
 
956
            Tk_FreeGC(butPtr->display, butPtr->disabledGC);
 
957
        }
 
958
        butPtr->disabledGC = newGC;
 
959
    }
 
960
 
 
961
    if (butPtr->copyGC == None) {
 
962
        butPtr->copyGC = Tk_GetGC(butPtr->tkwin, 0, &gcValues);
 
963
    }
 
964
 
 
965
    TkpComputeButtonGeometry(butPtr);
 
966
 
 
967
    /*
 
968
     * Lastly, arrange for the button to be redisplayed.
 
969
     */
 
970
 
 
971
    if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
 
972
        Tcl_DoWhenIdle(TkpDisplayButton, (ClientData) butPtr);
 
973
        butPtr->flags |= REDRAW_PENDING;
 
974
    }
 
975
}
 
976
 
 
977
/*
 
978
 *--------------------------------------------------------------
 
979
 *
 
980
 * ButtonEventProc --
 
981
 *
 
982
 *      This procedure is invoked by the Tk dispatcher for various
 
983
 *      events on buttons.
 
984
 *
 
985
 * Results:
 
986
 *      None.
 
987
 *
 
988
 * Side effects:
 
989
 *      When the window gets deleted, internal structures get
 
990
 *      cleaned up.  When it gets exposed, it is redisplayed.
 
991
 *
 
992
 *--------------------------------------------------------------
 
993
 */
 
994
 
 
995
static void
 
996
ButtonEventProc(clientData, eventPtr)
 
997
    ClientData clientData;      /* Information about window. */
 
998
    XEvent *eventPtr;           /* Information about event. */
 
999
{
 
1000
    TkButton *butPtr = (TkButton *) clientData;
 
1001
    if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
 
1002
        goto redraw;
 
1003
    } else if (eventPtr->type == ConfigureNotify) {
 
1004
        /*
 
1005
         * Must redraw after size changes, since layout could have changed
 
1006
         * and borders will need to be redrawn.
 
1007
         */
 
1008
 
 
1009
        goto redraw;
 
1010
    } else if (eventPtr->type == DestroyNotify) {
 
1011
        TkpDestroyButton(butPtr);
 
1012
        if (butPtr->tkwin != NULL) {
 
1013
            butPtr->tkwin = NULL;
 
1014
            Tcl_DeleteCommandFromToken(butPtr->interp, butPtr->widgetCmd);
 
1015
        }
 
1016
        if (butPtr->flags & REDRAW_PENDING) {
 
1017
            Tcl_CancelIdleCall(TkpDisplayButton, (ClientData) butPtr);
 
1018
        }
 
1019
        DestroyButton(butPtr);
 
1020
    } else if (eventPtr->type == FocusIn) {
 
1021
        if (eventPtr->xfocus.detail != NotifyInferior) {
 
1022
            butPtr->flags |= GOT_FOCUS;
 
1023
            if (butPtr->highlightWidth > 0) {
 
1024
                goto redraw;
 
1025
            }
 
1026
        }
 
1027
    } else if (eventPtr->type == FocusOut) {
 
1028
        if (eventPtr->xfocus.detail != NotifyInferior) {
 
1029
            butPtr->flags &= ~GOT_FOCUS;
 
1030
            if (butPtr->highlightWidth > 0) {
 
1031
                goto redraw;
 
1032
            }
 
1033
        }
 
1034
    }
 
1035
    return;
 
1036
 
 
1037
    redraw:
 
1038
    if ((butPtr->tkwin != NULL) && !(butPtr->flags & REDRAW_PENDING)) {
 
1039
        Tcl_DoWhenIdle(TkpDisplayButton, (ClientData) butPtr);
 
1040
        butPtr->flags |= REDRAW_PENDING;
 
1041
    }
 
1042
}
 
1043
 
 
1044
/*
 
1045
 *----------------------------------------------------------------------
 
1046
 *
 
1047
 * ButtonCmdDeletedProc --
 
1048
 *
 
1049
 *      This procedure is invoked when a widget command is deleted.  If
 
1050
 *      the widget isn't already in the process of being destroyed,
 
1051
 *      this command destroys it.
 
1052
 *
 
1053
 * Results:
 
1054
 *      None.
 
1055
 *
 
1056
 * Side effects:
 
1057
 *      The widget is destroyed.
 
1058
 *
 
1059
 *----------------------------------------------------------------------
 
1060
 */
 
1061
 
 
1062
static void
 
1063
ButtonCmdDeletedProc(clientData)
 
1064
    ClientData clientData;      /* Pointer to widget record for widget. */
 
1065
{
 
1066
    TkButton *butPtr = (TkButton *) clientData;
 
1067
    Tk_Window tkwin = butPtr->tkwin;
 
1068
 
 
1069
    /*
 
1070
     * This procedure could be invoked either because the window was
 
1071
     * destroyed and the command was then deleted (in which case tkwin
 
1072
     * is NULL) or because the command was deleted, and then this procedure
 
1073
     * destroys the widget.
 
1074
     */
 
1075
 
 
1076
    if (tkwin != NULL) {
 
1077
        butPtr->tkwin = NULL;
 
1078
        Tk_DestroyWindow(tkwin);
 
1079
    }
 
1080
}
 
1081
 
 
1082
/*
 
1083
 *----------------------------------------------------------------------
 
1084
 *
 
1085
 * TkInvokeButton --
 
1086
 *
 
1087
 *      This procedure is called to carry out the actions associated
 
1088
 *      with a button, such as invoking a Tcl command or setting a
 
1089
 *      variable.  This procedure is invoked, for example, when the
 
1090
 *      button is invoked via the mouse.
 
1091
 *
 
1092
 * Results:
 
1093
 *      A standard Tcl return value.  Information is also left in
 
1094
 *      interp->result.
 
1095
 *
 
1096
 * Side effects:
 
1097
 *      Depends on the button and its associated command.
 
1098
 *
 
1099
 *----------------------------------------------------------------------
 
1100
 */
 
1101
 
 
1102
int
 
1103
TkInvokeButton(butPtr)
 
1104
    register TkButton *butPtr;          /* Information about button. */
 
1105
{
 
1106
    if (butPtr->type == TYPE_CHECK_BUTTON) {
 
1107
        if (butPtr->flags & SELECTED) {
 
1108
            if (Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->offValue,
 
1109
                    TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
 
1110
                return TCL_ERROR;
 
1111
            }
 
1112
        } else {
 
1113
            if (Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
 
1114
                    TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
 
1115
                return TCL_ERROR;
 
1116
            }
 
1117
        }
 
1118
    } else if (butPtr->type == TYPE_RADIO_BUTTON) {
 
1119
        if (Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
 
1120
                TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
 
1121
            return TCL_ERROR;
 
1122
        }
 
1123
    }
 
1124
    if ((butPtr->type != TYPE_LABEL) && (butPtr->command != NULL)) {
 
1125
        return TkCopyAndGlobalEval(butPtr->interp, butPtr->command);
 
1126
    }
 
1127
    return TCL_OK;
 
1128
}
 
1129
 
 
1130
/*
 
1131
 *--------------------------------------------------------------
 
1132
 *
 
1133
 * ButtonVarProc --
 
1134
 *
 
1135
 *      This procedure is invoked when someone changes the
 
1136
 *      state variable associated with a radio button.  Depending
 
1137
 *      on the new value of the button's variable, the button
 
1138
 *      may be selected or deselected.
 
1139
 *
 
1140
 * Results:
 
1141
 *      NULL is always returned.
 
1142
 *
 
1143
 * Side effects:
 
1144
 *      The button may become selected or deselected.
 
1145
 *
 
1146
 *--------------------------------------------------------------
 
1147
 */
 
1148
 
 
1149
        /* ARGSUSED */
 
1150
static char *
 
1151
ButtonVarProc(clientData, interp, name1, name2, flags)
 
1152
    ClientData clientData;      /* Information about button. */
 
1153
    Tcl_Interp *interp;         /* Interpreter containing variable. */
 
1154
    char *name1;                /* Name of variable. */
 
1155
    char *name2;                /* Second part of variable name. */
 
1156
    int flags;                  /* Information about what happened. */
 
1157
{
 
1158
    register TkButton *butPtr = (TkButton *) clientData;
 
1159
    char *value;
 
1160
 
 
1161
    /*
 
1162
     * If the variable is being unset, then just re-establish the
 
1163
     * trace unless the whole interpreter is going away.
 
1164
     */
 
1165
 
 
1166
    if (flags & TCL_TRACE_UNSETS) {
 
1167
        butPtr->flags &= ~SELECTED;
 
1168
        if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
 
1169
            Tcl_TraceVar(interp, butPtr->selVarName,
 
1170
                    TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
 
1171
                    ButtonVarProc, clientData);
 
1172
        }
 
1173
        goto redisplay;
 
1174
    }
 
1175
 
 
1176
    /*
 
1177
     * Use the value of the variable to update the selected status of
 
1178
     * the button.
 
1179
     */
 
1180
 
 
1181
    value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
 
1182
    if (value == NULL) {
 
1183
        value = "";
 
1184
    }
 
1185
    if (strcmp(value, butPtr->onValue) == 0) {
 
1186
        if (butPtr->flags & SELECTED) {
 
1187
            return (char *) NULL;
 
1188
        }
 
1189
        butPtr->flags |= SELECTED;
 
1190
    } else if (butPtr->flags & SELECTED) {
 
1191
        butPtr->flags &= ~SELECTED;
 
1192
    } else {
 
1193
        return (char *) NULL;
 
1194
    }
 
1195
 
 
1196
    redisplay:
 
1197
    if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
 
1198
            && !(butPtr->flags & REDRAW_PENDING)) {
 
1199
        Tcl_DoWhenIdle(TkpDisplayButton, (ClientData) butPtr);
 
1200
        butPtr->flags |= REDRAW_PENDING;
 
1201
    }
 
1202
    return (char *) NULL;
 
1203
}
 
1204
 
 
1205
/*
 
1206
 *--------------------------------------------------------------
 
1207
 *
 
1208
 * ButtonTextVarProc --
 
1209
 *
 
1210
 *      This procedure is invoked when someone changes the variable
 
1211
 *      whose contents are to be displayed in a button.
 
1212
 *
 
1213
 * Results:
 
1214
 *      NULL is always returned.
 
1215
 *
 
1216
 * Side effects:
 
1217
 *      The text displayed in the button will change to match the
 
1218
 *      variable.
 
1219
 *
 
1220
 *--------------------------------------------------------------
 
1221
 */
 
1222
 
 
1223
        /* ARGSUSED */
 
1224
static char *
 
1225
ButtonTextVarProc(clientData, interp, name1, name2, flags)
 
1226
    ClientData clientData;      /* Information about button. */
 
1227
    Tcl_Interp *interp;         /* Interpreter containing variable. */
 
1228
    char *name1;                /* Not used. */
 
1229
    char *name2;                /* Not used. */
 
1230
    int flags;                  /* Information about what happened. */
 
1231
{
 
1232
    register TkButton *butPtr = (TkButton *) clientData;
 
1233
    char *value;
 
1234
 
 
1235
    /*
 
1236
     * If the variable is unset, then immediately recreate it unless
 
1237
     * the whole interpreter is going away.
 
1238
     */
 
1239
 
 
1240
    if (flags & TCL_TRACE_UNSETS) {
 
1241
        if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
 
1242
            Tcl_SetVar(interp, butPtr->textVarName, butPtr->text,
 
1243
                    TCL_GLOBAL_ONLY);
 
1244
            Tcl_TraceVar(interp, butPtr->textVarName,
 
1245
                    TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
 
1246
                    ButtonTextVarProc, clientData);
 
1247
        }
 
1248
        return (char *) NULL;
 
1249
    }
 
1250
 
 
1251
    value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
 
1252
    if (value == NULL) {
 
1253
        value = "";
 
1254
    }
 
1255
    if (butPtr->text != NULL) {
 
1256
        ckfree(butPtr->text);
 
1257
    }
 
1258
    butPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
 
1259
    strcpy(butPtr->text, value);
 
1260
    TkpComputeButtonGeometry(butPtr);
 
1261
 
 
1262
    if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
 
1263
            && !(butPtr->flags & REDRAW_PENDING)) {
 
1264
        Tcl_DoWhenIdle(TkpDisplayButton, (ClientData) butPtr);
 
1265
        butPtr->flags |= REDRAW_PENDING;
 
1266
    }
 
1267
    return (char *) NULL;
 
1268
}
 
1269
 
 
1270
/*
 
1271
 *----------------------------------------------------------------------
 
1272
 *
 
1273
 * ButtonImageProc --
 
1274
 *
 
1275
 *      This procedure is invoked by the image code whenever the manager
 
1276
 *      for an image does something that affects the size of contents
 
1277
 *      of an image displayed in a button.
 
1278
 *
 
1279
 * Results:
 
1280
 *      None.
 
1281
 *
 
1282
 * Side effects:
 
1283
 *      Arranges for the button to get redisplayed.
 
1284
 *
 
1285
 *----------------------------------------------------------------------
 
1286
 */
 
1287
 
 
1288
static void
 
1289
ButtonImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
 
1290
    ClientData clientData;              /* Pointer to widget record. */
 
1291
    int x, y;                           /* Upper left pixel (within image)
 
1292
                                         * that must be redisplayed. */
 
1293
    int width, height;                  /* Dimensions of area to redisplay
 
1294
                                         * (may be <= 0). */
 
1295
    int imgWidth, imgHeight;            /* New dimensions of image. */
 
1296
{
 
1297
    register TkButton *butPtr = (TkButton *) clientData;
 
1298
 
 
1299
    if (butPtr->tkwin != NULL) {
 
1300
        TkpComputeButtonGeometry(butPtr);
 
1301
        if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
 
1302
            Tcl_DoWhenIdle(TkpDisplayButton, (ClientData) butPtr);
 
1303
            butPtr->flags |= REDRAW_PENDING;
 
1304
        }
 
1305
    }
 
1306
}
 
1307
 
 
1308
/*
 
1309
 *----------------------------------------------------------------------
 
1310
 *
 
1311
 * ButtonSelectImageProc --
 
1312
 *
 
1313
 *      This procedure is invoked by the image code whenever the manager
 
1314
 *      for an image does something that affects the size of contents
 
1315
 *      of the image displayed in a button when it is selected.
 
1316
 *
 
1317
 * Results:
 
1318
 *      None.
 
1319
 *
 
1320
 * Side effects:
 
1321
 *      May arrange for the button to get redisplayed.
 
1322
 *
 
1323
 *----------------------------------------------------------------------
 
1324
 */
 
1325
 
 
1326
static void
 
1327
ButtonSelectImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
 
1328
    ClientData clientData;              /* Pointer to widget record. */
 
1329
    int x, y;                           /* Upper left pixel (within image)
 
1330
                                         * that must be redisplayed. */
 
1331
    int width, height;                  /* Dimensions of area to redisplay
 
1332
                                         * (may be <= 0). */
 
1333
    int imgWidth, imgHeight;            /* New dimensions of image. */
 
1334
{
 
1335
    register TkButton *butPtr = (TkButton *) clientData;
 
1336
 
 
1337
    /*
 
1338
     * Don't recompute geometry:  it's controlled by the primary image.
 
1339
     */
 
1340
 
 
1341
    if ((butPtr->flags & SELECTED) && (butPtr->tkwin != NULL)
 
1342
            && Tk_IsMapped(butPtr->tkwin)
 
1343
            && !(butPtr->flags & REDRAW_PENDING)) {
 
1344
        Tcl_DoWhenIdle(TkpDisplayButton, (ClientData) butPtr);
 
1345
        butPtr->flags |= REDRAW_PENDING;
 
1346
    }
 
1347
}