textctrl.cpp

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

CPP
2,166
字号
#if wxUSE_RICHEDIT
    wxString classname = wxGetWindowClass(GetHWND());

    if ( classname.IsSameAs(_T("EDIT"), false /* no case */) )
    {
        m_verRichEdit = 0;
    }
    else // rich edit?
    {
        wxChar c;
        if ( wxSscanf(classname, _T("RichEdit%d0%c"), &m_verRichEdit, &c) != 2 )
        {
            wxLogDebug(_T("Unknown edit control '%s'."), classname.c_str());

            m_verRichEdit = 0;
        }
    }
#endif // wxUSE_RICHEDIT

    if (style & ES_MULTILINE)
        m_windowStyle |= wxTE_MULTILINE;
    if (style & ES_PASSWORD)
        m_windowStyle |= wxTE_PASSWORD;
    if (style & ES_READONLY)
        m_windowStyle |= wxTE_READONLY;
    if (style & ES_WANTRETURN)
        m_windowStyle |= wxTE_PROCESS_ENTER;
    if (style & ES_CENTER)
        m_windowStyle |= wxTE_CENTRE;
    if (style & ES_RIGHT)
        m_windowStyle |= wxTE_RIGHT;
}

WXDWORD wxTextCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const
{
    long msStyle = wxControl::MSWGetStyle(style, exstyle);

    // styles which we alaways add by default
    if ( style & wxTE_MULTILINE )
    {
        wxASSERT_MSG( !(style & wxTE_PROCESS_ENTER),
                      wxT("wxTE_PROCESS_ENTER style is ignored for multiline text controls (they always process it)") );

        msStyle |= ES_MULTILINE | ES_WANTRETURN;
        if ( !(style & wxTE_NO_VSCROLL) )
        {
            // always adjust the vertical scrollbar automatically if we have it
            msStyle |= WS_VSCROLL | ES_AUTOVSCROLL;

#if wxUSE_RICHEDIT
            // we have to use this style for the rich edit controls because
            // without it the vertical scrollbar never appears at all in
            // richedit 3.0 because of our ECO_NOHIDESEL hack (search for it)
            if ( style & wxTE_RICH2 )
            {
                msStyle |= ES_DISABLENOSCROLL;
            }
#endif // wxUSE_RICHEDIT
        }

        style |= wxTE_PROCESS_ENTER;
    }
    else // !multiline
    {
        // there is really no reason to not have this style for single line
        // text controls
        msStyle |= ES_AUTOHSCROLL;
    }

    // note that wxTE_DONTWRAP is the same as wxHSCROLL so if we have a horz
    // scrollbar, there is no wrapping -- which makes sense
    if ( style & wxTE_DONTWRAP )
    {
        // automatically scroll the control horizontally as necessary
        //
        // NB: ES_AUTOHSCROLL is needed for richedit controls or they don't
        //     show horz scrollbar at all, even in spite of WS_HSCROLL, and as
        //     it doesn't seem to do any harm for plain edit controls, add it
        //     always
        msStyle |= WS_HSCROLL | ES_AUTOHSCROLL;
    }

    if ( style & wxTE_READONLY )
        msStyle |= ES_READONLY;

    if ( style & wxTE_PASSWORD )
        msStyle |= ES_PASSWORD;

    if ( style & wxTE_NOHIDESEL )
        msStyle |= ES_NOHIDESEL;

    // note that we can't do do "& wxTE_LEFT" as wxTE_LEFT == 0
    if ( style & wxTE_CENTRE )
        msStyle |= ES_CENTER;
    else if ( style & wxTE_RIGHT )
        msStyle |= ES_RIGHT;
    else
        msStyle |= ES_LEFT; // ES_LEFT is 0 as well but for consistency...

    return msStyle;
}

void wxTextCtrl::SetWindowStyleFlag(long style)
{
#if wxUSE_RICHEDIT
    // we have to deal with some styles separately because they can't be
    // changed by simply calling SetWindowLong(GWL_STYLE) but can be changed
    // using richedit-specific EM_SETOPTIONS
    if ( IsRich() &&
            ((style & wxTE_NOHIDESEL) != (GetWindowStyle() & wxTE_NOHIDESEL)) )
    {
        bool set = (style & wxTE_NOHIDESEL) != 0;

        ::SendMessage(GetHwnd(), EM_SETOPTIONS, set ? ECOOP_OR : ECOOP_AND,
                      set ? ECO_NOHIDESEL : ~ECO_NOHIDESEL);
    }
#endif // wxUSE_RICHEDIT

    wxControl::SetWindowStyleFlag(style);
}

