From b5f1ab1fb4a53001ab61083d12a3597ffe7cc72e Mon Sep 17 00:00:00 2001 From: vincentdarley Date: Mon, 4 Apr 2005 21:58:28 +0000 Subject: fix to elide search and insertofftime 0 text widget bugs --- ChangeLog | 6 ++ generic/tkText.c | 166 ++++++++++++++++++++++++++++++++++++++++++------------- tests/text.test | 117 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 251 insertions(+), 38 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2f198bd..ba5f332 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2005-04-04 Vince Darley + + * generic/tkText.c: + * tests/text.test: fix to elide searching problems [Bug 1174269] + and disappearing cursor with insertofftime 0 [Bug 1169429]. + 2005-04-03 Peter Spjuth * tests/grid.test: diff --git a/generic/tkText.c b/generic/tkText.c index 8067165..f7dc176 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.56 2005/02/14 23:00:29 vincentdarley Exp $ + * RCS: @(#) $Id: tkText.c,v 1.57 2005/04/04 22:00:05 vincentdarley Exp $ */ #include "default.h" @@ -249,7 +249,8 @@ struct SearchSpec; /* Forward declaration. */ typedef ClientData SearchAddLineProc _ANSI_ARGS_((int lineNum, struct SearchSpec *searchSpecPtr, - Tcl_Obj *theLine, int *lenPtr)); + Tcl_Obj *theLine, int *lenPtr, + int *extraLinesPtr)); typedef int SearchMatchProc _ANSI_ARGS_((int lineNum, struct SearchSpec *searchSpecPtr, ClientData clientData, Tcl_Obj *theLine, @@ -3380,6 +3381,15 @@ TextBlinkProc(clientData) if ((textPtr->state == TK_TEXT_STATE_DISABLED) || !(textPtr->flags & GOT_FOCUS) || (textPtr->insertOffTime == 0)) { + if ((textPtr->insertOffTime == 0) && !(textPtr->flags & INSERT_ON)) { + /* + * The widget was configured to have zero offtime while + * the insertion point was not displayed. We have to + * display it once. + */ + textPtr->flags |= INSERT_ON; + goto redrawInsert; + } return; } if (textPtr->flags & INSERT_ON) { @@ -3391,6 +3401,7 @@ TextBlinkProc(clientData) textPtr->insertBlinkHandler = Tcl_CreateTimerHandler( textPtr->insertOnTime, TextBlinkProc, (ClientData) textPtr); } + redrawInsert: TkTextMarkSegToIndex(textPtr, textPtr->insertMarkPtr, &index); if (TkTextCharBbox(textPtr, &index, &x, &y, &w, &h, &charWidth) == 0) { if (textPtr->insertCursorType) { @@ -3833,6 +3844,11 @@ TextSearchIndexInLine(searchSpecPtr, linePtr, byteIndex) * 'theLine' (not just what we added to it, but the length including * what was already in there). This is in bytes for an exact search * and in chars for a regexp search. + * + * Also 'extraLinesPtr' (if non-NULL) will have its value + * incremented by 1 for each additional logical line we have + * added because a newline is elided (this will only ever happen + * if we have chosen not to search elided text, of course). * * Side effects: * Memory may be allocated or re-allocated for theLine's string @@ -3842,16 +3858,23 @@ TextSearchIndexInLine(searchSpecPtr, linePtr, byteIndex) */ static ClientData -TextSearchAddNextLine(lineNum, searchSpecPtr, theLine, lenPtr) +TextSearchAddNextLine(lineNum, searchSpecPtr, theLine, lenPtr, extraLinesPtr) int lineNum; /* Line we must add */ SearchSpec *searchSpecPtr; /* Search parameters */ Tcl_Obj *theLine; /* Object to append to */ int *lenPtr; /* For returning the total length */ + int *extraLinesPtr; /* If non-NULL, will have its value + * incremented by the number of + * additional logical lines which are + * merged into this one by newlines + * being elided */ { - TkTextLine *linePtr; + TkTextLine *linePtr, *thisLinePtr; TkTextIndex curIndex; TkTextSegment *segPtr; TkText *textPtr = (TkText*)(searchSpecPtr->clientData); + int nothingYet = 1; + /* * Extract the text from the line. */ @@ -3861,17 +3884,50 @@ TextSearchAddNextLine(lineNum, searchSpecPtr, theLine, lenPtr) return NULL; } curIndex.tree = textPtr->sharedTextPtr->tree; - curIndex.linePtr = linePtr; curIndex.byteIndex = 0; - for (segPtr = linePtr->segPtr; segPtr != NULL; - curIndex.byteIndex += segPtr->size, segPtr = segPtr->nextPtr) { - if ((segPtr->typePtr != &tkTextCharType) - || (!searchSpecPtr->searchElide - && TkTextIsElided(textPtr, &curIndex, NULL))) { - continue; + thisLinePtr = linePtr; + + while (thisLinePtr != NULL) { + int elideWraps = 0; + curIndex.linePtr = thisLinePtr; + curIndex.byteIndex = 0; + for (segPtr = thisLinePtr->segPtr; segPtr != NULL; + curIndex.byteIndex += segPtr->size, segPtr = segPtr->nextPtr) { + if (!searchSpecPtr->searchElide + && TkTextIsElided(textPtr, &curIndex, NULL)) { + /* + * If we reach the end of the logical line, and if we + * have at least one character in the string, then we + * continue wrapping to the next logical line. If + * there are no characters yet, then the entire line + * of characters is elided and there's no need to + * complicate matters by wrapping - we'll look at the + * next line in due course. + */ + if (segPtr->nextPtr == NULL && !nothingYet) { + elideWraps = 1; + } + continue; + } + if (segPtr->typePtr != &tkTextCharType) { + continue; + } + Tcl_AppendToObj(theLine, segPtr->body.chars, segPtr->size); + nothingYet = 0; + } + if (!elideWraps) { + break; + } + lineNum++; + if (lineNum >= searchSpecPtr->numLines) { + break; + } + thisLinePtr = TkBTreeNextLine(textPtr, thisLinePtr); + if (thisLinePtr != NULL && extraLinesPtr != NULL) { + /* Tell our caller we have an extra line merged in */ + *extraLinesPtr = (*extraLinesPtr) + 1; } - Tcl_AppendToObj(theLine, segPtr->body.chars, segPtr->size); } - + /* * If we're ignoring case, convert the line to lower case. * There is no need to do this for regexp searches, since @@ -3982,23 +4038,56 @@ TextSearchFoundMatch(lineNum, searchSpecPtr, clientData, theLine, } curIndex.tree = textPtr->sharedTextPtr->tree; - curIndex.linePtr = linePtr; curIndex.byteIndex = 0; /* Find the starting point */ - for (segPtr = linePtr->segPtr, leftToScan = matchOffset; - leftToScan >= 0 && segPtr; segPtr = segPtr->nextPtr) { - if (segPtr->typePtr != &tkTextCharType) { - matchOffset += segPtr->size; - } else if (!searchSpecPtr->searchElide - && TkTextIsElided(textPtr, &curIndex, NULL)) { - if (searchSpecPtr->exact) { + leftToScan = matchOffset; + while (1) { + curIndex.linePtr = linePtr; + curIndex.byteIndex = 0; + /* + * Note that we allow leftToScan to be zero because we want + * to skip over any preceding non-textual items. + */ + for (segPtr = linePtr->segPtr; + leftToScan >= 0 && segPtr; segPtr = segPtr->nextPtr) { + if (segPtr->typePtr != &tkTextCharType) { matchOffset += segPtr->size; + } else if (!searchSpecPtr->searchElide + && TkTextIsElided(textPtr, &curIndex, NULL)) { + if (searchSpecPtr->exact) { + matchOffset += segPtr->size; + } else { + matchOffset += Tcl_NumUtfChars(segPtr->body.chars, -1); + } } else { - matchOffset += Tcl_NumUtfChars(segPtr->body.chars, -1); + leftToScan -= segPtr->size; } + curIndex.byteIndex += segPtr->size; + } + if (segPtr == NULL && leftToScan >= 0) { + /* This will only happen if we are eliding newlines */ + linePtr = TkBTreeNextLine(textPtr, linePtr); + if (linePtr == NULL) { + /* + * If we reach the end of the text, we have a serious + * problem, unless there's actually nothing left to look + * for. + */ + if (leftToScan == 0) { + break; + } else { + Tcl_Panic("Reached end of text in a match"); + } + } + /* + * We've wrapped to the beginning of the next logical line, + * which has been merged with the previous one whose newline + * was elided. + */ + lineNum++; + matchOffset = 0; } else { - leftToScan -= segPtr->size; + break; } - curIndex.byteIndex += segPtr->size; } /* Calculate and store the found index in the result */ if (searchSpecPtr->exact) { @@ -5121,7 +5210,8 @@ SearchCore(interp, searchSpecPtr, patObj) for (passes = 0; passes < 2; ) { ClientData lineInfo; int linesSearched = 1; - + int extraLinesSearched = 0; + if (lineNum >= searchSpecPtr->numLines) { /* * Don't search the dummy last line of the text. @@ -5138,7 +5228,7 @@ SearchCore(interp, searchSpecPtr, patObj) */ lineInfo = (*searchSpecPtr->addLineProc)(lineNum, - searchSpecPtr, theLine, &lastOffset); + searchSpecPtr, theLine, &lastOffset, &linesSearched); if (lineInfo == NULL) { /* @@ -5305,10 +5395,10 @@ SearchCore(interp, searchSpecPtr, patObj) if (extraLines > maxExtraLines) { if ((*searchSpecPtr->addLineProc)(lineNum + extraLines, searchSpecPtr, theLine, - &lastTotal) == NULL) { + &lastTotal, &extraLines) == NULL) { p = NULL; if (!searchSpecPtr->backwards) { - linesSearched = extraLines + 1; + extraLinesSearched = extraLines; } break; } @@ -5354,7 +5444,8 @@ SearchCore(interp, searchSpecPtr, patObj) if (p == NULL) { break; } - linesSearched = extraLines; + /* We've found a multi-line match */ + extraLinesSearched = extraLines - 1; } } backwardsMatch: @@ -5441,7 +5532,7 @@ SearchCore(interp, searchSpecPtr, patObj) if (!match || ((info.extendStart == info.matches[0].start) && (info.matches[0].end == (lastOffset - firstOffset)))) { - int extraLines = 1; + int extraLines = 0; int prevFullLine; /* * If we find a match that overlaps more than one @@ -5481,7 +5572,7 @@ SearchCore(interp, searchSpecPtr, patObj) */ if (!match && !searchSpecPtr->backwards && (firstOffset == 0)) { - linesSearched = extraLines + 1; + extraLinesSearched = extraLines; } break; } @@ -5495,13 +5586,13 @@ SearchCore(interp, searchSpecPtr, patObj) if (extraLines > maxExtraLines) { if ((*searchSpecPtr->addLineProc)(lineNum + extraLines, searchSpecPtr, theLine, - &lastTotal) == NULL) { + &lastTotal, &extraLines) == NULL) { /* * There are no more acceptable lines, so * we can say we have searched all of these */ if (!match && !searchSpecPtr->backwards) { - linesSearched = extraLines + 1; + extraLinesSearched = extraLines; } break; } @@ -5557,7 +5648,7 @@ SearchCore(interp, searchSpecPtr, patObj) } if (match && ((firstOffset + info.matches[0].end) >= prevFullLine)) { - linesSearched = extraLines; + extraLinesSearched = extraLines - 1; lastFullLine = prevFullLine; } /* @@ -5588,7 +5679,7 @@ SearchCore(interp, searchSpecPtr, patObj) } if (lastBackwardsLineMatch != -1) { - if ((lineNum + linesSearched) + if ((lineNum + linesSearched + extraLinesSearched) == lastBackwardsLineMatch) { /* Possible overlap or inclusion */ int thisOffset = firstOffset + info.matches[0].end @@ -5628,7 +5719,7 @@ SearchCore(interp, searchSpecPtr, patObj) /* No overlap */ goto recordBackwardsMatch; } - } else if (lineNum + linesSearched + } else if (lineNum + linesSearched + extraLinesSearched < lastBackwardsLineMatch) { /* No overlap */ goto recordBackwardsMatch; @@ -5833,7 +5924,8 @@ SearchCore(interp, searchSpecPtr, patObj) */ nextLine: - + linesSearched += extraLinesSearched; + while (linesSearched-- > 0) { /* * If we have just completed the 'stopLine', we are done diff --git a/tests/text.test b/tests/text.test index 7ea2e2b..306dee4 100644 --- a/tests/text.test +++ b/tests/text.test @@ -6,7 +6,7 @@ # Copyright (c) 1998-1999 by Scriptics Corporation. # All rights reserved. # -# RCS: @(#) $Id: text.test,v 1.37 2004/12/04 00:04:41 dkf Exp $ +# RCS: @(#) $Id: text.test,v 1.38 2005/04/04 21:59:00 vincentdarley Exp $ package require tcltest 2.1 eval tcltest::configure $argv @@ -2714,6 +2714,121 @@ test text-20.179 {TextSearchCmd, empty search range} { .t2 search -backwards -- a 2.0 2.0 } {} +test text-20.180 {TextSearchCmd, elide up to match} { + deleteWindows + pack [text .t2] + .t2 insert 1.0 "a\nb\nc" + .t2 tag configure e -elide 1 + set res {} + lappend res [.t2 search -regexp a 1.0] + lappend res [.t2 search -regexp b 1.0] + lappend res [.t2 search -regexp c 1.0] + .t2 tag add e 1.0 2.0 + lappend res [.t2 search -regexp a 1.0] + lappend res [.t2 search -regexp b 1.0] + lappend res [.t2 search -regexp c 1.0] + lappend res [.t2 search -elide -regexp a 1.0] + lappend res [.t2 search -elide -regexp b 1.0] + lappend res [.t2 search -elide -regexp c 1.0] +} {1.0 2.0 3.0 {} 2.0 3.0 1.0 2.0 3.0} + +test text-20.181 {TextSearchCmd, elide up to match, backwards} { + deleteWindows + pack [text .t2] + .t2 insert 1.0 "a\nb\nc" + .t2 tag configure e -elide 1 + set res {} + lappend res [.t2 search -backward -regexp a 1.0] + lappend res [.t2 search -backward -regexp b 1.0] + lappend res [.t2 search -backward -regexp c 1.0] + .t2 tag add e 1.0 2.0 + lappend res [.t2 search -backward -regexp a 1.0] + lappend res [.t2 search -backward -regexp b 1.0] + lappend res [.t2 search -backward -regexp c 1.0] + lappend res [.t2 search -backward -elide -regexp a 1.0] + lappend res [.t2 search -backward -elide -regexp b 1.0] + lappend res [.t2 search -backward -elide -regexp c 1.0] +} {1.0 2.0 3.0 {} 2.0 3.0 1.0 2.0 3.0} + +test text-20.182 {TextSearchCmd, elide up to match} { + deleteWindows + pack [text .t2] + .t2 insert 1.0 "a\nb\nc" + .t2 tag configure e -elide 1 + set res {} + lappend res [.t2 search a 1.0] + lappend res [.t2 search b 1.0] + lappend res [.t2 search c 1.0] + .t2 tag add e 1.0 2.0 + lappend res [.t2 search a 1.0] + lappend res [.t2 search b 1.0] + lappend res [.t2 search c 1.0] + lappend res [.t2 search -elide a 1.0] + lappend res [.t2 search -elide b 1.0] + lappend res [.t2 search -elide c 1.0] +} {1.0 2.0 3.0 {} 2.0 3.0 1.0 2.0 3.0} + +test text-20.183 {TextSearchCmd, elide up to match, backwards} { + deleteWindows + pack [text .t2] + .t2 insert 1.0 "a\nb\nc" + .t2 tag configure e -elide 1 + set res {} + lappend res [.t2 search -backward a 1.0] + lappend res [.t2 search -backward b 1.0] + lappend res [.t2 search -backward c 1.0] + .t2 tag add e 1.0 2.0 + lappend res [.t2 search -backward a 1.0] + lappend res [.t2 search -backward b 1.0] + lappend res [.t2 search -backward c 1.0] + lappend res [.t2 search -backward -elide a 1.0] + lappend res [.t2 search -backward -elide b 1.0] + lappend res [.t2 search -backward -elide c 1.0] +} {1.0 2.0 3.0 {} 2.0 3.0 1.0 2.0 3.0} + +test text-20.184 {TextSearchCmd, elide up to match} { + deleteWindows + pack [text .t2] + .t2 insert 1.0 "aa\nbb\ncc" + .t2 tag configure e -elide 1 + set res {} + lappend res [.t2 search ab 1.0] + lappend res [.t2 search bc 1.0] + .t2 tag add e 1.1 2.1 + lappend res [.t2 search ab 1.0] + lappend res [.t2 search b 1.0] + .t2 tag remove e 1.0 end + .t2 tag add e 2.1 3.1 + lappend res [.t2 search bc 1.0] + lappend res [.t2 search c 1.0] + .t2 tag remove e 1.0 end + .t2 tag add e 2.1 3.0 + lappend res [.t2 search bc 1.0] + lappend res [.t2 search c 1.0] +} {{} {} 1.0 2.1 2.0 3.1 2.0 3.0} + +test text-20.185 {TextSearchCmd, elide up to match} { + deleteWindows + pack [text .t2] + .t2 insert 1.0 "aa\nbb\ncc" + .t2 tag configure e -elide 1 + set res {} + lappend res [.t2 search -regexp ab 1.0] + lappend res [.t2 search -regexp bc 1.0] + .t2 tag add e 1.1 2.1 + lappend res [.t2 search -regexp ab 1.0] + lappend res [.t2 search -regexp b 1.0] + .t2 tag remove e 1.0 end + .t2 tag add e 2.1 3.1 + lappend res [.t2 search -regexp bc 1.0] + lappend res [.t2 search -regexp c 1.0] + .t2 tag remove e 1.0 end + .t2 tag add e 2.1 3.0 + lappend res [.t2 search -regexp bc 1.0] + lappend res [.t2 search -regexp c 1.0] +} {{} {} 1.0 2.1 2.0 3.1 2.0 3.0} + + deleteWindows text .t2 -highlightthickness 0 -bd 0 -relief flat -padx 0 -width 100 pack .t2 -- cgit v0.12