textctrl.cpp

来自「A*算法 A*算法 A*算法 A*算法A*算法A*算法」· C++ 代码 · 共 2,131 行 · 第 1/5 页

CPP
2,131
字号

        return true;
    }
    else // multiline
    {
        wxTextPos posCur = 0;
        size_t nLineCount = GetLineCount();
        for ( size_t nLine = 0; nLine < nLineCount; nLine++ )
        {
            // +1 is because the start the start of the next line is one
            // position after the end of this one
            wxTextPos posNew = posCur + GetLines()[nLine].length() + 1;
            if ( posNew > pos )
            {
                // we've found the line, now just calc the column
                if ( x )
                    *x = pos - posCur;

                if ( y )
                    *y = nLine;

#ifdef WXDEBUG_TEXT
                wxASSERT_MSG( XYToPosition(pos - posCur, nLine) == pos,
                              _T("XYToPosition() or PositionToXY() broken") );
#endif // WXDEBUG_TEXT

                return true;
            }
            else // go further down
            {
                posCur = posNew;
            }
        }

        // beyond the last line
        return false;
    }
}

wxTextCoord wxTextCtrl::GetRowsPerLine(wxTextCoord line) const
{
    // a normal line has one row
    wxTextCoord numRows = 1;

    if ( WrapLines() )
    {
        // add the number of additional rows
        numRows += WData().m_linesData[line].GetExtraRowCount();
    }

    return numRows;
}

wxTextCoord wxTextCtrl::GetRowCount() const
{
    wxTextCoord count = GetLineCount();
    if (count == 0)
        return 0;
    if ( WrapLines() )
    {
        count = GetFirstRowOfLine(count - 1) +
                    WData().m_linesData[count - 1].GetRowCount();
    }

    return count;
}

wxTextCoord wxTextCtrl::GetRowAfterLine(wxTextCoord line) const
{
    if ( !WrapLines() )
        return line + 1;

    if ( !WData().IsValidLine(line) )
    {
        LayoutLines(line);
    }

    return WData().m_linesData[line].GetNextRow();
}

wxTextCoord wxTextCtrl::GetFirstRowOfLine(wxTextCoord line) const
{
    if ( !WrapLines() )
        return line;

    if ( !WData().IsValidLine(line) )
    {
        LayoutLines(line);
    }

    return WData().m_linesData[line].GetFirstRow();
}

bool wxTextCtrl::PositionToLogicalXY(wxTextPos pos,
                                     wxCoord *xOut,
                                     wxCoord *yOut) const
{
    wxTextCoord col, line;

    // optimization for special (but common) case when we already have the col
    // and line
    if ( pos == m_curPos )
    {
        col = m_curCol;
        line = m_curRow;
    }
    else // must really calculate col/line from pos
    {
        if ( !PositionToXY(pos, &col, &line) )
            return false;
    }

    int hLine = GetLineHeight();
    wxCoord x, y;
    wxString textLine = GetLineText(line);
    if ( IsSingleLine() || !WrapLines() )
    {
        x = GetTextWidth(textLine.Left(col));
        y = line*hLine;
    }
    else // difficult case: multline control with line wrap
    {
        y = GetFirstRowOfLine(line);

        wxTextCoord colRowStart;
        y += GetRowInLine(line, col, &colRowStart);

        y *= hLine;

        // x is the width of the text before this position in this row
        x = GetTextWidth(textLine.Mid(colRowStart, col - colRowStart));
    }

    if ( xOut )
        *xOut = x;
    if ( yOut )
        *yOut = y;

    return true;
}

bool wxTextCtrl::PositionToDeviceXY(wxTextPos pos,
                                    wxCoord *xOut,
                                    wxCoord *yOut) const
{
    wxCoord x, y;
    if ( !PositionToLogicalXY(pos, &x, &y) )
        return false;

    // finally translate the logical text rect coords into physical client
    // coords
    CalcScrolledPosition(m_rectText.x + x, m_rectText.y + y, xOut, yOut);

    return true;
}