// ----------------------------------------------------------------------------
// set/get the controls text
// ----------------------------------------------------------------------------

wxString wxTextCtrl::GetValue() const
{
    // range 0..-1 is special for GetRange() and means to retrieve all text
    return GetRange(0, -1);
}

wxString wxTextCtrl::GetRange(long from, long to) const
{
    wxString str;

    if ( from >= to && to != -1 )
    {
        // nothing to retrieve
        return str;
    }

#if wxUSE_RICHEDIT
    if ( IsRich() )
    {
        int len = GetWindowTextLength(GetHwnd());
        if ( len > from )
        {
            if ( to == -1 )
                to = len;

#if !wxUSE_UNICODE
            // we must use EM_STREAMOUT if we don't want to lose all characters
            // not representable in the current character set (EM_GETTEXTRANGE
            // simply replaces them with question marks...)
            if ( GetRichVersion() > 1 )
            {
                // we must have some encoding, otherwise any 8bit chars in the
                // control are simply *lost* (replaced by '?')
                wxFontEncoding encoding = wxFONTENCODING_SYSTEM;

                wxFont font = m_defaultStyle.GetFont();
                if ( !font.Ok() )
                    font = GetFont();

                if ( font.Ok() )
                {
                   encoding = font.GetEncoding();
                }

                if ( encoding == wxFONTENCODING_SYSTEM )
                {
                    encoding = wxLocale::GetSystemEncoding();
                }

                if ( encoding == wxFONTENCODING_SYSTEM )
                {
                    encoding = wxFONTENCODING_ISO8859_1;
                }

                str = StreamOut(encoding);

                if ( !str.empty() )
                {
                    // we have to manually extract the required part, luckily
                    // this is easy in this case as EOL characters in str are
                    // just LFs because we remove CRs in wxRichEditStreamOut
                    str = str.Mid(from, to - from);
                }
            }

            // StreamOut() wasn't used or failed, try to do it in normal way
            if ( str.empty() )
#endif // !wxUSE_UNICODE
            {
                // alloc one extra WORD as needed by the control
                wxStringBuffer tmp(str, ++len);
                wxChar *p = tmp;

                TEXTRANGE textRange;
                textRange.chrg.cpMin = from;
                textRange.chrg.cpMax = to;
                textRange.lpstrText = p;

                (void)::SendMessage(GetHwnd(), EM_GETTEXTRANGE,
                                    0, (LPARAM)&textRange);

                if ( m_verRichEdit > 1 )
                {
                    // RichEdit 2.0 uses just CR ('\r') for the
                    // newlines which is neither Unix nor Windows
                    // style - convert it to something reasonable
                    for ( ; *p; p++ )
                    {
                        if ( *p == _T('\r') )
                            *p = _T('\n');
                    }
                }
            }

            if ( m_verRichEdit == 1 )
            {
                // convert to the canonical form - see comment below
                str = wxTextFile::Translate(str, wxTextFileType_Unix);
            }
        }
        //else: no text at all, leave the string empty
    }
    else
#endif // wxUSE_RICHEDIT
    {
        // retrieve all text
        str = wxGetWindowText(GetHWND());

        // need only a range?
        if ( from < to )
        {
            str = str.Mid(from, to - from);
        }

        // WM_GETTEXT uses standard DOS CR+LF (\r\n) convention - convert to the
        // canonical one (same one as above) for consistency with the other kinds
        // of controls and, more importantly, with the other ports
        str = wxTextFile::Translate(str, wxTextFileType_Unix);
    }

    return str;
}

void wxTextCtrl::SetValue(const wxString& value)
{
    // if the text is long enough, it's faster to just set it instead of first
    // comparing it with the old one (chances are that it will be different
    // anyhow, this comparison is there to avoid flicker for small single-line
    // edit controls mostly)
    if ( (value.length() > 0x400) || (value != GetValue()) )
    {
        DoWriteText(value, false /* not selection only */);

        // for compatibility, don't move the cursor when doing SetValue()
        SetInsertionPoint(0);
    }
    else // same text
    {
        // still send an event for consistency
        SendUpdateEvent();
    }

    // we should reset the modified flag even if the value didn't really change

    // mark the control as being not dirty - we changed its text, not the
    // user
    DiscardEdits();
}

#if wxUSE_RICHEDIT && (!wxUSE_UNICODE || wxUSE_UNICODE_MSLU)

// TODO: using memcpy() would improve performance a lot for big amounts of text

