// This module implements the "official" high-level API of the Qt port of
// Scintilla.  It is modelled on QTextEdit - a method of the same name should
// behave in the same way.
//
// Copyright (c) 2006
// 	Riverbank Computing Limited <info@riverbankcomputing.co.uk>
// 
// This file is part of QScintilla.
// 
// This copy of QScintilla is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or (at your option) any
// later version.
// 
// QScintilla is supplied in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
// details.
// 
// You should have received a copy of the GNU General Public License along with
// QScintilla; see the file LICENSE.  If not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.


#include <string.h>
#include <qapplication.h>
#include <qcolor.h>

#include "qextscintilla.h"
#include "qextscintillalexer.h"
#include "qextscintillaapis.h"
#include "qextscintillacommandset.h"


// Make sure these match the values in Scintilla.h.  We don't #include that
// file because it just causes more clashes.
#define KEYWORDSET_MAX  8
#define MARKER_MAX  31


// The default fold margin width.
static const int defaultFoldMarginWidth = 14;

// The default set of characters that make up a word.
static const char *defaultWordChars = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";


// The ctor.
QextScintilla::QextScintilla(QWidget *parent,const char *name,WFlags f)
    : QextScintillaBase(parent,name,f), allocatedMarkers(0), oldPos(-1),
      selText(FALSE), fold(NoFoldStyle), autoInd(FALSE),
      braceMode(NoBraceMatch), acSource(AcsDocument), acThresh(-1),
      acStart(""), acAPIs(0), ctAPIs(0), maxCallTips(-1),
      showSingle(FALSE), modified(FALSE), explicit_fillups(FALSE),
      fillups_enabled(FALSE), saved_fillups("")
{
    connect(this,SIGNAL(SCN_MODIFYATTEMPTRO()),
             SIGNAL(modificationAttempted()));

    connect(this,SIGNAL(SCN_MODIFIED(int,int,const char *,int,int,int,int,int)),
             SLOT(handleModified(int,int,const char *,int,int,int,int,int)));
    connect(this,SIGNAL(SCN_CALLTIPCLICK(int)),
             SLOT(handleCallTipClick(int)));
    connect(this,SIGNAL(SCN_CHARADDED(int)),
             SLOT(handleCharAdded(int)));
    connect(this,SIGNAL(SCN_MARGINCLICK(int,int,int)),
             SLOT(handleMarginClick(int,int,int)));
    connect(this,SIGNAL(SCN_SAVEPOINTREACHED()),
             SLOT(handleSavePointReached()));
    connect(this,SIGNAL(SCN_SAVEPOINTLEFT()),
             SLOT(handleSavePointLeft()));
    connect(this,SIGNAL(SCN_UPDATEUI()),
             SLOT(handleUpdateUI()));
    connect(this,SIGNAL(QSCN_SELCHANGED(bool)),
             SLOT(handleSelectionChanged(bool)));
    connect(this,SIGNAL(SCN_USERLISTSELECTION(const char *,int)),
             SLOT(handleUserListSelection(const char *,int)));

    // Set the default font.
    setFont(QApplication::font());

    // Set the default fore and background colours.
    QColorGroup cg = QApplication::palette().active();
    setColor(cg.text());
    setPaper(cg.base());

#if defined(Q_OS_WIN)
    setEolMode(EolWindows);
#elif defined(Q_OS_MAC)
    setEolMode(EolMac);
#else
    setEolMode(EolUnix);
#endif

    // Capturing the mouse seems to cause problems on multi-head systems.
    // Qt should do the right thing anyway.
    SendScintilla(SCI_SETMOUSEDOWNCAPTURES,0UL);

    SendScintilla(SCI_SETPROPERTY,"fold","1");

    setMatchedBraceForegroundColor(blue);
    setUnmatchedBraceForegroundColor(red);

    setLexer();

    // Set the visible policy.  These are the same as SciTE's defaults
    // which, presumably, are sensible.
    SendScintilla(SCI_SETVISIBLEPOLICY,VISIBLE_STRICT|VISIBLE_SLOP,4);

    // Create the standard command set.
    stdCmds = new QextScintillaCommandSet(this);

    doc.display(this,0);
}


// The dtor.
QextScintilla::~QextScintilla()
{
    doc.undisplay(this);
    delete stdCmds;
}


// Return the current text colour.
QColor QextScintilla::color() const
{
    return nl_text_colour;
}


// Set the text colour.
void QextScintilla::setColor(const QColor &c)
{
    if (lex.isNull())
    {
        // Assume style 0 applies to everything so that we don't need to use
        // SCI_STYLECLEARALL which clears everything.
        SendScintilla(SCI_STYLESETFORE, 0, c);
        nl_text_colour = c;
    }
}


// Return the current paper colour.
QColor QextScintilla::paper() const
{
    return nl_paper_colour;
}


// Set the paper colour.
void QextScintilla::setPaper(const QColor &c)
{
    if (lex.isNull())
    {
        // Assume style 0 applies to everything so that we don't need to use
        // SCI_STYLECLEARALL which clears everything.  We still have to set the
        // default style as well for the background without any text.
        SendScintilla(SCI_STYLESETBACK, 0, c);
        SendScintilla(SCI_STYLESETBACK, STYLE_DEFAULT, c);
        nl_paper_colour = c;
    }
}


// Set the default font.
void QextScintilla::setFont(const QFont &f)
{
    if (lex.isNull())
    {
        // Assume style 0 applies to everything so that we don't need to use
        // SCI_STYLECLEARALL which clears everything.
        setStylesFont(f, 0);
        nl_font = f;
    }
}


// Enable/disable auto-indent.
void QextScintilla::setAutoIndent(bool autoindent)
{
    autoInd = autoindent;
}


// Set the brace matching mode.
void QextScintilla::setBraceMatching(BraceMatch bm)
{
    braceMode = bm;
}


// Handle the addition of a character.
void QextScintilla::handleCharAdded(int ch)
{
    // Ignore if there is a selection.
    long pos = SendScintilla(SCI_GETSELECTIONSTART);

    if (pos != SendScintilla(SCI_GETSELECTIONEND) || pos == 0)
        return;

    // If auto-completion is already active then see if this character is a
    // start character.  If it is then create a new list which will be a
    // subset of the current one.  The case where it isn't a start
    // character seem to be handled correctly elsewhere.
    if (isListActive())
    {
        if (isAutoCStartChar(ch))
        {
            cancelList();
            startAutoCompletion(acSource, FALSE, FALSE);
        }

        return;
    }

    // Handle call tips.
    if (strchr("(),", ch) != NULL)
        callTip();

    // Handle auto-indentation.
    if (autoInd)
        if (lex.isNull() || (lex -> autoIndentStyle() & AiMaintain))
            maintainIndentation(ch,pos);
        else
            autoIndentation(ch,pos);

    // See if we might want to start auto-completion.
    if (!isCallTipActive())
        if (isAutoCStartChar(ch))
            startAutoCompletion(acSource, FALSE, FALSE);
        else if (acThresh >= 1 && isWordChar(ch))
            startAutoCompletion(acSource, TRUE, FALSE);
}


// See if a call tip is active.
bool QextScintilla::isCallTipActive()
{
    return SendScintilla(SCI_CALLTIPACTIVE);
}


// Handle a possible change to any current call tip.
void QextScintilla::callTip()
{
    if (!ctAPIs)
        return;

    long pos = SendScintilla(SCI_GETCURRENTPOS);
    long linenr = SendScintilla(SCI_LINEFROMPOSITION,pos);
    long linelen = SendScintilla(SCI_LINELENGTH,linenr);

    char *lbuf = new char[linelen + 1];

    int loff = SendScintilla(SCI_GETCURLINE,linelen + 1,lbuf);

    int commas = 0, start = -1;

    // Move backwards through the line looking for the start of the current
    // call tip and working out which argument it is.
    while (loff > 0)
    {
        char ch = lbuf[--loff];

        if (ch == ',')
            ++commas;
        else if (ch == ')')
        {
            int depth = 1;

            // Ignore everything back to the start of the
            // corresponding parenthesis.
            while (loff > 0)
            {
                ch = lbuf[--loff];

                if (ch == ')')
                    ++depth;
                else if (ch == '(' && --depth == 0)
                    break;
            }
        }
        else if (ch == '(' && loff > 0)
        {
            if (isWordChar(lbuf[loff - 1]))
            {
                // The parenthesis is preceded by a word so
                // find the start of that word.
                lbuf[loff--] = '\0';

                while (loff >= 0)
                {
                    if (!isWordChar(lbuf[loff]) && !isAutoCStartChar(lbuf[loff]))
                        break;

                    --loff;
                }

                start = loff + 1;
                break;
            }

            // We are between parentheses that do not correspond to
            // a call tip, so reset the argument count.
            commas = 0;
        }
    }

    // Cancel any existing call tip.
    SendScintilla(SCI_CALLTIPCANCEL);

    // Done if there is no new call tip to set.
    if (start < 0)
    {
        delete []lbuf;
        return;
    }

    QString ct = ctAPIs -> callTips(&lbuf[start],maxCallTips,commas);

    delete []lbuf;

    if (ct.isEmpty())
        return;

    ctpos = SendScintilla(SCI_POSITIONFROMLINE,linenr) + start;

    SendScintilla(SCI_CALLTIPSHOW,ctpos,ct.latin1());

    // Done if there is more than one line in the call tip or there isn't a
    // down arrow at the start.
    if (ct[0] == '\002' || ct.find('\n') >= 0)
        return;

    // Highlight the current argument.
    int astart;

    if (commas == 0)
        astart = ct.find('(');
    else
    {
        astart = -1;

        do
            astart = ct.find(',',astart + 1);
        while (astart >= 0 && --commas > 0);
    }

    int len = ct.length();

    if (astart < 0 || ++astart == len)
        return;

    // The end is at the next comma or unmatched closing parenthesis.
    int aend, depth = 0;

    for (aend = astart; aend < len; ++aend)
    {
        QChar ch = ct.at(aend);

        if (ch == ',' && depth == 0)
            break;
        else if (ch == '(')
            ++depth;
        else if (ch == ')')
        {
            if (depth == 0)
                break;

            --depth;
        }
    }

    if (astart != aend)
        SendScintilla(SCI_CALLTIPSETHLT,astart,aend);
}


