summaryrefslogtreecommitdiffstats
path: root/tk8.6/generic/tkTextMark.c
diff options
context:
space:
mode:
Diffstat (limited to 'tk8.6/generic/tkTextMark.c')
-rw-r--r--tk8.6/generic/tkTextMark.c1027
1 files changed, 1027 insertions, 0 deletions
diff --git a/tk8.6/generic/tkTextMark.c b/tk8.6/generic/tkTextMark.c
new file mode 100644
index 0000000..6a41c77
--- /dev/null
+++ b/tk8.6/generic/tkTextMark.c
@@ -0,0 +1,1027 @@
+/*
+ * tkTextMark.c --
+ *
+ * This file contains the functions that implement marks for text
+ * widgets.
+ *
+ * Copyright (c) 1994 The Regents of the University of California.
+ * Copyright (c) 1994-1997 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution of
+ * this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "tkInt.h"
+#include "tkText.h"
+#include "tk3d.h"
+
+/*
+ * Macro that determines the size of a mark segment:
+ */
+
+#define MSEG_SIZE ((unsigned) (Tk_Offset(TkTextSegment, body) \
+ + sizeof(TkTextMark)))
+
+/*
+ * Forward references for functions defined in this file:
+ */
+
+static Tcl_Obj * GetMarkName(TkText *textPtr, TkTextSegment *segPtr);
+static void InsertUndisplayProc(TkText *textPtr,
+ TkTextDispChunk *chunkPtr);
+static int MarkDeleteProc(TkTextSegment *segPtr,
+ TkTextLine *linePtr, int treeGone);
+static TkTextSegment * MarkCleanupProc(TkTextSegment *segPtr,
+ TkTextLine *linePtr);
+static void MarkCheckProc(TkTextSegment *segPtr,
+ TkTextLine *linePtr);
+static int MarkLayoutProc(TkText *textPtr, TkTextIndex *indexPtr,
+ TkTextSegment *segPtr, int offset, int maxX,
+ int maxChars, int noCharsYet, TkWrapMode wrapMode,
+ TkTextDispChunk *chunkPtr);
+static int MarkFindNext(Tcl_Interp *interp,
+ TkText *textPtr, Tcl_Obj *markName);
+static int MarkFindPrev(Tcl_Interp *interp,
+ TkText *textPtr, Tcl_Obj *markName);
+
+
+/*
+ * The following structures declare the "mark" segment types. There are
+ * actually two types for marks, one with left gravity and one with right
+ * gravity. They are identical except for their gravity property.
+ */
+
+const Tk_SegType tkTextRightMarkType = {
+ "mark", /* name */
+ 0, /* leftGravity */
+ NULL, /* splitProc */
+ MarkDeleteProc, /* deleteProc */
+ MarkCleanupProc, /* cleanupProc */
+ NULL, /* lineChangeProc */
+ MarkLayoutProc, /* layoutProc */
+ MarkCheckProc /* checkProc */
+};
+
+const Tk_SegType tkTextLeftMarkType = {
+ "mark", /* name */
+ 1, /* leftGravity */
+ NULL, /* splitProc */
+ MarkDeleteProc, /* deleteProc */
+ MarkCleanupProc, /* cleanupProc */
+ NULL, /* lineChangeProc */
+ MarkLayoutProc, /* layoutProc */
+ MarkCheckProc /* checkProc */
+};
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkTextMarkCmd --
+ *
+ * This function is invoked to process the "mark" options of the widget
+ * command for text widgets. See the user documentation for details on
+ * what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+TkTextMarkCmd(
+ register TkText *textPtr, /* Information about text widget. */
+ Tcl_Interp *interp, /* Current interpreter. */
+ int objc, /* Number of arguments. */
+ Tcl_Obj *const objv[]) /* Argument objects. Someone else has already
+ * parsed this command enough to know that
+ * objv[1] is "mark". */
+{
+ Tcl_HashEntry *hPtr;
+ TkTextSegment *markPtr;
+ Tcl_HashSearch search;
+ TkTextIndex index;
+ const Tk_SegType *newTypePtr;
+ int optionIndex;
+ static const char *const markOptionStrings[] = {
+ "gravity", "names", "next", "previous", "set", "unset", NULL
+ };
+ enum markOptions {
+ MARK_GRAVITY, MARK_NAMES, MARK_NEXT, MARK_PREVIOUS, MARK_SET,
+ MARK_UNSET
+ };
+
+ if (objc < 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "option ?arg ...?");
+ return TCL_ERROR;
+ }
+ if (Tcl_GetIndexFromObjStruct(interp, objv[2], markOptionStrings,
+ sizeof(char *), "mark option", 0, &optionIndex) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ switch ((enum markOptions) optionIndex) {
+ case MARK_GRAVITY: {
+ char c;
+ int length;
+ const char *str;
+
+ if (objc < 4 || objc > 5) {
+ Tcl_WrongNumArgs(interp, 3, objv, "markName ?gravity?");
+ return TCL_ERROR;
+ }
+ str = Tcl_GetStringFromObj(objv[3], &length);
+ if (length == 6 && !strcmp(str, "insert")) {
+ markPtr = textPtr->insertMarkPtr;
+ } else if (length == 7 && !strcmp(str, "current")) {
+ markPtr = textPtr->currentMarkPtr;
+ } else {
+ hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, str);
+ if (hPtr == NULL) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "there is no mark named \"%s\"", str));
+ Tcl_SetErrorCode(interp, "TK", "LOOKUP", "TEXT_MARK", str,
+ NULL);
+ return TCL_ERROR;
+ }
+ markPtr = Tcl_GetHashValue(hPtr);
+ }
+ if (objc == 4) {
+ const char *typeStr;
+
+ if (markPtr->typePtr == &tkTextRightMarkType) {
+ typeStr = "right";
+ } else {
+ typeStr = "left";
+ }
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(typeStr, -1));
+ return TCL_OK;
+ }
+ str = Tcl_GetStringFromObj(objv[4],&length);
+ c = str[0];
+ if ((c == 'l') && (strncmp(str, "left", (unsigned) length) == 0)) {
+ newTypePtr = &tkTextLeftMarkType;
+ } else if ((c == 'r') &&
+ (strncmp(str, "right", (unsigned) length) == 0)) {
+ newTypePtr = &tkTextRightMarkType;
+ } else {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "bad mark gravity \"%s\": must be left or right", str));
+ Tcl_SetErrorCode(interp, "TK", "VALUE", "MARK_GRAVITY", NULL);
+ return TCL_ERROR;
+ }
+ TkTextMarkSegToIndex(textPtr, markPtr, &index);
+ TkBTreeUnlinkSegment(markPtr, markPtr->body.mark.linePtr);
+ markPtr->typePtr = newTypePtr;
+ TkBTreeLinkSegment(markPtr, &index);
+ break;
+ }
+ case MARK_NAMES: {
+ Tcl_Obj *resultObj;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 3, objv, NULL);
+ return TCL_ERROR;
+ }
+ resultObj = Tcl_NewObj();
+ Tcl_ListObjAppendElement(NULL, resultObj, Tcl_NewStringObj(
+ "insert", -1));
+ Tcl_ListObjAppendElement(NULL, resultObj, Tcl_NewStringObj(
+ "current", -1));
+ for (hPtr = Tcl_FirstHashEntry(&textPtr->sharedTextPtr->markTable,
+ &search); hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
+ Tcl_ListObjAppendElement(NULL, resultObj, Tcl_NewStringObj(
+ Tcl_GetHashKey(&textPtr->sharedTextPtr->markTable, hPtr),
+ -1));
+ }
+ Tcl_SetObjResult(interp, resultObj);
+ break;
+ }
+ case MARK_NEXT:
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 3, objv, "index");
+ return TCL_ERROR;
+ }
+ return MarkFindNext(interp, textPtr, objv[3]);
+ case MARK_PREVIOUS:
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 3, objv, "index");
+ return TCL_ERROR;
+ }
+ return MarkFindPrev(interp, textPtr, objv[3]);
+ case MARK_SET:
+ if (objc != 5) {
+ Tcl_WrongNumArgs(interp, 3, objv, "markName index");
+ return TCL_ERROR;
+ }
+ if (TkTextGetObjIndex(interp, textPtr, objv[4], &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ TkTextSetMark(textPtr, Tcl_GetString(objv[3]), &index);
+ return TCL_OK;
+ case MARK_UNSET: {
+ int i;
+
+ for (i = 3; i < objc; i++) {
+ hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable,
+ Tcl_GetString(objv[i]));
+ if (hPtr != NULL) {
+ markPtr = Tcl_GetHashValue(hPtr);
+
+ /*
+ * Special case not needed with peer widgets.
+ */
+
+ if ((markPtr == textPtr->insertMarkPtr)
+ || (markPtr == textPtr->currentMarkPtr)) {
+ continue;
+ }
+ TkBTreeUnlinkSegment(markPtr, markPtr->body.mark.linePtr);
+ Tcl_DeleteHashEntry(hPtr);
+ ckfree(markPtr);
+ }
+ }
+ break;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkTextSetMark --
+ *
+ * Set a mark to a particular position, creating a new mark if one
+ * doesn't already exist.
+ *
+ * Results:
+ * The return value is a pointer to the mark that was just set.
+ *
+ * Side effects:
+ * A new mark is created, or an existing mark is moved.
+ *
+ *----------------------------------------------------------------------
+ */
+
+TkTextSegment *
+TkTextSetMark(
+ TkText *textPtr, /* Text widget in which to create mark. */
+ const char *name, /* Name of mark to set. */
+ TkTextIndex *indexPtr) /* Where to set mark. */
+{
+ Tcl_HashEntry *hPtr = NULL;
+ TkTextSegment *markPtr;
+ TkTextIndex insertIndex;
+ int isNew, widgetSpecific;
+
+ if (!strcmp(name, "insert")) {
+ widgetSpecific = 1;
+ markPtr = textPtr->insertMarkPtr;
+ isNew = (markPtr == NULL ? 1 : 0);
+ } else if (!strcmp(name, "current")) {
+ widgetSpecific = 2;
+ markPtr = textPtr->currentMarkPtr;
+ isNew = (markPtr == NULL ? 1 : 0);
+ } else {
+ widgetSpecific = 0;
+ hPtr = Tcl_CreateHashEntry(&textPtr->sharedTextPtr->markTable, name,
+ &isNew);
+ markPtr = Tcl_GetHashValue(hPtr);
+ }
+ if (!isNew) {
+ /*
+ * If this is the insertion point that's being moved, be sure to force
+ * a display update at the old position. Also, don't let the insertion
+ * cursor be after the final newline of the file.
+ */
+
+ if (markPtr == textPtr->insertMarkPtr) {
+ TkTextIndex index, index2;
+ int nblines;
+
+ 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(NULL, textPtr, &index, &index2);
+
+ /*
+ * The number of lines in the widget is zero if and only if it is
+ * a partial peer with -startline == -endline, i.e. an empty
+ * peer. In this case the mark shall be set exactly at the given
+ * index, and not one character backwards (bug 3487407).
+ */
+
+ nblines = TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr);
+ if ((TkBTreeLinesTo(textPtr, indexPtr->linePtr) == nblines)
+ && (nblines > 0)) {
+ TkTextIndexBackChars(NULL,indexPtr, 1, &insertIndex,
+ COUNT_INDICES);
+ indexPtr = &insertIndex;
+ }
+ }
+ TkBTreeUnlinkSegment(markPtr, markPtr->body.mark.linePtr);
+ } else {
+ markPtr = ckalloc(MSEG_SIZE);
+ markPtr->typePtr = &tkTextRightMarkType;
+ markPtr->size = 0;
+ markPtr->body.mark.textPtr = textPtr;
+ markPtr->body.mark.linePtr = indexPtr->linePtr;
+ markPtr->body.mark.hPtr = hPtr;
+ if (widgetSpecific == 0) {
+ Tcl_SetHashValue(hPtr, markPtr);
+ } else if (widgetSpecific == 1) {
+ textPtr->insertMarkPtr = markPtr;
+ } else {
+ textPtr->currentMarkPtr = markPtr;
+ }
+ }
+ TkBTreeLinkSegment(markPtr, indexPtr);
+
+ /*
+ * If the mark is the insertion cursor, then update the screen at the
+ * mark's new location.
+ */
+
+ if (markPtr == textPtr->insertMarkPtr) {
+ TkTextIndex index2;
+
+ TkTextIndexForwChars(NULL, indexPtr, 1, &index2, COUNT_INDICES);
+
+ /*
+ * While we wish to redisplay, no heights have changed, so no need to
+ * call TkTextInvalidateLineMetrics
+ */
+
+ TkTextChanged(NULL, textPtr, indexPtr, &index2);
+ }
+ return markPtr;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkTextMarkSegToIndex --
+ *
+ * Given a segment that is a mark, create an index that refers to the
+ * next text character (or other text segment with non-zero size) after
+ * the mark.
+ *
+ * Results:
+ * *IndexPtr is filled in with index information.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+TkTextMarkSegToIndex(
+ TkText *textPtr, /* Text widget containing mark. */
+ TkTextSegment *markPtr, /* Mark segment. */
+ TkTextIndex *indexPtr) /* Index information gets stored here. */
+{
+ TkTextSegment *segPtr;
+
+ indexPtr->tree = textPtr->sharedTextPtr->tree;
+ indexPtr->linePtr = markPtr->body.mark.linePtr;
+ indexPtr->byteIndex = 0;
+ for (segPtr = indexPtr->linePtr->segPtr; segPtr != markPtr;
+ segPtr = segPtr->nextPtr) {
+ indexPtr->byteIndex += segPtr->size;
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkTextMarkNameToIndex --
+ *
+ * Given the name of a mark, return an index corresponding to the mark
+ * name.
+ *
+ * Results:
+ * The return value is TCL_OK if "name" exists as a mark in the text
+ * widget and is located within its -starline/-endline range. In this
+ * case *indexPtr is filled in with the next segment who is after the
+ * mark whose size is non-zero. TCL_ERROR is returned if the mark
+ * doesn't exist in the text widget, or if it is out of its -starline/
+ * -endline range. In this latter case *indexPtr still contains valid
+ * information, in particular TkTextMarkNameToIndex called with the
+ * "insert" or "current" mark name may return TCL_ERROR, but *indexPtr
+ * contains the correct index of this mark before -startline or after
+ * -endline.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+TkTextMarkNameToIndex(
+ TkText *textPtr, /* Text widget containing mark. */
+ const char *name, /* Name of mark. */
+ TkTextIndex *indexPtr) /* Index information gets stored here. */
+{
+ TkTextSegment *segPtr;
+ TkTextIndex index;
+ int start, end;
+
+ if (textPtr == NULL) {
+ return TCL_ERROR;
+ }
+
+ if (!strcmp(name, "insert")) {
+ segPtr = textPtr->insertMarkPtr;
+ } else if (!strcmp(name, "current")) {
+ segPtr = textPtr->currentMarkPtr;
+ } else {
+ Tcl_HashEntry *hPtr =
+ Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, name);
+
+ if (hPtr == NULL) {
+ return TCL_ERROR;
+ }
+ segPtr = Tcl_GetHashValue(hPtr);
+ }
+ TkTextMarkSegToIndex(textPtr, segPtr, indexPtr);
+
+ /* If indexPtr refers to somewhere outside the -startline/-endline
+ * range limits of the widget, error out since the mark indeed is not
+ * reachable from this text widget (it may be reachable from a peer)
+ * (bug 1630271).
+ */
+
+ if (textPtr->start != NULL) {
+ start = TkBTreeLinesTo(NULL, textPtr->start);
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, NULL, start, 0,
+ &index);
+ if (TkTextIndexCmp(indexPtr, &index) < 0) {
+ return TCL_ERROR;
+ }
+ }
+ if (textPtr->end != NULL) {
+ end = TkBTreeLinesTo(NULL, textPtr->end);
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, NULL, end, 0,
+ &index);
+ if (TkTextIndexCmp(indexPtr, &index) > 0) {
+ return TCL_ERROR;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * MarkDeleteProc --
+ *
+ * This function is invoked by the text B-tree code whenever a mark lies
+ * in a range of characters being deleted.
+ *
+ * Results:
+ * Returns 1 to indicate that deletion has been rejected.
+ *
+ * Side effects:
+ * None (even if the whole tree is being deleted we don't free up the
+ * mark; it will be done elsewhere).
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static int
+MarkDeleteProc(
+ TkTextSegment *segPtr, /* Segment being deleted. */
+ TkTextLine *linePtr, /* Line containing segment. */
+ int treeGone) /* Non-zero means the entire tree is being
+ * deleted, so everything must get cleaned
+ * up. */
+{
+ return 1;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * MarkCleanupProc --
+ *
+ * This function is invoked by the B-tree code whenever a mark segment is
+ * moved from one line to another.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The linePtr field of the segment gets updated.
+ *
+ *--------------------------------------------------------------
+ */
+
+static TkTextSegment *
+MarkCleanupProc(
+ TkTextSegment *markPtr, /* Mark segment that's being moved. */
+ TkTextLine *linePtr) /* Line that now contains segment. */
+{
+ markPtr->body.mark.linePtr = linePtr;
+ return markPtr;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * MarkLayoutProc --
+ *
+ * This function is the "layoutProc" for mark segments.
+ *
+ * Results:
+ * If the mark isn't the insertion cursor then the return value is -1 to
+ * indicate that this segment shouldn't be displayed. If the mark is the
+ * insertion character then 1 is returned and the chunkPtr structure is
+ * filled in.
+ *
+ * Side effects:
+ * None, except for filling in chunkPtr.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+MarkLayoutProc(
+ TkText *textPtr, /* Text widget being layed out. */
+ TkTextIndex *indexPtr, /* Identifies first character in chunk. */
+ TkTextSegment *segPtr, /* Segment corresponding to indexPtr. */
+ int offset, /* Offset within segPtr corresponding to
+ * indexPtr (always 0). */
+ int maxX, /* Chunk must not occupy pixels at this
+ * position or higher. */
+ int maxChars, /* Chunk must not include more than this many
+ * characters. */
+ int noCharsYet, /* Non-zero means no characters have been
+ * assigned to this line yet. */
+ TkWrapMode wrapMode, /* Not used. */
+ register TkTextDispChunk *chunkPtr)
+ /* Structure to fill in with information about
+ * this chunk. The x field has already been
+ * set by the caller. */
+{
+ if (segPtr != textPtr->insertMarkPtr) {
+ return -1;
+ }
+
+ chunkPtr->displayProc = TkTextInsertDisplayProc;
+ chunkPtr->undisplayProc = InsertUndisplayProc;
+ chunkPtr->measureProc = NULL;
+ chunkPtr->bboxProc = NULL;
+ chunkPtr->numBytes = 0;
+ chunkPtr->minAscent = 0;
+ chunkPtr->minDescent = 0;
+ chunkPtr->minHeight = 0;
+ chunkPtr->width = 0;
+
+ /*
+ * Note: can't break a line after the insertion cursor: this prevents the
+ * insertion cursor from being stranded at the end of a line.
+ */
+
+ chunkPtr->breakIndex = -1;
+ chunkPtr->clientData = textPtr;
+ return 1;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkTextInsertDisplayProc --
+ *
+ * This function is called to display the insertion cursor.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Graphics are drawn.
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+void
+TkTextInsertDisplayProc(
+ TkText *textPtr, /* The current text widget. */
+ TkTextDispChunk *chunkPtr, /* Chunk that is to be drawn. */
+ int x, /* X-position in dst at which to draw this
+ * chunk (may differ from the x-position in
+ * the chunk because of scrolling). */
+ int y, /* Y-position at which to draw this chunk in
+ * dst (x-position is in the chunk itself). */
+ int height, /* Total height of line. */
+ int baseline, /* Offset of baseline from y. */
+ Display *display, /* Display to use for drawing. */
+ Drawable dst, /* Pixmap or window in which to draw chunk. */
+ int screenY) /* Y-coordinate in text window that
+ * corresponds to y. */
+{
+ /*
+ * We have no need for the clientData.
+ */
+
+ /* TkText *textPtr = chunkPtr->clientData; */
+ TkTextIndex index;
+ int halfWidth = textPtr->insertWidth/2;
+ int rightSideWidth;
+ int ix = 0, iy = 0, iw = 0, ih = 0, charWidth = 0;
+
+ if (textPtr->insertCursorType) {
+ TkTextMarkSegToIndex(textPtr, textPtr->insertMarkPtr, &index);
+ TkTextIndexBbox(textPtr, &index, &ix, &iy, &iw, &ih, &charWidth);
+ rightSideWidth = charWidth + halfWidth;
+ } else {
+ rightSideWidth = halfWidth;
+ }
+
+ if ((x + rightSideWidth) < 0) {
+ /*
+ * The insertion cursor is off-screen. Indicate caret at 0,0 and
+ * return.
+ */
+
+ Tk_SetCaretPos(textPtr->tkwin, 0, 0, height);
+ return;
+ }
+
+ Tk_SetCaretPos(textPtr->tkwin, x - halfWidth, screenY, height);
+
+ /*
+ * As a special hack to keep the cursor visible on mono displays (or
+ * anywhere else that the selection and insertion cursors have the same
+ * color) write the default background in the cursor area (instead of
+ * nothing) when the cursor isn't on. Otherwise the selection might hide
+ * the cursor.
+ */
+
+ if (textPtr->flags & GOT_FOCUS) {
+ if (textPtr->flags & INSERT_ON) {
+ Tk_Fill3DRectangle(textPtr->tkwin, dst, textPtr->insertBorder,
+ x - halfWidth, y, charWidth + textPtr->insertWidth,
+ height, textPtr->insertBorderWidth, TK_RELIEF_RAISED);
+ } else if (textPtr->selBorder == textPtr->insertBorder) {
+ Tk_Fill3DRectangle(textPtr->tkwin, dst, textPtr->border,
+ x - halfWidth, y, charWidth + textPtr->insertWidth,
+ height, 0, TK_RELIEF_FLAT);
+ }
+ } else if (textPtr->insertUnfocussed == TK_TEXT_INSERT_NOFOCUS_HOLLOW) {
+ if (textPtr->insertBorderWidth < 1) {
+ /*
+ * Hack to work around the fact that a "solid" border always
+ * paints in black.
+ */
+
+ TkBorder *borderPtr = (TkBorder *) textPtr->insertBorder;
+
+ XDrawRectangle(Tk_Display(textPtr->tkwin), dst, borderPtr->bgGC,
+ x - halfWidth, y, charWidth + textPtr->insertWidth - 1,
+ height - 1);
+ } else {
+ Tk_Draw3DRectangle(textPtr->tkwin, dst, textPtr->insertBorder,
+ x - halfWidth, y, charWidth + textPtr->insertWidth,
+ height, textPtr->insertBorderWidth, TK_RELIEF_RAISED);
+ }
+ } else if (textPtr->insertUnfocussed == TK_TEXT_INSERT_NOFOCUS_SOLID) {
+ Tk_Fill3DRectangle(textPtr->tkwin, dst, textPtr->insertBorder,
+ x - halfWidth, y, charWidth + textPtr->insertWidth, height,
+ textPtr->insertBorderWidth, TK_RELIEF_RAISED);
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * InsertUndisplayProc --
+ *
+ * This function is called when the insertion cursor is no longer at a
+ * visible point on the display. It does nothing right now.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static void
+InsertUndisplayProc(
+ TkText *textPtr, /* Overall information about text widget. */
+ TkTextDispChunk *chunkPtr) /* Chunk that is about to be freed. */
+{
+ return;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * MarkCheckProc --
+ *
+ * This function is invoked by the B-tree code to perform consistency
+ * checks on mark segments.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The function panics if it detects anything wrong with
+ * the mark.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+MarkCheckProc(
+ TkTextSegment *markPtr, /* Segment to check. */
+ TkTextLine *linePtr) /* Line containing segment. */
+{
+ Tcl_HashSearch search;
+ Tcl_HashEntry *hPtr;
+
+ if (markPtr->body.mark.linePtr != linePtr) {
+ Tcl_Panic("MarkCheckProc: markPtr->body.mark.linePtr bogus");
+ }
+
+ /*
+ * These two marks are not in the hash table
+ */
+
+ if (markPtr->body.mark.textPtr->insertMarkPtr == markPtr) {
+ return;
+ }
+ if (markPtr->body.mark.textPtr->currentMarkPtr == markPtr) {
+ return;
+ }
+
+ /*
+ * Make sure that the mark is still present in the text's mark hash table.
+ */
+
+ for (hPtr = Tcl_FirstHashEntry(
+ &markPtr->body.mark.textPtr->sharedTextPtr->markTable,
+ &search); hPtr != markPtr->body.mark.hPtr;
+ hPtr = Tcl_NextHashEntry(&search)) {
+ if (hPtr == NULL) {
+ Tcl_Panic("MarkCheckProc couldn't find hash table entry for mark");
+ }
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * MarkFindNext --
+ *
+ * This function searches forward for the next mark.
+ *
+ * Results:
+ * A standard Tcl result, which is a mark name or an empty string.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+MarkFindNext(
+ Tcl_Interp *interp, /* For error reporting */
+ TkText *textPtr, /* The widget */
+ Tcl_Obj *obj) /* The starting index or mark name */
+{
+ TkTextIndex index;
+ Tcl_HashEntry *hPtr;
+ register TkTextSegment *segPtr;
+ int offset;
+ const char *string = Tcl_GetString(obj);
+
+ if (!strcmp(string, "insert")) {
+ segPtr = textPtr->insertMarkPtr;
+ TkTextMarkSegToIndex(textPtr, segPtr, &index);
+ segPtr = segPtr->nextPtr;
+ } else if (!strcmp(string, "current")) {
+ segPtr = textPtr->currentMarkPtr;
+ TkTextMarkSegToIndex(textPtr, segPtr, &index);
+ segPtr = segPtr->nextPtr;
+ } else {
+ hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, string);
+ if (hPtr != NULL) {
+ /*
+ * If given a mark name, return the next mark in the list of
+ * segments, even if it happens to be at the same character
+ * position.
+ */
+
+ segPtr = Tcl_GetHashValue(hPtr);
+ TkTextMarkSegToIndex(textPtr, segPtr, &index);
+ segPtr = segPtr->nextPtr;
+ } else {
+ /*
+ * For non-mark name indices we want to return any marks that are
+ * right at the index.
+ */
+
+ if (TkTextGetObjIndex(interp, textPtr, obj, &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (offset = 0, segPtr = index.linePtr->segPtr;
+ segPtr != NULL && offset < index.byteIndex;
+ offset += segPtr->size, segPtr = segPtr->nextPtr) {
+ /* Empty loop body */ ;
+ }
+ }
+ }
+
+ while (1) {
+ /*
+ * segPtr points at the first possible candidate, or NULL if we ran
+ * off the end of the line.
+ */
+
+ for ( ; segPtr != NULL ; segPtr = segPtr->nextPtr) {
+ if (segPtr->typePtr == &tkTextRightMarkType ||
+ segPtr->typePtr == &tkTextLeftMarkType) {
+ Tcl_Obj *markName = GetMarkName(textPtr, segPtr);
+
+ if (markName != NULL) {
+ Tcl_SetObjResult(interp, markName);
+ return TCL_OK;
+ }
+ }
+ }
+ index.linePtr = TkBTreeNextLine(textPtr, index.linePtr);
+ if (index.linePtr == NULL) {
+ return TCL_OK;
+ }
+ index.byteIndex = 0;
+ segPtr = index.linePtr->segPtr;
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * MarkFindPrev --
+ *
+ * This function searches backwards for the previous mark.
+ *
+ * Results:
+ * A standard Tcl result, which is a mark name or an empty string.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+MarkFindPrev(
+ Tcl_Interp *interp, /* For error reporting */
+ TkText *textPtr, /* The widget */
+ Tcl_Obj *obj) /* The starting index or mark name */
+{
+ TkTextIndex index;
+ Tcl_HashEntry *hPtr;
+ register TkTextSegment *segPtr, *seg2Ptr, *prevPtr;
+ int offset;
+ const char *string = Tcl_GetString(obj);
+
+ if (!strcmp(string, "insert")) {
+ segPtr = textPtr->insertMarkPtr;
+ TkTextMarkSegToIndex(textPtr, segPtr, &index);
+ } else if (!strcmp(string, "current")) {
+ segPtr = textPtr->currentMarkPtr;
+ TkTextMarkSegToIndex(textPtr, segPtr, &index);
+ } else {
+ hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, string);
+ if (hPtr != NULL) {
+ /*
+ * If given a mark name, return the previous mark in the list of
+ * segments, even if it happens to be at the same character
+ * position.
+ */
+
+ segPtr = Tcl_GetHashValue(hPtr);
+ TkTextMarkSegToIndex(textPtr, segPtr, &index);
+ } else {
+ /*
+ * For non-mark name indices we do not return any marks that are
+ * right at the index.
+ */
+
+ if (TkTextGetObjIndex(interp, textPtr, obj, &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (offset = 0, segPtr = index.linePtr->segPtr;
+ segPtr != NULL && offset < index.byteIndex;
+ offset += segPtr->size, segPtr = segPtr->nextPtr) {
+ /* Empty loop body */
+ }
+ }
+ }
+
+ while (1) {
+ /*
+ * segPtr points just past the first possible candidate, or at the
+ * beginning of the line.
+ */
+
+ for (prevPtr = NULL, seg2Ptr = index.linePtr->segPtr;
+ seg2Ptr != NULL && seg2Ptr != segPtr;
+ seg2Ptr = seg2Ptr->nextPtr) {
+ if (seg2Ptr->typePtr == &tkTextRightMarkType ||
+ seg2Ptr->typePtr == &tkTextLeftMarkType) {
+ if (seg2Ptr->body.mark.hPtr == NULL) {
+ if (seg2Ptr != textPtr->currentMarkPtr &&
+ seg2Ptr != textPtr->insertMarkPtr) {
+ /*
+ * This is an insert or current mark from a
+ * peer of textPtr.
+ */
+ continue;
+ }
+ }
+ prevPtr = seg2Ptr;
+ }
+ }
+ if (prevPtr != NULL) {
+ Tcl_Obj *markName = GetMarkName(textPtr, prevPtr);
+
+ if (markName != NULL) {
+ Tcl_SetObjResult(interp, markName);
+ return TCL_OK;
+ }
+ }
+ index.linePtr = TkBTreePreviousLine(textPtr, index.linePtr);
+ if (index.linePtr == NULL) {
+ return TCL_OK;
+ }
+ segPtr = NULL;
+ }
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * GetMarkName --
+ * Returns the name of the mark that is the given text segment, or NULL
+ * if it is unnamed (i.e., a widget-specific mark that isn't "current" or
+ * "insert").
+ *
+ * ------------------------------------------------------------------------
+ */
+
+static Tcl_Obj *
+GetMarkName(
+ TkText *textPtr,
+ TkTextSegment *segPtr)
+{
+ const char *markName;
+
+ if (segPtr == textPtr->currentMarkPtr) {
+ markName = "current";
+ } else if (segPtr == textPtr->insertMarkPtr) {
+ markName = "insert";
+ } else if (segPtr->body.mark.hPtr == NULL) {
+ /*
+ * Ignore widget-specific marks for the other widgets. This is either
+ * an insert or a current mark (markPtr->body.mark.hPtr actually
+ * receives NULL for these marks in TkTextSetMark). The insert and
+ * current marks for textPtr having already been tested above, the
+ * current segment is an insert or current mark from a peer of
+ * textPtr, which we don't want to return.
+ */
+
+ return NULL;
+ } else {
+ markName = Tcl_GetHashKey(&textPtr->sharedTextPtr->markTable,
+ segPtr->body.mark.hPtr);
+ }
+ return Tcl_NewStringObj(markName, -1);
+}
+
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */