|
|
1.1 root 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:
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:
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:
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:
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:
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:
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:
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:
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.