// Handle a call tip click.
void QextScintilla::handleCallTipClick(int dir)
{
    if (!ctAPIs)
        return;

    QString ct = ctAPIs -> callTipsNextPrev(dir);

    if (ct.isNull())
        return;

    SendScintilla(SCI_CALLTIPSHOW,ctpos,ct.latin1());
}


// Possibly start auto-completion.
void QextScintilla::startAutoCompletion(AutoCompletionSource acs,
        bool checkThresh, bool single)
{
    // Get the current line.
    long len = SendScintilla(SCI_GETCURLINE) + 1;

    char *line = new char[len];

    int wend = SendScintilla(SCI_GETCURLINE, len, line);

    // Find the start of the auto-completion text.
    int wstart = wend;
    bool numeric = true;

    while (wstart > 0)
    {
        char ch = line[wstart - 1];

        // Don't auto-complete numbers.
        if (ch < '0' || ch > '9')
            numeric = false;

        if (!isWordChar(ch) && !isAutoCStartChar(ch))
            break;

        --wstart;
    }

    int wlen = wend - wstart;

    if (numeric || wlen == 0 || (checkThresh && wlen < acThresh))
        return;

    // Isolate the auto-completion text.
    char *word = &line[wstart];
    line[wend] = '\0';

    // Generate the string representing the valid words to select from.
    QStringList wlist;
    bool cs = !SendScintilla(SCI_AUTOCGETIGNORECASE);

    if (acs == AcsAll || acs == AcsDocument)
    {
        SendScintilla(SCI_SETSEARCHFLAGS,SCFIND_WORDSTART | (cs ? SCFIND_MATCHCASE : 0));

        long pos = 0;
        long dlen = SendScintilla(SCI_GETLENGTH);
        long caret = SendScintilla(SCI_GETCURRENTPOS);
        QString root(word);

        for (;;)
        {
            long fstart;

            SendScintilla(SCI_SETTARGETSTART,pos);
            SendScintilla(SCI_SETTARGETEND,dlen);

            if ((fstart = SendScintilla(SCI_SEARCHINTARGET,wlen,word)) < 0)
                break;

            // Move past the root part.
            pos = fstart + wlen;

            // Skip if this is the word we are auto-completing.
            if (pos == caret)
                continue;

            // Get the rest of this word.
            QString w(root);

            while (pos < dlen)
            {
                char ch = SendScintilla(SCI_GETCHARAT,pos);

                if (!isWordChar(ch))
                    break;

                w += ch;

                ++pos;
            }

            // Add the word if it isn't already there.
            if (wlist.findIndex(w) < 0)
                wlist.append(w);
        }
    }

    if ((acs == AcsAll || acs == AcsAPIs) && acAPIs)
        acAPIs->autoCompletionList(word, cs, wlist);

    delete []line;

    if (wlist.isEmpty())
        return;

    wlist.sort();

    const char sep = '\x03';

    SendScintilla(SCI_AUTOCSETCHOOSESINGLE,single);
    SendScintilla(SCI_AUTOCSETSEPARATOR, sep);
    SendScintilla(SCI_AUTOCSHOW, wlen, wlist.join(QChar(sep)).latin1());
}


// Check if a character is an auto-completion start character.
bool QextScintilla::isAutoCStartChar(char ch) const
{
    const char *start_chars = 0;

    if (!lex.isNull())
        start_chars = lex->autoCompletionStartCharacters();

    if (!start_chars)
        start_chars = acStart;

    return (strchr(start_chars, ch) != NULL);
}


// Maintain the indentation of the previous line.
void QextScintilla::maintainIndentation(char ch,long pos)
{
    if (ch != '\r' && ch != '\n')
        return;

    int curr_line = SendScintilla(SCI_LINEFROMPOSITION,pos);

    // Get the indentation of the preceding non-zero length line.
    int ind = 0;

    for (int line = curr_line - 1; line >= 0; --line)
    {
        if (SendScintilla(SCI_GETLINEENDPOSITION,line) >
            SendScintilla(SCI_POSITIONFROMLINE,line))
        {
            ind = indentation(line);
            break;
        }
    }

    if (ind > 0)
        autoIndentLine(pos,curr_line,ind);
}


// Implement auto-indentation.
void QextScintilla::autoIndentation(char ch,long pos)
{
    int curr_line = SendScintilla(SCI_LINEFROMPOSITION,pos);
    int ind_width = indentationWidth();
    long curr_line_start = SendScintilla(SCI_POSITIONFROMLINE,curr_line);

    const char *block_start = lex -> blockStart();
    bool start_single = (block_start && strlen(block_start) == 1);

    const char *block_end = lex -> blockEnd();
    bool end_single = (block_end && strlen(block_end) == 1);

    if (end_single && block_end[0] == ch)
    {
        if ((lex -> autoIndentStyle() & AiClosing) && rangeIsWhitespace(curr_line_start,pos - 1))
            autoIndentLine(pos,curr_line,blockIndent(curr_line - 1) - indentationWidth());
    }
    else if (start_single && block_start[0] == ch)
    {
        // De-indent if we have already indented because the previous
        // line was a start of block keyword.
        if ((lex->autoIndentStyle() & AiOpening) && curr_line > 0 && getIndentState(curr_line - 1) == isKeywordStart && rangeIsWhitespace(curr_line_start, pos - 1))
            autoIndentLine(pos,curr_line,blockIndent(curr_line - 1) - indentationWidth());
    }
    else if (ch == '\r' || ch == '\n')
        autoIndentLine(pos,curr_line,blockIndent(curr_line - 1));
}


// Set the indentation for a line.
void QextScintilla::autoIndentLine(long pos,int line,int indent)
{
    if (indent < 0)
        return;

    long pos_before = SendScintilla(SCI_GETLINEINDENTPOSITION,line);
    SendScintilla(SCI_SETLINEINDENTATION,line,indent);
    long pos_after = SendScintilla(SCI_GETLINEINDENTPOSITION,line);
    long new_pos = -1;

    if (pos_after > pos_before)
        new_pos = pos + (pos_after - pos_before);
    else if (pos_after < pos_before && pos >= pos_after)
        if (pos >= pos_before)
            new_pos = pos + (pos_after - pos_before);
        else
            new_pos = pos_after;

    if (new_pos >= 0)
        SendScintilla(SCI_SETSEL,new_pos,new_pos);
}


// Return the indentation of the block defined by the given line (or something
// significant before).
int QextScintilla::blockIndent(int line)
{
    if (line < 0)
        return 0;

    // Handle the trvial case.
    if (!lex -> blockStartKeyword() && !lex -> blockStart() && !lex -> blockEnd())
        return indentation(line);

    int line_limit = line - lex -> blockLookback();

    if (line_limit < 0)
        line_limit = 0;

    for (int l = line; l >= line_limit; --l)
    {
        IndentState istate = getIndentState(l);

        if (istate != isNone)
        {
            int ind_width = indentationWidth();
            int ind = indentation(l);

            if (istate == isBlockStart)
            {
                if (lex -> autoIndentStyle() & AiOpening)
                    ind += ind_width;
            }
            else if (istate == isBlockEnd)
            {
                if (!(lex -> autoIndentStyle() & AiClosing))
                    ind -= ind_width;

                if (ind < 0)
                    ind = 0;
            }
            else if (line == l)
                ind += ind_width;

            return ind;
        }
    }

    return indentation(line);
}


// Return TRUE if all characters starting at spos up to, but not including
// epos, are spaces or tabs.
bool QextScintilla::rangeIsWhitespace(long spos,long epos)
{
    while (spos < epos)
    {
        char ch = SendScintilla(SCI_GETCHARAT,spos);

        if (ch != ' ' && ch != '\t')
            return FALSE;

        ++spos;
    }

    return TRUE;
}


// Returns the indentation state of a line.
QextScintilla::IndentState QextScintilla::getIndentState(int line)
{
    IndentState istate;

    // Get the styled text.
    long spos = SendScintilla(SCI_POSITIONFROMLINE,line);
    long epos = SendScintilla(SCI_POSITIONFROMLINE,line + 1);

    char *text = new char[(epos - spos + 1) * 2];

    SendScintilla(SCI_GETSTYLEDTEXT,spos,epos,text);

    int style, bstart_off, bend_off;

    // Block start/end takes precedence over keywords.
    const char *bstart_words = lex->blockStart(&style);
    bstart_off = findStyledWord(text, style, bstart_words);

    const char *bend_words = lex->blockEnd(&style);
    bend_off = findStyledWord(text, style, bend_words);

    // If there is a block start but no block end characters then ignore it
    // unless the block start is the last significant thing on the line,
    // ie. assume Python-like blocking.
    if (bstart_off >= 0 && !bend_words)
        for (int i = bstart_off * 2; text[i] != '\0'; i += 2)
            if (!QChar(text[i]).isSpace())
                return isNone;

    if (bstart_off > bend_off)
        istate = isBlockStart;
    else if (bend_off > bstart_off)
        istate = isBlockEnd;
    else
    {
        const char *words = lex->blockStartKeyword(&style);

        istate = (findStyledWord(text,style,words) >= 0) ? isKeywordStart : isNone;
    }

    delete[] text;

    return istate;
}


// text is a pointer to some styled text (ie. a character byte followed by a
// style byte).  style is a style number.  words is a space separated list of
// words.  Returns the position in the text immediately after the last one of
// the words with the style.  The reason we are after the last, and not the
// first, occurance is that we are looking for words that start and end a block
// where the latest one is the most significant.
int QextScintilla::findStyledWord(const char *text,int style,const char *words)
{
    if (!words)
        return -1;

    // Find the range of text with the style we are looking for.
    const char *stext;

    for (stext = text; stext[1] != style; stext += 2)
        if (stext[0] == '\0')
            return -1;

    // Move to the last character.
    const char *etext = stext;

    while (etext[2] != '\0')
        etext += 2;

    // Backtrack until we find the style.  There will be one.
    while (etext[1] != style)
        etext -= 2;

    // Look for each word in turn.
    while (words[0] != '\0')
    {
        // Find the end of the word.
        const char *eword = words;

        while (eword[1] != ' ' && eword[1] != '\0')
            ++eword;

        // Now search the text backwards.
        const char *wp = eword;

        for (const char *tp = etext; tp >= stext; tp -= 2)
        {
            if (tp[0] != wp[0] || tp[1] != style)
            {
                // Reset the search.
                wp = eword;
                continue;
            }

            // See if all the word has matched.
            if (wp-- == words)
                return ((tp - text) / 2) + (eword - words) + 1;
        }

        // Move to the start of the next word if there is one.
        words = eword + 1;

        if (words[0] == ' ')
            ++words;
    }

    return -1;
}


