textctrl.cpp

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

CPP
2,166
字号
        wxLogLastError(_T("EM_STREAMOUT"));
    }
    else // streamed out ok
    {
        // NUL-terminate the string because its length could have been
        // decreased by wxRichEditStreamOut
        *(wchBuf.data() + data.len) = L'\0';

        // now convert to the given encoding (this is a possibly lossful
        // conversion but what else can we do)
        wxCSConv conv(encoding);
        size_t lenNeeded = conv.WC2MB(NULL, wchBuf, 0);
        if ( lenNeeded++ )
        {
            conv.WC2MB(wxStringBuffer(out, lenNeeded), wchBuf, lenNeeded);
        }
    }

#if !wxUSE_WCHAR_T
    free(wchBuf);
#endif // !wxUSE_WCHAR_T

    return out;
}

#endif // !wxUSE_UNICODE_MSLU

#endif // wxUSE_RICHEDIT

void wxTextCtrl::WriteText(const wxString& value)
{
    DoWriteText(value);
}

void wxTextCtrl::DoWriteText(const wxString& value, bool selectionOnly)
{
    wxString valueDos;
    if ( m_windowStyle & wxTE_MULTILINE )
        valueDos = wxTextFile::Translate(value, wxTextFileType_Dos);
    else
        valueDos = value;

#if wxUSE_RICHEDIT
    // there are several complications with the rich edit controls here
    bool done = false;
    if ( IsRich() )
    {
        // first, ensure that the new text will be in the default style
        if ( !m_defaultStyle.IsDefault() )
        {
            long start, end;
            GetSelection(&start, &end);
            SetStyle(start, end, m_defaultStyle);
        }

#if wxUSE_UNICODE_MSLU
        // RichEdit doesn't have Unicode version of EM_REPLACESEL on Win9x,
        // but EM_STREAMIN works
        if ( wxUsingUnicowsDll() && GetRichVersion() > 1 )
        {
           done = StreamIn(valueDos, wxFONTENCODING_SYSTEM, selectionOnly);
        }
#endif // wxUSE_UNICODE_MSLU

#if !wxUSE_UNICODE
        // next check if the text we're inserting must be shown in a non
        // default charset -- this only works for RichEdit > 1.0
        if ( GetRichVersion() > 1 )
        {
            wxFont font = m_defaultStyle.GetFont();
            if ( !font.Ok() )
                font = GetFont();

            if ( font.Ok() )
            {
               wxFontEncoding encoding = font.GetEncoding();
               if ( encoding != wxFONTENCODING_SYSTEM )
               {
                   // we have to use EM_STREAMIN to force richedit control 2.0+
                   // to show any text in the non default charset -- otherwise
                   // it thinks it knows better than we do and always shows it
                   // in the default one
                   done = StreamIn(valueDos, encoding, selectionOnly);
               }
            }
        }
#endif // !wxUSE_UNICODE
    }

    if ( !done )
#endif // wxUSE_RICHEDIT
    {
        // in some cases we get 2 EN_CHANGE notifications after the SendMessage
        // call (this happens for plain EDITs with EM_REPLACESEL and under some
        // -- undetermined -- conditions with rich edit) and sometimes we don't
        // get any events at all (plain EDIT with WM_SETTEXT), so ensure that
        // we generate exactly one of them by ignoring all but the first one in
        // SendUpdateEvent() and generating one ourselves if we hadn't got any
        // notifications from Windows
        UpdatesCountFilter ucf(m_updatesCount);

        ::SendMessage(GetHwnd(), selectionOnly ? EM_REPLACESEL : WM_SETTEXT,
                      // EM_REPLACESEL takes 1 to indicate the operation should be redoable
                      selectionOnly ? 1 : 0, (LPARAM)valueDos.c_str());

        if ( !ucf.GotUpdate() )
        {
            SendUpdateEvent();
        }
    }
}

void wxTextCtrl::AppendText(const wxString& text)
{
    SetInsertionPointEnd();

    WriteText(text);

#if wxUSE_RICHEDIT
    // don't do this if we're frozen, saves some time
    if ( !IsFrozen() && IsMultiLine() && GetRichVersion() > 1 )
    {
        // setting the caret to the end and showing it simply doesn't work for
        // RichEdit 2.0 -- force it to still do what we want
        ::SendMessage(GetHwnd(), EM_LINESCROLL, 0, GetNumberOfLines());
    }
#endif // wxUSE_RICHEDIT
}

