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

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tkTextWind.c --
  3.  *
  4.  * This file contains code that allows arbitrary windows to be
  5.  * nested inside text widgets.  It also implements the "window"
  6.  * widget command for texts.
  7.  *
  8.  * Copyright (c) 1994 The Regents of the University of California.
  9.  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  *
  14.  * RCS: @(#) $Id: tkTextWind.c,v 1.6 2002/08/05 04:30:40 dgp Exp $
  15.  */
  16. #include "tk.h"
  17. #include "tkText.h"
  18. #include "tkPort.h"
  19. /*
  20.  * The following structure is the official type record for the
  21.  * embedded window geometry manager:
  22.  */
  23. static void EmbWinRequestProc _ANSI_ARGS_((ClientData clientData,
  24.     Tk_Window tkwin));
  25. static void EmbWinLostSlaveProc _ANSI_ARGS_((ClientData clientData,
  26.     Tk_Window tkwin));
  27. static Tk_GeomMgr textGeomType = {
  28.     "text", /* name */
  29.     EmbWinRequestProc, /* requestProc */
  30.     EmbWinLostSlaveProc, /* lostSlaveProc */
  31. };
  32. /*
  33.  * Definitions for alignment values:
  34.  */
  35. #define ALIGN_BOTTOM 0
  36. #define ALIGN_CENTER 1
  37. #define ALIGN_TOP 2
  38. #define ALIGN_BASELINE 3
  39. /*
  40.  * Macro that determines the size of an embedded window segment:
  41.  */
  42. #define EW_SEG_SIZE ((unsigned) (Tk_Offset(TkTextSegment, body) 
  43. + sizeof(TkTextEmbWindow)))
  44. /*
  45.  * Prototypes for procedures defined in this file:
  46.  */
  47. static int AlignParseProc _ANSI_ARGS_((ClientData clientData,
  48.     Tcl_Interp *interp, Tk_Window tkwin,
  49.     CONST char *value, char *widgRec, int offset));
  50. static char * AlignPrintProc _ANSI_ARGS_((ClientData clientData,
  51.     Tk_Window tkwin, char *widgRec, int offset,
  52.     Tcl_FreeProc **freeProcPtr));
  53. static TkTextSegment * EmbWinCleanupProc _ANSI_ARGS_((TkTextSegment *segPtr,
  54.     TkTextLine *linePtr));
  55. static void EmbWinCheckProc _ANSI_ARGS_((TkTextSegment *segPtr,
  56.     TkTextLine *linePtr));
  57. static void EmbWinBboxProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
  58.     int index, int y, int lineHeight, int baseline,
  59.     int *xPtr, int *yPtr, int *widthPtr,
  60.     int *heightPtr));
  61. static int EmbWinConfigure _ANSI_ARGS_((TkText *textPtr,
  62.     TkTextSegment *ewPtr, int argc, CONST char **argv));
  63. static void EmbWinDelayedUnmap _ANSI_ARGS_((
  64.     ClientData clientData));
  65. static int EmbWinDeleteProc _ANSI_ARGS_((TkTextSegment *segPtr,
  66.     TkTextLine *linePtr, int treeGone));
  67. static void EmbWinDisplayProc _ANSI_ARGS_((
  68.     TkTextDispChunk *chunkPtr, int x, int y,
  69.     int lineHeight, int baseline, Display *display,
  70.     Drawable dst, int screenY));
  71. static int EmbWinLayoutProc _ANSI_ARGS_((TkText *textPtr,
  72.     TkTextIndex *indexPtr, TkTextSegment *segPtr,
  73.     int offset, int maxX, int maxChars,
  74.     int noCharsYet, TkWrapMode wrapMode,
  75.     TkTextDispChunk *chunkPtr));
  76. static void EmbWinStructureProc _ANSI_ARGS_((ClientData clientData,
  77.     XEvent *eventPtr));
  78. static void EmbWinUndisplayProc _ANSI_ARGS_((TkText *textPtr,
  79.     TkTextDispChunk *chunkPtr));
  80. /*
  81.  * The following structure declares the "embedded window" segment type.
  82.  */
  83. static Tk_SegType tkTextEmbWindowType = {
  84.     "window", /* name */
  85.     0, /* leftGravity */
  86.     (Tk_SegSplitProc *) NULL, /* splitProc */
  87.     EmbWinDeleteProc, /* deleteProc */
  88.     EmbWinCleanupProc, /* cleanupProc */
  89.     (Tk_SegLineChangeProc *) NULL, /* lineChangeProc */
  90.     EmbWinLayoutProc, /* layoutProc */
  91.     EmbWinCheckProc /* checkProc */
  92. };
  93. /*
  94.  * Information used for parsing window configuration options:
  95.  */
  96. static Tk_CustomOption alignOption = {AlignParseProc, AlignPrintProc,
  97. (ClientData) NULL};
  98. static Tk_ConfigSpec configSpecs[] = {
  99.     {TK_CONFIG_CUSTOM, "-align", (char *) NULL, (char *) NULL,
  100. "center", 0, TK_CONFIG_DONT_SET_DEFAULT, &alignOption},
  101.     {TK_CONFIG_STRING, "-create", (char *) NULL, (char *) NULL,
  102. (char *) NULL, Tk_Offset(TkTextEmbWindow, create),
  103. TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK},
  104.     {TK_CONFIG_INT, "-padx", (char *) NULL, (char *) NULL,
  105. "0", Tk_Offset(TkTextEmbWindow, padX),
  106. TK_CONFIG_DONT_SET_DEFAULT},
  107.     {TK_CONFIG_INT, "-pady", (char *) NULL, (char *) NULL,
  108. "0", Tk_Offset(TkTextEmbWindow, padY),
  109. TK_CONFIG_DONT_SET_DEFAULT},
  110.     {TK_CONFIG_BOOLEAN, "-stretch", (char *) NULL, (char *) NULL,
  111. "0", Tk_Offset(TkTextEmbWindow, stretch),
  112. TK_CONFIG_DONT_SET_DEFAULT},
  113.     {TK_CONFIG_WINDOW, "-window", (char *) NULL, (char *) NULL,
  114. (char *) NULL, Tk_Offset(TkTextEmbWindow, tkwin),
  115. TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK},
  116.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  117. (char *) NULL, 0, 0}
  118. };
  119. /*
  120.  *--------------------------------------------------------------
  121.  *
  122.  * TkTextWindowCmd --
  123.  *
  124.  * This procedure implements the "window" widget command
  125.  * for text widgets.  See the user documentation for details
  126.  * on what it does.
  127.  *
  128.  * Results:
  129.  * A standard Tcl result or error.
  130.  *
  131.  * Side effects:
  132.  * See the user documentation.
  133.  *
  134.  *--------------------------------------------------------------
  135.  */
  136. int
  137. TkTextWindowCmd(textPtr, interp, argc, argv)
  138.     register TkText *textPtr; /* Information about text widget. */
  139.     Tcl_Interp *interp; /* Current interpreter. */
  140.     int argc; /* Number of arguments. */
  141.     CONST char **argv; /* Argument strings.  Someone else has already
  142.  * parsed this command enough to know that
  143.  * argv[1] is "window". */
  144. {
  145.     size_t length;
  146.     register TkTextSegment *ewPtr;
  147.     if (argc < 3) {
  148. Tcl_AppendResult(interp, "wrong # args: should be "",
  149. argv[0], " window option ?arg arg ...?"", (char *) NULL);
  150. return TCL_ERROR;
  151.     }
  152.     length = strlen(argv[2]);
  153.     if ((strncmp(argv[2], "cget", length) == 0) && (length >= 2)) {
  154. TkTextIndex index;
  155. TkTextSegment *ewPtr;
  156. if (argc != 5) {
  157.     Tcl_AppendResult(interp, "wrong # args: should be "",
  158.     argv[0], " window cget index option"",
  159.     (char *) NULL);
  160.     return TCL_ERROR;
  161. }
  162. if (TkTextGetIndex(interp, textPtr, argv[3], &index) != TCL_OK) {
  163.     return TCL_ERROR;
  164. }
  165. ewPtr = TkTextIndexToSeg(&index, (int *) NULL);
  166. if (ewPtr->typePtr != &tkTextEmbWindowType) {
  167.     Tcl_AppendResult(interp, "no embedded window at index "",
  168.     argv[3], """, (char *) NULL);
  169.     return TCL_ERROR;
  170. }
  171. return Tk_ConfigureValue(interp, textPtr->tkwin, configSpecs,
  172. (char *) &ewPtr->body.ew, argv[4], 0);
  173.     } else if ((strncmp(argv[2], "configure", length) == 0) && (length >= 2)) {
  174. TkTextIndex index;
  175. TkTextSegment *ewPtr;
  176. if (argc < 4) {
  177.     Tcl_AppendResult(interp, "wrong # args: should be "",
  178.     argv[0], " window configure index ?option value ...?"",
  179.     (char *) NULL);
  180.     return TCL_ERROR;
  181. }
  182. if (TkTextGetIndex(interp, textPtr, argv[3], &index) != TCL_OK) {
  183.     return TCL_ERROR;
  184. }
  185. ewPtr = TkTextIndexToSeg(&index, (int *) NULL);
  186. if (ewPtr->typePtr != &tkTextEmbWindowType) {
  187.     Tcl_AppendResult(interp, "no embedded window at index "",
  188.     argv[3], """, (char *) NULL);
  189.     return TCL_ERROR;
  190. }
  191. if (argc == 4) {
  192.     return Tk_ConfigureInfo(interp, textPtr->tkwin, configSpecs,
  193.     (char *) &ewPtr->body.ew, (char *) NULL, 0);
  194. } else if (argc == 5) {
  195.     return Tk_ConfigureInfo(interp, textPtr->tkwin, configSpecs,
  196.     (char *) &ewPtr->body.ew, argv[4], 0);
  197. } else {
  198.     TkTextChanged(textPtr, &index, &index);
  199.     return EmbWinConfigure(textPtr, ewPtr, argc-4, argv+4);
  200. }
  201.     } else if ((strncmp(argv[2], "create", length) == 0) && (length >= 2)) {
  202. TkTextIndex index;
  203. int lineIndex;
  204. /*
  205.  * Add a new window.  Find where to put the new window, and
  206.  * mark that position for redisplay.
  207.  */
  208. if (argc < 4) {
  209.     Tcl_AppendResult(interp, "wrong # args: should be "",
  210.     argv[0], " window create index ?option value ...?"",
  211.     (char *) NULL);
  212.     return TCL_ERROR;
  213. }
  214. if (TkTextGetIndex(interp, textPtr, argv[3], &index) != TCL_OK) {
  215.     return TCL_ERROR;
  216. }
  217. /*
  218.  * Don't allow insertions on the last (dummy) line of the text.
  219.  */
  220.     
  221. lineIndex = TkBTreeLineIndex(index.linePtr);
  222. if (lineIndex == TkBTreeNumLines(textPtr->tree)) {
  223.     lineIndex--;
  224.     TkTextMakeByteIndex(textPtr->tree, lineIndex, 1000000, &index);
  225. }
  226. /*
  227.  * Create the new window segment and initialize it.
  228.  */
  229. ewPtr = (TkTextSegment *) ckalloc(EW_SEG_SIZE);
  230. ewPtr->typePtr = &tkTextEmbWindowType;
  231. ewPtr->size = 1;
  232. ewPtr->body.ew.textPtr = textPtr;
  233. ewPtr->body.ew.linePtr = NULL;
  234. ewPtr->body.ew.tkwin = NULL;
  235. ewPtr->body.ew.create = NULL;
  236. ewPtr->body.ew.align = ALIGN_CENTER;
  237. ewPtr->body.ew.padX = ewPtr->body.ew.padY = 0;
  238. ewPtr->body.ew.stretch = 0;
  239. ewPtr->body.ew.chunkCount = 0;
  240. ewPtr->body.ew.displayed = 0;
  241. /*
  242.  * Link the segment into the text widget, then configure it (delete
  243.  * it again if the configuration fails).
  244.  */
  245. TkTextChanged(textPtr, &index, &index);
  246. TkBTreeLinkSegment(ewPtr, &index);
  247. if (EmbWinConfigure(textPtr, ewPtr, argc-4, argv+4) != TCL_OK) {
  248.     TkTextIndex index2;
  249.     TkTextIndexForwChars(&index, 1, &index2);
  250.     TkBTreeDeleteChars(&index, &index2);
  251.     return TCL_ERROR;
  252. }
  253.     } else if (strncmp(argv[2], "names", length) == 0) {
  254. Tcl_HashSearch search;
  255. Tcl_HashEntry *hPtr;
  256. if (argc != 3) {
  257.     Tcl_AppendResult(interp, "wrong # args: should be "",
  258.     argv[0], " window names"", (char *) NULL);
  259.     return TCL_ERROR;
  260. }
  261. for (hPtr = Tcl_FirstHashEntry(&textPtr->windowTable, &search);
  262. hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
  263.     Tcl_AppendElement(interp,
  264.     Tcl_GetHashKey(&textPtr->markTable, hPtr));
  265. }
  266.     } else {
  267. Tcl_AppendResult(interp, "bad window option "", argv[2],
  268. "": must be cget, configure, create, or names",
  269. (char *) NULL);
  270. return TCL_ERROR;
  271.     }
  272.     return TCL_OK;
  273. }
  274. /*
  275.  *--------------------------------------------------------------
  276.  *
  277.  * EmbWinConfigure --
  278.  *
  279.  * This procedure is called to handle configuration options
  280.  * for an embedded window, using an argc/argv list.
  281.  *
  282.  * Results:
  283.  * The return value is a standard Tcl result.  If TCL_ERROR is
  284.  * returned, then the interp's result contains an error message..
  285.  *
  286.  * Side effects:
  287.  * Configuration information for the embedded window changes,
  288.  * such as alignment, stretching, or name of the embedded
  289.  * window.
  290.  *
  291.  *--------------------------------------------------------------
  292.  */
  293. static int
  294. EmbWinConfigure(textPtr, ewPtr, argc, argv)
  295.     TkText *textPtr; /* Information about text widget that
  296.  * contains embedded window. */
  297.     TkTextSegment *ewPtr; /* Embedded window to be configured. */
  298.     int argc; /* Number of strings in argv. */
  299.     CONST char **argv; /* Array of strings describing configuration
  300.  * options. */
  301. {
  302.     Tk_Window oldWindow;
  303.     Tcl_HashEntry *hPtr;
  304.     int new;
  305.     oldWindow = ewPtr->body.ew.tkwin;
  306.     if (Tk_ConfigureWidget(textPtr->interp, textPtr->tkwin, configSpecs,
  307.     argc, argv, (char *) &ewPtr->body.ew, TK_CONFIG_ARGV_ONLY)
  308.     != TCL_OK) {
  309. return TCL_ERROR;
  310.     }
  311.     if (oldWindow != ewPtr->body.ew.tkwin) {
  312. if (oldWindow != NULL) {
  313.     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&textPtr->windowTable,
  314.     Tk_PathName(oldWindow)));
  315.     Tk_DeleteEventHandler(oldWindow, StructureNotifyMask,
  316.     EmbWinStructureProc, (ClientData) ewPtr);
  317.     Tk_ManageGeometry(oldWindow, (Tk_GeomMgr *) NULL,
  318.     (ClientData) NULL);
  319.     if (textPtr->tkwin != Tk_Parent(oldWindow)) {
  320. Tk_UnmaintainGeometry(oldWindow, textPtr->tkwin);
  321.     } else {
  322. Tk_UnmapWindow(oldWindow);
  323.     }
  324. }
  325. if (ewPtr->body.ew.tkwin != NULL) {
  326.     Tk_Window ancestor, parent;
  327.     /*
  328.      * Make sure that the text is either the parent of the
  329.      * embedded window or a descendant of that parent.  Also,
  330.      * don't allow a top-level window to be managed inside
  331.      * a text.
  332.      */
  333.     parent = Tk_Parent(ewPtr->body.ew.tkwin);
  334.     for (ancestor = textPtr->tkwin; ;
  335.     ancestor = Tk_Parent(ancestor)) {
  336. if (ancestor == parent) {
  337.     break;
  338. }
  339. if (Tk_TopWinHierarchy(ancestor)) {
  340.     badMaster:
  341.     Tcl_AppendResult(textPtr->interp, "can't embed ",
  342.     Tk_PathName(ewPtr->body.ew.tkwin), " in ",
  343.     Tk_PathName(textPtr->tkwin), (char *) NULL);
  344.     ewPtr->body.ew.tkwin = NULL;
  345.     return TCL_ERROR;
  346. }
  347.     }
  348.     if (Tk_TopWinHierarchy(ewPtr->body.ew.tkwin)
  349.     || (ewPtr->body.ew.tkwin == textPtr->tkwin)) {
  350. goto badMaster;
  351.     }
  352.     /*
  353.      * Take over geometry management for the window, plus create
  354.      * an event handler to find out when it is deleted.
  355.      */
  356.     Tk_ManageGeometry(ewPtr->body.ew.tkwin, &textGeomType,
  357.     (ClientData) ewPtr);
  358.     Tk_CreateEventHandler(ewPtr->body.ew.tkwin, StructureNotifyMask,
  359.     EmbWinStructureProc, (ClientData) ewPtr);
  360.     /*
  361.      * Special trick!  Must enter into the hash table *after*
  362.      * calling Tk_ManageGeometry:  if the window was already managed
  363.      * elsewhere in this text, the Tk_ManageGeometry call will cause
  364.      * the entry to be removed, which could potentially lose the new
  365.      * entry.
  366.      */
  367.     hPtr = Tcl_CreateHashEntry(&textPtr->windowTable,
  368.     Tk_PathName(ewPtr->body.ew.tkwin), &new);
  369.     Tcl_SetHashValue(hPtr, ewPtr);
  370. }
  371.     }
  372.     return TCL_OK;
  373. }
  374. /*
  375.  *--------------------------------------------------------------
  376.  *
  377.  * AlignParseProc --
  378.  *
  379.  * This procedure is invoked by Tk_ConfigureWidget during
  380.  * option processing to handle "-align" options for embedded
  381.  * windows.
  382.  *
  383.  * Results:
  384.  * A standard Tcl return value.
  385.  *
  386.  * Side effects:
  387.  * The alignment for the embedded window may change.
  388.  *
  389.  *--------------------------------------------------------------
  390.  */
  391. /* ARGSUSED */
  392. static int
  393. AlignParseProc(clientData, interp, tkwin, value, widgRec, offset)
  394.     ClientData clientData; /* Not used.*/
  395.     Tcl_Interp *interp; /* Used for reporting errors. */
  396.     Tk_Window tkwin; /* Window for text widget. */
  397.     CONST char *value; /* Value of option. */
  398.     char *widgRec; /* Pointer to TkTextEmbWindow
  399.  * structure. */
  400.     int offset; /* Offset into item (ignored). */
  401. {
  402.     register TkTextEmbWindow *embPtr = (TkTextEmbWindow *) widgRec;
  403.     if (strcmp(value, "baseline") == 0) {
  404. embPtr->align = ALIGN_BASELINE;
  405.     } else if (strcmp(value, "bottom") == 0) {
  406. embPtr->align = ALIGN_BOTTOM;
  407.     } else if (strcmp(value, "center") == 0) {
  408. embPtr->align = ALIGN_CENTER;
  409.     } else if (strcmp(value, "top") == 0) {
  410. embPtr->align = ALIGN_TOP;
  411.     } else {
  412. Tcl_AppendResult(interp, "bad alignment "", value,
  413. "": must be baseline, bottom, center, or top",
  414. (char *) NULL);
  415. return TCL_ERROR;
  416.     }
  417.     return TCL_OK;
  418. }
  419. /*
  420.  *--------------------------------------------------------------
  421.  *
  422.  * AlignPrintProc --
  423.  *
  424.  * This procedure is invoked by the Tk configuration code
  425.  * to produce a printable string for the "-align" configuration
  426.  * option for embedded windows.
  427.  *
  428.  * Results:
  429.  * The return value is a string describing the embedded
  430.  * window's current alignment.
  431.  *
  432.  * Side effects:
  433.  * None.
  434.  *
  435.  *--------------------------------------------------------------
  436.  */
  437. /* ARGSUSED */
  438. static char *
  439. AlignPrintProc(clientData, tkwin, widgRec, offset, freeProcPtr)
  440.     ClientData clientData; /* Ignored. */
  441.     Tk_Window tkwin; /* Window for text widget. */
  442.     char *widgRec; /* Pointer to TkTextEmbWindow
  443.  * structure. */
  444.     int offset; /* Ignored. */
  445.     Tcl_FreeProc **freeProcPtr; /* Pointer to variable to fill in with
  446.  * information about how to reclaim
  447.  * storage for return string. */
  448. {
  449.     switch (((TkTextEmbWindow *) widgRec)->align) {
  450. case ALIGN_BASELINE:
  451.     return "baseline";
  452. case ALIGN_BOTTOM:
  453.     return "bottom";
  454. case ALIGN_CENTER:
  455.     return "center";
  456. case ALIGN_TOP:
  457.     return "top";
  458. default:
  459.     return "??";
  460.     }
  461. }
  462. /*
  463.  *--------------------------------------------------------------
  464.  *
  465.  * EmbWinStructureProc --
  466.  *
  467.  * This procedure is invoked by the Tk event loop whenever
  468.  * StructureNotify events occur for a window that's embedded
  469.  * in a text widget.  This procedure's only purpose is to
  470.  * clean up when windows are deleted.
  471.  *
  472.  * Results:
  473.  * None.
  474.  *
  475.  * Side effects:
  476.  * The window is disassociated from the window segment, and
  477.  * the portion of the text is redisplayed.
  478.  *
  479.  *--------------------------------------------------------------
  480.  */
  481. static void
  482. EmbWinStructureProc(clientData, eventPtr)
  483.     ClientData clientData; /* Pointer to record describing window item. */
  484.     XEvent *eventPtr; /* Describes what just happened. */
  485. {
  486.     register TkTextSegment *ewPtr = (TkTextSegment *) clientData;
  487.     TkTextIndex index;
  488.     if (eventPtr->type != DestroyNotify) {
  489. return;
  490.     }
  491.     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&ewPtr->body.ew.textPtr->windowTable,
  492.     Tk_PathName(ewPtr->body.ew.tkwin)));
  493.     ewPtr->body.ew.tkwin = NULL;
  494.     index.tree = ewPtr->body.ew.textPtr->tree;
  495.     index.linePtr = ewPtr->body.ew.linePtr;
  496.     index.byteIndex = TkTextSegToOffset(ewPtr, ewPtr->body.ew.linePtr);
  497.     TkTextChanged(ewPtr->body.ew.textPtr, &index, &index);
  498. }
  499. /*
  500.  *--------------------------------------------------------------
  501.  *
  502.  * EmbWinRequestProc --
  503.  *
  504.  * This procedure is invoked whenever a window that's associated
  505.  * with a window canvas item changes its requested dimensions.
  506.  *
  507.  * Results:
  508.  * None.
  509.  *
  510.  * Side effects:
  511.  * The size and location on the screen of the window may change,
  512.  * depending on the options specified for the window item.
  513.  *
  514.  *--------------------------------------------------------------
  515.  */
  516. /* ARGSUSED */
  517. static void
  518. EmbWinRequestProc(clientData, tkwin)
  519.     ClientData clientData; /* Pointer to record for window item. */
  520.     Tk_Window tkwin; /* Window that changed its desired
  521.  * size. */
  522. {
  523.     TkTextSegment *ewPtr = (TkTextSegment *) clientData;
  524.     TkTextIndex index;
  525.     index.tree = ewPtr->body.ew.textPtr->tree;
  526.     index.linePtr = ewPtr->body.ew.linePtr;
  527.     index.byteIndex = TkTextSegToOffset(ewPtr, ewPtr->body.ew.linePtr);
  528.     TkTextChanged(ewPtr->body.ew.textPtr, &index, &index);
  529. }
  530. /*
  531.  *--------------------------------------------------------------
  532.  *
  533.  * EmbWinLostSlaveProc --
  534.  *
  535.  * This procedure is invoked by the Tk geometry manager when
  536.  * a slave window managed by a text widget is claimed away
  537.  * by another geometry manager.
  538.  *
  539.  * Results:
  540.  * None.
  541.  *
  542.  * Side effects:
  543.  * The window is disassociated from the window segment, and
  544.  * the portion of the text is redisplayed.
  545.  *
  546.  *--------------------------------------------------------------
  547.  */
  548. static void
  549. EmbWinLostSlaveProc(clientData, tkwin)
  550.     ClientData clientData; /* Pointer to record describing window item. */
  551.     Tk_Window tkwin; /* Window that was claimed away by another
  552.  * geometry manager. */
  553. {
  554.     register TkTextSegment *ewPtr = (TkTextSegment *) clientData;
  555.     TkTextIndex index;
  556.     Tk_DeleteEventHandler(ewPtr->body.ew.tkwin, StructureNotifyMask,
  557.     EmbWinStructureProc, (ClientData) ewPtr);
  558.     Tcl_CancelIdleCall(EmbWinDelayedUnmap, (ClientData) ewPtr);
  559.     if (ewPtr->body.ew.textPtr->tkwin != Tk_Parent(tkwin)) {
  560. Tk_UnmaintainGeometry(tkwin, ewPtr->body.ew.textPtr->tkwin);
  561.     } else {
  562. Tk_UnmapWindow(tkwin);
  563.     }
  564.     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&ewPtr->body.ew.textPtr->windowTable,
  565.     Tk_PathName(ewPtr->body.ew.tkwin)));
  566.     ewPtr->body.ew.tkwin = NULL;
  567.     index.tree = ewPtr->body.ew.textPtr->tree;
  568.     index.linePtr = ewPtr->body.ew.linePtr;
  569.     index.byteIndex = TkTextSegToOffset(ewPtr, ewPtr->body.ew.linePtr);
  570.     TkTextChanged(ewPtr->body.ew.textPtr, &index, &index);
  571. }
  572. /*
  573.  *--------------------------------------------------------------
  574.  *
  575.  * EmbWinDeleteProc --
  576.  *
  577.  * This procedure is invoked by the text B-tree code whenever
  578.  * an embedded window lies in a range of characters being deleted.
  579.  *
  580.  * Results:
  581.  * Returns 0 to indicate that the deletion has been accepted.
  582.  *
  583.  * Side effects:
  584.  * The embedded window is deleted, if it exists, and any resources
  585.  * associated with it are released.
  586.  *
  587.  *--------------------------------------------------------------
  588.  */
  589. /* ARGSUSED */
  590. static int
  591. EmbWinDeleteProc(ewPtr, linePtr, treeGone)
  592.     TkTextSegment *ewPtr; /* Segment being deleted. */
  593.     TkTextLine *linePtr; /* Line containing segment. */
  594.     int treeGone; /* Non-zero means the entire tree is
  595.  * being deleted, so everything must
  596.  * get cleaned up. */
  597. {
  598.     Tcl_HashEntry *hPtr;
  599.     if (ewPtr->body.ew.tkwin != NULL) {
  600. hPtr = Tcl_FindHashEntry(&ewPtr->body.ew.textPtr->windowTable,
  601. Tk_PathName(ewPtr->body.ew.tkwin));
  602. if (hPtr != NULL) {
  603.     /*
  604.      * (It's possible for there to be no hash table entry for this
  605.      * window, if an error occurred while creating the window segment
  606.      * but before the window got added to the table)
  607.      */
  608.     Tcl_DeleteHashEntry(hPtr);
  609. }
  610. /*
  611.  * Delete the event handler for the window before destroying
  612.  * the window, so that EmbWinStructureProc doesn't get called
  613.  * (we'll already do everything that it would have done, and
  614.  * it will just get confused).
  615.  */
  616. Tk_DeleteEventHandler(ewPtr->body.ew.tkwin, StructureNotifyMask,
  617. EmbWinStructureProc, (ClientData) ewPtr);
  618. Tk_DestroyWindow(ewPtr->body.ew.tkwin);
  619.     }
  620.     Tcl_CancelIdleCall(EmbWinDelayedUnmap, (ClientData) ewPtr);
  621.     Tk_FreeOptions(configSpecs, (char *) &ewPtr->body.ew,
  622.     ewPtr->body.ew.textPtr->display, 0);
  623.     ckfree((char *) ewPtr);
  624.     return 0;
  625. }
  626. /*
  627.  *--------------------------------------------------------------
  628.  *
  629.  * EmbWinCleanupProc --
  630.  *
  631.  * This procedure is invoked by the B-tree code whenever a
  632.  * segment containing an embedded window is moved from one
  633.  * line to another.
  634.  *
  635.  * Results:
  636.  * None.
  637.  *
  638.  * Side effects:
  639.  * The linePtr field of the segment gets updated.
  640.  *
  641.  *--------------------------------------------------------------
  642.  */
  643. static TkTextSegment *
  644. EmbWinCleanupProc(ewPtr, linePtr)
  645.     TkTextSegment *ewPtr; /* Mark segment that's being moved. */
  646.     TkTextLine *linePtr; /* Line that now contains segment. */
  647. {
  648.     ewPtr->body.ew.linePtr = linePtr;
  649.     return ewPtr;
  650. }
  651. /*
  652.  *--------------------------------------------------------------
  653.  *
  654.  * EmbWinLayoutProc --
  655.  *
  656.  * This procedure is the "layoutProc" for embedded window
  657.  * segments.
  658.  *
  659.  * Results:
  660.  * 1 is returned to indicate that the segment should be
  661.  * displayed.  The chunkPtr structure is filled in.
  662.  *
  663.  * Side effects:
  664.  * None, except for filling in chunkPtr.
  665.  *
  666.  *--------------------------------------------------------------
  667.  */
  668. /*ARGSUSED*/
  669. static int
  670. EmbWinLayoutProc(textPtr, indexPtr, ewPtr, offset, maxX, maxChars,
  671. noCharsYet, wrapMode, chunkPtr)
  672.     TkText *textPtr; /* Text widget being layed out. */
  673.     TkTextIndex *indexPtr; /* Identifies first character in chunk. */
  674.     TkTextSegment *ewPtr; /* Segment corresponding to indexPtr. */
  675.     int offset; /* Offset within segPtr corresponding to
  676.  * indexPtr (always 0). */
  677.     int maxX; /* Chunk must not occupy pixels at this
  678.  * position or higher. */
  679.     int maxChars; /* Chunk must not include more than this
  680.  * many characters. */
  681.     int noCharsYet; /* Non-zero means no characters have been
  682.  * assigned to this line yet. */
  683.     TkWrapMode wrapMode; /* Wrap mode to use for line: TEXT_WRAPMODE_CHAR,
  684.  * TEXT_WRAPMODE_NONE, or TEXT_WRAPMODE_WORD. */
  685.     register TkTextDispChunk *chunkPtr;
  686. /* Structure to fill in with information
  687.  * about this chunk.  The x field has already
  688.  * been set by the caller. */
  689. {
  690.     int width, height;
  691.     if (offset != 0) {
  692. panic("Non-zero offset in EmbWinLayoutProc");
  693.     }
  694.     if ((ewPtr->body.ew.tkwin == NULL) && (ewPtr->body.ew.create != NULL)) {
  695. int code, new;
  696. Tcl_DString name;
  697. Tk_Window ancestor;
  698. Tcl_HashEntry *hPtr;
  699. /*
  700.  * The window doesn't currently exist.  Create it by evaluating
  701.  * the creation script.  The script must return the window's
  702.  * path name:  look up that name to get back to the window
  703.  * token.  Then register ourselves as the geometry manager for
  704.  * the window.
  705.  */
  706. code = Tcl_GlobalEval(textPtr->interp, ewPtr->body.ew.create);
  707. if (code != TCL_OK) {
  708.     createError:
  709.     Tcl_BackgroundError(textPtr->interp);
  710.     goto gotWindow;
  711. }
  712. Tcl_DStringInit(&name);
  713. Tcl_DStringAppend(&name, Tcl_GetStringResult(textPtr->interp), -1);
  714. Tcl_ResetResult(textPtr->interp);
  715. ewPtr->body.ew.tkwin = Tk_NameToWindow(textPtr->interp,
  716. Tcl_DStringValue(&name), textPtr->tkwin);
  717. if (ewPtr->body.ew.tkwin == NULL) {
  718.     goto createError;
  719. }
  720. for (ancestor = textPtr->tkwin; ;
  721. ancestor = Tk_Parent(ancestor)) {
  722.     if (ancestor == Tk_Parent(ewPtr->body.ew.tkwin)) {
  723. break;
  724.     }
  725.     if (Tk_TopWinHierarchy(ancestor)) {
  726. badMaster:
  727. Tcl_AppendResult(textPtr->interp, "can't embed ",
  728. Tk_PathName(ewPtr->body.ew.tkwin), " relative to ",
  729. Tk_PathName(textPtr->tkwin), (char *) NULL);
  730. Tcl_BackgroundError(textPtr->interp);
  731. ewPtr->body.ew.tkwin = NULL;
  732. goto gotWindow;
  733.     }
  734. }
  735. if (Tk_TopWinHierarchy(ewPtr->body.ew.tkwin)
  736. || (textPtr->tkwin == ewPtr->body.ew.tkwin)) {
  737.     goto badMaster;
  738. }
  739. Tk_ManageGeometry(ewPtr->body.ew.tkwin, &textGeomType,
  740. (ClientData) ewPtr);
  741. Tk_CreateEventHandler(ewPtr->body.ew.tkwin, StructureNotifyMask,
  742. EmbWinStructureProc, (ClientData) ewPtr);
  743. /*
  744.  * Special trick!  Must enter into the hash table *after*
  745.  * calling Tk_ManageGeometry:  if the window was already managed
  746.  * elsewhere in this text, the Tk_ManageGeometry call will cause
  747.  * the entry to be removed, which could potentially lose the new
  748.  * entry.
  749.  */
  750. hPtr = Tcl_CreateHashEntry(&textPtr->windowTable,
  751. Tk_PathName(ewPtr->body.ew.tkwin), &new);
  752. Tcl_SetHashValue(hPtr, ewPtr);
  753.     }
  754.     /*
  755.      * See if there's room for this window on this line.
  756.      */
  757.     gotWindow:
  758.     if (ewPtr->body.ew.tkwin == NULL) {
  759. width = 0;
  760. height = 0;
  761.     } else {
  762. width = Tk_ReqWidth(ewPtr->body.ew.tkwin) + 2*ewPtr->body.ew.padX;
  763. height = Tk_ReqHeight(ewPtr->body.ew.tkwin) + 2*ewPtr->body.ew.padY;
  764.     }
  765.     if ((width > (maxX - chunkPtr->x))
  766.     && !noCharsYet && (textPtr->wrapMode != TEXT_WRAPMODE_NONE)) {
  767. return 0;
  768.     }
  769.     /*
  770.      * Fill in the chunk structure.
  771.      */
  772.     chunkPtr->displayProc = EmbWinDisplayProc;
  773.     chunkPtr->undisplayProc = EmbWinUndisplayProc;
  774.     chunkPtr->measureProc = (Tk_ChunkMeasureProc *) NULL;
  775.     chunkPtr->bboxProc = EmbWinBboxProc;
  776.     chunkPtr->numBytes = 1;
  777.     if (ewPtr->body.ew.align == ALIGN_BASELINE) {
  778. chunkPtr->minAscent = height - ewPtr->body.ew.padY;
  779. chunkPtr->minDescent = ewPtr->body.ew.padY;
  780. chunkPtr->minHeight = 0;
  781.     } else {
  782. chunkPtr->minAscent = 0;
  783. chunkPtr->minDescent = 0;
  784. chunkPtr->minHeight = height;
  785.     }
  786.     chunkPtr->width = width;
  787.     chunkPtr->breakIndex = -1;
  788.     chunkPtr->breakIndex = 1;
  789.     chunkPtr->clientData = (ClientData) ewPtr;
  790.     ewPtr->body.ew.chunkCount += 1;
  791.     return 1;
  792. }
  793. /*
  794.  *--------------------------------------------------------------
  795.  *
  796.  * EmbWinCheckProc --
  797.  *
  798.  * This procedure is invoked by the B-tree code to perform
  799.  * consistency checks on embedded windows.
  800.  *
  801.  * Results:
  802.  * None.
  803.  *
  804.  * Side effects:
  805.  * The procedure panics if it detects anything wrong with
  806.  * the embedded window.
  807.  *
  808.  *--------------------------------------------------------------
  809.  */
  810. static void
  811. EmbWinCheckProc(ewPtr, linePtr)
  812.     TkTextSegment *ewPtr; /* Segment to check. */
  813.     TkTextLine *linePtr; /* Line containing segment. */
  814. {
  815.     if (ewPtr->nextPtr == NULL) {
  816. panic("EmbWinCheckProc: embedded window is last segment in line");
  817.     }
  818.     if (ewPtr->size != 1) {
  819. panic("EmbWinCheckProc: embedded window has size %d", ewPtr->size);
  820.     }
  821. }
  822. /*
  823.  *--------------------------------------------------------------
  824.  *
  825.  * EmbWinDisplayProc --
  826.  *
  827.  * This procedure is invoked by the text displaying code
  828.  * when it is time to actually draw an embedded window
  829.  * chunk on the screen.
  830.  *
  831.  * Results:
  832.  * None.
  833.  *
  834.  * Side effects:
  835.  * The embedded window gets moved to the correct location
  836.  * and mapped onto the screen.
  837.  *
  838.  *--------------------------------------------------------------
  839.  */
  840. static void
  841. EmbWinDisplayProc(chunkPtr, x, y, lineHeight, baseline, display, dst, screenY)
  842.     TkTextDispChunk *chunkPtr; /* Chunk that is to be drawn. */
  843.     int x; /* X-position in dst at which to
  844.  * draw this chunk (differs from
  845.  * the x-position in the chunk because
  846.  * of scrolling). */
  847.     int y; /* Top of rectangular bounding box
  848.  * for line: tells where to draw this
  849.  * chunk in dst (x-position is in
  850.  * the chunk itself). */
  851.     int lineHeight; /* Total height of line. */
  852.     int baseline; /* Offset of baseline from y. */
  853.     Display *display; /* Display to use for drawing. */
  854.     Drawable dst; /* Pixmap or window in which to draw */
  855.     int screenY; /* Y-coordinate in text window that
  856.  * corresponds to y. */
  857. {
  858.     TkTextSegment *ewPtr = (TkTextSegment *) chunkPtr->clientData;
  859.     int lineX, windowX, windowY, width, height;
  860.     Tk_Window tkwin;
  861.     tkwin = ewPtr->body.ew.tkwin;
  862.     if (tkwin == NULL) {
  863. return;
  864.     }
  865.     if ((x + chunkPtr->width) <= 0) {
  866. /*
  867.  * The window is off-screen;  just unmap it.
  868.  */
  869. if (ewPtr->body.ew.textPtr->tkwin != Tk_Parent(tkwin)) {
  870.     Tk_UnmaintainGeometry(tkwin, ewPtr->body.ew.textPtr->tkwin);
  871. } else {
  872.     Tk_UnmapWindow(tkwin);
  873. }
  874. return;
  875.     }
  876.     /*
  877.      * Compute the window's location and size in the text widget, taking
  878.      * into account the align and stretch values for the window.
  879.      */
  880.     EmbWinBboxProc(chunkPtr, 0, screenY, lineHeight, baseline, &lineX,
  881.     &windowY, &width, &height);
  882.     windowX = lineX - chunkPtr->x + x;
  883.     if (ewPtr->body.ew.textPtr->tkwin == Tk_Parent(tkwin)) {
  884. if ((windowX != Tk_X(tkwin)) || (windowY != Tk_Y(tkwin))
  885. || (Tk_ReqWidth(tkwin) != Tk_Width(tkwin))
  886. || (height != Tk_Height(tkwin))) {
  887.     Tk_MoveResizeWindow(tkwin, windowX, windowY, width, height);
  888. }
  889. Tk_MapWindow(tkwin);
  890.     } else {
  891. Tk_MaintainGeometry(tkwin, ewPtr->body.ew.textPtr->tkwin,
  892. windowX, windowY, width, height);
  893.     }
  894.     /*
  895.      * Mark the window as displayed so that it won't get unmapped.
  896.      */
  897.     ewPtr->body.ew.displayed = 1;
  898. }
  899. /*
  900.  *--------------------------------------------------------------
  901.  *
  902.  * EmbWinUndisplayProc --
  903.  *
  904.  * This procedure is called when the chunk for an embedded
  905.  * window is no longer going to be displayed.  It arranges
  906.  * for the window associated with the chunk to be unmapped.
  907.  *
  908.  * Results:
  909.  * None.
  910.  *
  911.  * Side effects:
  912.  * The window is scheduled for unmapping.
  913.  *
  914.  *--------------------------------------------------------------
  915.  */
  916. static void
  917. EmbWinUndisplayProc(textPtr, chunkPtr)
  918.     TkText *textPtr; /* Overall information about text
  919.  * widget. */
  920.     TkTextDispChunk *chunkPtr; /* Chunk that is about to be freed. */
  921. {
  922.     TkTextSegment *ewPtr = (TkTextSegment *) chunkPtr->clientData;
  923.     ewPtr->body.ew.chunkCount--;
  924.     if (ewPtr->body.ew.chunkCount == 0) {
  925. /*
  926.  * Don't unmap the window immediately, since there's a good chance
  927.  * that it will immediately be redisplayed, perhaps even in the
  928.  * same place.  Instead, schedule the window to be unmapped later;
  929.  * the call to EmbWinDelayedUnmap will be cancelled in the likely
  930.  * event that the unmap becomes unnecessary.
  931.  */
  932. ewPtr->body.ew.displayed = 0;
  933. Tcl_DoWhenIdle(EmbWinDelayedUnmap, (ClientData) ewPtr);
  934.     }
  935. }
  936. /*
  937.  *--------------------------------------------------------------
  938.  *
  939.  * EmbWinBboxProc --
  940.  *
  941.  * This procedure is called to compute the bounding box of
  942.  * the area occupied by an embedded window.
  943.  *
  944.  * Results:
  945.  * There is no return value.  *xPtr and *yPtr are filled in
  946.  * with the coordinates of the upper left corner of the
  947.  * window, and *widthPtr and *heightPtr are filled in with
  948.  * the dimensions of the window in pixels.  Note:  not all
  949.  * of the returned bbox is necessarily visible on the screen
  950.  * (the rightmost part might be off-screen to the right,
  951.  * and the bottommost part might be off-screen to the bottom).
  952.  *
  953.  * Side effects:
  954.  * None.
  955.  *
  956.  *--------------------------------------------------------------
  957.  */
  958. static void
  959. EmbWinBboxProc(chunkPtr, index, y, lineHeight, baseline, xPtr, yPtr,
  960. widthPtr, heightPtr)
  961.     TkTextDispChunk *chunkPtr; /* Chunk containing desired char. */
  962.     int index; /* Index of desired character within
  963.  * the chunk. */
  964.     int y; /* Topmost pixel in area allocated
  965.  * for this line. */
  966.     int lineHeight; /* Total height of line. */
  967.     int baseline; /* Location of line's baseline, in
  968.  * pixels measured down from y. */
  969.     int *xPtr, *yPtr; /* Gets filled in with coords of
  970.  * character's upper-left pixel. */
  971.     int *widthPtr; /* Gets filled in with width of
  972.  * character, in pixels. */
  973.     int *heightPtr; /* Gets filled in with height of
  974.  * character, in pixels. */
  975. {
  976.     TkTextSegment *ewPtr = (TkTextSegment *) chunkPtr->clientData;
  977.     Tk_Window tkwin;
  978.     tkwin = ewPtr->body.ew.tkwin;
  979.     if (tkwin != NULL) {
  980. *widthPtr = Tk_ReqWidth(tkwin);
  981. *heightPtr = Tk_ReqHeight(tkwin);
  982.     } else {
  983. *widthPtr = 0;
  984. *heightPtr = 0;
  985.     }
  986.     *xPtr = chunkPtr->x + ewPtr->body.ew.padX;
  987.     if (ewPtr->body.ew.stretch) {
  988. if (ewPtr->body.ew.align == ALIGN_BASELINE) {
  989.     *heightPtr = baseline - ewPtr->body.ew.padY;
  990. } else {
  991.     *heightPtr = lineHeight - 2*ewPtr->body.ew.padY;
  992. }
  993.     }
  994.     switch (ewPtr->body.ew.align) {
  995. case ALIGN_BOTTOM:
  996.     *yPtr = y + (lineHeight - *heightPtr - ewPtr->body.ew.padY);
  997.     break;
  998. case ALIGN_CENTER:
  999.     *yPtr = y + (lineHeight - *heightPtr)/2;
  1000.     break;
  1001. case ALIGN_TOP:
  1002.     *yPtr = y + ewPtr->body.ew.padY;
  1003.     break;
  1004. case ALIGN_BASELINE:
  1005.     *yPtr = y + (baseline - *heightPtr);
  1006.     break;
  1007.     }
  1008. }
  1009. /*
  1010.  *--------------------------------------------------------------
  1011.  *
  1012.  * EmbWinDelayedUnmap --
  1013.  *
  1014.  * This procedure is an idle handler that does the actual
  1015.  * work of unmapping an embedded window.  See the comment
  1016.  * in EmbWinUndisplayProc for details.
  1017.  *
  1018.  * Results:
  1019.  * None.
  1020.  *
  1021.  * Side effects:
  1022.  * The window gets unmapped, unless its chunk reference count
  1023.  * has become non-zero again.
  1024.  *
  1025.  *--------------------------------------------------------------
  1026.  */
  1027. static void
  1028. EmbWinDelayedUnmap(clientData)
  1029.     ClientData clientData; /* Token for the window to
  1030.  * be unmapped. */
  1031. {
  1032.     TkTextSegment *ewPtr = (TkTextSegment *) clientData;
  1033.     if (!ewPtr->body.ew.displayed && (ewPtr->body.ew.tkwin != NULL)) {
  1034. if (ewPtr->body.ew.textPtr->tkwin != Tk_Parent(ewPtr->body.ew.tkwin)) {
  1035.     Tk_UnmaintainGeometry(ewPtr->body.ew.tkwin,
  1036.     ewPtr->body.ew.textPtr->tkwin);
  1037. } else {
  1038.     Tk_UnmapWindow(ewPtr->body.ew.tkwin);
  1039. }
  1040.     }
  1041. }
  1042. /*
  1043.  *--------------------------------------------------------------
  1044.  *
  1045.  * TkTextWindowIndex --
  1046.  *
  1047.  * Given the name of an embedded window within a text widget,
  1048.  * returns an index corresponding to the window's position
  1049.  * in the text.
  1050.  *
  1051.  * Results:
  1052.  * The return value is 1 if there is an embedded window by
  1053.  * the given name in the text widget, 0 otherwise.  If the
  1054.  * window exists, *indexPtr is filled in with its index.
  1055.  *
  1056.  * Side effects:
  1057.  * None.
  1058.  *
  1059.  *--------------------------------------------------------------
  1060.  */
  1061. int
  1062. TkTextWindowIndex(textPtr, name, indexPtr)
  1063.     TkText *textPtr; /* Text widget containing window. */
  1064.     CONST char *name; /* Name of window. */
  1065.     TkTextIndex *indexPtr; /* Index information gets stored here. */
  1066. {
  1067.     Tcl_HashEntry *hPtr;
  1068.     TkTextSegment *ewPtr;
  1069.     hPtr = Tcl_FindHashEntry(&textPtr->windowTable, name);
  1070.     if (hPtr == NULL) {
  1071. return 0;
  1072.     }
  1073.     ewPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
  1074.     indexPtr->tree = textPtr->tree;
  1075.     indexPtr->linePtr = ewPtr->body.ew.linePtr;
  1076.     indexPtr->byteIndex = TkTextSegToOffset(ewPtr, indexPtr->linePtr);
  1077.     return 1;
  1078. }