// Return TRUE if the code page is UTF8.
bool QextScintilla::isUtf8()
{
    return (SendScintilla(SCI_GETCODEPAGE) == SC_CP_UTF8);
}


// Set the code page.
void QextScintilla::setUtf8(bool cp)
{
    SendScintilla(SCI_SETCODEPAGE,(cp ? SC_CP_UTF8 : 0));
}


// Return the end-of-line mode.
QextScintilla::EolMode QextScintilla::eolMode()
{
    return (EolMode)SendScintilla(SCI_GETEOLMODE);
}


// Set the end-of-line mode.
void QextScintilla::setEolMode(EolMode mode)
{
    SendScintilla(SCI_SETEOLMODE,mode);
}


// Convert the end-of-lines to a particular mode.
void QextScintilla::convertEols(EolMode mode)
{
    SendScintilla(SCI_CONVERTEOLS,mode);
}


// Return the edge colour.
QColor QextScintilla::edgeColor()
{
        long res = SendScintilla(SCI_GETEDGECOLOUR);

        return QColor((int)res, ((int)(res >> 8)) & 0x00ff, ((int)(res >> 16)) & 0x00ff);
}


// Set the edge colour.
void QextScintilla::setEdgeColor(const QColor &col)
{
    SendScintilla(SCI_SETEDGECOLOUR,col);
}


// Return the edge column.
int QextScintilla::edgeColumn()
{
    return SendScintilla(SCI_GETEDGECOLUMN);
}


// Set the edge column.
void QextScintilla::setEdgeColumn(int colnr)
{
    SendScintilla(SCI_SETEDGECOLUMN,colnr);
}


// Return the edge mode.
QextScintilla::EdgeMode QextScintilla::edgeMode()
{
    return (EdgeMode)SendScintilla(SCI_GETEDGEMODE);
}


// Set the edge mode.
void QextScintilla::setEdgeMode(EdgeMode mode)
{
    SendScintilla(SCI_SETEDGEMODE,mode);
}


// Return the end-of-line visibility.
bool QextScintilla::eolVisibility()
{
    return SendScintilla(SCI_GETVIEWEOL);
}


// Set the end-of-line visibility.
void QextScintilla::setEolVisibility(bool visible)
{
    SendScintilla(SCI_SETVIEWEOL,visible);
}


// Return the whitespace visibility.
QextScintilla::WhitespaceVisibility QextScintilla::whitespaceVisibility()
{
    return (WhitespaceVisibility)SendScintilla(SCI_GETVIEWWS);
}


// Set the whitespace visibility.
void QextScintilla::setWhitespaceVisibility(WhitespaceVisibility mode)
{
    SendScintilla(SCI_SETVIEWWS,mode);
}


// Return the line wrap mode.
QextScintilla::WrapMode QextScintilla::wrapMode()
{
    return (WrapMode)SendScintilla(SCI_GETWRAPMODE);
}


// Set the line wrap mode.
void QextScintilla::setWrapMode(WrapMode mode)
{
    SendScintilla(SCI_SETLAYOUTCACHE, (mode == WrapNone ? SC_CACHE_CARET : SC_CACHE_DOCUMENT));
    SendScintilla(SCI_SETWRAPMODE, mode);
}


// Set the line wrap visual flags.
void QextScintilla::setWrapVisualFlags(WrapVisualFlag eflag,
        WrapVisualFlag sflag, int sindent)
{
    int flags = SC_WRAPVISUALFLAG_NONE;
    int loc = SC_WRAPVISUALFLAGLOC_DEFAULT;

    if (eflag == WrapFlagByText)
    {
        flags |= SC_WRAPVISUALFLAG_END;
        loc |= SC_WRAPVISUALFLAGLOC_END_BY_TEXT;
    }
    else if (eflag == WrapFlagByBorder)
        flags |= SC_WRAPVISUALFLAG_END;

    if (sflag == WrapFlagByText)
    {
        flags |= SC_WRAPVISUALFLAG_START;
        loc |= SC_WRAPVISUALFLAGLOC_START_BY_TEXT;
    }
    else if (sflag == WrapFlagByBorder)
        flags |= SC_WRAPVISUALFLAG_START;

    SendScintilla(SCI_SETWRAPVISUALFLAGS, flags);
    SendScintilla(SCI_SETWRAPVISUALFLAGSLOCATION, loc);
    SendScintilla(SCI_SETWRAPSTARTINDENT, sindent);
}


// Set the folding style.
void QextScintilla::setFolding(FoldStyle folding)
{
    fold = folding;

    if (folding == NoFoldStyle)
    {
        SendScintilla(SCI_SETMARGINWIDTHN,2,0L);
        return;
    }

    int mask = SendScintilla(SCI_GETMODEVENTMASK);
    SendScintilla(SCI_SETMODEVENTMASK,mask | SC_MOD_CHANGEFOLD);

    SendScintilla(SCI_SETFOLDFLAGS,SC_FOLDFLAG_LINEAFTER_CONTRACTED);

    SendScintilla(SCI_SETMARGINTYPEN,2,(long)SC_MARGIN_SYMBOL);
    SendScintilla(SCI_SETMARGINMASKN,2,SC_MASK_FOLDERS);
    SendScintilla(SCI_SETMARGINSENSITIVEN,2,1);

    // Set the marker symbols to use.
    switch (folding)
    {
    case PlainFoldStyle:
        setFoldMarker(SC_MARKNUM_FOLDEROPEN,SC_MARK_MINUS);
        setFoldMarker(SC_MARKNUM_FOLDER,SC_MARK_PLUS);
        setFoldMarker(SC_MARKNUM_FOLDERSUB);
        setFoldMarker(SC_MARKNUM_FOLDERTAIL);
        setFoldMarker(SC_MARKNUM_FOLDEREND);
        setFoldMarker(SC_MARKNUM_FOLDEROPENMID);
        setFoldMarker(SC_MARKNUM_FOLDERMIDTAIL);

        break;

    case CircledFoldStyle:
        setFoldMarker(SC_MARKNUM_FOLDEROPEN,SC_MARK_CIRCLEMINUS);
        setFoldMarker(SC_MARKNUM_FOLDER,SC_MARK_CIRCLEPLUS);
        setFoldMarker(SC_MARKNUM_FOLDERSUB);
        setFoldMarker(SC_MARKNUM_FOLDERTAIL);
        setFoldMarker(SC_MARKNUM_FOLDEREND);
        setFoldMarker(SC_MARKNUM_FOLDEROPENMID);
        setFoldMarker(SC_MARKNUM_FOLDERMIDTAIL);

        break;

    case BoxedFoldStyle:
        setFoldMarker(SC_MARKNUM_FOLDEROPEN,SC_MARK_BOXMINUS);
        setFoldMarker(SC_MARKNUM_FOLDER,SC_MARK_BOXPLUS);
        setFoldMarker(SC_MARKNUM_FOLDERSUB);
        setFoldMarker(SC_MARKNUM_FOLDERTAIL);
        setFoldMarker(SC_MARKNUM_FOLDEREND);
        setFoldMarker(SC_MARKNUM_FOLDEROPENMID);
        setFoldMarker(SC_MARKNUM_FOLDERMIDTAIL);

        break;

    case CircledTreeFoldStyle:
        setFoldMarker(SC_MARKNUM_FOLDEROPEN,SC_MARK_CIRCLEMINUS);
        setFoldMarker(SC_MARKNUM_FOLDER,SC_MARK_CIRCLEPLUS);
        setFoldMarker(SC_MARKNUM_FOLDERSUB,SC_MARK_VLINE);
        setFoldMarker(SC_MARKNUM_FOLDERTAIL,SC_MARK_LCORNERCURVE);
        setFoldMarker(SC_MARKNUM_FOLDEREND,SC_MARK_CIRCLEPLUSCONNECTED);
        setFoldMarker(SC_MARKNUM_FOLDEROPENMID,SC_MARK_CIRCLEMINUSCONNECTED);
        setFoldMarker(SC_MARKNUM_FOLDERMIDTAIL,SC_MARK_TCORNERCURVE);

        break;

    case BoxedTreeFoldStyle:
        setFoldMarker(SC_MARKNUM_FOLDEROPEN,SC_MARK_BOXMINUS);
        setFoldMarker(SC_MARKNUM_FOLDER,SC_MARK_BOXPLUS);
        setFoldMarker(SC_MARKNUM_FOLDERSUB,SC_MARK_VLINE);
        setFoldMarker(SC_MARKNUM_FOLDERTAIL,SC_MARK_LCORNER);
        setFoldMarker(SC_MARKNUM_FOLDEREND,SC_MARK_BOXPLUSCONNECTED);
        setFoldMarker(SC_MARKNUM_FOLDEROPENMID,SC_MARK_BOXMINUSCONNECTED);
        setFoldMarker(SC_MARKNUM_FOLDERMIDTAIL,SC_MARK_TCORNER);

        break;
    }

    SendScintilla(SCI_SETMARGINWIDTHN,2,defaultFoldMarginWidth);
}


// Set up a folder marker.
void QextScintilla::setFoldMarker(int marknr,int mark)
{
    SendScintilla(SCI_MARKERDEFINE,marknr,mark);

    if (mark != SC_MARK_EMPTY)
    {
        SendScintilla(SCI_MARKERSETFORE,marknr,white);
        SendScintilla(SCI_MARKERSETBACK,marknr,black);
    }
}


