summaryrefslogtreecommitdiffstats
path: root/generic
diff options
context:
space:
mode:
authorvincentdarley <vincentdarley>2004-09-10 12:13:38 (GMT)
committervincentdarley <vincentdarley>2004-09-10 12:13:38 (GMT)
commit09324dada308a84a1d5ba8b14bff2a5ce8b6eaf9 (patch)
treec17ff6a17da4273024607033b6c1bd7bf35d2d8f /generic
parent77f2c1e62ab0760dc6ee615d6bbcb81b11d76a6f (diff)
downloadtk-09324dada308a84a1d5ba8b14bff2a5ce8b6eaf9.zip
tk-09324dada308a84a1d5ba8b14bff2a5ce8b6eaf9.tar.gz
tk-09324dada308a84a1d5ba8b14bff2a5ce8b6eaf9.tar.bz2
text widget 'peer' subcommand -- TIP#169 implementation
Diffstat (limited to 'generic')
-rw-r--r--generic/tkTest.c11
-rw-r--r--generic/tkText.c1632
-rw-r--r--generic/tkText.h355
-rw-r--r--generic/tkTextBTree.c1045
-rw-r--r--generic/tkTextDisp.c489
-rw-r--r--generic/tkTextImage.c71
-rw-r--r--generic/tkTextIndex.c216
-rw-r--r--generic/tkTextMark.c247
-rw-r--r--generic/tkTextTag.c486
-rw-r--r--generic/tkTextWind.c510
-rw-r--r--generic/tkUndo.c302
-rw-r--r--generic/tkUndo.h118
12 files changed, 4155 insertions, 1327 deletions
diff --git a/generic/tkTest.c b/generic/tkTest.c
index f3f5931..7a5256f 100644
--- a/generic/tkTest.c
+++ b/generic/tkTest.c
@@ -13,7 +13,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tkTest.c,v 1.23 2004/03/17 18:15:44 das Exp $
+ * RCS: @(#) $Id: tkTest.c,v 1.24 2004/09/10 12:13:40 vincentdarley Exp $
*/
#include "tkInt.h"
@@ -2338,7 +2338,8 @@ TesttextCmd(clientData, interp, argc, argv)
lineIndex = atoi(argv[3]) - 1;
byteIndex = atoi(argv[4]);
- TkTextMakeByteIndex(textPtr->tree, lineIndex, byteIndex, &index);
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr, lineIndex,
+ byteIndex, &index);
} else if (strncmp(argv[2], "forwbytes", len) == 0) {
if (argc != 5) {
return TCL_ERROR;
@@ -2347,7 +2348,7 @@ TesttextCmd(clientData, interp, argc, argv)
return TCL_ERROR;
}
byteOffset = atoi(argv[4]);
- TkTextIndexForwBytes(&index, byteOffset, &index);
+ TkTextIndexForwBytes(textPtr, &index, byteOffset, &index);
} else if (strncmp(argv[2], "backbytes", len) == 0) {
if (argc != 5) {
return TCL_ERROR;
@@ -2356,13 +2357,13 @@ TesttextCmd(clientData, interp, argc, argv)
return TCL_ERROR;
}
byteOffset = atoi(argv[4]);
- TkTextIndexBackBytes(&index, byteOffset, &index);
+ TkTextIndexBackBytes(textPtr, &index, byteOffset, &index);
} else {
return TCL_ERROR;
}
TkTextSetMark(textPtr, "insert", &index);
- TkTextPrintIndex(&index, buf);
+ TkTextPrintIndex(textPtr, &index, buf);
sprintf(buf + strlen(buf), " %d", index.byteIndex);
Tcl_AppendResult(interp, buf, NULL);
diff --git a/generic/tkText.c b/generic/tkText.c
index fecbdb0..a920b20 100644
--- a/generic/tkText.c
+++ b/generic/tkText.c
@@ -14,7 +14,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tkText.c,v 1.51 2004/06/09 22:39:08 vincentdarley Exp $
+ * RCS: @(#) $Id: tkText.c,v 1.52 2004/09/10 12:13:40 vincentdarley Exp $
*/
#include "default.h"
@@ -38,6 +38,13 @@
#include "tkText.h"
+/*
+ * Used to avoid having to allocate and deallocate arrays on the
+ * fly for commonly used procedures. Must be > 0.
+ */
+
+#define PIXEL_CLIENTS 5
+
/*
* The 'TkTextState' enum in tkText.h is used to define a type for the
* -state option of the Text widget. These values are used as indices
@@ -59,6 +66,36 @@ static char *wrapStrings[] = {
};
/*
+ * The following functions and custom option type are used to define the
+ * "line" option type, and thereby handle the text widget '-startline',
+ * '-endline' configuration options which are of that type.
+ *
+ * We do not need a 'freeProc' because all changes to these two options
+ * are handled through the TK_TEXT_LINE_RANGE flag in the optionSpecs
+ * list, and the internal storage is just a pointer, which therefore
+ * doesn't need freeing.
+ */
+static int SetLineStartEnd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin,
+ Tcl_Obj **value, char *recordPtr, int internalOffset,
+ char *oldInternalPtr, int flags));
+static Tcl_Obj* GetLineStartEnd _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin,
+ char *recordPtr, int internalOffset));
+static void RestoreLineStartEnd _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin, char *internalPtr,
+ char *oldInternalPtr));
+static int ObjectIsEmpty _ANSI_ARGS_((Tcl_Obj *objPtr));
+
+static Tk_ObjCustomOption lineOption = {
+ "line", /* name */
+ SetLineStartEnd, /* setProc */
+ GetLineStartEnd, /* getProc */
+ RestoreLineStartEnd, /* restoreProc */
+ (Tk_CustomOptionFreeProc *)NULL, /* freeProc */
+ 0
+};
+
+/*
* Information used to parse text configuration options:
*/
@@ -83,6 +120,9 @@ static Tk_OptionSpec optionSpecs[] = {
{TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor",
DEF_TEXT_CURSOR, -1, Tk_Offset(TkText, cursor),
TK_OPTION_NULL_OK, 0, 0},
+ {TK_OPTION_CUSTOM, "-endline", (char *) NULL, (char *) NULL,
+ NULL, -1, Tk_Offset(TkText, end), TK_OPTION_NULL_OK,
+ (ClientData) &lineOption, TK_TEXT_LINE_RANGE},
{TK_OPTION_BOOLEAN, "-exportselection", "exportSelection",
"ExportSelection", DEF_TEXT_EXPORT_SELECTION, -1,
Tk_Offset(TkText, exportSelection), 0, 0, 0},
@@ -106,6 +146,15 @@ static Tk_OptionSpec optionSpecs[] = {
{TK_OPTION_PIXELS, "-highlightthickness", "highlightThickness",
"HighlightThickness", DEF_TEXT_HIGHLIGHT_WIDTH, -1,
Tk_Offset(TkText, highlightWidth), 0, 0, TK_TEXT_LINE_GEOMETRY},
+ {TK_OPTION_BORDER, "-inactiveselectbackground", "inactiveSelectBackground",
+ "Foreground",
+#ifdef ALWAYS_SHOW_SELECTION
+ DEF_TEXT_SELECT_COLOR,
+#else
+ NULL,
+#endif
+ -1, Tk_Offset(TkText, inactiveSelBorder),
+ TK_OPTION_NULL_OK, (ClientData) DEF_TEXT_SELECT_MONO, 0},
{TK_OPTION_BORDER, "-insertbackground", "insertBackground", "Foreground",
DEF_TEXT_INSERT_BG,
-1, Tk_Offset(TkText, insertBorder),
@@ -154,6 +203,9 @@ static Tk_OptionSpec optionSpecs[] = {
{TK_OPTION_PIXELS, "-spacing3", "spacing3", "Spacing",
DEF_TEXT_SPACING3, -1, Tk_Offset(TkText, spacing3),
TK_OPTION_DONT_SET_DEFAULT, 0 , TK_TEXT_LINE_GEOMETRY },
+ {TK_OPTION_CUSTOM, "-startline", (char *) NULL, (char *) NULL,
+ NULL, -1, Tk_Offset(TkText, start), TK_OPTION_NULL_OK,
+ (ClientData) &lineOption, TK_TEXT_LINE_RANGE},
{TK_OPTION_STRING_TABLE, "-state", "state", "State",
DEF_TEXT_STATE, -1, Tk_Offset(TkText, state),
0, (ClientData) stateStrings, 0},
@@ -271,36 +323,43 @@ int tkTextDebug = 0;
static int ConfigureText _ANSI_ARGS_((Tcl_Interp *interp,
TkText *textPtr, int objc, Tcl_Obj *CONST objv[]));
-static int DeleteChars _ANSI_ARGS_((TkText *textPtr,
+static int DeleteChars _ANSI_ARGS_((TkSharedText *sharedPtr,
+ TkText *textPtr,
CONST TkTextIndex *indexPtr1,
- CONST TkTextIndex *indexPtr2, int noViewUpdate));
+ CONST TkTextIndex *indexPtr2, int viewUpdate));
static int CountIndices _ANSI_ARGS_((CONST TkText *textPtr,
CONST TkTextIndex *indexPtr1,
CONST TkTextIndex *indexPtr2,
TkTextCountType type));
static void DestroyText _ANSI_ARGS_((TkText *textPtr));
-static int InsertChars _ANSI_ARGS_((TkText *textPtr,
+static int InsertChars _ANSI_ARGS_((TkSharedText *sharedTextPtr,
+ TkText *textPtr,
TkTextIndex *indexPtr, Tcl_Obj *stringPtr,
- int noViewUpdate));
+ int viewUpdate));
static void TextBlinkProc _ANSI_ARGS_((ClientData clientData));
static void TextCmdDeletedProc _ANSI_ARGS_((
ClientData clientData));
+static int CreateWidget _ANSI_ARGS_((TkSharedText *sharedPtr,
+ Tk_Window tkwin, Tcl_Interp *interp,
+ CONST TkText *parent,
+ int objc, Tcl_Obj *CONST objv[]));
static void TextEventProc _ANSI_ARGS_((ClientData clientData,
XEvent *eventPtr));
static int TextFetchSelection _ANSI_ARGS_((ClientData clientData,
int offset, char *buffer, int maxBytes));
static int TextIndexSortProc _ANSI_ARGS_((CONST VOID *first,
CONST VOID *second));
-static int TextInsertCmd _ANSI_ARGS_((TkText *textPtr,
+static int TextInsertCmd _ANSI_ARGS_((TkSharedText *sharedTextPtr,
+ TkText *textPtr,
Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[],
- CONST TkTextIndex *indexPtr, int noViewUpdate));
+ CONST TkTextIndex *indexPtr, int viewUpdate));
static int TextReplaceCmd _ANSI_ARGS_((TkText *textPtr,
Tcl_Interp *interp,
CONST TkTextIndex *indexFromPtr,
CONST TkTextIndex *indexToPtr,
int objc, Tcl_Obj *CONST objv[],
- int noViewUpdate));
+ int viewUpdate));
static int TextSearchCmd _ANSI_ARGS_((TkText *textPtr,
Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[]));
@@ -310,6 +369,9 @@ static int TextEditCmd _ANSI_ARGS_((TkText *textPtr,
static int TextWidgetObjCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[]));
+static int SharedTextObjCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]));
static void TextWorldChangedCallback _ANSI_ARGS_((
ClientData instanceData));
static void TextWorldChanged _ANSI_ARGS_((TkText *textPtr,
@@ -321,7 +383,8 @@ static void DumpLine _ANSI_ARGS_((Tcl_Interp *interp,
TkText *textPtr, int what, TkTextLine *linePtr,
int start, int end, int lineno,
CONST char *command));
-static int DumpSegment _ANSI_ARGS_((Tcl_Interp *interp,
+static int DumpSegment _ANSI_ARGS_((TkText *textPtr,
+ Tcl_Interp *interp,
CONST char *key,
CONST char *value, CONST char * command,
CONST TkTextIndex *index, int what));
@@ -330,7 +393,7 @@ static int TextEditRedo _ANSI_ARGS_((TkText *textPtr));
static Tcl_Obj* TextGetText _ANSI_ARGS_((CONST TkText *textPtr,
CONST TkTextIndex * index1,
CONST TkTextIndex * index2, int visibleOnly));
-static void UpdateDirtyFlag _ANSI_ARGS_((TkText *textPtr));
+static void UpdateDirtyFlag _ANSI_ARGS_((TkSharedText *sharedPtr));
static void TextPushUndoAction _ANSI_ARGS_((TkText *textPtr,
Tcl_Obj *undoString, int insert,
CONST TkTextIndex *index1Ptr,
@@ -338,6 +401,10 @@ static void TextPushUndoAction _ANSI_ARGS_((TkText *textPtr,
static int TextSearchIndexInLine _ANSI_ARGS_((
CONST SearchSpec *searchSpecPtr,
TkTextLine *linePtr, int byteIndex));
+static int TextPeerCmd _ANSI_ARGS_((TkText *textPtr,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
+static TkUndoProc TextUndoRedoCallback;
/*
* Declarations of the three search procs required by
@@ -386,16 +453,52 @@ Tk_TextObjCmd(clientData, interp, objc, objv)
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
Tk_Window tkwin = (Tk_Window) clientData;
- Tk_Window new;
- Tk_OptionTable optionTable;
- register TkText *textPtr;
- TkTextIndex startIndex;
if (objc < 2) {
Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?");
return TCL_ERROR;
}
+ return CreateWidget(NULL, tkwin, interp, NULL, objc, objv);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * CreateWidget --
+ *
+ * This procedure is invoked to process the "text" Tcl command,
+ * (when called by Tk_TextObjCmd) and the "$text peer create"
+ * text widget sub-command (called from TextPeerCmd).
+ *
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result, places the name of the widget
+ * created into the interp's result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+CreateWidget(sharedPtr, tkwin, interp, parent, objc, objv)
+ TkSharedText *sharedPtr; /* Shared widget info, or null */
+ Tk_Window tkwin; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ CONST TkText *parent; /* If non-NULL then take default
+ * start, end from this parent. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ register TkText *textPtr;
+ Tk_OptionTable optionTable;
+ TkTextIndex startIndex;
+ Tk_Window new;
+
/*
* Create the window.
*/
@@ -408,7 +511,9 @@ Tk_TextObjCmd(clientData, interp, objc, objv)
/*
* Create the text widget and initialize everything to zero,
- * then set the necessary initial (non-NULL) values.
+ * then set the necessary initial (non-NULL) values. It is
+ * important that the 'set' tag and 'insert', 'current' mark
+ * pointers are all NULL to start.
*/
textPtr = (TkText *) ckalloc(sizeof(TkText));
@@ -420,11 +525,61 @@ Tk_TextObjCmd(clientData, interp, objc, objv)
textPtr->widgetCmd = Tcl_CreateObjCommand(interp,
Tk_PathName(textPtr->tkwin), TextWidgetObjCmd,
(ClientData) textPtr, TextCmdDeletedProc);
- textPtr->tree = TkBTreeCreate(textPtr);
- Tcl_InitHashTable(&textPtr->tagTable, TCL_STRING_KEYS);
- Tcl_InitHashTable(&textPtr->markTable, TCL_STRING_KEYS);
- Tcl_InitHashTable(&textPtr->windowTable, TCL_STRING_KEYS);
- Tcl_InitHashTable(&textPtr->imageTable, TCL_STRING_KEYS);
+
+ if (sharedPtr == NULL) {
+ sharedPtr = (TkSharedText *) ckalloc(sizeof(TkSharedText));
+ memset((VOID *) sharedPtr, 0, sizeof(TkSharedText));
+
+ sharedPtr->refCount = 0;
+ sharedPtr->peers = NULL;
+ sharedPtr->tree = TkBTreeCreate(sharedPtr);
+
+ Tcl_InitHashTable(&sharedPtr->tagTable, TCL_STRING_KEYS);
+ Tcl_InitHashTable(&sharedPtr->markTable, TCL_STRING_KEYS);
+ Tcl_InitHashTable(&sharedPtr->windowTable, TCL_STRING_KEYS);
+ Tcl_InitHashTable(&sharedPtr->imageTable, TCL_STRING_KEYS);
+ sharedPtr->undoStack = TkUndoInitStack(interp,0);
+ sharedPtr->undo = 1;
+ sharedPtr->isDirtyIncrement = 1;
+ sharedPtr->autoSeparators = 1;
+ sharedPtr->lastEditMode = TK_TEXT_EDIT_OTHER;
+ sharedPtr->stateEpoch = 0;
+ }
+
+ /* Add the new widget to the shared list */
+ textPtr->sharedTextPtr = sharedPtr;
+ sharedPtr->refCount++;
+ textPtr->next = sharedPtr->peers;
+ sharedPtr->peers = textPtr;
+ /*
+ * This refCount will be held until DestroyText is called.
+ * Note also that the later call to 'TkTextCreateDInfo'
+ * will add more refCounts.
+ */
+ textPtr->refCount = 1;
+
+ /*
+ * Specify start and end lines in the B-tree. The default
+ * is the same as the parent, but this can be adjusted to
+ * display more or less if the start, end where given
+ * as configuration options.
+ */
+ if (parent != NULL) {
+ textPtr->start = parent->start;
+ textPtr->end = parent->end;
+ } else {
+ textPtr->start = NULL;
+ textPtr->end = NULL;
+ }
+
+ /*
+ * Register with the B-tree. In some sense it would be best
+ * if we could do this later (after configuration options),
+ * so that any changes to start,end do not require a total
+ * recalculation.
+ */
+ TkBTreeAddClient(sharedPtr->tree, textPtr, textPtr->charHeight);
+
textPtr->state = TK_TEXT_STATE_NORMAL;
textPtr->relief = TK_RELIEF_FLAT;
textPtr->cursor = None;
@@ -433,33 +588,31 @@ Tk_TextObjCmd(clientData, interp, objc, objv)
textPtr->wrapMode = TEXT_WRAPMODE_CHAR;
textPtr->prevWidth = Tk_Width(new);
textPtr->prevHeight = Tk_Height(new);
- /*
- * This refCount will be held until DestroyText is called.
- * Note also that the following call to 'TkTextCreateDInfo'
- * will add more refCounts.
- */
- textPtr->refCount = 1;
+ /* This will add refCounts to textPtr */
TkTextCreateDInfo(textPtr);
- TkTextMakeByteIndex(textPtr->tree, 0, 0, &startIndex);
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
+ 0, 0, &startIndex);
TkTextSetYView(textPtr, &startIndex, 0);
textPtr->exportSelection = 1;
textPtr->pickEvent.type = LeaveNotify;
- textPtr->undoStack = TkUndoInitStack(interp,0);
- textPtr->undo = 1;
- textPtr->isDirtyIncrement = 1;
- textPtr->autoSeparators = 1;
- textPtr->lastEditMode = TK_TEXT_EDIT_OTHER;
+ textPtr->undo = textPtr->sharedTextPtr->undo;
+ textPtr->maxUndo = textPtr->sharedTextPtr->maxUndo;
+ textPtr->autoSeparators = textPtr->sharedTextPtr->autoSeparators;
textPtr->tabOptionPtr = NULL;
- textPtr->stateEpoch = 0;
/*
* Create the "sel" tag and the "current" and "insert" marks.
*/
textPtr->selBorder = NULL;
+ textPtr->inactiveSelBorder = NULL;
textPtr->selBorderWidth = 0;
textPtr->selBorderWidthPtr = NULL;
textPtr->selFgColorPtr = NULL;
+ /*
+ * Note: it is important that textPtr->selTagPtr is NULL before
+ * this initial call.
+ */
textPtr->selTagPtr = TkTextCreateTag(textPtr, "sel", NULL);
textPtr->selTagPtr->reliefString =
(char *) ckalloc(sizeof(DEF_TEXT_SELECT_RELIEF));
@@ -477,7 +630,7 @@ Tk_TextObjCmd(clientData, interp, objc, objv)
Tk_SetClass(textPtr->tkwin, "Text");
Tk_SetClassProcs(textPtr->tkwin, &textClass, (ClientData) textPtr);
- textPtr->optionTable = optionTable;
+ textPtr->optionTable = optionTable;
Tk_CreateEventHandler(textPtr->tkwin,
ExposureMask|StructureNotifyMask|FocusChangeMask,
@@ -499,8 +652,8 @@ Tk_TextObjCmd(clientData, interp, objc, objv)
return TCL_ERROR;
}
- Tcl_SetStringObj(Tcl_GetObjResult(interp), Tk_PathName(textPtr->tkwin),
- -1);
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj(Tk_PathName(textPtr->tkwin),-1));
return TCL_OK;
}
@@ -536,14 +689,14 @@ TextWidgetObjCmd(clientData, interp, objc, objv)
static CONST char *optionStrings[] = {
"bbox", "cget", "compare", "configure", "count", "debug",
"delete", "dlineinfo", "dump", "edit", "get", "image", "index",
- "insert", "mark", "replace", "scan", "search", "see",
+ "insert", "mark", "peer", "replace", "scan", "search", "see",
"tag", "window", "xview", "yview", (char *) NULL
};
enum options {
TEXT_BBOX, TEXT_CGET, TEXT_COMPARE, TEXT_CONFIGURE, TEXT_COUNT,
TEXT_DEBUG, TEXT_DELETE, TEXT_DLINEINFO, TEXT_DUMP, TEXT_EDIT,
TEXT_GET, TEXT_IMAGE, TEXT_INDEX, TEXT_INSERT, TEXT_MARK,
- TEXT_REPLACE, TEXT_SCAN, TEXT_SEARCH, TEXT_SEE,
+ TEXT_PEER, TEXT_REPLACE, TEXT_SCAN, TEXT_SEARCH, TEXT_SEE,
TEXT_TAG, TEXT_WINDOW, TEXT_XVIEW, TEXT_YVIEW
};
@@ -740,8 +893,9 @@ TextWidgetObjCmd(clientData, interp, objc, objv)
indexToPtr = tmpPtr;
}
- lastPtr = TkBTreeFindLine(textPtr->tree,
- TkBTreeNumLines(textPtr->tree));
+ lastPtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree,
+ textPtr,
+ TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr));
fromPtr = indexFromPtr->linePtr;
if (fromPtr == lastPtr) {
goto countDone;
@@ -753,7 +907,7 @@ TextWidgetObjCmd(clientData, interp, objc, objv)
*/
while (fromPtr != indexToPtr->linePtr) {
value += TkTextUpdateOneLine(textPtr, fromPtr, 0, NULL);
- fromPtr = TkBTreeNextLine(fromPtr);
+ fromPtr = TkBTreeNextLine(textPtr, fromPtr);
}
/*
* Now we need to adjust the count to add on the
@@ -770,7 +924,7 @@ TextWidgetObjCmd(clientData, interp, objc, objv)
if (index.byteIndex >= indexFromPtr->byteIndex) {
break;
}
- TkTextIndexForwBytes(&index, 1, &index);
+ TkTextIndexForwBytes(textPtr, &index, 1, &index);
value--;
}
if (indexToPtr->linePtr != lastPtr) {
@@ -781,7 +935,7 @@ TextWidgetObjCmd(clientData, interp, objc, objv)
if (index.byteIndex >= indexToPtr->byteIndex) {
break;
}
- TkTextIndexForwBytes(&index, 1, &index);
+ TkTextIndexForwBytes(textPtr, &index, 1, &index);
value++;
}
}
@@ -793,8 +947,8 @@ TextWidgetObjCmd(clientData, interp, objc, objv)
value = CountIndices(textPtr, indexFromPtr, indexToPtr,
COUNT_INDICES);
} else if (c == 'l' && !strncmp("-lines",option,length)) {
- value = TkBTreeLineIndex(indexToPtr->linePtr)
- - TkBTreeLineIndex(indexFromPtr->linePtr);
+ value = TkBTreeLinesTo(textPtr, indexToPtr->linePtr)
+ - TkBTreeLinesTo(textPtr, indexFromPtr->linePtr);
} else if (c == 'u' && !strncmp("-update",option,length)) {
update = 1;
continue;
@@ -809,8 +963,8 @@ TextWidgetObjCmd(clientData, interp, objc, objv)
} else if (c == 'y' && !strncmp("-ypixels",option,length)) {
if (update) {
TkTextUpdateLineMetrics(textPtr,
- TkBTreeLineIndex(indexFromPtr->linePtr),
- TkBTreeLineIndex(indexToPtr->linePtr), -1);
+ TkBTreeLinesTo(textPtr, indexFromPtr->linePtr),
+ TkBTreeLinesTo(textPtr, indexToPtr->linePtr), -1);
}
value = TkTextIndexYPixels(textPtr, indexToPtr)
- TkTextIndexYPixels(textPtr, indexFromPtr);
@@ -895,7 +1049,7 @@ TextWidgetObjCmd(clientData, interp, objc, objv)
} else {
indexPtr2 = NULL;
}
- DeleteChars(textPtr, indexPtr1, indexPtr2, 0);
+ DeleteChars(NULL, textPtr, indexPtr1, indexPtr2, 1);
} else {
int i;
/*
@@ -990,8 +1144,8 @@ TextWidgetObjCmd(clientData, interp, objc, objv)
* We don't need to check the return value
* because all indices are preparsed above.
*/
- DeleteChars(textPtr, &indices[i],
- &indices[i+1], 0);
+ DeleteChars(NULL, textPtr, &indices[i],
+ &indices[i+1], 1);
}
}
ckfree((char *) indices);
@@ -1085,7 +1239,8 @@ TextWidgetObjCmd(clientData, interp, objc, objv)
goto done;
}
if (i+1 == objc) {
- TkTextIndexForwChars(NULL, index1Ptr, 1, &index2, COUNT_INDICES);
+ TkTextIndexForwChars(NULL, index1Ptr,
+ 1, &index2, COUNT_INDICES);
index2Ptr = &index2;
} else {
index2Ptr = TkTextGetIndexFromObj(interp, textPtr,
@@ -1165,8 +1320,9 @@ TextWidgetObjCmd(clientData, interp, objc, objv)
goto done;
}
if (textPtr->state == TK_TEXT_STATE_NORMAL) {
- result = TextInsertCmd(textPtr, interp, objc-3, objv+3,
- indexPtr, 0);
+ result = TextInsertCmd(NULL, textPtr, interp,
+ objc-3, objv+3,
+ indexPtr, 1);
}
break;
}
@@ -1174,6 +1330,10 @@ TextWidgetObjCmd(clientData, interp, objc, objv)
result = TkTextMarkCmd(textPtr, interp, objc, objv);
break;
}
+ case TEXT_PEER: {
+ result = TextPeerCmd(textPtr, interp, objc, objv);
+ break;
+ }
case TEXT_REPLACE: {
CONST TkTextIndex *indexFromPtr, *indexToPtr;
@@ -1219,7 +1379,7 @@ TextWidgetObjCmd(clientData, interp, objc, objv)
* off-screen.
*/
byteIndex = textPtr->topIndex.byteIndex;
- lineNum = TkBTreeLineIndex(textPtr->topIndex.linePtr);
+ lineNum = TkBTreeLinesTo(textPtr, textPtr->topIndex.linePtr);
TkTextMarkSegToIndex(textPtr, textPtr->insertMarkPtr,
&index);
@@ -1250,31 +1410,32 @@ TextWidgetObjCmd(clientData, interp, objc, objv)
result = TextReplaceCmd(textPtr, interp,
indexFromPtr, indexToPtr,
- objc, objv, 1);
+ objc, objv, 0);
if (result == TCL_OK) {
/*
* Move the insertion position to the correct
* place
*/
- TkTextIndexForwChars(NULL, indexFromPtr, deleteInsertOffset,
+ TkTextIndexForwChars(NULL, indexFromPtr,
+ deleteInsertOffset,
&index, COUNT_INDICES);
- TkBTreeUnlinkSegment(textPtr->tree,
- textPtr->insertMarkPtr,
+ TkBTreeUnlinkSegment(textPtr->insertMarkPtr,
textPtr->insertMarkPtr->body.mark.linePtr);
TkBTreeLinkSegment(textPtr->insertMarkPtr, &index);
}
} else {
result = TextReplaceCmd(textPtr, interp,
indexFromPtr, indexToPtr,
- objc, objv, 0);
+ objc, objv, 1);
}
if (result == TCL_OK) {
/*
* Now ensure the top-line is in the right
* place
*/
- TkTextMakeByteIndex(textPtr->tree, lineNum,
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree,
+ textPtr, lineNum,
byteIndex, &index);
TkTextSetYView(textPtr, &index, TK_TEXT_NOPIXELADJUST);
}
@@ -1320,6 +1481,190 @@ TextWidgetObjCmd(clientData, interp, objc, objv)
}
/*
+ *--------------------------------------------------------------
+ *
+ * SharedTextObjCmd --
+ *
+ * This procedure is invoked to process commands on the shared
+ * portion of a text widget. Currently it is not actually exported
+ * as a Tcl command, and is only used internally to process parts
+ * of undo/redo scripts. See the user documentation for 'text' for
+ * details on what it does - the only subcommands it currently
+ * supports are 'insert' and 'delete'.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation for "text".
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+SharedTextObjCmd(clientData, interp, objc, objv)
+ ClientData clientData; /* Information about shared test
+ * B-tree. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ register TkSharedText *sharedPtr = (TkSharedText *) clientData;
+ int result = TCL_OK;
+ int index;
+
+ static CONST char *optionStrings[] = {
+ "delete", "insert", (char *) NULL
+ };
+ enum options {
+ TEXT_DELETE, TEXT_INSERT
+ };
+
+ if (objc < 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
+ return TCL_ERROR;
+ }
+
+ if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0,
+ &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ switch ((enum options) index) {
+ case TEXT_DELETE: {
+ if (objc < 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "index1 ?index2 ...?");
+ result = TCL_ERROR;
+ goto done;
+ }
+ if (objc < 5) {
+ /*
+ * Simple case requires no predetermination of indices.
+ */
+ TkTextIndex index1;
+
+ /*
+ * Parse the starting and stopping indices.
+ */
+
+ result = TkTextSharedGetObjIndex(interp, sharedPtr,
+ objv[2], &index1);
+ if (result != TCL_OK) {
+ goto done;
+ }
+ if (objc == 4) {
+ TkTextIndex index2;
+ result = TkTextSharedGetObjIndex(interp, sharedPtr,
+ objv[3], &index2);
+ if (result != TCL_OK) {
+ goto done;
+ }
+ DeleteChars(sharedPtr, NULL, &index1, &index2, 1);
+ } else {
+ DeleteChars(sharedPtr, NULL, &index1, NULL, 1);
+ }
+ } else {
+ /* Too many arguments */
+ result = TCL_ERROR;
+ }
+ break;
+ }
+ case TEXT_INSERT: {
+ TkTextIndex index1;
+ if (objc < 4) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "index chars ?tagList chars tagList ...?");
+ result = TCL_ERROR;
+ goto done;
+ }
+ result = TkTextSharedGetObjIndex(interp, sharedPtr,
+ objv[2], &index1);
+ if (result != TCL_OK) {
+ goto done;
+ }
+ result = TextInsertCmd(sharedPtr, NULL, interp, objc-3, objv+3,
+ &index1, 1);
+ break;
+ }
+ }
+
+ done:
+ return result;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TextPeerCmd --
+ *
+ * This procedure is invoked to process the "text peer" Tcl
+ * command. See the user documentation for details on what it
+ * does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+TextPeerCmd(textPtr, interp, objc, objv)
+ TkText *textPtr; /* Information about text widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ Tk_Window tkwin = textPtr->tkwin;
+ int index;
+
+ static CONST char *peerOptionStrings[] = {
+ "create", "names", (char *) NULL
+ };
+ enum peerOptions {
+ PEER_CREATE, PEER_NAMES
+ };
+
+ if (objc < 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "option ?arg arg ...?");
+ return TCL_ERROR;
+ }
+
+ if (Tcl_GetIndexFromObj(interp, objv[2], peerOptionStrings,
+ "peer option", 0, &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ switch ((enum editOptions)index) {
+ case PEER_CREATE: {
+ if (objc < 4) {
+ Tcl_WrongNumArgs(interp, 3, objv, "pathName ?options?");
+ return TCL_ERROR;
+ }
+ return CreateWidget(textPtr->sharedTextPtr, tkwin,
+ interp, textPtr, objc-2, objv+2);
+ }
+ case PEER_NAMES: {
+ TkText *tPtr = textPtr->sharedTextPtr->peers;
+ if (objc > 3) {
+ Tcl_WrongNumArgs(interp, 3, objv, NULL);
+ return TCL_ERROR;
+ }
+ while (tPtr != NULL) {
+ if (tPtr != textPtr) {
+ Tcl_AppendElement(interp, Tk_PathName(tPtr->tkwin));
+ }
+ tPtr = tPtr->next;
+ }
+ }
+ }
+
+ return TCL_OK;
+}
+
+/*
*----------------------------------------------------------------------
*
* TextReplaceCmd --
@@ -1332,20 +1677,24 @@ TextWidgetObjCmd(clientData, interp, objc, objv)
*
* Side effects:
* See the user documentation.
+ *
+ * If 'viewUpdate' is false, then textPtr->topIndex may no longer
+ * be a valid index after this function returns. The caller is
+ * responsible for ensuring a correct index is in place.
*
*----------------------------------------------------------------------
*/
static int
TextReplaceCmd(textPtr, interp, indexFromPtr, indexToPtr,
- objc, objv, noViewUpdate)
+ objc, objv, viewUpdate)
TkText *textPtr; /* Information about text widget. */
Tcl_Interp *interp; /* Current interpreter. */
CONST TkTextIndex *indexFromPtr;/* Index from which to replace */
CONST TkTextIndex *indexToPtr; /* Index to which to replace */
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
- int noViewUpdate; /* Don't update the view if set */
+ int viewUpdate; /* Update vertical view if set. */
{
int result;
/*
@@ -1356,22 +1705,23 @@ TextReplaceCmd(textPtr, interp, indexFromPtr, indexToPtr,
* that the autoSeparators setting is off, so that we don't
* get an undo-separator between the delete and insert.
*/
- int origAutoSep = textPtr->autoSeparators;
+ int origAutoSep = textPtr->sharedTextPtr->autoSeparators;
- if (textPtr->undo) {
- textPtr->autoSeparators = 0;
- if (origAutoSep && textPtr->lastEditMode != TK_TEXT_EDIT_REPLACE) {
- TkUndoInsertUndoSeparator(textPtr->undoStack);
+ if (textPtr->sharedTextPtr->undo) {
+ textPtr->sharedTextPtr->autoSeparators = 0;
+ if (origAutoSep
+ && textPtr->sharedTextPtr->lastEditMode != TK_TEXT_EDIT_REPLACE) {
+ TkUndoInsertUndoSeparator(textPtr->sharedTextPtr->undoStack);
}
}
- DeleteChars(textPtr, indexFromPtr, indexToPtr, noViewUpdate);
- result = TextInsertCmd(textPtr, interp, objc-4, objv+4,
- indexFromPtr, noViewUpdate);
+ DeleteChars(NULL, textPtr, indexFromPtr, indexToPtr, viewUpdate);
+ result = TextInsertCmd(NULL, textPtr, interp, objc-4, objv+4,
+ indexFromPtr, viewUpdate);
- if (textPtr->undo) {
- textPtr->lastEditMode = TK_TEXT_EDIT_REPLACE;
- textPtr->autoSeparators = origAutoSep;
+ if (textPtr->sharedTextPtr->undo) {
+ textPtr->sharedTextPtr->lastEditMode = TK_TEXT_EDIT_REPLACE;
+ textPtr->sharedTextPtr->autoSeparators = origAutoSep;
}
return result;
@@ -1450,7 +1800,8 @@ DestroyText(textPtr)
Tcl_HashSearch search;
Tcl_HashEntry *hPtr;
TkTextTag *tagPtr;
-
+ TkSharedText *sharedTextPtr = textPtr->sharedTextPtr;
+
/*
* Free up all the stuff that requires special handling. We have
* already called let Tk_FreeConfigOptions to handle all the standard
@@ -1463,33 +1814,119 @@ DestroyText(textPtr)
TkTextFreeDInfo(textPtr);
textPtr->dInfoPtr = NULL;
- TkBTreeDestroy(textPtr->tree);
- for (hPtr = Tcl_FirstHashEntry(&textPtr->tagTable, &search);
- hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
- tagPtr = (TkTextTag *) Tcl_GetHashValue(hPtr);
- TkTextFreeTag(textPtr, tagPtr);
- }
- Tcl_DeleteHashTable(&textPtr->tagTable);
- for (hPtr = Tcl_FirstHashEntry(&textPtr->markTable, &search);
- hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
- ckfree((char *) Tcl_GetHashValue(hPtr));
- }
- Tcl_DeleteHashTable(&textPtr->markTable);
+ /*
+ * Remove ourselves from the peer list
+ */
+ if (sharedTextPtr->peers == textPtr) {
+ sharedTextPtr->peers = textPtr->next;
+ } else {
+ TkText *nextPtr = sharedTextPtr->peers;
+ while (nextPtr != NULL) {
+ if (nextPtr->next == textPtr) {
+ nextPtr->next = textPtr->next;
+ break;
+ }
+ nextPtr = nextPtr->next;
+ }
+ }
+
+ /*
+ * Always clean up the widget-specific tags first. Common tags
+ * (i.e. most) will only be cleaned up when the shared structure
+ * is cleaned up.
+ *
+ * We also need to clean up widget-specific marks ('insert',
+ * 'current'), since otherwise marks will never disappear from
+ * the B-tree.
+ */
+
+ TkTextDeleteTag(textPtr, textPtr->selTagPtr);
+ TkBTreeUnlinkSegment(textPtr->insertMarkPtr,
+ textPtr->insertMarkPtr->body.mark.linePtr);
+ ckfree((char *) textPtr->insertMarkPtr);
+ TkBTreeUnlinkSegment(textPtr->currentMarkPtr,
+ textPtr->currentMarkPtr->body.mark.linePtr);
+ ckfree((char *) textPtr->currentMarkPtr);
+
+ /*
+ * Now we've cleaned up everything of relevance to us in the B-tree,
+ * so we disassociate outselves from it.
+ *
+ * When the refCount reaches zero, it's time to clean up
+ * the shared portion of the text widget
+ */
+ sharedTextPtr->refCount--;
+
+ if (sharedTextPtr->refCount > 0) {
+ TkBTreeRemoveClient(sharedTextPtr->tree, textPtr);
+
+ /* Free up any embedded windows which belong to this widget. */
+ for (hPtr = Tcl_FirstHashEntry(&sharedTextPtr->windowTable, &search);
+ hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
+ TkTextEmbWindowClient *loop;
+ TkTextSegment *ewPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
+
+ loop = ewPtr->body.ew.clients;
+ if (loop->textPtr == textPtr) {
+ ewPtr->body.ew.clients = loop->next;
+ TkTextWinFreeClient(hPtr, loop);
+ } else {
+ TkTextEmbWindowClient *client = ewPtr->body.ew.clients;
+ client = loop->next;
+ while (client != NULL) {
+ if (client->textPtr == textPtr) {
+ loop->next = client->next;
+ TkTextWinFreeClient(hPtr, client);
+ break;
+ } else {
+ loop = loop->next;
+ }
+ client = loop->next;
+ }
+ }
+ }
+ } else {
+ /*
+ * No need to call 'TkBTreeRemoveClient' first, since this
+ * will do everything in one go, more quickly.
+ */
+ TkBTreeDestroy(sharedTextPtr->tree);
+
+ for (hPtr = Tcl_FirstHashEntry(&sharedTextPtr->tagTable, &search);
+ hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
+ tagPtr = (TkTextTag *) Tcl_GetHashValue(hPtr);
+ /*
+ * No need to use 'TkTextDeleteTag' since we've already
+ * removed the B-tree completely
+ */
+ TkTextFreeTag(textPtr, tagPtr);
+ }
+ Tcl_DeleteHashTable(&sharedTextPtr->tagTable);
+ for (hPtr = Tcl_FirstHashEntry(&sharedTextPtr->markTable, &search);
+ hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
+ ckfree((char *) Tcl_GetHashValue(hPtr));
+ }
+ Tcl_DeleteHashTable(&sharedTextPtr->markTable);
+ TkUndoFreeStack(sharedTextPtr->undoStack);
+
+ Tcl_DeleteHashTable(&sharedTextPtr->windowTable);
+ Tcl_DeleteHashTable(&sharedTextPtr->imageTable);
+
+ if (sharedTextPtr->bindingTable != NULL) {
+ Tk_DeleteBindingTable(sharedTextPtr->bindingTable);
+ }
+ }
+
if (textPtr->tabArrayPtr != NULL) {
ckfree((char *) textPtr->tabArrayPtr);
}
if (textPtr->insertBlinkHandler != NULL) {
Tcl_DeleteTimerHandler(textPtr->insertBlinkHandler);
}
- if (textPtr->bindingTable != NULL) {
- Tk_DeleteBindingTable(textPtr->bindingTable);
- }
- TkUndoFreeStack(textPtr->undoStack);
textPtr->tkwin = NULL;
textPtr->refCount--;
- Tcl_DeleteCommandFromToken(textPtr->interp,
- textPtr->widgetCmd);
+ Tcl_DeleteCommandFromToken(textPtr->interp, textPtr->widgetCmd);
if (textPtr->refCount == 0) {
ckfree((char *) textPtr);
}
@@ -1533,7 +1970,15 @@ ConfigureText(interp, textPtr, objc, objv)
return TCL_ERROR;
}
- TkUndoSetDepth(textPtr->undoStack, textPtr->maxUndo);
+ /*
+ * Copy down shared flags
+ */
+ textPtr->sharedTextPtr->undo = textPtr->undo;
+ textPtr->sharedTextPtr->maxUndo = textPtr->maxUndo;
+ textPtr->sharedTextPtr->autoSeparators = textPtr->autoSeparators;
+
+ TkUndoSetDepth(textPtr->sharedTextPtr->undoStack,
+ textPtr->sharedTextPtr->maxUndo);
/*
* A few other options also need special processing, such as parsing
@@ -1542,6 +1987,78 @@ ConfigureText(interp, textPtr, objc, objv)
Tk_SetBackgroundFromBorder(textPtr->tkwin, textPtr->border);
+ if (mask & TK_TEXT_LINE_RANGE) {
+ int start, end, current;
+
+ /*
+ * Line start and/or end have been adjusted. We need to validate
+ * the first displayed line and arrange for re-layout.
+ */
+ TkBTreeClientRangeChanged(textPtr, textPtr->charHeight);
+
+ if (textPtr->start != NULL) {
+ start = TkBTreeLinesTo(NULL, textPtr->start);
+ } else {
+ start = 0;
+ }
+ if (textPtr->end != NULL) {
+ end = TkBTreeLinesTo(NULL, textPtr->end);
+ } else {
+ end = TkBTreeNumLines(textPtr->sharedTextPtr->tree, NULL);
+ }
+ if (start > end) {
+ Tcl_AppendResult(interp, "-startline must be less than or equal to -endline", NULL);
+ Tk_RestoreSavedOptions(&savedOptions);
+ return TCL_ERROR;
+ }
+ current = TkBTreeLinesTo(NULL, textPtr->topIndex.linePtr);
+ if (current < start || current > end) {
+ TkTextSearch search;
+ TkTextIndex index1, first, last;
+ int selChanged = 0;
+
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, NULL,
+ start, 0, &index1);
+ TkTextSetYView(textPtr, &index1, 0);
+ /*
+ * We may need to adjust the selection. So we have to
+ * check whether the "sel" tag was applied to anything
+ * outside the current start,end.
+ */
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, NULL, 0, 0, &first);
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, NULL,
+ TkBTreeNumLines(textPtr->sharedTextPtr->tree, NULL),
+ 0, &last);
+ TkBTreeStartSearch(&first, &last, textPtr->selTagPtr, &search);
+ if (!TkBTreeCharTagged(&first, textPtr->selTagPtr)
+ && !TkBTreeNextTag(&search)) {
+ /* Nothing tagged with "sel" */
+ } else {
+ int line = TkBTreeLinesTo(NULL, search.curIndex.linePtr);
+ if (line < start) {
+ selChanged = 1;
+ } else {
+ TkTextLine *linePtr = search.curIndex.linePtr;
+ while (TkBTreeNextTag(&search)) {
+ linePtr = search.curIndex.linePtr;
+ }
+ line = TkBTreeLinesTo(NULL, linePtr);
+ if (line >= end) {
+ selChanged = 1;
+ }
+ }
+ }
+ if (selChanged) {
+ /*
+ * Send an event that the selection has changed, and
+ * abort any partial-selections in progress.
+ */
+ TkTextSelectionEvent(textPtr);
+ textPtr->abortSelections = 1;
+ }
+ }
+ }
+
/*
* Don't allow negative spacings.
*/
@@ -1589,7 +2106,6 @@ ConfigureText(interp, textPtr, objc, objv)
}
textPtr->selTagPtr->fgColor = textPtr->selFgColorPtr;
textPtr->selTagPtr->affectsDisplay = 0;
- textPtr->selTagPtr->affectsDisplay = 0;
textPtr->selTagPtr->affectsDisplayGeometry = 0;
if ((textPtr->selTagPtr->elideString != NULL)
|| (textPtr->selTagPtr->tkfont != None)
@@ -1615,7 +2131,7 @@ ConfigureText(interp, textPtr, objc, objv)
|| (textPtr->selTagPtr->underlineString != NULL)) {
textPtr->selTagPtr->affectsDisplay = 1;
}
- TkTextRedrawTag(textPtr, (TkTextIndex *) NULL, (TkTextIndex *) NULL,
+ TkTextRedrawTag(NULL, textPtr, (TkTextIndex *) NULL, (TkTextIndex *) NULL,
textPtr->selTagPtr, 1);
/*
@@ -1627,9 +2143,9 @@ ConfigureText(interp, textPtr, objc, objv)
TkTextSearch search;
TkTextIndex first, last;
- TkTextMakeByteIndex(textPtr->tree, 0, 0, &first);
- TkTextMakeByteIndex(textPtr->tree,
- TkBTreeNumLines(textPtr->tree), 0, &last);
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr, 0, 0, &first);
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
+ TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr), 0, &last);
TkBTreeStartSearch(&first, &last, textPtr->selTagPtr, &search);
if (TkBTreeCharTagged(&first, textPtr->selTagPtr)
|| TkBTreeNextTag(&search)) {
@@ -1805,7 +2321,7 @@ TextEventProc(clientData, eventPtr)
* Hence we don't want the automatic config options freeing
* process to delete them as well.
*/
-
+
textPtr->selBorder = NULL;
textPtr->selBorderWidthPtr = NULL;
textPtr->selBorderWidth = 0;
@@ -1846,16 +2362,16 @@ TextEventProc(clientData, eventPtr)
textPtr->flags &= ~(GOT_FOCUS | INSERT_ON);
textPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
}
-#ifndef ALWAYS_SHOW_SELECTION
- TkTextRedrawTag(textPtr, NULL, NULL, textPtr->selTagPtr, 1);
-#endif
+ if (textPtr->inactiveSelBorder != textPtr->selBorder) {
+ TkTextRedrawTag(NULL, textPtr, NULL, NULL, textPtr->selTagPtr, 1);
+ }
TkTextMarkSegToIndex(textPtr, textPtr->insertMarkPtr, &index);
TkTextIndexForwChars(NULL, &index, 1, &index2, COUNT_INDICES);
/*
* While we wish to redisplay, no heights have changed, so
* no need to call TkTextInvalidateLineMetrics
*/
- TkTextChanged(textPtr, &index, &index2);
+ TkTextChanged(NULL, textPtr, &index, &index2);
if (textPtr->highlightWidth > 0) {
TkTextRedrawRegion(textPtr, 0, 0, textPtr->highlightWidth,
textPtr->highlightWidth);
@@ -1921,25 +2437,34 @@ TextCmdDeletedProc(clientData)
* The characters in "stringPtr" get added to the text just before
* the character indicated by "indexPtr".
*
- * Unless 'noViewUpdate' is set, we may adjust the window
+ * If 'viewUpdate' is true, we may adjust the window
* contents' y-position, and scrollbar setting.
*
*----------------------------------------------------------------------
*/
static int
-InsertChars(textPtr, indexPtr, stringPtr, noViewUpdate)
+InsertChars(sharedTextPtr, textPtr, indexPtr, stringPtr, viewUpdate)
+ TkSharedText *sharedTextPtr;
TkText *textPtr; /* Overall information about text widget. */
TkTextIndex *indexPtr; /* Where to insert new characters. May be
* modified if the index is not valid
* for insertion (e.g. if at "end"). */
Tcl_Obj *stringPtr; /* Null-terminated string containing new
* information to add to text. */
- int noViewUpdate; /* Don't update the view if set */
+ int viewUpdate; /* Update the view if set */
{
- int lineIndex, resetView, offset, length;
+ int lineIndex, length;
+ TkText *tPtr;
+ int *lineAndByteIndex;
+ int resetViewCount;
+ int pixels[2*PIXEL_CLIENTS];
CONST char *string = Tcl_GetStringFromObj(stringPtr, &length);
+
+ if (sharedTextPtr == NULL) {
+ sharedTextPtr = textPtr->sharedTextPtr;
+ }
/*
* Don't allow insertions on the last (dummy) line of the text.
@@ -1947,10 +2472,11 @@ InsertChars(textPtr, indexPtr, stringPtr, noViewUpdate)
* modified.
*/
- lineIndex = TkBTreeLineIndex(indexPtr->linePtr);
- if (lineIndex == TkBTreeNumLines(textPtr->tree)) {
+ lineIndex = TkBTreeLinesTo(textPtr, indexPtr->linePtr);
+ if (lineIndex == TkBTreeNumLines(sharedTextPtr->tree, textPtr)) {
lineIndex--;
- TkTextMakeByteIndex(textPtr->tree, lineIndex, 1000000, indexPtr);
+ TkTextMakeByteIndex(sharedTextPtr->tree, textPtr, lineIndex,
+ 1000000, indexPtr);
}
/*
@@ -1960,51 +2486,83 @@ InsertChars(textPtr, indexPtr, stringPtr, noViewUpdate)
* after the insertion, since the insertion could invalidate it.
*/
- resetView = offset = 0;
- if (indexPtr->linePtr == textPtr->topIndex.linePtr) {
- resetView = 1;
- offset = textPtr->topIndex.byteIndex;
- if (offset > indexPtr->byteIndex) {
- offset += length;
+ resetViewCount = 0;
+ if (sharedTextPtr->refCount > PIXEL_CLIENTS) {
+ lineAndByteIndex = (int*)ckalloc(sizeof(int)*
+ 2*sharedTextPtr->refCount);
+ } else {
+ lineAndByteIndex = pixels;
+ }
+ for (tPtr = sharedTextPtr->peers; tPtr != NULL ;
+ tPtr = tPtr->next) {
+ lineAndByteIndex[resetViewCount] = -1;
+ if (indexPtr->linePtr == tPtr->topIndex.linePtr) {
+ lineAndByteIndex[resetViewCount] =
+ TkBTreeLinesTo(tPtr, indexPtr->linePtr);
+ lineAndByteIndex[resetViewCount+1] = tPtr->topIndex.byteIndex;
+ if (lineAndByteIndex[resetViewCount+1] > indexPtr->byteIndex) {
+ lineAndByteIndex[resetViewCount+1] += length;
+ }
}
+ resetViewCount += 2;
}
- TkTextChanged(textPtr, indexPtr, indexPtr);
- textPtr->stateEpoch ++;
- TkBTreeInsertChars(indexPtr, string);
+
+ TkTextChanged(sharedTextPtr, NULL, indexPtr, indexPtr);
+
+ sharedTextPtr->stateEpoch++;
+
+ TkBTreeInsertChars(sharedTextPtr->tree, indexPtr, string);
/*
* Push the insertion on the undo stack
*/
- if (textPtr->undo) {
+ if (sharedTextPtr->undo) {
TkTextIndex toIndex;
- if (textPtr->autoSeparators &&
- textPtr->lastEditMode != TK_TEXT_EDIT_INSERT) {
- TkUndoInsertUndoSeparator(textPtr->undoStack);
+ if (sharedTextPtr->autoSeparators &&
+ sharedTextPtr->lastEditMode != TK_TEXT_EDIT_INSERT) {
+ TkUndoInsertUndoSeparator(sharedTextPtr->undoStack);
}
- textPtr->lastEditMode = TK_TEXT_EDIT_INSERT;
+ sharedTextPtr->lastEditMode = TK_TEXT_EDIT_INSERT;
- TkTextIndexForwBytes(indexPtr, length, &toIndex);
+ TkTextIndexForwBytes(textPtr, indexPtr, length, &toIndex);
TextPushUndoAction(textPtr, stringPtr, 1, indexPtr, &toIndex);
}
- UpdateDirtyFlag(textPtr);
-
- if (resetView && !noViewUpdate) {
- TkTextIndex newTop;
- TkTextMakeByteIndex(textPtr->tree, lineIndex, 0, &newTop);
- TkTextIndexForwBytes(&newTop, offset, &newTop);
- TkTextSetYView(textPtr, &newTop, 0);
+ UpdateDirtyFlag(sharedTextPtr);
+
+ resetViewCount = 0;
+ for (tPtr = sharedTextPtr->peers; tPtr != NULL ;
+ tPtr = tPtr->next) {
+ if (lineAndByteIndex[resetViewCount] != -1) {
+ if ((tPtr != textPtr) || viewUpdate) {
+ TkTextIndex newTop;
+ TkTextMakeByteIndex(sharedTextPtr->tree, tPtr,
+ lineAndByteIndex[resetViewCount],
+ 0, &newTop);
+ TkTextIndexForwBytes(tPtr, &newTop,
+ lineAndByteIndex[resetViewCount+1],
+ &newTop);
+ TkTextSetYView(tPtr, &newTop, 0);
+ }
+ }
+ resetViewCount += 2;
}
-
+ if (sharedTextPtr->refCount > PIXEL_CLIENTS) {
+ ckfree((char*)lineAndByteIndex);
+ }
+
/*
* Invalidate any selection retrievals in progress.
*/
- textPtr->abortSelections = 1;
-
+ for (tPtr = sharedTextPtr->peers; tPtr != NULL ;
+ tPtr = tPtr->next) {
+ tPtr->abortSelections = 1;
+ }
+
/* For convenience, return the length of the string */
return length;
}
@@ -2036,35 +2594,31 @@ TextPushUndoAction (textPtr, undoString, insert, index1Ptr, index2Ptr)
CONST TkTextIndex *index1Ptr;/* Index describing first location */
CONST TkTextIndex *index2Ptr;/* Index describing second location */
{
+ TkUndoSubAtom *iAtom, *dAtom;
+
/* Create the helpers */
- Tcl_Obj *cmdNameObj = Tcl_NewObj();
Tcl_Obj *seeInsertObj = Tcl_NewObj();
Tcl_Obj *markSet1InsertObj = Tcl_NewObj();
Tcl_Obj *markSet2InsertObj = Tcl_NewObj();
Tcl_Obj *insertCmdObj = Tcl_NewObj();
Tcl_Obj *deleteCmdObj = Tcl_NewObj();
- Tcl_Obj *insertCmd = Tcl_NewObj();
- Tcl_Obj *deleteCmd = Tcl_NewObj();
-
/* Get the index positions */
- Tcl_Obj *index1Obj = TkTextNewIndexObj(textPtr, index1Ptr);
- Tcl_Obj *index2Obj = TkTextNewIndexObj(textPtr, index2Ptr);
-
- /* Get the fully qualified name */
- Tcl_GetCommandFullName(textPtr->interp, textPtr->widgetCmd, cmdNameObj);
+ Tcl_Obj *index1Obj = TkTextNewIndexObj(NULL, index1Ptr);
+ Tcl_Obj *index2Obj = TkTextNewIndexObj(NULL, index2Ptr);
/* These need refCounts, because they are used more than once below */
- Tcl_IncrRefCount(cmdNameObj);
Tcl_IncrRefCount(seeInsertObj);
Tcl_IncrRefCount(index1Obj);
Tcl_IncrRefCount(index2Obj);
- Tcl_ListObjAppendElement(NULL, seeInsertObj, cmdNameObj);
+ Tcl_ListObjAppendElement(NULL, seeInsertObj,
+ Tcl_NewStringObj(Tk_PathName(textPtr->tkwin), -1));
Tcl_ListObjAppendElement(NULL, seeInsertObj, Tcl_NewStringObj("see",3));
Tcl_ListObjAppendElement(NULL, seeInsertObj, Tcl_NewStringObj("insert",6));
- Tcl_ListObjAppendElement(NULL, markSet1InsertObj, cmdNameObj);
+ Tcl_ListObjAppendElement(NULL, markSet1InsertObj,
+ Tcl_NewStringObj(Tk_PathName(textPtr->tkwin), -1));
Tcl_ListObjAppendElement(NULL, markSet1InsertObj,
Tcl_NewStringObj("mark",4));
Tcl_ListObjAppendElement(NULL, markSet1InsertObj,
@@ -2075,25 +2629,40 @@ TextPushUndoAction (textPtr, undoString, insert, index1Ptr, index2Ptr)
Tcl_ListObjAppendElement(NULL, markSet1InsertObj, index1Obj);
Tcl_ListObjAppendElement(NULL, markSet2InsertObj, index2Obj);
- Tcl_ListObjAppendElement(NULL, insertCmdObj, cmdNameObj);
Tcl_ListObjAppendElement(NULL, insertCmdObj, Tcl_NewStringObj("insert",6));
Tcl_ListObjAppendElement(NULL, insertCmdObj, index1Obj);
- /* Only use of 'undoString' */
+ /* Only use of 'undoString' is here */
Tcl_ListObjAppendElement(NULL, insertCmdObj, undoString);
- Tcl_ListObjAppendElement(NULL, deleteCmdObj, cmdNameObj);
Tcl_ListObjAppendElement(NULL, deleteCmdObj, Tcl_NewStringObj("delete",6));
Tcl_ListObjAppendElement(NULL, deleteCmdObj, index1Obj);
Tcl_ListObjAppendElement(NULL, deleteCmdObj, index2Obj);
- Tcl_ListObjAppendElement(NULL, insertCmd, insertCmdObj);
- Tcl_ListObjAppendElement(NULL, insertCmd, markSet2InsertObj);
- Tcl_ListObjAppendElement(NULL, insertCmd, seeInsertObj);
- Tcl_ListObjAppendElement(NULL, deleteCmd, deleteCmdObj);
- Tcl_ListObjAppendElement(NULL, deleteCmd, markSet1InsertObj);
- Tcl_ListObjAppendElement(NULL, deleteCmd, seeInsertObj);
+ /*
+ * Note: we don't wish to use textPtr->widgetCmd in these callbacks
+ * because if we delete the textPtr, but peers still exist, we will
+ * then have references to a non-existent Tcl_Command in the undo
+ * stack, which will lead to crashes later. Also, the behaviour of
+ * the widget wrt bindings (%W substitutions) always uses the widget
+ * path name, so there is no good reason the undo stack should do
+ * otherwise.
+ *
+ * For the 'insert' and 'delete' actions, we have to register a
+ * functional callback, because these actions are defined to
+ * operate on the underlying data shared by all peers.
+ */
+ iAtom = TkUndoMakeSubAtom(&TextUndoRedoCallback,
+ (ClientData)textPtr->sharedTextPtr,
+ insertCmdObj, NULL);
+ TkUndoMakeCmdSubAtom(NULL, markSet2InsertObj, iAtom);
+ TkUndoMakeCmdSubAtom(NULL, seeInsertObj, iAtom);
+
+ dAtom = TkUndoMakeSubAtom(&TextUndoRedoCallback,
+ (ClientData)textPtr->sharedTextPtr,
+ deleteCmdObj, NULL);
+ TkUndoMakeCmdSubAtom(NULL, markSet1InsertObj, dAtom);
+ TkUndoMakeCmdSubAtom(NULL, seeInsertObj, dAtom);
- Tcl_DecrRefCount(cmdNameObj);
Tcl_DecrRefCount(seeInsertObj);
Tcl_DecrRefCount(index1Obj);
Tcl_DecrRefCount(index2Obj);
@@ -2102,13 +2671,14 @@ TextPushUndoAction (textPtr, undoString, insert, index1Ptr, index2Ptr)
* Depending whether the action is to insert or delete, we provide
* the appropriate second and third arguments to TkUndoPushAction.
* (The first is the 'actionCommand', and the second the
- * 'revertCommand'). The final '1' says we are providing a list
- * of scripts to execute rather than a single script.
+ * 'revertCommand').
*/
if (insert) {
- TkUndoPushAction(textPtr->undoStack, insertCmd, deleteCmd, 1);
+ TkUndoPushAction(textPtr->sharedTextPtr->undoStack,
+ iAtom, dAtom);
} else {
- TkUndoPushAction(textPtr->undoStack, deleteCmd, insertCmd, 1);
+ TkUndoPushAction(textPtr->sharedTextPtr->undoStack,
+ dAtom, iAtom);
}
}
@@ -2116,6 +2686,104 @@ TextPushUndoAction (textPtr, undoString, insert, index1Ptr, index2Ptr)
/*
*----------------------------------------------------------------------
*
+ * TextUndoRedoCallback --
+ *
+ * This procedure is registered with the generic undo/redo code
+ * to handle 'insert' and 'delete' actions on all text widgets.
+ * We cannot perform those actions on any particular text widget,
+ * because that text widget might have been deleted by the time
+ * we get here.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * Will insert or delete text, depending on the first word
+ * contained in objPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TextUndoRedoCallback(interp, clientData, objPtr)
+ Tcl_Interp *interp; /* Current interpreter. */
+ ClientData clientData; /* Passed from undo code, but
+ * contains our shared text
+ * data structure. */
+ Tcl_Obj *objPtr; /* Arguments of a command to
+ * be handled by the shared
+ * text data structure. */
+{
+ TkSharedText *sharedPtr = (TkSharedText*)clientData;
+ int objc;
+ Tcl_Obj **objv;
+ int res;
+ TkText *textPtr;
+
+ res = Tcl_ListObjGetElements(interp, objPtr, &objc, &objv);
+ if (res != TCL_OK) {
+ return res;
+ }
+
+ /*
+ * If possible, use a real text widget to perform the undo/redo
+ * action (i.e. insertion or deletion of text). This provides
+ * maximum compatibility with older versions of Tk, in which the
+ * user may rename the text widget to allow capture of undo or
+ * redo actions.
+ *
+ * In particular, this sorting of capture is useful in text editors
+ * based on the Tk text widget, which need to know which new text
+ * needs re-coloring.
+ *
+ * It would be better if the text widget provided some other
+ * mechanism to allow capture of this information ("What has
+ * just changed in the text widget?"). What we have here is
+ * not entirely satisfactory under all circumstances.
+ */
+ textPtr = sharedPtr->peers;
+ while (textPtr != NULL) {
+ if (textPtr->start == NULL && textPtr->end == NULL) {
+ Tcl_Obj *cmdNameObj, *evalObj;
+
+ evalObj = Tcl_NewObj();
+ Tcl_IncrRefCount(evalObj);
+ /*
+ * We might wish to use the real, current command-name
+ * for the widget, but this will break any code that has
+ * over-ridden the widget, and is expecting to observe
+ * the insert/delete actions which are caused by undo/redo
+ * operations.
+ *
+ * cmdNameObj = Tcl_NewObj();
+ * Tcl_GetCommandFullName(interp, textPtr->widgetCmd,
+ * cmdNameObj);
+ *
+ * While such interception is not explicitly documented as
+ * supported, it does occur, and so until we can provide
+ * some alternative mechanism for such code to do what
+ * it needs, we allow it to take place here.
+ */
+ cmdNameObj = Tcl_NewStringObj(Tk_PathName(textPtr->tkwin), -1);
+ Tcl_ListObjAppendElement(NULL, evalObj, cmdNameObj);
+ Tcl_ListObjAppendList(NULL, evalObj, objPtr);
+ res = Tcl_EvalObjEx(interp, evalObj, TCL_EVAL_GLOBAL);
+ Tcl_DecrRefCount(evalObj);
+ return res;
+ }
+ textPtr = textPtr->next;
+ }
+ /*
+ * If there's no current text widget which shows everything, then
+ * we fall back on acting directly. This means there is no way to
+ * intercept from the Tcl level.
+ */
+ return SharedTextObjCmd((ClientData)sharedPtr, interp, objc+1, objv-1);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* CountIndices --
*
* This procedure implements most of the functionality of the
@@ -2174,14 +2842,22 @@ CountIndices(textPtr, indexPtr1, indexPtr2, type)
* Side effects:
* Characters get deleted from the text.
*
- * Unless 'noViewUpdate' is set, we may adjust the window
+ * If 'viewUpdate' is true, we may adjust the window
* contents' y-position, and scrollbar setting.
+ *
+ * If 'viewUpdate' is false, true we can guarantee that
+ * textPtr->topIndex points to a valid TkTextLine after this
+ * procedure returns. However, if 'viewUpdate' is false, then
+ * there is no such guarantee (topIndex.linePtr can be garbage).
+ * The caller is expected to take actions to ensure the topIndex
+ * is validated before laying out the window again.
*
*----------------------------------------------------------------------
*/
static int
-DeleteChars(textPtr, indexPtr1, indexPtr2, noViewUpdate)
+DeleteChars(sharedTextPtr, textPtr, indexPtr1, indexPtr2, viewUpdate)
+ TkSharedText *sharedTextPtr; /* Shared portion of peer widgets. */
TkText *textPtr; /* Overall information about text widget. */
CONST TkTextIndex *indexPtr1;/* Index describing location of first
* character to delete. */
@@ -2189,11 +2865,19 @@ DeleteChars(textPtr, indexPtr1, indexPtr2, noViewUpdate)
* character to delete. NULL means just
* delete the one character given by
* indexPtr1. */
- int noViewUpdate; /* Don't update the view if set */
+ int viewUpdate; /* Update vertical view if set. */
{
- int line1, line2, line, byteIndex, resetView;
+ int line1, line2;
TkTextIndex index1, index2;
+ TkText *tPtr;
+ int *lineAndByteIndex;
+ int resetViewCount;
+ int pixels[2*PIXEL_CLIENTS];
+ if (sharedTextPtr == NULL) {
+ sharedTextPtr = textPtr->sharedTextPtr;
+ }
+
/*
* Prepare the starting and stopping indices.
*/
@@ -2226,9 +2910,9 @@ DeleteChars(textPtr, indexPtr1, indexPtr2, noViewUpdate)
* deleting the newline and then adding a "clean" one back again).
*/
- line1 = TkBTreeLineIndex(index1.linePtr);
- line2 = TkBTreeLineIndex(index2.linePtr);
- if (line2 == TkBTreeNumLines(textPtr->tree)) {
+ line1 = TkBTreeLinesTo(textPtr, index1.linePtr);
+ line2 = TkBTreeLinesTo(textPtr, index2.linePtr);
+ if (line2 == TkBTreeNumLines(sharedTextPtr->tree, textPtr)) {
TkTextTag **arrayPtr;
int arraySize, i;
TkTextIndex oldIndex2;
@@ -2240,7 +2924,7 @@ DeleteChars(textPtr, indexPtr1, indexPtr2, noViewUpdate)
TkTextIndexBackChars(NULL, &index1, 1, &index1, COUNT_INDICES);
line1--;
}
- arrayPtr = TkBTreeGetTags(&index2, &arraySize);
+ arrayPtr = TkBTreeGetTags(&index2, NULL, &arraySize);
if (arrayPtr != NULL) {
for (i = 0; i < arraySize; i++) {
TkBTreeTag(&index2, &oldIndex2, arrayPtr[i], 0);
@@ -2260,39 +2944,30 @@ DeleteChars(textPtr, indexPtr1, indexPtr2, noViewUpdate)
Tcl_HashEntry *hPtr;
int i;
- for (i = 0, hPtr = Tcl_FirstHashEntry(&textPtr->tagTable, &search);
+ for (i = 0, hPtr = Tcl_FirstHashEntry(&sharedTextPtr->tagTable,
+ &search);
hPtr != NULL; i++, hPtr = Tcl_NextHashEntry(&search)) {
TkTextTag *tagPtr;
tagPtr = (TkTextTag *) Tcl_GetHashValue(hPtr);
- if (TkBTreeTag(&index1, &index2, tagPtr, 0)) {
+ TkBTreeTag(&index1, &index2, tagPtr, 0);
+ }
+ /*
+ * Special case for the sel tag which is not in the hash table.
+ * We need to do this once for each peer text widget.
+ */
+
+ for (tPtr = sharedTextPtr->peers; tPtr != NULL ;
+ tPtr = tPtr->next) {
+ if (TkBTreeTag(&index1, &index2, tPtr->selTagPtr, 0)) {
/*
- * If the tag is "sel", and we actually adjusted anything
- * then grab the selection if we're supposed to export it
- * and don't already have it. Also, invalidate
- * partially-completed selection retrievals.
- *
- * This code copied from tkTextTag.c's 'tag remove'
+ * Send an event that the selection changed.
+ * This is equivalent to
+ * "event generate $textWidget <<Selection>>"
*/
- if (tagPtr == textPtr->selTagPtr) {
- XEvent event;
- /*
- * Send an event that the selection changed.
- * This is equivalent to
- * "event generate $textWidget <<Selection>>"
- */
-
- memset((VOID *) &event, 0, sizeof(event));
- event.xany.type = VirtualEvent;
- event.xany.serial = NextRequest(Tk_Display(textPtr->tkwin));
- event.xany.send_event = False;
- event.xany.window = Tk_WindowId(textPtr->tkwin);
- event.xany.display = Tk_Display(textPtr->tkwin);
- ((XVirtualEvent *) &event)->name = Tk_GetUid("Selection");
- Tk_HandleEvent(&event);
+ TkTextSelectionEvent(textPtr);
- textPtr->abortSelections = 1;
- }
+ tPtr->abortSelections = 1;
}
}
}
@@ -2306,80 +2981,125 @@ DeleteChars(textPtr, indexPtr1, indexPtr2, noViewUpdate)
* will be, then do the deletion, then reset the view.
*/
- TkTextChanged(textPtr, &index1, &index2);
- resetView = 0;
- line = 0;
- byteIndex = 0;
- if (TkTextIndexCmp(&index2, &textPtr->topIndex) >= 0) {
- if (TkTextIndexCmp(&index1, &textPtr->topIndex) <= 0) {
- /*
- * Deletion range straddles topIndex: use the beginning
- * of the range as the new topIndex.
- */
+ TkTextChanged(sharedTextPtr, NULL, &index1, &index2);
+
+ resetViewCount = 0;
+ if (sharedTextPtr->refCount > PIXEL_CLIENTS) {
+ lineAndByteIndex = (int*)ckalloc(sizeof(int)*
+ 2*sharedTextPtr->refCount);
+ } else {
+ lineAndByteIndex = pixels;
+ }
+ for (tPtr = sharedTextPtr->peers; tPtr != NULL ;
+ tPtr = tPtr->next) {
+ int line = 0;
+ int byteIndex = 0;
+ int resetView = 0;
+ if (TkTextIndexCmp(&index2, &tPtr->topIndex) >= 0) {
+ if (TkTextIndexCmp(&index1, &tPtr->topIndex) <= 0) {
+ /*
+ * Deletion range straddles topIndex: use the beginning
+ * of the range as the new topIndex.
+ */
- resetView = 1;
- line = line1;
- byteIndex = index1.byteIndex;
- } else if (index1.linePtr == textPtr->topIndex.linePtr) {
+ resetView = 1;
+ line = line1;
+ byteIndex = index1.byteIndex;
+ } else if (index1.linePtr == tPtr->topIndex.linePtr) {
+ /*
+ * Deletion range starts on top line but after topIndex.
+ * Use the current topIndex as the new one.
+ */
+
+ resetView = 1;
+ line = line1;
+ byteIndex = tPtr->topIndex.byteIndex;
+ }
+ } else if (index2.linePtr == tPtr->topIndex.linePtr) {
/*
- * Deletion range starts on top line but after topIndex.
- * Use the current topIndex as the new one.
+ * Deletion range ends on top line but before topIndex.
+ * Figure out what will be the new character index for
+ * the character currently pointed to by topIndex.
*/
resetView = 1;
- line = line1;
- byteIndex = textPtr->topIndex.byteIndex;
+ line = line2;
+ byteIndex = tPtr->topIndex.byteIndex;
+ if (index1.linePtr != index2.linePtr) {
+ byteIndex -= index2.byteIndex;
+ } else {
+ byteIndex -= (index2.byteIndex - index1.byteIndex);
+ }
}
- } else if (index2.linePtr == textPtr->topIndex.linePtr) {
- /*
- * Deletion range ends on top line but before topIndex.
- * Figure out what will be the new character index for
- * the character currently pointed to by topIndex.
- */
-
- resetView = 1;
- line = line2;
- byteIndex = textPtr->topIndex.byteIndex;
- if (index1.linePtr != index2.linePtr) {
- byteIndex -= index2.byteIndex;
+ if (resetView) {
+ lineAndByteIndex[resetViewCount] = line;
+ lineAndByteIndex[resetViewCount+1] = byteIndex;
} else {
- byteIndex -= (index2.byteIndex - index1.byteIndex);
+ lineAndByteIndex[resetViewCount] = -1;
}
+ resetViewCount+=2;
}
-
+
/*
* Push the deletion on the undo stack
*/
- if (textPtr->undo) {
+ if (sharedTextPtr->undo) {
Tcl_Obj *get;
- if (textPtr->autoSeparators
- && (textPtr->lastEditMode != TK_TEXT_EDIT_DELETE)) {
- TkUndoInsertUndoSeparator(textPtr->undoStack);
+ if (sharedTextPtr->autoSeparators
+ && (sharedTextPtr->lastEditMode != TK_TEXT_EDIT_DELETE)) {
+ TkUndoInsertUndoSeparator(sharedTextPtr->undoStack);
}
- textPtr->lastEditMode = TK_TEXT_EDIT_DELETE;
+ sharedTextPtr->lastEditMode = TK_TEXT_EDIT_DELETE;
get = TextGetText(textPtr, &index1, &index2, 0);
TextPushUndoAction(textPtr, get, 0, &index1, &index2);
}
- UpdateDirtyFlag(textPtr);
-
- textPtr->stateEpoch ++;
- TkBTreeDeleteChars(&index1, &index2);
-
- if (resetView && !noViewUpdate) {
- TkTextMakeByteIndex(textPtr->tree, line, byteIndex, &index1);
- TkTextSetYView(textPtr, &index1, 0);
+ UpdateDirtyFlag(sharedTextPtr);
+
+ sharedTextPtr->stateEpoch++;
+
+ TkBTreeDeleteChars(sharedTextPtr->tree, &index1, &index2);
+
+ resetViewCount = 0;
+ for (tPtr = sharedTextPtr->peers; tPtr != NULL ;
+ tPtr = tPtr->next) {
+ int line = lineAndByteIndex[resetViewCount];
+ if (line != -1) {
+ int byteIndex = lineAndByteIndex[resetViewCount+1];
+ if (tPtr == textPtr) {
+ if (viewUpdate) {
+ TkTextMakeByteIndex(sharedTextPtr->tree, textPtr,
+ line, byteIndex, &index1);
+ TkTextSetYView(tPtr, &index1, 0);
+ }
+ } else {
+ TkTextMakeByteIndex(sharedTextPtr->tree, NULL,
+ line, byteIndex, &index1);
+ TkTextSetYView(tPtr, &index1, 0);
+ }
+ }
+ resetViewCount += 2;
}
+ if (sharedTextPtr->refCount > PIXEL_CLIENTS) {
+ ckfree((char*)lineAndByteIndex);
+ }
+
+ if (line1 >= line2) {
+
+ /*
+ * Invalidate any selection retrievals in progress, assuming
+ * we didn't check for this case above.
+ */
- /*
- * Invalidate any selection retrievals in progress.
- */
-
- textPtr->abortSelections = 1;
-
+ for (tPtr = sharedTextPtr->peers; tPtr != NULL ;
+ tPtr = tPtr->next) {
+ tPtr->abortSelections = 1;
+ }
+ }
+
return TCL_OK;
}
@@ -2434,12 +3154,15 @@ TextFetchSelection(clientData, offset, buffer, maxBytes)
*/
if (offset == 0) {
- TkTextMakeByteIndex(textPtr->tree, 0, 0, &textPtr->selIndex);
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
+ 0, 0, &textPtr->selIndex);
textPtr->abortSelections = 0;
} else if (textPtr->abortSelections) {
return 0;
}
- TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree), 0, &eof);
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
+ TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr),
+ 0, &eof);
TkBTreeStartSearch(&textPtr->selIndex, &eof, textPtr->selTagPtr, &search);
if (!TkBTreeCharTagged(&textPtr->selIndex, textPtr->selTagPtr)) {
if (!TkBTreeNextTag(&search)) {
@@ -2503,7 +3226,7 @@ TextFetchSelection(clientData, offset, buffer, maxBytes)
maxBytes -= chunkSize;
count += chunkSize;
}
- TkTextIndexForwBytes(&textPtr->selIndex, chunkSize,
+ TkTextIndexForwBytes(textPtr, &textPtr->selIndex, chunkSize,
&textPtr->selIndex);
}
@@ -2547,7 +3270,6 @@ TkTextLostSelection(clientData)
ClientData clientData; /* Information about text widget. */
{
register TkText *textPtr = (TkText *) clientData;
- XEvent event;
#ifdef ALWAYS_SHOW_SELECTION
TkTextIndex start, end;
@@ -2561,17 +3283,49 @@ TkTextLostSelection(clientData)
* just remove the "sel" tag from everything in the widget.
*/
- TkTextMakeByteIndex(textPtr->tree, 0, 0, &start);
- TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree), 0, &end);
- TkTextRedrawTag(textPtr, &start, &end, textPtr->selTagPtr, 1);
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr, 0, 0, &start);
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
+ TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr),
+ 0, &end);
+ TkTextRedrawTag(NULL, textPtr, &start, &end, textPtr->selTagPtr, 1);
TkBTreeTag(&start, &end, textPtr->selTagPtr, 0);
#endif
-
+
/*
* Send an event that the selection changed. This is equivalent to
* "event generate $textWidget <<Selection>>"
*/
+ TkTextSelectionEvent(textPtr);
+ textPtr->flags &= ~GOT_SELECTION;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkTextSelectionEvent --
+ *
+ * When anything relevant to the "sel" tag has been changed,
+ * call this procedure to generate a <<Selection>> event.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If <<Selection>> bindings are present, they will trigger.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkTextSelectionEvent(textPtr)
+ TkText *textPtr;
+{
+ /*
+ * Send an event that the selection changed. This is equivalent to
+ * "event generate $textWidget <<Selection>>"
+ */
+ XEvent event;
memset((VOID *) &event, 0, sizeof(event));
event.xany.type = VirtualEvent;
event.xany.serial = NextRequest(Tk_Display(textPtr->tkwin));
@@ -2580,8 +3334,6 @@ TkTextLostSelection(clientData)
event.xany.display = Tk_Display(textPtr->tkwin);
((XVirtualEvent *) &event)->name = Tk_GetUid("Selection");
Tk_HandleEvent(&event);
-
- textPtr->flags &= ~GOT_SELECTION;
}
/*
@@ -2651,24 +3403,29 @@ TextBlinkProc(clientData)
* Side effects:
* See the user documentation.
*
- * Unless 'noViewUpdate' is set, we may adjust the window
+ * If 'viewUpdate' is true, we may adjust the window
* contents' y-position, and scrollbar setting.
*
*----------------------------------------------------------------------
*/
static int
-TextInsertCmd(textPtr, interp, objc, objv, indexPtr, noViewUpdate)
- TkText *textPtr; /* Information about text widget. */
- Tcl_Interp *interp; /* Current interpreter. */
- int objc; /* Number of arguments. */
- Tcl_Obj *CONST objv[]; /* Argument objects. */
- CONST TkTextIndex *indexPtr;/* Index at which to insert */
- int noViewUpdate; /* Don't update the view if set */
+TextInsertCmd(sharedTextPtr, textPtr, interp, objc, objv, indexPtr, viewUpdate)
+ TkSharedText *sharedTextPtr; /* Shared portion of peer widgets. */
+ TkText *textPtr; /* Information about text widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+ CONST TkTextIndex *indexPtr; /* Index at which to insert */
+ int viewUpdate; /* Update the view if set */
{
TkTextIndex index1, index2;
int j;
+ if (sharedTextPtr == NULL) {
+ sharedTextPtr = textPtr->sharedTextPtr;
+ }
+
index1 = *indexPtr;
for (j = 0; j < objc; j += 2) {
/*
@@ -2678,14 +3435,15 @@ TextInsertCmd(textPtr, interp, objc, objv, indexPtr, noViewUpdate)
* allowable index for insertion, otherwise
* subsequent tag insertions will fail.
*/
- int length = InsertChars(textPtr, &index1, objv[j], noViewUpdate);
+ int length = InsertChars(sharedTextPtr, textPtr, &index1,
+ objv[j], viewUpdate);
if (objc > (j+1)) {
Tcl_Obj **tagNamePtrs;
TkTextTag **oldTagArrayPtr;
int numTags;
- TkTextIndexForwBytes(&index1, length, &index2);
- oldTagArrayPtr = TkBTreeGetTags(&index1, &numTags);
+ TkTextIndexForwBytes(textPtr, &index1, length, &index2);
+ oldTagArrayPtr = TkBTreeGetTags(&index1, NULL, &numTags);
if (oldTagArrayPtr != NULL) {
int i;
for (i = 0; i < numTags; i++) {
@@ -2702,10 +3460,9 @@ TextInsertCmd(textPtr, interp, objc, objv, indexPtr, noViewUpdate)
int i;
for (i = 0; i < numTags; i++) {
- TkBTreeTag(&index1, &index2,
- TkTextCreateTag(textPtr,
- Tcl_GetString(tagNamePtrs[i]), NULL),
- 1);
+ CONST char *strTag = Tcl_GetString(tagNamePtrs[i]);
+ TkBTreeTag(&index1, &index2,
+ TkTextCreateTag(textPtr, strTag, NULL), 1);
}
index1 = index2;
}
@@ -2769,7 +3526,7 @@ TextSearchCmd(textPtr, interp, objc, objv)
searchSpec.noLineStop = 0;
searchSpec.overlap = 0;
searchSpec.strictLimits = 0;
- searchSpec.numLines = TkBTreeNumLines(textPtr->tree);
+ searchSpec.numLines = TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr);
searchSpec.clientData = (ClientData)textPtr;
searchSpec.addLineProc = &TextSearchAddNextLine;
searchSpec.foundMatchProc = &TextSearchFoundMatch;
@@ -2961,13 +3718,20 @@ TextSearchGetLineIndex(interp, objPtr, searchSpecPtr, linePosPtr, offsetPosPtr)
return TCL_ERROR;
}
- line = TkBTreeLineIndex(indexPtr->linePtr);
+ line = TkBTreeLinesTo(textPtr, indexPtr->linePtr);
if (line >= searchSpecPtr->numLines) {
TkTextLine *linePtr;
+ int count = 0;
+ TkTextSegment *segPtr;
+
line = searchSpecPtr->numLines-1;
- linePtr = TkBTreeFindLine(textPtr->tree, line);
+ linePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree, textPtr, line);
+ /* Count the number of bytes in this line */
+ for (segPtr = linePtr->segPtr; segPtr != NULL; segPtr = segPtr->nextPtr) {
+ count += segPtr->size;
+ }
*offsetPosPtr = TextSearchIndexInLine(searchSpecPtr, linePtr,
- TkBTreeBytesInLine(linePtr));
+ count);
} else {
*offsetPosPtr = TextSearchIndexInLine(searchSpecPtr,
indexPtr->linePtr, indexPtr->byteIndex);
@@ -3011,7 +3775,7 @@ TextSearchIndexInLine(searchSpecPtr, linePtr, byteIndex)
TkText *textPtr = (TkText*)(searchSpecPtr->clientData);
index = 0;
- curIndex.tree = textPtr->tree;
+ curIndex.tree = textPtr->sharedTextPtr->tree;
curIndex.linePtr = linePtr; curIndex.byteIndex = 0;
for (segPtr = linePtr->segPtr, leftToScan = byteIndex;
leftToScan > 0;
@@ -3076,11 +3840,11 @@ TextSearchAddNextLine(lineNum, searchSpecPtr, theLine, lenPtr)
* Extract the text from the line.
*/
- linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
+ linePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree, textPtr, lineNum);
if (linePtr == NULL) {
return NULL;
}
- curIndex.tree = textPtr->tree;
+ curIndex.tree = textPtr->sharedTextPtr->tree;
curIndex.linePtr = linePtr; curIndex.byteIndex = 0;
for (segPtr = linePtr->segPtr; segPtr != NULL;
curIndex.byteIndex += segPtr->size, segPtr = segPtr->nextPtr) {
@@ -3198,10 +3962,10 @@ TextSearchFoundMatch(lineNum, searchSpecPtr, clientData, theLine,
linePtr = (TkTextLine *)clientData;
if (linePtr == NULL) {
- linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
+ linePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree, textPtr, lineNum);
}
- curIndex.tree = textPtr->tree;
+ curIndex.tree = textPtr->sharedTextPtr->tree;
curIndex.linePtr = linePtr; curIndex.byteIndex = 0;
/* Find the starting point */
for (segPtr = linePtr->segPtr, leftToScan = matchOffset;
@@ -3222,10 +3986,10 @@ TextSearchFoundMatch(lineNum, searchSpecPtr, clientData, theLine,
}
/* Calculate and store the found index in the result */
if (searchSpecPtr->exact) {
- TkTextMakeByteIndex(textPtr->tree, lineNum,
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr, lineNum,
matchOffset, &foundIndex);
} else {
- TkTextMakeCharIndex(textPtr->tree, lineNum,
+ TkTextMakeCharIndex(textPtr->sharedTextPtr->tree, textPtr, lineNum,
matchOffset, &foundIndex);
}
if (searchSpecPtr->all) {
@@ -3252,7 +4016,7 @@ TextSearchFoundMatch(lineNum, searchSpecPtr, clientData, theLine,
* ever happen with searches which have matched across
* multiple lines
*/
- linePtr = TkBTreeNextLine(linePtr);
+ linePtr = TkBTreeNextLine(textPtr, linePtr);
segPtr = linePtr->segPtr;
curIndex.linePtr = linePtr; curIndex.byteIndex = 0;
}
@@ -3559,7 +4323,7 @@ TextDumpCmd(textPtr, interp, objc, objv)
if (TkTextGetObjIndex(interp, textPtr, objv[arg], &index1) != TCL_OK) {
return TCL_ERROR;
}
- lineno = TkBTreeLineIndex(index1.linePtr);
+ lineno = TkBTreeLinesTo(textPtr, index1.linePtr);
arg++;
atEnd = 0;
if (objc == arg) {
@@ -3585,7 +4349,7 @@ TextDumpCmd(textPtr, interp, objc, objv)
DumpLine(interp, textPtr, what, index1.linePtr,
index1.byteIndex, 32000000, lineno, command);
linePtr = index1.linePtr;
- while ((linePtr = TkBTreeNextLine(linePtr)) != (TkTextLine *)NULL) {
+ while ((linePtr = TkBTreeNextLine(textPtr, linePtr)) != NULL) {
lineno++;
if (linePtr == index2.linePtr) {
break;
@@ -3661,35 +4425,47 @@ DumpLine(interp, textPtr, what, linePtr, startByte, endByte, lineno, command)
savedChar = segPtr->body.chars[last];
segPtr->body.chars[last] = '\0';
- TkTextMakeByteIndex(textPtr->tree, lineno, offset + first, &index);
- DumpSegment(interp, "text", segPtr->body.chars + first,
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr, lineno,
+ offset + first, &index);
+ DumpSegment(textPtr, interp, "text", segPtr->body.chars + first,
command, &index, what);
segPtr->body.chars[last] = savedChar;
} else if ((offset >= startByte)) {
if ((what & TK_DUMP_MARK) && (segPtr->typePtr->name[0] == 'm')) {
+ char *name;
TkTextMark *markPtr = (TkTextMark *)&segPtr->body;
- char *name = Tcl_GetHashKey(&textPtr->markTable, markPtr->hPtr);
-
- TkTextMakeByteIndex(textPtr->tree, lineno, offset, &index);
- DumpSegment(interp, "mark", name, command, &index, what);
+ if (segPtr == textPtr->insertMarkPtr) {
+ name = "insert";
+ } else if (segPtr == textPtr->currentMarkPtr) {
+ name = "current";
+ } else {
+ name = Tcl_GetHashKey(&textPtr->sharedTextPtr->markTable,
+ markPtr->hPtr);
+ }
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
+ lineno, offset, &index);
+ DumpSegment(textPtr, interp, "mark", name, command, &index, what);
} else if ((what & TK_DUMP_TAG) &&
(segPtr->typePtr == &tkTextToggleOnType)) {
- TkTextMakeByteIndex(textPtr->tree, lineno, offset, &index);
- DumpSegment(interp, "tagon",
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
+ lineno, offset, &index);
+ DumpSegment(textPtr, interp, "tagon",
segPtr->body.toggle.tagPtr->name,
command, &index, what);
} else if ((what & TK_DUMP_TAG) &&
(segPtr->typePtr == &tkTextToggleOffType)) {
- TkTextMakeByteIndex(textPtr->tree, lineno, offset, &index);
- DumpSegment(interp, "tagoff",
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
+ lineno, offset, &index);
+ DumpSegment(textPtr, interp, "tagoff",
segPtr->body.toggle.tagPtr->name,
command, &index, what);
} else if ((what & TK_DUMP_IMG) &&
(segPtr->typePtr->name[0] == 'i')) {
TkTextEmbImage *eiPtr = (TkTextEmbImage *)&segPtr->body;
char *name = (eiPtr->name == NULL) ? "" : eiPtr->name;
- TkTextMakeByteIndex(textPtr->tree, lineno, offset, &index);
- DumpSegment(interp, "image", name,
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
+ lineno, offset, &index);
+ DumpSegment(textPtr, interp, "image", name,
command, &index, what);
} else if ((what & TK_DUMP_WIN) &&
(segPtr->typePtr->name[0] == 'w')) {
@@ -3700,8 +4476,9 @@ DumpLine(interp, textPtr, what, linePtr, startByte, endByte, lineno, command)
} else {
pathname = Tk_PathName(ewPtr->tkwin);
}
- TkTextMakeByteIndex(textPtr->tree, lineno, offset, &index);
- DumpSegment(interp, "window", pathname,
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
+ lineno, offset, &index);
+ DumpSegment(textPtr, interp, "window", pathname,
command, &index, what);
}
}
@@ -3725,7 +4502,8 @@ DumpLine(interp, textPtr, what, linePtr, startByte, endByte, lineno, command)
*----------------------------------------------------------------------
*/
static int
-DumpSegment(interp, key, value, command, index, what)
+DumpSegment(textPtr, interp, key, value, command, index, what)
+ TkText *textPtr;
Tcl_Interp *interp;
CONST char *key; /* Segment type key */
CONST char *value; /* Segment value */
@@ -3734,7 +4512,7 @@ DumpSegment(interp, key, value, command, index, what)
int what; /* Look for TK_DUMP_INDEX bit */
{
char buffer[TK_POS_CHARS];
- TkTextPrintIndex(index, buffer);
+ TkTextPrintIndex(textPtr, index, buffer);
if (command == NULL) {
Tcl_AppendElement(interp, key);
Tcl_AppendElement(interp, value);
@@ -3777,24 +4555,24 @@ TextEditUndo(textPtr)
{
int status;
- if (!textPtr->undo) {
+ if (!textPtr->sharedTextPtr->undo) {
return TCL_OK;
}
/* Turn off the undo feature */
- textPtr->undo = 0;
+ textPtr->sharedTextPtr->undo = 0;
/* The dirty counter should count downwards as we are undoing things */
- textPtr->isDirtyIncrement = -1;
+ textPtr->sharedTextPtr->isDirtyIncrement = -1;
/* revert one compound action */
- status = TkUndoRevert(textPtr->undoStack);
+ status = TkUndoRevert(textPtr->sharedTextPtr->undoStack);
/* Restore the isdirty increment */
- textPtr->isDirtyIncrement = 1;
+ textPtr->sharedTextPtr->isDirtyIncrement = 1;
/* Turn back on the undo feature */
- textPtr->undo = 1;
+ textPtr->sharedTextPtr->undo = 1;
return status;
}
@@ -3821,18 +4599,18 @@ TextEditRedo(textPtr)
{
int status;
- if (!textPtr->undo) {
+ if (!textPtr->sharedTextPtr->undo) {
return TCL_OK;
}
/* Turn off the undo feature temporarily */
- textPtr->undo = 0;
+ textPtr->sharedTextPtr->undo = 0;
/* reapply one compound action */
- status = TkUndoApply(textPtr->undoStack);
+ status = TkUndoApply(textPtr->sharedTextPtr->undoStack);
/* Turn back on the undo feature */
- textPtr->undo = 1;
+ textPtr->sharedTextPtr->undo = 1;
return status;
}
@@ -3883,7 +4661,8 @@ TextEditCmd(textPtr, interp, objc, objv)
switch ((enum editOptions)index) {
case EDIT_MODIFIED: {
if (objc == 3) {
- Tcl_SetObjResult(interp, Tcl_NewBooleanObj(textPtr->isDirty));
+ Tcl_SetObjResult(interp,
+ Tcl_NewBooleanObj(textPtr->sharedTextPtr->isDirty));
} else if (objc != 4) {
Tcl_WrongNumArgs(interp, 3, objv, "?boolean?");
return TCL_ERROR;
@@ -3899,11 +4678,11 @@ TextEditCmd(textPtr, interp, objc, objv)
*/
if (setModified) {
- textPtr->isDirty = 1;
- textPtr->modifiedSet = 1;
+ textPtr->sharedTextPtr->isDirty = 1;
+ textPtr->sharedTextPtr->modifiedSet = 1;
} else {
- textPtr->isDirty = 0;
- textPtr->modifiedSet = 0;
+ textPtr->sharedTextPtr->isDirty = 0;
+ textPtr->sharedTextPtr->modifiedSet = 0;
}
/*
@@ -3938,7 +4717,7 @@ TextEditCmd(textPtr, interp, objc, objv)
Tcl_WrongNumArgs(interp, 3, objv, NULL);
return TCL_ERROR;
}
- TkUndoClearStacks(textPtr->undoStack);
+ TkUndoClearStacks(textPtr->sharedTextPtr->undoStack);
break;
}
case EDIT_SEPARATOR: {
@@ -3946,7 +4725,7 @@ TextEditCmd(textPtr, interp, objc, objv)
Tcl_WrongNumArgs(interp, 3, objv, NULL);
return TCL_ERROR;
}
- TkUndoInsertUndoSeparator(textPtr->undoStack);
+ TkUndoInsertUndoSeparator(textPtr->sharedTextPtr->undoStack);
break;
}
case EDIT_UNDO: {
@@ -4009,8 +4788,9 @@ TextGetText(textPtr, indexPtr1,indexPtr2, visibleOnly)
TkTextIndex tmpIndex;
Tcl_Obj *resultPtr = Tcl_NewObj();
- TkTextMakeByteIndex(indexPtr1->tree, TkBTreeLineIndex(indexPtr1->linePtr),
- indexPtr1->byteIndex, &tmpIndex);
+ TkTextMakeByteIndex(indexPtr1->tree, textPtr,
+ TkBTreeLinesTo(textPtr, indexPtr1->linePtr),
+ indexPtr1->byteIndex, &tmpIndex);
if (TkTextIndexCmp(indexPtr1, indexPtr2) < 0) {
while (1) {
@@ -4041,7 +4821,7 @@ TextGetText(textPtr, indexPtr1,indexPtr2, visibleOnly)
last - offset);
}
}
- TkTextIndexForwBytes(&tmpIndex, last-offset, &tmpIndex);
+ TkTextIndexForwBytes(textPtr, &tmpIndex, last-offset, &tmpIndex);
}
}
return resultPtr;
@@ -4064,31 +4844,35 @@ TextGetText(textPtr, indexPtr1,indexPtr2, visibleOnly)
*/
static void
-UpdateDirtyFlag (textPtr)
- TkText *textPtr; /* Information about text widget. */
+UpdateDirtyFlag (sharedTextPtr)
+ TkSharedText *sharedTextPtr; /* Information about text widget. */
{
int oldDirtyFlag;
- if (textPtr->modifiedSet) {
+ if (sharedTextPtr->modifiedSet) {
return;
}
- oldDirtyFlag = textPtr->isDirty;
- textPtr->isDirty += textPtr->isDirtyIncrement;
- if (textPtr->isDirty == 0 || oldDirtyFlag == 0) {
- XEvent event;
- /*
- * Send an event that the text was modified. This is equivalent to
- * "event generate $textWidget <<Modified>>"
- */
+ oldDirtyFlag = sharedTextPtr->isDirty;
+ sharedTextPtr->isDirty += sharedTextPtr->isDirtyIncrement;
+ if (sharedTextPtr->isDirty == 0 || oldDirtyFlag == 0) {
+ TkText *textPtr;
+ for (textPtr = sharedTextPtr->peers; textPtr != NULL;
+ textPtr = textPtr->next) {
+ XEvent event;
+ /*
+ * Send an event that the text was modified. This is equivalent to
+ * "event generate $textWidget <<Modified>>"
+ */
- memset((VOID *) &event, 0, sizeof(event));
- event.xany.type = VirtualEvent;
- event.xany.serial = NextRequest(Tk_Display(textPtr->tkwin));
- event.xany.send_event = False;
- event.xany.window = Tk_WindowId(textPtr->tkwin);
- event.xany.display = Tk_Display(textPtr->tkwin);
- ((XVirtualEvent *) &event)->name = Tk_GetUid("Modified");
- Tk_HandleEvent(&event);
+ memset((VOID *) &event, 0, sizeof(event));
+ event.xany.type = VirtualEvent;
+ event.xany.serial = NextRequest(Tk_Display(textPtr->tkwin));
+ event.xany.send_event = False;
+ event.xany.window = Tk_WindowId(textPtr->tkwin);
+ event.xany.display = Tk_Display(textPtr->tkwin);
+ ((XVirtualEvent *) &event)->name = Tk_GetUid("Modified");
+ Tk_HandleEvent(&event);
+ }
}
}
@@ -4321,6 +5105,15 @@ SearchCore(interp, searchSpecPtr, patObj)
lineInfo = (*searchSpecPtr->addLineProc)(lineNum,
searchSpecPtr, theLine, &lastOffset);
+ if (lineInfo == NULL) {
+ /*
+ * This should not happen, since 'lineNum' should be valid
+ * in the call above. However, let's try to be flexible and
+ * not cause a crash below.
+ */
+ goto nextLine;
+ }
+
if (lineNum == searchSpecPtr->stopLine && searchSpecPtr->backwards) {
firstOffset = searchSpecPtr->stopOffset;
} else {
@@ -5075,3 +5868,162 @@ SearchCore(interp, searchSpecPtr, patObj)
return code;
}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetLineStartEnd -
+ *
+ * Converts an internal TkTextLine ptr into a Tcl string obj
+ * containing the line number. (Handler for the 'line'
+ * configuration option type)
+ *
+ * Results:
+ * Tcl_Obj containing the string representation of the line value.
+ *
+ * Side effects:
+ * Creates a new Tcl_Obj.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_Obj *
+GetLineStartEnd(clientData, tkwin, recordPtr, internalOffset)
+ ClientData clientData;
+ Tk_Window tkwin;
+ char *recordPtr; /* Pointer to widget record. */
+ int internalOffset; /* Offset within *recordPtr containing the
+ * line value. */
+{
+ TkTextLine *linePtr = *(TkTextLine **)(recordPtr + internalOffset);
+
+ if (linePtr == NULL) {
+ return Tcl_NewObj();
+ } else {
+ return Tcl_NewIntObj(1+TkBTreeLinesTo(NULL, linePtr));
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SetLineStartEnd --
+ *
+ * Converts a Tcl_Obj representing a widget's (start or end) line
+ * into a TkTextLine* value. (Handler for the 'line' configuration
+ * option type)
+ *
+ * Results:
+ * Standard Tcl result.
+ *
+ * Side effects:
+ * May store the TkTextLine* value into the internal representation
+ * pointer. May change the pointer to the Tcl_Obj to NULL to indicate
+ * that the specified string was empty and that is acceptable.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+SetLineStartEnd(clientData, interp, tkwin, value, recordPtr, internalOffset,
+ oldInternalPtr, flags)
+ ClientData clientData;
+ Tcl_Interp *interp; /* Current interp; may be used for errors. */
+ Tk_Window tkwin; /* Window for which option is being set. */
+ Tcl_Obj **value; /* Pointer to the pointer to the value object.
+ * We use a pointer to the pointer because
+ * we may need to return a value (NULL). */
+ char *recordPtr; /* Pointer to storage for the widget record. */
+ int internalOffset; /* Offset within *recordPtr at which the
+ internal value is to be stored. */
+ char *oldInternalPtr; /* Pointer to storage for the old value. */
+ int flags; /* Flags for the option, set Tk_SetOptions. */
+{
+ TkTextLine *linePtr = NULL;
+ char *internalPtr;
+ TkText *textPtr = (TkText*)recordPtr;
+
+ if (internalOffset >= 0) {
+ internalPtr = recordPtr + internalOffset;
+ } else {
+ internalPtr = NULL;
+ }
+
+ if (flags & TK_OPTION_NULL_OK && ObjectIsEmpty(*value)) {
+ *value = NULL;
+ } else {
+ int line;
+ if (Tcl_GetIntFromObj(interp, *value, &line) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ linePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree, NULL, line-1);
+ }
+
+ if (internalPtr != NULL) {
+ *((TkTextLine **) oldInternalPtr) = *((TkTextLine **) internalPtr);
+ *((TkTextLine **) internalPtr) = linePtr;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RestoreLineStartEnd --
+ *
+ * Restore a line option value from a saved value. (Handler for
+ * the 'line' configuration option type)
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Restores the old value.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+RestoreLineStartEnd(clientData, tkwin, internalPtr, oldInternalPtr)
+ ClientData clientData;
+ Tk_Window tkwin;
+ char *internalPtr; /* Pointer to storage for value. */
+ char *oldInternalPtr; /* Pointer to old value. */
+{
+ *(TkTextLine **)internalPtr = *(TkTextLine **)oldInternalPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ObjectIsEmpty --
+ *
+ * This procedure tests whether the string value of an object is
+ * empty.
+ *
+ * Results:
+ * The return value is 1 if the string value of objPtr has length
+ * zero, and 0 otherwise.
+ *
+ * Side effects:
+ * May cause object shimmering, since this function can force a
+ * conversion to a string object.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ObjectIsEmpty(objPtr)
+ Tcl_Obj *objPtr; /* Object to test. May be NULL. */
+{
+ int length;
+
+ if (objPtr == NULL) {
+ return 1;
+ }
+ if (objPtr->bytes != NULL) {
+ return (objPtr->length == 0);
+ }
+ Tcl_GetStringFromObj(objPtr, &length);
+ return (length == 0);
+}
diff --git a/generic/tkText.h b/generic/tkText.h
index 57560e8..14b2a71 100644
--- a/generic/tkText.h
+++ b/generic/tkText.h
@@ -10,7 +10,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tkText.h,v 1.24 2003/12/15 11:51:05 vincentdarley Exp $
+ * RCS: @(#) $Id: tkText.h,v 1.25 2004/09/10 12:13:41 vincentdarley Exp $
*/
#ifndef _TKTEXT
@@ -50,15 +50,16 @@ typedef struct TkTextLine {
* means end of list. */
struct TkTextSegment *segPtr; /* First in ordered list of segments
* that make up the line. */
- int pixelHeight; /* The number of vertical
- * pixels taken up by this
- * line, whether currently
- * displayed or not. This
- * number is only updated
- * asychronously. */
- int pixelCalculationEpoch; /* The last epoch at which the
- * pixel height was
- * recalculated. */
+ int *pixels; /* Array containing two integers
+ * for each referring text widget.
+ * The first of these is the number
+ * of vertical pixels taken up by
+ * this line, whether currently
+ * displayed or not. This number
+ * is only updated asychronously.
+ * The second of these is the last
+ * epoch at which the pixel height
+ * was recalculated. */
} TkTextLine;
/*
@@ -94,7 +95,7 @@ typedef struct TkTextMark {
TkTextLine *linePtr; /* Line structure that contains the
* segment. */
Tcl_HashEntry *hPtr; /* Pointer to hash table entry for mark
- * (in textPtr->markTable). */
+ * (in sharedTextPtr->markTable). */
} TkTextMark;
/*
@@ -103,14 +104,33 @@ typedef struct TkTextMark {
* file tkTextWind.c
*/
-typedef struct TkTextEmbWindow {
+typedef struct TkTextEmbWindowClient {
struct TkText *textPtr; /* Information about the overall text
* widget. */
- TkTextLine *linePtr; /* Line structure that contains this
- * window. */
Tk_Window tkwin; /* Window for this segment. NULL
* means that the window hasn't
* been created yet. */
+ int chunkCount; /* Number of display chunks that
+ * refer to this window. */
+ int displayed; /* Non-zero means that the window
+ * has been displayed on the screen
+ * recently. */
+ struct TkTextSegment *parent;
+ struct TkTextEmbWindowClient *next;
+} TkTextEmbWindowClient;
+
+typedef struct TkTextEmbWindow {
+ struct TkSharedText *sharedTextPtr; /* Information about the shared
+ * portion of the text widget. */
+ Tk_Window tkwin; /* Window for this segment.
+ * This is just a temporary
+ * value, copied from
+ * 'clients', to make option
+ * table updating easier. NULL
+ * means that the window hasn't
+ * been created yet. */
+ TkTextLine *linePtr; /* Line structure that contains this
+ * window. */
char *create; /* Script to create window on-demand.
* NULL means no such script.
* Malloc-ed. */
@@ -122,13 +142,11 @@ typedef struct TkTextEmbWindow {
int stretch; /* Should window stretch to fill
* vertical space of line (except for
* pady)? 0 or 1. */
- int chunkCount; /* Number of display chunks that
- * refer to this window. */
- int displayed; /* Non-zero means that the window
- * has been displayed on the screen
- * recently. */
Tk_OptionTable optionTable; /* Token representing the
* configuration specifications. */
+ TkTextEmbWindowClient *clients; /* Linked list of peer-widget
+ * specific information for
+ * this embedded window. */
} TkTextEmbWindow;
/*
@@ -138,8 +156,10 @@ typedef struct TkTextEmbWindow {
*/
typedef struct TkTextEmbImage {
- struct TkText *textPtr; /* Information about the overall text
- * widget. */
+ struct TkSharedText *sharedTextPtr; /* Information about the shared
+ * portion of the text widget.
+ * This is used when the image
+ * changes or is deleted. */
TkTextLine *linePtr; /* Line structure that contains this
* image. */
char *imageString; /* Name of the image for this segment */
@@ -215,6 +235,7 @@ typedef struct TkTextIndex {
typedef struct TkTextDispChunk TkTextDispChunk;
typedef void Tk_ChunkDisplayProc _ANSI_ARGS_((
+ struct TkText *textPtr,
TkTextDispChunk *chunkPtr, int x, int y,
int height, int baseline, Display *display,
Drawable dst, int screenY));
@@ -224,6 +245,7 @@ typedef void Tk_ChunkUndisplayProc _ANSI_ARGS_((
typedef int Tk_ChunkMeasureProc _ANSI_ARGS_((
TkTextDispChunk *chunkPtr, int x));
typedef void Tk_ChunkBboxProc _ANSI_ARGS_((
+ struct TkText *textPtr,
TkTextDispChunk *chunkPtr, int index, int y,
int lineHeight, int baseline, int *xPtr,
int *yPtr, int *widthPtr, int *heightPtr));
@@ -293,8 +315,8 @@ struct TkTextDispChunk {
/*
* One data structure of the following type is used for each tag in a
- * text widget. These structures are kept in textPtr->tagTable and
- * referred to in other structures.
+ * text widget. These structures are kept in sharedTextPtr->tagTable
+ * and referred to in other structures.
*/
typedef enum { TEXT_WRAPMODE_CHAR, TEXT_WRAPMODE_NONE,
@@ -302,10 +324,17 @@ typedef enum { TEXT_WRAPMODE_CHAR, TEXT_WRAPMODE_NONE,
} TkWrapMode;
typedef struct TkTextTag {
- CONST char *name; /* Name of this tag. This field is actually
- * a pointer to the key from the entry in
- * textPtr->tagTable, so it needn't be freed
- * explicitly. */
+ CONST char *name; /* Name of this tag. This field is
+ * actually a pointer to the key from
+ * the entry in
+ * sharedTextPtr->tagTable, so it
+ * needn't be freed explicitly. For
+ * 'sel' tags this is just a static
+ * string, so again need not be freed.
+ * */
+ CONST struct TkText *textPtr;/* If non-NULL, then this tag only
+ * applies to the given text widget
+ * (when there are peer widgets). */
int priority; /* Priority of this tag within widget. 0
* means lowest priority. Exactly one tag
* has each integer value between 0 and
@@ -504,30 +533,29 @@ typedef enum {
} TkTextState;
/*
- * A data structure of the following type is kept for each text widget that
- * currently exists for this process:
+ * A data structure of the following type is shared between each text widget
+ * that are peers.
*/
-typedef struct TkText {
- Tk_Window tkwin; /* Window that embodies the text. NULL
- * means that the window has been destroyed
- * but the data structures haven't yet been
- * cleaned up.*/
- Display *display; /* Display for widget. Needed, among other
- * things, to allow resources to be freed
- * even after tkwin has gone away. */
- Tcl_Interp *interp; /* Interpreter associated with widget. Used
- * to delete widget command. */
- Tcl_Command widgetCmd; /* Token for text's widget command. */
+typedef struct TkSharedText {
+ int refCount; /* Reference count this shared object */
+
TkTextBTree tree; /* B-tree representation of text and tags for
* widget. */
Tcl_HashTable tagTable; /* Hash table that maps from tag names to
- * pointers to TkTextTag structures. */
+ * pointers to TkTextTag structures.
+ * The "sel" tag does not feature in
+ * this table, since there's one of
+ * those for each text peer. */
int numTags; /* Number of tags currently defined for
* widget; needed to keep track of
* priorities. */
Tcl_HashTable markTable; /* Hash table that maps from mark names to
- * pointers to mark segments. */
+ * pointers to mark segments. The
+ * special "insert" and "current" marks
+ * are not stored in this table, but
+ * directly accessed as fields of
+ * textPtr. */
Tcl_HashTable windowTable; /* Hash table that maps from window names
* to pointers to window segments. If a
* window segment doesn't yet have an
@@ -538,6 +566,96 @@ typedef struct TkText {
* image segment doesn't yet have an
* associated image, there is no entry for
* it here. */
+ Tk_BindingTable bindingTable;
+ /* Table of all bindings currently defined
+ * for this widget. NULL means that no
+ * bindings exist, so the table hasn't been
+ * created. Each "object" used for this
+ * table is the name of a tag. */
+ int stateEpoch; /* This is incremented each time the
+ * B-tree's contents change
+ * structurally, and means that any
+ * cached TkTextIndex objects are no
+ * longer valid. */
+
+ /*
+ * Information related to the undo/redo functonality
+ */
+
+ TkUndoRedoStack *undoStack; /* The undo/redo stack */
+
+ int undo; /* Non-zero means the undo/redo behaviour is
+ * enabled */
+
+ int maxUndo; /* The maximum depth of the undo stack
+ * expressed as the maximum number of
+ * compound statements */
+
+ int autoSeparators; /* Non-zero means the separators will be
+ * inserted automatically */
+
+ int modifiedSet; /* Flag indicating that the 'dirtynesss' of
+ * the text widget has been expplicitly set.
+ */
+
+ int isDirty; /* Flag indicating the 'dirtynesss' of the text
+ * widget. If the flag is not zero, unsaved
+ * modifications have been applied to the
+ * text widget */
+
+ int isDirtyIncrement; /* Amount with which the isDirty flag is
+ * incremented every edit action */
+
+ TkTextEditMode lastEditMode;/* Keeps track of what the last edit
+ * mode was */
+
+ /*
+ * Keep track of all the peers
+ */
+ struct TkText *peers;
+
+} TkSharedText;
+
+/*
+ * A data structure of the following type is kept for each text widget that
+ * currently exists for this process:
+ */
+
+typedef struct TkText {
+ /*
+ * Information related to and accessed by widget peers and the
+ * TkSharedText handling routines.
+ */
+
+ TkSharedText *sharedTextPtr;/* Shared section of all peers. */
+ struct TkText *next; /* Next in list of linked peers. */
+ TkTextLine *start; /* First B-tree line to show, or NULL
+ * to start at the beginning. */
+ TkTextLine *end; /* Last B-tree line to show, or NULL
+ * for up to the end. */
+ int pixelReference; /* Counter into the current tree
+ * reference index corresponding to
+ * this widget */
+
+ int abortSelections; /* Set to 1 whenever the text is modified
+ * in a way that interferes with selection
+ * retrieval: used to abort incremental
+ * selection retrievals. */
+
+ /*
+ * Standard Tk widget information and text-widget specific items
+ */
+
+ Tk_Window tkwin; /* Window that embodies the text. NULL
+ * means that the window has been destroyed
+ * but the data structures haven't yet been
+ * cleaned up.*/
+ Display *display; /* Display for widget. Needed, among other
+ * things, to allow resources to be freed
+ * even after tkwin has gone away. */
+ Tcl_Interp *interp; /* Interpreter associated with widget. Used
+ * to delete widget command. */
+ Tcl_Command widgetCmd; /* Token for text's widget command. */
int state; /* Either STATE_NORMAL or STATE_DISABLED. A
* text widget is read-only when disabled. */
/*
@@ -603,27 +721,22 @@ typedef struct TkText {
* a new selection has been made. */
Tk_3DBorder selBorder; /* Border and background for selected
* characters. This is a copy of information
- * in *cursorTagPtr, so it shouldn't be
+ * in *selTagPtr, so it shouldn't be
* explicitly freed. */
+ Tk_3DBorder inactiveSelBorder;/* Border and background for selected
+ * characters when they don't have the
+ * focus. */
int selBorderWidth; /* Width of border around selection. */
Tcl_Obj* selBorderWidthPtr; /* Width of border around selection. */
XColor *selFgColorPtr; /* Foreground color for selected text.
* This is a copy of information in
- * *cursorTagPtr, so it shouldn't be
+ * *selTagPtr, so it shouldn't be
* explicitly freed. */
int exportSelection; /* Non-zero means tie "sel" tag to X
* selection. */
TkTextIndex selIndex; /* Used during multi-pass selection retrievals.
* This index identifies the next character
* to be returned from the selection. */
- int abortSelections; /* Set to 1 whenever the text is modified
- * in a way that interferes with selection
- * retrieval: used to abort incremental
- * selection retrievals. */
- int selOffset; /* Offset in selection corresponding to
- * selLine and selCh. -1 means neither
- * this information nor selIndex is of any
- * use. */
/*
* Information related to insertion cursor:
@@ -647,12 +760,6 @@ typedef struct TkText {
* Information used for event bindings associated with tags:
*/
- Tk_BindingTable bindingTable;
- /* Table of all bindings currently defined
- * for this widget. NULL means that no
- * bindings exist, so the table hasn't been
- * created. Each "object" used for this
- * table is the address of a tag. */
TkTextSegment *currentMarkPtr;
/* Pointer to segment for "current" mark,
* or NULL if none. */
@@ -682,44 +789,24 @@ typedef struct TkText {
Tk_OptionTable optionTable; /* Token representing the configuration
* specifications. */
- int stateEpoch; /* This is incremented each time the widget's
- * contents change, and means that any cached
- * TkTextIndex objects are no longer valid. */
int refCount; /* Number of cached TkTextIndex objects
* refering to us */
-
+ int insertCursorType; /* 0 = standard insertion cursor,
+ * 1 = block cursor. */
/*
- * Information related to the undo/redo functonality
+ * Copies of information from the shared section relating to the
+ * undo/redo functonality
*/
- TkUndoRedoStack *undoStack; /* The undo/redo stack */
-
int undo; /* Non-zero means the undo/redo behaviour is
* enabled */
int maxUndo; /* The maximum depth of the undo stack
- * expressed as the maximum number of
- * compound statements */
+ * expressed as the maximum number of
+ * compound statements */
int autoSeparators; /* Non-zero means the separators will be
* inserted automatically */
-
- int modifiedSet; /* Flag indicating that the 'dirtynesss' of
- * the text widget has been expplicitly set.
- */
-
- int isDirty; /* Flag indicating the 'dirtynesss' of the text
- * widget. If the flag is not zero, unsaved
- * modifications have been applied to the
- * text widget */
-
- int isDirtyIncrement; /* Amount with which the isDirty flag is
- * incremented every edit action */
-
- TkTextEditMode lastEditMode;/* Keeps track of what the last edit
- * mode was */
- int insertCursorType; /* 0 = standard insertion cursor,
- * 1 = block cursor. */
} TkText;
/*
@@ -837,6 +924,7 @@ typedef struct TkTextElideInfo {
int elide; /* Is the state currently elided */
int elidePriority; /* Tag priority controlling elide state */
TkTextSegment *segPtr; /* Segment to look at next */
+ int segOffset; /* Offset of segment within line */
int deftagCnts[LOTSA_TAGS];
TkTextTag *deftagPtrs[LOTSA_TAGS];
int *tagCnts; /* 0 or 1 depending if the tag with
@@ -864,6 +952,11 @@ typedef struct TkTextElideInfo {
* calculations of individual lines displayed in the widget.
*/
#define TK_TEXT_LINE_GEOMETRY 1
+/*
+ * Mask used for those options which may impact the start and
+ * end lines used in the widget.
+ */
+#define TK_TEXT_LINE_RANGE 2
/*
* Used as 'action' values in calls to TkTextInvalidateLineMetrics
@@ -892,38 +985,59 @@ EXTERN Tk_SegType tkTextToggleOnType;
EXTERN Tk_SegType tkTextToggleOffType;
/*
+ * Convenience macros for use by B-tree clients which want to access
+ * pixel information on each line. Currently only used by TkTextDisp.c
+ */
+
+#define TkBTreeLinePixelCount(text, line) \
+ line->pixels[2*text->pixelReference]
+#define TkBTreeLinePixelEpoch(text, line) \
+ line->pixels[1+2*text->pixelReference]
+
+/*
* Declarations for procedures that are used by the text-related files
* but shouldn't be used anywhere else in Tk (or by Tk clients):
*/
-
-EXTERN int TkBTreeAdjustPixelHeight _ANSI_ARGS_((TkTextLine *linePtr,
- int newPixelHeight));
+
+EXTERN int TkBTreeAdjustPixelHeight _ANSI_ARGS_((CONST TkText *textPtr,
+ TkTextLine *linePtr, int newPixelHeight));
EXTERN int TkBTreeCharTagged _ANSI_ARGS_((CONST TkTextIndex *indexPtr,
TkTextTag *tagPtr));
EXTERN void TkBTreeCheck _ANSI_ARGS_((TkTextBTree tree));
-EXTERN int TkBTreeCharsInLine _ANSI_ARGS_((TkTextLine *linePtr));
-EXTERN int TkBTreeBytesInLine _ANSI_ARGS_((TkTextLine *linePtr));
-EXTERN TkTextBTree TkBTreeCreate _ANSI_ARGS_((TkText *textPtr));
+EXTERN TkTextBTree TkBTreeCreate _ANSI_ARGS_((TkSharedText *sharedTextPtr));
+EXTERN void TkBTreeAddClient _ANSI_ARGS_((TkTextBTree tree,
+ TkText *textPtr,
+ int defaultHeight));
+EXTERN void TkBTreeClientRangeChanged _ANSI_ARGS_((TkText *textPtr,
+ int defaultHeight));
+EXTERN void TkBTreeRemoveClient _ANSI_ARGS_((TkTextBTree tree,
+ TkText *textPtr));
EXTERN void TkBTreeDestroy _ANSI_ARGS_((TkTextBTree tree));
-EXTERN void TkBTreeDeleteChars _ANSI_ARGS_((TkTextIndex *index1Ptr,
- TkTextIndex *index2Ptr));
-EXTERN TkTextLine * TkBTreeFindLine _ANSI_ARGS_((TkTextBTree tree,
- int line));
+EXTERN void TkBTreeDeleteChars _ANSI_ARGS_((TkTextBTree tree,
+ TkTextIndex *index1Ptr, TkTextIndex *index2Ptr));
+EXTERN TkTextLine * TkBTreeFindLine _ANSI_ARGS_((TkTextBTree tree,
+ CONST TkText *textPtr, int line));
EXTERN TkTextLine * TkBTreeFindPixelLine _ANSI_ARGS_((TkTextBTree tree,
- int pixels, int *pixelOffset));
+ CONST TkText *textPtr, int pixels, int *pixelOffset));
EXTERN TkTextTag ** TkBTreeGetTags _ANSI_ARGS_((CONST TkTextIndex *indexPtr,
- int *numTagsPtr));
-EXTERN void TkBTreeInsertChars _ANSI_ARGS_((TkTextIndex *indexPtr,
- CONST char *string));
-EXTERN int TkBTreeLineIndex _ANSI_ARGS_((TkTextLine *linePtr));
-EXTERN int TkBTreePixels _ANSI_ARGS_((TkTextLine *linePtr));
+ CONST TkText *textPtr, int *numTagsPtr));
+EXTERN void TkBTreeInsertChars _ANSI_ARGS_((TkTextBTree tree,
+ TkTextIndex *indexPtr, CONST char *string));
+EXTERN int TkBTreeLinesTo _ANSI_ARGS_((CONST TkText *textPtr,
+ TkTextLine *linePtr));
+EXTERN int TkBTreePixelsTo _ANSI_ARGS_((CONST TkText *textPtr,
+ TkTextLine *linePtr));
EXTERN void TkBTreeLinkSegment _ANSI_ARGS_((TkTextSegment *segPtr,
TkTextIndex *indexPtr));
-EXTERN TkTextLine * TkBTreeNextLine _ANSI_ARGS_((TkTextLine *linePtr));
+EXTERN TkTextLine * TkBTreeNextLine _ANSI_ARGS_((CONST TkText *textPtr,
+ TkTextLine *linePtr));
EXTERN int TkBTreeNextTag _ANSI_ARGS_((TkTextSearch *searchPtr));
-EXTERN int TkBTreeNumLines _ANSI_ARGS_((TkTextBTree tree));
-EXTERN int TkBTreeNumPixels _ANSI_ARGS_((TkTextBTree tree));
-EXTERN TkTextLine * TkBTreePreviousLine _ANSI_ARGS_((TkTextLine *linePtr));
+EXTERN int TkBTreeNumLines _ANSI_ARGS_((TkTextBTree tree,
+ CONST TkText *textPtr));
+EXTERN int TkBTreeNumPixels _ANSI_ARGS_((TkTextBTree tree,
+ CONST TkText *textPtr));
+EXTERN TkTextLine * TkBTreePreviousLine _ANSI_ARGS_((TkText *textPtr,
+ TkTextLine *linePtr));
EXTERN int TkBTreePrevTag _ANSI_ARGS_((TkTextSearch *searchPtr));
EXTERN void TkBTreeStartSearch _ANSI_ARGS_((TkTextIndex *index1Ptr,
TkTextIndex *index2Ptr, TkTextTag *tagPtr,
@@ -934,11 +1048,13 @@ EXTERN void TkBTreeStartSearchBack _ANSI_ARGS_((TkTextIndex *index1Ptr,
EXTERN int TkBTreeTag _ANSI_ARGS_((TkTextIndex *index1Ptr,
TkTextIndex *index2Ptr, TkTextTag *tagPtr,
int add));
-EXTERN void TkBTreeUnlinkSegment _ANSI_ARGS_((TkTextBTree tree,
+EXTERN void TkBTreeUnlinkSegment _ANSI_ARGS_((
TkTextSegment *segPtr, TkTextLine *linePtr));
EXTERN void TkTextBindProc _ANSI_ARGS_((ClientData clientData,
XEvent *eventPtr));
-EXTERN void TkTextChanged _ANSI_ARGS_((TkText *textPtr,
+EXTERN void TkTextSelectionEvent _ANSI_ARGS_((TkText *textPtr));
+EXTERN void TkTextChanged _ANSI_ARGS_((TkSharedText *sharedTextPtr,
+ TkText *textPtr,
CONST TkTextIndex *index1Ptr,
CONST TkTextIndex *index2Ptr));
EXTERN int TkTextCharBbox _ANSI_ARGS_((TkText *textPtr,
@@ -952,13 +1068,15 @@ EXTERN void TkTextCreateDInfo _ANSI_ARGS_((TkText *textPtr));
EXTERN int TkTextDLineInfo _ANSI_ARGS_((TkText *textPtr,
CONST TkTextIndex *indexPtr, int *xPtr, int *yPtr,
int *widthPtr, int *heightPtr, int *basePtr));
-EXTERN void TkTextEmbWinDisplayProc _ANSI_ARGS_((
+EXTERN void TkTextEmbWinDisplayProc _ANSI_ARGS_((TkText *textPtr,
TkTextDispChunk *chunkPtr, int x, int y,
int lineHeight, int baseline, Display *display,
Drawable dst, int screenY));
EXTERN TkTextTag * TkTextCreateTag _ANSI_ARGS_((TkText *textPtr,
CONST char *tagName, int *newTag));
EXTERN void TkTextFreeDInfo _ANSI_ARGS_((TkText *textPtr));
+EXTERN void TkTextDeleteTag _ANSI_ARGS_((TkText *textPtr,
+ TkTextTag *tagPtr));
EXTERN void TkTextFreeTag _ANSI_ARGS_((TkText *textPtr,
TkTextTag *tagPtr));
EXTERN int TkTextGetIndex _ANSI_ARGS_((Tcl_Interp *interp,
@@ -967,6 +1085,9 @@ EXTERN int TkTextGetIndex _ANSI_ARGS_((Tcl_Interp *interp,
EXTERN int TkTextGetObjIndex _ANSI_ARGS_((Tcl_Interp *interp,
TkText *textPtr, Tcl_Obj *idxPtr,
TkTextIndex *indexPtr));
+EXTERN int TkTextSharedGetObjIndex _ANSI_ARGS_((Tcl_Interp *interp,
+ TkSharedText *sharedTextPtr, Tcl_Obj *idxPtr,
+ TkTextIndex *indexPtr));
EXTERN CONST TkTextIndex* TkTextGetIndexFromObj _ANSI_ARGS_((Tcl_Interp *interp,
TkText *textPtr, Tcl_Obj *objPtr));
EXTERN TkTextTabArray * TkTextGetTabs _ANSI_ARGS_((Tcl_Interp *interp,
@@ -974,7 +1095,7 @@ EXTERN TkTextTabArray * TkTextGetTabs _ANSI_ARGS_((Tcl_Interp *interp,
EXTERN void TkTextFindDisplayLineEnd _ANSI_ARGS_((
TkText *textPtr, TkTextIndex *indexPtr,
int end, int *xOffset));
-EXTERN void TkTextIndexBackBytes _ANSI_ARGS_((
+EXTERN void TkTextIndexBackBytes _ANSI_ARGS_((CONST TkText *textPtr,
CONST TkTextIndex *srcPtr, int count,
TkTextIndex *dstPtr));
EXTERN void TkTextIndexBackChars _ANSI_ARGS_((
@@ -988,7 +1109,7 @@ EXTERN int TkTextIndexCount _ANSI_ARGS_((CONST TkText *textPtr,
CONST TkTextIndex *index1Ptr,
CONST TkTextIndex *index2Ptr,
TkTextCountType type));
-EXTERN int TkTextIndexForwBytes _ANSI_ARGS_((
+EXTERN int TkTextIndexForwBytes _ANSI_ARGS_((CONST TkText *textPtr,
CONST TkTextIndex *srcPtr, int count,
TkTextIndex *dstPtr));
EXTERN void TkTextIndexForwChars _ANSI_ARGS_((
@@ -1001,14 +1122,14 @@ EXTERN int TkTextIndexYPixels _ANSI_ARGS_((TkText *textPtr,
CONST TkTextIndex *indexPtr));
EXTERN TkTextSegment * TkTextIndexToSeg _ANSI_ARGS_((
CONST TkTextIndex *indexPtr, int *offsetPtr));
-EXTERN void TkTextInsertDisplayProc _ANSI_ARGS_((
+EXTERN void TkTextInsertDisplayProc _ANSI_ARGS_((TkText *textPtr,
TkTextDispChunk *chunkPtr, int x, int y, int height,
int baseline, Display *display, Drawable dst,
int screenY));
EXTERN void TkTextLostSelection _ANSI_ARGS_((
ClientData clientData));
EXTERN TkTextIndex * TkTextMakeCharIndex _ANSI_ARGS_((TkTextBTree tree,
- int lineIndex, int charIndex,
+ TkText *textPtr, int lineIndex, int charIndex,
TkTextIndex *indexPtr));
EXTERN int TkTextMeasureDown _ANSI_ARGS_((TkText *textPtr,
TkTextIndex *srcPtr, int distance));
@@ -1018,11 +1139,12 @@ EXTERN int TkTextIsElided _ANSI_ARGS_((CONST TkText *textPtr,
CONST TkTextIndex *indexPtr,
TkTextElideInfo *infoPtr));
EXTERN TkTextIndex * TkTextMakeByteIndex _ANSI_ARGS_((TkTextBTree tree,
- int lineIndex, int byteIndex,
+ CONST TkText *textPtr, int lineIndex, int byteIndex,
TkTextIndex *indexPtr));
EXTERN int TkTextMakePixelIndex _ANSI_ARGS_((TkText *textPtr,
int pixelIndex, TkTextIndex *indexPtr));
-EXTERN void TkTextInvalidateLineMetrics _ANSI_ARGS_((TkText *textPtr,
+EXTERN void TkTextInvalidateLineMetrics _ANSI_ARGS_((
+ TkSharedText *sharedTextPtr, TkText *textPtr,
TkTextLine *linePtr, int lineCount, int action));
EXTERN int TkTextUpdateLineMetrics _ANSI_ARGS_((TkText *textPtr,
int lineNum, int endLine, int doThisMuch));
@@ -1040,13 +1162,14 @@ EXTERN void TkTextPickCurrent _ANSI_ARGS_((TkText *textPtr,
XEvent *eventPtr));
EXTERN void TkTextPixelIndex _ANSI_ARGS_((TkText *textPtr,
int x, int y, TkTextIndex *indexPtr, int *nearest));
-EXTERN int TkTextPrintIndex _ANSI_ARGS_((
+EXTERN int TkTextPrintIndex _ANSI_ARGS_((CONST TkText *textPtr,
CONST TkTextIndex *indexPtr, char *string));
EXTERN Tcl_Obj* TkTextNewIndexObj _ANSI_ARGS_((TkText *textPtr,
CONST TkTextIndex *indexPtr));
EXTERN void TkTextRedrawRegion _ANSI_ARGS_((TkText *textPtr,
int x, int y, int width, int height));
-EXTERN void TkTextRedrawTag _ANSI_ARGS_((TkText *textPtr,
+EXTERN void TkTextRedrawTag _ANSI_ARGS_((TkSharedText *sharedTextPtr,
+ TkText *textPtr,
TkTextIndex *index1Ptr, TkTextIndex *index2Ptr,
TkTextTag *tagPtr, int withTag));
EXTERN void TkTextRelayoutWindow _ANSI_ARGS_((TkText *textPtr,
@@ -1076,6 +1199,8 @@ EXTERN int TkTextXviewCmd _ANSI_ARGS_((TkText *textPtr,
Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
EXTERN int TkTextYviewCmd _ANSI_ARGS_((TkText *textPtr,
Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+EXTERN void TkTextWinFreeClient _ANSI_ARGS_((Tcl_HashEntry *hPtr,
+ TkTextEmbWindowClient *client));
# undef TCL_STORAGE_CLASS
# define TCL_STORAGE_CLASS DLLIMPORT
diff --git a/generic/tkTextBTree.c b/generic/tkTextBTree.c
index 9f3b8e5..964dfcd 100644
--- a/generic/tkTextBTree.c
+++ b/generic/tkTextBTree.c
@@ -4,14 +4,14 @@
* This file contains code that manages the B-tree representation
* of text for Tk's text widget and implements character and
* toggle segment types.
- *
+ *
* Copyright (c) 1992-1994 The Regents of the University of California.
* Copyright (c) 1994-1995 Sun Microsystems, Inc.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tkTextBTree.c,v 1.14 2004/06/07 16:23:52 vincentdarley Exp $
+ * RCS: @(#) $Id: tkTextBTree.c,v 1.15 2004/09/10 12:13:41 vincentdarley Exp $
*/
#include "tkInt.h"
@@ -19,6 +19,29 @@
#include "tkText.h"
/*
+ * Implementation notes:
+ *
+ * Most of this file is independent of the text widget implementation
+ * and representation now. Without much effort this could be developed
+ * further into a new Tcl object type of which the Tk text widget is one
+ * example of a client.
+ *
+ * The B-tree is set up with a dummy last line of text which must not be
+ * displayed, and must _never_ have a non-zero pixel count. This dummy
+ * line is a historical convenience to avoid other code having to deal
+ * with NULL TkTextLines. Since Tk 8.5, with pixel line height
+ * calculations and peer widgets, this dummy line is becoming somewhat
+ * of a liability, and special case code has been required to deal with
+ * it. It is probably a good idea to investigate removing the dummy
+ * line completely. This could result in an overall simplification
+ * (although it would require new special case code to deal with the
+ * fact that '.text index end' would then not really point to a valid
+ * line, rather it would point to the beginning of a non-existent line
+ * one beyond all current lines - we could perhaps define that as a
+ * TkTextIndex with a NULL TkTextLine ptr).
+ */
+
+/*
* The data structure below keeps summary information about one tag as part
* of the tag information in a node.
*/
@@ -55,11 +78,19 @@ typedef struct Node {
int numChildren; /* Number of children of this node. */
int numLines; /* Total number of lines (leaves) in
* the subtree rooted here. */
- int numPixels; /* Total number of vertical
- * display pixels in the
- * subtree rooted here. */
+ int* numPixels; /* Array containing total number
+ * of vertical display pixels in
+ * the subtree rooted here, one
+ * entry for each peer widget. */
} Node;
+/*
+ * Used to avoid having to allocate and deallocate arrays on the
+ * fly for commonly used procedures. Must be > 0.
+ */
+
+#define PIXEL_CLIENTS 5
+
/*
* Upper and lower bounds on how many children a node may have:
* rebalance when either of these limits is exceeded. MAX_CHILDREN
@@ -70,13 +101,24 @@ typedef struct Node {
#define MIN_CHILDREN 6
/*
- * The data structure below defines an entire B-tree.
+ * The data structure below defines an entire B-tree. Since text
+ * widgets are the only current B-tree clients, 'clients' and
+ * 'pixelReferences' are identical.
*/
typedef struct BTree {
Node *rootPtr; /* Pointer to root of B-tree. */
- TkText *textPtr; /* Used to find tagTable in consistency
- * checking code */
+ int clients; /* Number of clients of this
+ * B-tree */
+ int pixelReferences; /* Number of clients of this
+ * B-tree which care about
+ * pixel heights */
+ TkSharedText *sharedTextPtr; /* Used to find tagTable in consistency
+ * checking code, and to access
+ * list of all B-tree clients */
+ int startEndCount;
+ TkTextLine **startEnd;
+ TkText **startEndRef;
} BTree;
/*
@@ -116,6 +158,10 @@ int tkBTreeDebug = 0;
* Forward declarations for procedures defined in this file:
*/
+static int AdjustPixelClient _ANSI_ARGS_((BTree *treePtr,
+ int defaultHeight, Node *nodePtr, TkTextLine *start,
+ TkTextLine *end, int useReference,
+ int newPixelReferences, int *counting));
static void ChangeNodeToggleCount _ANSI_ARGS_((Node *nodePtr,
TkTextTag *tagPtr, int delta));
static void CharCheckProc _ANSI_ARGS_((TkTextSegment *segPtr,
@@ -126,7 +172,8 @@ static TkTextSegment * CharCleanupProc _ANSI_ARGS_((TkTextSegment *segPtr,
TkTextLine *linePtr));
static TkTextSegment * CharSplitProc _ANSI_ARGS_((TkTextSegment *segPtr,
int index));
-static void CheckNodeConsistency _ANSI_ARGS_((Node *nodePtr));
+static void CheckNodeConsistency _ANSI_ARGS_((Node *nodePtr,
+ int references));
static void CleanupLine _ANSI_ARGS_((TkTextLine *linePtr));
static void DeleteSummaries _ANSI_ARGS_((Summary *tagPtr));
static void DestroyNode _ANSI_ARGS_((Node *nodePtr));
@@ -135,7 +182,10 @@ static TkTextSegment * FindTagEnd _ANSI_ARGS_((TkTextBTree tree,
static void IncCount _ANSI_ARGS_((TkTextTag *tagPtr, int inc,
TagInfo *tagInfoPtr));
static void Rebalance _ANSI_ARGS_((BTree *treePtr, Node *nodePtr));
-static void RecomputeNodeCounts _ANSI_ARGS_((Node *nodePtr));
+static void RecomputeNodeCounts _ANSI_ARGS_((BTree *treePtr,
+ Node *nodePtr));
+static void RemovePixelClient _ANSI_ARGS_((BTree *treePtr,
+ Node *nodePtr, int overwriteWithLast));
static TkTextSegment * SplitSeg _ANSI_ARGS_((TkTextIndex *indexPtr));
static void ToggleCheckProc _ANSI_ARGS_((TkTextSegment *segPtr,
TkTextLine *linePtr));
@@ -147,6 +197,14 @@ static void ToggleLineChangeProc _ANSI_ARGS_((TkTextSegment *segPtr,
TkTextLine *linePtr));
static TkTextSegment * FindTagStart _ANSI_ARGS_((TkTextBTree tree,
TkTextTag *tagPtr, TkTextIndex *indexPtr));
+static void AdjustStartEndRefs _ANSI_ARGS_((BTree *treePtr,
+ TkText *textPtr, int action));
+
+/*
+ * Actions for use by AdjustStartEndRefs
+ */
+#define TEXT_ADD_REFS 1
+#define TEXT_REMOVE_REFS 2
/*
* Type record for character segments:
@@ -213,8 +271,8 @@ Tk_SegType tkTextToggleOffType = {
*/
TkTextBTree
-TkBTreeCreate(textPtr)
- TkText *textPtr;
+TkBTreeCreate(sharedTextPtr)
+ TkSharedText *sharedTextPtr;
{
register BTree *treePtr;
register Node *rootPtr;
@@ -231,6 +289,7 @@ TkBTreeCreate(textPtr)
rootPtr = (Node *) ckalloc(sizeof(Node));
linePtr = (TkTextLine *) ckalloc(sizeof(TkTextLine));
linePtr2 = (TkTextLine *) ckalloc(sizeof(TkTextLine));
+
rootPtr->parentPtr = NULL;
rootPtr->nextPtr = NULL;
rootPtr->summaryPtr = NULL;
@@ -238,12 +297,13 @@ TkBTreeCreate(textPtr)
rootPtr->children.linePtr = linePtr;
rootPtr->numChildren = 2;
rootPtr->numLines = 2;
- rootPtr->numPixels = textPtr->charHeight;
- linePtr->pixelHeight = textPtr->charHeight;
- linePtr->pixelCalculationEpoch = 0;
- /* The last line permanently has a pixel height of zero */
- linePtr2->pixelHeight = 0;
- linePtr2->pixelCalculationEpoch = 1;
+ /*
+ * The tree currently has no registered clients, so all pixel
+ * count pointers are simply NULL
+ */
+ rootPtr->numPixels = NULL;
+ linePtr->pixels = NULL;
+ linePtr2->pixels = NULL;
linePtr->parentPtr = rootPtr;
linePtr->nextPtr = linePtr2;
@@ -266,21 +326,152 @@ TkBTreeCreate(textPtr)
segPtr->body.chars[1] = 0;
treePtr = (BTree *) ckalloc(sizeof(BTree));
+ treePtr->sharedTextPtr = sharedTextPtr;
treePtr->rootPtr = rootPtr;
- treePtr->textPtr = textPtr;
-
+ treePtr->clients = 0;
+ treePtr->pixelReferences = 0;
+ treePtr->startEndCount = 0;
+ treePtr->startEnd = NULL;
+ treePtr->startEndRef = NULL;
+
return (TkTextBTree) treePtr;
}
/*
*----------------------------------------------------------------------
*
+ * TkBTreeAddClient --
+ *
+ * This procedure is called to provide a client with access to
+ * a given B-tree. If the client wishes to make use of the
+ * B-tree's pixel height storage, caching and calculation
+ * mechanisms, then a non-negative 'defaultHeight' must be
+ * provided. In this case the return value is a pixel tree
+ * reference which must be provided in all of the B-tree API which
+ * refers to or modifies pixel heights:
+ *
+ * TkBTreeAdjustPixelHeight,
+ * TkBTreeFindPixelLine,
+ * TkBTreeNumPixels,
+ * TkBTreePixelsTo,
+ * (and two private functions AdjustPixelClient, RemovePixelClient).
+ *
+ * If this is not provided, then the above functions must
+ * never be called for this client.
+ *
+ * Results:
+ * The return value is the pixelReference used by the B-tree to
+ * refer to pixel counts for the new client. It should be stored
+ * by the caller. If defaultHeight was negative, then the return
+ * value will be -1.
+ *
+ * Side effects:
+ * Memory may be allocated and initialized.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkBTreeAddClient(tree, textPtr, defaultHeight)
+ TkTextBTree tree; /* B-tree to add a client to */
+ TkText *textPtr; /* Client to add */
+ int defaultHeight; /* Default line height for the new
+ * client, or -1 if no pixel
+ * heights are to be kept. */
+{
+ register BTree *treePtr = (BTree*) tree;
+
+ if (treePtr == NULL) {
+ Tcl_Panic("NULL treePtr in TkBTreeAddClient");
+ }
+
+ if (textPtr->start != NULL || textPtr->end != NULL) {
+ AdjustStartEndRefs(treePtr, textPtr, TEXT_ADD_REFS);
+ }
+
+ if (defaultHeight >= 0) {
+ TkTextLine *end;
+ int counting = (textPtr->start == NULL ? 1 : 0);
+ int useReference = treePtr->pixelReferences;
+ /*
+ * We must set the 'end' value in AdjustPixelClient so that
+ * the last dummy line in the B-tree doesn't contain
+ * a pixel height.
+ */
+ end = textPtr->end;
+ if (end == NULL) {
+ end = TkBTreeFindLine(tree, NULL, TkBTreeNumLines(tree, NULL));
+ }
+ AdjustPixelClient(treePtr, defaultHeight, treePtr->rootPtr,
+ textPtr->start, end, useReference,
+ 1 + useReference, &counting);
+
+ textPtr->pixelReference = useReference;
+ treePtr->pixelReferences++;
+ } else {
+ textPtr->pixelReference = -1;
+ }
+ treePtr->clients++;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkBTreeClientRangeChanged --
+ *
+ * Called when the -startline or -endline options of a text
+ * widget client of the B-tree have changed.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Lots of processing of the B-tree is done, with potential
+ * for memory to be allocated and initialized for the pixel
+ * heights of the widget.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkBTreeClientRangeChanged(textPtr, defaultHeight)
+ TkText *textPtr; /* Client whose start, end have
+ * changed. */
+ int defaultHeight; /* Default line height for the new
+ * client, or -1 if no pixel
+ * heights are to be kept. */
+{
+ TkTextLine *end;
+ BTree *treePtr = (BTree*) textPtr->sharedTextPtr->tree;
+
+ int counting = (textPtr->start == NULL ? 1 : 0);
+ int useReference = textPtr->pixelReference;
+
+ AdjustStartEndRefs(treePtr, textPtr, TEXT_ADD_REFS | TEXT_REMOVE_REFS);
+ /*
+ * We must set the 'end' value in AdjustPixelClient so that
+ * the last dummy line in the B-tree doesn't contain
+ * a pixel height.
+ */
+ end = textPtr->end;
+ if (end == NULL) {
+ end = TkBTreeFindLine(textPtr->sharedTextPtr->tree,
+ NULL, TkBTreeNumLines(textPtr->sharedTextPtr->tree, NULL));
+ }
+ AdjustPixelClient(treePtr, defaultHeight, treePtr->rootPtr,
+ textPtr->start, end, useReference,
+ treePtr->pixelReferences, &counting);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* TkBTreeDestroy --
*
* Delete a B-tree, recycling all of the storage it contains.
*
* Results:
- * The tree given by treePtr is deleted. TreePtr should never
+ * The tree is deleted, so 'tree' should never
* again be used.
*
* Side effects:
@@ -291,17 +482,347 @@ TkBTreeCreate(textPtr)
void
TkBTreeDestroy(tree)
- TkTextBTree tree; /* Pointer to tree to delete. */
+ TkTextBTree tree; /* Tree to clean up */
{
BTree *treePtr = (BTree *) tree;
+ /*
+ * There's no need to loop over each client of the tree, calling
+ * 'TkBTreeRemoveClient', since the 'DestroyNode' will clean
+ * everything up itself.
+ */
+
DestroyNode(treePtr->rootPtr);
+ if (treePtr->startEnd != NULL) {
+ ckfree((char *) treePtr->startEnd);
+ ckfree((char *) treePtr->startEndRef);
+ }
ckfree((char *) treePtr);
}
/*
*----------------------------------------------------------------------
*
+ * TkBTreeRemoveClient --
+ *
+ * Remove a client widget from its B-tree, cleaning up the pixel
+ * arrays which it uses if necessary. If this is the last such
+ * widget, we also destroy the whole tree.
+ *
+ * Results:
+ * All tree-specific aspects of the given client are deleted.
+ * If no more references exist, then the given tree is also
+ * deleted (in which case 'tree' must not be used again).
+ *
+ * Side effects:
+ * Memory may be freed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkBTreeRemoveClient(tree, textPtr)
+ TkTextBTree tree; /* Tree to remove client from */
+ TkText *textPtr; /* Client to remove. */
+{
+ BTree *treePtr = (BTree *) tree;
+ int pixelReference = textPtr->pixelReference;
+
+ if (treePtr->clients == 1) {
+ /* The last reference to the tree */
+ DestroyNode(treePtr->rootPtr);
+ ckfree((char *) treePtr);
+ return;
+ } else if (pixelReference == -1) {
+ /* A client which doesn't care about pixels */
+ treePtr->clients--;
+ } else {
+ /* Clean up pixel data for the given reference */
+
+ if (pixelReference == (treePtr->pixelReferences-1)) {
+ /*
+ * The widget we're removing has the last index,
+ * so deletion is easier.
+ */
+ RemovePixelClient(treePtr, treePtr->rootPtr, -1);
+ } else {
+ TkText *adjustPtr;
+
+ RemovePixelClient(treePtr, treePtr->rootPtr, pixelReference);
+
+ /*
+ * Now we need to adjust the 'pixelReference' of the
+ * peer widget whose storage we've just moved.
+ */
+ adjustPtr = treePtr->sharedTextPtr->peers;
+ while (adjustPtr != NULL) {
+ if (adjustPtr->pixelReference == (treePtr->pixelReferences-1)) {
+ adjustPtr->pixelReference = pixelReference;
+ break;
+ }
+ adjustPtr = adjustPtr->next;
+ }
+ if (adjustPtr == NULL) {
+ Tcl_Panic("Couldn't find text widget with correct reference");
+ }
+ }
+ treePtr->pixelReferences--;
+ treePtr->clients--;
+ }
+
+ if (textPtr->start != NULL || textPtr->end != NULL) {
+ AdjustStartEndRefs(treePtr, textPtr, TEXT_REMOVE_REFS);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AdjustStartEndRefs --
+ *
+ * Modify B-tree's cache of start, end lines for the given text
+ * widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The number of cached items may change
+ * (treePtr->startEndCount).
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+AdjustStartEndRefs(treePtr, textPtr, action)
+ BTree *treePtr; /* The entire B-tree */
+ TkText *textPtr; /* The text widget for which we want to
+ * adjust it's start and end cache. */
+ int action; /* Action to perform */
+{
+ if (action & TEXT_REMOVE_REFS) {
+ int i = 0;
+ int count = 0;
+
+ while (i < treePtr->startEndCount) {
+ if (i != count) {
+ treePtr->startEnd[count] = treePtr->startEnd[i];
+ treePtr->startEndRef[count] = treePtr->startEndRef[i];
+ }
+ if (treePtr->startEndRef[i] != textPtr) {
+ count++;
+ }
+ i++;
+ }
+ treePtr->startEndCount = count;
+ treePtr->startEnd = (TkTextLine**)ckrealloc((char*)treePtr->startEnd,
+ sizeof(TkTextLine*)*count);
+ treePtr->startEndRef = (TkText**)ckrealloc((char*)treePtr->startEndRef,
+ sizeof(TkText*)*count);
+ }
+ if ((action & TEXT_ADD_REFS)
+ && (textPtr->start != NULL || textPtr->end != NULL)) {
+ int count;
+
+ if (textPtr->start != NULL) treePtr->startEndCount++;
+ if (textPtr->end != NULL) treePtr->startEndCount++;
+
+ count = treePtr->startEndCount;
+
+ treePtr->startEnd = (TkTextLine**)ckrealloc((char*)treePtr->startEnd,
+ sizeof(TkTextLine*)*count);
+ treePtr->startEndRef = (TkText**)ckrealloc((char*)treePtr->startEndRef,
+ sizeof(TkText*)*count);
+
+ if (textPtr->start != NULL) {
+ count--;
+ treePtr->startEnd[count] = textPtr->start;
+ treePtr->startEndRef[count] = treePtr->sharedTextPtr->peers;
+ }
+ if (textPtr->end != NULL) {
+ count--;
+ treePtr->startEnd[count] = textPtr->end;
+ treePtr->startEndRef[count] = treePtr->sharedTextPtr->peers;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AdjustPixelClient --
+ *
+ * Utility procedure used to update all data structures for the
+ * existence of a new peer widget based on this B-tree, or for
+ * the modification of the start, end lines of an existing peer
+ * widget.
+ *
+ * Immediately _after_ calling this, treePtr->clients and
+ * treePtr->pixelReferences should be adjusted if needed (i.e.
+ * if this is a new peer).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * All the storage for Nodes and TkTextLines in the tree may
+ * be adjusted.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+AdjustPixelClient(treePtr, defaultHeight, nodePtr, start, end,
+ useReference, newPixelReferences, counting)
+ BTree *treePtr; /* Pointer to tree */
+ int defaultHeight; /* Default pixel line
+ * height, which can be zero. */
+ Node *nodePtr; /* Adjust from this node
+ * downwards */
+ TkTextLine *start; /* First line for this pixel
+ * client */
+ TkTextLine *end; /* Last line for this pixel
+ * client */
+ int useReference; /* pixel reference for the
+ * client we are adding or
+ * changing */
+ int newPixelReferences; /* New number of pixel
+ * references to this B-tree */
+ int *counting; /* References an integer which
+ * is zero if we're outside the
+ * relevant range for this
+ * client, and 1 if we're
+ * inside. */
+{
+ int pixelCount = 0;
+
+ /*
+ * Traverse entire tree down from nodePtr, reallocating pixel
+ * structures for each Node and TkTextLine, adding room for the new
+ * peer's pixel information (1 extra int per Node, 2 extra ints per
+ * TkTextLine). Also copy the information from the last peer into
+ * the new space (so it contains something sensible).
+ */
+
+ if (nodePtr->level != 0) {
+ Node *loopPtr = nodePtr->children.nodePtr;
+ while (loopPtr != NULL) {
+ pixelCount += AdjustPixelClient(treePtr, defaultHeight, loopPtr,
+ start, end, useReference,
+ newPixelReferences, counting);
+ loopPtr = loopPtr->nextPtr;
+ }
+ } else {
+ register TkTextLine *linePtr = nodePtr->children.linePtr;
+
+ while (linePtr != NULL) {
+ if (!*counting && (linePtr == start)) {
+ *counting = 1;
+ }
+ if (*counting && (linePtr == end)) {
+ *counting = 0;
+ }
+ if (newPixelReferences != treePtr->pixelReferences) {
+ linePtr->pixels = (int*)ckrealloc((char*)linePtr->pixels,
+ sizeof(int)*2*newPixelReferences);
+ }
+ /*
+ * Notice that for the very last line, we are never counting
+ * and therefore this always has a height of 0 and an epoch
+ * of 1.
+ */
+ linePtr->pixels[2*useReference] = (*counting ? defaultHeight : 0);
+ linePtr->pixels[1+2*useReference] = (*counting ? 0 : 1);
+ pixelCount += linePtr->pixels[2*useReference];
+
+ linePtr = linePtr->nextPtr;
+ }
+ }
+ if (newPixelReferences != treePtr->pixelReferences) {
+ nodePtr->numPixels = (int*)ckrealloc((char*)nodePtr->numPixels,
+ sizeof(int)*newPixelReferences);
+ }
+ nodePtr->numPixels[useReference] = pixelCount;
+ return pixelCount;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RemovePixelClient --
+ *
+ * Utility procedure used to update all data structures for the
+ * removal of a peer widget which used to be based on this B-tree.
+ *
+ * Immediately _after_ calling this, treePtr->clients should
+ * be decremented.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * All the storage for Nodes and TkTextLines in the tree may
+ * be adjusted.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+RemovePixelClient(treePtr, nodePtr, overwriteWithLast)
+ BTree *treePtr; /* Pointer to tree */
+ Node *nodePtr; /* Adjust from this node
+ * downwards */
+ int overwriteWithLast; /* Over-write this peer widget's
+ * information with the last one
+ */
+{
+ /*
+ * Traverse entire tree down from nodePtr, reallocating pixel
+ * structures for each Node and TkTextLine, removing space allocated
+ * for one peer. If 'overwriteWithLast' is not -1, then copy the
+ * information which was in the last slot on top of one of the
+ * others (i.e. it's not the last one we're deleting).
+ */
+
+ if (overwriteWithLast != -1) {
+ nodePtr->numPixels[overwriteWithLast]
+ = nodePtr->numPixels[treePtr->pixelReferences-1];
+ }
+ if (treePtr->pixelReferences == 1) {
+ nodePtr->numPixels = NULL;
+ } else {
+ nodePtr->numPixels = (int*)ckrealloc((char*)nodePtr->numPixels,
+ sizeof(int)*(treePtr->pixelReferences-1));
+ }
+ if (nodePtr->level != 0) {
+ nodePtr = nodePtr->children.nodePtr;
+ while (nodePtr != NULL) {
+ RemovePixelClient(treePtr, nodePtr, overwriteWithLast);
+ nodePtr = nodePtr->nextPtr;
+ }
+ } else {
+ register TkTextLine *linePtr = nodePtr->children.linePtr;
+ while (linePtr != NULL) {
+ if (overwriteWithLast != -1) {
+ linePtr->pixels[2*overwriteWithLast]
+ = linePtr->pixels[2*(treePtr->pixelReferences-1)];
+ linePtr->pixels[1+2*overwriteWithLast]
+ = linePtr->pixels[1+2*(treePtr->pixelReferences-1)];
+ }
+ if (treePtr->pixelReferences == 1) {
+ linePtr->pixels = NULL;
+ } else {
+ linePtr->pixels = (int*)ckrealloc((char*)linePtr->pixels,
+ sizeof(int)*2*(treePtr->pixelReferences-1));
+ }
+ linePtr = linePtr->nextPtr;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* DestroyNode --
*
* This is a recursive utility procedure used during the deletion
@@ -318,7 +839,7 @@ TkBTreeDestroy(tree)
static void
DestroyNode(nodePtr)
- register Node *nodePtr;
+ register Node *nodePtr; /* Destroy from this node downwards */
{
if (nodePtr->level == 0) {
TkTextLine *linePtr;
@@ -332,6 +853,7 @@ DestroyNode(nodePtr)
linePtr->segPtr = segPtr->nextPtr;
(*segPtr->typePtr->deleteProc)(segPtr, linePtr, 1);
}
+ ckfree((char *) linePtr->pixels);
ckfree((char *) linePtr);
}
} else {
@@ -344,6 +866,7 @@ DestroyNode(nodePtr)
}
}
DeleteSummaries(nodePtr->summaryPtr);
+ ckfree((char *) nodePtr->numPixels);
ckfree((char *) nodePtr);
}
@@ -396,7 +919,8 @@ DeleteSummaries(summaryPtr)
*/
int
-TkBTreeAdjustPixelHeight(linePtr, newPixelHeight)
+TkBTreeAdjustPixelHeight(textPtr, linePtr, newPixelHeight)
+ CONST TkText *textPtr; /* Client of the B-tree */
register TkTextLine *linePtr; /* The logical line to update */
int newPixelHeight; /* The line's known height
* in pixels */
@@ -404,8 +928,9 @@ TkBTreeAdjustPixelHeight(linePtr, newPixelHeight)
register Node *nodePtr;
int changeToPixelCount; /* Counts change to total number of
* pixels in file. */
-
- changeToPixelCount = newPixelHeight - linePtr->pixelHeight;
+ int pixelReference = textPtr->pixelReference;
+
+ changeToPixelCount = newPixelHeight - linePtr->pixels[2*pixelReference];
/*
* Increment the pixel counts in all the parent nodes of the
@@ -413,16 +938,16 @@ TkBTreeAdjustPixelHeight(linePtr, newPixelHeight)
*/
nodePtr = linePtr->parentPtr;
- nodePtr->numPixels += changeToPixelCount;
+ nodePtr->numPixels[pixelReference] += changeToPixelCount;
while (nodePtr->parentPtr != NULL) {
nodePtr = nodePtr->parentPtr;
- nodePtr->numPixels += changeToPixelCount;
+ nodePtr->numPixels[pixelReference] += changeToPixelCount;
}
- linePtr->pixelHeight = newPixelHeight;
+ linePtr->pixels[2*pixelReference] = newPixelHeight;
- return nodePtr->numPixels;
+ return nodePtr->numPixels[pixelReference];
}
/*
@@ -444,7 +969,8 @@ TkBTreeAdjustPixelHeight(linePtr, newPixelHeight)
*/
void
-TkBTreeInsertChars(indexPtr, string)
+TkBTreeInsertChars(tree, indexPtr, string)
+ TkTextBTree tree; /* Tree to insert into */
register TkTextIndex *indexPtr; /* Indicates where to insert text.
* When the procedure returns, this
* index is no longer valid because
@@ -471,9 +997,12 @@ TkBTreeInsertChars(indexPtr, string)
* one in current chunk. */
int changeToLineCount; /* Counts change to total number of
* lines in file. */
- int changeToPixelCount; /* Counts change to total number of
+ int *changeToPixelCount; /* Counts change to total number of
* pixels in file. */
-
+ int ref;
+ int pixels[PIXEL_CLIENTS];
+
+ BTree *treePtr = (BTree*)tree;
prevPtr = SplitSeg(indexPtr);
linePtr = indexPtr->linePtr;
curPtr = prevPtr;
@@ -485,7 +1014,16 @@ TkBTreeInsertChars(indexPtr, string)
*/
changeToLineCount = 0;
- changeToPixelCount = 0;
+ if (treePtr->pixelReferences > PIXEL_CLIENTS) {
+ changeToPixelCount = (int*) ckalloc(sizeof(int) *
+ treePtr->pixelReferences);
+ } else {
+ changeToPixelCount = pixels;
+ }
+ for (ref = 0; ref < treePtr->pixelReferences; ref++) {
+ changeToPixelCount[ref] = 0;
+ }
+
while (*string != 0) {
for (eol = string; *eol != 0; eol++) {
if (*eol == '\n') {
@@ -517,22 +1055,27 @@ TkBTreeInsertChars(indexPtr, string)
*/
newLinePtr = (TkTextLine *) ckalloc(sizeof(TkTextLine));
+ newLinePtr->pixels =
+ (int*) ckalloc(sizeof(int)*2*treePtr->pixelReferences);
+
newLinePtr->parentPtr = linePtr->parentPtr;
newLinePtr->nextPtr = linePtr->nextPtr;
linePtr->nextPtr = newLinePtr;
newLinePtr->segPtr = segPtr->nextPtr;
/*
* Set up a starting default height, which will be re-adjusted
- * later
+ * later. We need to do this for each referenced widget
*/
- newLinePtr->pixelHeight = linePtr->pixelHeight;
- newLinePtr->pixelCalculationEpoch = 0;
+ for (ref = 0; ref < treePtr->pixelReferences; ref++) {
+ newLinePtr->pixels[2*ref] = linePtr->pixels[2*ref];
+ newLinePtr->pixels[1+2*ref] = 0;
+ changeToPixelCount[ref] += newLinePtr->pixels[2*ref];
+ }
segPtr->nextPtr = NULL;
linePtr = newLinePtr;
curPtr = NULL;
changeToLineCount++;
- changeToPixelCount += newLinePtr->pixelHeight;
string = eol;
}
@@ -543,7 +1086,7 @@ TkBTreeInsertChars(indexPtr, string)
* the function is robust to that case anyway. (We must never
* re-calculated the line height of the last line).
*/
- TkTextInvalidateLineMetrics(((BTree*)indexPtr->tree)->textPtr,
+ TkTextInvalidateLineMetrics(treePtr->sharedTextPtr, NULL,
indexPtr->linePtr, changeToLineCount,
TK_TEXT_INVALIDATE_INSERT);
@@ -565,12 +1108,18 @@ TkBTreeInsertChars(indexPtr, string)
for (nodePtr = linePtr->parentPtr ; nodePtr != NULL;
nodePtr = nodePtr->parentPtr) {
nodePtr->numLines += changeToLineCount;
- nodePtr->numPixels += changeToPixelCount;
+ for (ref = 0; ref < treePtr->pixelReferences; ref++) {
+ nodePtr->numPixels[ref] += changeToPixelCount[ref];
+ }
+ }
+ if (treePtr->pixelReferences > PIXEL_CLIENTS) {
+ ckfree((char*)changeToPixelCount);
}
+
nodePtr = linePtr->parentPtr;
nodePtr->numChildren += changeToLineCount;
if (nodePtr->numChildren > MAX_CHILDREN) {
- Rebalance((BTree *) indexPtr->tree, nodePtr);
+ Rebalance(treePtr, nodePtr);
}
if (tkBTreeDebug) {
@@ -715,7 +1264,8 @@ CleanupLine(linePtr)
*/
void
-TkBTreeDeleteChars(index1Ptr, index2Ptr)
+TkBTreeDeleteChars(tree, index1Ptr, index2Ptr)
+ TkTextBTree tree; /* Tree to delete from */
register TkTextIndex *index1Ptr; /* Indicates first character that is
* to be deleted. */
register TkTextIndex *index2Ptr; /* Indicates character just after the
@@ -729,6 +1279,8 @@ TkBTreeDeleteChars(index1Ptr, index2Ptr)
TkTextLine *curLinePtr;
Node *curNodePtr, *nodePtr;
int changeToLineCount = 0;
+ int ref;
+ BTree *treePtr = (BTree*)tree;
/*
* Tricky point: split at index2Ptr first; otherwise the split
@@ -767,7 +1319,7 @@ TkBTreeDeleteChars(index1Ptr, index2Ptr)
* (unless it's the starting line for the range).
*/
- nextLinePtr = TkBTreeNextLine(curLinePtr);
+ nextLinePtr = TkBTreeNextLine(NULL, curLinePtr);
if (curLinePtr != index1Ptr->linePtr) {
if (curNodePtr == index1Ptr->linePtr->parentPtr) {
index1Ptr->linePtr->nextPtr = curLinePtr->nextPtr;
@@ -777,10 +1329,35 @@ TkBTreeDeleteChars(index1Ptr, index2Ptr)
for (nodePtr = curNodePtr; nodePtr != NULL;
nodePtr = nodePtr->parentPtr) {
nodePtr->numLines--;
- nodePtr->numPixels -= curLinePtr->pixelHeight;
+ for (ref = 0; ref < treePtr->pixelReferences; ref++) {
+ nodePtr->numPixels[ref] -= curLinePtr->pixels[2*ref];
+ }
}
changeToLineCount++;
curNodePtr->numChildren--;
+ /* Check if we need to adjust any partial clients */
+ if (treePtr->startEnd != NULL) {
+ int checkCount = 0;
+ while (checkCount < treePtr->startEndCount) {
+ if (treePtr->startEnd[checkCount] == curLinePtr) {
+ TkText *peer = treePtr->startEndRef[checkCount];
+ /*
+ * We're deleting a line which is the start
+ * or end of a current client. This means
+ * we need to adjust that client.
+ */
+ treePtr->startEnd[checkCount] = nextLinePtr;
+ if (peer->start == curLinePtr) {
+ peer->start = nextLinePtr;
+ }
+ if (peer->end == curLinePtr) {
+ peer->end = nextLinePtr;
+ }
+ }
+ checkCount++;
+ }
+ }
+ ckfree((char *) curLinePtr->pixels);
ckfree((char *) curLinePtr);
}
curLinePtr = nextLinePtr;
@@ -851,7 +1428,9 @@ TkBTreeDeleteChars(index1Ptr, index2Ptr)
for (nodePtr = curNodePtr; nodePtr != NULL;
nodePtr = nodePtr->parentPtr) {
nodePtr->numLines--;
- nodePtr->numPixels -= index2Ptr->linePtr->pixelHeight;
+ for (ref = 0; ref < treePtr->pixelReferences; ref++) {
+ nodePtr->numPixels[ref] -= index2Ptr->linePtr->pixels[2*ref];
+ }
}
changeToLineCount++;
curNodePtr->numChildren--;
@@ -864,6 +1443,36 @@ TkBTreeDeleteChars(index1Ptr, index2Ptr)
}
prevLinePtr->nextPtr = index2Ptr->linePtr->nextPtr;
}
+ /*
+ * Check if we need to adjust any partial clients. In this case
+ * if we're deleting the line, we actually move back to the
+ * previous line for our (start,end) storage. We do this
+ * because we still want the portion of the second line that
+ * still exists to be in the start,end range.
+ */
+ if (treePtr->startEnd != NULL) {
+ int checkCount = 0;
+
+ while (treePtr->startEnd[checkCount] != NULL) {
+ if (treePtr->startEnd[checkCount] == index2Ptr->linePtr) {
+ TkText *peer = treePtr->startEndRef[checkCount];
+ /*
+ * We're deleting a line which is the start
+ * or end of a current client. This means
+ * we need to adjust that client.
+ */
+ treePtr->startEnd[checkCount] = index1Ptr->linePtr;
+ if (peer->start == index2Ptr->linePtr) {
+ peer->start = index1Ptr->linePtr;
+ }
+ if (peer->end == index2Ptr->linePtr) {
+ peer->end = index1Ptr->linePtr;
+ }
+ }
+ checkCount++;
+ }
+ }
+ ckfree((char *) index2Ptr->linePtr->pixels);
ckfree((char *) index2Ptr->linePtr);
Rebalance((BTree *) index2Ptr->tree, curNodePtr);
@@ -881,8 +1490,8 @@ TkBTreeDeleteChars(index1Ptr, index2Ptr)
* of text. I _believe_ that it isn't possible to get this far with
* the last line, but it is good to be safe.
*/
- if (TkBTreeNextLine(index1Ptr->linePtr) != NULL) {
- TkTextInvalidateLineMetrics(((BTree*)index1Ptr->tree)->textPtr,
+ if (TkBTreeNextLine(NULL, index1Ptr->linePtr) != NULL) {
+ TkTextInvalidateLineMetrics(treePtr->sharedTextPtr, NULL,
index1Ptr->linePtr, changeToLineCount,
TK_TEXT_INVALIDATE_DELETE);
}
@@ -915,21 +1524,42 @@ TkBTreeDeleteChars(index1Ptr, index2Ptr)
*/
TkTextLine *
-TkBTreeFindLine(tree, line)
+TkBTreeFindLine(tree, textPtr, line)
TkTextBTree tree; /* B-tree in which to find line. */
+ CONST TkText *textPtr; /* Relative to this client of the
+ * B-tree */
int line; /* Index of desired line. */
{
BTree *treePtr = (BTree *) tree;
register Node *nodePtr;
register TkTextLine *linePtr;
- int linesLeft;
+ if (treePtr == NULL) {
+ treePtr = (BTree *) textPtr->sharedTextPtr->tree;
+ }
+
nodePtr = treePtr->rootPtr;
- linesLeft = line;
if ((line < 0) || (line >= nodePtr->numLines)) {
return NULL;
}
-
+
+ /*
+ * Check for the any start/end offset for this text widget
+ */
+ if (textPtr != NULL) {
+ if (textPtr->start != NULL) {
+ line += TkBTreeLinesTo(NULL, textPtr->start);
+ if (line >= nodePtr->numLines) {
+ return NULL;
+ }
+ }
+ if (textPtr->end != NULL) {
+ if (line > TkBTreeLinesTo(NULL, textPtr->end)) {
+ return NULL;
+ }
+ }
+ }
+
/*
* Work down through levels of the tree until a node is found at
* level 0.
@@ -937,12 +1567,12 @@ TkBTreeFindLine(tree, line)
while (nodePtr->level != 0) {
for (nodePtr = nodePtr->children.nodePtr;
- nodePtr->numLines <= linesLeft;
+ nodePtr->numLines <= line;
nodePtr = nodePtr->nextPtr) {
if (nodePtr == NULL) {
Tcl_Panic("TkBTreeFindLine ran out of nodes");
}
- linesLeft -= nodePtr->numLines;
+ line -= nodePtr->numLines;
}
}
@@ -950,12 +1580,12 @@ TkBTreeFindLine(tree, line)
* Work through the lines attached to the level-0 node.
*/
- for (linePtr = nodePtr->children.linePtr; linesLeft > 0;
+ for (linePtr = nodePtr->children.linePtr; line > 0;
linePtr = linePtr->nextPtr) {
if (linePtr == NULL) {
Tcl_Panic("TkBTreeFindLine ran out of lines");
}
- linesLeft -= 1;
+ line -= 1;
}
return linePtr;
}
@@ -985,24 +1615,25 @@ TkBTreeFindLine(tree, line)
*/
TkTextLine *
-TkBTreeFindPixelLine(tree, pixels, pixelOffset)
- TkTextBTree tree; /* B-tree in which to find line. */
- int pixels; /* Index of desired line. */
+TkBTreeFindPixelLine(tree, textPtr, pixels, pixelOffset)
+ TkTextBTree tree; /* B-tree to use. */
+ CONST TkText *textPtr; /* Relative to this client of the
+ * B-tree */
+ int pixels; /* Pixel index of desired line. */
int *pixelOffset; /* Used to return offset */
{
BTree *treePtr = (BTree *) tree;
register Node *nodePtr;
register TkTextLine *linePtr;
- int pixelsLeft;
-
+ int pixelReference = textPtr->pixelReference;
+
nodePtr = treePtr->rootPtr;
- pixelsLeft = pixels;
- if ((pixels < 0) || (pixels > nodePtr->numPixels)) {
+ if ((pixels < 0) || (pixels > nodePtr->numPixels[pixelReference])) {
return NULL;
}
- if (nodePtr->numPixels == 0) {
+ if (nodePtr->numPixels[pixelReference] == 0) {
Tcl_Panic("TkBTreeFindPixelLine called with empty window");
}
@@ -1013,12 +1644,12 @@ TkBTreeFindPixelLine(tree, pixels, pixelOffset)
while (nodePtr->level != 0) {
for (nodePtr = nodePtr->children.nodePtr;
- nodePtr->numPixels <= pixelsLeft;
+ nodePtr->numPixels[pixelReference] <= pixels;
nodePtr = nodePtr->nextPtr) {
if (nodePtr == NULL) {
Tcl_Panic("TkBTreeFindPixelLine ran out of nodes");
}
- pixelsLeft -= nodePtr->numPixels;
+ pixels -= nodePtr->numPixels[pixelReference];
}
}
@@ -1027,15 +1658,15 @@ TkBTreeFindPixelLine(tree, pixels, pixelOffset)
*/
for (linePtr = nodePtr->children.linePtr;
- linePtr->pixelHeight < pixelsLeft;
+ linePtr->pixels[2*pixelReference] < pixels;
linePtr = linePtr->nextPtr) {
if (linePtr == NULL) {
Tcl_Panic("TkBTreeFindPixelLine ran out of lines");
}
- pixelsLeft -= linePtr->pixelHeight;
+ pixels -= linePtr->pixels[2*pixelReference];
}
if (pixelOffset != NULL && linePtr != NULL) {
- *pixelOffset = pixelsLeft;
+ *pixelOffset = pixels;
}
return linePtr;
}
@@ -1060,16 +1691,22 @@ TkBTreeFindPixelLine(tree, pixels, pixelOffset)
*/
TkTextLine *
-TkBTreeNextLine(linePtr)
+TkBTreeNextLine(textPtr, linePtr)
+ CONST TkText *textPtr; /* Next line in the context of
+ * this client */
register TkTextLine *linePtr; /* Pointer to existing line in
* B-tree. */
{
register Node *nodePtr;
if (linePtr->nextPtr != NULL) {
- return linePtr->nextPtr;
+ if (textPtr != NULL && (linePtr == textPtr->end)) {
+ return NULL;
+ } else {
+ return linePtr->nextPtr;
+ }
}
-
+
/*
* This was the last line associated with the particular parent node.
* Search up the tree for the next node, then search down from that
@@ -1111,7 +1748,9 @@ TkBTreeNextLine(linePtr)
*/
TkTextLine *
-TkBTreePreviousLine(linePtr)
+TkBTreePreviousLine(textPtr, linePtr)
+ TkText *textPtr; /* Relative to this client of the
+ * B-tree */
register TkTextLine *linePtr; /* Pointer to existing line in
* B-tree. */
{
@@ -1119,6 +1758,10 @@ TkBTreePreviousLine(linePtr)
register Node *node2Ptr;
register TkTextLine *prevPtr;
+ if (textPtr != NULL && textPtr->start == linePtr) {
+ return NULL;
+ }
+
/*
* Find the line under this node just before the starting line.
*/
@@ -1166,7 +1809,7 @@ TkBTreePreviousLine(linePtr)
/*
*----------------------------------------------------------------------
*
- * TkBTreePixels --
+ * TkBTreePixelsTo --
*
* Given a pointer to a line in a B-tree, return the numerical
* pixel index of the top of that line (i.e. the result does
@@ -1187,16 +1830,19 @@ TkBTreePreviousLine(linePtr)
*/
int
-TkBTreePixels(linePtr)
+TkBTreePixelsTo(textPtr, linePtr)
+ CONST TkText *textPtr; /* Relative to this client of the
+ * B-tree */
TkTextLine *linePtr; /* Pointer to existing line in
* B-tree. */
{
register TkTextLine *linePtr2;
- register Node *nodePtr, *parentPtr, *nodePtr2;
+ register Node *nodePtr, *parentPtr;
int index;
+ int pixelReference = textPtr->pixelReference;
/*
- * First count how many lines precede this one in its level-0
+ * First count how many pixels precede this line in its level-0
* node.
*/
@@ -1205,25 +1851,26 @@ TkBTreePixels(linePtr)
for (linePtr2 = nodePtr->children.linePtr; linePtr2 != linePtr;
linePtr2 = linePtr2->nextPtr) {
if (linePtr2 == NULL) {
- Tcl_Panic("TkBTreePixels couldn't find line");
+ Tcl_Panic("TkBTreePixelsTo couldn't find line");
}
- index += linePtr2->pixelHeight;
+ index += linePtr2->pixels[2*pixelReference];
}
/*
* Now work up through the levels of the tree one at a time,
- * counting how many lines are in nodes preceding the current
+ * counting how many pixels are in nodes preceding the current
* node.
*/
for (parentPtr = nodePtr->parentPtr ; parentPtr != NULL;
nodePtr = parentPtr, parentPtr = parentPtr->parentPtr) {
+ register Node *nodePtr2;
for (nodePtr2 = parentPtr->children.nodePtr; nodePtr2 != nodePtr;
nodePtr2 = nodePtr2->nextPtr) {
if (nodePtr2 == NULL) {
- Tcl_Panic("TkBTreePixels couldn't find node");
+ Tcl_Panic("TkBTreePixelsTo couldn't find node");
}
- index += nodePtr2->numPixels;
+ index += nodePtr2->numPixels[pixelReference];
}
}
return index;
@@ -1232,7 +1879,7 @@ TkBTreePixels(linePtr)
/*
*----------------------------------------------------------------------
*
- * TkBTreeLineIndex --
+ * TkBTreeLinesTo --
*
* Given a pointer to a line in a B-tree, return the numerical
* index of that line.
@@ -1248,7 +1895,9 @@ TkBTreePixels(linePtr)
*/
int
-TkBTreeLineIndex(linePtr)
+TkBTreeLinesTo(textPtr, linePtr)
+ CONST TkText *textPtr; /* Relative to this client of the
+ * B-tree */
TkTextLine *linePtr; /* Pointer to existing line in
* B-tree. */
{
@@ -1266,7 +1915,7 @@ TkBTreeLineIndex(linePtr)
for (linePtr2 = nodePtr->children.linePtr; linePtr2 != linePtr;
linePtr2 = linePtr2->nextPtr) {
if (linePtr2 == NULL) {
- Tcl_Panic("TkBTreeLineIndex couldn't find line");
+ Tcl_Panic("TkBTreeLinesTo couldn't find line");
}
index += 1;
}
@@ -1282,11 +1931,14 @@ TkBTreeLineIndex(linePtr)
for (nodePtr2 = parentPtr->children.nodePtr; nodePtr2 != nodePtr;
nodePtr2 = nodePtr2->nextPtr) {
if (nodePtr2 == NULL) {
- Tcl_Panic("TkBTreeLineIndex couldn't find node");
+ Tcl_Panic("TkBTreeLinesTo couldn't find node");
}
index += nodePtr2->numLines;
}
}
+ if (textPtr != NULL && textPtr->start != NULL) {
+ index -= TkBTreeLinesTo(NULL, textPtr->start);
+ }
return index;
}
@@ -1352,8 +2004,7 @@ TkBTreeLinkSegment(segPtr, indexPtr)
/* ARGSUSED */
void
-TkBTreeUnlinkSegment(tree, segPtr, linePtr)
- TkTextBTree tree; /* Tree containing segment. */
+TkBTreeUnlinkSegment(segPtr, linePtr)
TkTextSegment *segPtr; /* Segment to be unlinked. */
TkTextLine *linePtr; /* Line that currently contains
* segment. */
@@ -1951,8 +2602,8 @@ TkBTreeStartSearch(index1Ptr, index2Ptr, tagPtr, searchPtr)
}
searchPtr->lastPtr = TkTextIndexToSeg(index2Ptr, (int *) NULL);
searchPtr->tagPtr = tagPtr;
- searchPtr->linesLeft = TkBTreeLineIndex(index2Ptr->linePtr) + 1
- - TkBTreeLineIndex(index1Ptr->linePtr);
+ searchPtr->linesLeft = TkBTreeLinesTo(NULL, index2Ptr->linePtr) + 1
+ - TkBTreeLinesTo(NULL, index1Ptr->linePtr);
searchPtr->allTags = (tagPtr == NULL);
if (searchPtr->linesLeft == 1) {
/*
@@ -2042,7 +2693,7 @@ TkBTreeStartSearchBack(index1Ptr, index2Ptr, tagPtr, searchPtr)
searchPtr->curIndex = index0;
index1Ptr = &index0;
} else {
- TkTextIndexBackChars(NULL,index1Ptr, 1, &searchPtr->curIndex,
+ TkTextIndexBackChars(NULL, index1Ptr, 1, &searchPtr->curIndex,
COUNT_INDICES);
}
searchPtr->segPtr = NULL;
@@ -2054,7 +2705,7 @@ TkBTreeStartSearchBack(index1Ptr, index2Ptr, tagPtr, searchPtr)
* at the second index specified by the user.
*/
- if ((TkBTreeLineIndex(index2Ptr->linePtr) == 0) &&
+ if ((TkBTreeLinesTo(NULL, index2Ptr->linePtr) == 0) &&
(index2Ptr->byteIndex == 0)) {
backOne = *index2Ptr;
searchPtr->lastPtr = NULL; /* Signals special case for 1.0 */
@@ -2063,8 +2714,8 @@ TkBTreeStartSearchBack(index1Ptr, index2Ptr, tagPtr, searchPtr)
searchPtr->lastPtr = TkTextIndexToSeg(&backOne, (int *) NULL);
}
searchPtr->tagPtr = tagPtr;
- searchPtr->linesLeft = TkBTreeLineIndex(index1Ptr->linePtr) + 1
- - TkBTreeLineIndex(backOne.linePtr);
+ searchPtr->linesLeft = TkBTreeLinesTo(NULL, index1Ptr->linePtr) + 1
+ - TkBTreeLinesTo(NULL, backOne.linePtr);
searchPtr->allTags = (tagPtr == NULL);
if (searchPtr->linesLeft == 1) {
/*
@@ -2588,9 +3239,12 @@ TkBTreeCharTagged(indexPtr, tagPtr)
/* ARGSUSED */
TkTextTag **
-TkBTreeGetTags(indexPtr, numTagsPtr)
+TkBTreeGetTags(indexPtr, textPtr, numTagsPtr)
CONST TkTextIndex *indexPtr;/* Indicates a particular position in
* the B-tree. */
+ CONST TkText *textPtr; /* If non-NULL, then only return tags
+ * for this text widget (when there are
+ * peer widgets). */
int *numTagsPtr; /* Store number of tags found at this
* location. */
{
@@ -2664,13 +3318,17 @@ TkBTreeGetTags(indexPtr, numTagsPtr)
/*
* Go through the tag information and squash out all of the tags
* that have even toggle counts (these tags exist before the point
- * of interest, but not at the desired character itself).
+ * of interest, but not at the desired character itself). Also
+ * squash out all tags that don't belong to the requested widget.
*/
for (src = 0, dst = 0; src < tagInfo.numTags; src++) {
if (tagInfo.counts[src] & 1) {
- tagInfo.tagPtrs[dst] = tagInfo.tagPtrs[src];
- dst++;
+ CONST TkText *tagTextPtr = tagInfo.tagPtrs[src]->textPtr;
+ if (tagTextPtr == NULL || textPtr == NULL || tagTextPtr == textPtr) {
+ tagInfo.tagPtrs[dst] = tagInfo.tagPtrs[src];
+ dst++;
+ }
}
}
*numTagsPtr = dst;
@@ -2688,17 +3346,21 @@ TkBTreeGetTags(indexPtr, numTagsPtr)
* TkTextIsElided --
*
* Special case to just return information about elided attribute.
- * Specialized from TkBTreeGetTags(indexPtr, numTagsPtr) and
+ * Specialized from TkBTreeGetTags(indexPtr, textPtr, numTagsPtr) and
* GetStyle(textPtr, indexPtr). Just need to keep track of
* invisibility settings for each priority, pick highest one active
* at end.
*
* Note that this returns all elide information up to and including
* the given index (quite obviously). However, this does mean that
- * indexPtr is a line-start and one then iterates from the beginning
+ * if indexPtr is a line-start and one then iterates from the beginning
* of that line forwards, one will actually revisit the segPtrs of
* size zero (for tag toggling, for example) which have already been
* seen here.
+ *
+ * For this reason we fill in the fields 'segPtr' and 'segOffset'
+ * of elideInfo, enabling our caller easily to calculate
+ * incremental changes from where we left off.
*
* Results:
* Returns whether this text should be elided or not.
@@ -2738,7 +3400,7 @@ TkTextIsElided(textPtr, indexPtr, elideInfo)
infoPtr->elide = 0; /* if nobody says otherwise, it's visible */
infoPtr->tagCnts = infoPtr->deftagCnts;
infoPtr->tagPtrs = infoPtr->deftagPtrs;
- infoPtr->numTags = textPtr->numTags;
+ infoPtr->numTags = textPtr->sharedTextPtr->numTags;
/* Almost always avoid malloc, so stay out of system calls */
if (LOTSA_TAGS < infoPtr->numTags) {
@@ -2774,7 +3436,8 @@ TkTextIsElided(textPtr, indexPtr, elideInfo)
* so that our caller knows where to start.
*/
infoPtr->segPtr = segPtr;
-
+ infoPtr->segOffset = index;
+
/*
* Record toggles for tags in lines that are predecessors of
* indexPtr->linePtr but under the same level-0 node.
@@ -2829,13 +3492,12 @@ TkTextIsElided(textPtr, indexPtr, elideInfo)
infoPtr->elidePriority = -1;
for (i = infoPtr->numTags-1; i >=0; i--) {
if (infoPtr->tagCnts[i] & 1) {
-#ifndef ALWAYS_SHOW_SELECTION
- /* who would make the selection elided? */
+ /* Who would make the selection elided? */
if ((tagPtr == textPtr->selTagPtr)
- && !(textPtr->flags & GOT_FOCUS)) {
+ && !(textPtr->flags & GOT_FOCUS)
+ && (textPtr->inactiveSelBorder == NULL)) {
continue;
}
-#endif
infoPtr->elide = infoPtr->tagPtrs[i]->elide;
/* Note: i == infoPtr->tagPtrs[i]->priority */
infoPtr->elidePriority = i;
@@ -2987,7 +3649,7 @@ TkBTreeCheck(tree)
/*
* Make sure that the tag toggle counts and the tag root pointers are OK.
*/
- for (entryPtr = Tcl_FirstHashEntry(&treePtr->textPtr->tagTable, &search);
+ for (entryPtr = Tcl_FirstHashEntry(&treePtr->sharedTextPtr->tagTable, &search);
entryPtr != NULL ; entryPtr = Tcl_NextHashEntry(&search)) {
tagPtr = (TkTextTag *) Tcl_GetHashValue(entryPtr);
nodePtr = tagPtr->tagRootPtr;
@@ -3045,7 +3707,7 @@ TkBTreeCheck(tree)
*/
nodePtr = treePtr->rootPtr;
- CheckNodeConsistency(treePtr->rootPtr);
+ CheckNodeConsistency(treePtr->rootPtr, treePtr->pixelReferences);
/*
* Make sure that there are at least two lines in the text and
@@ -3113,15 +3775,19 @@ TkBTreeCheck(tree)
*/
static void
-CheckNodeConsistency(nodePtr)
+CheckNodeConsistency(nodePtr, references)
register Node *nodePtr; /* Node whose subtree should be
* checked. */
+ int references; /* Number of referring widgets
+ * which have pixel counts. */
{
register Node *childNodePtr;
register Summary *summaryPtr, *summaryPtr2;
register TkTextLine *linePtr;
register TkTextSegment *segPtr;
- int numChildren, numLines, numPixels, toggleCount, minChildren;
+ int numChildren, numLines, toggleCount, minChildren, i;
+ int *numPixels;
+ int pixels[PIXEL_CLIENTS];
if (nodePtr->parentPtr != NULL) {
minChildren = MIN_CHILDREN;
@@ -3138,7 +3804,15 @@ CheckNodeConsistency(nodePtr)
numChildren = 0;
numLines = 0;
- numPixels = 0;
+ if (references > PIXEL_CLIENTS) {
+ numPixels = (int*)ckalloc(sizeof(int)*references);
+ } else {
+ numPixels = pixels;
+ }
+ for (i = 0; i<references; i++) {
+ numPixels[i] = 0;
+ }
+
if (nodePtr->level == 0) {
for (linePtr = nodePtr->children.linePtr; linePtr != NULL;
linePtr = linePtr->nextPtr) {
@@ -3166,7 +3840,9 @@ CheckNodeConsistency(nodePtr)
}
numChildren++;
numLines++;
- numPixels += linePtr->pixelHeight;
+ for (i = 0; i<references; i++) {
+ numPixels[i] += linePtr->pixels[2*i];
+ }
}
} else {
for (childNodePtr = nodePtr->children.nodePtr; childNodePtr != NULL;
@@ -3178,7 +3854,7 @@ CheckNodeConsistency(nodePtr)
Tcl_Panic("CheckNodeConsistency: level mismatch (%d %d)",
nodePtr->level, childNodePtr->level);
}
- CheckNodeConsistency(childNodePtr);
+ CheckNodeConsistency(childNodePtr, references);
for (summaryPtr = childNodePtr->summaryPtr; summaryPtr != NULL;
summaryPtr = summaryPtr->nextPtr) {
for (summaryPtr2 = nodePtr->summaryPtr; ;
@@ -3198,7 +3874,9 @@ CheckNodeConsistency(nodePtr)
}
numChildren++;
numLines += childNodePtr->numLines;
- numPixels += childNodePtr->numPixels;
+ for (i = 0; i<references; i++) {
+ numPixels[i] += childNodePtr->numPixels[i];
+ }
}
}
if (numChildren != nodePtr->numChildren) {
@@ -3209,11 +3887,16 @@ CheckNodeConsistency(nodePtr)
Tcl_Panic("CheckNodeConsistency: mismatch in numLines (%d %d)",
numLines, nodePtr->numLines);
}
- if (numPixels != nodePtr->numPixels) {
- Tcl_Panic("CheckNodeConsistency: mismatch in numPixels (%d %d)",
- numPixels, nodePtr->numPixels);
+ for (i = 0; i<references; i++) {
+ if (numPixels[i] != nodePtr->numPixels[i]) {
+ Tcl_Panic("CheckNodeConsistency: mismatch in numPixels (%d %d) for widget (%d)",
+ numPixels[i], nodePtr->numPixels[i], i);
+ }
}
-
+ if (references > PIXEL_CLIENTS) {
+ ckfree((char*)numPixels);
+ }
+
for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
summaryPtr = summaryPtr->nextPtr) {
if (summaryPtr->tagPtr->toggleCount == summaryPtr->toggleCount) {
@@ -3319,11 +4002,20 @@ Rebalance(treePtr, nodePtr)
newPtr->children.nodePtr = nodePtr;
newPtr->numChildren = 1;
newPtr->numLines = nodePtr->numLines;
- newPtr->numPixels = nodePtr->numPixels;
- RecomputeNodeCounts(newPtr);
+ newPtr->numPixels = (int*) ckalloc(sizeof(int)*
+ treePtr->pixelReferences);
+ for (i=0; i<treePtr->pixelReferences; i++) {
+ newPtr->numPixels[i] = nodePtr->numPixels[i];
+ }
+ RecomputeNodeCounts(treePtr, newPtr);
treePtr->rootPtr = newPtr;
}
newPtr = (Node *) ckalloc(sizeof(Node));
+ newPtr->numPixels = (int*) ckalloc(sizeof(int)*
+ treePtr->pixelReferences);
+ for (i=0; i<treePtr->pixelReferences; i++) {
+ newPtr->numPixels[i] = 0;
+ }
newPtr->parentPtr = nodePtr->parentPtr;
newPtr->nextPtr = nodePtr->nextPtr;
nodePtr->nextPtr = newPtr;
@@ -3347,11 +4039,11 @@ Rebalance(treePtr, nodePtr)
newPtr->children.nodePtr = childPtr->nextPtr;
childPtr->nextPtr = NULL;
}
- RecomputeNodeCounts(nodePtr);
+ RecomputeNodeCounts(treePtr, nodePtr);
nodePtr->parentPtr->numChildren++;
nodePtr = newPtr;
if (nodePtr->numChildren <= MAX_CHILDREN) {
- RecomputeNodeCounts(nodePtr);
+ RecomputeNodeCounts(treePtr, nodePtr);
break;
}
}
@@ -3462,7 +4154,7 @@ Rebalance(treePtr, nodePtr)
*/
if (totalChildren <= MAX_CHILDREN) {
- RecomputeNodeCounts(nodePtr);
+ RecomputeNodeCounts(treePtr, nodePtr);
nodePtr->nextPtr = otherPtr->nextPtr;
nodePtr->parentPtr->numChildren--;
DeleteSummaries(otherPtr->summaryPtr);
@@ -3482,8 +4174,8 @@ Rebalance(treePtr, nodePtr)
otherPtr->children.nodePtr = halfwayNodePtr->nextPtr;
halfwayNodePtr->nextPtr = NULL;
}
- RecomputeNodeCounts(nodePtr);
- RecomputeNodeCounts(otherPtr);
+ RecomputeNodeCounts(treePtr, nodePtr);
+ RecomputeNodeCounts(treePtr, otherPtr);
}
}
}
@@ -3511,7 +4203,8 @@ Rebalance(treePtr, nodePtr)
*/
static void
-RecomputeNodeCounts(nodePtr)
+RecomputeNodeCounts(treePtr, nodePtr)
+ register BTree *treePtr; /* The whole B-tree */
register Node *nodePtr; /* Node whose tag summary information
* must be recomputed. */
{
@@ -3520,7 +4213,8 @@ RecomputeNodeCounts(nodePtr)
register TkTextLine *linePtr;
register TkTextSegment *segPtr;
TkTextTag *tagPtr;
-
+ int ref;
+
/*
* Zero out all the existing counts for the node, but don't delete
* the existing Summary records (most of them will probably be reused).
@@ -3532,7 +4226,9 @@ RecomputeNodeCounts(nodePtr)
}
nodePtr->numChildren = 0;
nodePtr->numLines = 0;
- nodePtr->numPixels = 0;
+ for (ref = 0; ref<treePtr->pixelReferences; ref++) {
+ nodePtr->numPixels[ref] = 0;
+ }
/*
* Scan through the children, adding the childrens' tag counts into
@@ -3545,7 +4241,9 @@ RecomputeNodeCounts(nodePtr)
linePtr = linePtr->nextPtr) {
nodePtr->numChildren++;
nodePtr->numLines++;
- nodePtr->numPixels += linePtr->pixelHeight;
+ for (ref = 0; ref<treePtr->pixelReferences; ref++) {
+ nodePtr->numPixels[ref] += linePtr->pixels[2*ref];
+ }
linePtr->parentPtr = nodePtr;
for (segPtr = linePtr->segPtr; segPtr != NULL;
segPtr = segPtr->nextPtr) {
@@ -3577,7 +4275,9 @@ RecomputeNodeCounts(nodePtr)
childPtr = childPtr->nextPtr) {
nodePtr->numChildren++;
nodePtr->numLines += childPtr->numLines;
- nodePtr->numPixels += childPtr->numPixels;
+ for (ref = 0; ref<treePtr->pixelReferences; ref++) {
+ nodePtr->numPixels[ref] += childPtr->numPixels[ref];
+ }
childPtr->parentPtr = nodePtr;
for (summaryPtr2 = childPtr->summaryPtr; summaryPtr2 != NULL;
summaryPtr2 = summaryPtr2->nextPtr) {
@@ -3646,7 +4346,7 @@ RecomputeNodeCounts(nodePtr)
*
* TkBTreeNumLines --
*
- * This procedure returns a count of the number of lines of
+ * This procedure returns a count of the number of logical lines of
* text present in a given B-tree.
*
* Results:
@@ -3661,11 +4361,24 @@ RecomputeNodeCounts(nodePtr)
*/
int
-TkBTreeNumLines(tree)
+TkBTreeNumLines(tree, textPtr)
TkTextBTree tree; /* Information about tree. */
+ CONST TkText *textPtr; /* Relative to this client of the
+ * B-tree */
{
BTree *treePtr = (BTree *) tree;
- return treePtr->rootPtr->numLines - 1;
+ int count;
+
+ if (textPtr != NULL && textPtr->end != NULL) {
+ count = TkBTreeLinesTo(NULL, textPtr->end);
+ } else {
+ count = treePtr->rootPtr->numLines - 1;
+ }
+ if (textPtr != NULL && textPtr->start != NULL) {
+ count -= TkBTreeLinesTo(NULL, textPtr->start);
+ }
+
+ return count;
}
/*
@@ -3674,13 +4387,14 @@ TkBTreeNumLines(tree)
* TkBTreeNumPixels --
*
* This procedure returns a count of the number of pixels of
- * text present in a given B-tree.
+ * text present in a given widget's B-tree representation.
*
* Results:
* The return value is a count of the number of usable pixels in
- * tree (since the dummy line used to mark the end of the tree is
- * maintained with zero height, it doesn't feature in this
- * calculation).
+ * tree (since the dummy line used to mark the end of the B-tree is
+ * maintained with zero height, as are any lines that are before or
+ * after the '-start -end' range of the text widget in question,
+ * the number stored at the root is the number we want).
*
* Side effects:
* None.
@@ -3689,11 +4403,13 @@ TkBTreeNumLines(tree)
*/
int
-TkBTreeNumPixels(tree)
- TkTextBTree tree; /* Information about tree. */
+TkBTreeNumPixels(tree, textPtr)
+ TkTextBTree tree; /* The B-tree */
+ CONST TkText *textPtr; /* Relative to this client of the
+ * B-tree */
{
BTree *treePtr = (BTree *) tree;
- return treePtr->rootPtr->numPixels;
+ return treePtr->rootPtr->numPixels[textPtr->pixelReference];
}
/*
@@ -4056,54 +4772,3 @@ ToggleCheckProc(segPtr, linePtr)
}
}
}
-
-/*
- *----------------------------------------------------------------------
- *
- * TkBTreeCharsInLine --
- *
- * This procedure returns a count of the number of characters
- * in a given line.
- *
- * Results:
- * The return value is the character count for linePtr.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
-int
-TkBTreeCharsInLine(linePtr)
- TkTextLine *linePtr; /* Line whose characters should be
- * counted. */
-{
- TkTextSegment *segPtr;
- int count;
-
- count = 0;
- for (segPtr = linePtr->segPtr; segPtr != NULL; segPtr = segPtr->nextPtr) {
- if (segPtr->typePtr == &tkTextCharType) {
- count += Tcl_NumUtfChars(segPtr->body.chars, segPtr->size);
- } else {
- count += segPtr->size;
- }
- }
- return count;
-}
-
-int
-TkBTreeBytesInLine(linePtr)
- TkTextLine *linePtr; /* Line whose characters should be
- * counted. */
-{
- TkTextSegment *segPtr;
- int count;
-
- count = 0;
- for (segPtr = linePtr->segPtr; segPtr != NULL; segPtr = segPtr->nextPtr) {
- count += segPtr->size;
- }
- return count;
-}
diff --git a/generic/tkTextDisp.c b/generic/tkTextDisp.c
index ef977bb..63b8da6 100644
--- a/generic/tkTextDisp.c
+++ b/generic/tkTextDisp.c
@@ -3,9 +3,9 @@
*
* This module provides facilities to display text widgets. It is
* the only place where information is kept about the screen layout
- * of text widgets. (Well, strictly, each TkTextLine caches its
- * last observed pixel height, but that information is
- * calculated here).
+ * of text widgets. (Well, strictly, each TkTextLine and B-tree
+ * node caches its last observed pixel height, but that information
+ * originates here).
*
* Copyright (c) 1992-1994 The Regents of the University of California.
* Copyright (c) 1994-1997 Sun Microsystems, Inc.
@@ -13,7 +13,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tkTextDisp.c,v 1.43 2004/06/08 20:28:19 dgp Exp $
+ * RCS: @(#) $Id: tkTextDisp.c,v 1.44 2004/09/10 12:13:41 vincentdarley Exp $
*/
#include "tkPort.h"
@@ -425,11 +425,13 @@ static int lineHeightsRecalculated; /* Number of line layouts purely
static void AdjustForTab _ANSI_ARGS_((TkText *textPtr,
TkTextTabArray *tabArrayPtr, int index,
TkTextDispChunk *chunkPtr));
-static void CharBboxProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
+static void CharBboxProc _ANSI_ARGS_((TkText *textPtr,
+ TkTextDispChunk *chunkPtr,
int index, int y, int lineHeight, int baseline,
int *xPtr, int *yPtr, int *widthPtr,
int *heightPtr));
-static void CharDisplayProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
+static void CharDisplayProc _ANSI_ARGS_((TkText *textPtr,
+ TkTextDispChunk *chunkPtr,
int x, int y, int height, int baseline,
Display *display, Drawable dst, int screenY));
static int CharMeasureProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
@@ -444,7 +446,8 @@ static void CharUndisplayProc _ANSI_ARGS_((TkText *textPtr,
as potentially many elided DLine chunks if large, tag toggle-filled
elided region.
*/
-static void ElideBboxProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
+static void ElideBboxProc _ANSI_ARGS_((TkText *textPtr,
+ TkTextDispChunk *chunkPtr,
int index, int y, int lineHeight, int baseline,
int *xPtr, int *yPtr, int *widthPtr,
int *heightPtr));
@@ -488,8 +491,16 @@ static void YScrollByPixels _ANSI_ARGS_((TkText *textPtr,
static int SizeOfTab _ANSI_ARGS_((TkText *textPtr,
TkTextTabArray *tabArrayPtr, int *indexPtr,
int x, int maxX));
+static void TextChanged _ANSI_ARGS_((TkText *textPtr,
+ CONST TkTextIndex *index1Ptr,
+ CONST TkTextIndex *index2Ptr));
static void TextInvalidateRegion _ANSI_ARGS_((TkText *textPtr,
TkRegion region));
+static void TextRedrawTag _ANSI_ARGS_((TkText *textPtr,
+ TkTextIndex *index1Ptr, TkTextIndex *index2Ptr,
+ TkTextTag *tagPtr, int withTag));
+static void TextInvalidateLineMetrics _ANSI_ARGS_((TkText *textPtr,
+ TkTextLine *linePtr, int lineCount, int action));
static int CalculateDisplayLineHeight _ANSI_ARGS_((
TkText *textPtr, CONST TkTextIndex *indexPtr,
int *byteCountPtr));
@@ -507,7 +518,7 @@ static void AsyncUpdateYScrollbar _ANSI_ARGS_((ClientData
clientData));
/*
- * Result values returned by TextGetScrollInfo:
+ * Result values returned by TextGetScrollInfoObj:
*/
#define TKTEXT_SCROLL_MOVETO 1
@@ -687,7 +698,7 @@ GetStyle(textPtr, indexPtr)
* priority tag).
*/
- tagPtrs = TkBTreeGetTags(indexPtr, &numTags);
+ tagPtrs = TkBTreeGetTags(indexPtr, textPtr, &numTags);
borderPrio = borderWidthPrio = reliefPrio = bgStipplePrio = -1;
fgPrio = fontPrio = fgStipplePrio = -1;
underlinePrio = elidePrio = justifyPrio = offsetPrio = -1;
@@ -706,21 +717,25 @@ GetStyle(textPtr, indexPtr)
styleValues.wrapMode = textPtr->wrapMode;
styleValues.elide = 0;
for (i = 0 ; i < numTags; i++) {
+ Tk_3DBorder border;
tagPtr = tagPtrs[i];
-
+ border = tagPtr->border;
+
/*
- * On Windows and Mac, we need to skip the selection tag if
+ * If this is the selection tag, and inactiveSelBorder is NULL
+ * (the default on Windows and Mac), then we need to skip it if
* we don't have focus.
*/
-#ifndef ALWAYS_SHOW_SELECTION
if ((tagPtr == textPtr->selTagPtr) && !(textPtr->flags & GOT_FOCUS)) {
- continue;
+ if (textPtr->inactiveSelBorder == NULL) {
+ continue;
+ }
+ border = textPtr->inactiveSelBorder;
}
-#endif
- if ((tagPtr->border != NULL) && (tagPtr->priority > borderPrio)) {
- styleValues.border = tagPtr->border;
+ if ((border != NULL) && (tagPtr->priority > borderPrio)) {
+ styleValues.border = border;
borderPrio = tagPtr->priority;
}
if ((tagPtr->borderWidthPtr != NULL)
@@ -1090,11 +1105,12 @@ LayoutDLine(textPtr, indexPtr)
* update the line's pixel height, and bring
* its pixel calculation up to date.
*/
- dlPtr->index.linePtr->pixelCalculationEpoch
+ TkBTreeLinePixelEpoch(textPtr, dlPtr->index.linePtr)
= textPtr->dInfoPtr->lineMetricUpdateEpoch;
- if (dlPtr->index.linePtr->pixelHeight != 0) {
- TkBTreeAdjustPixelHeight(dlPtr->index.linePtr, 0);
+ if (TkBTreeLinePixelCount(textPtr, dlPtr->index.linePtr) != 0) {
+ TkBTreeAdjustPixelHeight(textPtr,
+ dlPtr->index.linePtr, 0);
}
}
TkTextFreeElideInfo(&info);
@@ -1532,8 +1548,8 @@ UpdateDisplayInfo(textPtr)
*--------------------------------------------------------------
*/
- lastLinePtr = TkBTreeFindLine(textPtr->tree,
- TkBTreeNumLines(textPtr->tree));
+ lastLinePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree, textPtr,
+ TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr));
dlPtr = dInfoPtr->dLinePtr;
prevPtr = NULL;
y = dInfoPtr->y - dInfoPtr->newTopPixelOffset;
@@ -1583,7 +1599,7 @@ UpdateDisplayInfo(textPtr)
* information.
*/
- TkTextPrintIndex(&index, string);
+ TkTextPrintIndex(textPtr, &index, string);
Tcl_SetVar2(textPtr->interp, "tk_textRelayout", (char *) NULL,
string,
TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
@@ -1646,7 +1662,7 @@ UpdateDisplayInfo(textPtr)
if (lineHeight != -1) {
lineHeight += dlPtr->height;
}
- TkTextIndexForwBytes(&index, dlPtr->byteCount, &index);
+ TkTextIndexForwBytes(textPtr, &index, dlPtr->byteCount, &index);
prevPtr = dlPtr;
dlPtr = dlPtr->nextPtr;
@@ -1670,7 +1686,8 @@ UpdateDisplayInfo(textPtr)
}
if ((lineHeight != -1)
- && (lineHeight > prevPtr->index.linePtr->pixelHeight)) {
+ && (lineHeight > TkBTreeLinePixelCount(textPtr,
+ prevPtr->index.linePtr))) {
/*
* The logical line height we just calculated is actually
* larger than the currently cached height of the
@@ -1680,7 +1697,9 @@ UpdateDisplayInfo(textPtr)
* with DLine pointers do not exceed counts made
* through the BTree.
*/
- TkBTreeAdjustPixelHeight(prevPtr->index.linePtr, lineHeight);
+ TkBTreeAdjustPixelHeight(textPtr,
+ prevPtr->index.linePtr,
+ lineHeight);
/*
* I believe we can be 100% sure that we started at the
* beginning of the logical line, so we can also adjust
@@ -1689,7 +1708,7 @@ UpdateDisplayInfo(textPtr)
* have got this right for the first line in the
* re-display.
*/
- prevPtr->index.linePtr->pixelCalculationEpoch =
+ TkBTreeLinePixelEpoch(textPtr, prevPtr->index.linePtr) =
dInfoPtr->lineMetricUpdateEpoch;
}
lineHeight = 0;
@@ -1757,16 +1776,25 @@ UpdateDisplayInfo(textPtr)
*/
spaceLeft = maxY - y;
- lineNum = TkBTreeLineIndex(dInfoPtr->dLinePtr->index.linePtr);
- bytesToCount = dInfoPtr->dLinePtr->index.byteIndex;
- if (bytesToCount == 0) {
- bytesToCount = INT_MAX;
- lineNum--;
+ if (dInfoPtr->dLinePtr == NULL) {
+ /*
+ * No lines have been laid out. This must be an empty
+ * peer widget.
+ */
+ lineNum = -1;
+ } else {
+ lineNum = TkBTreeLinesTo(textPtr, dInfoPtr->dLinePtr->index.linePtr);
+ bytesToCount = dInfoPtr->dLinePtr->index.byteIndex;
+ if (bytesToCount == 0) {
+ bytesToCount = INT_MAX;
+ lineNum--;
+ }
}
for ( ; (lineNum >= 0) && (spaceLeft > 0); lineNum--) {
int pixelHeight = 0;
- index.linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
+ index.linePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree,
+ textPtr, lineNum);
index.byteIndex = 0;
lowestPtr = NULL;
@@ -1778,7 +1806,7 @@ UpdateDisplayInfo(textPtr)
if (dlPtr->length == 0 && dlPtr->height == 0) {
bytesToCount--; break;
} /* elide */
- TkTextIndexForwBytes(&index, dlPtr->byteCount, &index);
+ TkTextIndexForwBytes(textPtr, &index, dlPtr->byteCount, &index);
bytesToCount -= dlPtr->byteCount;
} while ((bytesToCount > 0)
&& (index.linePtr == lowestPtr->index.linePtr));
@@ -1788,15 +1816,17 @@ UpdateDisplayInfo(textPtr)
* on the value of 'bytesToCount', so we only want
* to set this if it is genuinely bigger).
*/
- if (pixelHeight > lowestPtr->index.linePtr->pixelHeight) {
- TkBTreeAdjustPixelHeight(lowestPtr->index.linePtr,
- pixelHeight);
+ if (pixelHeight > TkBTreeLinePixelCount(textPtr,
+ lowestPtr->index.linePtr)) {
+ TkBTreeAdjustPixelHeight(textPtr,
+ lowestPtr->index.linePtr,
+ pixelHeight);
if (index.linePtr != lowestPtr->index.linePtr) {
/*
* We examined the entire line, so can update
* the epoch.
*/
- lowestPtr->index.linePtr->pixelCalculationEpoch =
+ TkBTreeLinePixelEpoch(textPtr, lowestPtr->index.linePtr) =
dInfoPtr->lineMetricUpdateEpoch;
}
}
@@ -1815,7 +1845,7 @@ UpdateDisplayInfo(textPtr)
if (tkTextDebug) {
char string[TK_POS_CHARS];
- TkTextPrintIndex(&dlPtr->index, string);
+ TkTextPrintIndex(textPtr, &dlPtr->index, string);
Tcl_SetVar2(textPtr->interp, "tk_textRelayout",
(char *) NULL, string,
TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
@@ -1835,7 +1865,7 @@ UpdateDisplayInfo(textPtr)
if (lineNum >= 0) {
dInfoPtr->newTopPixelOffset = -spaceLeft;
if (spaceLeft > 0 ||
- dInfoPtr->newTopPixelOffset >= dInfoPtr->dLinePtr->height) {
+ dInfoPtr->newTopPixelOffset >= dInfoPtr->dLinePtr->height) {
/* Bad situation */
Tcl_Panic("Pixel height problem while laying out text widget");
}
@@ -1848,15 +1878,17 @@ UpdateDisplayInfo(textPtr)
* Update them.
*/
- textPtr->topIndex = dInfoPtr->dLinePtr->index;
- y = dInfoPtr->y - dInfoPtr->newTopPixelOffset;
- for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
- dlPtr = dlPtr->nextPtr) {
- if (y > dInfoPtr->maxY) {
- Tcl_Panic("Added too many new lines in UpdateDisplayInfo");
+ if (dInfoPtr->dLinePtr != NULL) {
+ textPtr->topIndex = dInfoPtr->dLinePtr->index;
+ y = dInfoPtr->y - dInfoPtr->newTopPixelOffset;
+ for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
+ dlPtr = dlPtr->nextPtr) {
+ if (y > dInfoPtr->maxY) {
+ Tcl_Panic("Added too many new lines in UpdateDisplayInfo");
+ }
+ dlPtr->y = y;
+ y += dlPtr->height;
}
- dlPtr->y = y;
- y += dlPtr->height;
}
}
@@ -1873,42 +1905,44 @@ UpdateDisplayInfo(textPtr)
*/
dlPtr = dInfoPtr->dLinePtr;
- if ((dlPtr->flags & HAS_3D_BORDER) && !(dlPtr->flags & TOP_LINE)) {
- dlPtr->flags |= OLD_Y_INVALID;
- }
- while (1) {
- if ((dlPtr->flags & TOP_LINE) && (dlPtr != dInfoPtr->dLinePtr)
- && (dlPtr->flags & HAS_3D_BORDER)) {
- dlPtr->flags |= OLD_Y_INVALID;
- }
- /*
- * If the old top-line was not completely showing (i.e. the
- * pixelOffset is non-zero) and is no longer the top-line, then
- * we must re-draw it.
- */
- if ((dlPtr->flags & TOP_LINE)
- && dInfoPtr->topPixelOffset!=0 && dlPtr!=dInfoPtr->dLinePtr) {
- dlPtr->flags |= OLD_Y_INVALID;
- }
- if ((dlPtr->flags & BOTTOM_LINE) && (dlPtr->nextPtr != NULL)
- && (dlPtr->flags & HAS_3D_BORDER)) {
+ if (dlPtr != NULL) {
+ if ((dlPtr->flags & HAS_3D_BORDER) && !(dlPtr->flags & TOP_LINE)) {
dlPtr->flags |= OLD_Y_INVALID;
}
- if (dlPtr->nextPtr == NULL) {
- if ((dlPtr->flags & HAS_3D_BORDER)
- && !(dlPtr->flags & BOTTOM_LINE)) {
+ while (1) {
+ if ((dlPtr->flags & TOP_LINE) && (dlPtr != dInfoPtr->dLinePtr)
+ && (dlPtr->flags & HAS_3D_BORDER)) {
dlPtr->flags |= OLD_Y_INVALID;
}
- dlPtr->flags &= ~TOP_LINE;
- dlPtr->flags |= BOTTOM_LINE;
- break;
+ /*
+ * If the old top-line was not completely showing (i.e. the
+ * pixelOffset is non-zero) and is no longer the top-line, then
+ * we must re-draw it.
+ */
+ if ((dlPtr->flags & TOP_LINE)
+ && dInfoPtr->topPixelOffset!=0 && dlPtr!=dInfoPtr->dLinePtr) {
+ dlPtr->flags |= OLD_Y_INVALID;
+ }
+ if ((dlPtr->flags & BOTTOM_LINE) && (dlPtr->nextPtr != NULL)
+ && (dlPtr->flags & HAS_3D_BORDER)) {
+ dlPtr->flags |= OLD_Y_INVALID;
+ }
+ if (dlPtr->nextPtr == NULL) {
+ if ((dlPtr->flags & HAS_3D_BORDER)
+ && !(dlPtr->flags & BOTTOM_LINE)) {
+ dlPtr->flags |= OLD_Y_INVALID;
+ }
+ dlPtr->flags &= ~TOP_LINE;
+ dlPtr->flags |= BOTTOM_LINE;
+ break;
+ }
+ dlPtr->flags &= ~(TOP_LINE|BOTTOM_LINE);
+ dlPtr = dlPtr->nextPtr;
}
- dlPtr->flags &= ~(TOP_LINE|BOTTOM_LINE);
- dlPtr = dlPtr->nextPtr;
+ dInfoPtr->dLinePtr->flags |= TOP_LINE;
+ dInfoPtr->topPixelOffset = dInfoPtr->newTopPixelOffset;
}
- dInfoPtr->dLinePtr->flags |= TOP_LINE;
- dInfoPtr->topPixelOffset = dInfoPtr->newTopPixelOffset;
-
+
/*
* Arrange for scrollbars to be updated.
*/
@@ -2017,7 +2051,7 @@ FreeDLines(textPtr, firstPtr, lastPtr, action)
* information.
*/
- TkTextPrintIndex(&firstPtr->index, string);
+ TkTextPrintIndex(textPtr, &firstPtr->index, string);
Tcl_SetVar2(textPtr->interp, "tk_textHeightCalc", (char *) NULL,
string, TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
}
@@ -2116,7 +2150,7 @@ DisplayDLine(textPtr, dlPtr, prevPtr, pixmap)
chunkPtr = chunkPtr->nextPtr) {
if (chunkPtr->displayProc == TkTextInsertDisplayProc) {
int x = chunkPtr->x + dInfoPtr->x - dInfoPtr->curXPixelOffset;
- (*chunkPtr->displayProc)(chunkPtr, x, dlPtr->spaceAbove,
+ (*chunkPtr->displayProc)(textPtr, chunkPtr, x, dlPtr->spaceAbove,
dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
dlPtr->y + dlPtr->spaceAbove);
@@ -2161,7 +2195,7 @@ DisplayDLine(textPtr, dlPtr, prevPtr, pixmap)
*/
x = -chunkPtr->width;
}
- (*chunkPtr->displayProc)(chunkPtr, x, dlPtr->spaceAbove,
+ (*chunkPtr->displayProc)(textPtr, chunkPtr, x, dlPtr->spaceAbove,
dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
dlPtr->y + dlPtr->spaceAbove);
@@ -2698,22 +2732,29 @@ TkTextUpdateLineMetrics(textPtr, lineNum, endLine, doThisMuch)
{
TkTextLine *linePtr = NULL;
int count = 0;
- int totalLines = TkBTreeNumLines(textPtr->tree);
+ int totalLines = TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr);
+
+ if (totalLines == 0) {
+ /* Empty peer widget */
+ return endLine;
+ }
while (1) {
/* Get a suitable line */
if (lineNum == -1 && linePtr == NULL) {
lineNum = 0;
- linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
+ linePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree,
+ textPtr, lineNum);
} else {
if (lineNum == -1 || linePtr == NULL) {
if (lineNum == -1) {
lineNum = 0;
}
- linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
+ linePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree,
+ textPtr, lineNum);
} else {
lineNum++;
- linePtr = TkBTreeNextLine(linePtr);
+ linePtr = TkBTreeNextLine(textPtr, linePtr);
}
if (lineNum == endLine) {
/*
@@ -2735,7 +2776,7 @@ TkTextUpdateLineMetrics(textPtr, lineNum, endLine, doThisMuch)
}
/* Now update the line's metrics if necessary */
- if (linePtr->pixelCalculationEpoch
+ if (TkBTreeLinePixelEpoch(textPtr, linePtr)
!= textPtr->dInfoPtr->lineMetricUpdateEpoch) {
if (doThisMuch == -1) {
count += 8 * TkTextUpdateOneLine(textPtr, linePtr,
@@ -2753,7 +2794,8 @@ TkTextUpdateLineMetrics(textPtr, lineNum, endLine, doThisMuch)
* we are looking at a long line wrapped many
* times, which we will examine in pieces.
*/
- if (textPtr->dInfoPtr->metricEpoch == textPtr->stateEpoch
+ if (textPtr->dInfoPtr->metricEpoch ==
+ textPtr->sharedTextPtr->stateEpoch
&& textPtr->dInfoPtr->metricIndex.linePtr == linePtr) {
indexPtr = &textPtr->dInfoPtr->metricIndex;
pixelHeight = textPtr->dInfoPtr->metricPixelHeight;
@@ -2764,7 +2806,7 @@ TkTextUpdateLineMetrics(textPtr, lineNum, endLine, doThisMuch)
* it when it is out of date.
*/
textPtr->dInfoPtr->metricEpoch = -1;
- index.tree = textPtr->tree;
+ index.tree = textPtr->sharedTextPtr->tree;
index.linePtr = linePtr;
index.byteIndex = 0;
index.textPtr = NULL;
@@ -2787,9 +2829,11 @@ TkTextUpdateLineMetrics(textPtr, lineNum, endLine, doThisMuch)
*/
if (pixelHeight == 0) {
textPtr->dInfoPtr->metricIndex = index;
- textPtr->dInfoPtr->metricEpoch = textPtr->stateEpoch;
+ textPtr->dInfoPtr->metricEpoch =
+ textPtr->sharedTextPtr->stateEpoch;
}
- textPtr->dInfoPtr->metricPixelHeight = linePtr->pixelHeight;
+ textPtr->dInfoPtr->metricPixelHeight =
+ TkBTreeLinePixelCount(textPtr, linePtr);
break;
}
}
@@ -2832,7 +2876,7 @@ TkTextUpdateLineMetrics(textPtr, lineNum, endLine, doThisMuch)
/*
*----------------------------------------------------------------------
*
- * TkTextInvalidateLineMetrics --
+ * TkTextInvalidateLineMetrics, TextInvalidateLineMetrics --
*
* Mark a number of text lines as having invalid line metric
* calculations. Never call this with linePtr as the last
@@ -2854,7 +2898,9 @@ TkTextUpdateLineMetrics(textPtr, lineNum, endLine, doThisMuch)
*/
void
-TkTextInvalidateLineMetrics(textPtr, linePtr, lineCount, action)
+TkTextInvalidateLineMetrics(sharedTextPtr, textPtr, linePtr, lineCount, action)
+ TkSharedText *sharedTextPtr;/* Shared widget section for all peers,
+ * or NULL. */
TkText *textPtr; /* Widget record for text widget. */
TkTextLine *linePtr; /* Invalidation starts from this line. */
int lineCount; /* And includes this many following
@@ -2863,23 +2909,44 @@ TkTextInvalidateLineMetrics(textPtr, linePtr, lineCount, action)
* occurred (insert, delete, or
* simple). */
{
+ if (sharedTextPtr == NULL) {
+ TextInvalidateLineMetrics(textPtr, linePtr, lineCount, action);
+ } else {
+ textPtr = sharedTextPtr->peers;
+ while (textPtr != NULL) {
+ TextInvalidateLineMetrics(textPtr, linePtr, lineCount, action);
+ textPtr = textPtr->next;
+ }
+ }
+}
+
+static void
+TextInvalidateLineMetrics(textPtr, linePtr, lineCount, action)
+ TkText *textPtr; /* Widget record for text widget. */
+ TkTextLine *linePtr; /* Invalidation starts from this line. */
+ int lineCount; /* And includes this many following
+ * lines. */
+ int action; /* Indicates what type of invalidation
+ * occurred (insert, delete, or
+ * simple). */
+{
int fromLine;
TextDInfo *dInfoPtr = textPtr->dInfoPtr;
if (linePtr != NULL) {
int counter = lineCount;
- fromLine = TkBTreeLineIndex(linePtr);
+ fromLine = TkBTreeLinesTo(textPtr, linePtr);
/*
* Invalidate the height calculations of each line in the
* given range.
*/
- linePtr->pixelCalculationEpoch = 0;
+ TkBTreeLinePixelEpoch(textPtr, linePtr) = 0;
while (counter > 0 && linePtr != 0) {
- linePtr = TkBTreeNextLine(linePtr);
+ linePtr = TkBTreeNextLine(textPtr, linePtr);
if (linePtr != NULL) {
- linePtr->pixelCalculationEpoch = 0;
+ TkBTreeLinePixelEpoch(textPtr, linePtr) = 0;
}
counter--;
}
@@ -3071,7 +3138,7 @@ TkTextFindDisplayLineEnd(textPtr, indexPtr, end, xOffset)
return;
}
FreeDLines(textPtr, dlPtr, NULL, DLINE_FREE_TEMP);
- TkTextIndexForwBytes(&index, byteCount, &index);
+ TkTextIndexForwBytes(textPtr, &index, byteCount, &index);
}
}
}
@@ -3169,7 +3236,7 @@ TkTextIndexYPixels(textPtr, indexPtr)
int pixelHeight;
TkTextIndex index;
- pixelHeight = TkBTreePixels(indexPtr->linePtr);
+ pixelHeight = TkBTreePixelsTo(textPtr, indexPtr->linePtr);
/*
* Iterate through all display-lines corresponding to the single
@@ -3182,7 +3249,7 @@ TkTextIndexYPixels(textPtr, indexPtr)
return pixelHeight;
}
- index.tree = textPtr->tree;
+ index.tree = textPtr->sharedTextPtr->tree;
index.linePtr = indexPtr->linePtr;
index.byteIndex = 0;
index.textPtr = NULL;
@@ -3260,7 +3327,7 @@ TkTextUpdateOneLine(textPtr, linePtr, pixelHeight, indexPtr)
int displayLines, partialCalc;
if (indexPtr == NULL) {
- index.tree = textPtr->tree;
+ index.tree = textPtr->sharedTextPtr->tree;
index.linePtr = linePtr;
index.byteIndex = 0;
index.textPtr = NULL;
@@ -3298,7 +3365,7 @@ TkTextUpdateOneLine(textPtr, linePtr, pixelHeight, indexPtr)
displayLines++;
}
- if (TkTextIndexForwBytes(indexPtr, bytes, indexPtr)) {
+ if (TkTextIndexForwBytes(textPtr, indexPtr, bytes, indexPtr)) {
break;
}
@@ -3326,13 +3393,14 @@ TkTextUpdateOneLine(textPtr, linePtr, pixelHeight, indexPtr)
* yet up to date, that will happen in TkBTreeAdjustPixelHeight
* just below).
*/
- linePtr->pixelCalculationEpoch = textPtr->dInfoPtr->lineMetricUpdateEpoch;
+ TkBTreeLinePixelEpoch(textPtr, linePtr)
+ = textPtr->dInfoPtr->lineMetricUpdateEpoch;
/*
* Also cancel any partial line height calculation state.
*/
textPtr->dInfoPtr->metricEpoch = -1;
- if (linePtr->pixelHeight == pixelHeight) {
+ if (TkBTreeLinePixelCount(textPtr, linePtr) == pixelHeight) {
return displayLines;
}
}
@@ -3342,16 +3410,17 @@ TkTextUpdateOneLine(textPtr, linePtr, pixelHeight, indexPtr)
* of the entire widget, which may be used just below for
* reporting/debugging purposes.
*/
- pixelHeight = TkBTreeAdjustPixelHeight(linePtr, pixelHeight);
+ pixelHeight = TkBTreeAdjustPixelHeight(textPtr,
+ linePtr, pixelHeight);
if (tkTextDebug) {
char buffer[2 * TCL_INTEGER_SPACE + 1];
- if (TkBTreeNextLine(linePtr) == NULL) {
+ if (TkBTreeNextLine(textPtr, linePtr) == NULL) {
Tcl_Panic("Mustn't ever update line height of last artificial line");
}
- sprintf(buffer, "%d %d", TkBTreeLineIndex(linePtr), pixelHeight);
+ sprintf(buffer, "%d %d", TkBTreeLinesTo(textPtr, linePtr), pixelHeight);
Tcl_SetVar2(textPtr->interp, "tk_textNumPixels", (char *) NULL,
buffer, TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
}
@@ -3713,7 +3782,7 @@ DisplayText(clientData)
if ((dlPtr->flags & OLD_Y_INVALID) || dlPtr->oldY != dlPtr->y) {
if (tkTextDebug) {
char string[TK_POS_CHARS];
- TkTextPrintIndex(&dlPtr->index, string);
+ TkTextPrintIndex(textPtr, &dlPtr->index, string);
Tcl_SetVar2(textPtr->interp, "tk_textRedraw",
(char *) NULL, string,
TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
@@ -3764,7 +3833,8 @@ DisplayText(clientData)
*/
x = -chunkPtr->width;
}
- TkTextEmbWinDisplayProc(chunkPtr, x, dlPtr->spaceAbove,
+ TkTextEmbWinDisplayProc(textPtr, chunkPtr,
+ x, dlPtr->spaceAbove,
dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
dlPtr->baseline - dlPtr->spaceAbove, (Display *) NULL,
(Drawable) None, dlPtr->y + dlPtr->spaceAbove);
@@ -3986,7 +4056,7 @@ TextInvalidateRegion(textPtr, region)
/*
*----------------------------------------------------------------------
*
- * TkTextChanged --
+ * TkTextChanged, TextChanged --
*
* This procedure is invoked when info in a text widget is about
* to be modified in a way that changes how it is displayed (e.g.
@@ -4011,13 +4081,35 @@ TextInvalidateRegion(textPtr, region)
*/
void
-TkTextChanged(textPtr, index1Ptr, index2Ptr)
- TkText *textPtr; /* Widget record for text widget. */
+TkTextChanged(sharedTextPtr, textPtr, index1Ptr, index2Ptr)
+ TkSharedText *sharedTextPtr; /* Shared widget section, or NULL */
+ TkText *textPtr; /* Widget record for text widget,
+ * or NULL. */
CONST TkTextIndex *index1Ptr; /* Index of first character to
* redisplay. */
CONST TkTextIndex *index2Ptr; /* Index of character just after last one
* to redisplay. */
{
+ if (sharedTextPtr == NULL) {
+ TextChanged(textPtr, index1Ptr, index2Ptr);
+ } else {
+ textPtr = sharedTextPtr->peers;
+ while (textPtr != NULL) {
+ TextChanged(textPtr, index1Ptr, index2Ptr);
+ textPtr = textPtr->next;
+ }
+ }
+}
+
+static void
+TextChanged(textPtr, index1Ptr, index2Ptr)
+ TkText *textPtr; /* Widget record for text widget,
+ * or NULL. */
+ CONST TkTextIndex *index1Ptr; /* Index of first character to
+ * redisplay. */
+ CONST TkTextIndex *index2Ptr; /* Index of character just after last one
+ * to redisplay. */
+{
TextDInfo *dInfoPtr = textPtr->dInfoPtr;
DLine *firstPtr, *lastPtr;
TkTextIndex rounded;
@@ -4075,7 +4167,7 @@ TkTextChanged(textPtr, index1Ptr, index2Ptr)
/*
*----------------------------------------------------------------------
*
- * TkTextRedrawTag --
+ * TkTextRedrawTag, TextRedrawTag --
*
* This procedure is invoked to request a redraw of all characters
* in a given range that have a particular tag on or off. It's
@@ -4092,7 +4184,32 @@ TkTextChanged(textPtr, index1Ptr, index2Ptr)
*/
void
-TkTextRedrawTag(textPtr, index1Ptr, index2Ptr, tagPtr, withTag)
+TkTextRedrawTag(sharedTextPtr, textPtr, index1Ptr, index2Ptr, tagPtr, withTag)
+ TkSharedText *sharedTextPtr; /* Shared widget section, or NULL */
+ TkText *textPtr; /* Widget record for text widget. */
+ TkTextIndex *index1Ptr; /* First character in range to consider
+ * for redisplay. NULL means start at
+ * beginning of text. */
+ TkTextIndex *index2Ptr; /* Character just after last one to consider
+ * for redisplay. NULL means process all
+ * the characters in the text. */
+ TkTextTag *tagPtr; /* Information about tag. */
+ int withTag; /* 1 means redraw characters that have the
+ * tag, 0 means redraw those without. */
+{
+ if (sharedTextPtr == NULL) {
+ TextRedrawTag(textPtr, index1Ptr, index2Ptr, tagPtr, withTag);
+ } else {
+ textPtr = sharedTextPtr->peers;
+ while (textPtr != NULL) {
+ TextRedrawTag(textPtr, index1Ptr, index2Ptr, tagPtr, withTag);
+ textPtr = textPtr->next;
+ }
+ }
+}
+
+static void
+TextRedrawTag(textPtr, index1Ptr, index2Ptr, tagPtr, withTag)
TkText *textPtr; /* Widget record for text widget. */
TkTextIndex *index1Ptr; /* First character in range to consider
* for redisplay. NULL means start at
@@ -4126,18 +4243,18 @@ TkTextRedrawTag(textPtr, index1Ptr, index2Ptr, tagPtr, withTag)
int lineCount;
if (index2Ptr == NULL) {
endLine = NULL;
- lineCount = TkBTreeNumLines(textPtr->tree);
+ lineCount = TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr);
} else {
endLine = index2Ptr->linePtr;
- lineCount = TkBTreeLineIndex(endLine);
+ lineCount = TkBTreeLinesTo(textPtr, endLine);
}
if (index1Ptr == NULL) {
startLine = NULL;
} else {
startLine = index1Ptr->linePtr;
- lineCount -= TkBTreeLineIndex(startLine);
+ lineCount -= TkBTreeLinesTo(textPtr, startLine);
}
- TkTextInvalidateLineMetrics(textPtr, startLine, lineCount,
+ TkTextInvalidateLineMetrics(NULL, textPtr, startLine, lineCount,
TK_TEXT_INVALIDATE_ONLY);
}
@@ -4159,8 +4276,9 @@ TkTextRedrawTag(textPtr, index1Ptr, index2Ptr, tagPtr, withTag)
*/
if (index2Ptr == NULL) {
- index2Ptr = TkTextMakeByteIndex(textPtr->tree,
- TkBTreeNumLines(textPtr->tree), 0, &endOfText);
+ int lastLine = TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr);
+ index2Ptr = TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
+ lastLine, 0, &endOfText);
}
/*
@@ -4380,6 +4498,12 @@ TkTextRelayoutWindow(textPtr, mask)
dInfoPtr->currentMetricUpdateLine = -1;
+ /*
+ * Also cancel any partial line-height calculations (for
+ * long-wrapped lines) in progress
+ */
+ dInfoPtr->metricEpoch = -1;
+
if (dInfoPtr->lineUpdateTimer == NULL) {
textPtr->refCount++;
dInfoPtr->lineUpdateTimer = Tcl_CreateTimerHandler(1,
@@ -4438,9 +4562,9 @@ TkTextSetYView(textPtr, indexPtr, pickPlace)
* text, round it back to the last real line.
*/
- lineIndex = TkBTreeLineIndex(indexPtr->linePtr);
- if (lineIndex == TkBTreeNumLines(indexPtr->tree)) {
- TkTextIndexBackChars(NULL,indexPtr, 1, &rounded, COUNT_INDICES);
+ lineIndex = TkBTreeLinesTo(textPtr, indexPtr->linePtr);
+ if (lineIndex == TkBTreeNumLines(indexPtr->tree, textPtr)) {
+ TkTextIndexBackChars(textPtr, indexPtr, 1, &rounded, COUNT_INDICES);
indexPtr = &rounded;
}
@@ -4608,8 +4732,8 @@ TkTextMeasureDown(textPtr, srcPtr, distance)
DLine *dlPtr;
TkTextIndex loop;
- lastLinePtr =
- TkBTreeFindLine(textPtr->tree, TkBTreeNumLines(textPtr->tree));
+ lastLinePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree, textPtr,
+ TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr));
do {
dlPtr = LayoutDLine(textPtr, srcPtr);
@@ -4620,7 +4744,7 @@ TkTextMeasureDown(textPtr, srcPtr, distance)
break;
}
distance -= dlPtr->height;
- TkTextIndexForwBytes(srcPtr, dlPtr->byteCount, &loop);
+ TkTextIndexForwBytes(textPtr, srcPtr, dlPtr->byteCount, &loop);
FreeDLines(textPtr, dlPtr, (DLine *) NULL, DLINE_FREE_TEMP);
if (loop.linePtr == lastLinePtr) {
break;
@@ -4681,7 +4805,7 @@ MeasureUp(textPtr, srcPtr, distance, dstPtr, overlap)
bytesToCount = srcPtr->byteIndex + 1;
index.tree = srcPtr->tree;
- for (lineNum = TkBTreeLineIndex(srcPtr->linePtr); lineNum >= 0;
+ for (lineNum = TkBTreeLinesTo(textPtr, srcPtr->linePtr); lineNum >= 0;
lineNum--) {
/*
* Layout an entire text line (potentially > 1 display line).
@@ -4693,14 +4817,14 @@ MeasureUp(textPtr, srcPtr, distance, dstPtr, overlap)
* in the list).
*/
- index.linePtr = TkBTreeFindLine(srcPtr->tree, lineNum);
+ index.linePtr = TkBTreeFindLine(srcPtr->tree, textPtr, lineNum);
index.byteIndex = 0;
lowestPtr = NULL;
do {
dlPtr = LayoutDLine(textPtr, &index);
dlPtr->nextPtr = lowestPtr;
lowestPtr = dlPtr;
- TkTextIndexForwBytes(&index, dlPtr->byteCount, &index);
+ TkTextIndexForwBytes(textPtr, &index, dlPtr->byteCount, &index);
bytesToCount -= dlPtr->byteCount;
} while (bytesToCount>0 && index.linePtr==dlPtr->index.linePtr);
@@ -4740,7 +4864,7 @@ MeasureUp(textPtr, srcPtr, distance, dstPtr, overlap)
* in the text.
*/
- TkTextMakeByteIndex(textPtr->tree, 0, 0, dstPtr);
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr, 0, 0, dstPtr);
if (overlap != NULL) {
*overlap = 0;
}
@@ -4792,8 +4916,9 @@ TkTextSeeCmd(textPtr, interp, objc, objv)
* text, round it back to the last real line.
*/
- if (TkBTreeLineIndex(index.linePtr) == TkBTreeNumLines(index.tree)) {
- TkTextIndexBackChars(NULL,&index, 1, &index, COUNT_INDICES);
+ if (TkBTreeLinesTo(textPtr, index.linePtr)
+ == TkBTreeNumLines(index.tree, textPtr)) {
+ TkTextIndexBackChars(textPtr, &index, 1, &index, COUNT_INDICES);
}
/*
@@ -4840,7 +4965,7 @@ TkTextSeeCmd(textPtr, interp, objc, objv)
*/
if (chunkPtr != NULL) {
- (*chunkPtr->bboxProc)(chunkPtr, byteCount,
+ (*chunkPtr->bboxProc)(textPtr, chunkPtr, byteCount,
dlPtr->y + dlPtr->spaceAbove,
dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
dlPtr->baseline - dlPtr->spaceAbove, &x, &y, &width,
@@ -5004,14 +5129,15 @@ YScrollByPixels(textPtr, offset)
* distance.
*/
- lastLinePtr = TkBTreeFindLine(textPtr->tree,
- TkBTreeNumLines(textPtr->tree));
+ lastLinePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree, textPtr,
+ TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr));
offset += dInfoPtr->topPixelOffset;
dInfoPtr->newTopPixelOffset = 0;
while (offset > 0) {
dlPtr = LayoutDLine(textPtr, &textPtr->topIndex);
dlPtr->nextPtr = NULL;
- TkTextIndexForwBytes(&textPtr->topIndex, dlPtr->byteCount, &new);
+ TkTextIndexForwBytes(textPtr, &textPtr->topIndex,
+ dlPtr->byteCount, &new);
if (offset <= dlPtr->height) {
/* Adjust the top overlap accordingly */
dInfoPtr->newTopPixelOffset = offset;
@@ -5074,18 +5200,19 @@ YScrollByLines(textPtr, offset)
*/
bytesToCount = textPtr->topIndex.byteIndex + 1;
- index.tree = textPtr->tree;
+ index.tree = textPtr->sharedTextPtr->tree;
offset--; /* Skip line containing topIndex. */
- for (lineNum = TkBTreeLineIndex(textPtr->topIndex.linePtr);
+ for (lineNum = TkBTreeLinesTo(textPtr, textPtr->topIndex.linePtr);
lineNum >= 0; lineNum--) {
- index.linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
+ index.linePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree,
+ textPtr, lineNum);
index.byteIndex = 0;
lowestPtr = NULL;
do {
dlPtr = LayoutDLine(textPtr, &index);
dlPtr->nextPtr = lowestPtr;
lowestPtr = dlPtr;
- TkTextIndexForwBytes(&index, dlPtr->byteCount, &index);
+ TkTextIndexForwBytes(textPtr, &index, dlPtr->byteCount, &index);
bytesToCount -= dlPtr->byteCount;
} while ((bytesToCount > 0)
&& (index.linePtr == dlPtr->index.linePtr));
@@ -5116,7 +5243,8 @@ YScrollByLines(textPtr, offset)
* overlapping the top window border.
*/
- TkTextMakeByteIndex(textPtr->tree, 0, 0, &textPtr->topIndex);
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
+ 0, 0, &textPtr->topIndex);
dInfoPtr->newTopPixelOffset = 0;
} else {
/*
@@ -5124,13 +5252,14 @@ YScrollByLines(textPtr, offset)
* Just count lines from the current top of the window.
*/
- lastLinePtr = TkBTreeFindLine(textPtr->tree,
- TkBTreeNumLines(textPtr->tree));
+ lastLinePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree, textPtr,
+ TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr));
for (i = 0; i < offset; i++) {
dlPtr = LayoutDLine(textPtr, &textPtr->topIndex);
if (dlPtr->length == 0 && dlPtr->height == 0) offset++;
dlPtr->nextPtr = NULL;
- TkTextIndexForwBytes(&textPtr->topIndex, dlPtr->byteCount, &new);
+ TkTextIndexForwBytes(textPtr, &textPtr->topIndex,
+ dlPtr->byteCount, &new);
FreeDLines(textPtr, dlPtr, (DLine *) NULL, DLINE_FREE);
if (new.linePtr == lastLinePtr) {
break;
@@ -5209,7 +5338,8 @@ TkTextYviewCmd(textPtr, interp, objc, objv)
if ((objc == 3) || pickPlace) {
int lineNum;
if (Tcl_GetIntFromObj(interp, objv[2+pickPlace], &lineNum) == TCL_OK) {
- TkTextMakeByteIndex(textPtr->tree, lineNum, 0, &index);
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
+ lineNum, 0, &index);
TkTextSetYView(textPtr, &index, 0);
return TCL_OK;
}
@@ -5236,7 +5366,8 @@ TkTextYviewCmd(textPtr, interp, objc, objv)
case TKTEXT_SCROLL_ERROR:
return TCL_ERROR;
case TKTEXT_SCROLL_MOVETO: {
- int numPixels = TkBTreeNumPixels(textPtr->tree);
+ int numPixels = TkBTreeNumPixels(textPtr->sharedTextPtr->tree,
+ textPtr);
int topMostPixel;
if (numPixels == 0) {
/*
@@ -5549,7 +5680,7 @@ GetYPixelCount(textPtr, dlPtr)
* for any difference between the top of the logical line and
* the display line.
*/
- int count = TkBTreePixels(linePtr);
+ int count = TkBTreePixelsTo(textPtr, linePtr);
/*
* For the common case where this dlPtr is also the start of the
@@ -5574,7 +5705,7 @@ GetYPixelCount(textPtr, dlPtr)
* of 1000 wrapped lines all from a single logical line -- but that
* sort of optimization is left for the future).
*/
- count += linePtr->pixelHeight;
+ count += TkBTreeLinePixelCount(textPtr, linePtr);
do {
count -= dlPtr->height;
@@ -5590,7 +5721,8 @@ GetYPixelCount(textPtr, dlPtr)
TkTextIndex index;
int notFirst = 0;
while (1) {
- TkTextIndexForwBytes(&dlPtr->index, dlPtr->byteCount, &index);
+ TkTextIndexForwBytes(textPtr, &dlPtr->index,
+ dlPtr->byteCount, &index);
if (notFirst) {
FreeDLines(textPtr, dlPtr, (DLine *)NULL, DLINE_FREE_TEMP);
}
@@ -5608,7 +5740,7 @@ GetYPixelCount(textPtr, dlPtr)
* suite uses this information.
*/
- TkTextPrintIndex(&index, string);
+ TkTextPrintIndex(textPtr, &index, string);
Tcl_SetVar2(textPtr->interp, "tk_textHeightCalc",
(char *) NULL, string,
TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
@@ -5671,7 +5803,8 @@ GetYView(interp, textPtr, report)
return;
}
- totalPixels = TkBTreeNumPixels(textPtr->tree);
+ totalPixels = TkBTreeNumPixels(textPtr->sharedTextPtr->tree,
+ textPtr);
if (totalPixels == 0) {
first = 0.0;
@@ -5846,8 +5979,8 @@ FindDLine(dlPtr, indexPtr)
if (dlPtr == NULL) {
return NULL;
}
- if (TkBTreeLineIndex(indexPtr->linePtr)
- < TkBTreeLineIndex(dlPtr->index.linePtr)) {
+ if (TkBTreeLinesTo(NULL, indexPtr->linePtr)
+ < TkBTreeLinesTo(NULL, dlPtr->index.linePtr)) {
/*
* The first display line is already past the desired line.
*/
@@ -5866,7 +5999,12 @@ FindDLine(dlPtr, indexPtr)
return NULL;
}
}
- linePtr = TkBTreeNextLine(linePtr);
+ /*
+ * VMD: some concern here as to whether this logic,
+ * or the caller's logic will work well with
+ * partial peer widgets.
+ */
+ linePtr = TkBTreeNextLine(NULL, linePtr);
if (linePtr == NULL) {
Tcl_Panic("FindDLine reached end of text");
}
@@ -5956,23 +6094,31 @@ TkTextPixelIndex(textPtr, x, y, indexPtr, nearest)
* Find the display line containing the desired y-coordinate.
*/
- for (dlPtr = validDlPtr = dInfoPtr->dLinePtr;
+ if (dInfoPtr->dLinePtr == NULL) {
+ if (nearest != NULL) {
+ *nearest = 1;
+ }
+ *indexPtr = textPtr->topIndex;
+ return;
+ } else {
+ for (dlPtr = validDlPtr = dInfoPtr->dLinePtr;
y >= (dlPtr->y + dlPtr->height);
dlPtr = dlPtr->nextPtr) {
- if (dlPtr->chunkPtr !=NULL) validDlPtr = dlPtr;
- if (dlPtr->nextPtr == NULL) {
- /*
- * Y-coordinate is off the bottom of the displayed text.
- * Use the last character on the last line.
- */
+ if (dlPtr->chunkPtr !=NULL) validDlPtr = dlPtr;
+ if (dlPtr->nextPtr == NULL) {
+ /*
+ * Y-coordinate is off the bottom of the displayed text.
+ * Use the last character on the last line.
+ */
- x = dInfoPtr->maxX - 1;
- nearby = 1;
- break;
+ x = dInfoPtr->maxX - 1;
+ nearby = 1;
+ break;
+ }
}
+ if (dlPtr->chunkPtr == NULL) dlPtr = validDlPtr;
}
- if (dlPtr->chunkPtr == NULL) dlPtr = validDlPtr;
-
+
if (nearest != NULL) {
*nearest = nearby;
}
@@ -6133,7 +6279,7 @@ DlineXOfIndex(textPtr, dlPtr, byteIndex)
while (byteIndex > 0) {
if (byteIndex < chunkPtr->numBytes) {
int y, width, height;
- (*chunkPtr->bboxProc)(chunkPtr, byteIndex,
+ (*chunkPtr->bboxProc)(textPtr, chunkPtr, byteIndex,
dlPtr->y + dlPtr->spaceAbove,
dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
dlPtr->baseline - dlPtr->spaceAbove, &x, &y, &width,
@@ -6233,7 +6379,8 @@ TkTextCharBbox(textPtr, indexPtr, xPtr, yPtr, widthPtr, heightPtr, charWidthPtr)
* horizontal scrolling.
*/
- (*chunkPtr->bboxProc)(chunkPtr, byteIndex, dlPtr->y + dlPtr->spaceAbove,
+ (*chunkPtr->bboxProc)(textPtr, chunkPtr, byteIndex,
+ dlPtr->y + dlPtr->spaceAbove,
dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
dlPtr->baseline - dlPtr->spaceAbove, xPtr, yPtr, widthPtr,
heightPtr);
@@ -6343,8 +6490,9 @@ TkTextDLineInfo(textPtr, indexPtr, xPtr, yPtr, widthPtr, heightPtr, basePtr)
* Get bounding-box information about an elided chunk
*/
static void
-ElideBboxProc(chunkPtr, index, y, lineHeight, baseline, xPtr, yPtr,
+ElideBboxProc(textPtr, chunkPtr, index, y, lineHeight, baseline, xPtr, yPtr,
widthPtr, heightPtr)
+ TkText *textPtr;
TkTextDispChunk *chunkPtr; /* Chunk containing desired char. */
int index; /* Index of desired character within
* the chunk. */
@@ -6556,7 +6704,8 @@ TkTextCharLayoutProc(textPtr, indexPtr, segPtr, byteOffset, maxX, maxBytes,
*/
static void
-CharDisplayProc(chunkPtr, x, y, height, baseline, display, dst, screenY)
+CharDisplayProc(textPtr, chunkPtr, x, y, height, baseline, display, dst, screenY)
+ TkText *textPtr;
TkTextDispChunk *chunkPtr; /* Chunk that is to be drawn. */
int x; /* X-position in dst at which to
* draw this chunk (may differ from
@@ -6721,8 +6870,9 @@ CharMeasureProc(chunkPtr, x)
*/
static void
-CharBboxProc(chunkPtr, byteIndex, y, lineHeight, baseline, xPtr, yPtr,
+CharBboxProc(textPtr, chunkPtr, byteIndex, y, lineHeight, baseline, xPtr, yPtr,
widthPtr, heightPtr)
+ TkText *textPtr;
TkTextDispChunk *chunkPtr; /* Chunk containing desired char. */
int byteIndex; /* Byte offset of desired character
* within the chunk. */
@@ -7317,3 +7467,4 @@ TextGetScrollInfoObj(interp, textPtr, objc, objv, dblPtr, intPtr)
"\": must be moveto or scroll", (char *) NULL);
return TKTEXT_SCROLL_ERROR;
}
+
diff --git a/generic/tkTextImage.c b/generic/tkTextImage.c
index 87cdb2e..fbbec00 100644
--- a/generic/tkTextImage.c
+++ b/generic/tkTextImage.c
@@ -10,7 +10,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tkTextImage.c,v 1.13 2004/03/16 19:53:09 hobbs Exp $
+ * RCS: @(#) $Id: tkTextImage.c,v 1.14 2004/09/10 12:13:41 vincentdarley Exp $
*/
#include "tk.h"
@@ -32,7 +32,8 @@ static TkTextSegment * EmbImageCleanupProc _ANSI_ARGS_((TkTextSegment *segPtr,
TkTextLine *linePtr));
static void EmbImageCheckProc _ANSI_ARGS_((TkTextSegment *segPtr,
TkTextLine *linePtr));
-static void EmbImageBboxProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
+static void EmbImageBboxProc _ANSI_ARGS_((TkText *textPtr,
+ TkTextDispChunk *chunkPtr,
int index, int y, int lineHeight, int baseline,
int *xPtr, int *yPtr, int *widthPtr,
int *heightPtr));
@@ -40,7 +41,7 @@ static int EmbImageConfigure _ANSI_ARGS_((TkText *textPtr,
TkTextSegment *eiPtr, int objc, Tcl_Obj *CONST objv[]));
static int EmbImageDeleteProc _ANSI_ARGS_((TkTextSegment *segPtr,
TkTextLine *linePtr, int treeGone));
-static void EmbImageDisplayProc _ANSI_ARGS_((
+static void EmbImageDisplayProc _ANSI_ARGS_((TkText *textPtr,
TkTextDispChunk *chunkPtr, int x, int y,
int lineHeight, int baseline, Display *display,
Drawable dst, int screenY));
@@ -200,13 +201,14 @@ TkTextImageCmd(textPtr, interp, objc, objv)
return TCL_OK;
}
} else {
- TkTextChanged(textPtr, &index, &index);
+ TkTextChanged(textPtr->sharedTextPtr, NULL, &index, &index);
/*
* It's probably not true that all window configuration
* can change the line height, so we could be more
* efficient here and only call this when necessary.
*/
- TkTextInvalidateLineMetrics(textPtr, index.linePtr, 0,
+ TkTextInvalidateLineMetrics(textPtr->sharedTextPtr, NULL,
+ index.linePtr, 0,
TK_TEXT_INVALIDATE_ONLY);
return EmbImageConfigure(textPtr, eiPtr, objc-4, objv+4);
}
@@ -231,10 +233,11 @@ TkTextImageCmd(textPtr, interp, objc, objv)
* Don't allow insertions on the last (dummy) line of the text.
*/
- lineIndex = TkBTreeLineIndex(index.linePtr);
- if (lineIndex == TkBTreeNumLines(textPtr->tree)) {
+ lineIndex = TkBTreeLinesTo(textPtr, index.linePtr);
+ if (lineIndex == TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr)) {
lineIndex--;
- TkTextMakeByteIndex(textPtr->tree, lineIndex, 1000000, &index);
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
+ lineIndex, 1000000, &index);
}
/*
@@ -244,7 +247,7 @@ TkTextImageCmd(textPtr, interp, objc, objv)
eiPtr = (TkTextSegment *) ckalloc(EI_SEG_SIZE);
eiPtr->typePtr = &tkTextEmbImageType;
eiPtr->size = 1;
- eiPtr->body.ei.textPtr = textPtr;
+ eiPtr->body.ei.sharedTextPtr = textPtr->sharedTextPtr;
eiPtr->body.ei.linePtr = NULL;
eiPtr->body.ei.imageName = NULL;
eiPtr->body.ei.imageString = NULL;
@@ -260,16 +263,17 @@ TkTextImageCmd(textPtr, interp, objc, objv)
* it again if the configuration fails).
*/
- TkTextChanged(textPtr, &index, &index);
+ TkTextChanged(textPtr->sharedTextPtr, NULL, &index, &index);
TkBTreeLinkSegment(eiPtr, &index);
if (EmbImageConfigure(textPtr, eiPtr, objc-4, objv+4) != TCL_OK) {
TkTextIndex index2;
TkTextIndexForwChars(NULL, &index, 1, &index2, COUNT_INDICES);
- TkBTreeDeleteChars(&index, &index2);
+ TkBTreeDeleteChars(textPtr->sharedTextPtr->tree, &index, &index2);
return TCL_ERROR;
}
- TkTextInvalidateLineMetrics(textPtr, index.linePtr, 0,
+ TkTextInvalidateLineMetrics(textPtr->sharedTextPtr, NULL,
+ index.linePtr, 0,
TK_TEXT_INVALIDATE_ONLY);
return TCL_OK;
}
@@ -281,10 +285,10 @@ TkTextImageCmd(textPtr, interp, objc, objv)
Tcl_WrongNumArgs(interp, 3, objv, NULL);
return TCL_ERROR;
}
- for (hPtr = Tcl_FirstHashEntry(&textPtr->imageTable, &search);
+ for (hPtr = Tcl_FirstHashEntry(&textPtr->sharedTextPtr->imageTable, &search);
hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
Tcl_AppendElement(interp,
- Tcl_GetHashKey(&textPtr->markTable, hPtr));
+ Tcl_GetHashKey(&textPtr->sharedTextPtr->markTable, hPtr));
}
return TCL_OK;
}
@@ -382,12 +386,12 @@ EmbImageConfigure(textPtr, eiPtr, objc, objv)
return TCL_ERROR;
}
len = strlen(name);
- for (hPtr = Tcl_FirstHashEntry(&textPtr->imageTable, &search);
+ for (hPtr = Tcl_FirstHashEntry(&textPtr->sharedTextPtr->imageTable, &search);
hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
- char *haveName = Tcl_GetHashKey(&textPtr->imageTable, hPtr);
+ char *haveName = Tcl_GetHashKey(&textPtr->sharedTextPtr->imageTable, hPtr);
if (strncmp(name, haveName, len) == 0) {
new = 0;
- sscanf(haveName+len,"#%d",&new);
+ sscanf(haveName+len, "#%d", &new);
if (new > count) {
count = new;
}
@@ -402,11 +406,11 @@ EmbImageConfigure(textPtr, eiPtr, objc, objv)
if (conflict) {
char buf[4 + TCL_INTEGER_SPACE];
- sprintf(buf, "#%d",count+1);
- Tcl_DStringAppend(&newName,buf, -1);
+ sprintf(buf, "#%d", count+1);
+ Tcl_DStringAppend(&newName, buf, -1);
}
name = Tcl_DStringValue(&newName);
- hPtr = Tcl_CreateHashEntry(&textPtr->imageTable, name, &new);
+ hPtr = Tcl_CreateHashEntry(&textPtr->sharedTextPtr->imageTable, name, &new);
Tcl_SetHashValue(hPtr, eiPtr);
Tcl_AppendResult(textPtr->interp, name , (char *) NULL);
eiPtr->body.ei.name = ckalloc((unsigned) Tcl_DStringLength(&newName)+1);
@@ -446,7 +450,7 @@ EmbImageDeleteProc(eiPtr, linePtr, treeGone)
Tcl_HashEntry *hPtr;
if (eiPtr->body.ei.image != NULL) {
- hPtr = Tcl_FindHashEntry(&eiPtr->body.ei.textPtr->imageTable,
+ hPtr = Tcl_FindHashEntry(&eiPtr->body.ei.sharedTextPtr->imageTable,
eiPtr->body.ei.name);
if (hPtr != NULL) {
/*
@@ -459,8 +463,12 @@ EmbImageDeleteProc(eiPtr, linePtr, treeGone)
}
Tk_FreeImage(eiPtr->body.ei.image);
}
+ /*
+ * No need to supply a tkwin argument, since we have no
+ * window-specific options.
+ */
Tk_FreeConfigOptions((char *) &eiPtr->body.ei, eiPtr->body.ei.optionTable,
- eiPtr->body.ei.textPtr->tkwin);
+ NULL);
ckfree((char *) eiPtr);
return 0;
}
@@ -632,7 +640,9 @@ EmbImageCheckProc(eiPtr, linePtr)
*/
static void
-EmbImageDisplayProc(chunkPtr, x, y, lineHeight, baseline, display, dst, screenY)
+EmbImageDisplayProc(textPtr, chunkPtr, x, y, lineHeight, baseline, display,
+ dst, screenY)
+ TkText *textPtr;
TkTextDispChunk *chunkPtr; /* Chunk that is to be drawn. */
int x; /* X-position in dst at which to
* draw this chunk (differs from
@@ -666,7 +676,7 @@ EmbImageDisplayProc(chunkPtr, x, y, lineHeight, baseline, display, dst, screenY)
* into account the align value for the image.
*/
- EmbImageBboxProc(chunkPtr, 0, y, lineHeight, baseline, &lineX,
+ EmbImageBboxProc(textPtr, chunkPtr, 0, y, lineHeight, baseline, &lineX,
&imageY, &width, &height);
imageX = lineX - chunkPtr->x + x;
@@ -698,8 +708,9 @@ EmbImageDisplayProc(chunkPtr, x, y, lineHeight, baseline, display, dst, screenY)
*/
static void
-EmbImageBboxProc(chunkPtr, index, y, lineHeight, baseline, xPtr, yPtr,
+EmbImageBboxProc(textPtr, chunkPtr, index, y, lineHeight, baseline, xPtr, yPtr,
widthPtr, heightPtr)
+ TkText *textPtr;
TkTextDispChunk *chunkPtr; /* Chunk containing desired char. */
int index; /* Index of desired character within
* the chunk. */
@@ -771,12 +782,12 @@ TkTextImageIndex(textPtr, name, indexPtr)
Tcl_HashEntry *hPtr;
TkTextSegment *eiPtr;
- hPtr = Tcl_FindHashEntry(&textPtr->imageTable, name);
+ hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->imageTable, name);
if (hPtr == NULL) {
return 0;
}
eiPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
- indexPtr->tree = textPtr->tree;
+ indexPtr->tree = textPtr->sharedTextPtr->tree;
indexPtr->linePtr = eiPtr->body.ei.linePtr;
indexPtr->byteIndex = TkTextSegToOffset(eiPtr, indexPtr->linePtr);
return 1;
@@ -812,15 +823,15 @@ EmbImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
TkTextSegment *eiPtr = (TkTextSegment *) clientData;
TkTextIndex index;
- index.tree = eiPtr->body.ei.textPtr->tree;
+ index.tree = eiPtr->body.ei.sharedTextPtr->tree;
index.linePtr = eiPtr->body.ei.linePtr;
index.byteIndex = TkTextSegToOffset(eiPtr, eiPtr->body.ei.linePtr);
- TkTextChanged(eiPtr->body.ei.textPtr, &index, &index);
+ TkTextChanged(eiPtr->body.ei.sharedTextPtr, NULL, &index, &index);
/*
* It's probably not true that all image changes
* can change the line height, so we could be more
* efficient here and only call this when necessary.
*/
- TkTextInvalidateLineMetrics(eiPtr->body.ei.textPtr,
+ TkTextInvalidateLineMetrics(eiPtr->body.ei.sharedTextPtr, NULL,
index.linePtr, 0, TK_TEXT_INVALIDATE_ONLY);
}
diff --git a/generic/tkTextIndex.c b/generic/tkTextIndex.c
index 44deddd..b405880 100644
--- a/generic/tkTextIndex.c
+++ b/generic/tkTextIndex.c
@@ -10,7 +10,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tkTextIndex.c,v 1.18 2004/06/04 10:51:18 vincentdarley Exp $
+ * RCS: @(#) $Id: tkTextIndex.c,v 1.19 2004/09/10 12:13:42 vincentdarley Exp $
*/
#include "default.h"
@@ -40,7 +40,8 @@ static CONST char * ForwBack _ANSI_ARGS_((TkText *textPtr,
static CONST char * StartEnd _ANSI_ARGS_((TkText *textPtr,
CONST char *string, TkTextIndex *indexPtr));
static int GetIndex _ANSI_ARGS_((Tcl_Interp *interp,
- TkText *textPtr, CONST char *string,
+ TkSharedText *sharedPtr, TkText *textPtr,
+ CONST char *string,
TkTextIndex *indexPtr, int *canCachePtr));
/*
@@ -120,7 +121,7 @@ UpdateStringOfTextIndex(objPtr)
CONST TkTextIndex *indexPtr = GET_TEXTINDEX(objPtr);
- len = TkTextPrintIndex(indexPtr, buffer);
+ len = TkTextPrintIndex(indexPtr->textPtr, indexPtr, buffer);
objPtr->bytes = ckalloc((unsigned) len + 1);
strcpy(objPtr->bytes, buffer);
@@ -178,7 +179,7 @@ MakeObjIndex(textPtr, objPtr, origPtr)
if (textPtr != NULL) {
textPtr->refCount++;
- SET_INDEXEPOCH(objPtr, textPtr->stateEpoch);
+ SET_INDEXEPOCH(objPtr, textPtr->sharedTextPtr->stateEpoch);
} else {
SET_INDEXEPOCH(objPtr, 0);
}
@@ -201,7 +202,7 @@ TkTextGetIndexFromObj(interp, textPtr, objPtr)
indexPtr = GET_TEXTINDEX(objPtr);
epoch = GET_INDEXEPOCH(objPtr);
- if (epoch == textPtr->stateEpoch) {
+ if (epoch == textPtr->sharedTextPtr->stateEpoch) {
if (indexPtr->textPtr == textPtr) {
return indexPtr;
}
@@ -214,7 +215,7 @@ TkTextGetIndexFromObj(interp, textPtr, objPtr)
* date (text has been added/deleted since).
*/
- if (GetIndex(interp, textPtr, Tcl_GetString(objPtr),
+ if (GetIndex(interp, NULL, textPtr, Tcl_GetString(objPtr),
&index, &cache) != TCL_OK) {
return NULL;
}
@@ -317,21 +318,26 @@ TkTextMakePixelIndex(textPtr, pixelIndex, indexPtr)
{
int pixelOffset = 0;
- indexPtr->tree = textPtr->tree;
+ indexPtr->tree = textPtr->sharedTextPtr->tree;
indexPtr->textPtr = textPtr;
if (pixelIndex < 0) {
pixelIndex = 0;
}
- indexPtr->linePtr = TkBTreeFindPixelLine(textPtr->tree, pixelIndex,
+ indexPtr->linePtr = TkBTreeFindPixelLine(textPtr->sharedTextPtr->tree,
+ textPtr,
+ pixelIndex,
&pixelOffset);
/*
* 'pixedlIndex' was too large, so we try again, just to find
* the last pixel in the window
*/
if (indexPtr->linePtr == NULL) {
- indexPtr->linePtr = TkBTreeFindPixelLine(textPtr->tree,
- TkBTreeNumPixels(textPtr->tree)-1, &pixelOffset);
+ int lastMinusOne = TkBTreeNumPixels(textPtr->sharedTextPtr->tree,
+ textPtr)-1;
+ indexPtr->linePtr = TkBTreeFindPixelLine(textPtr->sharedTextPtr->tree,
+ textPtr,
+ lastMinusOne, &pixelOffset);
indexPtr->byteIndex = 0;
return pixelOffset;
}
@@ -364,9 +370,10 @@ TkTextMakePixelIndex(textPtr, pixelIndex, indexPtr)
*/
TkTextIndex *
-TkTextMakeByteIndex(tree, lineIndex, byteIndex, indexPtr)
+TkTextMakeByteIndex(tree, textPtr, lineIndex, byteIndex, indexPtr)
TkTextBTree tree; /* Tree that lineIndex and byteIndex refer
* to. */
+ CONST TkText *textPtr;
int lineIndex; /* Index of desired line (0 means first
* line of text). */
int byteIndex; /* Byte index of desired character. */
@@ -385,9 +392,10 @@ TkTextMakeByteIndex(tree, lineIndex, byteIndex, indexPtr)
if (byteIndex < 0) {
byteIndex = 0;
}
- indexPtr->linePtr = TkBTreeFindLine(tree, lineIndex);
+ indexPtr->linePtr = TkBTreeFindLine(tree, textPtr, lineIndex);
if (indexPtr->linePtr == NULL) {
- indexPtr->linePtr = TkBTreeFindLine(tree, TkBTreeNumLines(tree));
+ indexPtr->linePtr = TkBTreeFindLine(tree, textPtr,
+ TkBTreeNumLines(tree, textPtr));
byteIndex = 0;
}
if (byteIndex == 0) {
@@ -455,9 +463,10 @@ TkTextMakeByteIndex(tree, lineIndex, byteIndex, indexPtr)
*/
TkTextIndex *
-TkTextMakeCharIndex(tree, lineIndex, charIndex, indexPtr)
+TkTextMakeCharIndex(tree, textPtr, lineIndex, charIndex, indexPtr)
TkTextBTree tree; /* Tree that lineIndex and charIndex refer
* to. */
+ TkText *textPtr;
int lineIndex; /* Index of desired line (0 means first
* line of text). */
int charIndex; /* Index of desired character. */
@@ -476,9 +485,10 @@ TkTextMakeCharIndex(tree, lineIndex, charIndex, indexPtr)
if (charIndex < 0) {
charIndex = 0;
}
- indexPtr->linePtr = TkBTreeFindLine(tree, lineIndex);
+ indexPtr->linePtr = TkBTreeFindLine(tree, textPtr, lineIndex);
if (indexPtr->linePtr == NULL) {
- indexPtr->linePtr = TkBTreeFindLine(tree, TkBTreeNumLines(tree));
+ indexPtr->linePtr = TkBTreeFindLine(tree, textPtr,
+ TkBTreeNumLines(tree, textPtr));
charIndex = 0;
}
@@ -626,7 +636,36 @@ TkTextGetObjIndex(interp, textPtr, idxObj, indexPtr)
* of position. */
TkTextIndex *indexPtr; /* Index structure to fill in. */
{
- return GetIndex(interp, textPtr, Tcl_GetString(idxObj), indexPtr, NULL);
+ return GetIndex(interp, NULL, textPtr, Tcl_GetString(idxObj), indexPtr, NULL);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TkTextSharedGetObjIndex --
+ *
+ * Simpler wrapper around the string based function, but could be
+ * enhanced with a new object type in the future.
+ *
+ * Results:
+ * see TkTextGetIndex
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+TkTextSharedGetObjIndex(interp, sharedTextPtr, idxObj, indexPtr)
+ Tcl_Interp *interp; /* Use this for error reporting. */
+ TkSharedText *sharedTextPtr;/* Information about text widget. */
+ Tcl_Obj *idxObj; /* Object containing textual description
+ * of position. */
+ TkTextIndex *indexPtr; /* Index structure to fill in. */
+{
+ return GetIndex(interp, sharedTextPtr, NULL,
+ Tcl_GetString(idxObj), indexPtr, NULL);
}
/*
@@ -655,7 +694,7 @@ TkTextGetIndex(interp, textPtr, string, indexPtr)
CONST char *string; /* Textual description of position. */
TkTextIndex *indexPtr; /* Index structure to fill in. */
{
- return GetIndex(interp, textPtr, string, indexPtr, NULL);
+ return GetIndex(interp, NULL, textPtr, string, indexPtr, NULL);
}
/*
@@ -682,8 +721,9 @@ TkTextGetIndex(interp, textPtr, string, indexPtr)
*/
static int
-GetIndex(interp, textPtr, string, indexPtr, canCachePtr)
+GetIndex(interp, sharedPtr, textPtr, string, indexPtr, canCachePtr)
Tcl_Interp *interp; /* Use this for error reporting. */
+ TkSharedText *sharedPtr;
TkText *textPtr; /* Information about text widget. */
CONST char *string; /* Textual description of position. */
TkTextIndex *indexPtr; /* Index structure to fill in. */
@@ -692,8 +732,6 @@ GetIndex(interp, textPtr, string, indexPtr, canCachePtr)
{
char *p, *end, *endOfBase;
Tcl_HashEntry *hPtr;
- TkTextTag *tagPtr;
- TkTextSearch search;
TkTextIndex first, last;
int wantLast, result;
char c;
@@ -701,6 +739,10 @@ GetIndex(interp, textPtr, string, indexPtr, canCachePtr)
Tcl_DString copy;
int canCache = 0;
+ if (sharedPtr == NULL) {
+ sharedPtr = textPtr->sharedTextPtr;
+ }
+
/*
*---------------------------------------------------------------------
* Stage 1: check to see if the index consists of nothing but a mark
@@ -720,7 +762,7 @@ GetIndex(interp, textPtr, string, indexPtr, canCachePtr)
*------------------------------------------------
*/
- indexPtr->tree = textPtr->tree;
+ indexPtr->tree = sharedPtr->tree;
/*
* First look for the form "tag.first" or "tag.last" where "tag"
@@ -733,6 +775,10 @@ GetIndex(interp, textPtr, string, indexPtr, canCachePtr)
Tcl_DStringInit(&copy);
p = strrchr(Tcl_DStringAppend(&copy, string, -1), '.');
if (p != NULL) {
+ TkTextSearch search;
+ TkTextTag *tagPtr;
+ CONST char *tagName;
+
if ((p[1] == 'f') && (strncmp(p+1, "first", 5) == 0)) {
wantLast = 0;
endOfBase = p+6;
@@ -742,23 +788,40 @@ GetIndex(interp, textPtr, string, indexPtr, canCachePtr)
} else {
goto tryxy;
}
- *p = 0;
- hPtr = Tcl_FindHashEntry(&textPtr->tagTable, Tcl_DStringValue(&copy));
- *p = '.';
- if (hPtr == NULL) {
+ tagPtr = NULL;
+ tagName = Tcl_DStringValue(&copy);
+ if (((p - tagName) == 3) && !strncmp(tagName, "sel", 3)) {
+ /*
+ * Special case for sel tag which is not stored in
+ * the hash table.
+ */
+ tagPtr = textPtr->selTagPtr;
+ } else {
+ *p = 0;
+ hPtr = Tcl_FindHashEntry(&sharedPtr->tagTable, tagName);
+ *p = '.';
+ if (hPtr != NULL) {
+ tagPtr = (TkTextTag *) Tcl_GetHashValue(hPtr);
+ }
+ }
+ if (tagPtr == NULL) {
goto tryxy;
}
- tagPtr = (TkTextTag *) Tcl_GetHashValue(hPtr);
- TkTextMakeByteIndex(textPtr->tree, 0, 0, &first);
- TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree), 0,
- &last);
+ TkTextMakeByteIndex(sharedPtr->tree, textPtr, 0, 0, &first);
+ TkTextMakeByteIndex(sharedPtr->tree, textPtr,
+ TkBTreeNumLines(sharedPtr->tree, textPtr),
+ 0, &last);
TkBTreeStartSearch(&first, &last, tagPtr, &search);
if (!TkBTreeCharTagged(&first, tagPtr) && !TkBTreeNextTag(&search)) {
+ if (tagPtr == textPtr->selTagPtr) {
+ tagName = "sel";
+ } else {
+ tagName = Tcl_GetHashKey(&sharedPtr->tagTable, hPtr);
+ }
Tcl_ResetResult(interp);
Tcl_AppendResult(interp,
"text doesn't contain any characters tagged with \"",
- Tcl_GetHashKey(&textPtr->tagTable, hPtr), "\"",
- (char *) NULL);
+ tagName, "\"", (char *) NULL);
Tcl_DStringFree(&copy);
return TCL_ERROR;
}
@@ -816,7 +879,8 @@ GetIndex(interp, textPtr, string, indexPtr, canCachePtr)
}
endOfBase = end;
}
- TkTextMakeCharIndex(textPtr->tree, lineIndex, charIndex, indexPtr);
+ TkTextMakeCharIndex(sharedPtr->tree, textPtr, lineIndex,
+ charIndex, indexPtr);
canCache = 1;
goto gotBase;
}
@@ -847,8 +911,9 @@ GetIndex(interp, textPtr, string, indexPtr, canCachePtr)
* Base position is end of text.
*/
- TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree),
- 0, indexPtr);
+ TkTextMakeByteIndex(sharedPtr->tree, textPtr,
+ TkBTreeNumLines(sharedPtr->tree, textPtr),
+ 0, indexPtr);
canCache = 1;
goto gotBase;
} else {
@@ -912,6 +977,9 @@ GetIndex(interp, textPtr, string, indexPtr, canCachePtr)
if (canCachePtr != NULL) {
*canCachePtr = canCache;
}
+ if (indexPtr->linePtr == NULL) {
+ Tcl_Panic("Bad index created");
+ }
return TCL_OK;
error:
@@ -941,7 +1009,8 @@ GetIndex(interp, textPtr, string, indexPtr, canCachePtr)
*/
int
-TkTextPrintIndex(indexPtr, string)
+TkTextPrintIndex(textPtr, indexPtr, string)
+ CONST TkText *textPtr;
CONST TkTextIndex *indexPtr;/* Pointer to index. */
char *string; /* Place to store the position. Must have
* at least TK_POS_CHARS characters. */
@@ -967,8 +1036,9 @@ TkTextPrintIndex(indexPtr, string)
} else {
charIndex += numBytes;
}
- return sprintf(string, "%d.%d", TkBTreeLineIndex(indexPtr->linePtr) + 1,
- charIndex);
+ return sprintf(string, "%d.%d",
+ TkBTreeLinesTo(textPtr, indexPtr->linePtr) + 1,
+ charIndex);
}
/*
@@ -1005,8 +1075,15 @@ TkTextIndexCmp(index1Ptr, index2Ptr)
return 0;
}
}
- line1 = TkBTreeLineIndex(index1Ptr->linePtr);
- line2 = TkBTreeLineIndex(index2Ptr->linePtr);
+ /*
+ * Assumption here that it is ok for comparisons to reflect
+ * the full B-tree and not just the portion that is available
+ * to any client. This should be true because the only
+ * indexPtr's we should be given are ones which are valid
+ * for the current client.
+ */
+ line1 = TkBTreeLinesTo(NULL, index1Ptr->linePtr);
+ line2 = TkBTreeLinesTo(NULL, index2Ptr->linePtr);
if (line1 < line2) {
return -1;
}
@@ -1208,7 +1285,7 @@ ForwBack(textPtr, string, indexPtr)
*/
TkTextIndexOfX(textPtr, xOffset, indexPtr);
} else {
- lineIndex = TkBTreeLineIndex(indexPtr->linePtr);
+ lineIndex = TkBTreeLinesTo(textPtr, indexPtr->linePtr);
if (*string == '+') {
lineIndex += count;
} else {
@@ -1237,8 +1314,9 @@ ForwBack(textPtr, string, indexPtr)
* same x-position in the new line.
*/
- TkTextMakeByteIndex(indexPtr->tree, lineIndex, indexPtr->byteIndex,
- indexPtr);
+ TkTextMakeByteIndex(indexPtr->tree, textPtr,
+ lineIndex, indexPtr->byteIndex,
+ indexPtr);
}
} else {
return NULL;
@@ -1269,7 +1347,8 @@ ForwBack(textPtr, string, indexPtr)
*/
int
-TkTextIndexForwBytes(srcPtr, byteCount, dstPtr)
+TkTextIndexForwBytes(textPtr, srcPtr, byteCount, dstPtr)
+ CONST TkText *textPtr;
CONST TkTextIndex *srcPtr; /* Source index. */
int byteCount; /* How many bytes forward to move. May be
* negative. */
@@ -1280,7 +1359,7 @@ TkTextIndexForwBytes(srcPtr, byteCount, dstPtr)
int lineLength;
if (byteCount < 0) {
- TkTextIndexBackBytes(srcPtr, -byteCount, dstPtr);
+ TkTextIndexBackBytes(textPtr, srcPtr, -byteCount, dstPtr);
return 0;
}
@@ -1306,7 +1385,7 @@ TkTextIndexForwBytes(srcPtr, byteCount, dstPtr)
return 0;
}
dstPtr->byteIndex -= lineLength;
- linePtr = TkBTreeNextLine(dstPtr->linePtr);
+ linePtr = TkBTreeNextLine(textPtr, dstPtr->linePtr);
if (linePtr == NULL) {
dstPtr->byteIndex = lineLength - 1;
return 1;
@@ -1374,7 +1453,16 @@ TkTextIndexForwChars(textPtr, srcPtr, charCount, dstPtr, type)
* Move forward specified number of chars.
*/
- segPtr = TkTextIndexToSeg(dstPtr, &byteOffset);
+ if (checkElided) {
+ /*
+ * In this case we have already calculated the information
+ * we need, so no need to use TkTextIndexToSeg()
+ */
+ segPtr = infoPtr->segPtr;
+ byteOffset = dstPtr->byteIndex - infoPtr->segOffset;
+ } else {
+ segPtr = TkTextIndexToSeg(dstPtr, &byteOffset);
+ }
while (1) {
/*
@@ -1470,7 +1558,7 @@ TkTextIndexForwChars(textPtr, srcPtr, charCount, dstPtr, type)
* that index.
*/
- linePtr = TkBTreeNextLine(dstPtr->linePtr);
+ linePtr = TkBTreeNextLine(textPtr, dstPtr->linePtr);
if (linePtr == NULL) {
dstPtr->byteIndex -= sizeof(char);
goto forwardCharDone;
@@ -1663,7 +1751,7 @@ TkTextIndexCount(textPtr, indexPtr1, indexPtr2, type)
* that index.
*/
- linePtr1 = TkBTreeNextLine(linePtr1);
+ linePtr1 = TkBTreeNextLine(textPtr, linePtr1);
if (linePtr1 == NULL) {
Tcl_Panic("Reached end of text widget when counting characters");
}
@@ -1697,7 +1785,8 @@ TkTextIndexCount(textPtr, indexPtr1, indexPtr2, type)
*/
void
-TkTextIndexBackBytes(srcPtr, byteCount, dstPtr)
+TkTextIndexBackBytes(textPtr, srcPtr, byteCount, dstPtr)
+ CONST TkText *textPtr;
CONST TkTextIndex *srcPtr; /* Source index. */
int byteCount; /* How many bytes backward to move. May be
* negative. */
@@ -1707,7 +1796,7 @@ TkTextIndexBackBytes(srcPtr, byteCount, dstPtr)
int lineIndex;
if (byteCount < 0) {
- TkTextIndexForwBytes(srcPtr, -byteCount, dstPtr);
+ TkTextIndexForwBytes(textPtr, srcPtr, -byteCount, dstPtr);
return;
}
@@ -1721,14 +1810,14 @@ TkTextIndexBackBytes(srcPtr, byteCount, dstPtr)
*/
if (lineIndex < 0) {
- lineIndex = TkBTreeLineIndex(dstPtr->linePtr);
+ lineIndex = TkBTreeLinesTo(textPtr, dstPtr->linePtr);
}
if (lineIndex == 0) {
dstPtr->byteIndex = 0;
return;
}
lineIndex--;
- dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, lineIndex);
+ dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, textPtr, lineIndex);
/*
* Compute the length of the line and add that to dstPtr->charIndex.
@@ -1801,12 +1890,21 @@ TkTextIndexBackChars(textPtr, srcPtr, charCount, dstPtr, type)
lineIndex = -1;
segSize = dstPtr->byteIndex;
- for (segPtr = dstPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
- if (segSize <= segPtr->size) {
- break;
+
+ if (checkElided) {
+ segPtr = infoPtr->segPtr;
+ segSize -= infoPtr->segOffset;
+ } else {
+ for (segPtr = dstPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
+ if (segSize <= segPtr->size) {
+ break;
+ }
+ segSize -= segPtr->size;
}
- segSize -= segPtr->size;
}
+ /*
+ * Now segPtr points to the segment containing the starting index
+ */
while (1) {
/*
* If we do need to pay attention to the visibility of
@@ -1907,14 +2005,14 @@ TkTextIndexBackChars(textPtr, srcPtr, charCount, dstPtr, type)
*/
if (lineIndex < 0) {
- lineIndex = TkBTreeLineIndex(dstPtr->linePtr);
+ lineIndex = TkBTreeLinesTo(textPtr, dstPtr->linePtr);
}
if (lineIndex == 0) {
dstPtr->byteIndex = 0;
goto backwadCharDone;
}
lineIndex--;
- dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, lineIndex);
+ dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, textPtr, lineIndex);
/*
* Compute the length of the line and add that to dstPtr->byteIndex.
diff --git a/generic/tkTextMark.c b/generic/tkTextMark.c
index 5933af6..a536f6c 100644
--- a/generic/tkTextMark.c
+++ b/generic/tkTextMark.c
@@ -10,7 +10,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tkTextMark.c,v 1.11 2004/01/13 02:06:01 davygrvy Exp $
+ * RCS: @(#) $Id: tkTextMark.c,v 1.12 2004/09/10 12:13:42 vincentdarley Exp $
*/
#include "tkInt.h"
@@ -139,13 +139,20 @@ TkTextMarkCmd(textPtr, interp, objc, objv)
Tcl_WrongNumArgs(interp, 3, objv, "markName ?gravity?");
return TCL_ERROR;
}
- hPtr = Tcl_FindHashEntry(&textPtr->markTable, Tcl_GetString(objv[3]));
- if (hPtr == NULL) {
- Tcl_AppendResult(interp, "there is no mark named \"",
- Tcl_GetString(objv[3]), "\"", (char *) NULL);
- return TCL_ERROR;
+ str = Tcl_GetStringFromObj(objv[3],&length);
+ if (length == 6 && !strcmp(str, "insert")) {
+ markPtr = textPtr->insertMarkPtr;
+ } else if (length == 7 && !strcmp(str, "current")) {
+ markPtr = textPtr->currentMarkPtr;
+ } else {
+ hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, str);
+ if (hPtr == NULL) {
+ Tcl_AppendResult(interp, "there is no mark named \"",
+ Tcl_GetString(objv[3]), "\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
}
- markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
if (objc == 4) {
if (markPtr->typePtr == &tkTextRightMarkType) {
Tcl_SetResult(interp, "right", TCL_STATIC);
@@ -167,8 +174,7 @@ TkTextMarkCmd(textPtr, interp, objc, objv)
return TCL_ERROR;
}
TkTextMarkSegToIndex(textPtr, markPtr, &index);
- TkBTreeUnlinkSegment(textPtr->tree, markPtr,
- markPtr->body.mark.linePtr);
+ TkBTreeUnlinkSegment(markPtr, markPtr->body.mark.linePtr);
markPtr->typePtr = newTypePtr;
TkBTreeLinkSegment(markPtr, &index);
break;
@@ -178,10 +184,12 @@ TkTextMarkCmd(textPtr, interp, objc, objv)
Tcl_WrongNumArgs(interp, 3, objv, NULL);
return TCL_ERROR;
}
- for (hPtr = Tcl_FirstHashEntry(&textPtr->markTable, &search);
+ Tcl_AppendElement(interp, "insert");
+ Tcl_AppendElement(interp, "current");
+ for (hPtr = Tcl_FirstHashEntry(&textPtr->sharedTextPtr->markTable, &search);
hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
Tcl_AppendElement(interp,
- Tcl_GetHashKey(&textPtr->markTable, hPtr));
+ Tcl_GetHashKey(&textPtr->sharedTextPtr->markTable, hPtr));
}
break;
}
@@ -213,15 +221,16 @@ TkTextMarkCmd(textPtr, interp, objc, objv)
case MARK_UNSET: {
int i;
for (i = 3; i < objc; i++) {
- hPtr = Tcl_FindHashEntry(&textPtr->markTable, Tcl_GetString(objv[i]));
+ hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable,
+ Tcl_GetString(objv[i]));
if (hPtr != NULL) {
markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
+ /* Special case not needed with peer widgets */
if ((markPtr == textPtr->insertMarkPtr)
|| (markPtr == textPtr->currentMarkPtr)) {
continue;
}
- TkBTreeUnlinkSegment(textPtr->tree, markPtr,
- markPtr->body.mark.linePtr);
+ TkBTreeUnlinkSegment(markPtr, markPtr->body.mark.linePtr);
Tcl_DeleteHashEntry(hPtr);
ckfree((char *) markPtr);
}
@@ -260,9 +269,21 @@ TkTextSetMark(textPtr, name, indexPtr)
TkTextSegment *markPtr;
TkTextIndex insertIndex;
int new;
-
- hPtr = Tcl_CreateHashEntry(&textPtr->markTable, name, &new);
- markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
+ int widgetSpecific;
+
+ if (!strcmp(name, "insert")) {
+ widgetSpecific = 1;
+ markPtr = textPtr->insertMarkPtr;
+ new = (markPtr == NULL ? 1 : 0);
+ } else if (!strcmp(name, "current")) {
+ widgetSpecific = 2;
+ markPtr = textPtr->currentMarkPtr;
+ new = (markPtr == NULL ? 1 : 0);
+ } else {
+ widgetSpecific = 0;
+ hPtr = Tcl_CreateHashEntry(&textPtr->sharedTextPtr->markTable, name, &new);
+ markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
+ }
if (!new) {
/*
* If this is the insertion point that's being moved, be sure
@@ -279,15 +300,14 @@ TkTextSetMark(textPtr, name, indexPtr)
* While we wish to redisplay, no heights have changed, so
* no need to call TkTextInvalidateLineMetrics.
*/
- TkTextChanged(textPtr, &index, &index2);
- if (TkBTreeLineIndex(indexPtr->linePtr)
- == TkBTreeNumLines(textPtr->tree)) {
+ TkTextChanged(NULL, textPtr, &index, &index2);
+ if (TkBTreeLinesTo(textPtr, indexPtr->linePtr)
+ == TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr)) {
TkTextIndexBackChars(NULL,indexPtr, 1, &insertIndex, COUNT_INDICES);
indexPtr = &insertIndex;
}
}
- TkBTreeUnlinkSegment(textPtr->tree, markPtr,
- markPtr->body.mark.linePtr);
+ TkBTreeUnlinkSegment(markPtr, markPtr->body.mark.linePtr);
} else {
markPtr = (TkTextSegment *) ckalloc(MSEG_SIZE);
markPtr->typePtr = &tkTextRightMarkType;
@@ -295,7 +315,13 @@ TkTextSetMark(textPtr, name, indexPtr)
markPtr->body.mark.textPtr = textPtr;
markPtr->body.mark.linePtr = indexPtr->linePtr;
markPtr->body.mark.hPtr = hPtr;
- Tcl_SetHashValue(hPtr, markPtr);
+ if (widgetSpecific == 0) {
+ Tcl_SetHashValue(hPtr, markPtr);
+ } else if (widgetSpecific == 1) {
+ textPtr->insertMarkPtr = markPtr;
+ } else {
+ textPtr->currentMarkPtr = markPtr;
+ }
}
TkBTreeLinkSegment(markPtr, indexPtr);
@@ -312,7 +338,7 @@ TkTextSetMark(textPtr, name, indexPtr)
* While we wish to redisplay, no heights have changed, so
* no need to call TkTextInvalidateLineMetrics
*/
- TkTextChanged(textPtr, indexPtr, &index2);
+ TkTextChanged(NULL, textPtr, indexPtr, &index2);
}
return markPtr;
}
@@ -343,7 +369,7 @@ TkTextMarkSegToIndex(textPtr, markPtr, indexPtr)
{
TkTextSegment *segPtr;
- indexPtr->tree = textPtr->tree;
+ indexPtr->tree = textPtr->sharedTextPtr->tree;
indexPtr->linePtr = markPtr->body.mark.linePtr;
indexPtr->byteIndex = 0;
for (segPtr = indexPtr->linePtr->segPtr; segPtr != markPtr;
@@ -379,14 +405,25 @@ TkTextMarkNameToIndex(textPtr, name, indexPtr)
CONST char *name; /* Name of mark. */
TkTextIndex *indexPtr; /* Index information gets stored here. */
{
- Tcl_HashEntry *hPtr;
+ TkTextSegment *segPtr;
- hPtr = Tcl_FindHashEntry(&textPtr->markTable, name);
- if (hPtr == NULL) {
- return TCL_ERROR;
+ if (textPtr == NULL) {
+ return TCL_ERROR;
}
- TkTextMarkSegToIndex(textPtr, (TkTextSegment *) Tcl_GetHashValue(hPtr),
- indexPtr);
+
+ if (!strcmp(name, "insert")) {
+ segPtr = textPtr->insertMarkPtr;
+ } else if (!strcmp(name, "current")) {
+ segPtr = textPtr->currentMarkPtr;
+ } else {
+ Tcl_HashEntry *hPtr;
+ hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, name);
+ if (hPtr == NULL) {
+ return TCL_ERROR;
+ }
+ segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
+ }
+ TkTextMarkSegToIndex(textPtr, segPtr, indexPtr);
return TCL_OK;
}
@@ -530,7 +567,9 @@ MarkLayoutProc(textPtr, indexPtr, segPtr, offset, maxX, maxChars,
/* ARGSUSED */
void
-TkTextInsertDisplayProc(chunkPtr, x, y, height, baseline, display, dst, screenY)
+TkTextInsertDisplayProc(textPtr, chunkPtr, x, y, height, baseline, display,
+ dst, screenY)
+ TkText *textPtr; /* The current text widget. */
TkTextDispChunk *chunkPtr; /* Chunk that is to be drawn. */
int x; /* X-position in dst at which to
* draw this chunk (may differ from
@@ -547,7 +586,8 @@ TkTextInsertDisplayProc(chunkPtr, x, y, height, baseline, display, dst, screenY)
int screenY; /* Y-coordinate in text window that
* corresponds to y. */
{
- TkText *textPtr = (TkText *) chunkPtr->clientData;
+ /* We have no need for the clientData */
+ /* TkText *textPtr = (TkText *) chunkPtr->clientData; */
TkTextIndex index;
int halfWidth = textPtr->insertWidth/2;
int rightSideWidth;
@@ -650,12 +690,22 @@ MarkCheckProc(markPtr, linePtr)
Tcl_Panic("MarkCheckProc: markPtr->body.mark.linePtr bogus");
}
+ /*
+ * These two marks are not in the hash table
+ */
+ if (markPtr->body.mark.textPtr->insertMarkPtr == markPtr) {
+ return;
+ }
+ if (markPtr->body.mark.textPtr->currentMarkPtr == markPtr) {
+ return;
+ }
+
/*
* Make sure that the mark is still present in the text's mark
* hash table.
*/
- for (hPtr = Tcl_FirstHashEntry(&markPtr->body.mark.textPtr->markTable,
+ for (hPtr = Tcl_FirstHashEntry(&markPtr->body.mark.textPtr->sharedTextPtr->markTable,
&search); hPtr != markPtr->body.mark.hPtr;
hPtr = Tcl_NextHashEntry(&search)) {
if (hPtr == NULL) {
@@ -691,30 +741,40 @@ MarkFindNext(interp, textPtr, string)
register TkTextSegment *segPtr;
int offset;
-
- hPtr = Tcl_FindHashEntry(&textPtr->markTable, string);
- if (hPtr != NULL) {
- /*
- * If given a mark name, return the next mark in the list of
- * segments, even if it happens to be at the same character position.
- */
- segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
+ if (!strcmp(string, "insert")) {
+ segPtr = textPtr->insertMarkPtr;
+ TkTextMarkSegToIndex(textPtr, segPtr, &index);
+ segPtr = segPtr->nextPtr;
+ } else if (!strcmp(string, "current")) {
+ segPtr = textPtr->currentMarkPtr;
TkTextMarkSegToIndex(textPtr, segPtr, &index);
segPtr = segPtr->nextPtr;
} else {
- /*
- * For non-mark name indices we want to return any marks that
- * are right at the index.
- */
- if (TkTextGetIndex(interp, textPtr, string, &index) != TCL_OK) {
- return TCL_ERROR;
- }
- for (offset = 0, segPtr = index.linePtr->segPtr;
- segPtr != NULL && offset < index.byteIndex;
- offset += segPtr->size, segPtr = segPtr->nextPtr) {
- /* Empty loop body */ ;
+ hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, string);
+ if (hPtr != NULL) {
+ /*
+ * If given a mark name, return the next mark in the list of
+ * segments, even if it happens to be at the same character position.
+ */
+ segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
+ TkTextMarkSegToIndex(textPtr, segPtr, &index);
+ segPtr = segPtr->nextPtr;
+ } else {
+ /*
+ * For non-mark name indices we want to return any marks that
+ * are right at the index.
+ */
+ if (TkTextGetIndex(interp, textPtr, string, &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (offset = 0, segPtr = index.linePtr->segPtr;
+ segPtr != NULL && offset < index.byteIndex;
+ offset += segPtr->size, segPtr = segPtr->nextPtr) {
+ /* Empty loop body */ ;
+ }
}
}
+
while (1) {
/*
* segPtr points at the first possible candidate,
@@ -723,13 +783,23 @@ MarkFindNext(interp, textPtr, string)
for ( ; segPtr != NULL ; segPtr = segPtr->nextPtr) {
if (segPtr->typePtr == &tkTextRightMarkType ||
segPtr->typePtr == &tkTextLeftMarkType) {
- Tcl_SetResult(interp,
- Tcl_GetHashKey(&textPtr->markTable, segPtr->body.mark.hPtr),
- TCL_STATIC);
+ if (segPtr == textPtr->currentMarkPtr) {
+ Tcl_SetResult(interp, "current", TCL_STATIC);
+ } else if (segPtr == textPtr->insertMarkPtr) {
+ Tcl_SetResult(interp, "insert", TCL_STATIC);
+ } else if (segPtr->body.mark.textPtr != textPtr) {
+ /* Ignore widget-specific marks for the other widgets */
+ continue;
+ } else {
+ Tcl_SetResult(interp,
+ Tcl_GetHashKey(&textPtr->sharedTextPtr->markTable,
+ segPtr->body.mark.hPtr),
+ TCL_STATIC);
+ }
return TCL_OK;
}
}
- index.linePtr = TkBTreeNextLine(index.linePtr);
+ index.linePtr = TkBTreeNextLine(textPtr, index.linePtr);
if (index.linePtr == (TkTextLine *) NULL) {
return TCL_OK;
}
@@ -765,27 +835,34 @@ MarkFindPrev(interp, textPtr, string)
register TkTextSegment *segPtr, *seg2Ptr, *prevPtr;
int offset;
-
- hPtr = Tcl_FindHashEntry(&textPtr->markTable, string);
- if (hPtr != NULL) {
- /*
- * If given a mark name, return the previous mark in the list of
- * segments, even if it happens to be at the same character position.
- */
- segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
+ if (!strcmp(string, "insert")) {
+ segPtr = textPtr->insertMarkPtr;
+ TkTextMarkSegToIndex(textPtr, segPtr, &index);
+ } else if (!strcmp(string, "current")) {
+ segPtr = textPtr->currentMarkPtr;
TkTextMarkSegToIndex(textPtr, segPtr, &index);
} else {
- /*
- * For non-mark name indices we do not return any marks that
- * are right at the index.
- */
- if (TkTextGetIndex(interp, textPtr, string, &index) != TCL_OK) {
- return TCL_ERROR;
- }
- for (offset = 0, segPtr = index.linePtr->segPtr;
- segPtr != NULL && offset < index.byteIndex;
- offset += segPtr->size, segPtr = segPtr->nextPtr) {
- /* Empty loop body */ ;
+ hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, string);
+ if (hPtr != NULL) {
+ /*
+ * If given a mark name, return the previous mark in the list of
+ * segments, even if it happens to be at the same character position.
+ */
+ segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
+ TkTextMarkSegToIndex(textPtr, segPtr, &index);
+ } else {
+ /*
+ * For non-mark name indices we do not return any marks that
+ * are right at the index.
+ */
+ if (TkTextGetIndex(interp, textPtr, string, &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (offset = 0, segPtr = index.linePtr->segPtr;
+ segPtr != NULL && offset < index.byteIndex;
+ offset += segPtr->size, segPtr = segPtr->nextPtr) {
+ /* Empty loop body */ ;
+ }
}
}
while (1) {
@@ -802,12 +879,22 @@ MarkFindPrev(interp, textPtr, string)
}
}
if (prevPtr != NULL) {
- Tcl_SetResult(interp,
- Tcl_GetHashKey(&textPtr->markTable, prevPtr->body.mark.hPtr),
- TCL_STATIC);
+ if (prevPtr == textPtr->currentMarkPtr) {
+ Tcl_SetResult(interp, "current", TCL_STATIC);
+ } else if (prevPtr == textPtr->insertMarkPtr) {
+ Tcl_SetResult(interp, "insert", TCL_STATIC);
+ } else if (segPtr->body.mark.textPtr != textPtr) {
+ /* Ignore widget-specific marks for the other widgets */
+ continue;
+ } else {
+ Tcl_SetResult(interp,
+ Tcl_GetHashKey(&textPtr->sharedTextPtr->markTable,
+ prevPtr->body.mark.hPtr),
+ TCL_STATIC);
+ }
return TCL_OK;
}
- index.linePtr = TkBTreePreviousLine(index.linePtr);
+ index.linePtr = TkBTreePreviousLine(textPtr, index.linePtr);
if (index.linePtr == (TkTextLine *) NULL) {
return TCL_OK;
}
diff --git a/generic/tkTextTag.c b/generic/tkTextTag.c
index b862d1b..41e61b1 100644
--- a/generic/tkTextTag.c
+++ b/generic/tkTextTag.c
@@ -11,7 +11,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tkTextTag.c,v 1.17 2003/12/10 12:57:59 vincentdarley Exp $
+ * RCS: @(#) $Id: tkTextTag.c,v 1.18 2004/09/10 12:13:42 vincentdarley Exp $
*/
#include "default.h"
@@ -91,6 +91,9 @@ static void SortTags _ANSI_ARGS_((int numTags,
TkTextTag **tagArrayPtr));
static int TagSortProc _ANSI_ARGS_((CONST VOID *first,
CONST VOID *second));
+static void TagBindEvent _ANSI_ARGS_((TkText *textPtr, XEvent *eventPtr,
+ int numTags, TkTextTag **tagArrayPtr));
+
/*
*--------------------------------------------------------------
@@ -134,7 +137,7 @@ TkTextTagCmd(textPtr, interp, objc, objv)
int i;
register TkTextTag *tagPtr;
- TkTextIndex first, last, index1, index2;
+ TkTextIndex index1, index2;
if (objc < 3) {
Tcl_WrongNumArgs(interp, 2, objv, "option ?arg arg ...?");
@@ -181,7 +184,8 @@ TkTextTagCmd(textPtr, interp, objc, objv)
}
if (tagPtr->affectsDisplay) {
- TkTextRedrawTag(textPtr, &index1, &index2, tagPtr, !addTag);
+ TkTextRedrawTag(textPtr->sharedTextPtr, NULL,
+ &index1, &index2, tagPtr, !addTag);
} else {
/*
* Still need to trigger enter/leave events on tags that
@@ -195,28 +199,22 @@ TkTextTagCmd(textPtr, interp, objc, objv)
* If the tag is "sel", and we actually adjusted
* something then grab the selection if we're
* supposed to export it and don't already have it.
+ *
* Also, invalidate partially-completed selection
- * retrievals.
+ * retrievals. We only need to check whether the
+ * tag is "sel" for this textPtr (not for other peer
+ * widget's "sel" tags) because we cannot reach this
+ * code path with a different widget's "sel" tag.
*/
if (tagPtr == textPtr->selTagPtr) {
- XEvent event;
/*
* Send an event that the selection changed.
* This is equivalent to
* "event generate $textWidget <<Selection>>"
*/
- memset((VOID *) &event, 0, sizeof(event));
- event.xany.type = VirtualEvent;
- event.xany.serial =
- NextRequest(Tk_Display(textPtr->tkwin));
- event.xany.send_event = False;
- event.xany.window = Tk_WindowId(textPtr->tkwin);
- event.xany.display = Tk_Display(textPtr->tkwin);
- ((XVirtualEvent *) &event)->name =
- Tk_GetUid("Selection");
- Tk_HandleEvent(&event);
+ TkTextSelectionEvent(textPtr);
if (addTag && textPtr->exportSelection
&& !(textPtr->flags & GOT_SELECTION)) {
@@ -243,8 +241,8 @@ TkTextTagCmd(textPtr, interp, objc, objv)
* one.
*/
- if (textPtr->bindingTable == NULL) {
- textPtr->bindingTable = Tk_CreateBindingTable(interp);
+ if (textPtr->sharedTextPtr->bindingTable == NULL) {
+ textPtr->sharedTextPtr->bindingTable = Tk_CreateBindingTable(interp);
}
if (objc == 6) {
@@ -253,15 +251,15 @@ TkTextTagCmd(textPtr, interp, objc, objv)
char *fifth = Tcl_GetString(objv[5]);
if (fifth[0] == 0) {
- return Tk_DeleteBinding(interp, textPtr->bindingTable,
- (ClientData) tagPtr, Tcl_GetString(objv[4]));
+ return Tk_DeleteBinding(interp, textPtr->sharedTextPtr->bindingTable,
+ (ClientData) tagPtr->name, Tcl_GetString(objv[4]));
}
if (fifth[0] == '+') {
fifth++;
append = 1;
}
- mask = Tk_CreateBinding(interp, textPtr->bindingTable,
- (ClientData) tagPtr, Tcl_GetString(objv[4]),
+ mask = Tk_CreateBinding(interp, textPtr->sharedTextPtr->bindingTable,
+ (ClientData) tagPtr->name, Tcl_GetString(objv[4]),
fifth, append);
if (mask == 0) {
return TCL_ERROR;
@@ -271,8 +269,8 @@ TkTextTagCmd(textPtr, interp, objc, objv)
|Button5MotionMask|ButtonPressMask|ButtonReleaseMask
|EnterWindowMask|LeaveWindowMask|KeyPressMask
|KeyReleaseMask|PointerMotionMask|VirtualEventMask)) {
- Tk_DeleteBinding(interp, textPtr->bindingTable,
- (ClientData) tagPtr, Tcl_GetString(objv[4]));
+ Tk_DeleteBinding(interp, textPtr->sharedTextPtr->bindingTable,
+ (ClientData) tagPtr->name, Tcl_GetString(objv[4]));
Tcl_ResetResult(interp);
Tcl_AppendResult(interp, "requested illegal events; ",
"only key, button, motion, enter, leave, and virtual ",
@@ -282,8 +280,8 @@ TkTextTagCmd(textPtr, interp, objc, objv)
} else if (objc == 5) {
CONST char *command;
- command = Tk_GetBinding(interp, textPtr->bindingTable,
- (ClientData) tagPtr, Tcl_GetString(objv[4]));
+ command = Tk_GetBinding(interp, textPtr->sharedTextPtr->bindingTable,
+ (ClientData) tagPtr->name, Tcl_GetString(objv[4]));
if (command == NULL) {
CONST char *string = Tcl_GetStringResult(interp);
@@ -302,8 +300,8 @@ TkTextTagCmd(textPtr, interp, objc, objv)
Tcl_SetResult(interp, (char *) command, TCL_STATIC);
}
} else {
- Tk_GetAllBindings(interp, textPtr->bindingTable,
- (ClientData) tagPtr);
+ Tk_GetAllBindings(interp, textPtr->sharedTextPtr->bindingTable,
+ (ClientData) tagPtr->name);
}
break;
}
@@ -506,7 +504,15 @@ TkTextTagCmd(textPtr, interp, objc, objv)
* since it can't possibly have been applied to
* anything yet.
*/
- TkTextRedrawTag(textPtr, (TkTextIndex *) NULL,
+
+ /*
+ * VMD: If this is the 'sel' tag, then we don't
+ * need to call this for all peers, unless we
+ * actually want to synchronize sel-style changes
+ * across the peers.
+ */
+ TkTextRedrawTag(textPtr->sharedTextPtr, NULL,
+ (TkTextIndex *) NULL,
(TkTextIndex *) NULL, tagPtr, 1);
}
return result;
@@ -521,9 +527,13 @@ TkTextTagCmd(textPtr, interp, objc, objv)
return TCL_ERROR;
}
for (i = 3; i < objc; i++) {
- hPtr = Tcl_FindHashEntry(&textPtr->tagTable,
+ hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->tagTable,
Tcl_GetString(objv[i]));
if (hPtr == NULL) {
+ /* Either this tag doesn't exist or it's the 'sel'
+ * tag (which is not in the hash table). Either
+ * way we don't want to delete it.
+ */
continue;
}
tagPtr = (TkTextTag *) Tcl_GetHashValue(hPtr);
@@ -531,46 +541,12 @@ TkTextTagCmd(textPtr, interp, objc, objv)
continue;
}
if (tagPtr->affectsDisplay) {
- TkTextRedrawTag(textPtr, (TkTextIndex *) NULL,
- (TkTextIndex *) NULL, tagPtr, 1);
- }
- TkTextMakeByteIndex(textPtr->tree, 0, 0, &first);
- TkTextMakeByteIndex(textPtr->tree,
- TkBTreeNumLines(textPtr->tree),
- 0, &last),
- TkBTreeTag(&first, &last, tagPtr, 0);
-
- if (tagPtr == textPtr->selTagPtr) {
- XEvent event;
- /*
- * Send an event that the selection changed.
- * This is equivalent to
- * "event generate $textWidget <<Selection>>"
- */
-
- memset((VOID *) &event, 0, sizeof(event));
- event.xany.type = VirtualEvent;
- event.xany.serial = NextRequest(Tk_Display(textPtr->tkwin));
- event.xany.send_event = False;
- event.xany.window = Tk_WindowId(textPtr->tkwin);
- event.xany.display = Tk_Display(textPtr->tkwin);
- ((XVirtualEvent *) &event)->name = Tk_GetUid("Selection");
- Tk_HandleEvent(&event);
+ TkTextRedrawTag(textPtr->sharedTextPtr, NULL,
+ (TkTextIndex *) NULL,
+ (TkTextIndex *) NULL, tagPtr, 1);
}
-
+ TkTextDeleteTag(textPtr, tagPtr);
Tcl_DeleteHashEntry(hPtr);
- if (textPtr->bindingTable != NULL) {
- Tk_DeleteAllBindings(textPtr->bindingTable,
- (ClientData) tagPtr);
- }
-
- /*
- * Update the tag priorities to reflect the deletion of this tag.
- */
-
- ChangeTagPriority(textPtr, tagPtr, textPtr->numTags-1);
- textPtr->numTags -= 1;
- TkTextFreeTag(textPtr, tagPtr);
}
break;
}
@@ -600,7 +576,12 @@ TkTextTagCmd(textPtr, interp, objc, objv)
prio = 0;
}
ChangeTagPriority(textPtr, tagPtr, prio);
- TkTextRedrawTag(textPtr, (TkTextIndex *) NULL,
+ /*
+ * If this is the 'sel' tag, then we don't actually need
+ * to call this for all peers.
+ */
+ TkTextRedrawTag(textPtr->sharedTextPtr, NULL,
+ (TkTextIndex *) NULL,
(TkTextIndex *) NULL,
tagPtr, 1);
break;
@@ -619,19 +600,22 @@ TkTextTagCmd(textPtr, interp, objc, objv)
Tcl_HashEntry *hPtr;
arrayPtr = (TkTextTag **) ckalloc((unsigned)
- (textPtr->numTags * sizeof(TkTextTag *)));
- for (i = 0, hPtr = Tcl_FirstHashEntry(&textPtr->tagTable,
- &search);
- hPtr != NULL; i++, hPtr = Tcl_NextHashEntry(&search)) {
+ (textPtr->sharedTextPtr->numTags * sizeof(TkTextTag *)));
+ for (i = 0,
+ hPtr = Tcl_FirstHashEntry(&textPtr->sharedTextPtr->tagTable,
+ &search);
+ hPtr != NULL; i++, hPtr = Tcl_NextHashEntry(&search)) {
arrayPtr[i] = (TkTextTag *) Tcl_GetHashValue(hPtr);
}
- arraySize = textPtr->numTags;
+ /* The 'sel' tag is not in the hash table */
+ arrayPtr[i] = textPtr->selTagPtr;
+ arraySize = textPtr->sharedTextPtr->numTags;
} else {
if (TkTextGetObjIndex(interp, textPtr, objv[3], &index1)
!= TCL_OK) {
return TCL_ERROR;
}
- arrayPtr = TkBTreeGetTags(&index1, &arraySize);
+ arrayPtr = TkBTreeGetTags(&index1, textPtr, &arraySize);
if (arrayPtr == NULL) {
return TCL_OK;
}
@@ -648,6 +632,7 @@ TkTextTagCmd(textPtr, interp, objc, objv)
break;
}
case TAG_NEXTRANGE: {
+ TkTextIndex last;
TkTextSearch tSearch;
char position[TK_POS_CHARS];
@@ -662,8 +647,9 @@ TkTextTagCmd(textPtr, interp, objc, objv)
if (TkTextGetObjIndex(interp, textPtr, objv[4], &index1) != TCL_OK) {
return TCL_ERROR;
}
- TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree),
- 0, &last);
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
+ TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr),
+ 0, &last);
if (objc == 5) {
index2 = last;
} else if (TkTextGetObjIndex(interp, textPtr, objv[5], &index2)
@@ -716,14 +702,15 @@ TkTextTagCmd(textPtr, interp, objc, objv)
if (TkTextIndexCmp(&tSearch.curIndex, &index2) >= 0) {
return TCL_OK;
}
- TkTextPrintIndex(&tSearch.curIndex, position);
+ TkTextPrintIndex(textPtr, &tSearch.curIndex, position);
Tcl_AppendElement(interp, position);
TkBTreeNextTag(&tSearch);
- TkTextPrintIndex(&tSearch.curIndex, position);
+ TkTextPrintIndex(textPtr, &tSearch.curIndex, position);
Tcl_AppendElement(interp, position);
break;
}
case TAG_PREVRANGE: {
+ TkTextIndex last;
TkTextSearch tSearch;
char position1[TK_POS_CHARS];
char position2[TK_POS_CHARS];
@@ -740,7 +727,8 @@ TkTextTagCmd(textPtr, interp, objc, objv)
return TCL_ERROR;
}
if (objc == 5) {
- TkTextMakeByteIndex(textPtr->tree, 0, 0, &index2);
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
+ 0, 0, &index2);
} else if (TkTextGetObjIndex(interp, textPtr, objv[5], &index2)
!= TCL_OK) {
return TCL_ERROR;
@@ -756,23 +744,69 @@ TkTextTagCmd(textPtr, interp, objc, objv)
TkBTreeStartSearchBack(&index1, &index2, tagPtr, &tSearch);
if (!TkBTreePrevTag(&tSearch)) {
+ /*
+ * Special case, there may be a tag off toggle at
+ * index1, and a tag on toggle before the start of a
+ * partial peer widget. In this case we missed it.
+ */
+ if (textPtr->start != NULL && (textPtr->start == index2.linePtr)
+ && (index2.byteIndex == 0) && TkBTreeCharTagged(&index2, tagPtr)
+ && (TkTextIndexCmp(&index2, &index1) < 0)) {
+ /*
+ * The first character is tagged, so just add the
+ * range from the first char to the start of the
+ * range.
+ */
+ TkTextPrintIndex(textPtr, &index2, position1);
+ TkTextPrintIndex(textPtr, &index1, position2);
+ Tcl_AppendElement(interp, position1);
+ Tcl_AppendElement(interp, position2);
+ }
return TCL_OK;
}
if (tSearch.segPtr->typePtr == &tkTextToggleOnType) {
- TkTextPrintIndex(&tSearch.curIndex, position1);
- TkTextMakeByteIndex(textPtr->tree,
- TkBTreeNumLines(textPtr->tree),
- 0, &last);
+ TkTextPrintIndex(textPtr, &tSearch.curIndex, position1);
+ if (textPtr->start != NULL) {
+ /*
+ * Make sure the first index is not before the
+ * first allowed text index in this widget.
+ */
+ TkTextIndex firstIndex;
+ firstIndex.linePtr = textPtr->start;
+ firstIndex.byteIndex = 0;
+ firstIndex.textPtr = NULL;
+ if (TkTextIndexCmp(&tSearch.curIndex, &firstIndex) < 0) {
+ if (TkTextIndexCmp(&firstIndex, &index1) >= 0) {
+ /*
+ * But now the new first index is actually
+ * too far along in the text, so nothing
+ * is returned.
+ */
+ return TCL_OK;
+ }
+ TkTextPrintIndex(textPtr, &firstIndex, position1);
+ }
+ }
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
+ TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr),
+ 0, &last);
TkBTreeStartSearch(&tSearch.curIndex, &last, tagPtr, &tSearch);
TkBTreeNextTag(&tSearch);
- TkTextPrintIndex(&tSearch.curIndex, position2);
+ TkTextPrintIndex(textPtr, &tSearch.curIndex, position2);
} else {
- TkTextPrintIndex(&tSearch.curIndex, position2);
+ TkTextPrintIndex(textPtr, &tSearch.curIndex, position2);
TkBTreePrevTag(&tSearch);
+ TkTextPrintIndex(textPtr, &tSearch.curIndex, position1);
if (TkTextIndexCmp(&tSearch.curIndex, &index2) < 0) {
- return TCL_OK;
+ if (textPtr->start != NULL
+ && index2.linePtr == textPtr->start
+ && index2.byteIndex == 0) {
+ /* It's ok */
+ TkTextPrintIndex(textPtr, &index2, position1);
+ } else {
+ return TCL_OK;
+ }
}
- TkTextPrintIndex(&tSearch.curIndex, position1);
}
Tcl_AppendElement(interp, position1);
Tcl_AppendElement(interp, position2);
@@ -801,17 +835,24 @@ TkTextTagCmd(textPtr, interp, objc, objv)
prio = tagPtr2->priority + 1;
}
} else {
- prio = textPtr->numTags-1;
+ prio = textPtr->sharedTextPtr->numTags-1;
}
ChangeTagPriority(textPtr, tagPtr, prio);
- TkTextRedrawTag(textPtr, (TkTextIndex *) NULL, (TkTextIndex *) NULL,
- tagPtr, 1);
+ /*
+ * If this is the 'sel' tag, then we don't actually need
+ * to call this for all peers.
+ */
+ TkTextRedrawTag(textPtr->sharedTextPtr, NULL,
+ (TkTextIndex *) NULL, (TkTextIndex *) NULL,
+ tagPtr, 1);
break;
}
case TAG_RANGES: {
+ TkTextIndex first, last;
TkTextSearch tSearch;
Tcl_Obj *listObj = Tcl_NewListObj(0, NULL);
-
+ int count = 0;
+
if (objc != 4) {
Tcl_WrongNumArgs(interp, 3, objv, "tagName");
return TCL_ERROR;
@@ -820,17 +861,31 @@ TkTextTagCmd(textPtr, interp, objc, objv)
if (tagPtr == NULL) {
return TCL_OK;
}
- TkTextMakeByteIndex(textPtr->tree, 0, 0, &first);
- TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree),
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
+ 0, 0, &first);
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
+ TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr),
0, &last);
TkBTreeStartSearch(&first, &last, tagPtr, &tSearch);
if (TkBTreeCharTagged(&first, tagPtr)) {
Tcl_ListObjAppendElement(interp, listObj,
TkTextNewIndexObj(textPtr, &first));
+ count++;
}
while (TkBTreeNextTag(&tSearch)) {
Tcl_ListObjAppendElement(interp, listObj,
TkTextNewIndexObj(textPtr, &tSearch.curIndex));
+ count++;
+ }
+ if (count % 2 == 1) {
+ /*
+ * If a text widget uses '-end', it won't necessarily
+ * run to the end of the B-tree, and therefore the
+ * tag range might not be closed. In this case we
+ * add the end of the range.
+ */
+ Tcl_ListObjAppendElement(interp, listObj,
+ TkTextNewIndexObj(textPtr, &last));
}
Tcl_SetObjResult(interp, listObj);
break;
@@ -867,13 +922,30 @@ TkTextCreateTag(textPtr, tagName, newTag)
register TkTextTag *tagPtr;
Tcl_HashEntry *hPtr;
int new;
-
- hPtr = Tcl_CreateHashEntry(&textPtr->tagTable, tagName, &new);
- if (newTag != NULL) {
- *newTag = new;
- }
- if (!new) {
- return (TkTextTag *) Tcl_GetHashValue(hPtr);
+ CONST char *name;
+
+ if (!strcmp(tagName, "sel")) {
+ if (textPtr->selTagPtr != NULL) {
+ if (newTag != NULL) {
+ *newTag = 0;
+ }
+ return textPtr->selTagPtr;
+ } else {
+ if (newTag != NULL) {
+ *newTag = 1;
+ }
+ }
+ name = "sel";
+ } else {
+ hPtr = Tcl_CreateHashEntry(&textPtr->sharedTextPtr->tagTable,
+ tagName, &new);
+ if (newTag != NULL) {
+ *newTag = new;
+ }
+ if (!new) {
+ return (TkTextTag *) Tcl_GetHashValue(hPtr);
+ }
+ name = Tcl_GetHashKey(&textPtr->sharedTextPtr->tagTable, hPtr);
}
/*
@@ -882,10 +954,11 @@ TkTextCreateTag(textPtr, tagName, newTag)
*/
tagPtr = (TkTextTag *) ckalloc(sizeof(TkTextTag));
- tagPtr->name = Tcl_GetHashKey(&textPtr->tagTable, hPtr);
+ tagPtr->name = name;
+ tagPtr->textPtr = NULL;
tagPtr->toggleCount = 0;
tagPtr->tagRootPtr = NULL;
- tagPtr->priority = textPtr->numTags;
+ tagPtr->priority = textPtr->sharedTextPtr->numTags;
tagPtr->border = NULL;
tagPtr->borderWidth = 0;
tagPtr->borderWidthPtr = NULL;
@@ -922,8 +995,13 @@ TkTextCreateTag(textPtr, tagName, newTag)
tagPtr->wrapMode = TEXT_WRAPMODE_NULL;
tagPtr->affectsDisplay = 0;
tagPtr->affectsDisplayGeometry = 0;
- textPtr->numTags++;
- Tcl_SetHashValue(hPtr, tagPtr);
+ textPtr->sharedTextPtr->numTags++;
+ if (!strcmp(tagName, "sel")) {
+ tagPtr->textPtr = textPtr;
+ textPtr->refCount++;
+ } else {
+ Tcl_SetHashValue(hPtr, tagPtr);
+ }
tagPtr->optionTable = Tk_CreateOptionTable(textPtr->interp, tagOptionSpecs);
return tagPtr;
}
@@ -956,8 +1034,14 @@ FindTag(interp, textPtr, tagName)
Tcl_Obj *tagName; /* Name of desired tag. */
{
Tcl_HashEntry *hPtr;
-
- hPtr = Tcl_FindHashEntry(&textPtr->tagTable, Tcl_GetString(tagName));
+ int len;
+ CONST char *str;
+ str = Tcl_GetStringFromObj(tagName, &len);
+ if (len == 3 && !strcmp(str,"sel")) {
+ return textPtr->selTagPtr;
+ }
+ hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->tagTable,
+ Tcl_GetString(tagName));
if (hPtr != NULL) {
return (TkTextTag *) Tcl_GetHashValue(hPtr);
}
@@ -971,6 +1055,76 @@ FindTag(interp, textPtr, tagName)
/*
*----------------------------------------------------------------------
*
+ * TkTextDeleteTag --
+ *
+ * This procedure is called to carry out most actions associated
+ * with the 'tag delete' sub-command. It will remove all
+ * evidence of the tag from the B-tree, and then call
+ * TkTextFreeTag to clean up the tag structure itself.
+ *
+ * The only actions this doesn't carry out it to check if
+ * the deletion of the tag requires something to be
+ * re-displayed, and to remove the tag from the tagTable
+ * (hash table) if that is necessary (i.e. if it's not the
+ * 'sel' tag). It is expected that the caller carry out both
+ * of these actions.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory and other resources are freed, the B-tree is
+ * manipulated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkTextDeleteTag(textPtr, tagPtr)
+ TkText *textPtr; /* Info about overall widget. */
+ register TkTextTag *tagPtr; /* Tag being deleted. */
+{
+ TkTextIndex first, last;
+
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr, 0, 0, &first);
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
+ TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr),
+ 0, &last),
+ TkBTreeTag(&first, &last, tagPtr, 0);
+
+ if (tagPtr == textPtr->selTagPtr) {
+ /*
+ * Send an event that the selection changed.
+ * This is equivalent to
+ * "event generate $textWidget <<Selection>>"
+ */
+ TkTextSelectionEvent(textPtr);
+ } else {
+ /*
+ * Since all peer widgets have an independent "sel" tag, we
+ * don't want removal of one sel tag to remove bindings which
+ * are still valid in other peer widgets.
+ */
+ if (textPtr->sharedTextPtr->bindingTable != NULL) {
+ Tk_DeleteAllBindings(textPtr->sharedTextPtr->bindingTable,
+ (ClientData) tagPtr->name);
+ }
+ }
+
+ /*
+ * Update the tag priorities to reflect the deletion of this tag.
+ */
+
+ ChangeTagPriority(textPtr, tagPtr,
+ textPtr->sharedTextPtr->numTags-1);
+ textPtr->sharedTextPtr->numTags -= 1;
+ TkTextFreeTag(textPtr, tagPtr);
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* TkTextFreeTag --
*
* This procedure is called when a tag is deleted to free up the
@@ -1010,6 +1164,20 @@ TkTextFreeTag(textPtr, tagPtr)
break;
}
}
+ /*
+ * If this tag is widget-specific (peer widgets) then clean
+ * up the refCount it holds.
+ */
+ if (tagPtr->textPtr != NULL) {
+ if (textPtr != tagPtr->textPtr) {
+ Tcl_Panic("Tag being deleted from wrong widget");
+ }
+ textPtr->refCount--;
+ if (textPtr->refCount == 0) {
+ ckfree((char *) textPtr);
+ }
+ tagPtr->textPtr = NULL;
+ }
/* Finally free the tag's memory */
ckfree((char *) tagPtr);
}
@@ -1109,9 +1277,10 @@ TagSortProc(first, second)
*
* Side effects:
* Priorities may be changed for some or all of the tags in
- * textPtr. The tags will be arranged so that there is exactly
- * one tag at each priority level between 0 and textPtr->numTags-1,
- * with tagPtr at priority "prio".
+ * textPtr. The tags will be arranged so that there is exactly one
+ * tag at each priority level between 0 and
+ * textPtr->sharedTextPtr->numTags-1, with tagPtr at priority
+ * "prio".
*
*----------------------------------------------------------------------
*/
@@ -1131,8 +1300,8 @@ ChangeTagPriority(textPtr, tagPtr, prio)
if (prio < 0) {
prio = 0;
}
- if (prio >= textPtr->numTags) {
- prio = textPtr->numTags-1;
+ if (prio >= textPtr->sharedTextPtr->numTags) {
+ prio = textPtr->sharedTextPtr->numTags-1;
}
if (prio == tagPtr->priority) {
return;
@@ -1145,7 +1314,14 @@ ChangeTagPriority(textPtr, tagPtr, prio)
high = prio;
delta = -1;
}
- for (hPtr = Tcl_FirstHashEntry(&textPtr->tagTable, &search);
+ /*
+ * Adjust first the 'sel' tag, then all others from the hash table
+ */
+ if ((textPtr->selTagPtr->priority >= low)
+ && (textPtr->selTagPtr->priority <= high)) {
+ textPtr->selTagPtr->priority += delta;
+ }
+ for (hPtr = Tcl_FirstHashEntry(&textPtr->sharedTextPtr->tagTable, &search);
hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
tagPtr2 = (TkTextTag *) Tcl_GetHashValue(hPtr);
if ((tagPtr2->priority >= low) && (tagPtr2->priority <= high)) {
@@ -1239,10 +1415,10 @@ TkTextBindProc(clientData, eventPtr)
}
TkTextPickCurrent(textPtr, eventPtr);
}
- if ((textPtr->numCurTags > 0) && (textPtr->bindingTable != NULL)
+ if ((textPtr->numCurTags > 0) && (textPtr->sharedTextPtr->bindingTable != NULL)
&& (textPtr->tkwin != NULL) && !(textPtr->flags & DESTROYED)) {
- Tk_BindEvent(textPtr->bindingTable, eventPtr, textPtr->tkwin,
- textPtr->numCurTags, (ClientData *) textPtr->curTagArrayPtr);
+ TagBindEvent(textPtr, eventPtr, textPtr->numCurTags,
+ textPtr->curTagArrayPtr);
}
if (repick) {
unsigned int oldState;
@@ -1374,7 +1550,7 @@ TkTextPickCurrent(textPtr, eventPtr)
newArrayPtr = NULL;
numNewTags = 0;
} else {
- newArrayPtr = TkBTreeGetTags(&index, &numNewTags);
+ newArrayPtr = TkBTreeGetTags(&index, textPtr, &numNewTags);
SortTags(numNewTags, newArrayPtr);
}
} else {
@@ -1421,7 +1597,7 @@ TkTextPickCurrent(textPtr, eventPtr)
oldArrayPtr = textPtr->curTagArrayPtr;
textPtr->curTagArrayPtr = newArrayPtr;
if (numOldTags != 0) {
- if ((textPtr->bindingTable != NULL) && (textPtr->tkwin != NULL)
+ if ((textPtr->sharedTextPtr->bindingTable != NULL) && (textPtr->tkwin != NULL)
&& !(textPtr->flags & DESTROYED)) {
event = textPtr->pickEvent;
event.type = LeaveNotify;
@@ -1433,8 +1609,7 @@ TkTextPickCurrent(textPtr, eventPtr)
*/
event.xcrossing.detail = NotifyAncestor;
- Tk_BindEvent(textPtr->bindingTable, &event, textPtr->tkwin,
- numOldTags, (ClientData *) oldArrayPtr);
+ TagBindEvent(textPtr, &event, numOldTags, oldArrayPtr);
}
ckfree((char *) oldArrayPtr);
}
@@ -1450,14 +1625,81 @@ TkTextPickCurrent(textPtr, eventPtr)
textPtr->pickEvent.xcrossing.y, &index, &nearby);
TkTextSetMark(textPtr, "current", &index);
if (numNewTags != 0) {
- if ((textPtr->bindingTable != NULL) && (textPtr->tkwin != NULL)
+ if ((textPtr->sharedTextPtr->bindingTable != NULL) && (textPtr->tkwin != NULL)
&& !(textPtr->flags & DESTROYED) && !nearby) {
event = textPtr->pickEvent;
event.type = EnterNotify;
event.xcrossing.detail = NotifyAncestor;
- Tk_BindEvent(textPtr->bindingTable, &event, textPtr->tkwin,
- numNewTags, (ClientData *) copyArrayPtr);
+ TagBindEvent(textPtr, &event, numNewTags, copyArrayPtr);
}
ckfree((char *) copyArrayPtr);
}
}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TagBindEvent --
+ *
+ * Trigger given events for all tags that match the
+ * relevant bindings. To handle the "sel" tag
+ * correctly in all peer widgets, we must use the
+ * name of the tags as the binding table element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Almost anything can be triggered by tag bindings,
+ * including deletion of the text widget.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+TagBindEvent(textPtr, eventPtr, numTags, tagArrayPtr)
+ TkText *textPtr; /* Text widget to fire bindings
+ * in. */
+ XEvent *eventPtr; /* What actually happened. */
+ int numTags; /* Number of relevant tags. */
+ TkTextTag **tagArrayPtr; /* Array of relevant tags. */
+{
+ #define NUM_BIND_TAGS 10
+ CONST char *nameArray[NUM_BIND_TAGS];
+ CONST char **nameArrPtr;
+ int i;
+
+ /*
+ * Try to avoid allocation unless there are lots of tags
+ */
+ if (numTags > NUM_BIND_TAGS) {
+ nameArrPtr = (CONST char**) ckalloc (numTags * sizeof(CONST char*));
+ } else {
+ nameArrPtr = nameArray;
+ }
+ /*
+ * We use tag names as keys in the hash table. We do this instead
+ * of using the actual tagPtr objects because we want one "sel" tag
+ * binding for all peer widgets, despite the fact that each has its
+ * own tagPtr object.
+ */
+ for (i = 0; i < numTags; i++) {
+ TkTextTag *tagPtr = tagArrayPtr[i];
+ if (tagPtr != NULL) {
+ nameArrPtr[i] = tagPtr->name;
+ } else {
+ /*
+ * Tag has been deleted elsewhere, and therefore nulled
+ * out in this array. Tk_BindEvent is clever enough to
+ * cope with NULLs being thrown at it.
+ */
+ nameArrPtr[i] = NULL;
+ }
+ }
+ Tk_BindEvent(textPtr->sharedTextPtr->bindingTable, eventPtr, textPtr->tkwin,
+ numTags, (ClientData *) nameArrPtr);
+
+ if (numTags > NUM_BIND_TAGS) {
+ ckfree((char*)nameArrPtr);
+ }
+}
diff --git a/generic/tkTextWind.c b/generic/tkTextWind.c
index 5c59603..5fd56e0 100644
--- a/generic/tkTextWind.c
+++ b/generic/tkTextWind.c
@@ -11,7 +11,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tkTextWind.c,v 1.12 2004/01/13 02:06:01 davygrvy Exp $
+ * RCS: @(#) $Id: tkTextWind.c,v 1.13 2004/09/10 12:13:42 vincentdarley Exp $
*/
#include "tk.h"
@@ -49,7 +49,8 @@ static TkTextSegment * EmbWinCleanupProc _ANSI_ARGS_((TkTextSegment *segPtr,
TkTextLine *linePtr));
static void EmbWinCheckProc _ANSI_ARGS_((TkTextSegment *segPtr,
TkTextLine *linePtr));
-static void EmbWinBboxProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
+static void EmbWinBboxProc _ANSI_ARGS_((TkText *textPtr,
+ TkTextDispChunk *chunkPtr,
int index, int y, int lineHeight, int baseline,
int *xPtr, int *yPtr, int *widthPtr,
int *heightPtr));
@@ -68,6 +69,8 @@ static void EmbWinStructureProc _ANSI_ARGS_((ClientData clientData,
XEvent *eventPtr));
static void EmbWinUndisplayProc _ANSI_ARGS_((TkText *textPtr,
TkTextDispChunk *chunkPtr));
+static TkTextEmbWindowClient* EmbWinGetClient _ANSI_ARGS_((CONST TkText *textPtr,
+ TkTextSegment *ewPtr));
/*
* The following structure declares the "embedded window" segment type.
@@ -173,6 +176,7 @@ TkTextWindowCmd(textPtr, interp, objc, objv)
TkTextIndex index;
TkTextSegment *ewPtr;
Tcl_Obj *objPtr;
+ TkTextEmbWindowClient *client;
if (objc != 5) {
Tcl_WrongNumArgs(interp, 3, objv, "index option");
@@ -187,6 +191,15 @@ TkTextWindowCmd(textPtr, interp, objc, objv)
Tcl_GetString(objv[3]), "\"", (char *) NULL);
return TCL_ERROR;
}
+
+ /* Copy over client specific value before querying */
+ client = EmbWinGetClient(textPtr, ewPtr);
+ if (client != NULL) {
+ ewPtr->body.ew.tkwin = client->tkwin;
+ } else {
+ ewPtr->body.ew.tkwin = NULL;
+ }
+
objPtr = Tk_GetOptionValue(interp, (char *) &ewPtr->body.ew,
ewPtr->body.ew.optionTable, objv[4], textPtr->tkwin);
if (objPtr == NULL) {
@@ -215,7 +228,18 @@ TkTextWindowCmd(textPtr, interp, objc, objv)
return TCL_ERROR;
}
if (objc <= 5) {
- Tcl_Obj* objPtr = Tk_GetOptionInfo(interp, (char *) &ewPtr->body.ew,
+ TkTextEmbWindowClient *client;
+ Tcl_Obj* objPtr;
+
+ /* Copy over client specific value before querying */
+ client = EmbWinGetClient(textPtr, ewPtr);
+ if (client != NULL) {
+ ewPtr->body.ew.tkwin = client->tkwin;
+ } else {
+ ewPtr->body.ew.tkwin = NULL;
+ }
+
+ objPtr = Tk_GetOptionInfo(interp, (char *) &ewPtr->body.ew,
ewPtr->body.ew.optionTable,
(objc == 5) ? objv[4] : (Tcl_Obj *) NULL,
textPtr->tkwin);
@@ -226,13 +250,14 @@ TkTextWindowCmd(textPtr, interp, objc, objv)
return TCL_OK;
}
} else {
- TkTextChanged(textPtr, &index, &index);
+ TkTextChanged(textPtr->sharedTextPtr, NULL, &index, &index);
/*
* It's probably not true that all window configuration
* can change the line height, so we could be more
* efficient here and only call this when necessary.
*/
- TkTextInvalidateLineMetrics(textPtr, index.linePtr, 0,
+ TkTextInvalidateLineMetrics(textPtr->sharedTextPtr, NULL,
+ index.linePtr, 0,
TK_TEXT_INVALIDATE_ONLY);
return EmbWinConfigure(textPtr, ewPtr, objc-4, objv+4);
}
@@ -241,7 +266,9 @@ TkTextWindowCmd(textPtr, interp, objc, objv)
case WIND_CREATE: {
TkTextIndex index;
int lineIndex;
-
+ TkTextEmbWindowClient *client;
+ int res;
+
/*
* Add a new window. Find where to put the new window, and
* mark that position for redisplay.
@@ -259,10 +286,11 @@ TkTextWindowCmd(textPtr, interp, objc, objv)
* Don't allow insertions on the last (dummy) line of the text.
*/
- lineIndex = TkBTreeLineIndex(index.linePtr);
- if (lineIndex == TkBTreeNumLines(textPtr->tree)) {
+ lineIndex = TkBTreeLinesTo(textPtr, index.linePtr);
+ if (lineIndex == TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr)) {
lineIndex--;
- TkTextMakeByteIndex(textPtr->tree, lineIndex, 1000000, &index);
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
+ lineIndex, 1000000, &index);
}
/*
@@ -272,31 +300,42 @@ TkTextWindowCmd(textPtr, interp, objc, objv)
ewPtr = (TkTextSegment *) ckalloc(EW_SEG_SIZE);
ewPtr->typePtr = &tkTextEmbWindowType;
ewPtr->size = 1;
- ewPtr->body.ew.textPtr = textPtr;
+ ewPtr->body.ew.sharedTextPtr = textPtr->sharedTextPtr;
ewPtr->body.ew.linePtr = NULL;
ewPtr->body.ew.tkwin = NULL;
ewPtr->body.ew.create = NULL;
ewPtr->body.ew.align = ALIGN_CENTER;
ewPtr->body.ew.padX = ewPtr->body.ew.padY = 0;
ewPtr->body.ew.stretch = 0;
- ewPtr->body.ew.chunkCount = 0;
- ewPtr->body.ew.displayed = 0;
ewPtr->body.ew.optionTable = Tk_CreateOptionTable(interp, optionSpecs);
+
+ client = (TkTextEmbWindowClient*) ckalloc(sizeof(TkTextEmbWindowClient));
+ client->next = NULL;
+ client->textPtr = textPtr;
+ client->tkwin = NULL;
+ client->chunkCount = 0;
+ client->displayed = 0;
+ client->parent = ewPtr;
+ ewPtr->body.ew.clients = client;
+
/*
* Link the segment into the text widget, then configure it (delete
* it again if the configuration fails).
*/
- TkTextChanged(textPtr, &index, &index);
+ TkTextChanged(textPtr->sharedTextPtr, NULL, &index, &index);
TkBTreeLinkSegment(ewPtr, &index);
- if (EmbWinConfigure(textPtr, ewPtr, objc-4, objv+4) != TCL_OK) {
+ res = EmbWinConfigure(textPtr, ewPtr, objc-4, objv+4);
+ client->tkwin = ewPtr->body.ew.tkwin;
+ if (res != TCL_OK) {
TkTextIndex index2;
- TkTextIndexForwChars(NULL,&index, 1, &index2, COUNT_INDICES);
- TkBTreeDeleteChars(&index, &index2);
+ TkTextIndexForwChars(NULL, &index, 1, &index2, COUNT_INDICES);
+ TkBTreeDeleteChars(textPtr->sharedTextPtr->tree, &index, &index2);
return TCL_ERROR;
}
- TkTextInvalidateLineMetrics(textPtr, index.linePtr, 0,
+ TkTextInvalidateLineMetrics(textPtr->sharedTextPtr, NULL,
+ index.linePtr, 0,
TK_TEXT_INVALIDATE_ONLY);
break;
}
@@ -308,10 +347,10 @@ TkTextWindowCmd(textPtr, interp, objc, objv)
Tcl_WrongNumArgs(interp, 3, objv, NULL);
return TCL_ERROR;
}
- for (hPtr = Tcl_FirstHashEntry(&textPtr->windowTable, &search);
+ for (hPtr = Tcl_FirstHashEntry(&textPtr->sharedTextPtr->windowTable, &search);
hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
Tcl_AppendElement(interp,
- Tcl_GetHashKey(&textPtr->markTable, hPtr));
+ Tcl_GetHashKey(&textPtr->sharedTextPtr->markTable, hPtr));
}
break;
}
@@ -335,6 +374,12 @@ TkTextWindowCmd(textPtr, interp, objc, objv)
* Configuration information for the embedded window changes,
* such as alignment, stretching, or name of the embedded
* window.
+ *
+ * Note that this procedure may leave widget specific client
+ * information with a NULL tkwin attached to ewPtr. While we could
+ * choose to clean up the client data structure here, there is no
+ * need to do so, and it is likely that the user is going to adjust
+ * the tkwin again soon.
*
*--------------------------------------------------------------
*/
@@ -349,21 +394,29 @@ EmbWinConfigure(textPtr, ewPtr, objc, objv)
* options. */
{
Tk_Window oldWindow;
- Tcl_HashEntry *hPtr;
- int new;
+ TkTextEmbWindowClient *client;
+ /* Copy over client specific value before querying or setting */
+ client = EmbWinGetClient(textPtr, ewPtr);
+ if (client != NULL) {
+ ewPtr->body.ew.tkwin = client->tkwin;
+ } else {
+ ewPtr->body.ew.tkwin = NULL;
+ }
+
oldWindow = ewPtr->body.ew.tkwin;
if (Tk_SetOptions(textPtr->interp, (char*)&ewPtr->body.ew,
ewPtr->body.ew.optionTable,
objc, objv, textPtr->tkwin, NULL, NULL) != TCL_OK) {
return TCL_ERROR;
}
+
if (oldWindow != ewPtr->body.ew.tkwin) {
if (oldWindow != NULL) {
- Tcl_DeleteHashEntry(Tcl_FindHashEntry(&textPtr->windowTable,
+ Tcl_DeleteHashEntry(Tcl_FindHashEntry(&textPtr->sharedTextPtr->windowTable,
Tk_PathName(oldWindow)));
Tk_DeleteEventHandler(oldWindow, StructureNotifyMask,
- EmbWinStructureProc, (ClientData) ewPtr);
+ EmbWinStructureProc, (ClientData) client);
Tk_ManageGeometry(oldWindow, (Tk_GeomMgr *) NULL,
(ClientData) NULL);
if (textPtr->tkwin != Tk_Parent(oldWindow)) {
@@ -372,9 +425,14 @@ EmbWinConfigure(textPtr, ewPtr, objc, objv)
Tk_UnmapWindow(oldWindow);
}
}
+ if (client != NULL) {
+ client->tkwin = NULL;
+ }
if (ewPtr->body.ew.tkwin != NULL) {
Tk_Window ancestor, parent;
-
+ Tcl_HashEntry *hPtr;
+ int new;
+
/*
* Make sure that the text is either the parent of the
* embedded window or a descendant of that parent. Also,
@@ -394,6 +452,9 @@ EmbWinConfigure(textPtr, ewPtr, objc, objv)
Tk_PathName(ewPtr->body.ew.tkwin), " in ",
Tk_PathName(textPtr->tkwin), (char *) NULL);
ewPtr->body.ew.tkwin = NULL;
+ if (client != NULL) {
+ client->tkwin = NULL;
+ }
return TCL_ERROR;
}
}
@@ -401,6 +462,19 @@ EmbWinConfigure(textPtr, ewPtr, objc, objv)
|| (ewPtr->body.ew.tkwin == textPtr->tkwin)) {
goto badMaster;
}
+
+ if (client == NULL) {
+ /* Have to make the new client */
+ client = (TkTextEmbWindowClient*) ckalloc(sizeof(TkTextEmbWindowClient));
+ client->next = ewPtr->body.ew.clients;
+ client->textPtr = textPtr;
+ client->tkwin = NULL;
+ client->chunkCount = 0;
+ client->displayed = 0;
+ client->parent = ewPtr;
+ ewPtr->body.ew.clients = client;
+ }
+ client->tkwin = ewPtr->body.ew.tkwin;
/*
* Take over geometry management for the window, plus create
@@ -408,9 +482,9 @@ EmbWinConfigure(textPtr, ewPtr, objc, objv)
*/
Tk_ManageGeometry(ewPtr->body.ew.tkwin, &textGeomType,
- (ClientData) ewPtr);
+ (ClientData) client);
Tk_CreateEventHandler(ewPtr->body.ew.tkwin, StructureNotifyMask,
- EmbWinStructureProc, (ClientData) ewPtr);
+ EmbWinStructureProc, (ClientData) client);
/*
* Special trick! Must enter into the hash table *after*
@@ -420,7 +494,7 @@ EmbWinConfigure(textPtr, ewPtr, objc, objv)
* entry.
*/
- hPtr = Tcl_CreateHashEntry(&textPtr->windowTable,
+ hPtr = Tcl_CreateHashEntry(&textPtr->sharedTextPtr->windowTable,
Tk_PathName(ewPtr->body.ew.tkwin), &new);
Tcl_SetHashValue(hPtr, ewPtr);
@@ -454,21 +528,29 @@ EmbWinStructureProc(clientData, eventPtr)
ClientData clientData; /* Pointer to record describing window item. */
XEvent *eventPtr; /* Describes what just happened. */
{
- register TkTextSegment *ewPtr = (TkTextSegment *) clientData;
+ TkTextEmbWindowClient *client = (TkTextEmbWindowClient*)clientData;
+ TkTextSegment *ewPtr = client->parent;
TkTextIndex index;
-
+ Tcl_HashEntry *hPtr;
+
if (eventPtr->type != DestroyNotify) {
return;
}
- Tcl_DeleteHashEntry(Tcl_FindHashEntry(&ewPtr->body.ew.textPtr->windowTable,
- Tk_PathName(ewPtr->body.ew.tkwin)));
+ hPtr = Tcl_FindHashEntry(&ewPtr->body.ew.sharedTextPtr->windowTable,
+ Tk_PathName(client->tkwin));
+ if (hPtr != NULL) {
+ /* This may not exist if the entire widget is being deleted */
+ Tcl_DeleteHashEntry(hPtr);
+ }
+
ewPtr->body.ew.tkwin = NULL;
- index.tree = ewPtr->body.ew.textPtr->tree;
+ client->tkwin = NULL;
+ index.tree = ewPtr->body.ew.sharedTextPtr->tree;
index.linePtr = ewPtr->body.ew.linePtr;
index.byteIndex = TkTextSegToOffset(ewPtr, ewPtr->body.ew.linePtr);
- TkTextChanged(ewPtr->body.ew.textPtr, &index, &index);
- TkTextInvalidateLineMetrics(ewPtr->body.ew.textPtr,
+ TkTextChanged(ewPtr->body.ew.sharedTextPtr, NULL, &index, &index);
+ TkTextInvalidateLineMetrics(ewPtr->body.ew.sharedTextPtr, NULL,
index.linePtr, 0, TK_TEXT_INVALIDATE_ONLY);
}
@@ -497,14 +579,15 @@ EmbWinRequestProc(clientData, tkwin)
Tk_Window tkwin; /* Window that changed its desired
* size. */
{
- TkTextSegment *ewPtr = (TkTextSegment *) clientData;
+ TkTextEmbWindowClient *client = (TkTextEmbWindowClient*)clientData;
+ TkTextSegment *ewPtr = client->parent;
TkTextIndex index;
- index.tree = ewPtr->body.ew.textPtr->tree;
+ index.tree = ewPtr->body.ew.sharedTextPtr->tree;
index.linePtr = ewPtr->body.ew.linePtr;
index.byteIndex = TkTextSegToOffset(ewPtr, ewPtr->body.ew.linePtr);
- TkTextChanged(ewPtr->body.ew.textPtr, &index, &index);
- TkTextInvalidateLineMetrics(ewPtr->body.ew.textPtr,
+ TkTextChanged(ewPtr->body.ew.sharedTextPtr, NULL, &index, &index);
+ TkTextInvalidateLineMetrics(ewPtr->body.ew.sharedTextPtr, NULL,
index.linePtr, 0, TK_TEXT_INVALIDATE_ONLY);
}
@@ -533,31 +616,107 @@ EmbWinLostSlaveProc(clientData, tkwin)
Tk_Window tkwin; /* Window that was claimed away by another
* geometry manager. */
{
- register TkTextSegment *ewPtr = (TkTextSegment *) clientData;
+ TkTextEmbWindowClient *client = (TkTextEmbWindowClient*)clientData;
+ TkTextSegment *ewPtr = client->parent;
TkTextIndex index;
-
- Tk_DeleteEventHandler(ewPtr->body.ew.tkwin, StructureNotifyMask,
- EmbWinStructureProc, (ClientData) ewPtr);
- Tcl_CancelIdleCall(EmbWinDelayedUnmap, (ClientData) ewPtr);
- if (ewPtr->body.ew.textPtr->tkwin != Tk_Parent(tkwin)) {
- Tk_UnmaintainGeometry(tkwin, ewPtr->body.ew.textPtr->tkwin);
+ Tcl_HashEntry *hPtr;
+ TkTextEmbWindowClient *loop;
+
+ Tk_DeleteEventHandler(client->tkwin, StructureNotifyMask,
+ EmbWinStructureProc, (ClientData) client);
+ Tcl_CancelIdleCall(EmbWinDelayedUnmap, (ClientData) client);
+ if (client->textPtr->tkwin != Tk_Parent(tkwin)) {
+ Tk_UnmaintainGeometry(tkwin, client->textPtr->tkwin);
} else {
Tk_UnmapWindow(tkwin);
}
- Tcl_DeleteHashEntry(Tcl_FindHashEntry(&ewPtr->body.ew.textPtr->windowTable,
- Tk_PathName(ewPtr->body.ew.tkwin)));
+ hPtr = Tcl_FindHashEntry(&ewPtr->body.ew.sharedTextPtr->windowTable,
+ Tk_PathName(client->tkwin));
+ Tcl_DeleteHashEntry(hPtr);
+ client->tkwin = NULL;
ewPtr->body.ew.tkwin = NULL;
- index.tree = ewPtr->body.ew.textPtr->tree;
+
+ /* Free up the memory allocation for this client */
+
+ loop = ewPtr->body.ew.clients;
+ if (loop == client) {
+ ewPtr->body.ew.clients = client->next;
+ } else {
+ while (loop->next != client) {
+ loop = loop->next;
+ }
+ loop->next = client->next;
+ }
+ ckfree((char *)client);
+
+ index.tree = ewPtr->body.ew.sharedTextPtr->tree;
index.linePtr = ewPtr->body.ew.linePtr;
index.byteIndex = TkTextSegToOffset(ewPtr, ewPtr->body.ew.linePtr);
- TkTextChanged(ewPtr->body.ew.textPtr, &index, &index);
- TkTextInvalidateLineMetrics(ewPtr->body.ew.textPtr,
+ TkTextChanged(ewPtr->body.ew.sharedTextPtr, NULL, &index, &index);
+ TkTextInvalidateLineMetrics(ewPtr->body.ew.sharedTextPtr, NULL,
index.linePtr, 0, TK_TEXT_INVALIDATE_ONLY);
}
/*
*--------------------------------------------------------------
*
+ * TkTextWinFreeClient --
+ *
+ * Free up the hash entry and client information for a
+ * given embedded window.
+ *
+ * It is assumed the caller will manage the linked list
+ * of clients associated with the relevant TkTextSegment.
+ *
+ * Results:
+ * Nothing.
+ *
+ * Side effects:
+ * The embedded window information for a single client is deleted,
+ * if it exists, and any resources associated with it are released.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+TkTextWinFreeClient(hPtr, client)
+ Tcl_HashEntry *hPtr; /* Hash entry corresponding to
+ * this client, or NULL */
+ TkTextEmbWindowClient *client; /* Client data structure, with
+ * the 'tkwin' field to be
+ * cleaned up. */
+{
+ if (hPtr != NULL) {
+ /*
+ * (It's possible for there to be no hash table entry
+ * for this window, if an error occurred while creating
+ * the window segment but before the window got added to
+ * the table)
+ */
+
+ Tcl_DeleteHashEntry(hPtr);
+ }
+
+ /*
+ * Delete the event handler for the window before destroying
+ * the window, so that EmbWinStructureProc doesn't get called
+ * (we'll already do everything that it would have done, and
+ * it will just get confused).
+ */
+
+ if (client->tkwin != NULL) {
+ Tk_DeleteEventHandler(client->tkwin, StructureNotifyMask,
+ EmbWinStructureProc, (ClientData) client);
+ Tk_DestroyWindow(client->tkwin);
+ }
+ Tcl_CancelIdleCall(EmbWinDelayedUnmap, (ClientData) client);
+ /* Free up this client */
+ ckfree((char *) client);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
* EmbWinDeleteProc --
*
* This procedure is invoked by the text B-tree code whenever
@@ -582,36 +741,28 @@ EmbWinDeleteProc(ewPtr, linePtr, treeGone)
* being deleted, so everything must
* get cleaned up. */
{
- Tcl_HashEntry *hPtr;
-
- if (ewPtr->body.ew.tkwin != NULL) {
- hPtr = Tcl_FindHashEntry(&ewPtr->body.ew.textPtr->windowTable,
- Tk_PathName(ewPtr->body.ew.tkwin));
- if (hPtr != NULL) {
- /*
- * (It's possible for there to be no hash table entry for this
- * window, if an error occurred while creating the window segment
- * but before the window got added to the table)
- */
-
- Tcl_DeleteHashEntry(hPtr);
- }
-
- /*
- * Delete the event handler for the window before destroying
- * the window, so that EmbWinStructureProc doesn't get called
- * (we'll already do everything that it would have done, and
- * it will just get confused).
- */
-
- Tk_DeleteEventHandler(ewPtr->body.ew.tkwin, StructureNotifyMask,
- EmbWinStructureProc, (ClientData) ewPtr);
- Tk_DestroyWindow(ewPtr->body.ew.tkwin);
+ TkTextEmbWindowClient *client;
+ client = ewPtr->body.ew.clients;
+
+ while (client != NULL) {
+ TkTextEmbWindowClient *next = client->next;
+ Tcl_HashEntry *hPtr = NULL;
+
+ if (client->tkwin != NULL) {
+ hPtr = Tcl_FindHashEntry(&ewPtr->body.ew.sharedTextPtr->windowTable,
+ Tk_PathName(client->tkwin));
+ }
+ TkTextWinFreeClient(hPtr, client);
+ client = next;
}
- Tcl_CancelIdleCall(EmbWinDelayedUnmap, (ClientData) ewPtr);
+ ewPtr->body.ew.clients = NULL;
+
Tk_FreeConfigOptions((char *) &ewPtr->body.ew, ewPtr->body.ew.optionTable,
- ewPtr->body.ew.tkwin);
+ NULL);
+
+ /* Free up all memory allocated */
ckfree((char *) ewPtr);
+
return 0;
}
@@ -683,17 +834,72 @@ EmbWinLayoutProc(textPtr, indexPtr, ewPtr, offset, maxX, maxChars,
* been set by the caller. */
{
int width, height;
-
+ TkTextEmbWindowClient *client;
+
if (offset != 0) {
Tcl_Panic("Non-zero offset in EmbWinLayoutProc");
}
+ client = EmbWinGetClient(textPtr, ewPtr);
+ if (client == NULL) {
+ ewPtr->body.ew.tkwin = NULL;
+ } else {
+ ewPtr->body.ew.tkwin = client->tkwin;
+ }
+
if ((ewPtr->body.ew.tkwin == NULL) && (ewPtr->body.ew.create != NULL)) {
int code, new;
Tcl_DString name;
Tk_Window ancestor;
Tcl_HashEntry *hPtr;
+ CONST char *before;
+ CONST char *string;
+ Tcl_DString buf;
+ Tcl_DString *dsPtr = NULL;
+ before = ewPtr->body.ew.create;
+
+ /*
+ * Find everything up to the next % character and append it
+ * to the result string.
+ */
+ string = before;
+ while (*string != 0) {
+ if (*string == '%') {
+ if (string[1] == '%' || string[1] == 'W') {
+ if (dsPtr == NULL) {
+ Tcl_DStringInit(&buf);
+ dsPtr = &buf;
+ }
+ if (string != before) {
+ Tcl_DStringAppend(dsPtr, before,
+ (int) (string-before));
+ before = string;
+ }
+ if (string[1] == '%') {
+ Tcl_DStringAppend(dsPtr, "%", 1);
+ } else {
+ /*
+ * Substitute string as proper Tcl
+ * list element.
+ */
+ int spaceNeeded, cvtFlags, length;
+ CONST char *str = Tk_PathName(textPtr->tkwin);
+ spaceNeeded = Tcl_ScanElement(str, &cvtFlags);
+ length = Tcl_DStringLength(dsPtr);
+ Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
+ spaceNeeded = Tcl_ConvertElement(str,
+ Tcl_DStringValue(dsPtr) + length,
+ cvtFlags | TCL_DONT_USE_BRACES);
+ Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
+ }
+ before += 2;
+ string++;
+ }
+ }
+ string++;
+ }
+
/*
* The window doesn't currently exist. Create it by evaluating
* the creation script. The script must return the window's
@@ -702,7 +908,14 @@ EmbWinLayoutProc(textPtr, indexPtr, ewPtr, offset, maxX, maxChars,
* the window.
*/
- code = Tcl_GlobalEval(textPtr->interp, ewPtr->body.ew.create);
+ if (dsPtr != NULL) {
+ Tcl_DStringAppend(dsPtr, before,
+ (int) (string-before));
+ code = Tcl_GlobalEval(textPtr->interp, Tcl_DStringValue(dsPtr));
+ Tcl_DStringFree(dsPtr);
+ } else {
+ code = Tcl_GlobalEval(textPtr->interp, ewPtr->body.ew.create);
+ }
if (code != TCL_OK) {
createError:
Tcl_BackgroundError(textPtr->interp);
@@ -735,10 +948,27 @@ EmbWinLayoutProc(textPtr, indexPtr, ewPtr, offset, maxX, maxChars,
|| (textPtr->tkwin == ewPtr->body.ew.tkwin)) {
goto badMaster;
}
- Tk_ManageGeometry(ewPtr->body.ew.tkwin, &textGeomType,
- (ClientData) ewPtr);
- Tk_CreateEventHandler(ewPtr->body.ew.tkwin, StructureNotifyMask,
- EmbWinStructureProc, (ClientData) ewPtr);
+
+ if (client == NULL) {
+ /*
+ * We just used a '-create' script to make a new window,
+ * which we now need to add to our client list.
+ */
+ client = (TkTextEmbWindowClient*) ckalloc(sizeof(TkTextEmbWindowClient));
+ client->next = ewPtr->body.ew.clients;
+ client->textPtr = textPtr;
+ client->tkwin = NULL;
+ client->chunkCount = 0;
+ client->displayed = 0;
+ client->parent = ewPtr;
+ ewPtr->body.ew.clients = client;
+ }
+
+ client->tkwin = ewPtr->body.ew.tkwin;
+ Tk_ManageGeometry(client->tkwin, &textGeomType,
+ (ClientData) client);
+ Tk_CreateEventHandler(client->tkwin, StructureNotifyMask,
+ EmbWinStructureProc, (ClientData) client);
/*
* Special trick! Must enter into the hash table *after*
@@ -748,8 +978,8 @@ EmbWinLayoutProc(textPtr, indexPtr, ewPtr, offset, maxX, maxChars,
* entry.
*/
- hPtr = Tcl_CreateHashEntry(&textPtr->windowTable,
- Tk_PathName(ewPtr->body.ew.tkwin), &new);
+ hPtr = Tcl_CreateHashEntry(&textPtr->sharedTextPtr->windowTable,
+ Tk_PathName(client->tkwin), &new);
Tcl_SetHashValue(hPtr, ewPtr);
}
@@ -792,7 +1022,9 @@ EmbWinLayoutProc(textPtr, indexPtr, ewPtr, offset, maxX, maxChars,
chunkPtr->breakIndex = -1;
chunkPtr->breakIndex = 1;
chunkPtr->clientData = (ClientData) ewPtr;
- ewPtr->body.ew.chunkCount += 1;
+ if (client != NULL) {
+ client->chunkCount += 1;
+ }
return 1;
}
@@ -847,8 +1079,9 @@ EmbWinCheckProc(ewPtr, linePtr)
*/
void
-TkTextEmbWinDisplayProc(chunkPtr, x, y, lineHeight, baseline,
+TkTextEmbWinDisplayProc(textPtr, chunkPtr, x, y, lineHeight, baseline,
display, dst, screenY)
+ TkText *textPtr; /* Information about text widget. */
TkTextDispChunk *chunkPtr; /* Chunk that is to be drawn. */
int x; /* X-position in dst at which to
* draw this chunk (differs from
@@ -867,17 +1100,20 @@ TkTextEmbWinDisplayProc(chunkPtr, x, y, lineHeight, baseline,
int screenY; /* Y-coordinate in text window that
* corresponds to y. */
{
- TkTextSegment *ewPtr = (TkTextSegment *) chunkPtr->clientData;
int lineX, windowX, windowY, width, height;
Tk_Window tkwin;
- TkText *textPtr;
+ TkTextSegment *ewPtr = (TkTextSegment*) chunkPtr->clientData;
+ TkTextEmbWindowClient *client = EmbWinGetClient(textPtr, ewPtr);
+
+ if (client == NULL) {
+ return;
+ }
- tkwin = ewPtr->body.ew.tkwin;
+ tkwin = client->tkwin;
if (tkwin == NULL) {
return;
}
- textPtr = ewPtr->body.ew.textPtr;
if ((x + chunkPtr->width) <= 0) {
/*
* The window is off-screen; just unmap it.
@@ -896,8 +1132,8 @@ TkTextEmbWinDisplayProc(chunkPtr, x, y, lineHeight, baseline,
* into account the align and stretch values for the window.
*/
- EmbWinBboxProc(chunkPtr, 0, screenY, lineHeight, baseline, &lineX,
- &windowY, &width, &height);
+ EmbWinBboxProc(textPtr, chunkPtr, 0, screenY, lineHeight, baseline,
+ &lineX, &windowY, &width, &height);
windowX = lineX - chunkPtr->x + x;
if (textPtr->tkwin == Tk_Parent(tkwin)) {
@@ -916,7 +1152,7 @@ TkTextEmbWinDisplayProc(chunkPtr, x, y, lineHeight, baseline,
* Mark the window as displayed so that it won't get unmapped.
*/
- ewPtr->body.ew.displayed = 1;
+ client->displayed = 1;
}
/*
@@ -943,10 +1179,13 @@ EmbWinUndisplayProc(textPtr, chunkPtr)
* widget. */
TkTextDispChunk *chunkPtr; /* Chunk that is about to be freed. */
{
- TkTextSegment *ewPtr = (TkTextSegment *) chunkPtr->clientData;
-
- ewPtr->body.ew.chunkCount--;
- if (ewPtr->body.ew.chunkCount == 0) {
+ TkTextSegment *ewPtr = (TkTextSegment*) chunkPtr->clientData;
+ TkTextEmbWindowClient *client = EmbWinGetClient(textPtr, ewPtr);
+
+ if (client == NULL) return;
+
+ client->chunkCount--;
+ if (client->chunkCount == 0) {
/*
* Don't unmap the window immediately, since there's a good chance
* that it will immediately be redisplayed, perhaps even in the
@@ -955,8 +1194,8 @@ EmbWinUndisplayProc(textPtr, chunkPtr)
* event that the unmap becomes unnecessary.
*/
- ewPtr->body.ew.displayed = 0;
- Tcl_DoWhenIdle(EmbWinDelayedUnmap, (ClientData) ewPtr);
+ client->displayed = 0;
+ Tcl_DoWhenIdle(EmbWinDelayedUnmap, (ClientData) client);
}
}
@@ -984,8 +1223,9 @@ EmbWinUndisplayProc(textPtr, chunkPtr)
*/
static void
-EmbWinBboxProc(chunkPtr, index, y, lineHeight, baseline, xPtr, yPtr,
+EmbWinBboxProc(textPtr, chunkPtr, index, y, lineHeight, baseline, xPtr, yPtr,
widthPtr, heightPtr)
+ TkText *textPtr; /* Information about text widget. */
TkTextDispChunk *chunkPtr; /* Chunk containing desired char. */
int index; /* Index of desired character within
* the chunk. */
@@ -1001,10 +1241,15 @@ EmbWinBboxProc(chunkPtr, index, y, lineHeight, baseline, xPtr, yPtr,
int *heightPtr; /* Gets filled in with height of
* window, in pixels. */
{
- TkTextSegment *ewPtr = (TkTextSegment *) chunkPtr->clientData;
Tk_Window tkwin;
+ TkTextSegment *ewPtr = (TkTextSegment*) chunkPtr->clientData;
+ TkTextEmbWindowClient *client = EmbWinGetClient(textPtr, ewPtr);
- tkwin = ewPtr->body.ew.tkwin;
+ if (client == NULL) {
+ tkwin = NULL;
+ } else {
+ tkwin = client->tkwin;
+ }
if (tkwin != NULL) {
*widthPtr = Tk_ReqWidth(tkwin);
*heightPtr = Tk_ReqHeight(tkwin);
@@ -1060,14 +1305,15 @@ EmbWinDelayedUnmap(clientData)
ClientData clientData; /* Token for the window to
* be unmapped. */
{
- TkTextSegment *ewPtr = (TkTextSegment *) clientData;
+ TkTextEmbWindowClient *client = (TkTextEmbWindowClient*) clientData;
+ TkTextSegment *ewPtr = client->parent;
- if (!ewPtr->body.ew.displayed && (ewPtr->body.ew.tkwin != NULL)) {
- if (ewPtr->body.ew.textPtr->tkwin != Tk_Parent(ewPtr->body.ew.tkwin)) {
- Tk_UnmaintainGeometry(ewPtr->body.ew.tkwin,
- ewPtr->body.ew.textPtr->tkwin);
+ if (!client->displayed && (client->tkwin != NULL)) {
+ if (client->textPtr->tkwin != Tk_Parent(client->tkwin)) {
+ Tk_UnmaintainGeometry(client->tkwin,
+ client->textPtr->tkwin);
} else {
- Tk_UnmapWindow(ewPtr->body.ew.tkwin);
+ Tk_UnmapWindow(client->tkwin);
}
}
}
@@ -1101,13 +1347,55 @@ TkTextWindowIndex(textPtr, name, indexPtr)
Tcl_HashEntry *hPtr;
TkTextSegment *ewPtr;
- hPtr = Tcl_FindHashEntry(&textPtr->windowTable, name);
+ hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->windowTable, name);
if (hPtr == NULL) {
return 0;
}
ewPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
- indexPtr->tree = textPtr->tree;
+ indexPtr->tree = textPtr->sharedTextPtr->tree;
indexPtr->linePtr = ewPtr->body.ew.linePtr;
indexPtr->byteIndex = TkTextSegToOffset(ewPtr, indexPtr->linePtr);
return 1;
}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * EmbWinGetClient --
+ *
+ * Given a text widget and a segment which contains an
+ * embedded window, find the text-widget specific
+ * information about the embedded window, if any.
+ *
+ * This procedure performs a completely linear lookup
+ * for a matching data structure. If we envisage using
+ * this code with dozens of peer widgets, then performance
+ * could become an issue and a more sophisticated lookup
+ * mechanism might be desirable.
+ *
+ * Results:
+ * NULL if no widget-specific info exists, otherwise
+ * the structure is returned.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+static TkTextEmbWindowClient*
+EmbWinGetClient(textPtr, ewPtr)
+ CONST TkText *textPtr; /* Information about text widget. */
+ TkTextSegment *ewPtr; /* Segment containing embedded
+ * window. */
+{
+ TkTextEmbWindowClient *client = ewPtr->body.ew.clients;
+ while (client != NULL) {
+ if (client->textPtr == textPtr) {
+ return client;
+ }
+ client = client->next;
+ }
+ return NULL;
+}
+
diff --git a/generic/tkUndo.c b/generic/tkUndo.c
index a1f5284..881bf28 100644
--- a/generic/tkUndo.c
+++ b/generic/tkUndo.c
@@ -8,13 +8,13 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tkUndo.c,v 1.4 2004/06/09 19:18:14 dkf Exp $
+ * RCS: @(#) $Id: tkUndo.c,v 1.5 2004/09/10 12:13:42 vincentdarley Exp $
*/
#include "tkUndo.h"
-static int UndoScriptsEvaluate _ANSI_ARGS_ ((Tcl_Interp *interp,
- Tcl_Obj *objPtr, TkUndoAtomType type));
+static int EvaluateActionList _ANSI_ARGS_ ((Tcl_Interp *interp,
+ TkUndoSubAtom *action));
/*
@@ -122,8 +122,29 @@ TkUndoClearStack(stack)
while ((elem = TkUndoPopStack(stack)) != NULL) {
if (elem->type != TK_UNDO_SEPARATOR) {
- Tcl_DecrRefCount(elem->apply);
- Tcl_DecrRefCount(elem->revert);
+ TkUndoSubAtom *sub;
+
+ sub = elem->apply;
+ while (sub->next != NULL) {
+ TkUndoSubAtom *next = sub->next;
+
+ if (sub->action != NULL) {
+ Tcl_DecrRefCount(sub->action);
+ }
+ ckfree((char *)sub);
+ sub = next;
+ }
+ sub = elem->revert;
+ while (sub->next != NULL) {
+ TkUndoSubAtom *next = sub->next;
+
+ if (sub->action != NULL) {
+ Tcl_DecrRefCount(sub->action);
+ }
+ ckfree((char *)sub);
+ sub = next;
+ }
+ sub = elem->revert;
}
ckfree((char *)elem);
}
@@ -149,29 +170,155 @@ TkUndoClearStack(stack)
*/
void
-TkUndoPushAction(stack, actionScript, revertScript, isList)
+TkUndoPushAction(stack, apply, revert)
TkUndoRedoStack *stack; /* An Undo or Redo stack */
- Tcl_Obj *actionScript; /* The script to get the action (redo) */
- Tcl_Obj *revertScript; /* The script to revert the action (undo) */
- int isList; /* Are the given objects lists of scripts? */
+ TkUndoSubAtom *apply;
+ TkUndoSubAtom *revert;
{
TkUndoAtom *atom;
atom = (TkUndoAtom *) ckalloc(sizeof(TkUndoAtom));
- if (isList) {
- atom->type = TK_UNDO_ACTION_LIST;
- } else {
- atom->type = TK_UNDO_ACTION;
+ atom->type = TK_UNDO_ACTION;
+ atom->apply = apply;
+ atom->revert = revert;
+
+ TkUndoPushStack(&stack->undoStack, atom);
+ TkUndoClearStack(&stack->redoStack);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkUndoMakeCmdSubAtom
+ *
+ * Create a new undo/redo step which must later be place into an
+ * undo stack with TkUndoPushAction. This sub-atom, if evaluated,
+ * will take the given command (if non-NULL), find its full Tcl
+ * command string, and then evaluate that command with the list
+ * elements of 'actionScript' appended.
+ *
+ * If 'subAtomList' is non-NULL, the newly created sub-atom
+ * is added onto the end of the linked list of which
+ * 'subAtomList' is a part. This makes it easy to build up
+ * a sequence of actions which will be pushed in one step.
+ *
+ * A refCount is retained on 'actionScript'.
+ *
+ * Note: if the undo stack can persist for longer than the
+ * Tcl_Command provided, the stack will cause crashes when
+ * actions are evaluated. In this case the 'command' argument
+ * should not be used. This is the case with peer text widgets,
+ * for example.
+ *
+ * Results:
+ * The newly created subAtom is returned. It must be passed
+ * to TkUndoPushAction otherwise a memory leak will result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+TkUndoSubAtom *
+TkUndoMakeCmdSubAtom(command, actionScript, subAtomList)
+ Tcl_Command command; /* Tcl command token for actions, may
+ * be NULL if not needed. */
+ Tcl_Obj *actionScript; /* The script to append to the command
+ * to perform the action (may be
+ * NULL if the command is not-null). */
+ TkUndoSubAtom *subAtomList; /* Add to the end of this list of
+ * actions if non-NULL */
+{
+ TkUndoSubAtom *atom;
+
+ if (command == NULL && actionScript == NULL) {
+ Tcl_Panic("NULL command and actionScript in TkUndoMakeCmdSubAtom");
+ }
+
+ atom = (TkUndoSubAtom *) ckalloc(sizeof(TkUndoSubAtom));
+ atom->command = command;
+ atom->funcPtr = NULL;
+ atom->clientData = NULL;
+ atom->next = NULL;
+ atom->action = actionScript;
+ if (atom->action != NULL) {
+ Tcl_IncrRefCount(atom->action);
+ }
+
+ if (subAtomList != NULL) {
+ while (subAtomList->next != NULL) {
+ subAtomList = subAtomList->next;
+ }
+ subAtomList->next = atom;
}
+ return atom;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkUndoMakeSubAtom
+ *
+ * Create a new undo/redo step which must later be place into an
+ * undo stack with TkUndoPushAction. This sub-atom, if evaluated,
+ * will take the given C-funcPtr (which must be non-NULL), and
+ * call it with three arguments: the undo stack's 'interp',
+ * the 'clientData' given and the 'actionScript'. The callback
+ * should return a standard Tcl return code (TCL_OK on success).
+ *
+ * If 'subAtomList' is non-NULL, the newly created sub-atom
+ * is added onto the end of the linked list of which
+ * 'subAtomList' is a part. This makes it easy to build up
+ * a sequence of actions which will be pushed in one step.
+ *
+ * A refCount is retained on 'actionScript'.
+ *
+ * Results:
+ * The newly created subAtom is returned. It must be passed
+ * to TkUndoPushAction otherwise a memory leak will result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
- atom->apply = actionScript;
- Tcl_IncrRefCount(atom->apply);
+TkUndoSubAtom *
+TkUndoMakeSubAtom(funcPtr, clientData, actionScript, subAtomList)
+ TkUndoProc *funcPtr; /* Callback function to perform
+ * the undo/redo. */
+ ClientData clientData; /* Data to pass to the callback
+ * function. */
+ Tcl_Obj *actionScript; /* Additional Tcl data to pass to
+ * the callback function (may be
+ * NULL). */
+ TkUndoSubAtom *subAtomList; /* Add to the end of this list of
+ * actions if non-NULL */
+{
+ TkUndoSubAtom *atom;
- atom->revert = revertScript;
- Tcl_IncrRefCount(atom->revert);
+ if (funcPtr == NULL) {
+ Tcl_Panic("NULL funcPtr in TkUndoMakeSubAtom");
+ }
- TkUndoPushStack(&(stack->undoStack), atom);
- TkUndoClearStack(&(stack->redoStack));
+ atom = (TkUndoSubAtom *) ckalloc(sizeof(TkUndoSubAtom));
+ atom->command = NULL;
+ atom->funcPtr = funcPtr;
+ atom->clientData = clientData;
+ atom->next = NULL;
+ atom->action = actionScript;
+ if (atom->action != NULL) {
+ Tcl_IncrRefCount(atom->action);
+ }
+
+ if (subAtomList != NULL) {
+ while (subAtomList->next != NULL) {
+ subAtomList = subAtomList->next;
+ }
+ subAtomList->next = atom;
+ }
+ return atom;
}
/*
@@ -239,7 +386,7 @@ TkUndoSetDepth(stack, maxdepth)
elem = stack->undoStack;
prevelem = NULL;
while (sepNumber <= stack->maxdepth) {
- if (elem!=NULL && elem->type==TK_UNDO_SEPARATOR) {
+ if (elem != NULL && elem->type == TK_UNDO_SEPARATOR) {
sepNumber++;
}
prevelem = elem;
@@ -247,7 +394,18 @@ TkUndoSetDepth(stack, maxdepth)
}
prevelem->next = NULL;
while (elem != NULL) {
+ TkUndoSubAtom *sub = elem->apply;
prevelem = elem;
+
+ while (sub->next != NULL) {
+ TkUndoSubAtom *next = sub->next;
+ if (sub->action != NULL) {
+ Tcl_DecrRefCount(sub->action);
+ }
+ ckfree((char *)sub);
+ sub = next;
+ }
+
elem = elem->next;
ckfree((char *) prevelem);
}
@@ -274,8 +432,8 @@ void
TkUndoClearStacks(stack)
TkUndoRedoStack *stack; /* An Undo/Redo stack */
{
- TkUndoClearStack(&(stack->undoStack));
- TkUndoClearStack(&(stack->redoStack));
+ TkUndoClearStack(&stack->undoStack);
+ TkUndoClearStack(&stack->redoStack);
stack->depth = 0;
}
@@ -300,7 +458,6 @@ TkUndoFreeStack(stack)
TkUndoRedoStack *stack; /* An Undo/Redo stack */
{
TkUndoClearStacks(stack);
- /* ckfree((TkUndoRedoStack *) stack); */
ckfree((char *) stack);
}
@@ -330,7 +487,7 @@ TkUndoInsertUndoSeparator(stack)
* int sepNumber = 0;
*/
- if (TkUndoInsertSeparator(&(stack->undoStack))) {
+ if (TkUndoInsertSeparator(&stack->undoStack)) {
stack->depth++;
TkUndoSetDepth(stack, stack->maxdepth);
#if 0
@@ -338,7 +495,7 @@ TkUndoInsertUndoSeparator(stack)
elem = stack->undoStack;
prevelem = NULL;
while (sepNumber < stack->depth) {
- if (elem!=NULL && elem->type==TK_UNDO_SEPARATOR) {
+ if (elem != NULL && elem->type == TK_UNDO_SEPARATOR) {
sepNumber++;
}
prevelem = elem;
@@ -380,31 +537,32 @@ TkUndoRevert(stack)
/* insert a separator on the undo and the redo stack */
TkUndoInsertUndoSeparator(stack);
- TkUndoInsertSeparator(&(stack->redoStack));
+ TkUndoInsertSeparator(&stack->redoStack);
/* Pop and skip the first separator if there is one*/
- elem = TkUndoPopStack(&(stack->undoStack));
+ elem = TkUndoPopStack(&stack->undoStack);
if (elem == NULL) {
return TCL_ERROR;
}
- if (elem!=NULL && elem->type==TK_UNDO_SEPARATOR) {
+ if (elem != NULL && elem->type == TK_UNDO_SEPARATOR) {
ckfree((char *) elem);
- elem = TkUndoPopStack(&(stack->undoStack));
+ elem = TkUndoPopStack(&stack->undoStack);
}
- while (elem!=NULL && elem->type!=TK_UNDO_SEPARATOR) {
- UndoScriptsEvaluate(stack->interp,elem->revert,elem->type);
+ while (elem != NULL && elem->type != TK_UNDO_SEPARATOR) {
+ /* Note that we currently ignore errors thrown here */
+ EvaluateActionList(stack->interp, elem->revert);
- TkUndoPushStack(&(stack->redoStack),elem);
- elem = TkUndoPopStack(&(stack->undoStack));
+ TkUndoPushStack(&stack->redoStack,elem);
+ elem = TkUndoPopStack(&stack->undoStack);
}
/* insert a separator on the redo stack */
- TkUndoInsertSeparator(&(stack->redoStack));
+ TkUndoInsertSeparator(&stack->redoStack);
stack->depth--;
@@ -434,31 +592,32 @@ TkUndoApply(stack)
/* insert a separator on the undo stack */
- TkUndoInsertSeparator(&(stack->undoStack));
+ TkUndoInsertSeparator(&stack->undoStack);
/* Pop and skip the first separator if there is one*/
- elem = TkUndoPopStack(&(stack->redoStack));
+ elem = TkUndoPopStack(&stack->redoStack);
if (elem == NULL) {
return TCL_ERROR;
}
- if (elem!=NULL && elem->type==TK_UNDO_SEPARATOR) {
+ if (elem != NULL && elem->type == TK_UNDO_SEPARATOR) {
ckfree((char *) elem);
- elem = TkUndoPopStack(&(stack->redoStack));
+ elem = TkUndoPopStack(&stack->redoStack);
}
- while (elem!=NULL && elem->type!=TK_UNDO_SEPARATOR) {
- UndoScriptsEvaluate(stack->interp,elem->apply,elem->type);
+ while (elem != NULL && elem->type != TK_UNDO_SEPARATOR) {
+ /* Note that we currently ignore errors thrown here */
+ EvaluateActionList(stack->interp, elem->apply);
- TkUndoPushStack(&(stack->undoStack), elem);
- elem = TkUndoPopStack(&(stack->redoStack));
+ TkUndoPushStack(&stack->undoStack, elem);
+ elem = TkUndoPopStack(&stack->redoStack);
}
/* insert a separator on the undo stack */
- TkUndoInsertSeparator(&(stack->undoStack));
+ TkUndoInsertSeparator(&stack->undoStack);
stack->depth++;
@@ -468,40 +627,53 @@ TkUndoApply(stack)
/*
*----------------------------------------------------------------------
*
- * UndoScriptsEvaluate --
- * Execute either a single script, or a set of scripts
+ * EvaluateActionList --
+ *
+ * Execute a linked list of undo/redo sub-atoms. If any sub-atom
+ * returns a non TCL_OK value, execution of subsequent sub-atoms
+ * is cancelled and the error returned immediately.
*
* Results:
* A Tcl status code
*
* Side effects:
- * None.
+ * The undo/redo subAtoms can perform arbitrary actions.
*
*----------------------------------------------------------------------
*/
static int
-UndoScriptsEvaluate(interp, objPtr, type)
- Tcl_Interp *interp;
- Tcl_Obj *objPtr;
- TkUndoAtomType type;
+EvaluateActionList(interp, action)
+ Tcl_Interp *interp; /* Interpreter to evaluate the action in. */
+ TkUndoSubAtom *action; /* Head of linked list of action steps
+ * to perform. */
{
- if (type == TK_UNDO_ACTION_LIST) {
- int objc;
- Tcl_Obj **objv;
- int res, i;
-
- res = Tcl_ListObjGetElements(interp, objPtr, &objc, &objv);
- if (res != TCL_OK) {
- return res;
- }
- for (i=0 ; i<objc ; i++) {
- res = Tcl_EvalObjEx(interp, objv[i], TCL_EVAL_GLOBAL);
- if (res != TCL_OK) {
- return res;
+ int result;
+
+ while (action != NULL) {
+ if (action->funcPtr != NULL) {
+ result = (*action->funcPtr)(interp, action->clientData,
+ action->action);
+ } else if (action->command != NULL) {
+ Tcl_Obj *cmdNameObj, *evalObj;
+
+ cmdNameObj = Tcl_NewObj();
+ evalObj = Tcl_NewObj();
+ Tcl_IncrRefCount(evalObj);
+ Tcl_GetCommandFullName(interp, action->command, cmdNameObj);
+ Tcl_ListObjAppendElement(NULL, evalObj, cmdNameObj);
+ if (action->action != NULL) {
+ Tcl_ListObjAppendList(NULL, evalObj, action->action);
}
+ result = Tcl_EvalObjEx(interp, evalObj, TCL_EVAL_GLOBAL);
+ Tcl_DecrRefCount(evalObj);
+ } else {
+ result = Tcl_EvalObjEx(interp, action->action, TCL_EVAL_GLOBAL);
+ }
+ if (result != TCL_OK) {
+ return result;
}
- return res;
+ action = action->next;
}
- return Tcl_EvalObjEx(interp, objPtr, TCL_EVAL_GLOBAL);
+ return result;
}
diff --git a/generic/tkUndo.h b/generic/tkUndo.h
index 9b072e6..3c34a0f 100644
--- a/generic/tkUndo.h
+++ b/generic/tkUndo.h
@@ -9,7 +9,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tkUndo.h,v 1.2 2003/05/19 13:04:24 vincentdarley Exp $
+ * RCS: @(#) $Id: tkUndo.h,v 1.3 2004/09/10 12:13:42 vincentdarley Exp $
*/
#ifndef _TKUNDO
@@ -24,27 +24,63 @@
# define TCL_STORAGE_CLASS DLLEXPORT
#endif
-/* enum definining the types used in an undo stack */
+/* Enum definining the types used in an undo stack */
typedef enum {
TK_UNDO_SEPARATOR, /* Marker */
- TK_UNDO_ACTION, /* Command */
- TK_UNDO_ACTION_LIST /* Command list */
+ TK_UNDO_ACTION /* Command */
} TkUndoAtomType;
-/* struct defining the basic undo/redo stack element */
+/*
+ * Callback proc type to carry out an undo or redo action
+ * via C code. (Actions can also be defined by Tcl scripts).
+ */
+
+typedef int (TkUndoProc) _ANSI_ARGS_((Tcl_Interp *interp,
+ ClientData clientData,
+ Tcl_Obj *objPtr));
+
+/*
+ * Struct defining a single action, one or more of which may
+ * be defined (and stored in a linked list) separately for each
+ * undo and redo action of an undo atom.
+ */
+
+typedef struct TkUndoSubAtom {
+ Tcl_Command command; /* Tcl token used to get the current
+ * Tcl command name which will be used
+ * to execute apply/revert scripts. If
+ * NULL then it is assumed the
+ * apply/revert scripts already contain
+ * everything. */
+ TkUndoProc *funcPtr; /* Function pointer for callback to
+ * perform undo/redo actions. */
+ ClientData clientData; /* data for 'funcPtr' */
+ Tcl_Obj *action; /* Command to apply the action that
+ * was taken */
+ struct TkUndoSubAtom *next; /* Pointer to the next element in the
+ * linked list */
+} TkUndoSubAtom;
+
+/*
+ * Struct representing a single undo+redo atom to be placed in
+ * the stack.
+ */
typedef struct TkUndoAtom {
TkUndoAtomType type; /* The type that will trigger the
* required action*/
- Tcl_Obj *apply; /* Command to apply the action that
- * was taken */
- Tcl_Obj *revert; /* The command to undo the action */
- struct TkUndoAtom * next; /* Pointer to the next element in the
+ TkUndoSubAtom *apply; /* Linked list of 'apply' actions to
+ * perform for this operation */
+ TkUndoSubAtom *revert; /* Linked list of 'revert' actions to
+ * perform for this operation */
+ struct TkUndoAtom *next; /* Pointer to the next element in the
* stack */
} TkUndoAtom;
-/* struct defining the basic undo/redo stack element */
+/*
+ * Struct defining a single undo+redo stack.
+ */
typedef struct TkUndoRedoStack {
TkUndoAtom * undoStack; /* The undo stack */
@@ -55,37 +91,37 @@ typedef struct TkUndoRedoStack {
int depth;
} TkUndoRedoStack;
-/* basic functions */
-
-EXTERN void TkUndoPushStack _ANSI_ARGS_((TkUndoAtom ** stack,
- TkUndoAtom * elem));
-
-EXTERN TkUndoAtom * TkUndoPopStack _ANSI_ARGS_((TkUndoAtom ** stack));
-
-EXTERN int TkUndoInsertSeparator _ANSI_ARGS_((TkUndoAtom ** stack));
-
-EXTERN void TkUndoClearStack _ANSI_ARGS_((TkUndoAtom ** stack));
-
-/* functions working on an undo/redo stack */
-
-EXTERN TkUndoRedoStack * TkUndoInitStack _ANSI_ARGS_((Tcl_Interp * interp,
- int maxdepth));
-
-EXTERN void TkUndoSetDepth _ANSI_ARGS_((TkUndoRedoStack * stack,
- int maxdepth));
-
-EXTERN void TkUndoClearStacks _ANSI_ARGS_((TkUndoRedoStack * stack));
-
-EXTERN void TkUndoFreeStack _ANSI_ARGS_((TkUndoRedoStack * stack));
-
-EXTERN void TkUndoInsertUndoSeparator _ANSI_ARGS_((TkUndoRedoStack * stack));
-
-EXTERN void TkUndoPushAction _ANSI_ARGS_((TkUndoRedoStack * stack,
- Tcl_Obj *actionScript, Tcl_Obj *revertScript, int isList));
-
-EXTERN int TkUndoRevert _ANSI_ARGS_((TkUndoRedoStack * stack));
-
-EXTERN int TkUndoApply _ANSI_ARGS_((TkUndoRedoStack * stack));
+/* Basic functions */
+
+EXTERN void TkUndoPushStack _ANSI_ARGS_((TkUndoAtom **stack,
+ TkUndoAtom *elem));
+EXTERN TkUndoAtom * TkUndoPopStack _ANSI_ARGS_((TkUndoAtom **stack));
+EXTERN int TkUndoInsertSeparator _ANSI_ARGS_((TkUndoAtom **stack));
+EXTERN void TkUndoClearStack _ANSI_ARGS_((TkUndoAtom **stack));
+
+/* Functions for working on an undo/redo stack */
+
+EXTERN TkUndoRedoStack * TkUndoInitStack _ANSI_ARGS_((Tcl_Interp *interp,
+ int maxdepth));
+EXTERN void TkUndoSetDepth _ANSI_ARGS_((TkUndoRedoStack *stack,
+ int maxdepth));
+EXTERN void TkUndoClearStacks _ANSI_ARGS_((TkUndoRedoStack *stack));
+EXTERN void TkUndoFreeStack _ANSI_ARGS_((TkUndoRedoStack *stack));
+EXTERN void TkUndoInsertUndoSeparator _ANSI_ARGS_((TkUndoRedoStack *stack));
+EXTERN TkUndoSubAtom * TkUndoMakeCmdSubAtom _ANSI_ARGS_((
+ Tcl_Command command,
+ Tcl_Obj *actionScript,
+ TkUndoSubAtom *subAtomList));
+EXTERN TkUndoSubAtom * TkUndoMakeSubAtom _ANSI_ARGS_((
+ TkUndoProc *funcPtr,
+ ClientData clientData,
+ Tcl_Obj *actionScript,
+ TkUndoSubAtom *subAtomList));
+EXTERN void TkUndoPushAction _ANSI_ARGS_((TkUndoRedoStack *stack,
+ TkUndoSubAtom *apply,
+ TkUndoSubAtom *revert));
+EXTERN int TkUndoRevert _ANSI_ARGS_((TkUndoRedoStack *stack));
+EXTERN int TkUndoApply _ANSI_ARGS_((TkUndoRedoStack *stack));
# undef TCL_STORAGE_CLASS
# define TCL_STORAGE_CLASS DLLIMPORT