Bug with showing selection

View: New views
8 Messages — Rating Filter:   Alert me  

Bug with showing selection

by mitchell-9 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi,

If you go to the third column of any line and select backwards one
character, the selection is shown to the beginning of the line (first
column) instead of to the second column. This occurs with both the
standard and block caret styles.

-Mitchell;

_______________________________________________
Scintilla-interest mailing list
Scintilla-interest@...
http://mailman.lyra.org/mailman/listinfo/scintilla-interest

Parent Message unknown Re: Bug with showing selection

by Florian Balmer :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

This sounds like the opposite case to the problem I've reported:
http://mailman.lyra.org/pipermail/scintilla-interest/2007-August/008994.html

Seems that there's some trouble with position calculations for the
display of the text selection, and it is handled in different ways for
different settings (with / without foreground color specified).

--Florian
_______________________________________________
Scintilla-interest mailing list
Scintilla-interest@...
http://mailman.lyra.org/mailman/listinfo/scintilla-interest

Re: Bug with showing selection

by Neil Hodgson-4 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Florian Balmer:

> This sounds like the opposite case to the problem I've reported:
> http://mailman.lyra.org/pipermail/scintilla-interest/2007-August/008994.html

    OK, I can reproduce these. Don't know when I'll have time to fix them.

    Neil
_______________________________________________
Scintilla-interest mailing list
Scintilla-interest@...
http://mailman.lyra.org/mailman/listinfo/scintilla-interest

Parent Message unknown Re: Bug with showing selection

by Florian Balmer :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

To complete the report, I found out that this issue has been
introduced with 1.74. Everything worked fine with 1.73.

Moreover, the second case reported by mitchell also affects only the
foreground color (if set), if alpha-blending for the selection is
enabled.

--Florian
_______________________________________________
Scintilla-interest mailing list
Scintilla-interest@...
http://mailman.lyra.org/mailman/listinfo/scintilla-interest

Parent Message unknown Re: Bug with showing selection

by Florian Balmer :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I *think* I have found a solution: BreakFinder::Insert() should also
accept values equal to nextBreak. Does not make sense down to the
smallest detail to me, somehow, but it works perfectly.

Probably the contributors of the original code should check that this
won't introduce new problems.

The diff is pasted below, and the modified source file is attached to
this email.

--Florian