// Handle a click in the fold margin.  This is mostly taken from SciTE.
void QextScintilla::foldClick(int lineClick,int bstate)
{
    if ((bstate & ShiftButton) && (bstate & ControlButton))
    {
        foldAll();
        return;
    }

    int levelClick = SendScintilla(SCI_GETFOLDLEVEL,lineClick);

    if (levelClick & SC_FOLDLEVELHEADERFLAG)
    {
        if (bstate & ShiftButton)
        {
            // Ensure all children are visible.
            SendScintilla(SCI_SETFOLDEXPANDED,lineClick,1);
            foldExpand(lineClick,TRUE,TRUE,100,levelClick);
        }
        else if (bstate & ControlButton)
        {
            if (SendScintilla(SCI_GETFOLDEXPANDED,lineClick))
            {
                // Contract this line and all its children.
                SendScintilla(SCI_SETFOLDEXPANDED,lineClick,0L);
                foldExpand(lineClick,FALSE,TRUE,0,levelClick);
            }
            else
            {
                // Expand this line and all its children.
                SendScintilla(SCI_SETFOLDEXPANDED,lineClick,1);
                foldExpand(lineClick,TRUE,TRUE,100,levelClick);
            }
        }
        else
        {
            // Toggle this line.
            SendScintilla(SCI_TOGGLEFOLD,lineClick);
        }
    }
}


// Do the hard work of hiding and showing lines.  This is mostly taken from
// SciTE.
void QextScintilla::foldExpand(int &line,bool doExpand,bool force,
                   int visLevels,int level)
{
    int lineMaxSubord = SendScintilla(SCI_GETLASTCHILD,line,level & SC_FOLDLEVELNUMBERMASK);

    line++;

    while (line <= lineMaxSubord)
    {
        if (force)
        {
            if (visLevels > 0)
                SendScintilla(SCI_SHOWLINES,line,line);
            else
                SendScintilla(SCI_HIDELINES,line,line);
        }
        else if (doExpand)
            SendScintilla(SCI_SHOWLINES,line,line);

        int levelLine = level;

        if (levelLine == -1)
            levelLine = SendScintilla(SCI_GETFOLDLEVEL,line);

        if (levelLine & SC_FOLDLEVELHEADERFLAG)
        {
            if (force)
            {
                if (visLevels > 1)
                    SendScintilla(SCI_SETFOLDEXPANDED,line,1);
                else
                    SendScintilla(SCI_SETFOLDEXPANDED,line,0L);

                foldExpand(line,doExpand,force,visLevels - 1);
            }
            else if (doExpand)
            {
                if (!SendScintilla(SCI_GETFOLDEXPANDED,line))
                    SendScintilla(SCI_SETFOLDEXPANDED,line,1);

                foldExpand(line,TRUE,force,visLevels - 1);
            }
            else
                foldExpand(line,FALSE,force,visLevels - 1);
        }
        else
            line++;
    }
}


// Fully expand (if there is any line currently folded) all text.  Otherwise,
// fold all text.  This is mostly taken from SciTE.
void QextScintilla::foldAll(bool children)
{
    recolor();

    int maxLine = SendScintilla(SCI_GETLINECOUNT);
    bool expanding = TRUE;

    for (int lineSeek = 0; lineSeek < maxLine; lineSeek++)
    {
        if (SendScintilla(SCI_GETFOLDLEVEL,lineSeek) & SC_FOLDLEVELHEADERFLAG)
        {
            expanding = !SendScintilla(SCI_GETFOLDEXPANDED,lineSeek);
            break;
        }
    }

    for (int line = 0; line < maxLine; line++)
    {
        int level = SendScintilla(SCI_GETFOLDLEVEL,line);

        if (!(level & SC_FOLDLEVELHEADERFLAG))
            continue;

        if (children ||
            (SC_FOLDLEVELBASE == (level & SC_FOLDLEVELNUMBERMASK)))
        {
            if (expanding)
            {
                SendScintilla(SCI_SETFOLDEXPANDED,line,1);
                foldExpand(line,TRUE,FALSE,0,level);
                line--;
            }
            else
            {
                int lineMaxSubord = SendScintilla(SCI_GETLASTCHILD,line,-1);

                SendScintilla(SCI_SETFOLDEXPANDED,line,0L);

                if (lineMaxSubord > line)
                    SendScintilla(SCI_HIDELINES,line + 1,lineMaxSubord);
            }
        }
    }
}


// Handle a fold change.  This is mostly taken from SciTE.
void QextScintilla::foldChanged(int line,int levelNow,int levelPrev)
{
        if (levelNow & SC_FOLDLEVELHEADERFLAG)
    {
                if (!(levelPrev & SC_FOLDLEVELHEADERFLAG))
                        SendScintilla(SCI_SETFOLDEXPANDED,line,1);
        }
    else if (levelPrev & SC_FOLDLEVELHEADERFLAG)
    {
                if (!SendScintilla(SCI_GETFOLDEXPANDED,line))
        {
                        // Removing the fold from one that has been contracted
            // so should expand.  Otherwise lines are left
            // invisible with no way to make them visible.
                        foldExpand(line,TRUE,FALSE,0,levelPrev);
                }
        }
}


// Toggle the fold for a line if it contains a fold marker.
void QextScintilla::foldLine(int line)
{
    SendScintilla(SCI_TOGGLEFOLD,line);
}


// Handle the SCN_MODIFIED notification.
void QextScintilla::handleModified(int pos,int mtype,const char *text,int len,
                   int added,int line,int foldNow,int foldPrev)
{
    if (mtype & SC_MOD_CHANGEFOLD)
    {
        if (fold)
            foldChanged(line,foldNow,foldPrev);
    }
    else if (mtype & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT))
        emit textChanged();
}


// Zoom in a number of points.
void QextScintilla::zoomIn(int range)
{
    zoomTo(SendScintilla(SCI_GETZOOM) + range);
}


// Zoom in a single point.
void QextScintilla::zoomIn()
{
    SendScintilla(SCI_ZOOMIN);
}


// Zoom out a number of points.
void QextScintilla::zoomOut(int range)
{
    zoomTo(SendScintilla(SCI_GETZOOM) - range);
}


// Zoom out a single point.
void QextScintilla::zoomOut()
{
    SendScintilla(SCI_ZOOMOUT);
}


// Set the zoom to a number of points.
void QextScintilla::zoomTo(int size)
{
    if (size < -10)
        size = -10;
    else if (size > 20)
        size = 20;

    SendScintilla(SCI_SETZOOM,size);
}


// Find the first occurrence of a string.
bool QextScintilla::findFirst(const QString &expr,bool re,bool cs,bool wo,
                              bool wrap,bool forward,int line,int index,
                  bool show)
{
    findState.inProgress = FALSE;

    if (expr.isEmpty())
        return FALSE;

    findState.expr = expr;
    findState.wrap = wrap;
    findState.forward = forward;

    findState.flags = (cs ? SCFIND_MATCHCASE : 0) |
              (wo ? SCFIND_WHOLEWORD : 0) |
              (re ? SCFIND_REGEXP : 0);

    if (line < 0 || index < 0)
        findState.startpos = SendScintilla(SCI_GETCURRENTPOS);
    else
        findState.startpos = posFromLineIndex(line,index);

    if (forward)
        findState.endpos = SendScintilla(SCI_GETLENGTH);
    else
        findState.endpos = 0;

    findState.show = show;

    return doFind();
}


// Find the next occurrence of a string.
bool QextScintilla::findNext()
{
    if (!findState.inProgress)
        return FALSE;

    return doFind();
}


// Do the hard work of findFirst() and findNext().
bool QextScintilla::doFind()
{
    SendScintilla(SCI_SETSEARCHFLAGS,findState.flags);

    long pos = simpleFind();

    // See if it was found.  If not and wraparound is wanted, try again.
    if (pos == -1 && findState.wrap)
    {
        if (findState.forward)
        {
            findState.startpos = 0;
            findState.endpos = SendScintilla(SCI_GETLENGTH);
        }
        else
        {
            findState.startpos = SendScintilla(SCI_GETLENGTH);
            findState.endpos = 0;
        }

        pos = simpleFind();
    }

    if (pos == -1)
    {
        findState.inProgress = FALSE;
        return FALSE;
    }

    // It was found.
    long targstart = SendScintilla(SCI_GETTARGETSTART);
    long targend = SendScintilla(SCI_GETTARGETEND);

    // Ensure the text found is visible if required.
    if (findState.show)
    {
        int startLine = SendScintilla(SCI_LINEFROMPOSITION,targstart);
        int endLine = SendScintilla(SCI_LINEFROMPOSITION,targend);

        for (int i = startLine; i <= endLine; ++i)
            SendScintilla(SCI_ENSUREVISIBLEENFORCEPOLICY,i);
    }

    // Now set the selection.
    SendScintilla(SCI_SETSEL,targstart,targend);

    // Finally adjust the start position so that we don't find the same one
    // again.
    if (findState.forward)
        findState.startpos = targend;
    else if ((findState.startpos = targstart - 1) < 0)
        findState.startpos = 0;

    findState.inProgress = TRUE;
    return TRUE;
}


// Do a simple find between the start and end positions.
long QextScintilla::simpleFind()
{
    if (findState.startpos == findState.endpos)
        return -1;

    SendScintilla(SCI_SETTARGETSTART,findState.startpos);
    SendScintilla(SCI_SETTARGETEND,findState.endpos);

    long pos;

    if (isUtf8())
    {
        QCString s = findState.expr.utf8();

        pos = SendScintilla(SCI_SEARCHINTARGET,s.length(),s.data());
    }
    else
    {
        const char *s = findState.expr.latin1();

        pos = SendScintilla(SCI_SEARCHINTARGET,strlen(s),s);
    }

    return pos;
}


// Replace the text found with the previous findFirst() or findNext().
void QextScintilla::replace(const QString &replaceStr)
{
    if (!findState.inProgress)
        return;

    long start = SendScintilla(SCI_GETSELECTIONSTART);

    SendScintilla(SCI_TARGETFROMSELECTION);

    long len;
    int cmd = (findState.flags & SCFIND_REGEXP) ? SCI_REPLACETARGETRE : SCI_REPLACETARGET;

    if (isUtf8())
        len = SendScintilla(cmd,-1,replaceStr.utf8().data());
    else
        len = SendScintilla(cmd,-1,replaceStr.latin1());

    // Reset the selection.
    SendScintilla(SCI_SETSELECTIONSTART,start);
    SendScintilla(SCI_SETSELECTIONEND,start + len);

    if (findState.forward)
        findState.startpos = start + len;
}


// Query the modified state.
bool QextScintilla::isModified()
{
    // We don't use SCI_GETMODIFY as it seems to be buggy in Scintilla
    // v1.61.
    return modified;
}


