summaryrefslogtreecommitdiffstats
path: root/generic/tkText.c
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/tkText.c
parent77f2c1e62ab0760dc6ee615d6bbcb81b11d76a6f (diff)
downloadtk-09324dada308a84a1d5ba8b14bff2a5ce8b6eaf9.zip
tk-09324dada308a84a1d5ba8b14bff2a5ce8b6eaf9.tar.gz
tk-09324dada308a84a1d5ba8b14bff2a5ce8b6eaf9.tar.bz2
text widget 'peer' subcommand -- TIP#169 implementation
Diffstat (limited to 'generic/tkText.c')
-rw-r--r--generic/tkText.c1632
1 files changed, 1292 insertions, 340 deletions
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);
+}