void wxTextCtrl::Clear()
{
    ::SetWindowText(GetHwnd(), wxEmptyString);

#if wxUSE_RICHEDIT
    if ( !IsRich() )
#endif // wxUSE_RICHEDIT
    {
        // rich edit controls send EN_UPDATE from WM_SETTEXT handler themselves
        // but the normal ones don't -- make Clear() behaviour consistent by
        // always sending this event

        // Windows already sends an update event for single-line
        // controls.
        if ( m_windowStyle & wxTE_MULTILINE )
            SendUpdateEvent();
    }
}

#ifdef __WIN32__

bool wxTextCtrl::EmulateKeyPress(const wxKeyEvent& event)
{
    SetFocus();

    size_t lenOld = GetValue().length();

    wxUint32 code = event.GetRawKeyCode();
    ::keybd_event((BYTE)code, 0, 0 /* key press */, 0);
    ::keybd_event((BYTE)code, 0, KEYEVENTF_KEYUP, 0);

    // assume that any alphanumeric key changes the total number of characters
    // in the control - this should work in 99% of cases
    return GetValue().length() != lenOld;
}

#endif // __WIN32__

// ----------------------------------------------------------------------------
// Clipboard operations
// ----------------------------------------------------------------------------

void wxTextCtrl::Copy()
{
    if (CanCopy())
    {
        ::SendMessage(GetHwnd(), WM_COPY, 0, 0L);
    }
}

void wxTextCtrl::Cut()
{
    if (CanCut())
    {
        ::SendMessage(GetHwnd(), WM_CUT, 0, 0L);
    }
}

void wxTextCtrl::Paste()
{
    if (CanPaste())
    {
        ::SendMessage(GetHwnd(), WM_PASTE, 0, 0L);
    }
}

bool wxTextCtrl::HasSelection() const
{
    long from, to;
    GetSelection(&from, &to);
    return from != to;
}

bool wxTextCtrl::CanCopy() const
{
    // Can copy if there's a selection
    return HasSelection();
}

bool wxTextCtrl::CanCut() const
{
    return CanCopy() && IsEditable();
}

bool wxTextCtrl::CanPaste() const
{
    if ( !IsEditable() )
        return false;

#if wxUSE_RICHEDIT
    if ( IsRich() )
    {
        UINT cf = 0; // 0 == any format

        return ::SendMessage(GetHwnd(), EM_CANPASTE, cf, 0) != 0;
    }
#endif // wxUSE_RICHEDIT

    // Standard edit control: check for straight text on clipboard
    if ( !::OpenClipboard(GetHwndOf(wxTheApp->GetTopWindow())) )
        return false;

    bool isTextAvailable = ::IsClipboardFormatAvailable(CF_TEXT) != 0;
    ::CloseClipboard();

    return isTextAvailable;
}

// ----------------------------------------------------------------------------
// Accessors
// ----------------------------------------------------------------------------

void wxTextCtrl::SetEditable(bool editable)
{
    HWND hWnd = GetHwnd();
    ::SendMessage(hWnd, EM_SETREADONLY, (WPARAM)!editable, (LPARAM)0L);
}

void wxTextCtrl::SetInsertionPoint(long pos)
{
    DoSetSelection(pos, pos);
}

void wxTextCtrl::SetInsertionPointEnd()
{
    // we must not do anything if the caret is already there because calling
    // SetInsertionPoint() thaws the controls if Freeze() had been called even
    // if it doesn't actually move the caret anywhere and so the simple fact of
    // doing it results in horrible flicker when appending big amounts of text
    // to the control in a few chunks (see DoAddText() test in the text sample)
    if ( GetInsertionPoint() == GetLastPosition() )
    {
        return;
    }

    long pos;

#if wxUSE_RICHEDIT
    if ( m_verRichEdit == 1 )
    {
        // we don't have to waste time calling GetLastPosition() in this case
        pos = -1;
    }
    else // !RichEdit 1.0
#endif // wxUSE_RICHEDIT
    {
        pos = GetLastPosition();
    }

    SetInsertionPoint(pos);
}

long wxTextCtrl::GetInsertionPoint() const
{
#if wxUSE_RICHEDIT
    if ( IsRich() )
    {
        CHARRANGE range;
        range.cpMin = 0;
        range.cpMax = 0;
        ::SendMessage(GetHwnd(), EM_EXGETSEL, 0, (LPARAM) &range);
        return range.cpMin;
    }
#endif // wxUSE_RICHEDIT

    DWORD Pos = (DWORD)::SendMessage(GetHwnd(), EM_GETSEL, 0, 0L);
    return Pos & 0xFFFF;
}