wxPoint wxTextCtrl::GetCaretPosition() const
{
    wxCoord xCaret, yCaret;
    if ( !PositionToDeviceXY(m_curPos, &xCaret, &yCaret) )
    {
        wxFAIL_MSG( _T("Caret can't be beyond the text!") );
    }

    return wxPoint(xCaret, yCaret);
}

// pos may be -1 to show the current position
void wxTextCtrl::ShowPosition(wxTextPos pos)
{
    bool showCaret = GetCaret() && GetCaret()->IsVisible();
    if (showCaret)
        HideCaret();

    if ( IsSingleLine() )
    {
        ShowHorzPosition(GetTextWidth(m_value.Left(pos)));
    }
    else if ( MData().m_scrollRangeX || MData().m_scrollRangeY ) // multiline with scrollbars
    {
        int xStart, yStart;
        GetViewStart(&xStart, &yStart);

        if ( pos == -1 )
            pos = m_curPos;

        wxCoord x, y;
        PositionToLogicalXY(pos, &x, &y);

        wxRect rectText = GetRealTextArea();

        // scroll the position vertically into view: if it is currently above
        // it, make it the first one, otherwise the last one
        if ( MData().m_scrollRangeY )
        {
            y /= GetLineHeight();

            if ( y < yStart )
            {
                Scroll(0, y);
            }
            else // we are currently in or below the view area
            {
                // find the last row currently shown
                wxTextCoord yEnd;

                if ( WrapLines() )
                {
                    // to find the last row we need to use the generic HitTest
                    wxTextCoord col;

                    // OPT this is a bit silly: we undo this in HitTest(), so
                    //     it would be better to factor out the common
                    //     functionality into a separate function (OTOH it
                    //     won't probably save us that much)
                    wxPoint pt(0, rectText.height - 1);
                    pt += GetClientAreaOrigin();
                    pt += m_rectText.GetPosition();
                    HitTest(pt, &col, &yEnd);

                    // find the row inside the line
                    yEnd = GetFirstRowOfLine(yEnd) + GetRowInLine(yEnd, col);
                }
                else
                {
                    // finding the last line is easy if each line has exactly
                    // one row
                    yEnd = yStart + rectText.height / GetLineHeight() - 1;
                }

                if ( yEnd < y )
                {
                    // scroll down: the current item should appear at the
                    // bottom of the view
                    Scroll(0, y - (yEnd - yStart));
                }
            }
        }

        // scroll the position horizontally into view
        //
        // we follow what I believe to be Windows behaviour here, that is if
        // the position is already entirely in the view we do nothing, but if
        // we do have to scroll the window to bring it into view, we scroll it
        // not just enough to show the position but slightly more so that this
        // position is at 1/3 of the window width from the closest border to it
        // (I'm not sure that Windows does exactly this but it looks like this)
        if ( MData().m_scrollRangeX )
        {
            // unlike for the rows, xStart doesn't correspond to the starting
            // column as they all have different widths, so we need to
            // translate everything to pixels

            // we want the text between x and x2 be entirely inside the view
            // (i.e. the current character)

            // make xStart the first visible pixel (and not position)
            int wChar = GetAverageWidth();
            xStart *= wChar;

            if ( x < xStart )
            {
                // we want the position of this column be 1/3 to the right of
                // the left edge
                x -= rectText.width / 3;
                if ( x < 0 )
                    x = 0;
                Scroll(x / wChar, y);
            }
            else // maybe we're beyond the right border of the view?
            {
                wxTextCoord col, row;
                if ( PositionToXY(pos, &col, &row) )
                {
                    wxString lineText = GetLineText(row);
                    wxCoord x2 = x + GetTextWidth(lineText[(size_t)col]);
                    if ( x2 > xStart + rectText.width )
                    {
                        // we want the position of this column be 1/3 to the
                        // left of the right edge, i.e. 2/3 right of the left
                        // one
                        x2 -= (2*rectText.width)/3;
                        if ( x2 < 0 )
                            x2 = 0;
                        Scroll(x2 / wChar, row);
                    }
                }
            }
        }
    }
    //else: multiline but no scrollbars, hence nothing to do

    if (showCaret)
        ShowCaret();
}