// Set the modified state.
void QextScintilla::setModified(bool m)
{
    if (!m)
        SendScintilla(SCI_SETSAVEPOINT);
}


// Handle the SCN_MARGINCLICK notification.
void QextScintilla::handleMarginClick(int pos,int modifiers,int margin)
{
    int state = 0;

    if (modifiers & SCMOD_SHIFT)
        state |= ShiftButton;

    if (modifiers & SCMOD_CTRL)
        state |= ControlButton;

    if (modifiers & SCMOD_ALT)
        state |= AltButton;

    int line = SendScintilla(SCI_LINEFROMPOSITION,pos);

    if (fold && margin == 2)
        foldClick(line,state);
    else
        emit marginClicked(margin,line,(ButtonState)state);
}


// Handle the SCN_SAVEPOINTREACHED notification.
void QextScintilla::handleSavePointReached()
{
    if (modified)
    {
        modified = FALSE;
        emit modificationChanged(FALSE);
    }
}


// Handle the SCN_SAVEPOINTLEFT notification.
void QextScintilla::handleSavePointLeft()
{
    if (!modified)
    {
        modified = TRUE;
        emit modificationChanged(TRUE);
    }
}


// Handle the QSCN_SELCHANGED signal.
void QextScintilla::handleSelectionChanged(bool yes)
{
    selText = yes;

    emit copyAvailable(yes);
    emit selectionChanged();
}


// Get the current selection.
void QextScintilla::getSelection(int *lineFrom,int *indexFrom,
                 int *lineTo,int *indexTo)
{
    if (selText)
    {
        lineIndexFromPos(SendScintilla(SCI_GETSELECTIONSTART),
                 lineFrom,indexFrom);
        lineIndexFromPos(SendScintilla(SCI_GETSELECTIONEND),
                 lineTo,indexTo);
    }
    else
        *lineFrom = *indexFrom = *lineTo = *indexTo = -1;
}


// Sets the current selection.
void QextScintilla::setSelection(int lineFrom,int indexFrom,
                 int lineTo,int indexTo)
{
    SendScintilla(SCI_SETSELECTIONSTART,posFromLineIndex(lineFrom,indexFrom));
    SendScintilla(SCI_SETSELECTIONEND,posFromLineIndex(lineTo,indexTo));
}


// Set the background colour of selected text.
void QextScintilla::setSelectionBackgroundColor(const QColor &col)
{
    SendScintilla(SCI_SETSELBACK,1,col);

    int alpha = qAlpha(col.rgb());
    
    if (alpha < 255)
        SendScintilla(SCI_SETSELALPHA, alpha);
}


// Set the foreground colour of selected text.
void QextScintilla::setSelectionForegroundColor(const QColor &col)
{
    SendScintilla(SCI_SETSELFORE,1,col);
}


// Reset the background colour of selected text to the default.
void QextScintilla::resetSelectionBackgroundColor()
{
    SendScintilla(SCI_SETSELALPHA, SC_ALPHA_NOALPHA);
    SendScintilla(SCI_SETSELBACK,0UL);
}


// Reset the foreground colour of selected text to the default.
void QextScintilla::resetSelectionForegroundColor()
{
    SendScintilla(SCI_SETSELFORE,0UL);
}


// Set the width of the caret.
void QextScintilla::setCaretWidth(int width)
{
    SendScintilla(SCI_SETCARETWIDTH,width);
}


// Set the foreground colour of the caret.
void QextScintilla::setCaretForegroundColor(const QColor &col)
{
    SendScintilla(SCI_SETCARETFORE,col);
}


// Set the background colour of the line containing the caret.
void QextScintilla::setCaretLineBackgroundColor(const QColor &col)
{
    SendScintilla(SCI_SETCARETLINEBACK,col);

    int alpha = qAlpha(col.rgb());
    
    if (alpha < 255)
        SendScintilla(SCI_SETCARETLINEBACKALPHA, alpha);
}


// Set the state of the background colour of the line containing the caret.
void QextScintilla::setCaretLineVisible(bool enable)
{
    SendScintilla(SCI_SETCARETLINEVISIBLE,enable);
}


// Query the read-only state.
bool QextScintilla::isReadOnly()
{
    return SendScintilla(SCI_GETREADONLY);
}


// Set the read-only state.
void QextScintilla::setReadOnly(bool ro)
{
    SendScintilla(SCI_SETREADONLY,ro);
}


// Append the given text.
void QextScintilla::append(const QString &text)
{
    bool ro = ensureRW();

    if (isUtf8())
    {
        QCString s = text.utf8();

        SendScintilla(SCI_APPENDTEXT,s.length(),s.data());
    }
    else
    {
        const char *s = text.latin1();

        SendScintilla(SCI_APPENDTEXT,strlen(s),s);
    }

    SendScintilla(SCI_EMPTYUNDOBUFFER);

    setReadOnly(ro);
}


// Insert the given text at the current position.
void QextScintilla::insert(const QString &text)
{
    bool ro = ensureRW();

    SendScintilla(SCI_BEGINUNDOACTION);

    if (isUtf8())
        SendScintilla(SCI_INSERTTEXT,-1,text.utf8().data());
    else
        SendScintilla(SCI_INSERTTEXT,-1,text.latin1());

    SendScintilla(SCI_ENDUNDOACTION);

    setReadOnly(ro);
}


// Insert the given text at the given position.
void QextScintilla::insertAt(const QString &text,int line,int index)
{
    bool ro = ensureRW();
    long position = posFromLineIndex(line,index);

    SendScintilla(SCI_BEGINUNDOACTION);

    if (isUtf8())
        SendScintilla(SCI_INSERTTEXT,position,text.utf8().data());
    else
        SendScintilla(SCI_INSERTTEXT,position,text.latin1());

    SendScintilla(SCI_ENDUNDOACTION);

    setReadOnly(ro);
}


// Begin a sequence of undoable actions.
void QextScintilla::beginUndoAction()
{
    SendScintilla(SCI_BEGINUNDOACTION);
}


// End a sequence of undoable actions.
void QextScintilla::endUndoAction()
{
    SendScintilla(SCI_ENDUNDOACTION);
}


// Redo a sequence of actions.
void QextScintilla::redo()
{
    SendScintilla(SCI_REDO);
}


// Undo a sequence of actions.
void QextScintilla::undo()
{
    SendScintilla(SCI_UNDO);
}


// See if there is something to redo.
bool QextScintilla::isRedoAvailable()
{
    return SendScintilla(SCI_CANREDO);
}


// See if there is something to undo.
bool QextScintilla::isUndoAvailable()
{
    return SendScintilla(SCI_CANUNDO);
}


// Return the number of lines.
int QextScintilla::lines()
{
    return SendScintilla(SCI_GETLINECOUNT);
}


// Return the line at a position.
int QextScintilla::lineAt(const QPoint &pos)
{
    long chpos = SendScintilla(SCI_POSITIONFROMPOINTCLOSE,pos.x(),pos.y());

    if (chpos < 0)
        return -1;

    return SendScintilla(SCI_LINEFROMPOSITION,chpos);
}


// Return the length of a line.
int QextScintilla::lineLength(int line)
{
    if (line < 0 || line >= SendScintilla(SCI_GETLINECOUNT))
        return -1;

    return SendScintilla(SCI_LINELENGTH,line);
}


// Return the length of the current text.
int QextScintilla::length()
{
    return SendScintilla(SCI_GETTEXTLENGTH);
}


// Remove any selected text.
void QextScintilla::removeSelectedText()
{
    SendScintilla(SCI_REPLACESEL,"");
}


// Return the current selected text.
QString QextScintilla::selectedText()
{
    if (!selText)
        return QString();

    // Scintilla doesn't tell us the length of the selected text so we use
    // the length of the whole document.
    char *buf = new char[length() + 1];

    SendScintilla(SCI_GETSELTEXT,buf);

    QString qs = convertText(buf);
    delete[] buf;

    return qs;
}


// Return the current text.
QString QextScintilla::text()
{
    int buflen = length() + 1;
    char *buf = new char[buflen];

    SendScintilla(SCI_GETTEXT,buflen,buf);

    QString qs = convertText(buf);
    delete[] buf;

    return qs;
}


// Return the text of a line.
QString QextScintilla::text(int line)
{
    int line_len = lineLength(line);

    if (line_len < 1)
        return QString();

    char *buf = new char[line_len + 1];

    SendScintilla(SCI_GETLINE,line,buf);
    buf[line_len] = '\0';

    QString qs = convertText(buf);
    delete[] buf;

    return qs;
}


// Set the given text.
void QextScintilla::setText(const QString &text)
{
    bool ro = ensureRW();

    if (isUtf8())
        SendScintilla(SCI_SETTEXT,text.utf8().data());
    else
        SendScintilla(SCI_SETTEXT,text.latin1());

    SendScintilla(SCI_EMPTYUNDOBUFFER);

    setReadOnly(ro);
}


// Get the cursor position
void QextScintilla::getCursorPosition(int *line,int *index)
{
    long pos = SendScintilla(SCI_GETCURRENTPOS);
    long lin = SendScintilla(SCI_LINEFROMPOSITION,pos);
    long linpos = SendScintilla(SCI_POSITIONFROMLINE,lin);

    *line = lin;
    *index = pos - linpos;
}


// Set the cursor position
void QextScintilla::setCursorPosition(int line,int index)
{
    SendScintilla(SCI_GOTOPOS,posFromLineIndex(line,index));
}


// Ensure the cursor is visible.
void QextScintilla::ensureCursorVisible()
{
    SendScintilla(SCI_SCROLLCARET);
}


// Ensure a line is visible.
void QextScintilla::ensureLineVisible(int line)
{
    SendScintilla(SCI_ENSUREVISIBLEENFORCEPOLICY,line);
}


// Copy text to the clipboard.
void QextScintilla::copy()
{
    SendScintilla(SCI_COPY);
}


// Cut text to the clipboard.
void QextScintilla::cut()
{
    SendScintilla(SCI_CUT);
}


// Paste text from the clipboard.
void QextScintilla::paste()
{
    SendScintilla(SCI_PASTE);
}


