summaryrefslogtreecommitdiffstats
path: root/generic/tkTextMark.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tkTextMark.c')
-rw-r--r--generic/tkTextMark.c841
1 files changed, 521 insertions, 320 deletions
diff --git a/generic/tkTextMark.c b/generic/tkTextMark.c
index c84bc9c..71a7949 100644
--- a/generic/tkTextMark.c
+++ b/generic/tkTextMark.c
@@ -1,19 +1,18 @@
-/*
+/*
* tkTextMark.c --
*
- * This file contains the procedure that implement marks for
- * text widgets.
+ * 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.
+ * 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 "tkPort.h"
/*
* Macro that determines the size of a mark segment:
@@ -23,55 +22,53 @@
+ sizeof(TkTextMark)))
/*
- * Forward references for procedures defined in this file:
+ * Forward references for functions defined in this file:
*/
-static void InsertUndisplayProc _ANSI_ARGS_((TkText *textPtr,
- TkTextDispChunk *chunkPtr));
-static int MarkDeleteProc _ANSI_ARGS_((TkTextSegment *segPtr,
- TkTextLine *linePtr, int treeGone));
-static TkTextSegment * MarkCleanupProc _ANSI_ARGS_((TkTextSegment *segPtr,
- TkTextLine *linePtr));
-static void MarkCheckProc _ANSI_ARGS_((TkTextSegment *segPtr,
- TkTextLine *linePtr));
-static int MarkLayoutProc _ANSI_ARGS_((TkText *textPtr,
- TkTextIndex *indexPtr, TkTextSegment *segPtr,
- int offset, int maxX, int maxChars,
- int noCharsYet, TkWrapMode wrapMode,
- TkTextDispChunk *chunkPtr));
-static int MarkFindNext _ANSI_ARGS_((Tcl_Interp *interp,
- TkText *textPtr, CONST char *markName));
-static int MarkFindPrev _ANSI_ARGS_((Tcl_Interp *interp,
- TkText *textPtr, CONST char *markName));
+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, const char *markName);
+static int MarkFindPrev(Tcl_Interp *interp,
+ TkText *textPtr, const char *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.
+ * 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.
*/
-Tk_SegType tkTextRightMarkType = {
- "mark", /* name */
- 0, /* leftGravity */
- (Tk_SegSplitProc *) NULL, /* splitProc */
- MarkDeleteProc, /* deleteProc */
- MarkCleanupProc, /* cleanupProc */
- (Tk_SegLineChangeProc *) NULL, /* lineChangeProc */
- MarkLayoutProc, /* layoutProc */
- MarkCheckProc /* checkProc */
+const Tk_SegType tkTextRightMarkType = {
+ "mark", /* name */
+ 0, /* leftGravity */
+ NULL, /* splitProc */
+ MarkDeleteProc, /* deleteProc */
+ MarkCleanupProc, /* cleanupProc */
+ NULL, /* lineChangeProc */
+ MarkLayoutProc, /* layoutProc */
+ MarkCheckProc /* checkProc */
};
-Tk_SegType tkTextLeftMarkType = {
- "mark", /* name */
- 1, /* leftGravity */
- (Tk_SegSplitProc *) NULL, /* splitProc */
- MarkDeleteProc, /* deleteProc */
- MarkCleanupProc, /* cleanupProc */
- (Tk_SegLineChangeProc *) 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 */
};
/*
@@ -79,9 +76,9 @@ Tk_SegType tkTextLeftMarkType = {
*
* TkTextMarkCmd --
*
- * This procedure is invoked to process the "mark" options of
- * the widget command for text widgets. See the user documentation
- * for details on what it does.
+ * 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.
@@ -93,44 +90,62 @@ Tk_SegType tkTextLeftMarkType = {
*/
int
-TkTextMarkCmd(textPtr, interp, argc, argv)
- register TkText *textPtr; /* Information about text widget. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- CONST char **argv; /* Argument strings. Someone else has already
+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
- * argv[1] is "mark". */
+ * objv[1] is "mark". */
{
- int c, i;
- size_t length;
Tcl_HashEntry *hPtr;
TkTextSegment *markPtr;
Tcl_HashSearch search;
TkTextIndex index;
- Tk_SegType *newTypePtr;
-
- if (argc < 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " mark option ?arg arg ...?\"", (char *) NULL);
+ const Tk_SegType *newTypePtr;
+ int optionIndex;
+ static const char *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 arg ...?");
+ return TCL_ERROR;
+ }
+ if (Tcl_GetIndexFromObj(interp, objv[2], markOptionStrings, "mark option",
+ 0, &optionIndex) != TCL_OK) {
return TCL_ERROR;
}
- c = argv[2][0];
- length = strlen(argv[2]);
- if ((c == 'g') && (strncmp(argv[2], "gravity", length) == 0)) {
- if (argc < 4 || argc > 5) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " mark gravity markName ?gravity?\"",
- (char *) NULL);
+
+ switch ((enum markOptions) optionIndex) {
+ case MARK_GRAVITY: {
+ char c;
+ int length;
+ char *str;
+
+ if (objc < 4 || objc > 5) {
+ Tcl_WrongNumArgs(interp, 3, objv, "markName ?gravity?");
return TCL_ERROR;
}
- hPtr = Tcl_FindHashEntry(&textPtr->markTable, argv[3]);
- if (hPtr == NULL) {
- Tcl_AppendResult(interp, "there is no mark named \"",
- argv[3], "\"", (char *) NULL);
- return TCL_ERROR;
+ str = Tcl_GetStringFromObj(objv[3],&length);
+ if (length == 6 && !strcmp(str, "insert")) {
+ markPtr = textPtr->insertMarkPtr;
+ } else if (length == 7 && !strcmp(str, "current")) {
+ markPtr = textPtr->currentMarkPtr;
+ } else {
+ hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, str);
+ if (hPtr == NULL) {
+ Tcl_AppendResult(interp, "there is no mark named \"",
+ Tcl_GetString(objv[3]), "\"", NULL);
+ return TCL_ERROR;
+ }
+ markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
}
- markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
- if (argc == 4) {
+ if (objc == 4) {
if (markPtr->typePtr == &tkTextRightMarkType) {
Tcl_SetResult(interp, "right", TCL_STATIC);
} else {
@@ -138,77 +153,83 @@ TkTextMarkCmd(textPtr, interp, argc, argv)
}
return TCL_OK;
}
- length = strlen(argv[4]);
- c = argv[4][0];
- if ((c == 'l') && (strncmp(argv[4], "left", length) == 0)) {
+ 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(argv[4], "right", length) == 0)) {
+ } else if ((c == 'r') &&
+ (strncmp(str, "right", (unsigned)length) == 0)) {
newTypePtr = &tkTextRightMarkType;
} else {
- Tcl_AppendResult(interp, "bad mark gravity \"",
- argv[4], "\": must be left or right", (char *) NULL);
+ Tcl_AppendResult(interp, "bad mark gravity \"", str,
+ "\": must be left or right", NULL);
return TCL_ERROR;
}
TkTextMarkSegToIndex(textPtr, markPtr, &index);
- TkBTreeUnlinkSegment(textPtr->tree, markPtr,
- markPtr->body.mark.linePtr);
+ TkBTreeUnlinkSegment(markPtr, markPtr->body.mark.linePtr);
markPtr->typePtr = newTypePtr;
TkBTreeLinkSegment(markPtr, &index);
- } else if ((c == 'n') && (strncmp(argv[2], "names", length) == 0)) {
- if (argc != 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " mark names\"", (char *) NULL);
+ break;
+ }
+ case MARK_NAMES:
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 3, objv, NULL);
return TCL_ERROR;
}
- for (hPtr = Tcl_FirstHashEntry(&textPtr->markTable, &search);
- hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
+ Tcl_AppendElement(interp, "insert");
+ Tcl_AppendElement(interp, "current");
+ for (hPtr = Tcl_FirstHashEntry(&textPtr->sharedTextPtr->markTable,
+ &search); hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
Tcl_AppendElement(interp,
- Tcl_GetHashKey(&textPtr->markTable, hPtr));
+ Tcl_GetHashKey(&textPtr->sharedTextPtr->markTable, hPtr));
}
- } else if ((c == 'n') && (strncmp(argv[2], "next", length) == 0)) {
- if (argc != 4) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " mark next index\"", (char *) NULL);
+ break;
+ case MARK_NEXT:
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 3, objv, "index");
return TCL_ERROR;
}
- return MarkFindNext(interp, textPtr, argv[3]);
- } else if ((c == 'p') && (strncmp(argv[2], "previous", length) == 0)) {
- if (argc != 4) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " mark previous index\"", (char *) NULL);
+ return MarkFindNext(interp, textPtr, Tcl_GetString(objv[3]));
+ case MARK_PREVIOUS:
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 3, objv, "index");
return TCL_ERROR;
}
- return MarkFindPrev(interp, textPtr, argv[3]);
- } else if ((c == 's') && (strncmp(argv[2], "set", length) == 0)) {
- if (argc != 5) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " mark set markName index\"", (char *) NULL);
+ return MarkFindPrev(interp, textPtr, Tcl_GetString(objv[3]));
+ case MARK_SET:
+ if (objc != 5) {
+ Tcl_WrongNumArgs(interp, 3, objv, "markName index");
return TCL_ERROR;
}
- if (TkTextGetIndex(interp, textPtr, argv[4], &index) != TCL_OK) {
+ if (TkTextGetObjIndex(interp, textPtr, objv[4], &index) != TCL_OK) {
return TCL_ERROR;
}
- TkTextSetMark(textPtr, argv[3], &index);
- } else if ((c == 'u') && (strncmp(argv[2], "unset", length) == 0)) {
- for (i = 3; i < argc; i++) {
- hPtr = Tcl_FindHashEntry(&textPtr->markTable, argv[i]);
+ 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 = (TkTextSegment *) Tcl_GetHashValue(hPtr);
+
+ /*
+ * Special case not needed with peer widgets.
+ */
+
if ((markPtr == textPtr->insertMarkPtr)
|| (markPtr == textPtr->currentMarkPtr)) {
continue;
}
- TkBTreeUnlinkSegment(textPtr->tree, markPtr,
- markPtr->body.mark.linePtr);
+ TkBTreeUnlinkSegment(markPtr, markPtr->body.mark.linePtr);
Tcl_DeleteHashEntry(hPtr);
ckfree((char *) markPtr);
}
}
- } else {
- Tcl_AppendResult(interp, "bad mark option \"", argv[2],
- "\": must be gravity, names, next, previous, set, or unset",
- (char *) NULL);
- return TCL_ERROR;
+ break;
+ }
}
return TCL_OK;
}
@@ -218,8 +239,8 @@ TkTextMarkCmd(textPtr, interp, argc, argv)
*
* TkTextSetMark --
*
- * Set a mark to a particular position, creating a new mark if
- * one doesn't already exist.
+ * 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.
@@ -231,39 +252,67 @@ TkTextMarkCmd(textPtr, interp, argc, argv)
*/
TkTextSegment *
-TkTextSetMark(textPtr, name, indexPtr)
- TkText *textPtr; /* Text widget in which to create mark. */
- CONST char *name; /* Name of mark to set. */
- TkTextIndex *indexPtr; /* Where to set mark. */
+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;
+ Tcl_HashEntry *hPtr = NULL;
TkTextSegment *markPtr;
TkTextIndex insertIndex;
- int new;
-
- hPtr = Tcl_CreateHashEntry(&textPtr->markTable, name, &new);
- markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
- if (!new) {
+ 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 = (TkTextSegment *) 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 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(&index, 1, &index2);
- TkTextChanged(textPtr, &index, &index2);
- if (TkBTreeLineIndex(indexPtr->linePtr)
- == TkBTreeNumLines(textPtr->tree)) {
- TkTextIndexBackChars(indexPtr, 1, &insertIndex);
+ 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(textPtr->tree, markPtr,
- markPtr->body.mark.linePtr);
+ TkBTreeUnlinkSegment(markPtr, markPtr->body.mark.linePtr);
} else {
markPtr = (TkTextSegment *) ckalloc(MSEG_SIZE);
markPtr->typePtr = &tkTextRightMarkType;
@@ -271,7 +320,13 @@ TkTextSetMark(textPtr, name, indexPtr)
markPtr->body.mark.textPtr = textPtr;
markPtr->body.mark.linePtr = indexPtr->linePtr;
markPtr->body.mark.hPtr = hPtr;
- Tcl_SetHashValue(hPtr, markPtr);
+ if (widgetSpecific == 0) {
+ Tcl_SetHashValue(hPtr, markPtr);
+ } else if (widgetSpecific == 1) {
+ textPtr->insertMarkPtr = markPtr;
+ } else {
+ textPtr->currentMarkPtr = markPtr;
+ }
}
TkBTreeLinkSegment(markPtr, indexPtr);
@@ -283,8 +338,14 @@ TkTextSetMark(textPtr, name, indexPtr)
if (markPtr == textPtr->insertMarkPtr) {
TkTextIndex index2;
- TkTextIndexForwChars(indexPtr, 1, &index2);
- TkTextChanged(textPtr, indexPtr, &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;
}
@@ -294,9 +355,9 @@ TkTextSetMark(textPtr, name, indexPtr)
*
* 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.
+ * 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.
@@ -308,14 +369,14 @@ TkTextSetMark(textPtr, name, indexPtr)
*/
void
-TkTextMarkSegToIndex(textPtr, markPtr, indexPtr)
- TkText *textPtr; /* Text widget containing mark. */
- TkTextSegment *markPtr; /* Mark segment. */
- TkTextIndex *indexPtr; /* Index information gets stored here. */
+TkTextMarkSegToIndex(
+ TkText *textPtr, /* Text widget containing mark. */
+ TkTextSegment *markPtr, /* Mark segment. */
+ TkTextIndex *indexPtr) /* Index information gets stored here. */
{
TkTextSegment *segPtr;
- indexPtr->tree = textPtr->tree;
+ indexPtr->tree = textPtr->sharedTextPtr->tree;
indexPtr->linePtr = markPtr->body.mark.linePtr;
indexPtr->byteIndex = 0;
for (segPtr = indexPtr->linePtr->segPtr; segPtr != markPtr;
@@ -329,15 +390,20 @@ TkTextMarkSegToIndex(textPtr, markPtr, indexPtr)
*
* TkTextMarkNameToIndex --
*
- * Given the name of a mark, return an index corresponding
- * to the mark name.
+ * 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. In this case *indexPtr is filled in with
- * the next segment whose after the mark whose size is
- * non-zero. TCL_ERROR is returned if the mark doesn't exist
- * in the text widget.
+ * 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.
@@ -346,19 +412,55 @@ TkTextMarkSegToIndex(textPtr, markPtr, indexPtr)
*/
int
-TkTextMarkNameToIndex(textPtr, name, indexPtr)
- TkText *textPtr; /* Text widget containing mark. */
- CONST char *name; /* Name of mark. */
- TkTextIndex *indexPtr; /* Index information gets stored here. */
+TkTextMarkNameToIndex(
+ TkText *textPtr, /* Text widget containing mark. */
+ const char *name, /* Name of mark. */
+ TkTextIndex *indexPtr) /* Index information gets stored here. */
{
- Tcl_HashEntry *hPtr;
+ TkTextSegment *segPtr;
+ TkTextIndex index;
+ int start, end;
- hPtr = Tcl_FindHashEntry(&textPtr->markTable, name);
- if (hPtr == NULL) {
- return TCL_ERROR;
+ 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;
+ hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, name);
+ if (hPtr == NULL) {
+ return TCL_ERROR;
+ }
+ segPtr = (TkTextSegment *) 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;
+ }
}
- TkTextMarkSegToIndex(textPtr, (TkTextSegment *) Tcl_GetHashValue(hPtr),
- indexPtr);
return TCL_OK;
}
@@ -367,27 +469,27 @@ TkTextMarkNameToIndex(textPtr, name, indexPtr)
*
* MarkDeleteProc --
*
- * This procedure is invoked by the text B-tree code whenever
- * a mark lies in a range of characters being deleted.
+ * 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).
+ * 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(segPtr, linePtr, treeGone)
- 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. */
+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;
}
@@ -397,8 +499,8 @@ MarkDeleteProc(segPtr, linePtr, treeGone)
*
* MarkCleanupProc --
*
- * This procedure is invoked by the B-tree code whenever a
- * mark segment is moved from one line to another.
+ * This function is invoked by the B-tree code whenever a mark segment is
+ * moved from one line to another.
*
* Results:
* None.
@@ -410,9 +512,9 @@ MarkDeleteProc(segPtr, linePtr, treeGone)
*/
static TkTextSegment *
-MarkCleanupProc(markPtr, linePtr)
- TkTextSegment *markPtr; /* Mark segment that's being moved. */
- TkTextLine *linePtr; /* Line that now contains segment. */
+MarkCleanupProc(
+ TkTextSegment *markPtr, /* Mark segment that's being moved. */
+ TkTextLine *linePtr) /* Line that now contains segment. */
{
markPtr->body.mark.linePtr = linePtr;
return markPtr;
@@ -423,13 +525,13 @@ MarkCleanupProc(markPtr, linePtr)
*
* MarkLayoutProc --
*
- * This procedure is the "layoutProc" for mark segments.
+ * 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.
+ * 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.
@@ -437,26 +539,24 @@ MarkCleanupProc(markPtr, linePtr)
*--------------------------------------------------------------
*/
- /*ARGSUSED*/
static int
-MarkLayoutProc(textPtr, indexPtr, segPtr, offset, maxX, maxChars,
- noCharsYet, wrapMode, chunkPtr)
- 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
+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
+ 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
+ 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. */
+ 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;
@@ -464,8 +564,8 @@ MarkLayoutProc(textPtr, indexPtr, segPtr, offset, maxX, maxChars,
chunkPtr->displayProc = TkTextInsertDisplayProc;
chunkPtr->undisplayProc = InsertUndisplayProc;
- chunkPtr->measureProc = (Tk_ChunkMeasureProc *) NULL;
- chunkPtr->bboxProc = (Tk_ChunkBboxProc *) NULL;
+ chunkPtr->measureProc = NULL;
+ chunkPtr->bboxProc = NULL;
chunkPtr->numBytes = 0;
chunkPtr->minAscent = 0;
chunkPtr->minDescent = 0;
@@ -473,9 +573,8 @@ MarkLayoutProc(textPtr, indexPtr, segPtr, offset, maxX, maxChars,
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.
+ * 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;
@@ -488,8 +587,7 @@ MarkLayoutProc(textPtr, indexPtr, segPtr, offset, maxX, maxChars,
*
* TkTextInsertDisplayProc --
*
- * This procedure is called to display the insertion
- * cursor.
+ * This function is called to display the insertion cursor.
*
* Results:
* None.
@@ -502,30 +600,43 @@ MarkLayoutProc(textPtr, indexPtr, segPtr, offset, maxX, maxChars,
/* ARGSUSED */
void
-TkTextInsertDisplayProc(chunkPtr, x, y, height, baseline, display, dst, screenY)
- 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. */
+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. */
{
- TkText *textPtr = (TkText *) chunkPtr->clientData;
+ /*
+ * We have no need for the clientData.
+ */
+
+ /* TkText *textPtr = (TkText *) chunkPtr->clientData; */
+ TkTextIndex index;
int halfWidth = textPtr->insertWidth/2;
+ int rightSideWidth;
+ 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 + halfWidth) < 0) {
+ if ((x + rightSideWidth) < 0) {
/*
- * The insertion cursor is off-screen.
- * Indicate caret at 0,0 and return.
+ * The insertion cursor is off-screen. Indicate caret at 0,0 and
+ * return.
*/
Tk_SetCaretPos(textPtr->tkwin, 0, 0, height);
@@ -535,20 +646,20 @@ TkTextInsertDisplayProc(chunkPtr, x, y, height, baseline, display, dst, screenY)
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.
+ * 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 & INSERT_ON) {
Tk_Fill3DRectangle(textPtr->tkwin, dst, textPtr->insertBorder,
- x - halfWidth, y, textPtr->insertWidth, height,
+ 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, textPtr->insertWidth, height,
+ x - halfWidth, y, charWidth + textPtr->insertWidth, height,
0, TK_RELIEF_FLAT);
}
}
@@ -558,9 +669,8 @@ TkTextInsertDisplayProc(chunkPtr, x, y, height, baseline, display, dst, screenY)
*
* InsertUndisplayProc --
*
- * This procedure is called when the insertion cursor is no
- * longer at a visible point on the display. It does nothing
- * right now.
+ * 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.
@@ -573,10 +683,9 @@ TkTextInsertDisplayProc(chunkPtr, x, y, height, baseline, display, dst, screenY)
/* ARGSUSED */
static void
-InsertUndisplayProc(textPtr, chunkPtr)
- TkText *textPtr; /* Overall information about text
- * widget. */
- TkTextDispChunk *chunkPtr; /* Chunk that is about to be freed. */
+InsertUndisplayProc(
+ TkText *textPtr, /* Overall information about text widget. */
+ TkTextDispChunk *chunkPtr) /* Chunk that is about to be freed. */
{
return;
}
@@ -586,41 +695,52 @@ InsertUndisplayProc(textPtr, chunkPtr)
*
* MarkCheckProc --
*
- * This procedure is invoked by the B-tree code to perform
- * consistency checks on mark segments.
+ * This function is invoked by the B-tree code to perform consistency
+ * checks on mark segments.
*
* Results:
* None.
*
* Side effects:
- * The procedure panics if it detects anything wrong with
+ * The function panics if it detects anything wrong with
* the mark.
*
*--------------------------------------------------------------
*/
static void
-MarkCheckProc(markPtr, linePtr)
- TkTextSegment *markPtr; /* Segment to check. */
- TkTextLine *linePtr; /* Line containing segment. */
+MarkCheckProc(
+ TkTextSegment *markPtr, /* Segment to check. */
+ TkTextLine *linePtr) /* Line containing segment. */
{
Tcl_HashSearch search;
Tcl_HashEntry *hPtr;
if (markPtr->body.mark.linePtr != linePtr) {
- panic("MarkCheckProc: markPtr->body.mark.linePtr bogus");
+ Tcl_Panic("MarkCheckProc: markPtr->body.mark.linePtr bogus");
}
/*
- * Make sure that the mark is still present in the text's mark
- * hash table.
+ * These two marks are not in the hash table
*/
- for (hPtr = Tcl_FirstHashEntry(&markPtr->body.mark.textPtr->markTable,
+ 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) {
- panic("MarkCheckProc couldn't find hash table entry for mark");
+ Tcl_Panic("MarkCheckProc couldn't find hash table entry for mark");
}
}
}
@@ -630,7 +750,7 @@ MarkCheckProc(markPtr, linePtr)
*
* MarkFindNext --
*
- * This procedure searches forward for the next mark.
+ * This function searches forward for the next mark.
*
* Results:
* A standard Tcl result, which is a mark name or an empty string.
@@ -642,56 +762,88 @@ MarkCheckProc(markPtr, linePtr)
*/
static int
-MarkFindNext(interp, textPtr, string)
- Tcl_Interp *interp; /* For error reporting */
- TkText *textPtr; /* The widget */
- CONST char *string; /* The starting index or mark name */
+MarkFindNext(
+ Tcl_Interp *interp, /* For error reporting */
+ TkText *textPtr, /* The widget */
+ const char *string) /* The starting index or mark name */
{
TkTextIndex index;
Tcl_HashEntry *hPtr;
register TkTextSegment *segPtr;
int offset;
-
- hPtr = Tcl_FindHashEntry(&textPtr->markTable, string);
- if (hPtr != NULL) {
- /*
- * If given a mark name, return the next mark in the list of
- * segments, even if it happens to be at the same character position.
- */
- segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
+ if (!strcmp(string, "insert")) {
+ segPtr = textPtr->insertMarkPtr;
+ TkTextMarkSegToIndex(textPtr, segPtr, &index);
+ segPtr = segPtr->nextPtr;
+ } else if (!strcmp(string, "current")) {
+ segPtr = textPtr->currentMarkPtr;
TkTextMarkSegToIndex(textPtr, segPtr, &index);
segPtr = segPtr->nextPtr;
} else {
- /*
- * For non-mark name indices we want to return any marks that
- * are right at the index.
- */
- if (TkTextGetIndex(interp, textPtr, string, &index) != TCL_OK) {
- return TCL_ERROR;
- }
- for (offset = 0, segPtr = index.linePtr->segPtr;
- segPtr != NULL && offset < index.byteIndex;
- offset += segPtr->size, segPtr = segPtr->nextPtr) {
- /* Empty loop body */ ;
+ hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, string);
+ if (hPtr != NULL) {
+ /*
+ * If given a mark name, return the next mark in the list of
+ * segments, even if it happens to be at the same character
+ * position.
+ */
+
+ segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
+ TkTextMarkSegToIndex(textPtr, segPtr, &index);
+ segPtr = segPtr->nextPtr;
+ } else {
+ /*
+ * For non-mark name indices we want to return any marks that are
+ * right at the index.
+ */
+
+ if (TkTextGetIndex(interp, textPtr, string, &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (offset = 0, segPtr = index.linePtr->segPtr;
+ segPtr != NULL && offset < index.byteIndex;
+ offset += segPtr->size, segPtr = segPtr->nextPtr) {
+ /* Empty loop body */ ;
+ }
}
}
+
while (1) {
/*
- * segPtr points at the first possible candidate,
- * or NULL if we ran off the end of the line.
+ * 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_SetResult(interp,
- Tcl_GetHashKey(&textPtr->markTable, segPtr->body.mark.hPtr),
- TCL_STATIC);
+ if (segPtr == textPtr->currentMarkPtr) {
+ Tcl_SetResult(interp, "current", TCL_STATIC);
+ } else if (segPtr == textPtr->insertMarkPtr) {
+ Tcl_SetResult(interp, "insert", TCL_STATIC);
+ } else if (segPtr->body.mark.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.
+ */
+ continue;
+ } else {
+ Tcl_SetResult(interp,
+ Tcl_GetHashKey(&textPtr->sharedTextPtr->markTable,
+ segPtr->body.mark.hPtr), TCL_STATIC);
+ }
return TCL_OK;
}
}
- index.linePtr = TkBTreeNextLine(index.linePtr);
- if (index.linePtr == (TkTextLine *) NULL) {
+ index.linePtr = TkBTreeNextLine(textPtr, index.linePtr);
+ if (index.linePtr == NULL) {
return TCL_OK;
}
index.byteIndex = 0;
@@ -704,7 +856,7 @@ MarkFindNext(interp, textPtr, string)
*
* MarkFindPrev --
*
- * This procedure searches backwards for the previous mark.
+ * This function searches backwards for the previous mark.
*
* Results:
* A standard Tcl result, which is a mark name or an empty string.
@@ -716,62 +868,111 @@ MarkFindNext(interp, textPtr, string)
*/
static int
-MarkFindPrev(interp, textPtr, string)
- Tcl_Interp *interp; /* For error reporting */
- TkText *textPtr; /* The widget */
- CONST char *string; /* The starting index or mark name */
+MarkFindPrev(
+ Tcl_Interp *interp, /* For error reporting */
+ TkText *textPtr, /* The widget */
+ const char *string) /* The starting index or mark name */
{
TkTextIndex index;
Tcl_HashEntry *hPtr;
register TkTextSegment *segPtr, *seg2Ptr, *prevPtr;
int offset;
-
- hPtr = Tcl_FindHashEntry(&textPtr->markTable, string);
- if (hPtr != NULL) {
- /*
- * If given a mark name, return the previous mark in the list of
- * segments, even if it happens to be at the same character position.
- */
- segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
+ if (!strcmp(string, "insert")) {
+ segPtr = textPtr->insertMarkPtr;
+ TkTextMarkSegToIndex(textPtr, segPtr, &index);
+ } else if (!strcmp(string, "current")) {
+ segPtr = textPtr->currentMarkPtr;
TkTextMarkSegToIndex(textPtr, segPtr, &index);
} else {
- /*
- * For non-mark name indices we do not return any marks that
- * are right at the index.
- */
- if (TkTextGetIndex(interp, textPtr, string, &index) != TCL_OK) {
- return TCL_ERROR;
- }
- for (offset = 0, segPtr = index.linePtr->segPtr;
- segPtr != NULL && offset < index.byteIndex;
- offset += segPtr->size, segPtr = segPtr->nextPtr) {
- /* Empty loop body */ ;
+ hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, string);
+ if (hPtr != NULL) {
+ /*
+ * If given a mark name, return the previous mark in the list of
+ * segments, even if it happens to be at the same character
+ * position.
+ */
+
+ segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
+ TkTextMarkSegToIndex(textPtr, segPtr, &index);
+ } else {
+ /*
+ * For non-mark name indices we do not return any marks that are
+ * right at the index.
+ */
+
+ if (TkTextGetIndex(interp, textPtr, string, &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (offset = 0, segPtr = index.linePtr->segPtr;
+ segPtr != NULL && offset < index.byteIndex;
+ offset += segPtr->size, segPtr = segPtr->nextPtr) {
+ /* Empty loop body */
+ }
}
}
+
while (1) {
/*
- * segPtr points just past the first possible candidate,
- * or at the begining of the line.
+ * segPtr points just past the first possible candidate, or at the
+ * beginning of the line.
*/
- for (prevPtr = NULL, seg2Ptr = index.linePtr->segPtr;
+
+ 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_SetResult(interp,
- Tcl_GetHashKey(&textPtr->markTable, prevPtr->body.mark.hPtr),
- TCL_STATIC);
- return TCL_OK;
+ if (prevPtr == textPtr->currentMarkPtr) {
+ Tcl_SetResult(interp, "current", TCL_STATIC);
+ return TCL_OK;
+ } else if (prevPtr == textPtr->insertMarkPtr) {
+ Tcl_SetResult(interp, "insert", TCL_STATIC);
+ return TCL_OK;
+ } else if (prevPtr->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.
+ */
+ } else {
+ Tcl_SetResult(interp,
+ Tcl_GetHashKey(&textPtr->sharedTextPtr->markTable,
+ prevPtr->body.mark.hPtr), TCL_STATIC);
+ return TCL_OK;
+ }
}
- index.linePtr = TkBTreePreviousLine(index.linePtr);
- if (index.linePtr == (TkTextLine *) NULL) {
+ index.linePtr = TkBTreePreviousLine(textPtr, index.linePtr);
+ if (index.linePtr == NULL) {
return TCL_OK;
}
segPtr = NULL;
}
}
+
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */