OSDN Git Service

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