tkMessage.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:27k
源码类别:

通讯编程

开发平台:

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