summaryrefslogtreecommitdiffstats
path: root/generic
diff options
context:
space:
mode:
Diffstat (limited to 'generic')
-rw-r--r--generic/tkText.c56
-rw-r--r--generic/tkText.h2
-rw-r--r--generic/tkTextDisp.c98
3 files changed, 150 insertions, 6 deletions
diff --git a/generic/tkText.c b/generic/tkText.c
index 6e982b0..a2b7dde 100644
--- a/generic/tkText.c
+++ b/generic/tkText.c
@@ -689,15 +689,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 +1373,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 +1496,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 {
+ result = Tcl_EvalObjEx(interp, cmd, TCL_EVAL_GLOBAL);
+ Tcl_DecrRefCount(cmd);
+ }
+ 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 +2014,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);
}
diff --git a/generic/tkText.h b/generic/tkText.h
index 6f5f153..49ee479 100644
--- a/generic/tkText.h
+++ b/generic/tkText.h
@@ -782,6 +782,7 @@ 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 +1125,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 ef8d6f4..ba584ac 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 != NULL) {
+ int code;
+ Tcl_Preserve((ClientData)textPtr->interp);
+ code = Tcl_EvalObjEx(textPtr->interp, textPtr->afterSyncCmd, TCL_EVAL_GLOBAL);
+ if (code != TCL_OK && code != TCL_CONTINUE
+ && code != TCL_BREAK) {
+ Tcl_AddErrorInfo(textPtr->interp, "\n (text sync)");
+ Tcl_BackgroundError(textPtr->interp);
+ }
+ Tcl_Release((ClientData)textPtr->interp);
+ Tcl_DecrRefCount(textPtr->afterSyncCmd);
+ textPtr->afterSyncCmd = 0;
+ }
+
+ /*
+ * 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);
}
}
@@ -5055,6 +5119,7 @@ TkTextRelayoutWindow(
textPtr->refCount++;
dInfoPtr->lineUpdateTimer = Tcl_CreateTimerHandler(1,
AsyncUpdateLineMetrics, (ClientData) textPtr);
+ GenerateWidgetViewSyncEvent(textPtr, 0);
}
}
}
@@ -6058,6 +6123,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