1 |
/* $Header$ */ |
2 |
|
3 |
/* |
4 |
* tkMessage.c -- |
5 |
* |
6 |
* This module implements a message widgets for the Tk |
7 |
* toolkit. A message widget displays a multi-line string |
8 |
* in a window according to a particular aspect ratio. |
9 |
* |
10 |
* Copyright (c) 1990-1994 The Regents of the University of California. |
11 |
* Copyright (c) 1994-1997 Sun Microsystems, Inc. |
12 |
* |
13 |
* See the file "license.terms" for information on usage and redistribution |
14 |
* of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
15 |
* |
16 |
* RCS: @(#) $Id: tkmessage.c,v 1.1.1.1 2001/06/13 05:06:15 dtashley Exp $ |
17 |
*/ |
18 |
|
19 |
#include "tkPort.h" |
20 |
#include "default.h" |
21 |
#include "tkInt.h" |
22 |
|
23 |
/* |
24 |
* A data structure of the following type is kept for each message |
25 |
* widget managed by this file: |
26 |
*/ |
27 |
|
28 |
typedef struct { |
29 |
Tk_Window tkwin; /* Window that embodies the message. NULL |
30 |
* means that the window has been destroyed |
31 |
* but the data structures haven't yet been |
32 |
* cleaned up.*/ |
33 |
Display *display; /* Display containing widget. Used, among |
34 |
* other things, so that resources can be |
35 |
* freed even after tkwin has gone away. */ |
36 |
Tcl_Interp *interp; /* Interpreter associated with message. */ |
37 |
Tcl_Command widgetCmd; /* Token for message's widget command. */ |
38 |
|
39 |
/* |
40 |
* Information used when displaying widget: |
41 |
*/ |
42 |
|
43 |
char *string; /* String displayed in message. */ |
44 |
int numChars; /* Number of characters in string, not |
45 |
* including terminating NULL. */ |
46 |
char *textVarName; /* Name of variable (malloc'ed) or NULL. |
47 |
* If non-NULL, message displays the contents |
48 |
* of this variable. */ |
49 |
Tk_3DBorder border; /* Structure used to draw 3-D border and |
50 |
* background. NULL means a border hasn't |
51 |
* been created yet. */ |
52 |
int borderWidth; /* Width of border. */ |
53 |
int relief; /* 3-D effect: TK_RELIEF_RAISED, etc. */ |
54 |
int highlightWidth; /* Width in pixels of highlight to draw |
55 |
* around widget when it has the focus. |
56 |
* <= 0 means don't draw a highlight. */ |
57 |
XColor *highlightBgColorPtr; |
58 |
/* Color for drawing traversal highlight |
59 |
* area when highlight is off. */ |
60 |
XColor *highlightColorPtr; /* Color for drawing traversal highlight. */ |
61 |
Tk_Font tkfont; /* Information about text font, or NULL. */ |
62 |
XColor *fgColorPtr; /* Foreground color in normal mode. */ |
63 |
int padX, padY; /* User-requested extra space around text. */ |
64 |
int width; /* User-requested width, in pixels. 0 means |
65 |
* compute width using aspect ratio below. */ |
66 |
int aspect; /* Desired aspect ratio for window |
67 |
* (100*width/height). */ |
68 |
int msgWidth; /* Width in pixels needed to display |
69 |
* message. */ |
70 |
int msgHeight; /* Height in pixels needed to display |
71 |
* message. */ |
72 |
Tk_Anchor anchor; /* Where to position text within window region |
73 |
* if window is larger or smaller than |
74 |
* needed. */ |
75 |
Tk_Justify justify; /* Justification for text. */ |
76 |
|
77 |
GC textGC; /* GC for drawing text in normal mode. */ |
78 |
Tk_TextLayout textLayout; /* Saved layout information. */ |
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, tkfont), 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 MessageWorldChanged _ANSI_ARGS_(( |
178 |
ClientData instanceData)); |
179 |
static void ComputeMessageGeometry _ANSI_ARGS_((Message *msgPtr)); |
180 |
static int ConfigureMessage _ANSI_ARGS_((Tcl_Interp *interp, |
181 |
Message *msgPtr, int argc, char **argv, |
182 |
int flags)); |
183 |
static void DestroyMessage _ANSI_ARGS_((char *memPtr)); |
184 |
static void DisplayMessage _ANSI_ARGS_((ClientData clientData)); |
185 |
|
186 |
/* |
187 |
* The structure below defines message class behavior by means of procedures |
188 |
* that can be invoked from generic window code. |
189 |
*/ |
190 |
|
191 |
static TkClassProcs messageClass = { |
192 |
NULL, /* createProc. */ |
193 |
MessageWorldChanged, /* geometryProc. */ |
194 |
NULL /* modalProc. */ |
195 |
}; |
196 |
|
197 |
|
198 |
/* |
199 |
*-------------------------------------------------------------- |
200 |
* |
201 |
* Tk_MessageCmd -- |
202 |
* |
203 |
* This procedure is invoked to process the "message" Tcl |
204 |
* command. See the user documentation for details on what |
205 |
* it does. |
206 |
* |
207 |
* Results: |
208 |
* A standard Tcl result. |
209 |
* |
210 |
* Side effects: |
211 |
* See the user documentation. |
212 |
* |
213 |
*-------------------------------------------------------------- |
214 |
*/ |
215 |
|
216 |
int |
217 |
Tk_MessageCmd(clientData, interp, argc, argv) |
218 |
ClientData clientData; /* Main window associated with |
219 |
* interpreter. */ |
220 |
Tcl_Interp *interp; /* Current interpreter. */ |
221 |
int argc; /* Number of arguments. */ |
222 |
char **argv; /* Argument strings. */ |
223 |
{ |
224 |
register Message *msgPtr; |
225 |
Tk_Window new; |
226 |
Tk_Window tkwin = (Tk_Window) clientData; |
227 |
|
228 |
if (argc < 2) { |
229 |
Tcl_AppendResult(interp, "wrong # args: should be \"", |
230 |
argv[0], " pathName ?options?\"", (char *) NULL); |
231 |
return TCL_ERROR; |
232 |
} |
233 |
|
234 |
new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL); |
235 |
if (new == NULL) { |
236 |
return TCL_ERROR; |
237 |
} |
238 |
|
239 |
msgPtr = (Message *) ckalloc(sizeof(Message)); |
240 |
msgPtr->tkwin = new; |
241 |
msgPtr->display = Tk_Display(new); |
242 |
msgPtr->interp = interp; |
243 |
msgPtr->widgetCmd = Tcl_CreateCommand(interp, Tk_PathName(msgPtr->tkwin), |
244 |
MessageWidgetCmd, (ClientData) msgPtr, MessageCmdDeletedProc); |
245 |
msgPtr->textLayout = NULL; |
246 |
msgPtr->string = NULL; |
247 |
msgPtr->numChars = 0; |
248 |
msgPtr->textVarName = NULL; |
249 |
msgPtr->border = NULL; |
250 |
msgPtr->borderWidth = 0; |
251 |
msgPtr->relief = TK_RELIEF_FLAT; |
252 |
msgPtr->highlightWidth = 0; |
253 |
msgPtr->highlightBgColorPtr = NULL; |
254 |
msgPtr->highlightColorPtr = NULL; |
255 |
msgPtr->tkfont = NULL; |
256 |
msgPtr->fgColorPtr = NULL; |
257 |
msgPtr->textGC = None; |
258 |
msgPtr->padX = 0; |
259 |
msgPtr->padY = 0; |
260 |
msgPtr->anchor = TK_ANCHOR_CENTER; |
261 |
msgPtr->width = 0; |
262 |
msgPtr->aspect = 150; |
263 |
msgPtr->msgWidth = 0; |
264 |
msgPtr->msgHeight = 0; |
265 |
msgPtr->justify = TK_JUSTIFY_LEFT; |
266 |
msgPtr->cursor = None; |
267 |
msgPtr->takeFocus = NULL; |
268 |
msgPtr->flags = 0; |
269 |
|
270 |
Tk_SetClass(msgPtr->tkwin, "Message"); |
271 |
TkSetClassProcs(msgPtr->tkwin, &messageClass, (ClientData) msgPtr); |
272 |
Tk_CreateEventHandler(msgPtr->tkwin, |
273 |
ExposureMask|StructureNotifyMask|FocusChangeMask, |
274 |
MessageEventProc, (ClientData) msgPtr); |
275 |
if (ConfigureMessage(interp, msgPtr, argc-2, argv+2, 0) != TCL_OK) { |
276 |
goto error; |
277 |
} |
278 |
|
279 |
Tcl_SetResult(interp, Tk_PathName(msgPtr->tkwin), TCL_STATIC); |
280 |
return TCL_OK; |
281 |
|
282 |
error: |
283 |
Tk_DestroyWindow(msgPtr->tkwin); |
284 |
return TCL_ERROR; |
285 |
} |
286 |
|
287 |
/* |
288 |
*-------------------------------------------------------------- |
289 |
* |
290 |
* MessageWidgetCmd -- |
291 |
* |
292 |
* This procedure is invoked to process the Tcl command |
293 |
* that corresponds to a widget managed by this module. |
294 |
* See the user documentation for details on what it does. |
295 |
* |
296 |
* Results: |
297 |
* A standard Tcl result. |
298 |
* |
299 |
* Side effects: |
300 |
* See the user documentation. |
301 |
* |
302 |
*-------------------------------------------------------------- |
303 |
*/ |
304 |
|
305 |
static int |
306 |
MessageWidgetCmd(clientData, interp, argc, argv) |
307 |
ClientData clientData; /* Information about message widget. */ |
308 |
Tcl_Interp *interp; /* Current interpreter. */ |
309 |
int argc; /* Number of arguments. */ |
310 |
char **argv; /* Argument strings. */ |
311 |
{ |
312 |
register Message *msgPtr = (Message *) clientData; |
313 |
size_t length; |
314 |
int c; |
315 |
|
316 |
if (argc < 2) { |
317 |
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |
318 |
" option ?arg arg ...?\"", (char *) NULL); |
319 |
return TCL_ERROR; |
320 |
} |
321 |
c = argv[1][0]; |
322 |
length = strlen(argv[1]); |
323 |
if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0) |
324 |
&& (length >= 2)) { |
325 |
if (argc != 3) { |
326 |
Tcl_AppendResult(interp, "wrong # args: should be \"", |
327 |
argv[0], " cget option\"", |
328 |
(char *) NULL); |
329 |
return TCL_ERROR; |
330 |
} |
331 |
return Tk_ConfigureValue(interp, msgPtr->tkwin, configSpecs, |
332 |
(char *) msgPtr, argv[2], 0); |
333 |
} else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0) |
334 |
&& (length >= 2)) { |
335 |
if (argc == 2) { |
336 |
return Tk_ConfigureInfo(interp, msgPtr->tkwin, configSpecs, |
337 |
(char *) msgPtr, (char *) NULL, 0); |
338 |
} else if (argc == 3) { |
339 |
return Tk_ConfigureInfo(interp, msgPtr->tkwin, configSpecs, |
340 |
(char *) msgPtr, argv[2], 0); |
341 |
} else { |
342 |
return ConfigureMessage(interp, msgPtr, argc-2, argv+2, |
343 |
TK_CONFIG_ARGV_ONLY); |
344 |
} |
345 |
} else { |
346 |
Tcl_AppendResult(interp, "bad option \"", argv[1], |
347 |
"\": must be cget or configure", (char *) NULL); |
348 |
return TCL_ERROR; |
349 |
} |
350 |
} |
351 |
|
352 |
/* |
353 |
*---------------------------------------------------------------------- |
354 |
* |
355 |
* DestroyMessage -- |
356 |
* |
357 |
* This procedure is invoked by Tcl_EventuallyFree or Tcl_Release |
358 |
* to clean up the internal structure of a message at a safe time |
359 |
* (when no-one is using it anymore). |
360 |
* |
361 |
* Results: |
362 |
* None. |
363 |
* |
364 |
* Side effects: |
365 |
* Everything associated with the message is freed up. |
366 |
* |
367 |
*---------------------------------------------------------------------- |
368 |
*/ |
369 |
|
370 |
static void |
371 |
DestroyMessage(memPtr) |
372 |
char *memPtr; /* Info about message widget. */ |
373 |
{ |
374 |
register Message *msgPtr = (Message *) memPtr; |
375 |
|
376 |
/* |
377 |
* Free up all the stuff that requires special handling, then |
378 |
* let Tk_FreeOptions handle all the standard option-related |
379 |
* stuff. |
380 |
*/ |
381 |
|
382 |
Tk_FreeTextLayout(msgPtr->textLayout); |
383 |
if (msgPtr->textVarName != NULL) { |
384 |
Tcl_UntraceVar(msgPtr->interp, msgPtr->textVarName, |
385 |
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, |
386 |
MessageTextVarProc, (ClientData) msgPtr); |
387 |
} |
388 |
if (msgPtr->textGC != None) { |
389 |
Tk_FreeGC(msgPtr->display, msgPtr->textGC); |
390 |
} |
391 |
Tk_FreeOptions(configSpecs, (char *) msgPtr, msgPtr->display, 0); |
392 |
ckfree((char *) msgPtr); |
393 |
} |
394 |
|
395 |
/* |
396 |
*---------------------------------------------------------------------- |
397 |
* |
398 |
* ConfigureMessage -- |
399 |
* |
400 |
* This procedure is called to process an argv/argc list, plus |
401 |
* the Tk option database, in order to configure (or |
402 |
* reconfigure) a message widget. |
403 |
* |
404 |
* Results: |
405 |
* The return value is a standard Tcl result. If TCL_ERROR is |
406 |
* returned, then the interp's result contains an error message. |
407 |
* |
408 |
* Side effects: |
409 |
* Configuration information, such as text string, colors, font, |
410 |
* etc. get set for msgPtr; old resources get freed, if there |
411 |
* were any. |
412 |
* |
413 |
*---------------------------------------------------------------------- |
414 |
*/ |
415 |
|
416 |
static int |
417 |
ConfigureMessage(interp, msgPtr, argc, argv, flags) |
418 |
Tcl_Interp *interp; /* Used for error reporting. */ |
419 |
register Message *msgPtr; /* Information about widget; may or may |
420 |
* not already have values for some fields. */ |
421 |
int argc; /* Number of valid entries in argv. */ |
422 |
char **argv; /* Arguments. */ |
423 |
int flags; /* Flags to pass to Tk_ConfigureWidget. */ |
424 |
{ |
425 |
/* |
426 |
* Eliminate any existing trace on a variable monitored by the message. |
427 |
*/ |
428 |
|
429 |
if (msgPtr->textVarName != NULL) { |
430 |
Tcl_UntraceVar(interp, msgPtr->textVarName, |
431 |
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, |
432 |
MessageTextVarProc, (ClientData) msgPtr); |
433 |
} |
434 |
|
435 |
if (Tk_ConfigureWidget(interp, msgPtr->tkwin, configSpecs, |
436 |
argc, argv, (char *) msgPtr, flags) != TCL_OK) { |
437 |
return TCL_ERROR; |
438 |
} |
439 |
|
440 |
/* |
441 |
* If the message is to display the value of a variable, then set up |
442 |
* a trace on the variable's value, create the variable if it doesn't |
443 |
* exist, and fetch its current value. |
444 |
*/ |
445 |
|
446 |
if (msgPtr->textVarName != NULL) { |
447 |
char *value; |
448 |
|
449 |
value = Tcl_GetVar(interp, msgPtr->textVarName, TCL_GLOBAL_ONLY); |
450 |
if (value == NULL) { |
451 |
Tcl_SetVar(interp, msgPtr->textVarName, msgPtr->string, |
452 |
TCL_GLOBAL_ONLY); |
453 |
} else { |
454 |
if (msgPtr->string != NULL) { |
455 |
ckfree(msgPtr->string); |
456 |
} |
457 |
msgPtr->string = strcpy(ckalloc(strlen(value) + 1), value); |
458 |
} |
459 |
Tcl_TraceVar(interp, msgPtr->textVarName, |
460 |
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, |
461 |
MessageTextVarProc, (ClientData) msgPtr); |
462 |
} |
463 |
|
464 |
/* |
465 |
* A few other options need special processing, such as setting |
466 |
* the background from a 3-D border or handling special defaults |
467 |
* that couldn't be specified to Tk_ConfigureWidget. |
468 |
*/ |
469 |
|
470 |
msgPtr->numChars = Tcl_NumUtfChars(msgPtr->string, -1); |
471 |
|
472 |
if (msgPtr->highlightWidth < 0) { |
473 |
msgPtr->highlightWidth = 0; |
474 |
} |
475 |
|
476 |
MessageWorldChanged((ClientData) msgPtr); |
477 |
return TCL_OK; |
478 |
} |
479 |
|
480 |
/* |
481 |
*--------------------------------------------------------------------------- |
482 |
* |
483 |
* MessageWorldChanged -- |
484 |
* |
485 |
* This procedure is called when the world has changed in some |
486 |
* way and the widget needs to recompute all its graphics contexts |
487 |
* and determine its new geometry. |
488 |
* |
489 |
* Results: |
490 |
* None. |
491 |
* |
492 |
* Side effects: |
493 |
* Message will be relayed out and redisplayed. |
494 |
* |
495 |
*--------------------------------------------------------------------------- |
496 |
*/ |
497 |
|
498 |
static void |
499 |
MessageWorldChanged(instanceData) |
500 |
ClientData instanceData; /* Information about widget. */ |
501 |
{ |
502 |
XGCValues gcValues; |
503 |
GC gc = None; |
504 |
Tk_FontMetrics fm; |
505 |
Message *msgPtr; |
506 |
|
507 |
msgPtr = (Message *) instanceData; |
508 |
|
509 |
if (msgPtr->border != NULL) { |
510 |
Tk_SetBackgroundFromBorder(msgPtr->tkwin, msgPtr->border); |
511 |
} |
512 |
|
513 |
gcValues.font = Tk_FontId(msgPtr->tkfont); |
514 |
gcValues.foreground = msgPtr->fgColorPtr->pixel; |
515 |
gc = Tk_GetGC(msgPtr->tkwin, GCForeground | GCFont, &gcValues); |
516 |
if (msgPtr->textGC != None) { |
517 |
Tk_FreeGC(msgPtr->display, msgPtr->textGC); |
518 |
} |
519 |
msgPtr->textGC = gc; |
520 |
|
521 |
Tk_GetFontMetrics(msgPtr->tkfont, &fm); |
522 |
if (msgPtr->padX < 0) { |
523 |
msgPtr->padX = fm.ascent / 2; |
524 |
} |
525 |
if (msgPtr->padY == -1) { |
526 |
msgPtr->padY = fm.ascent / 4; |
527 |
} |
528 |
|
529 |
/* |
530 |
* Recompute the desired geometry for the window, and arrange for |
531 |
* the window to be redisplayed. |
532 |
*/ |
533 |
|
534 |
ComputeMessageGeometry(msgPtr); |
535 |
if ((msgPtr->tkwin != NULL) && Tk_IsMapped(msgPtr->tkwin) |
536 |
&& !(msgPtr->flags & REDRAW_PENDING)) { |
537 |
Tcl_DoWhenIdle(DisplayMessage, (ClientData) msgPtr); |
538 |
msgPtr->flags |= REDRAW_PENDING; |
539 |
} |
540 |
} |
541 |
|
542 |
/* |
543 |
*-------------------------------------------------------------- |
544 |
* |
545 |
* ComputeMessageGeometry -- |
546 |
* |
547 |
* Compute the desired geometry for a message window, |
548 |
* taking into account the desired aspect ratio for the |
549 |
* window. |
550 |
* |
551 |
* Results: |
552 |
* None. |
553 |
* |
554 |
* Side effects: |
555 |
* Tk_GeometryRequest is called to inform the geometry |
556 |
* manager of the desired geometry for this window. |
557 |
* |
558 |
*-------------------------------------------------------------- |
559 |
*/ |
560 |
|
561 |
static void |
562 |
ComputeMessageGeometry(msgPtr) |
563 |
register Message *msgPtr; /* Information about window. */ |
564 |
{ |
565 |
int width, inc, height; |
566 |
int thisWidth, thisHeight, maxWidth; |
567 |
int aspect, lowerBound, upperBound, inset; |
568 |
|
569 |
Tk_FreeTextLayout(msgPtr->textLayout); |
570 |
|
571 |
inset = msgPtr->borderWidth + msgPtr->highlightWidth; |
572 |
|
573 |
/* |
574 |
* Compute acceptable bounds for the final aspect ratio. |
575 |
*/ |
576 |
|
577 |
aspect = msgPtr->aspect/10; |
578 |
if (aspect < 5) { |
579 |
aspect = 5; |
580 |
} |
581 |
lowerBound = msgPtr->aspect - aspect; |
582 |
upperBound = msgPtr->aspect + aspect; |
583 |
|
584 |
/* |
585 |
* Do the computation in multiple passes: start off with |
586 |
* a very wide window, and compute its height. Then change |
587 |
* the width and try again. Reduce the size of the change |
588 |
* and iterate until dimensions are found that approximate |
589 |
* the desired aspect ratio. Or, if the user gave an explicit |
590 |
* width then just use that. |
591 |
*/ |
592 |
|
593 |
if (msgPtr->width > 0) { |
594 |
width = msgPtr->width; |
595 |
inc = 0; |
596 |
} else { |
597 |
width = WidthOfScreen(Tk_Screen(msgPtr->tkwin))/2; |
598 |
inc = width/2; |
599 |
} |
600 |
|
601 |
for ( ; ; inc /= 2) { |
602 |
msgPtr->textLayout = Tk_ComputeTextLayout(msgPtr->tkfont, |
603 |
msgPtr->string, msgPtr->numChars, width, msgPtr->justify, |
604 |
0, &thisWidth, &thisHeight); |
605 |
maxWidth = thisWidth + 2 * (inset + msgPtr->padX); |
606 |
height = thisHeight + 2 * (inset + msgPtr->padY); |
607 |
|
608 |
if (inc <= 2) { |
609 |
break; |
610 |
} |
611 |
aspect = (100 * maxWidth) / height; |
612 |
|
613 |
if (aspect < lowerBound) { |
614 |
width += inc; |
615 |
} else if (aspect > upperBound) { |
616 |
width -= inc; |
617 |
} else { |
618 |
break; |
619 |
} |
620 |
Tk_FreeTextLayout(msgPtr->textLayout); |
621 |
} |
622 |
msgPtr->msgWidth = thisWidth; |
623 |
msgPtr->msgHeight = thisHeight; |
624 |
Tk_GeometryRequest(msgPtr->tkwin, maxWidth, height); |
625 |
Tk_SetInternalBorder(msgPtr->tkwin, inset); |
626 |
} |
627 |
|
628 |
/* |
629 |
*-------------------------------------------------------------- |
630 |
* |
631 |
* DisplayMessage -- |
632 |
* |
633 |
* This procedure redraws the contents of a message window. |
634 |
* |
635 |
* Results: |
636 |
* None. |
637 |
* |
638 |
* Side effects: |
639 |
* Information appears on the screen. |
640 |
* |
641 |
*-------------------------------------------------------------- |
642 |
*/ |
643 |
|
644 |
static void |
645 |
DisplayMessage(clientData) |
646 |
ClientData clientData; /* Information about window. */ |
647 |
{ |
648 |
register Message *msgPtr = (Message *) clientData; |
649 |
register Tk_Window tkwin = msgPtr->tkwin; |
650 |
int x, y; |
651 |
int borderWidth = msgPtr->highlightWidth; |
652 |
|
653 |
msgPtr->flags &= ~REDRAW_PENDING; |
654 |
if ((msgPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { |
655 |
return; |
656 |
} |
657 |
if (msgPtr->border != NULL) { |
658 |
borderWidth += msgPtr->borderWidth; |
659 |
} |
660 |
if (msgPtr->relief == TK_RELIEF_FLAT) { |
661 |
borderWidth = msgPtr->highlightWidth; |
662 |
} |
663 |
Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), msgPtr->border, |
664 |
borderWidth, borderWidth, |
665 |
Tk_Width(tkwin) - 2 * borderWidth, |
666 |
Tk_Height(tkwin) - 2 * borderWidth, |
667 |
0, TK_RELIEF_FLAT); |
668 |
|
669 |
/* |
670 |
* Compute starting y-location for message based on message size |
671 |
* and anchor option. |
672 |
*/ |
673 |
|
674 |
TkComputeAnchor(msgPtr->anchor, tkwin, msgPtr->padX, msgPtr->padY, |
675 |
msgPtr->msgWidth, msgPtr->msgHeight, &x, &y); |
676 |
Tk_DrawTextLayout(Tk_Display(tkwin), Tk_WindowId(tkwin), msgPtr->textGC, |
677 |
msgPtr->textLayout, x, y, 0, -1); |
678 |
|
679 |
if (borderWidth > msgPtr->highlightWidth) { |
680 |
Tk_Draw3DRectangle(tkwin, Tk_WindowId(tkwin), msgPtr->border, |
681 |
msgPtr->highlightWidth, msgPtr->highlightWidth, |
682 |
Tk_Width(tkwin) - 2*msgPtr->highlightWidth, |
683 |
Tk_Height(tkwin) - 2*msgPtr->highlightWidth, |
684 |
msgPtr->borderWidth, msgPtr->relief); |
685 |
} |
686 |
if (msgPtr->highlightWidth != 0) { |
687 |
GC fgGC, bgGC; |
688 |
|
689 |
bgGC = Tk_GCForColor(msgPtr->highlightBgColorPtr, Tk_WindowId(tkwin)); |
690 |
if (msgPtr->flags & GOT_FOCUS) { |
691 |
fgGC = Tk_GCForColor(msgPtr->highlightColorPtr, Tk_WindowId(tkwin)); |
692 |
TkpDrawHighlightBorder(tkwin, fgGC, bgGC, msgPtr->highlightWidth, |
693 |
Tk_WindowId(tkwin)); |
694 |
} else { |
695 |
TkpDrawHighlightBorder(tkwin, bgGC, bgGC, msgPtr->highlightWidth, |
696 |
Tk_WindowId(tkwin)); |
697 |
} |
698 |
} |
699 |
} |
700 |
|
701 |
/* |
702 |
*-------------------------------------------------------------- |
703 |
* |
704 |
* MessageEventProc -- |
705 |
* |
706 |
* This procedure is invoked by the Tk dispatcher for various |
707 |
* events on messages. |
708 |
* |
709 |
* Results: |
710 |
* None. |
711 |
* |
712 |
* Side effects: |
713 |
* When the window gets deleted, internal structures get |
714 |
* cleaned up. When it gets exposed, it is redisplayed. |
715 |
* |
716 |
*-------------------------------------------------------------- |
717 |
*/ |
718 |
|
719 |
static void |
720 |
MessageEventProc(clientData, eventPtr) |
721 |
ClientData clientData; /* Information about window. */ |
722 |
XEvent *eventPtr; /* Information about event. */ |
723 |
{ |
724 |
Message *msgPtr = (Message *) clientData; |
725 |
|
726 |
if (((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) |
727 |
|| (eventPtr->type == ConfigureNotify)) { |
728 |
goto redraw; |
729 |
} else if (eventPtr->type == DestroyNotify) { |
730 |
if (msgPtr->tkwin != NULL) { |
731 |
msgPtr->tkwin = NULL; |
732 |
Tcl_DeleteCommandFromToken(msgPtr->interp, msgPtr->widgetCmd); |
733 |
} |
734 |
if (msgPtr->flags & REDRAW_PENDING) { |
735 |
Tcl_CancelIdleCall(DisplayMessage, (ClientData) msgPtr); |
736 |
} |
737 |
Tcl_EventuallyFree((ClientData) msgPtr, DestroyMessage); |
738 |
} else if (eventPtr->type == FocusIn) { |
739 |
if (eventPtr->xfocus.detail != NotifyInferior) { |
740 |
msgPtr->flags |= GOT_FOCUS; |
741 |
if (msgPtr->highlightWidth > 0) { |
742 |
goto redraw; |
743 |
} |
744 |
} |
745 |
} else if (eventPtr->type == FocusOut) { |
746 |
if (eventPtr->xfocus.detail != NotifyInferior) { |
747 |
msgPtr->flags &= ~GOT_FOCUS; |
748 |
if (msgPtr->highlightWidth > 0) { |
749 |
goto redraw; |
750 |
} |
751 |
} |
752 |
} |
753 |
return; |
754 |
|
755 |
redraw: |
756 |
if ((msgPtr->tkwin != NULL) && !(msgPtr->flags & REDRAW_PENDING)) { |
757 |
Tcl_DoWhenIdle(DisplayMessage, (ClientData) msgPtr); |
758 |
msgPtr->flags |= REDRAW_PENDING; |
759 |
} |
760 |
} |
761 |
|
762 |
/* |
763 |
*---------------------------------------------------------------------- |
764 |
* |
765 |
* MessageCmdDeletedProc -- |
766 |
* |
767 |
* This procedure is invoked when a widget command is deleted. If |
768 |
* the widget isn't already in the process of being destroyed, |
769 |
* this command destroys it. |
770 |
* |
771 |
* Results: |
772 |
* None. |
773 |
* |
774 |
* Side effects: |
775 |
* The widget is destroyed. |
776 |
* |
777 |
*---------------------------------------------------------------------- |
778 |
*/ |
779 |
|
780 |
static void |
781 |
MessageCmdDeletedProc(clientData) |
782 |
ClientData clientData; /* Pointer to widget record for widget. */ |
783 |
{ |
784 |
Message *msgPtr = (Message *) clientData; |
785 |
Tk_Window tkwin = msgPtr->tkwin; |
786 |
|
787 |
/* |
788 |
* This procedure could be invoked either because the window was |
789 |
* destroyed and the command was then deleted (in which case tkwin |
790 |
* is NULL) or because the command was deleted, and then this procedure |
791 |
* destroys the widget. |
792 |
*/ |
793 |
|
794 |
if (tkwin != NULL) { |
795 |
msgPtr->tkwin = NULL; |
796 |
Tk_DestroyWindow(tkwin); |
797 |
} |
798 |
} |
799 |
|
800 |
/* |
801 |
*-------------------------------------------------------------- |
802 |
* |
803 |
* MessageTextVarProc -- |
804 |
* |
805 |
* This procedure is invoked when someone changes the variable |
806 |
* whose contents are to be displayed in a message. |
807 |
* |
808 |
* Results: |
809 |
* NULL is always returned. |
810 |
* |
811 |
* Side effects: |
812 |
* The text displayed in the message will change to match the |
813 |
* variable. |
814 |
* |
815 |
*-------------------------------------------------------------- |
816 |
*/ |
817 |
|
818 |
/* ARGSUSED */ |
819 |
static char * |
820 |
MessageTextVarProc(clientData, interp, name1, name2, flags) |
821 |
ClientData clientData; /* Information about message. */ |
822 |
Tcl_Interp *interp; /* Interpreter containing variable. */ |
823 |
char *name1; /* Name of variable. */ |
824 |
char *name2; /* Second part of variable name. */ |
825 |
int flags; /* Information about what happened. */ |
826 |
{ |
827 |
register Message *msgPtr = (Message *) clientData; |
828 |
char *value; |
829 |
|
830 |
/* |
831 |
* If the variable is unset, then immediately recreate it unless |
832 |
* the whole interpreter is going away. |
833 |
*/ |
834 |
|
835 |
if (flags & TCL_TRACE_UNSETS) { |
836 |
if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) { |
837 |
Tcl_SetVar(interp, msgPtr->textVarName, msgPtr->string, |
838 |
TCL_GLOBAL_ONLY); |
839 |
Tcl_TraceVar(interp, msgPtr->textVarName, |
840 |
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, |
841 |
MessageTextVarProc, clientData); |
842 |
} |
843 |
return (char *) NULL; |
844 |
} |
845 |
|
846 |
value = Tcl_GetVar(interp, msgPtr->textVarName, TCL_GLOBAL_ONLY); |
847 |
if (value == NULL) { |
848 |
value = ""; |
849 |
} |
850 |
if (msgPtr->string != NULL) { |
851 |
ckfree(msgPtr->string); |
852 |
} |
853 |
msgPtr->numChars = Tcl_NumUtfChars(value, -1); |
854 |
msgPtr->string = (char *) ckalloc((unsigned) (strlen(value) + 1)); |
855 |
strcpy(msgPtr->string, value); |
856 |
ComputeMessageGeometry(msgPtr); |
857 |
|
858 |
if ((msgPtr->tkwin != NULL) && Tk_IsMapped(msgPtr->tkwin) |
859 |
&& !(msgPtr->flags & REDRAW_PENDING)) { |
860 |
Tcl_DoWhenIdle(DisplayMessage, (ClientData) msgPtr); |
861 |
msgPtr->flags |= REDRAW_PENDING; |
862 |
} |
863 |
return (char *) NULL; |
864 |
} |
865 |
|
866 |
/* End of tkmessage.c */ |