1 |
dashley |
71 |
/* $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 */ |