DWORD CALLBACK
wxRichEditStreamIn(DWORD dwCookie, BYTE *buf, LONG cb, LONG *pcb)
{
    *pcb = 0;

    const wchar_t ** const ppws = (const wchar_t **)dwCookie;

    wchar_t *wbuf = (wchar_t *)buf;
    const wchar_t *wpc = *ppws;
    while ( cb && *wpc )
    {
        *wbuf++ = *wpc++;

        cb -= sizeof(wchar_t);
        (*pcb) += sizeof(wchar_t);
    }

    *ppws = wpc;

    return 0;
}

// helper struct used to pass parameters from wxTextCtrl to wxRichEditStreamOut
struct wxStreamOutData
{
    wchar_t *wpc;
    size_t len;
};

DWORD CALLBACK
wxRichEditStreamOut(DWORD_PTR dwCookie, BYTE *buf, LONG cb, LONG *pcb)
{
    *pcb = 0;

    wxStreamOutData *data = (wxStreamOutData *)dwCookie;

    const wchar_t *wbuf = (const wchar_t *)buf;
    wchar_t *wpc = data->wpc;
    while ( cb )
    {
        wchar_t wch = *wbuf++;

        // turn "\r\n" into "\n" on the fly
        if ( wch != L'\r' )
            *wpc++ = wch;
        else
            data->len--;

        cb -= sizeof(wchar_t);
        (*pcb) += sizeof(wchar_t);
    }

    data->wpc = wpc;

    return 0;
}


#if wxUSE_UNICODE_MSLU
    #define UNUSED_IF_MSLU(param)
#else
    #define UNUSED_IF_MSLU(param) param
#endif

bool
wxTextCtrl::StreamIn(const wxString& value,
                     wxFontEncoding UNUSED_IF_MSLU(encoding),
                     bool selectionOnly)
{
#if wxUSE_UNICODE_MSLU
    const wchar_t *wpc = value.c_str();
#else // !wxUSE_UNICODE_MSLU
    wxCSConv conv(encoding);

    const size_t len = conv.MB2WC(NULL, value, value.length());

#if wxUSE_WCHAR_T
    wxWCharBuffer wchBuf(len);
    wchar_t *wpc = wchBuf.data();
#else
    wchar_t *wchBuf = (wchar_t *)malloc((len + 1)*sizeof(wchar_t));
    wchar_t *wpc = wchBuf;
#endif

    conv.MB2WC(wpc, value, value.length());
#endif // wxUSE_UNICODE_MSLU

    // finally, stream it in the control
    EDITSTREAM eds;
    wxZeroMemory(eds);
    eds.dwCookie = (DWORD)&wpc;
    // the cast below is needed for broken (very) old mingw32 headers
    eds.pfnCallback = (EDITSTREAMCALLBACK)wxRichEditStreamIn;

    // same problem as in DoWriteText(): we can get multiple events here
    UpdatesCountFilter ucf(m_updatesCount);

    ::SendMessage(GetHwnd(), EM_STREAMIN,
                  SF_TEXT |
                  SF_UNICODE |
                  (selectionOnly ? SFF_SELECTION : 0),
                  (LPARAM)&eds);

    // It's okay for EN_UPDATE to not be sent if the selection is empty and
    // the text is empty, otherwise warn the programmer about it.
    wxASSERT_MSG( ucf.GotUpdate() || ( !HasSelection() && value.empty() ),
                  _T("EM_STREAMIN didn't send EN_UPDATE?") );

    if ( eds.dwError )
    {
        wxLogLastError(_T("EM_STREAMIN"));
    }

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

    return true;
}

#if !wxUSE_UNICODE_MSLU

wxString
wxTextCtrl::StreamOut(wxFontEncoding encoding, bool selectionOnly) const
{
    wxString out;

    const int len = GetWindowTextLength(GetHwnd());

#if wxUSE_WCHAR_T
    wxWCharBuffer wchBuf(len);
    wchar_t *wpc = wchBuf.data();
#else
    wchar_t *wchBuf = (wchar_t *)malloc((len + 1)*sizeof(wchar_t));
    wchar_t *wpc = wchBuf;
#endif

    wxStreamOutData data;
    data.wpc = wpc;
    data.len = len;

    EDITSTREAM eds;
    wxZeroMemory(eds);
    eds.dwCookie = (DWORD)&data;
    eds.pfnCallback = wxRichEditStreamOut;

    ::SendMessage
      (
        GetHwnd(),
        EM_STREAMOUT,
        SF_TEXT | SF_UNICODE | (selectionOnly ? SFF_SELECTION : 0),
        (LPARAM)&eds
      );

    if ( eds.dwError )
    {

⌨️ 快捷键说明

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