--- PositionCache.cxx Fri Jul 27 03:52:48 2007
+++ PositionCache.cxx Thu Aug 16 13:46:08 2007
@@ -345,7 +345,7 @@
 }

 void BreakFinder::Insert(int val) {
- if (val > nextBreak) {
+ if (val >= nextBreak) {
    for (unsigned int j = 0; j<saeLen; j++) {
      if (val == selAndEdge[j]) {
        return;

// Scintilla source code edit control
/** @file PositionCache.cxx
 ** Classes for caching layout information.
 **/
// Copyright 1998-2007 by Neil Hodgson <neilh@...>
// The License.txt file describes the conditions under which this software may be distributed.

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>

#include "Platform.h"

#include "Scintilla.h"

#include "SplitVector.h"
#include "Partitioning.h"
#include "RunStyles.h"
#include "ContractionState.h"
#include "CellBuffer.h"
#include "KeyMap.h"
#include "Indicator.h"
#include "XPM.h"
#include "LineMarker.h"
#include "Style.h"
#include "ViewStyle.h"
#include "CharClassify.h"
#include "Decoration.h"
#include "Document.h"
#include "PositionCache.h"

#ifdef SCI_NAMESPACE
using namespace Scintilla;
#endif

static inline bool IsControlCharacter(int ch) {
        // iscntrl returns true for lots of chars > 127 which are displayable
        return ch >= 0 && ch < ' ';
}

LineLayout::LineLayout(int maxLineLength_) :
        lineStarts(0),
        lenLineStarts(0),
        lineNumber(-1),
        inCache(false),
        maxLineLength(-1),
        numCharsInLine(0),
        validity(llInvalid),
        xHighlightGuide(0),
        highlightColumn(0),
        selStart(0),
        selEnd(0),
        containsCaret(false),
        edgeColumn(0),
        chars(0),
        styles(0),
        styleBitsSet(0),
        indicators(0),
        positions(0),
        hsStart(0),
        hsEnd(0),
        widthLine(wrapWidthInfinite),
        lines(1) {
        Resize(maxLineLength_);
}

LineLayout::~LineLayout() {
        Free();
}

void LineLayout::Resize(int maxLineLength_) {
        if (maxLineLength_ > maxLineLength) {
                Free();
                chars = new char[maxLineLength_ + 1];
                styles = new unsigned char[maxLineLength_ + 1];
                indicators = new char[maxLineLength_ + 1];
                // Extra position allocated as sometimes the Windows
                // GetTextExtentExPoint API writes an extra element.
                positions = new int[maxLineLength_ + 1 + 1];
                maxLineLength = maxLineLength_;
        }
}

void LineLayout::Free() {
        delete []chars;
        chars = 0;
        delete []styles;
        styles = 0;
        delete []indicators;
        indicators = 0;
        delete []positions;
        positions = 0;
        delete []lineStarts;
        lineStarts = 0;
}

void LineLayout::Invalidate(validLevel validity_) {
        if (validity > validity_)
                validity = validity_;
}

int LineLayout::LineStart(int line) const {
        if (line <= 0) {
                return 0;
        } else if ((line >= lines) || !lineStarts) {
                return numCharsInLine;
        } else {
                return lineStarts[line];
        }
}

int LineLayout::LineLastVisible(int line) const {
        if (line < 0) {
                return 0;
        } else if ((line >= lines-1) || !lineStarts) {
                int startLine = LineStart(line);
                int endLine = numCharsInLine;
                while ((endLine > startLine) && IsEOLChar(chars[endLine-1])) {
                        endLine--;
                }
                return endLine;
        } else {
                return lineStarts[line+1];
        }
}

bool LineLayout::InLine(int offset, int line) const {
        return ((offset >= LineStart(line)) && (offset < LineStart(line + 1)) ||
                ((offset == numCharsInLine) && (line == (lines-1))));
}

void LineLayout::SetLineStart(int line, int start) {
        if ((line >= lenLineStarts) && (line != 0)) {
                int newMaxLines = line + 20;
                int *newLineStarts = new int[newMaxLines];
                if (!newLineStarts)
                        return;
                for (int i = 0; i < newMaxLines; i++) {
                        if (i < lenLineStarts)
                                newLineStarts[i] = lineStarts[i];
                        else
                                newLineStarts[i] = 0;
                }
                delete []lineStarts;
                lineStarts = newLineStarts;
                lenLineStarts = newMaxLines;
        }
        lineStarts[line] = start;
}

void LineLayout::SetBracesHighlight(Range rangeLine, Position braces[],
                                    char bracesMatchStyle, int xHighlight) {
        if (rangeLine.ContainsCharacter(braces[0])) {
                int braceOffset = braces[0] - rangeLine.start;
                if (braceOffset < numCharsInLine) {
                        bracePreviousStyles[0] = styles[braceOffset];
                        styles[braceOffset] = bracesMatchStyle;
                }
        }
        if (rangeLine.ContainsCharacter(braces[1])) {
                int braceOffset = braces[1] - rangeLine.start;
                if (braceOffset < numCharsInLine) {
                        bracePreviousStyles[1] = styles[braceOffset];
                        styles[braceOffset] = bracesMatchStyle;
                }
        }
        if ((braces[0] >= rangeLine.start && braces[1] <= rangeLine.end) ||
                (braces[1] >= rangeLine.start && braces[0] <= rangeLine.end)) {
                xHighlightGuide = xHighlight;
        }
}

void LineLayout::RestoreBracesHighlight(Range rangeLine, Position braces[]) {
        if (rangeLine.ContainsCharacter(braces[0])) {
                int braceOffset = braces[0] - rangeLine.start;
                if (braceOffset < numCharsInLine) {
                        styles[braceOffset] = bracePreviousStyles[0];
                }
        }
        if (rangeLine.ContainsCharacter(braces[1])) {
                int braceOffset = braces[1] - rangeLine.start;
                if (braceOffset < numCharsInLine) {
                        styles[braceOffset] = bracePreviousStyles[1];
                }
        }
        xHighlightGuide = 0;
}

int LineLayout::FindBefore(int x, int lower, int upper) const {
        do {
                int middle = (upper + lower + 1) / 2; // Round high
                int posMiddle = positions[middle];
                if (x < posMiddle) {
                        upper = middle - 1;
                } else {
                        lower = middle;
                }
        } while (lower < upper);
        return lower;
}

LineLayoutCache::LineLayoutCache() :
        level(0), length(0), size(0), cache(0),
        allInvalidated(false), styleClock(-1), useCount(0) {
        Allocate(0);
}

LineLayoutCache::~LineLayoutCache() {
        Deallocate();
}

void LineLayoutCache::Allocate(int length_) {
        PLATFORM_ASSERT(cache == NULL);
        allInvalidated = false;
        length = length_;
        size = length;
        if (size > 1) {
                size = (size / 16 + 1) * 16;
        }
        if (size > 0) {
                cache = new LineLayout * [size];
        }
        for (int i = 0; i < size; i++)
                cache[i] = 0;
}

void LineLayoutCache::AllocateForLevel(int linesOnScreen, int linesInDoc) {
        PLATFORM_ASSERT(useCount == 0);
        int lengthForLevel = 0;
        if (level == llcCaret) {
                lengthForLevel = 1;
        } else if (level == llcPage) {
                lengthForLevel = linesOnScreen + 1;
        } else if (level == llcDocument) {
                lengthForLevel = linesInDoc;
        }
        if (lengthForLevel > size) {
                Deallocate();
                Allocate(lengthForLevel);
        } else {
                if (lengthForLevel < length) {
                        for (int i = lengthForLevel; i < length; i++) {
                                delete cache[i];
                                cache[i] = 0;
                        }
                }
                length = lengthForLevel;
        }
        PLATFORM_ASSERT(length == lengthForLevel);
        PLATFORM_ASSERT(cache != NULL || length == 0);
}

void LineLayoutCache::Deallocate() {
        PLATFORM_ASSERT(useCount == 0);
        for (int i = 0; i < length; i++)
                delete cache[i];
        delete []cache;
        cache = 0;
        length = 0;
        size = 0;
}

void LineLayoutCache::Invalidate(LineLayout::validLevel validity_) {
        if (cache && !allInvalidated) {
                for (int i = 0; i < length; i++) {
                        if (cache[i]) {
                                cache[i]->Invalidate(validity_);
                        }
                }
                if (validity_ == LineLayout::llInvalid) {
                        allInvalidated = true;
                }
        }
}

void LineLayoutCache::SetLevel(int level_) {
        allInvalidated = false;
        if ((level_ != -1) && (level != level_)) {
                level = level_;
                Deallocate();
        }
}

LineLayout *LineLayoutCache::Retrieve(int lineNumber, int lineCaret, int maxChars, int styleClock_,
                                      int linesOnScreen, int linesInDoc) {
        AllocateForLevel(linesOnScreen, linesInDoc);
        if (styleClock != styleClock_) {
                Invalidate(LineLayout::llCheckTextAndStyle);
                styleClock = styleClock_;
        }
        allInvalidated = false;
        int pos = -1;
        LineLayout *ret = 0;
        if (level == llcCaret) {
                pos = 0;
        } else if (level == llcPage) {
                if (lineNumber == lineCaret) {
                        pos = 0;
                } else if (length > 1) {
                        pos = 1 + (lineNumber % (length - 1));
                }
        } else if (level == llcDocument) {
                pos = lineNumber;
        }
        if (pos >= 0) {
                PLATFORM_ASSERT(useCount == 0);
                if (cache && (pos < length)) {
                        if (cache[pos]) {
                                if ((cache[pos]->lineNumber != lineNumber) ||
                                        (cache[pos]->maxLineLength < maxChars)) {
                                        delete cache[pos];
                                        cache[pos] = 0;
                                }
                        }
                        if (!cache[pos]) {
                                cache[pos] = new LineLayout(maxChars);
                        }
                        if (cache[pos]) {
                                cache[pos]->lineNumber = lineNumber;
                                cache[pos]->inCache = true;
                                ret = cache[pos];
                                useCount++;
                        }
                }
        }

        if (!ret) {
                ret = new LineLayout(maxChars);
                ret->lineNumber = lineNumber;
        }

        return ret;
}

void LineLayoutCache::Dispose(LineLayout *ll) {
        allInvalidated = false;
        if (ll) {
                if (!ll->inCache) {
                        delete ll;
                } else {
                        useCount--;
                }
        }
}

void BreakFinder::Insert(int val) {
        if (val >= nextBreak) {
                for (unsigned int j = 0; j<saeLen; j++) {
                        if (val == selAndEdge[j]) {
                                return;
                        } if (val < selAndEdge[j]) {
                                for (unsigned int k = saeLen; j>k; k--) {
                                        selAndEdge[k] = selAndEdge[k-1];
                                }
                                saeLen++;
                                selAndEdge[j] = val;
                                return;
                        }
                }
                // Not less than any so append
                selAndEdge[saeLen++] = val;
        }
}

BreakFinder::BreakFinder(LineLayout *ll_, int lineStart_, int lineEnd_, int posLineStart_, int xStart) :
        ll(ll_),
        lineStart(lineStart_),
        lineEnd(lineEnd_),
        posLineStart(posLineStart_),
        nextBreak(lineStart_),
        saeLen(0),
        saeCurrentPos(0),
        saeNext(0),
        subBreak(-1) {
        for (unsigned int j=0; j < sizeof(selAndEdge) / sizeof(selAndEdge[0]); j++) {
                selAndEdge[j] = 0;
        }

        // Search for first visible break
        // First find the first visible character
        nextBreak = ll->FindBefore(xStart, lineStart, lineEnd);
        // Now back to a style break
        while ((nextBreak > lineStart) && (ll->styles[nextBreak] == ll->styles[nextBreak - 1])) {
                nextBreak--;
        }

        if (ll->selStart != ll->selEnd) {
                Insert(ll->selStart - posLineStart - 1);
                Insert(ll->selEnd - posLineStart - 1);
        }

        Insert(ll->edgeColumn - 1);
        Insert(lineEnd - 1);
        saeNext = (saeLen > 0) ? selAndEdge[0] : -1;
}

int BreakFinder::First() {
        return nextBreak;
}

int BreakFinder::Next() {
        if (subBreak == -1) {
                int prev = nextBreak;
                while (nextBreak < lineEnd) {
                        if ((ll->styles[nextBreak] != ll->styles[nextBreak + 1]) ||
                                        (nextBreak == saeNext) ||
                                        IsControlCharacter(ll->chars[nextBreak]) || IsControlCharacter(ll->chars[nextBreak + 1])) {
                                if (nextBreak == saeNext) {
                                        saeCurrentPos++;
                                        saeNext = (saeLen > saeCurrentPos) ? selAndEdge[saeCurrentPos] : -1;
                                }
                                nextBreak++;
                                if ((nextBreak - prev) < lengthStartSubdivision) {
                                        return nextBreak;
                                }
                                break;
                        }
                        nextBreak++;
                }
                if ((nextBreak - prev) < lengthStartSubdivision) {
                        return nextBreak;
                }
                subBreak = prev;
        }
        // Splitting up a long run from prev to nextBreak in lots of approximately lengthEachSubdivision.
        // For very long runs add extra breaks after spaces or if no spaces before low punctuation.
        if ((nextBreak - subBreak) <= lengthEachSubdivision) {
                subBreak = -1;
                return nextBreak;
        } else {
                int lastGoodBreak = -1;
                int lastOKBreak = -1;
                int j;
                for (j = subBreak + 1; j <= nextBreak; j++) {
                        if (IsSpaceOrTab(ll->chars[j - 1]) && !IsSpaceOrTab(ll->chars[j])) {
                                lastGoodBreak = j;
                        }
                        if (ll->chars[j] < 'A') {
                                lastOKBreak = j;
                        }
                        if (((j - subBreak) >= lengthEachSubdivision) && ((lastGoodBreak >= 0) || (lastOKBreak >= 0))) {
                                break;
                        }
                }
                if (lastGoodBreak >= 0) {
                        subBreak = lastGoodBreak;
                } else if (lastOKBreak >= 0) {
                        subBreak = lastOKBreak;
                } else {
                        subBreak = nextBreak;
                }
                if (subBreak >= nextBreak) {
                        subBreak = -1;
                        return nextBreak;
                } else {
                        return subBreak;
                }
        }
}

PositionCacheEntry::PositionCacheEntry() :
        styleNumber(0), len(0), clock(0), positions(0) {
}

void PositionCacheEntry::Set(unsigned int styleNumber_, const char *s_,
        unsigned int len_, int *positions_, unsigned int clock_) {
        Clear();
        styleNumber = styleNumber_;
        len = len_;
        clock = clock_;
        if (s_ && positions_) {
                positions = new short[len + (len + 1) / 2];
                for (unsigned int i=0;i<len;i++) {
                        positions[i] = static_cast<short>(positions_[i]);
                }
                memcpy(reinterpret_cast<char *>(positions + len), s_, len);
        }
}

PositionCacheEntry::~PositionCacheEntry() {
        Clear();
}

void PositionCacheEntry::Clear() {
        delete []positions;
        positions = 0;
        styleNumber = 0;
        len = 0;
        clock = 0;
}

bool PositionCacheEntry::Retrieve(unsigned int styleNumber_, const char *s_,
        unsigned int len_, int *positions_) const {
        if ((styleNumber == styleNumber_) && (len == len_) &&
                (memcmp(reinterpret_cast<char *>(positions + len), s_, len)== 0)) {
                for (unsigned int i=0;i<len;i++) {
                        positions_[i] = positions[i];
                }
                return true;
        } else {
                return false;
        }
}

int PositionCacheEntry::Hash(unsigned int styleNumber, const char *s, unsigned int len) {
        unsigned int ret = s[0] << 7;
        for (unsigned int i=0; i<len; i++) {
                ret *= 1000003;
                ret ^= s[i];
        }
        ret *= 1000003;
        ret ^= len;
        ret *= 1000003;
        ret ^= styleNumber;
        return ret;
}

bool PositionCacheEntry::NewerThan(const PositionCacheEntry &other) {
        return clock > other.clock;
}

void PositionCacheEntry::ResetClock() {
        if (clock > 0) {
                clock = 1;
        }
}

PositionCache::PositionCache() {
        size = 0x400;
        clock = 1;
        pces = new PositionCacheEntry[size];
        allClear = true;
}

PositionCache::~PositionCache() {
        Clear();
        delete []pces;
}

void PositionCache::Clear() {
        if (!allClear) {
                for (size_t i=0;i<size;i++) {
                        pces[i].Clear();
                }
        }
        clock = 1;
        allClear = true;
}

void PositionCache::SetSize(size_t size_) {
        Clear();
        delete []pces;
        size = size_;
        pces = new PositionCacheEntry[size];
}

void PositionCache::MeasureWidths(Surface *surface, ViewStyle &vstyle, unsigned int styleNumber,
        const char *s, unsigned int len, int *positions) {
        allClear = false;
        int probe = -1;
        if ((size > 0) && (len < 30)) {
                // Only store short strings in the cache so it doesn't churn with
                // long comments with only a single comment.

                // Two way associative: try two probe positions.
                int hashValue = PositionCacheEntry::Hash(styleNumber, s, len);
                probe = hashValue % size;
                if (pces[probe].Retrieve(styleNumber, s, len, positions)) {
                        return;
                }
                int probe2 = (hashValue * 37) % size;
                if (pces[probe2].Retrieve(styleNumber, s, len, positions)) {
                        return;
                }
                // Not found. Choose the oldest of the two slots to replace
                if (pces[probe].NewerThan(pces[probe2])) {
                        probe = probe2;
                }
        }
        surface->MeasureWidths(vstyle.styles[styleNumber].font, s, len, positions);
        if (probe >= 0) {
                clock++;
                if (clock > 60000) {
                        // Since there are only 16 bits for the clock, wrap it round and
                        // reset all cache entries so none get stuck with a high clock.
                        for (size_t i=0;i<size;i++) {
                                pces[i].ResetClock();
                        }
                        clock = 2;
                }
                pces[probe].Set(styleNumber, s, len, positions, clock);
        }
}

_______________________________________________
Scintilla-interest mailing list
Scintilla-interest@...
http://mailman.lyra.org/mailman/listinfo/scintilla-interest

Re: Bug with showing selection

by Florian Balmer :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

> The diff is pasted below, and the modified source file is attached to
> this email.

I'm sorry the attachment didn't work as I expected...
--Florian
_______________________________________________
Scintilla-interest mailing list
Scintilla-interest@...
http://mailman.lyra.org/mailman/listinfo/scintilla-interest

Re: Bug with showing selection

by Neil Hodgson :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Florian Balmer:

> I *think* I have found a solution: BreakFinder::Insert() should also
> accept values equal to nextBreak. Does not make sense down to the
> smallest detail to me, somehow, but it works perfectly.

   Works OK for me too. The logic here is a bit too complex with some
of the indices deliberately off-by-one.

> Probably the contributors of the original code should check that this
> won't introduce new problems.

   That's me and I'm not completely certain either.

   Since the problem makes the current release worse than the previous
I may make a release just for this issue in the next few days if I
have time.

   Neil
_______________________________________________
Scintilla-interest mailing list
Scintilla-interest@...
http://mailman.lyra.org/mailman/listinfo/scintilla-interest

Re: Bug with showing selection

by Enrico Tröger :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Thu, 16 Aug 2007 13:59:37 +0200, "Florian Balmer"
<florian.balmer@...> wrote:

> I *think* I have found a solution: BreakFinder::Insert() should also
> accept values equal to nextBreak. Does not make sense down to the
> smallest detail to me, somehow, but it works perfectly.
>
> Probably the contributors of the original code should check that this
> won't introduce new problems.
>
> The diff is pasted below, and the modified source file is attached to
> this email.
Thanks for the fix. Works fine.

Regards,
Enrico

--
Get my GPG key from http://www.uvena.de/pub.key
Powered by Debian GNU/Linux, GnuPG and Sylpheed.


_______________________________________________
Scintilla-interest mailing list
Scintilla-interest@...
http://mailman.lyra.org/mailman/listinfo/scintilla-interest

attachment0 (196 bytes) Download Attachment