// ----------------------------------------------------------------------------
// word stuff
// ----------------------------------------------------------------------------

/*
    TODO: we could have (easy to do) vi-like options for word movement, i.e.
          distinguish between inlusive/exclusive words and between words and
          WORDS (in vim sense) and also, finally, make the set of characters
          which make up a word configurable - currently we use the exclusive
          WORDS only (coincidentally, this is what Windows edit control does)

          For future references, here is what vim help says:

          A word consists of a sequence of letters, digits and underscores, or
          a sequence of other non-blank characters, separated with white space
          (spaces, tabs, <EOL>).  This can be changed with the 'iskeyword'
          option.

          A WORD consists of a sequence of non-blank characters, separated with
          white space.  An empty line is also considered to be a word and a
          WORD.
 */

static inline bool IsWordChar(wxChar ch)
{
    return !wxIsspace(ch);
}

wxTextPos wxTextCtrl::GetWordStart() const
{
    if ( m_curPos == -1 || m_curPos == 0 )
        return 0;

    if ( m_curCol == 0 )
    {
        // go to the end of the previous line
        return m_curPos - 1;
    }

    // it shouldn't be possible to learn where the word starts in the password
    // text entry zone
    if ( IsPassword() )
        return 0;

    // start at the previous position
    const wxChar *p0 = GetLineText(m_curRow).c_str();
    const wxChar *p = p0 + m_curCol - 1;

    // find the end of the previous word
    while ( (p > p0) && !IsWordChar(*p) )
        p--;

    // now find the beginning of this word
    while ( (p > p0) && IsWordChar(*p) )
        p--;

    // we might have gone too far
    if ( !IsWordChar(*p) )
        p++;

    return (m_curPos - m_curCol) + p - p0;
}

wxTextPos wxTextCtrl::GetWordEnd() const
{
    if ( m_curPos == -1 )
        return 0;

    wxString line = GetLineText(m_curRow);
    if ( (size_t)m_curCol == line.length() )
    {
        // if we're on the last position in the line, go to the next one - if
        // it exists
        wxTextPos pos = m_curPos;
        if ( pos < GetLastPosition() )
            pos++;

        return pos;
    }

    // it shouldn't be possible to learn where the word ends in the password
    // text entry zone
    if ( IsPassword() )
        return GetLastPosition();

    // start at the current position
    const wxChar *p0 = line.c_str();
    const wxChar *p = p0 + m_curCol;

    // find the start of the next word
    while ( *p && !IsWordChar(*p) )
        p++;

    // now find the end of it
    while ( *p && IsWordChar(*p) )
        p++;

    // and find the start of the next word
    while ( *p && !IsWordChar(*p) )
        p++;

    return (m_curPos - m_curCol) + p - p0;
}

// ----------------------------------------------------------------------------
// clipboard stuff
// ----------------------------------------------------------------------------

void wxTextCtrl::Copy()
{
#if wxUSE_CLIPBOARD
    if ( HasSelection() )
    {
        wxClipboardLocker clipLock;

        // wxTextFile::Translate() is needed to transform all '\n' into "\r\n"
        wxString text = wxTextFile::Translate(GetTextToShow(GetSelectionText()));
        wxTextDataObject *data = new wxTextDataObject(text);
        wxTheClipboard->SetData(data);
    }
#endif // wxUSE_CLIPBOARD
}

void wxTextCtrl::Cut()
{
    (void)DoCut();

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?