// Select all text, or deselect any selected text.
void QextScintilla::selectAll(bool select)
{
    if (selText)
        SendScintilla(SCI_SETANCHOR,SendScintilla(SCI_GETCURRENTPOS));
    else
        SendScintilla(SCI_SELECTALL);
}


// Delete all text.
void QextScintilla::clear()
{
    bool ro = ensureRW();

    SendScintilla(SCI_BEGINUNDOACTION);
    SendScintilla(SCI_CLEARALL);
    SendScintilla(SCI_ENDUNDOACTION);

    setReadOnly(ro);
}


// Return the indentation of a line.
int QextScintilla::indentation(int line)
{
    return SendScintilla(SCI_GETLINEINDENTATION,line);
}


// Set the indentation of a line.
void QextScintilla::setIndentation(int line,int indentation)
{
    SendScintilla(SCI_BEGINUNDOACTION);
    SendScintilla(SCI_SETLINEINDENTATION,line,indentation);
    SendScintilla(SCI_ENDUNDOACTION);
}


// Indent a line.
void QextScintilla::indent(int line)
{
    setIndentation(line,indentation(line) + indentWidth());
}


// Unindent a line.
void QextScintilla::unindent(int line)
{
    int newIndent = indentation(line) - indentWidth();

    if (newIndent < 0)
        newIndent = 0;

    setIndentation(line,newIndent);
}


// Return the indentation of the current line.
int QextScintilla::currentIndent()
{
    return indentation(SendScintilla(SCI_LINEFROMPOSITION,SendScintilla(SCI_GETCURRENTPOS)));
}


// Return the current indentation width.
int QextScintilla::indentWidth()
{
    int w = indentationWidth();

    if (w == 0)
        w = tabWidth();

    return w;
}


// Return the state of indentation guides.
bool QextScintilla::indentationGuides()
{
    return SendScintilla(SCI_GETINDENTATIONGUIDES);
}


// Enable and disable indentation guides.
void QextScintilla::setIndentationGuides(bool enable)
{
    SendScintilla(SCI_SETINDENTATIONGUIDES,enable);
}


// Set the background colour of indentation guides.
void QextScintilla::setIndentationGuidesBackgroundColor(const QColor &col)
{
    SendScintilla(SCI_STYLESETBACK,STYLE_INDENTGUIDE,col);
}


// Set the foreground colour of indentation guides.
void QextScintilla::setIndentationGuidesForegroundColor(const QColor &col)
{
    SendScintilla(SCI_STYLESETFORE,STYLE_INDENTGUIDE,col);
}


// Return the indentation width.
int QextScintilla::indentationWidth()
{
    return SendScintilla(SCI_GETINDENT);
}


// Set the indentation width.
void QextScintilla::setIndentationWidth(int width)
{
    SendScintilla(SCI_SETINDENT,width);
}


// Return the tab width.
int QextScintilla::tabWidth()
{
    return SendScintilla(SCI_GETTABWIDTH);
}


// Set the tab width.
void QextScintilla::setTabWidth(int width)
{
    SendScintilla(SCI_SETTABWIDTH,width);
}


// Return the effect of the backspace key.
bool QextScintilla::backspaceUnindents()
{
    return SendScintilla(SCI_GETBACKSPACEUNINDENTS);
}


// Set the effect of the backspace key.
void QextScintilla::setBackspaceUnindents(bool unindents)
{
    SendScintilla(SCI_SETBACKSPACEUNINDENTS,unindents);
}


// Return the effect of the tab key.
bool QextScintilla::tabIndents()
{
    return SendScintilla(SCI_GETTABINDENTS);
}


// Set the effect of the tab key.
void QextScintilla::setTabIndents(bool indents)
{
    SendScintilla(SCI_SETTABINDENTS,indents);
}


// Return the indentation use of tabs.
bool QextScintilla::indentationsUseTabs()
{
    return SendScintilla(SCI_GETUSETABS);
}


// Set the indentation use of tabs.
void QextScintilla::setIndentationsUseTabs(bool tabs)
{
    SendScintilla(SCI_SETUSETABS,tabs);
}


// Return the state of line numbers in a margin.
bool QextScintilla::marginLineNumbers(int margin)
{
    return SendScintilla(SCI_GETMARGINTYPEN,margin);
}


// Enable and disable line numbers in a margin.
void QextScintilla::setMarginLineNumbers(int margin,bool lnrs)
{
    SendScintilla(SCI_SETMARGINTYPEN,margin,lnrs ? SC_MARGIN_NUMBER : 0);
}


// Return the marker mask of a margin.
int QextScintilla::marginMarkerMask(int margin)
{
    return SendScintilla(SCI_GETMARGINMASKN,margin);
}


// Set the marker mask of a margin.
void QextScintilla::setMarginMarkerMask(int margin,int mask)
{
    SendScintilla(SCI_SETMARGINMASKN,margin,mask);
}


// Return the state of a margin's sensitivity.
bool QextScintilla::marginSensitivity(int margin)
{
    return SendScintilla(SCI_GETMARGINSENSITIVEN,margin);
}


// Enable and disable a margin's sensitivity.
void QextScintilla::setMarginSensitivity(int margin,bool sens)
{
    SendScintilla(SCI_SETMARGINSENSITIVEN,margin,sens);
}


// Return the width of a margin.
int QextScintilla::marginWidth(int margin)
{
    return SendScintilla(SCI_GETMARGINWIDTHN,margin);
}


// Set the width of a margin.
void QextScintilla::setMarginWidth(int margin,int width)
{
    SendScintilla(SCI_SETMARGINWIDTHN,margin,width);
}


// Set the width of a margin to the width of some text.
void QextScintilla::setMarginWidth(int margin,const QString &s)
{
    int width;

    if (isUtf8())
        width = SendScintilla(SCI_TEXTWIDTH,STYLE_LINENUMBER,s.utf8().data());
    else
        width = SendScintilla(SCI_TEXTWIDTH,STYLE_LINENUMBER,s.latin1());

    setMarginWidth(margin,width);
}


// Set the background colour of all margins.
void QextScintilla::setMarginsBackgroundColor(const QColor &col)
{
    handleStylePaperChange(col,STYLE_LINENUMBER);
}


// Set the foreground colour of all margins.
void QextScintilla::setMarginsForegroundColor(const QColor &col)
{
    handleStyleColorChange(col,STYLE_LINENUMBER);
}


// Set the font of all margins.
void QextScintilla::setMarginsFont(const QFont &f)
{
    setStylesFont(f,STYLE_LINENUMBER);
}


// Define a marker based on a symbol.
int QextScintilla::markerDefine(MarkerSymbol sym,int mnr)
{
    checkMarker(mnr);

    if (mnr >= 0)
        SendScintilla(SCI_MARKERDEFINE,mnr,static_cast<long>(sym));

    return mnr;
}


// Define a marker based on a character.
int QextScintilla::markerDefine(char ch,int mnr)
{
    checkMarker(mnr);

    if (mnr >= 0)
        SendScintilla(SCI_MARKERDEFINE,mnr,static_cast<long>(SC_MARK_CHARACTER) + ch);

    return mnr;
}


// Define a marker based on a QPixmap.
int QextScintilla::markerDefine(const QPixmap *pm,int mnr)
{
    checkMarker(mnr);

    if (mnr >= 0)
        SendScintilla(SCI_MARKERDEFINEPIXMAP,mnr,pm);

    return mnr;
}


// Add a marker to a line.
int QextScintilla::markerAdd(int linenr,int mnr)
{
    if (mnr < 0 || mnr > MARKER_MAX || (allocatedMarkers & (1 << mnr)) == 0)
        return -1;

    return SendScintilla(SCI_MARKERADD,linenr,mnr);
}


// Get the marker mask for a line.
unsigned QextScintilla::markersAtLine(int linenr)
{
    return SendScintilla(SCI_MARKERGET,linenr);
}


// Delete a marker from a line.
void QextScintilla::markerDelete(int linenr,int mnr)
{
    if (mnr <= MARKER_MAX)
    {
        if (mnr < 0)
        {
            unsigned am = allocatedMarkers;

            for (int m = 0; m <= MARKER_MAX; ++m)
            {
                if (am & 1)
                    SendScintilla(SCI_MARKERDELETE,linenr,m);

                am >>= 1;
            }
        }
        else if (allocatedMarkers & (1 << mnr))
            SendScintilla(SCI_MARKERDELETE,linenr,mnr);
    }
}


// Delete a marker from the text.
void QextScintilla::markerDeleteAll(int mnr)
{
    if (mnr <= MARKER_MAX)
    {
        if (mnr < 0)
            SendScintilla(SCI_MARKERDELETEALL,-1);
        else if (allocatedMarkers & (1 << mnr))
            SendScintilla(SCI_MARKERDELETEALL,mnr);
    }
}


// Delete a marker handle from the text.
void QextScintilla::markerDeleteHandle(int mhandle)
{
    SendScintilla(SCI_MARKERDELETEHANDLE,mhandle);
}


// Return the line containing a marker instance.
int QextScintilla::markerLine(int mhandle)
{
    return SendScintilla(SCI_MARKERLINEFROMHANDLE,mhandle);
}


// Search forwards for a marker.
int QextScintilla::markerFindNext(int linenr,unsigned mask)
{
    return SendScintilla(SCI_MARKERNEXT,linenr,mask);
}


// Search backwards for a marker.
int QextScintilla::markerFindPrevious(int linenr,unsigned mask)
{
    return SendScintilla(SCI_MARKERPREVIOUS,linenr,mask);
}


// Set the marker background colour.
void QextScintilla::setMarkerBackgroundColor(const QColor &col,int mnr)
{
    if (mnr <= MARKER_MAX)
    {
        int alpha = qAlpha(col.rgb());

        if (mnr < 0)
        {
            unsigned am = allocatedMarkers;

            for (int m = 0; m <= MARKER_MAX; ++m)
            {
                if (am & 1)
                {
                    SendScintilla(SCI_MARKERSETBACK,m,col);

                    if (alpha < 255)
                        SendScintilla(SCI_MARKERSETALPHA, m, alpha);
                }

                am >>= 1;
            }
        }
        else if (allocatedMarkers & (1 << mnr))
        {
            SendScintilla(SCI_MARKERSETBACK,mnr,col);

            if (alpha < 255)
                SendScintilla(SCI_MARKERSETALPHA, mnr, alpha);
        }
    }
}


