Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libtk/generic/tkMessage.c
1810 views
1
/*
2
* tkMessage.c --
3
*
4
* This module implements a message widgets for the Tk
5
* toolkit. A message widget displays a multi-line string
6
* in a window according to a particular aspect ratio.
7
*
8
* Copyright (c) 1990-1994 The Regents of the University of California.
9
* Copyright (c) 1994-1995 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
* SCCS: @(#) tkMessage.c 1.66 96/02/15 18:52:28
15
*/
16
17
#include "tkInt.h"
18
#include "tkDefault.h"
19
20
/*
21
* A data structure of the following type is kept for each message
22
* widget managed by this file:
23
*/
24
25
typedef struct {
26
Tk_Window tkwin; /* Window that embodies the message. NULL
27
* means that the window has been destroyed
28
* but the data structures haven't yet been
29
* cleaned up.*/
30
Display *display; /* Display containing widget. Used, among
31
* other things, so that resources can be
32
* freed even after tkwin has gone away. */
33
Tcl_Interp *interp; /* Interpreter associated with message. */
34
Tcl_Command widgetCmd; /* Token for message's widget command. */
35
Tk_Uid string; /* String displayed in message. */
36
int numChars; /* Number of characters in string, not
37
* including terminating NULL character. */
38
char *textVarName; /* Name of variable (malloc'ed) or NULL.
39
* If non-NULL, message displays the contents
40
* of this variable. */
41
42
/*
43
* Information used when displaying widget:
44
*/
45
46
Tk_3DBorder border; /* Structure used to draw 3-D border and
47
* background. NULL means a border hasn't
48
* been created yet. */
49
int borderWidth; /* Width of border. */
50
int relief; /* 3-D effect: TK_RELIEF_RAISED, etc. */
51
int highlightWidth; /* Width in pixels of highlight to draw
52
* around widget when it has the focus.
53
* <= 0 means don't draw a highlight. */
54
XColor *highlightBgColorPtr;
55
/* Color for drawing traversal highlight
56
* area when highlight is off. */
57
XColor *highlightColorPtr; /* Color for drawing traversal highlight. */
58
int inset; /* Total width of all borders, including
59
* traversal highlight and 3-D border.
60
* Indicates how much interior stuff must
61
* be offset from outside edges to leave
62
* room for borders. */
63
XFontStruct *fontPtr; /* Information about text font, or NULL. */
64
XColor *fgColorPtr; /* Foreground color in normal mode. */
65
GC textGC; /* GC for drawing text in normal mode. */
66
int padX, padY; /* User-requested extra space around text. */
67
Tk_Anchor anchor; /* Where to position text within window region
68
* if window is larger or smaller than
69
* needed. */
70
int width; /* User-requested width, in pixels. 0 means
71
* compute width using aspect ratio below. */
72
int aspect; /* Desired aspect ratio for window
73
* (100*width/height). */
74
int lineLength; /* Length of each line, in pixels. Computed
75
* from width and/or aspect. */
76
int msgHeight; /* Total number of pixels in vertical direction
77
* needed to display message. */
78
Tk_Justify justify; /* Justification for text. */
79
80
/*
81
* Miscellaneous information:
82
*/
83
84
Tk_Cursor cursor; /* Current cursor for window, or None. */
85
char *takeFocus; /* Value of -takefocus option; not used in
86
* the C code, but used by keyboard traversal
87
* scripts. Malloc'ed, but may be NULL. */
88
int flags; /* Various flags; see below for
89
* definitions. */
90
} Message;
91
92
/*
93
* Flag bits for messages:
94
*
95
* REDRAW_PENDING: Non-zero means a DoWhenIdle handler
96
* has already been queued to redraw
97
* this window.
98
* GOT_FOCUS: Non-zero means this button currently
99
* has the input focus.
100
*/
101
102
#define REDRAW_PENDING 1
103
#define GOT_FOCUS 4
104
105
/*
106
* Information used for argv parsing.
107
*/
108
109
static Tk_ConfigSpec configSpecs[] = {
110
{TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
111
DEF_MESSAGE_ANCHOR, Tk_Offset(Message, anchor), 0},
112
{TK_CONFIG_INT, "-aspect", "aspect", "Aspect",
113
DEF_MESSAGE_ASPECT, Tk_Offset(Message, aspect), 0},
114
{TK_CONFIG_BORDER, "-background", "background", "Background",
115
DEF_MESSAGE_BG_COLOR, Tk_Offset(Message, border),
116
TK_CONFIG_COLOR_ONLY},
117
{TK_CONFIG_BORDER, "-background", "background", "Background",
118
DEF_MESSAGE_BG_MONO, Tk_Offset(Message, border),
119
TK_CONFIG_MONO_ONLY},
120
{TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
121
(char *) NULL, 0, 0},
122
{TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
123
(char *) NULL, 0, 0},
124
{TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
125
DEF_MESSAGE_BORDER_WIDTH, Tk_Offset(Message, borderWidth), 0},
126
{TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
127
DEF_MESSAGE_CURSOR, Tk_Offset(Message, cursor), TK_CONFIG_NULL_OK},
128
{TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
129
(char *) NULL, 0, 0},
130
{TK_CONFIG_FONT, "-font", "font", "Font",
131
DEF_MESSAGE_FONT, Tk_Offset(Message, fontPtr), 0},
132
{TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
133
DEF_MESSAGE_FG, Tk_Offset(Message, fgColorPtr), 0},
134
{TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
135
"HighlightBackground", DEF_MESSAGE_HIGHLIGHT_BG,
136
Tk_Offset(Message, highlightBgColorPtr), 0},
137
{TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
138
DEF_MESSAGE_HIGHLIGHT, Tk_Offset(Message, highlightColorPtr), 0},
139
{TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
140
"HighlightThickness",
141
DEF_MESSAGE_HIGHLIGHT_WIDTH, Tk_Offset(Message, highlightWidth), 0},
142
{TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
143
DEF_MESSAGE_JUSTIFY, Tk_Offset(Message, justify), 0},
144
{TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
145
DEF_MESSAGE_PADX, Tk_Offset(Message, padX), 0},
146
{TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
147
DEF_MESSAGE_PADY, Tk_Offset(Message, padY), 0},
148
{TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
149
DEF_MESSAGE_RELIEF, Tk_Offset(Message, relief), 0},
150
{TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
151
DEF_MESSAGE_TAKE_FOCUS, Tk_Offset(Message, takeFocus),
152
TK_CONFIG_NULL_OK},
153
{TK_CONFIG_STRING, "-text", "text", "Text",
154
DEF_MESSAGE_TEXT, Tk_Offset(Message, string), 0},
155
{TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
156
DEF_MESSAGE_TEXT_VARIABLE, Tk_Offset(Message, textVarName),
157
TK_CONFIG_NULL_OK},
158
{TK_CONFIG_PIXELS, "-width", "width", "Width",
159
DEF_MESSAGE_WIDTH, Tk_Offset(Message, width), 0},
160
{TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
161
(char *) NULL, 0, 0}
162
};
163
164
/*
165
* Forward declarations for procedures defined later in this file:
166
*/
167
168
static void MessageCmdDeletedProc _ANSI_ARGS_((
169
ClientData clientData));
170
static void MessageEventProc _ANSI_ARGS_((ClientData clientData,
171
XEvent *eventPtr));
172
static char * MessageTextVarProc _ANSI_ARGS_((ClientData clientData,
173
Tcl_Interp *interp, char *name1, char *name2,
174
int flags));
175
static int MessageWidgetCmd _ANSI_ARGS_((ClientData clientData,
176
Tcl_Interp *interp, int argc, char **argv));
177
static void ComputeMessageGeometry _ANSI_ARGS_((Message *msgPtr));
178
static int ConfigureMessage _ANSI_ARGS_((Tcl_Interp *interp,
179
Message *msgPtr, int argc, char **argv,
180
int flags));
181
static void DestroyMessage _ANSI_ARGS_((char *memPtr));
182
static void DisplayMessage _ANSI_ARGS_((ClientData clientData));
183
184
/*
185
*--------------------------------------------------------------
186
*
187
* Tk_MessageCmd --
188
*
189
* This procedure is invoked to process the "message" Tcl
190
* command. See the user documentation for details on what
191
* it does.
192
*
193
* Results:
194
* A standard Tcl result.
195
*
196
* Side effects:
197
* See the user documentation.
198
*
199
*--------------------------------------------------------------
200
*/
201
202
int
203
Tk_MessageCmd(clientData, interp, argc, argv)
204
ClientData clientData; /* Main window associated with
205
* interpreter. */
206
Tcl_Interp *interp; /* Current interpreter. */
207
int argc; /* Number of arguments. */
208
char **argv; /* Argument strings. */
209
{
210
register Message *msgPtr;
211
Tk_Window new;
212
Tk_Window tkwin = (Tk_Window) clientData;
213
214
if (argc < 2) {
215
Tcl_AppendResult(interp, "wrong # args: should be \"",
216
argv[0], " pathName ?options?\"", (char *) NULL);
217
return TCL_ERROR;
218
}
219
220
new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
221
if (new == NULL) {
222
return TCL_ERROR;
223
}
224
225
msgPtr = (Message *) ckalloc(sizeof(Message));
226
msgPtr->tkwin = new;
227
msgPtr->display = Tk_Display(new);
228
msgPtr->interp = interp;
229
msgPtr->widgetCmd = Tcl_CreateCommand(interp, Tk_PathName(msgPtr->tkwin),
230
MessageWidgetCmd, (ClientData) msgPtr, MessageCmdDeletedProc);
231
msgPtr->string = NULL;
232
msgPtr->numChars = 0;
233
msgPtr->textVarName = NULL;
234
msgPtr->border = NULL;
235
msgPtr->borderWidth = 0;
236
msgPtr->relief = TK_RELIEF_FLAT;
237
msgPtr->highlightWidth = 0;
238
msgPtr->highlightBgColorPtr = NULL;
239
msgPtr->highlightColorPtr = NULL;
240
msgPtr->inset = 0;
241
msgPtr->fontPtr = NULL;
242
msgPtr->fgColorPtr = NULL;
243
msgPtr->textGC = None;
244
msgPtr->padX = 0;
245
msgPtr->padY = 0;
246
msgPtr->anchor = TK_ANCHOR_CENTER;
247
msgPtr->width = 0;
248
msgPtr->aspect = 150;
249
msgPtr->lineLength = 0;
250
msgPtr->msgHeight = 0;
251
msgPtr->justify = TK_JUSTIFY_LEFT;
252
msgPtr->cursor = None;
253
msgPtr->takeFocus = NULL;
254
msgPtr->flags = 0;
255
256
Tk_SetClass(msgPtr->tkwin, "Message");
257
Tk_CreateEventHandler(msgPtr->tkwin,
258
ExposureMask|StructureNotifyMask|FocusChangeMask,
259
MessageEventProc, (ClientData) msgPtr);
260
if (ConfigureMessage(interp, msgPtr, argc-2, argv+2, 0) != TCL_OK) {
261
goto error;
262
}
263
264
interp->result = Tk_PathName(msgPtr->tkwin);
265
return TCL_OK;
266
267
error:
268
Tk_DestroyWindow(msgPtr->tkwin);
269
return TCL_ERROR;
270
}
271
272
/*
273
*--------------------------------------------------------------
274
*
275
* MessageWidgetCmd --
276
*
277
* This procedure is invoked to process the Tcl command
278
* that corresponds to a widget managed by this module.
279
* See the user documentation for details on what it does.
280
*
281
* Results:
282
* A standard Tcl result.
283
*
284
* Side effects:
285
* See the user documentation.
286
*
287
*--------------------------------------------------------------
288
*/
289
290
static int
291
MessageWidgetCmd(clientData, interp, argc, argv)
292
ClientData clientData; /* Information about message widget. */
293
Tcl_Interp *interp; /* Current interpreter. */
294
int argc; /* Number of arguments. */
295
char **argv; /* Argument strings. */
296
{
297
register Message *msgPtr = (Message *) clientData;
298
size_t length;
299
int c;
300
301
if (argc < 2) {
302
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
303
" option ?arg arg ...?\"", (char *) NULL);
304
return TCL_ERROR;
305
}
306
c = argv[1][0];
307
length = strlen(argv[1]);
308
if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
309
&& (length >= 2)) {
310
if (argc != 3) {
311
Tcl_AppendResult(interp, "wrong # args: should be \"",
312
argv[0], " cget option\"",
313
(char *) NULL);
314
return TCL_ERROR;
315
}
316
return Tk_ConfigureValue(interp, msgPtr->tkwin, configSpecs,
317
(char *) msgPtr, argv[2], 0);
318
} else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
319
&& (length >= 2)) {
320
if (argc == 2) {
321
return Tk_ConfigureInfo(interp, msgPtr->tkwin, configSpecs,
322
(char *) msgPtr, (char *) NULL, 0);
323
} else if (argc == 3) {
324
return Tk_ConfigureInfo(interp, msgPtr->tkwin, configSpecs,
325
(char *) msgPtr, argv[2], 0);
326
} else {
327
return ConfigureMessage(interp, msgPtr, argc-2, argv+2,
328
TK_CONFIG_ARGV_ONLY);
329
}
330
} else {
331
Tcl_AppendResult(interp, "bad option \"", argv[1],
332
"\": must be cget or configure", (char *) NULL);
333
return TCL_ERROR;
334
}
335
}
336
337
/*
338
*----------------------------------------------------------------------
339
*
340
* DestroyMessage --
341
*
342
* This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
343
* to clean up the internal structure of a message at a safe time
344
* (when no-one is using it anymore).
345
*
346
* Results:
347
* None.
348
*
349
* Side effects:
350
* Everything associated with the message is freed up.
351
*
352
*----------------------------------------------------------------------
353
*/
354
355
static void
356
DestroyMessage(memPtr)
357
char *memPtr; /* Info about message widget. */
358
{
359
register Message *msgPtr = (Message *) memPtr;
360
361
/*
362
* Free up all the stuff that requires special handling, then
363
* let Tk_FreeOptions handle all the standard option-related
364
* stuff.
365
*/
366
367
if (msgPtr->textVarName != NULL) {
368
Tcl_UntraceVar(msgPtr->interp, msgPtr->textVarName,
369
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
370
MessageTextVarProc, (ClientData) msgPtr);
371
}
372
if (msgPtr->textGC != None) {
373
Tk_FreeGC(msgPtr->display, msgPtr->textGC);
374
}
375
Tk_FreeOptions(configSpecs, (char *) msgPtr, msgPtr->display, 0);
376
ckfree((char *) msgPtr);
377
}
378
379
/*
380
*----------------------------------------------------------------------
381
*
382
* ConfigureMessage --
383
*
384
* This procedure is called to process an argv/argc list, plus
385
* the Tk option database, in order to configure (or
386
* reconfigure) a message widget.
387
*
388
* Results:
389
* The return value is a standard Tcl result. If TCL_ERROR is
390
* returned, then interp->result contains an error message.
391
*
392
* Side effects:
393
* Configuration information, such as text string, colors, font,
394
* etc. get set for msgPtr; old resources get freed, if there
395
* were any.
396
*
397
*----------------------------------------------------------------------
398
*/
399
400
static int
401
ConfigureMessage(interp, msgPtr, argc, argv, flags)
402
Tcl_Interp *interp; /* Used for error reporting. */
403
register Message *msgPtr; /* Information about widget; may or may
404
* not already have values for some fields. */
405
int argc; /* Number of valid entries in argv. */
406
char **argv; /* Arguments. */
407
int flags; /* Flags to pass to Tk_ConfigureWidget. */
408
{
409
XGCValues gcValues;
410
GC newGC;
411
412
/*
413
* Eliminate any existing trace on a variable monitored by the message.
414
*/
415
416
if (msgPtr->textVarName != NULL) {
417
Tcl_UntraceVar(interp, msgPtr->textVarName,
418
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
419
MessageTextVarProc, (ClientData) msgPtr);
420
}
421
422
if (Tk_ConfigureWidget(interp, msgPtr->tkwin, configSpecs,
423
argc, argv, (char *) msgPtr, flags) != TCL_OK) {
424
return TCL_ERROR;
425
}
426
427
/*
428
* If the message is to display the value of a variable, then set up
429
* a trace on the variable's value, create the variable if it doesn't
430
* exist, and fetch its current value.
431
*/
432
433
if (msgPtr->textVarName != NULL) {
434
char *value;
435
436
value = Tcl_GetVar(interp, msgPtr->textVarName, TCL_GLOBAL_ONLY);
437
if (value == NULL) {
438
Tcl_SetVar(interp, msgPtr->textVarName, msgPtr->string,
439
TCL_GLOBAL_ONLY);
440
} else {
441
if (msgPtr->string != NULL) {
442
ckfree(msgPtr->string);
443
}
444
msgPtr->string = (char *) ckalloc((unsigned) (strlen(value) + 1));
445
strcpy(msgPtr->string, value);
446
}
447
Tcl_TraceVar(interp, msgPtr->textVarName,
448
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
449
MessageTextVarProc, (ClientData) msgPtr);
450
}
451
452
/*
453
* A few other options need special processing, such as setting
454
* the background from a 3-D border or handling special defaults
455
* that couldn't be specified to Tk_ConfigureWidget.
456
*/
457
458
msgPtr->numChars = strlen(msgPtr->string);
459
460
Tk_SetBackgroundFromBorder(msgPtr->tkwin, msgPtr->border);
461
462
if (msgPtr->highlightWidth < 0) {
463
msgPtr->highlightWidth = 0;
464
}
465
466
gcValues.font = msgPtr->fontPtr->fid;
467
gcValues.foreground = msgPtr->fgColorPtr->pixel;
468
newGC = Tk_GetGC(msgPtr->tkwin, GCForeground|GCFont,
469
&gcValues);
470
if (msgPtr->textGC != None) {
471
Tk_FreeGC(msgPtr->display, msgPtr->textGC);
472
}
473
msgPtr->textGC = newGC;
474
475
if (msgPtr->padX == -1) {
476
msgPtr->padX = msgPtr->fontPtr->ascent/2;
477
}
478
479
if (msgPtr->padY == -1) {
480
msgPtr->padY = msgPtr->fontPtr->ascent/4;
481
}
482
483
/*
484
* Recompute the desired geometry for the window, and arrange for
485
* the window to be redisplayed.
486
*/
487
488
ComputeMessageGeometry(msgPtr);
489
if ((msgPtr->tkwin != NULL) && Tk_IsMapped(msgPtr->tkwin)
490
&& !(msgPtr->flags & REDRAW_PENDING)) {
491
Tcl_DoWhenIdle(DisplayMessage, (ClientData) msgPtr);
492
msgPtr->flags |= REDRAW_PENDING;
493
}
494
495
return TCL_OK;
496
}
497
498
/*
499
*--------------------------------------------------------------
500
*
501
* ComputeMessageGeometry --
502
*
503
* Compute the desired geometry for a message window,
504
* taking into account the desired aspect ratio for the
505
* window.
506
*
507
* Results:
508
* None.
509
*
510
* Side effects:
511
* Tk_GeometryRequest is called to inform the geometry
512
* manager of the desired geometry for this window.
513
*
514
*--------------------------------------------------------------
515
*/
516
517
static void
518
ComputeMessageGeometry(msgPtr)
519
register Message *msgPtr; /* Information about window. */
520
{
521
char *p;
522
int width, inc, height, numLines;
523
int thisWidth, maxWidth;
524
int aspect, lowerBound, upperBound;
525
526
msgPtr->inset = msgPtr->borderWidth + msgPtr->highlightWidth;
527
528
/*
529
* Compute acceptable bounds for the final aspect ratio.
530
*/
531
532
aspect = msgPtr->aspect/10;
533
if (aspect < 5) {
534
aspect = 5;
535
}
536
lowerBound = msgPtr->aspect - aspect;
537
upperBound = msgPtr->aspect + aspect;
538
539
/*
540
* Do the computation in multiple passes: start off with
541
* a very wide window, and compute its height. Then change
542
* the width and try again. Reduce the size of the change
543
* and iterate until dimensions are found that approximate
544
* the desired aspect ratio. Or, if the user gave an explicit
545
* width then just use that.
546
*/
547
548
if (msgPtr->width > 0) {
549
width = msgPtr->width;
550
inc = 0;
551
} else {
552
width = WidthOfScreen(Tk_Screen(msgPtr->tkwin))/2;
553
inc = width/2;
554
}
555
for ( ; ; inc /= 2) {
556
maxWidth = 0;
557
for (numLines = 1, p = msgPtr->string; ; numLines++) {
558
if (*p == '\n') {
559
p++;
560
continue;
561
}
562
p += TkMeasureChars(msgPtr->fontPtr, p,
563
msgPtr->numChars - (p - msgPtr->string), 0, width, 0,
564
TK_WHOLE_WORDS|TK_AT_LEAST_ONE, &thisWidth);
565
if (thisWidth > maxWidth) {
566
maxWidth = thisWidth;
567
}
568
if (*p == 0) {
569
break;
570
}
571
572
/*
573
* Skip spaces and tabs at the beginning of a line, unless
574
* they follow a user-requested newline.
575
*/
576
577
while (isspace(UCHAR(*p))) {
578
if (*p == '\n') {
579
p++;
580
break;
581
}
582
p++;
583
}
584
}
585
586
height = numLines * (msgPtr->fontPtr->ascent
587
+ msgPtr->fontPtr->descent) + 2*msgPtr->inset
588
+ 2*msgPtr->padY;
589
if (inc <= 2) {
590
break;
591
}
592
aspect = (100*(maxWidth + 2*msgPtr->inset + 2*msgPtr->padX))/height;
593
if (aspect < lowerBound) {
594
width += inc;
595
} else if (aspect > upperBound) {
596
width -= inc;
597
} else {
598
break;
599
}
600
}
601
msgPtr->lineLength = maxWidth;
602
msgPtr->msgHeight = numLines * (msgPtr->fontPtr->ascent
603
+ msgPtr->fontPtr->descent);
604
Tk_GeometryRequest(msgPtr->tkwin,
605
maxWidth + 2*msgPtr->inset + 2*msgPtr->padX, height);
606
Tk_SetInternalBorder(msgPtr->tkwin, msgPtr->inset);
607
}
608
609
/*
610
*--------------------------------------------------------------
611
*
612
* DisplayMessage --
613
*
614
* This procedure redraws the contents of a message window.
615
*
616
* Results:
617
* None.
618
*
619
* Side effects:
620
* Information appears on the screen.
621
*
622
*--------------------------------------------------------------
623
*/
624
625
static void
626
DisplayMessage(clientData)
627
ClientData clientData; /* Information about window. */
628
{
629
register Message *msgPtr = (Message *) clientData;
630
register Tk_Window tkwin = msgPtr->tkwin;
631
char *p;
632
int x, y, lineLength, numChars, charsLeft;
633
634
msgPtr->flags &= ~REDRAW_PENDING;
635
if ((msgPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
636
return;
637
}
638
Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), msgPtr->border, 0, 0,
639
Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
640
641
/*
642
* Compute starting y-location for message based on message size
643
* and anchor option.
644
*/
645
646
switch (msgPtr->anchor) {
647
case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
648
y = msgPtr->inset + msgPtr->padY;
649
break;
650
case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
651
y = ((int) (Tk_Height(tkwin) - msgPtr->msgHeight))/2;
652
break;
653
default:
654
y = Tk_Height(tkwin) - msgPtr->inset - msgPtr->padY
655
- msgPtr->msgHeight;
656
break;
657
}
658
y += msgPtr->fontPtr->ascent;
659
660
/*
661
* Work through the string to display one line at a time.
662
* Display each line in three steps. First compute the
663
* line's width, then figure out where to display the
664
* line to justify it properly, then display the line.
665
*/
666
667
for (p = msgPtr->string, charsLeft = msgPtr->numChars; *p != 0;
668
y += msgPtr->fontPtr->ascent + msgPtr->fontPtr->descent) {
669
if (*p == '\n') {
670
p++;
671
charsLeft--;
672
continue;
673
}
674
numChars = TkMeasureChars(msgPtr->fontPtr, p, charsLeft, 0,
675
msgPtr->lineLength, 0, TK_WHOLE_WORDS|TK_AT_LEAST_ONE,
676
&lineLength);
677
switch (msgPtr->anchor) {
678
case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
679
x = msgPtr->inset + msgPtr->padX;
680
break;
681
case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
682
x = ((int) (Tk_Width(tkwin) - msgPtr->lineLength))/2;
683
break;
684
default:
685
x = Tk_Width(tkwin) - msgPtr->inset - msgPtr->padX
686
- msgPtr->lineLength;
687
break;
688
}
689
if (msgPtr->justify == TK_JUSTIFY_CENTER) {
690
x += (msgPtr->lineLength - lineLength)/2;
691
} else if (msgPtr->justify == TK_JUSTIFY_RIGHT) {
692
x += msgPtr->lineLength - lineLength;
693
}
694
TkDisplayChars(msgPtr->display, Tk_WindowId(tkwin),
695
msgPtr->textGC, msgPtr->fontPtr, p, numChars, x, y, x, 0);
696
p += numChars;
697
charsLeft -= numChars;
698
699
/*
700
* Skip blanks at the beginning of a line, unless they follow
701
* a user-requested newline.
702
*/
703
704
while (isspace(UCHAR(*p))) {
705
charsLeft--;
706
if (*p == '\n') {
707
p++;
708
break;
709
}
710
p++;
711
}
712
}
713
714
if (msgPtr->relief != TK_RELIEF_FLAT) {
715
Tk_Draw3DRectangle(tkwin, Tk_WindowId(tkwin), msgPtr->border,
716
msgPtr->highlightWidth, msgPtr->highlightWidth,
717
Tk_Width(tkwin) - 2*msgPtr->highlightWidth,
718
Tk_Height(tkwin) - 2*msgPtr->highlightWidth,
719
msgPtr->borderWidth, msgPtr->relief);
720
}
721
if (msgPtr->highlightWidth != 0) {
722
GC gc;
723
724
if (msgPtr->flags & GOT_FOCUS) {
725
gc = Tk_GCForColor(msgPtr->highlightColorPtr, Tk_WindowId(tkwin));
726
} else {
727
gc = Tk_GCForColor(msgPtr->highlightBgColorPtr, Tk_WindowId(tkwin));
728
}
729
Tk_DrawFocusHighlight(tkwin, gc, msgPtr->highlightWidth,
730
Tk_WindowId(tkwin));
731
}
732
}
733
734
/*
735
*--------------------------------------------------------------
736
*
737
* MessageEventProc --
738
*
739
* This procedure is invoked by the Tk dispatcher for various
740
* events on messages.
741
*
742
* Results:
743
* None.
744
*
745
* Side effects:
746
* When the window gets deleted, internal structures get
747
* cleaned up. When it gets exposed, it is redisplayed.
748
*
749
*--------------------------------------------------------------
750
*/
751
752
static void
753
MessageEventProc(clientData, eventPtr)
754
ClientData clientData; /* Information about window. */
755
XEvent *eventPtr; /* Information about event. */
756
{
757
Message *msgPtr = (Message *) clientData;
758
759
if (((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0))
760
|| (eventPtr->type == ConfigureNotify)) {
761
goto redraw;
762
} else if (eventPtr->type == DestroyNotify) {
763
if (msgPtr->tkwin != NULL) {
764
msgPtr->tkwin = NULL;
765
Tcl_DeleteCommand(msgPtr->interp,
766
Tcl_GetCommandName(msgPtr->interp, msgPtr->widgetCmd));
767
}
768
if (msgPtr->flags & REDRAW_PENDING) {
769
Tcl_CancelIdleCall(DisplayMessage, (ClientData) msgPtr);
770
}
771
Tcl_EventuallyFree((ClientData) msgPtr, DestroyMessage);
772
} else if (eventPtr->type == FocusIn) {
773
if (eventPtr->xfocus.detail != NotifyInferior) {
774
msgPtr->flags |= GOT_FOCUS;
775
if (msgPtr->highlightWidth > 0) {
776
goto redraw;
777
}
778
}
779
} else if (eventPtr->type == FocusOut) {
780
if (eventPtr->xfocus.detail != NotifyInferior) {
781
msgPtr->flags &= ~GOT_FOCUS;
782
if (msgPtr->highlightWidth > 0) {
783
goto redraw;
784
}
785
}
786
}
787
return;
788
789
redraw:
790
if ((msgPtr->tkwin != NULL) && !(msgPtr->flags & REDRAW_PENDING)) {
791
Tcl_DoWhenIdle(DisplayMessage, (ClientData) msgPtr);
792
msgPtr->flags |= REDRAW_PENDING;
793
}
794
}
795
796
/*
797
*----------------------------------------------------------------------
798
*
799
* MessageCmdDeletedProc --
800
*
801
* This procedure is invoked when a widget command is deleted. If
802
* the widget isn't already in the process of being destroyed,
803
* this command destroys it.
804
*
805
* Results:
806
* None.
807
*
808
* Side effects:
809
* The widget is destroyed.
810
*
811
*----------------------------------------------------------------------
812
*/
813
814
static void
815
MessageCmdDeletedProc(clientData)
816
ClientData clientData; /* Pointer to widget record for widget. */
817
{
818
Message *msgPtr = (Message *) clientData;
819
Tk_Window tkwin = msgPtr->tkwin;
820
821
/*
822
* This procedure could be invoked either because the window was
823
* destroyed and the command was then deleted (in which case tkwin
824
* is NULL) or because the command was deleted, and then this procedure
825
* destroys the widget.
826
*/
827
828
if (tkwin != NULL) {
829
msgPtr->tkwin = NULL;
830
Tk_DestroyWindow(tkwin);
831
}
832
}
833
834
/*
835
*--------------------------------------------------------------
836
*
837
* MessageTextVarProc --
838
*
839
* This procedure is invoked when someone changes the variable
840
* whose contents are to be displayed in a message.
841
*
842
* Results:
843
* NULL is always returned.
844
*
845
* Side effects:
846
* The text displayed in the message will change to match the
847
* variable.
848
*
849
*--------------------------------------------------------------
850
*/
851
852
/* ARGSUSED */
853
static char *
854
MessageTextVarProc(clientData, interp, name1, name2, flags)
855
ClientData clientData; /* Information about message. */
856
Tcl_Interp *interp; /* Interpreter containing variable. */
857
char *name1; /* Name of variable. */
858
char *name2; /* Second part of variable name. */
859
int flags; /* Information about what happened. */
860
{
861
register Message *msgPtr = (Message *) clientData;
862
char *value;
863
864
/*
865
* If the variable is unset, then immediately recreate it unless
866
* the whole interpreter is going away.
867
*/
868
869
if (flags & TCL_TRACE_UNSETS) {
870
if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
871
Tcl_SetVar(interp, msgPtr->textVarName, msgPtr->string,
872
TCL_GLOBAL_ONLY);
873
Tcl_TraceVar(interp, msgPtr->textVarName,
874
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
875
MessageTextVarProc, clientData);
876
}
877
return (char *) NULL;
878
}
879
880
value = Tcl_GetVar(interp, msgPtr->textVarName, TCL_GLOBAL_ONLY);
881
if (value == NULL) {
882
value = "";
883
}
884
if (msgPtr->string != NULL) {
885
ckfree(msgPtr->string);
886
}
887
msgPtr->numChars = strlen(value);
888
msgPtr->string = (char *) ckalloc((unsigned) (msgPtr->numChars + 1));
889
strcpy(msgPtr->string, value);
890
ComputeMessageGeometry(msgPtr);
891
892
if ((msgPtr->tkwin != NULL) && Tk_IsMapped(msgPtr->tkwin)
893
&& !(msgPtr->flags & REDRAW_PENDING)) {
894
Tcl_DoWhenIdle(DisplayMessage, (ClientData) msgPtr);
895
msgPtr->flags |= REDRAW_PENDING;
896
}
897
return (char *) NULL;
898
}
899
900