summaryrefslogtreecommitdiffstats
path: root/generic
diff options
context:
space:
mode:
Diffstat (limited to 'generic')
-rw-r--r--generic/tkText.c103
-rw-r--r--generic/tkText.h3
-rw-r--r--generic/tkTextDisp.c98
3 files changed, 198 insertions, 6 deletions
diff --git a/generic/tkText.c b/generic/tkText.c
index 6e982b0..0cb8431 100644
--- a/generic/tkText.c
+++ b/generic/tkText.c
@@ -397,6 +397,7 @@ static int TextSearchIndexInLine(const SearchSpec *searchSpecPtr,
static int TextPeerCmd(TkText *textPtr, Tcl_Interp *interp,
int objc, Tcl_Obj *const objv[]);
static TkUndoProc TextUndoRedoCallback;
+static void RunAfterSyncCmd(ClientData clientData);
/*
* Declarations of the three search procs required by the multi-line search
@@ -689,15 +690,16 @@ TextWidgetObjCmd(
static const char *optionStrings[] = {
"bbox", "cget", "compare", "configure", "count", "debug", "delete",
"dlineinfo", "dump", "edit", "get", "image", "index", "insert",
- "mark", "peer", "replace", "scan", "search", "see", "tag", "window",
- "xview", "yview", NULL
+ "mark", "peer", "pendingsync", "replace", "scan", "search",
+ "see", "sync", "tag", "window", "xview", "yview", 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_PEER, TEXT_REPLACE, TEXT_SCAN, TEXT_SEARCH, TEXT_SEE,
- TEXT_TAG, TEXT_WINDOW, TEXT_XVIEW, TEXT_YVIEW
+ TEXT_PEER, TEXT_PENDINGSYNC, TEXT_REPLACE, TEXT_SCAN,
+ TEXT_SEARCH, TEXT_SEE, TEXT_SYNC, TEXT_TAG, TEXT_WINDOW,
+ TEXT_XVIEW, TEXT_YVIEW
};
if (objc < 2) {
@@ -1372,6 +1374,16 @@ TextWidgetObjCmd(
case TEXT_PEER:
result = TextPeerCmd(textPtr, interp, objc, objv);
break;
+ case TEXT_PENDINGSYNC: {
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ result = TCL_ERROR;
+ goto done;
+ }
+ Tcl_SetObjResult(interp,
+ Tcl_NewBooleanObj(TkTextPendingsync(textPtr)));
+ break;
+ }
case TEXT_REPLACE: {
const TkTextIndex *indexFromPtr, *indexToPtr;
@@ -1485,6 +1497,39 @@ TextWidgetObjCmd(
case TEXT_SEE:
result = TkTextSeeCmd(textPtr, interp, objc, objv);
break;
+ case TEXT_SYNC: {
+ if (objc == 4) {
+ Tcl_Obj *cmd = objv[3];
+ const char *option = Tcl_GetString(objv[2]);
+ if (strncmp(option, "-command", objv[2]->length)) {
+ Tcl_AppendResult(interp, "wrong option \"", option, "\": should be \"-command\"", NULL);
+ result = TCL_ERROR;
+ goto done;
+ }
+ Tcl_IncrRefCount(cmd);
+ if (TkTextPendingsync(textPtr)) {
+ if (textPtr->afterSyncCmd) {
+ Tcl_DecrRefCount(textPtr->afterSyncCmd);
+ }
+ textPtr->afterSyncCmd = cmd;
+ } else {
+ textPtr->afterSyncCmd = cmd;
+ Tcl_DoWhenIdle(RunAfterSyncCmd, (ClientData) textPtr);
+ }
+ break;
+ } else if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?-command command?");
+ result = TCL_ERROR;
+ goto done;
+ }
+ if (textPtr->afterSyncCmd) {
+ Tcl_DecrRefCount(textPtr->afterSyncCmd);
+ }
+ textPtr->afterSyncCmd = NULL;
+ TkTextUpdateLineMetrics(textPtr, 1,
+ TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr), -1);
+ break;
+ }
case TEXT_TAG:
result = TkTextTagCmd(textPtr, interp, objc, objv);
break;
@@ -1970,6 +2015,10 @@ DestroyText(
textPtr->tkwin = NULL;
textPtr->refCount--;
Tcl_DeleteCommandFromToken(textPtr->interp, textPtr->widgetCmd);
+ if (textPtr->afterSyncCmd != 0){
+ Tcl_DecrRefCount(textPtr->afterSyncCmd);
+ textPtr->afterSyncCmd = 0;
+ }
if (textPtr->refCount == 0) {
ckfree((char *) textPtr);
}
@@ -6699,6 +6748,52 @@ TkpTesttextCmd(
}
/*
+ *----------------------------------------------------------------------
+ *
+ * RunAfterSyncCmd --
+ *
+ * This function is called by the event loop and excutes the command
+ * scheduled by [.text sync -command $cmd].
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Anything may happen, depending on $cmd contents.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+RunAfterSyncCmd(
+ ClientData clientData) /* Information about text widget. */
+{
+ register TkText *textPtr = (TkText *) clientData;
+ int code;
+
+ if ((textPtr->tkwin == NULL) || (textPtr->flags & DESTROYED)) {
+ /*
+ * The widget has been deleted. Don't do anything.
+ */
+
+ if (--textPtr->refCount == 0) {
+ ckfree((char *) textPtr);
+ }
+ return;
+ }
+
+ Tcl_Preserve((ClientData) textPtr->interp);
+ code = Tcl_EvalObjEx(textPtr->interp, textPtr->afterSyncCmd, TCL_EVAL_GLOBAL);
+ if (code == TCL_ERROR) {
+ Tcl_AddErrorInfo(textPtr->interp, "\n (text sync)");
+ Tcl_BackgroundError(textPtr->interp);
+ }
+ Tcl_Release((ClientData) textPtr->interp);
+ Tcl_DecrRefCount(textPtr->afterSyncCmd);
+ textPtr->afterSyncCmd = NULL;
+}
+
+/*
* Local Variables:
* mode: c
* c-basic-offset: 4
diff --git a/generic/tkText.h b/generic/tkText.h
index 6f5f153..ea8ce07 100644
--- a/generic/tkText.h
+++ b/generic/tkText.h
@@ -782,6 +782,8 @@ typedef struct TkText {
* statements. */
int autoSeparators; /* Non-zero means the separators will be
* inserted automatically. */
+ Tcl_Obj *afterSyncCmd; /* Command to be executed when lines are up to
+ * date */
} TkText;
/*
@@ -1124,6 +1126,7 @@ MODULE_SCOPE int TkTextMarkNameToIndex(TkText *textPtr,
MODULE_SCOPE void TkTextMarkSegToIndex(TkText *textPtr,
TkTextSegment *markPtr, TkTextIndex *indexPtr);
MODULE_SCOPE void TkTextEventuallyRepick(TkText *textPtr);
+MODULE_SCOPE Bool TkTextPendingsync(TkText *textPtr);
MODULE_SCOPE void TkTextPickCurrent(TkText *textPtr, XEvent *eventPtr);
MODULE_SCOPE void TkTextPixelIndex(TkText *textPtr, int x, int y,
TkTextIndex *indexPtr, int *nearest);
diff --git a/generic/tkTextDisp.c b/generic/tkTextDisp.c
index 133a7d7..807009b 100644
--- a/generic/tkTextDisp.c
+++ b/generic/tkTextDisp.c
@@ -590,6 +590,7 @@ static int TextGetScrollInfoObj(Tcl_Interp *interp,
Tcl_Obj *CONST objv[], double *dblPtr,
int *intPtr);
static void AsyncUpdateLineMetrics(ClientData clientData);
+static void GenerateWidgetViewSyncEvent(TkText *textPtr, Bool InSync);
static void AsyncUpdateYScrollbar(ClientData clientData);
static int IsStartOfNotMergedLine(TkText *textPtr,
CONST TkTextIndex *indexPtr);
@@ -2935,6 +2936,8 @@ AsyncUpdateLineMetrics(
lineNum = TkTextUpdateLineMetrics(textPtr, lineNum,
dInfoPtr->lastMetricUpdateLine, 256);
+ dInfoPtr->currentMetricUpdateLine = lineNum;
+
if (tkTextDebug) {
char buffer[2 * TCL_INTEGER_SPACE + 1];
@@ -2952,16 +2955,37 @@ AsyncUpdateLineMetrics(
/*
* We have looped over all lines, so we're done. We must release our
* refCount on the widget (the timer token was already set to NULL
- * above).
+ * above). If there is a registered aftersync command, run that first.
*/
+ if (textPtr->afterSyncCmd) {
+ int code;
+ Tcl_Preserve((ClientData) textPtr->interp);
+ code = Tcl_EvalObjEx(textPtr->interp, textPtr->afterSyncCmd,
+ TCL_EVAL_GLOBAL);
+ if (code == TCL_ERROR) {
+ Tcl_AddErrorInfo(textPtr->interp, "\n (text sync)");
+ Tcl_BackgroundError(textPtr->interp);
+ }
+ Tcl_Release((ClientData) textPtr->interp);
+ Tcl_DecrRefCount(textPtr->afterSyncCmd);
+ textPtr->afterSyncCmd = NULL;
+ }
+
+ /*
+ * Fire the <<WidgetViewSync>> event since the widget view is in sync
+ * with its internal data (actually it will be after the next trip
+ * through the event loop, because the widget redraws at idle-time).
+ */
+
+ GenerateWidgetViewSyncEvent(textPtr, 1);
+
textPtr->refCount--;
if (textPtr->refCount == 0) {
ckfree((char *) textPtr);
}
return;
}
- dInfoPtr->currentMetricUpdateLine = lineNum;
/*
* Re-arm the timer. We already have a refCount on the text widget so no
@@ -2975,6 +2999,45 @@ AsyncUpdateLineMetrics(
/*
*----------------------------------------------------------------------
*
+ * GenerateWidgetViewSyncEvent --
+ *
+ * Send the <<WidgetViewSync>> event related to the text widget
+ * line metrics asynchronous update.
+ * This is equivalent to:
+ * event generate $textWidget <<WidgetViewSync>> -detail $s
+ * where $s is the sync status: true (when the widget view is in
+ * sync with its internal data) or false (when it is not).
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * If corresponding bindings are present, they will trigger.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+GenerateWidgetViewSyncEvent(
+ TkText *textPtr, /* Information about text widget. */
+ Bool InSync) /* True if in sync, false otherwise */
+{
+ union {XEvent general; XVirtualEvent virtual;} event;
+
+ memset(&event, 0, sizeof(event));
+ event.general.xany.type = VirtualEvent;
+ event.general.xany.serial = NextRequest(Tk_Display(textPtr->tkwin));
+ event.general.xany.send_event = False;
+ event.general.xany.window = Tk_WindowId(textPtr->tkwin);
+ event.general.xany.display = Tk_Display(textPtr->tkwin);
+ event.virtual.name = Tk_GetUid("WidgetViewSync");
+ event.virtual.user_data = Tcl_NewBooleanObj(InSync);
+ Tk_HandleEvent(&event.general);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* TkTextUpdateLineMetrics --
*
* This function updates the pixel height calculations of a range of
@@ -3351,6 +3414,7 @@ TextInvalidateLineMetrics(
textPtr->refCount++;
dInfoPtr->lineUpdateTimer = Tcl_CreateTimerHandler(1,
AsyncUpdateLineMetrics, (ClientData) textPtr);
+ GenerateWidgetViewSyncEvent(textPtr, 0);
}
}
@@ -5062,6 +5126,7 @@ TkTextRelayoutWindow(
textPtr->refCount++;
dInfoPtr->lineUpdateTimer = Tcl_CreateTimerHandler(1,
AsyncUpdateLineMetrics, (ClientData) textPtr);
+ GenerateWidgetViewSyncEvent(textPtr, 0);
}
}
}
@@ -6065,6 +6130,35 @@ TkTextYviewCmd(
/*
*--------------------------------------------------------------
*
+ * TkTextPendingsync --
+ *
+ * This function checks if any line heights are not up-to-date.
+ *
+ * Results:
+ * Returns a boolean true if it is the case, or false if all line
+ * heights are up-to-date.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+Bool
+TkTextPendingsync(
+ TkText *textPtr) /* Information about text widget. */
+{
+ TextDInfo *dInfoPtr = textPtr->dInfoPtr;
+
+ return (
+ ((dInfoPtr->metricEpoch == -1) &&
+ (dInfoPtr->lastMetricUpdateLine == dInfoPtr->currentMetricUpdateLine)) ?
+ 0 : 1);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
* TkTextScanCmd --
*
* This function is invoked to process the "scan" option for the widget