// Set the marker foreground colour.
void QextScintilla::setMarkerForegroundColor(const QColor &col,int mnr)
{
    if (mnr <= MARKER_MAX)
    {
        if (mnr < 0)
        {
            unsigned am = allocatedMarkers;

            for (int m = 0; m <= MARKER_MAX; ++m)
            {
                if (am & 1)
                    SendScintilla(SCI_MARKERSETFORE,m,col);

                am >>= 1;
            }
        }
        else if (allocatedMarkers & (1 << mnr))
            SendScintilla(SCI_MARKERSETFORE,mnr,col);
    }
}


// Check a marker, allocating a marker number if necessary.
void QextScintilla::checkMarker(int &mnr)
{
    if (mnr >= 0)
    {
        // Check the explicit marker number isn't already allocated.
        if (mnr > MARKER_MAX || allocatedMarkers & (1 << mnr))
            mnr = -1;
    }
    else
    {
        unsigned am = allocatedMarkers;

        // Find the smallest unallocated marker number.
        for (mnr = 0; mnr <= MARKER_MAX; ++mnr)
        {
            if ((am & 1) == 0)
                break;

            am >>= 1;
        }
    }

    // Define the marker if it is valid.
    if (mnr >= 0)
        allocatedMarkers |= (1 << mnr);
}


// Reset the fold margin colours.
void QextScintilla::resetFoldMarginColors()
{
    SendScintilla(SCI_SETFOLDMARGINHICOLOUR,0,0L);
    SendScintilla(SCI_SETFOLDMARGINCOLOUR,0,0L);
}


// Set the fold margin colours.
void QextScintilla::setFoldMarginColors(const QColor &fore,const QColor &back)
{
    SendScintilla(SCI_SETFOLDMARGINHICOLOUR,1,fore);
    SendScintilla(SCI_SETFOLDMARGINCOLOUR,1,back);
}


// Set the call tips background colour.
void QextScintilla::setCallTipsBackgroundColor(const QColor &col)
{
    SendScintilla(SCI_CALLTIPSETBACK,col);
}


// Set the call tips foreground colour.
void QextScintilla::setCallTipsForegroundColor(const QColor &col)
{
    SendScintilla(SCI_CALLTIPSETFORE,col);
}


// Set the call tips highlight colour.
void QextScintilla::setCallTipsHighlightColor(const QColor &col)
{
    SendScintilla(SCI_CALLTIPSETFOREHLT,col);
}


// Set the matched brace background colour.
void QextScintilla::setMatchedBraceBackgroundColor(const QColor &col)
{
    SendScintilla(SCI_STYLESETBACK,STYLE_BRACELIGHT,col);
}


// Set the matched brace foreground colour.
void QextScintilla::setMatchedBraceForegroundColor(const QColor &col)
{
    SendScintilla(SCI_STYLESETFORE,STYLE_BRACELIGHT,col);
}


// Set the unmatched brace background colour.
void QextScintilla::setUnmatchedBraceBackgroundColor(const QColor &col)
{
    SendScintilla(SCI_STYLESETBACK,STYLE_BRACEBAD,col);
}


// Set the unmatched brace foreground colour.
void QextScintilla::setUnmatchedBraceForegroundColor(const QColor &col)
{
    SendScintilla(SCI_STYLESETFORE,STYLE_BRACEBAD,col);
}


// Set the lexer.
void QextScintilla::setLexer(QextScintillaLexer *lexer)
{
    // Disconnect any previous lexer.
    if (!lex.isNull())
    {
        lex -> disconnect(this);

        SendScintilla(SCI_STYLERESETDEFAULT);
    }

    // Connect up the new lexer.
    lex = lexer;

    if (lex)
    {
        int bits = SendScintilla(SCI_GETSTYLEBITSNEEDED);
        int nrStyles = 1 << bits;

        SendScintilla(SCI_SETSTYLEBITS,bits);

        connect(lex,SIGNAL(colorChanged(const QColor &,int)),
            SLOT(handleStyleColorChange(const QColor &,int)));
        connect(lex,SIGNAL(eolFillChanged(bool,int)),
            SLOT(handleStyleEolFillChange(bool,int)));
        connect(lex,SIGNAL(fontChanged(const QFont &,int)),
            SLOT(handleStyleFontChange(const QFont &,int)));
        connect(lex,SIGNAL(paperChanged(const QColor &,int)),
            SLOT(handleStylePaperChange(const QColor &,int)));
        connect(lex,SIGNAL(propertyChanged(const char *,const char *)),
            SLOT(handlePropertyChange(const char *,const char *)));

        SendScintilla(SCI_SETLEXERLANGUAGE,lex -> lexer());

        // Set the keywords.  Scintilla allows for sets numbered 0 to
        // KEYWORDSET_MAX (although the lexers only seem to exploit 0
        // to KEYWORDSET_MAX - 1).  We number from 1 in line with
        // SciTE's property files.
        for (int k = 0; k <= KEYWORDSET_MAX; ++k)
        {
            const char *kw = lex -> keywords(k + 1);

            if (kw)
                SendScintilla(SCI_SETKEYWORDS,k,kw);
        }

        // Initialise each style.
        for (int s = 0; s < nrStyles; ++s)
        {
            if (lex -> description(s).isNull())
                continue;

            handleStyleColorChange(lex -> color(s),s);
            handleStyleEolFillChange(lex -> eolFill(s),s);
            handleStyleFontChange(lex -> font(s),s);
            handleStylePaperChange(lex -> paper(s),s);
        }

        // Initialise the properties.
        lex -> refreshProperties();

        // Set the auto-completion fillups if they haven't been
        // explcitly set.
        if (fillups_enabled && !explicit_fillups)
            SendScintilla(SCI_AUTOCSETFILLUPS, lex->autoCompletionFillups());
    }
    else
    {
        SendScintilla(SCI_SETLEXER,SCLEX_NULL);

        setColor(nl_text_colour);
        setPaper(nl_paper_colour);
        setFont(nl_font);
    }
}


// Get the current lexer.
QextScintillaLexer *QextScintilla::lexer() const
{
    return lex;
}


// Handle a change in lexer style foreground colour.
void QextScintilla::handleStyleColorChange(const QColor &c,int style)
{
    SendScintilla(SCI_STYLESETFORE,style,c);
}


// Handle a change in lexer style end-of-line fill.
void QextScintilla::handleStyleEolFillChange(bool eolfill,int style)
{
    SendScintilla(SCI_STYLESETEOLFILLED,style,eolfill);
}


// Handle a change in lexer style font.
void QextScintilla::handleStyleFontChange(const QFont &f,int style)
{
    setStylesFont(f,style);

    if (style == lex->defaultStyle())
        setStylesFont(f, STYLE_DEFAULT);

    if (style == lex -> braceStyle())
    {
        setStylesFont(f,STYLE_BRACELIGHT);
        setStylesFont(f,STYLE_BRACEBAD);
    }
}


// Set the font for a style.
void QextScintilla::setStylesFont(const QFont &f,int style)
{
    SendScintilla(SCI_STYLESETFONT,style,f.family().latin1());
    SendScintilla(SCI_STYLESETSIZE,style,f.pointSize());
    SendScintilla(SCI_STYLESETBOLD,style,f.bold());
    SendScintilla(SCI_STYLESETITALIC,style,f.italic());
    SendScintilla(SCI_STYLESETUNDERLINE,style,f.underline());
}


// Handle a change in lexer style background colour.
void QextScintilla::handleStylePaperChange(const QColor &c,int style)
{
    SendScintilla(SCI_STYLESETBACK,style,c);
}


// Handle a change in lexer property.
void QextScintilla::handlePropertyChange(const char *prop,const char *val)
{
    SendScintilla(SCI_SETPROPERTY,prop,val);
}


// Handle a change to the user visible user interface.
void QextScintilla::handleUpdateUI()
{
    long newPos = SendScintilla(SCI_GETCURRENTPOS);

    if (newPos != oldPos)
    {
        oldPos = newPos;

        int line = SendScintilla(SCI_LINEFROMPOSITION,newPos);
        int col = SendScintilla(SCI_GETCOLUMN,newPos);

        emit cursorPositionChanged(line,col);
    }

    if (braceMode != NoBraceMatch)
        braceMatch();
}


// Handle brace matching.
void QextScintilla::braceMatch()
{
    long braceAtCaret, braceOpposite;

    findMatchingBrace(braceAtCaret,braceOpposite,braceMode);

    if (braceAtCaret >= 0 && braceOpposite < 0)
    {
        SendScintilla(SCI_BRACEBADLIGHT,braceAtCaret);
                SendScintilla(SCI_SETHIGHLIGHTGUIDE,0UL);
    }
    else
    {
        char chBrace = SendScintilla(SCI_GETCHARAT,braceAtCaret);

        SendScintilla(SCI_BRACEHIGHLIGHT,braceAtCaret,braceOpposite);

        long columnAtCaret = SendScintilla(SCI_GETCOLUMN,braceAtCaret);
                long columnOpposite = SendScintilla(SCI_GETCOLUMN,braceOpposite);

        if (chBrace == ':')
        {
            long lineStart = SendScintilla(SCI_LINEFROMPOSITION,braceAtCaret);
            long indentPos = SendScintilla(SCI_GETLINEINDENTPOSITION,lineStart);
                        long indentPosNext = SendScintilla(SCI_GETLINEINDENTPOSITION,lineStart + 1);

                        columnAtCaret = SendScintilla(SCI_GETCOLUMN,indentPos);

                        long columnAtCaretNext = SendScintilla(SCI_GETCOLUMN,indentPosNext);
                        long indentSize = SendScintilla(SCI_GETINDENT);

            if (columnAtCaretNext - indentSize > 1)
                columnAtCaret = columnAtCaretNext - indentSize;

            if (columnOpposite == 0)
                columnOpposite = columnAtCaret;
        }

        long column = columnAtCaret;

        if (column > columnOpposite)
            column = columnOpposite;

        SendScintilla(SCI_SETHIGHLIGHTGUIDE,column);
    }
}


