|
|
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.