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