// Check if the character at a position is a brace.
long QextScintilla::checkBrace(long pos,int brace_style,bool &colonMode)
{
    long brace_pos = -1;
    char ch = SendScintilla(SCI_GETCHARAT,pos);

    if (ch == ':')
    {
        // A bit of a hack.
        if (!lex.isNull() && strcmp(lex -> lexer(),"python") == 0)
        {
            brace_pos = pos;
            colonMode = TRUE;
        }
    }
    else if (ch && strchr("[](){}<>",ch))
    {
        if (brace_style < 0)
            brace_pos = pos;
        else
        {
            int style = SendScintilla(SCI_GETSTYLEAT,pos) & 0x1f;

            if (style == brace_style)
                brace_pos = pos;
        }
    }

    return brace_pos;
}


// Find a brace and it's match.  Return TRUE if the current position is inside
// a pair of braces.
bool QextScintilla::findMatchingBrace(long &brace,long &other,BraceMatch mode)
{
    bool colonMode = FALSE;
    int brace_style = (lex.isNull() ? -1 : lex -> braceStyle());

    brace = -1;
    other = -1;

    long caretPos = SendScintilla(SCI_GETCURRENTPOS);

    if (caretPos > 0)
        brace = checkBrace(caretPos - 1,brace_style,colonMode);

    bool isInside = FALSE;

    if (brace < 0 && mode == SloppyBraceMatch)
    {
        brace = checkBrace(caretPos,brace_style,colonMode);

        if (brace >= 0 && !colonMode)
            isInside = TRUE;
    }

    if (brace >= 0)
    {
        if (colonMode)
        {
            // Find the end of the Python indented block.
            long lineStart = SendScintilla(SCI_LINEFROMPOSITION,brace);
            long lineMaxSubord = SendScintilla(SCI_GETLASTCHILD,lineStart,-1);

            other = SendScintilla(SCI_GETLINEENDPOSITION,lineMaxSubord);
        }
        else
            other = SendScintilla(SCI_BRACEMATCH,brace);

        if (other > brace)
            isInside = !isInside;
    }

    return isInside;
}


// Move to the matching brace.
void QextScintilla::moveToMatchingBrace()
{
    gotoMatchingBrace(FALSE);
}


// Select to the matching brace.
void QextScintilla::selectToMatchingBrace()
{
    gotoMatchingBrace(TRUE);
}


// Move to the matching brace and optionally select the text.
void QextScintilla::gotoMatchingBrace(bool select)
{
    long braceAtCaret;
    long braceOpposite;

    bool isInside = findMatchingBrace(braceAtCaret,braceOpposite,SloppyBraceMatch);

    if (braceOpposite >= 0)
    {
        // Convert the character positions into caret positions based
        // on whether the caret position was inside or outside the
        // braces.
        if (isInside)
        {
            if (braceOpposite > braceAtCaret)
                braceAtCaret++;
            else
                braceOpposite++;
        }
        else
        {
            if (braceOpposite > braceAtCaret)
                braceOpposite++;
            else
                braceAtCaret++;
        }

        ensureLineVisible(SendScintilla(SCI_LINEFROMPOSITION,braceOpposite));

        if (select)
            SendScintilla(SCI_SETSEL,braceAtCaret,braceOpposite);
        else
            SendScintilla(SCI_SETSEL,braceOpposite,braceOpposite);
    }
}


// Return a position from a line number and an index within the line.
long QextScintilla::posFromLineIndex(int line,int index)
{
    long pos = SendScintilla(SCI_POSITIONFROMLINE,line);

    // Allow for multi-byte characters.
    for(int i = 0; i < index; i++)
        pos = SendScintilla(SCI_POSITIONAFTER,pos);

    return pos;
}


// Return a line number and an index within the line from a position.
void QextScintilla::lineIndexFromPos(long pos,int *line,int *index)
{
    long lin = SendScintilla(SCI_LINEFROMPOSITION,pos);
    long linpos = SendScintilla(SCI_POSITIONFROMLINE,lin);

    *line = lin;
    *index = pos - linpos;
}


// Convert a Scintilla string to a Qt Unicode string.
QString QextScintilla::convertText(const char *s)
{
    if (isUtf8())
        return QString::fromUtf8(s);

    QString qs;

    qs.setLatin1(s);

    return qs;
}


// Set the source of the auto-completion list.
void QextScintilla::setAutoCompletionSource(AutoCompletionSource source)
{
    acSource = source;
}


// Set the threshold for automatic auto-completion.
void QextScintilla::setAutoCompletionThreshold(int thresh)
{
    acThresh = thresh;
}


// Set the auto-completion start characters.
void QextScintilla::setAutoCompletionStartCharacters(const char *start)
{
    acStart = start;
}


// Set the APIs for auto-completion.
void QextScintilla::setAutoCompletionAPIs(QextScintillaAPIs *apis)
{
    acAPIs = apis;
}


// Explicitly auto-complete from all sources.
void QextScintilla::autoCompleteFromAll()
{
    startAutoCompletion(AcsAll, FALSE, showSingle);
}


// Explicitly auto-complete from the APIs.
void QextScintilla::autoCompleteFromAPIs()
{
    startAutoCompletion(AcsAPIs, FALSE, showSingle);
}


// Explicitly auto-complete from the document.
void QextScintilla::autoCompleteFromDocument()
{
    // If we are not in a word then ignore.
    if (currentCharInWord())
        startAutoCompletion(AcsDocument, FALSE, showSingle);
}


// Return TRUE if the current character (ie. the one before the carat) is part
// of a word.
bool QextScintilla::currentCharInWord()
{
    long pos = SendScintilla(SCI_GETCURRENTPOS);

    if (pos <= 0)
        return FALSE;

    return isWordChar(SendScintilla(SCI_GETCHARAT,pos - 1));
}


// Check if a character can be in a word.
bool QextScintilla::isWordChar(char ch) const
{
    const char *word_chars = 0;

    if (!lex.isNull())
        word_chars = lex->wordCharacters();

    if (!word_chars)
        word_chars = defaultWordChars;

    return (strchr(word_chars, ch) != NULL);
}


// Recolour the document.
void QextScintilla::recolor(int start,int end)
{
    SendScintilla(SCI_COLOURISE,start,end);
}


// Registered an image.
void QextScintilla::registerImage(int id,const QPixmap *pm)
{
    SendScintilla(SCI_REGISTERIMAGE,id,pm);
}


// Clear all registered images.
void QextScintilla::clearRegisteredImages()
{
    SendScintilla(SCI_CLEARREGISTEREDIMAGES);
}


// Set the fill-up characters for auto-completion.
void QextScintilla::setAutoCompletionFillups(const char *fillups)
{
    if (!fillups)
        fillups = "";

    SendScintilla(SCI_AUTOCSETFILLUPS, fillups);
    fillups_enabled = explicit_fillups = TRUE;

    // Save them in case we need to reenable them at some point.
    saved_fillups = fillups;
}


// Enable/disable fill-ups for auto-completion.
void QextScintilla::setAutoCompletionFillupsEnabled(bool enabled)
{
    const char *fillups;

    if (!enabled)
        fillups = "";
    else if (!explicit_fillups && !lex.isNull())
        fillups = lex->autoCompletionFillups();
    else
        fillups = saved_fillups.data();

    SendScintilla(SCI_AUTOCSETFILLUPS, fillups);
    fillups_enabled = enabled;
}


// Return the state of fill-ups for auto-completion.
bool QextScintilla::autoCompletionFillupsEnabled()
{
    return fillups_enabled;
}


// Set the case sensitivity for auto-completion.
void QextScintilla::setAutoCompletionCaseSensitivity(bool cs)
{
    SendScintilla(SCI_AUTOCSETIGNORECASE,!cs);
}


// Return the case sensitivity for auto-completion.
bool QextScintilla::autoCompletionCaseSensitivity()
{
    return !SendScintilla(SCI_AUTOCGETIGNORECASE);
}


// Set the replace word mode for auto-completion.
void QextScintilla::setAutoCompletionReplaceWord(bool replace)
{
    SendScintilla(SCI_AUTOCSETDROPRESTOFWORD,replace);
}


// Return the replace word mode for auto-completion.
bool QextScintilla::autoCompletionReplaceWord()
{
    return SendScintilla(SCI_AUTOCGETDROPRESTOFWORD);
}


// Set the single item mode for auto-completion.
void QextScintilla::setAutoCompletionShowSingle(bool single)
{
    showSingle = single;
}


// Return the single item mode for auto-completion.
bool QextScintilla::autoCompletionShowSingle()
{
    return showSingle;
}


// Set the APIs for call tips.
void QextScintilla::setCallTipsAPIs(QextScintillaAPIs *apis)
{
    ctAPIs = apis;
}


// Set maximum number of call tips displayed.
void QextScintilla::setCallTipsVisible(int nr)
{
    maxCallTips = nr;
}


// Set the document to display.
void QextScintilla::setDocument(const QextScintillaDocument &document)
{
    if (doc.pdoc != document.pdoc)
    {
        doc.undisplay(this);
        doc.attach(document);
        doc.display(this,&document);
    }
}


// Ensure the document is read-write and return True if was was read-only.
bool QextScintilla::ensureRW()
{
    bool ro = isReadOnly();

    if (ro)
        setReadOnly(FALSE);

    return ro;
}


// Return the number of the first visible line.
int QextScintilla::firstVisibleLine()
{
    return SendScintilla(SCI_GETFIRSTVISIBLELINE);
}


// Return the height in pixels of the text in a particular line.
int QextScintilla::textHeight(int linenr)
{
    return SendScintilla(SCI_TEXTHEIGHT, linenr);
}


// See if auto-completion or user list is active.
bool QextScintilla::isListActive()
{
    return SendScintilla(SCI_AUTOCACTIVE);
}


// Cancel any current auto-completion or user list.
void QextScintilla::cancelList()
{
    SendScintilla(SCI_AUTOCCANCEL);
}


// Display a user list.
void QextScintilla::showUserList(int id, const QStringList &list)
{
    // Sanity check to make sure auto-completion doesn't get confused.
    if (id <= 0)
        return;

    const char sep = '\x03';

    SendScintilla(SCI_AUTOCSETSEPARATOR, sep);
    SendScintilla(SCI_USERLISTSHOW, id, list.join(QChar(sep)).latin1());
}


// Translate the SCN_USERLISTSELECTION notification into something more useful.
void QextScintilla::handleUserListSelection(const char *text, int id)
{
    emit userListActivated(id, QString(text));
}