wxTextPos wxTextCtrl::GetLastPosition() const
{
    int numLines = GetNumberOfLines();
    long posStartLastLine = XYToPosition(0, numLines - 1);

    long lenLastLine = GetLengthOfLineContainingPos(posStartLastLine);

    return posStartLastLine + lenLastLine;
}

// If the return values from and to are the same, there is no
// selection.
void wxTextCtrl::GetSelection(long* from, long* to) const
{
#if wxUSE_RICHEDIT
    if ( IsRich() )
    {
        CHARRANGE charRange;
        ::SendMessage(GetHwnd(), EM_EXGETSEL, 0, (LPARAM) &charRange);

        *from = charRange.cpMin;
        *to = charRange.cpMax;
    }
    else
#endif // !wxUSE_RICHEDIT
    {
        DWORD dwStart, dwEnd;
        ::SendMessage(GetHwnd(), EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd);

        *from = dwStart;
        *to = dwEnd;
    }
}

bool wxTextCtrl::IsEditable() const
{
    // strangely enough, we may be called before the control is created: our
    // own Create() calls MSWGetStyle() which calls AcceptsFocus() which calls
    // us
    if ( !m_hWnd )
        return true;

    long style = ::GetWindowLong(GetHwnd(), GWL_STYLE);

    return (style & ES_READONLY) == 0;
}

// ----------------------------------------------------------------------------
// selection
// ----------------------------------------------------------------------------

void wxTextCtrl::SetSelection(long from, long to)
{
    // if from and to are both -1, it means (in wxWidgets) that all text should
    // be selected - translate into Windows convention
    if ( (from == -1) && (to == -1) )
    {
        from = 0;
        to = -1;
    }

    DoSetSelection(from, to);
}

void wxTextCtrl::DoSetSelection(long from, long to, bool scrollCaret)
{
    HWND hWnd = GetHwnd();

#if wxUSE_RICHEDIT
    if ( IsRich() )
    {
        CHARRANGE range;
        range.cpMin = from;
        range.cpMax = to;
        ::SendMessage(hWnd, EM_EXSETSEL, 0, (LPARAM) &range);
    }
    else
#endif // wxUSE_RICHEDIT
    {
        ::SendMessage(hWnd, EM_SETSEL, (WPARAM)from, (LPARAM)to);
    }

    if ( scrollCaret && !IsFrozen() )
    {
#if wxUSE_RICHEDIT
        // richedit 3.0 (i.e. the version living in riched20.dll distributed
        // with Windows 2000 and beyond) doesn't honour EM_SCROLLCARET when
        // emulating richedit 2.0 unless the control has focus or ECO_NOHIDESEL
        // option is set (but it does work ok in richedit 1.0 mode...)
        //
        // so to make it work we either need to give focus to it here which
        // will probably create many problems (dummy focus events; window
        // containing the text control being brought to foreground
        // unexpectedly; ...) or to temporarily set ECO_NOHIDESEL which may
        // create other problems too -- and in fact it does because if we turn
        // on/off this style while appending the text to the control, the
        // vertical scrollbar never appears in it even if we append tons of
        // text and to work around this the only solution I found was to use
        // ES_DISABLENOSCROLL
        //
        // this is very ugly but I don't see any other way to make this work
        long style = 0;
        if ( GetRichVersion() > 1 )
        {
            if ( !HasFlag(wxTE_NOHIDESEL) )
            {
                // setting ECO_NOHIDESEL also sets WS_VISIBLE and possibly
                // others, remember the style so we can reset it later if needed
                style = ::GetWindowLong(GetHwnd(), GWL_STYLE);
                ::SendMessage(GetHwnd(), EM_SETOPTIONS,
                              ECOOP_OR, ECO_NOHIDESEL);
            }
            //else: everything is already ok
        }
#endif // wxUSE_RICHEDIT

        ::SendMessage(hWnd, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);

#if wxUSE_RICHEDIT
        // restore ECO_NOHIDESEL if we changed it
        if ( GetRichVersion() > 1 && !HasFlag(wxTE_NOHIDESEL) )
        {
            ::SendMessage(GetHwnd(), EM_SETOPTIONS,
                          ECOOP_AND, ~ECO_NOHIDESEL);
            if ( style != ::GetWindowLong(GetHwnd(), GWL_STYLE) )
                ::SetWindowLong(GetHwnd(), GWL_STYLE, style);
        }
#endif // wxUSE_RICHEDIT
    }
}

// ----------------------------------------------------------------------------
// Working with files
// ----------------------------------------------------------------------------

bool wxTextCtrl::LoadFile(const wxString& file)

⌨️ 快捷键说明

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