X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/0cf5b09998e7d4606387975a56b6a954a98fd7b7..c6e2af4570ee868c93fc504b02f321efe5d5acee:/src/msw/textctrl.cpp diff --git a/src/msw/textctrl.cpp b/src/msw/textctrl.cpp index 6f16032cc9..77ad90c6d3 100644 --- a/src/msw/textctrl.cpp +++ b/src/msw/textctrl.cpp @@ -38,6 +38,7 @@ #include "wx/intl.h" #include "wx/log.h" #include "wx/app.h" + #include "wx/menu.h" #endif #include "wx/module.h" @@ -56,7 +57,11 @@ #include #include -#if wxUSE_RICHEDIT && (!defined(__GNUWIN32_OLD__) || defined(__CYGWIN10__)) +#if wxUSE_RICHEDIT + +// old mingw32 has richedit stuff directly in windows.h and doesn't have +// richedit.h at all +#if !defined(__GNUWIN32_OLD__) || defined(__CYGWIN10__) #include #endif @@ -97,9 +102,11 @@ // Watcom C++ doesn't define this #ifndef SCF_ALL -#define SCF_ALL 0x0004 + #define SCF_ALL 0x0004 #endif +#endif // wxUSE_RICHEDIT + // ---------------------------------------------------------------------------- // private functions // ---------------------------------------------------------------------------- @@ -149,17 +156,25 @@ BEGIN_EVENT_TABLE(wxTextCtrl, wxControl) EVT_CHAR(wxTextCtrl::OnChar) EVT_DROP_FILES(wxTextCtrl::OnDropFiles) +#if wxUSE_RICHEDIT + EVT_RIGHT_UP(wxTextCtrl::OnRightClick) +#endif + EVT_MENU(wxID_CUT, wxTextCtrl::OnCut) EVT_MENU(wxID_COPY, wxTextCtrl::OnCopy) EVT_MENU(wxID_PASTE, wxTextCtrl::OnPaste) EVT_MENU(wxID_UNDO, wxTextCtrl::OnUndo) EVT_MENU(wxID_REDO, wxTextCtrl::OnRedo) + EVT_MENU(wxID_CLEAR, wxTextCtrl::OnDelete) + EVT_MENU(wxID_SELECTALL, wxTextCtrl::OnSelectAll) EVT_UPDATE_UI(wxID_CUT, wxTextCtrl::OnUpdateCut) EVT_UPDATE_UI(wxID_COPY, wxTextCtrl::OnUpdateCopy) EVT_UPDATE_UI(wxID_PASTE, wxTextCtrl::OnUpdatePaste) EVT_UPDATE_UI(wxID_UNDO, wxTextCtrl::OnUpdateUndo) EVT_UPDATE_UI(wxID_REDO, wxTextCtrl::OnUpdateRedo) + EVT_UPDATE_UI(wxID_CLEAR, wxTextCtrl::OnUpdateDelete) + EVT_UPDATE_UI(wxID_SELECTALL, wxTextCtrl::OnUpdateSelectAll) #ifdef __WIN16__ EVT_ERASE_BACKGROUND(wxTextCtrl::OnEraseBackground) #endif @@ -177,7 +192,19 @@ void wxTextCtrl::Init() { #if wxUSE_RICHEDIT m_verRichEdit = 0; -#endif +#endif // wxUSE_RICHEDIT + + m_privateContextMenu = NULL; + m_suppressNextUpdate = FALSE; +} + +wxTextCtrl::~wxTextCtrl() +{ + if (m_privateContextMenu) + { + delete m_privateContextMenu; + m_privateContextMenu = NULL; + } } bool wxTextCtrl::Create(wxWindow *parent, wxWindowID id, @@ -195,44 +222,8 @@ bool wxTextCtrl::Create(wxWindow *parent, wxWindowID id, if ( parent ) parent->AddChild(this); - // translate wxWin style flags to MSW ones, checking for consistency while - // doing it - long msStyle = ES_LEFT | WS_TABSTOP; - - if ( m_windowStyle & wxCLIP_SIBLINGS ) - msStyle |= WS_CLIPSIBLINGS; - - if ( m_windowStyle & wxTE_MULTILINE ) - { - wxASSERT_MSG( !(m_windowStyle & wxTE_PROCESS_ENTER), - wxT("wxTE_PROCESS_ENTER style is ignored for multiline text controls (they always process it)") ); - - msStyle |= ES_MULTILINE | ES_WANTRETURN; - if ((m_windowStyle & wxTE_NO_VSCROLL) == 0) - msStyle |= WS_VSCROLL; - m_windowStyle |= wxTE_PROCESS_ENTER; - } - else // !multiline - { - // there is really no reason to not have this style for single line - // text controls - msStyle |= ES_AUTOHSCROLL; - } - - if ( m_windowStyle & wxHSCROLL ) - msStyle |= WS_HSCROLL | ES_AUTOHSCROLL; - - if ( m_windowStyle & wxTE_READONLY ) - msStyle |= ES_READONLY; - - if ( m_windowStyle & wxTE_PASSWORD ) - msStyle |= ES_PASSWORD; - - if ( m_windowStyle & wxTE_AUTO_SCROLL ) - msStyle |= ES_AUTOHSCROLL; - - if ( m_windowStyle & wxTE_NOHIDESEL ) - msStyle |= ES_NOHIDESEL; + // translate wxWin style flags to MSW ones + WXDWORD msStyle = MSWGetCreateWindowFlags(); // do create the control - either an EDIT or RICHEDIT wxString windowClass = wxT("EDIT"); @@ -292,8 +283,6 @@ bool wxTextCtrl::Create(wxWindow *parent, wxWindowID id, // have we managed to load any richedit version? if ( !s_errorGiven ) { - msStyle |= ES_AUTOVSCROLL; - m_verRichEdit = verRichEdit; if ( m_verRichEdit == 1 ) { @@ -336,10 +325,17 @@ bool wxTextCtrl::Create(wxWindow *parent, wxWindowID id, #if wxUSE_RICHEDIT if ( IsRich() ) { - // have to enable events manually - LPARAM mask = ENM_CHANGE | ENM_DROPFILES | ENM_SELCHANGE | ENM_UPDATE; + // enable the events we're interested in: we want to get EN_CHANGE as + // for the normal controls + LPARAM mask = ENM_CHANGE; - if ( m_windowStyle & wxTE_AUTO_URL ) + if ( GetRichVersion() == 1 ) + { + // we also need EN_MSGFILTER for richedit 1.0 for the reasons + // explained in its handler + mask |= ENM_MOUSEEVENTS; + } + else if ( m_windowStyle & wxTE_AUTO_URL ) { mask |= ENM_LINK; @@ -389,6 +385,99 @@ void wxTextCtrl::AdoptAttributesFromHWND() 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 +{ + // default border for the text controls is the sunken one + if ( (style & wxBORDER_MASK) == wxBORDER_DEFAULT ) + { + style |= wxBORDER_SUNKEN; + } + + 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; + + // 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; + } + } + + style |= wxTE_PROCESS_ENTER; + } + else // !multiline + { + // there is really no reason to not have this style for single line + // text controls + msStyle |= ES_AUTOHSCROLL; + } + + // styles which we add depending on the specified wxWindows styles + if ( style & wxHSCROLL ) + { + // automatically scroll the control horizontally as necessary + 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 if 0 as well but for consistency... + + return msStyle; +} + +void wxTextCtrl::SetWindowStyleFlag(long style) +{ + if ( (style & wxBORDER_MASK) == wxBORDER_DEFAULT ) + style |= wxBORDER_SUNKEN; + +#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); } // ---------------------------------------------------------------------------- @@ -524,7 +613,7 @@ bool wxTextCtrl::StreamIn(const wxString& value, bool selectionOnly) { // 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 + // text in the non default charset -- otherwise it thinks it knows better // than we do and always shows it in the default one // first get the Windows code page for this encoding @@ -560,6 +649,14 @@ bool wxTextCtrl::StreamIn(const wxString& value, // the cast below is needed for broken (very) old mingw32 headers eds.pfnCallback = (EDITSTREAMCALLBACK)wxRichEditStreamIn; + // we're going to receive 2 EN_CHANGE notifications if we got any selection + // (same problem as in DoWriteText()) + if ( selectionOnly && HasSelection() ) + { + // so suppress one of them + m_suppressNextUpdate = TRUE; + } + if ( !::SendMessage(GetHwnd(), EM_STREAMIN, SF_TEXT | SF_UNICODE | @@ -607,7 +704,7 @@ void wxTextCtrl::DoWriteText(const wxString& value, bool selectionOnly) #if wxUSE_UNICODE_MSLU // RichEdit doesn't have Unicode version of EM_REPLACESEL on Win9x, // but EM_STREAMIN works - if ( wxGetOsVersion() == wxWIN95 && GetRichVersion() > 1 ) + if ( wxUsingUnicowsDll() && GetRichVersion() > 1 ) { done = StreamIn(valueDos, wxFONTENCODING_SYSTEM, selectionOnly); } @@ -637,12 +734,38 @@ void wxTextCtrl::DoWriteText(const wxString& value, bool selectionOnly) if ( !done ) #endif // wxUSE_RICHEDIT { - if ( !selectionOnly ) + // in some cases we get 2 EN_CHANGE notifications after the SendMessage + // call below which is confusing for the client code and so should be + // avoided + // + // these cases are: (a) plain EDIT controls if EM_REPLACESEL is used + // and there is a non empty selection currently and (b) rich text + // controls in any case + if ( +#if wxUSE_RICHEDIT + IsRich() || +#endif // wxUSE_RICHEDIT + (selectionOnly && HasSelection()) ) { - SetSelection(-1, -1); + m_suppressNextUpdate = TRUE; } - ::SendMessage(GetHwnd(), EM_REPLACESEL, 0, (LPARAM)valueDos.c_str()); + ::SendMessage(GetHwnd(), selectionOnly ? EM_REPLACESEL : WM_SETTEXT, + 0, (LPARAM)valueDos.c_str()); + + // OTOH, non rich text controls don't generate any events at all when + // we use WM_SETTEXT -- have to emulate them here + if ( !selectionOnly +#if wxUSE_RICHEDIT + && !IsRich() +#endif // wxUSE_RICHEDIT + ) + { + // Windows already sends an update event for single-line + // controls. + if ( m_windowStyle & wxTE_MULTILINE ) + SendUpdateEvent(); + } } AdjustSpaceLimit(); @@ -653,13 +776,55 @@ void wxTextCtrl::AppendText(const wxString& text) SetInsertionPointEnd(); WriteText(text); + +#if wxUSE_RICHEDIT + if ( 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(), wxT("")); + +#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(code, 0, 0 /* key press */, 0); + ::keybd_event(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 // ---------------------------------------------------------------------------- @@ -688,14 +853,19 @@ void wxTextCtrl::Paste() } } -bool wxTextCtrl::CanCopy() const +bool wxTextCtrl::HasSelection() const { - // Can copy if there's a selection 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(); @@ -812,6 +982,12 @@ void wxTextCtrl::GetSelection(long* from, long* to) const 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; @@ -855,7 +1031,44 @@ void wxTextCtrl::DoSetSelection(long from, long to, bool scrollCaret) if ( scrollCaret ) { +#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 + if ( GetRichVersion() > 1 ) + { + if ( !HasFlag(wxTE_NOHIDESEL) ) + { + ::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); + } +#endif // wxUSE_RICHEDIT } #else // Win16 // WPARAM is 0: selection is scrolled into view @@ -1087,7 +1300,7 @@ bool wxTextCtrl::MSWShouldPreProcessMessage(WXMSG* pMsg) // usual preprocessing for them if ( msg->message == WM_KEYDOWN ) { - WORD vkey = msg->wParam; + WORD vkey = (WORD) msg->wParam; if ( (HIWORD(msg->lParam) & KF_ALTDOWN) == KF_ALTDOWN ) { if ( vkey == VK_BACK ) @@ -1095,24 +1308,42 @@ bool wxTextCtrl::MSWShouldPreProcessMessage(WXMSG* pMsg) } else // no Alt { - if ( wxIsCtrlDown() ) + // we want to process some Ctrl-foo and Shift-bar but no key + // combinations without either Ctrl or Shift nor with both of them + // pressed + const int ctrl = wxIsCtrlDown(), + shift = wxIsShiftDown(); + switch ( ctrl + shift ) { - switch ( vkey ) - { - case 'C': - case 'V': - case 'X': - case VK_INSERT: - case VK_DELETE: - case VK_HOME: - case VK_END: - return FALSE; - } - } - else if ( wxIsShiftDown() ) - { - if ( vkey == VK_INSERT || vkey == VK_DELETE ) - return FALSE; + default: + wxFAIL_MSG( _T("how many modifiers have we got?") ); + // fall through + + case 0: + case 2: + break; + + case 1: + // either Ctrl or Shift pressed + if ( ctrl ) + { + switch ( vkey ) + { + case 'C': + case 'V': + case 'X': + case VK_INSERT: + case VK_DELETE: + case VK_HOME: + case VK_END: + return FALSE; + } + } + else // Shift is pressed + { + if ( vkey == VK_INSERT || vkey == VK_DELETE ) + return FALSE; + } } } } @@ -1122,7 +1353,7 @@ bool wxTextCtrl::MSWShouldPreProcessMessage(WXMSG* pMsg) void wxTextCtrl::OnChar(wxKeyEvent& event) { - switch ( event.KeyCode() ) + switch ( event.GetKeyCode() ) { case WXK_RETURN: if ( !(m_windowStyle & wxTE_MULTILINE) ) @@ -1160,33 +1391,73 @@ void wxTextCtrl::OnChar(wxKeyEvent& event) long wxTextCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) { - // we always want the characters and the arrows + long lRc = wxTextCtrlBase::MSWWindowProc(nMsg, wParam, lParam); + if ( nMsg == WM_GETDLGCODE ) { - // we always want the chars and the arrows + // we always want the chars and the arrows: the arrows for navigation + // and the chars because we want Ctrl-C to work even in a read only + // control long lDlgCode = DLGC_WANTCHARS | DLGC_WANTARROWS; - // we may have several different cases: - // 1. normal case: both TAB and ENTER are used for dialog navigation - // 2. ctrl which wants TAB for itself: ENTER is used to pass to the next - // control in the dialog - // 3. ctrl which wants ENTER for itself: TAB is used for dialog navigation - // 4. ctrl which wants both TAB and ENTER: Ctrl-ENTER is used to pass to - // the next control - if ( m_windowStyle & wxTE_PROCESS_ENTER ) - lDlgCode |= DLGC_WANTMESSAGE; - if ( m_windowStyle & wxTE_PROCESS_TAB ) - lDlgCode |= DLGC_WANTTAB; + if ( IsEditable() ) + { + // we may have several different cases: + // 1. normal case: both TAB and ENTER are used for dlg navigation + // 2. ctrl which wants TAB for itself: ENTER is used to pass to the + // next control in the dialog + // 3. ctrl which wants ENTER for itself: TAB is used for dialog + // navigation + // 4. ctrl which wants both TAB and ENTER: Ctrl-ENTER is used to go + // to the next control + + // the multiline edit control should always get for itself + if ( HasFlag(wxTE_PROCESS_ENTER) || HasFlag(wxTE_MULTILINE) ) + lDlgCode |= DLGC_WANTMESSAGE; + + if ( HasFlag(wxTE_PROCESS_TAB) ) + lDlgCode |= DLGC_WANTTAB; + + lRc |= lDlgCode; + } + else // !editable + { + // NB: use "=", not "|=" as the base class version returns the + // same flags is this state as usual (i.e. including + // DLGC_WANTMESSAGE). This is strange (how does it work in the + // native Win32 apps?) but for now live with it. + lRc = lDlgCode; + } + } + + return lRc; +} + +// ---------------------------------------------------------------------------- +// text control event processing +// ---------------------------------------------------------------------------- + +bool wxTextCtrl::SendUpdateEvent() +{ + // is event reporting suspended? + if ( m_suppressNextUpdate ) + { + // do process the next one + m_suppressNextUpdate = FALSE; - return lDlgCode; + return FALSE; } - return wxTextCtrlBase::MSWWindowProc(nMsg, wParam, lParam); + wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId()); + InitCommandEvent(event); + event.SetString(GetValue()); + + return ProcessCommand(event); } bool wxTextCtrl::MSWCommand(WXUINT param, WXWORD WXUNUSED(id)) { - switch (param) + switch ( param ) { case EN_SETFOCUS: case EN_KILLFOCUS: @@ -1194,22 +1465,17 @@ bool wxTextCtrl::MSWCommand(WXUINT param, WXWORD WXUNUSED(id)) wxFocusEvent event(param == EN_KILLFOCUS ? wxEVT_KILL_FOCUS : wxEVT_SET_FOCUS, m_windowId); - event.SetEventObject( this ); + event.SetEventObject(this); GetEventHandler()->ProcessEvent(event); } break; case EN_CHANGE: - { - wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, m_windowId); - InitCommandEvent(event); - event.SetString(GetValue()); - ProcessCommand(event); - } + SendUpdateEvent(); break; case EN_MAXTEXT: - // the text size limit has been hit - increase it + // the text size limit has been hit -- try to increase it if ( !AdjustSpaceLimit() ) { wxCommandEvent event(wxEVT_COMMAND_TEXT_MAXLEN, m_windowId); @@ -1219,12 +1485,7 @@ bool wxTextCtrl::MSWCommand(WXUINT param, WXWORD WXUNUSED(id)) } break; - // the other notification messages are not processed - case EN_UPDATE: - case EN_ERRSPACE: - case EN_HSCROLL: - case EN_VSCROLL: - return FALSE; + // the other edit notification messages are not processed default: return FALSE; } @@ -1355,8 +1616,10 @@ bool wxTextCtrl::AdjustSpaceLimit() bool wxTextCtrl::AcceptsFocus() const { - // we don't want focus if we can't be edited - return IsEditable() && wxControl::AcceptsFocus(); + // we don't want focus if we can't be edited unless we're a multiline + // control because then it might be still nice to get focus from keyboard + // to be able to scroll it without mouse + return (IsEditable() || IsMultiLine()) && wxControl::AcceptsFocus(); } wxSize wxTextCtrl::DoGetBestSize() const @@ -1405,6 +1668,19 @@ void wxTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event)) Redo(); } +void wxTextCtrl::OnDelete(wxCommandEvent& event) +{ + long from, to; + GetSelection(& from, & to); + if (from != -1 && to != -1) + Remove(from, to); +} + +void wxTextCtrl::OnSelectAll(wxCommandEvent& event) +{ + SetSelection(-1, -1); +} + void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent& event) { event.Enable( CanCut() ); @@ -1430,6 +1706,44 @@ void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event) event.Enable( CanRedo() ); } +void wxTextCtrl::OnUpdateDelete(wxUpdateUIEvent& event) +{ + long from, to; + GetSelection(& from, & to); + event.Enable(from != -1 && to != -1 && from != to && IsEditable()) ; +} + +void wxTextCtrl::OnUpdateSelectAll(wxUpdateUIEvent& event) +{ + event.Enable(GetLastPosition() > 0); +} + +void wxTextCtrl::OnRightClick(wxMouseEvent& event) +{ +#if wxUSE_RICHEDIT + if (IsRich()) + { + if (!m_privateContextMenu) + { + m_privateContextMenu = new wxMenu; + m_privateContextMenu->Append(wxID_UNDO, _("&Undo")); + m_privateContextMenu->Append(wxID_REDO, _("&Redo")); + m_privateContextMenu->AppendSeparator(); + m_privateContextMenu->Append(wxID_CUT, _("Cu&t")); + m_privateContextMenu->Append(wxID_COPY, _("&Copy")); + m_privateContextMenu->Append(wxID_PASTE, _("&Paste")); + m_privateContextMenu->Append(wxID_CLEAR, _("&Delete")); + m_privateContextMenu->AppendSeparator(); + m_privateContextMenu->Append(wxID_SELECTALL, _("Select &All")); + } + PopupMenu(m_privateContextMenu, event.GetPosition()); + return; + } + else +#endif + event.Skip(); +} + // the rest of the file only deals with the rich edit controls #if wxUSE_RICHEDIT @@ -1440,63 +1754,94 @@ void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event) bool wxTextCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) { NMHDR *hdr = (NMHDR* )lParam; - if ( hdr->code == EN_LINK ) + switch ( hdr->code ) { - ENLINK *enlink = (ENLINK *)hdr; - - switch ( enlink->msg ) - { - case WM_SETCURSOR: - // ok, so it is hardcoded - do we really nee to customize it? - ::SetCursor(GetHcursorOf(wxCursor(wxCURSOR_HAND))); - *result = TRUE; - break; - - case WM_MOUSEMOVE: - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - case WM_LBUTTONDBLCLK: - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - case WM_RBUTTONDBLCLK: - // send a mouse event + case EN_MSGFILTER: + { + const MSGFILTER *msgf = (MSGFILTER *)lParam; + UINT msg = msgf->msg; + + // this is a bit crazy but richedit 1.0 sends us all mouse + // events _except_ WM_LBUTTONUP (don't ask me why) so we have + // generate the wxWin events for this message manually + // + // NB: in fact, this is still not totally correct as it does + // send us WM_LBUTTONUP if the selection was cleared by the + // last click -- so currently we get 2 events in this case, + // but as I don't see any obvious way to check for this I + // leave this code in place because it's still better than + // not getting left up events at all + if ( msg == WM_LBUTTONUP ) { - static const wxEventType eventsMouse[] = - { - wxEVT_MOTION, - wxEVT_LEFT_DOWN, - wxEVT_LEFT_UP, - wxEVT_LEFT_DCLICK, - wxEVT_RIGHT_DOWN, - wxEVT_RIGHT_UP, - wxEVT_RIGHT_DCLICK, - }; - - // the event ids are consecutive - wxMouseEvent - evtMouse(eventsMouse[enlink->msg - WM_MOUSEMOVE]); - - InitMouseEvent(evtMouse, - GET_X_LPARAM(enlink->lParam), - GET_Y_LPARAM(enlink->lParam), - enlink->wParam); - - wxTextUrlEvent event(m_windowId, evtMouse, - enlink->chrg.cpMin, - enlink->chrg.cpMax); - - InitCommandEvent(event); - - *result = ProcessCommand(event); + WXUINT flags = msgf->wParam; + int x = GET_X_LPARAM(msgf->lParam), + y = GET_Y_LPARAM(msgf->lParam); + + HandleMouseEvent(msg, x, y, flags); } - break; - } + } - return TRUE; - } + // return TRUE to process the event (and FALSE to ignore it) + return TRUE; - // not processed - return FALSE; + case EN_LINK: + { + const ENLINK *enlink = (ENLINK *)hdr; + + switch ( enlink->msg ) + { + case WM_SETCURSOR: + // ok, so it is hardcoded - do we really nee to + // customize it? + ::SetCursor(GetHcursorOf(wxCursor(wxCURSOR_HAND))); + *result = TRUE; + break; + + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_LBUTTONDBLCLK: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_RBUTTONDBLCLK: + // send a mouse event + { + static const wxEventType eventsMouse[] = + { + wxEVT_MOTION, + wxEVT_LEFT_DOWN, + wxEVT_LEFT_UP, + wxEVT_LEFT_DCLICK, + wxEVT_RIGHT_DOWN, + wxEVT_RIGHT_UP, + wxEVT_RIGHT_DCLICK, + }; + + // the event ids are consecutive + wxMouseEvent + evtMouse(eventsMouse[enlink->msg - WM_MOUSEMOVE]); + + InitMouseEvent(evtMouse, + GET_X_LPARAM(enlink->lParam), + GET_Y_LPARAM(enlink->lParam), + enlink->wParam); + + wxTextUrlEvent event(m_windowId, evtMouse, + enlink->chrg.cpMin, + enlink->chrg.cpMax); + + InitCommandEvent(event); + + *result = ProcessCommand(event); + } + break; + } + } + return TRUE; + } + + // not processed, leave it to the base class + return wxTextCtrlBase::MSWOnNotify(idCtrl, lParam, result); } // ---------------------------------------------------------------------------- @@ -1547,6 +1892,8 @@ bool wxTextCtrl::SetForegroundColour(const wxColour& colour) // styling support for rich edit controls // ---------------------------------------------------------------------------- +#if wxUSE_RICHEDIT + bool wxTextCtrl::SetStyle(long start, long end, const wxTextAttr& style) { if ( !IsRich() ) @@ -1680,6 +2027,21 @@ bool wxTextCtrl::SetStyle(long start, long end, const wxTextAttr& style) return ok; } +bool wxTextCtrl::SetDefaultStyle(const wxTextAttr& style) +{ + if ( !wxTextCtrlBase::SetDefaultStyle(style) ) + return FALSE; + + // we have to do this or the style wouldn't apply for the text typed by the + // user + long posLast = GetLastPosition(); + SetStyle(posLast, posLast, m_defaultStyle); + + return TRUE; +} + +#endif + // ---------------------------------------------------------------------------- // wxRichEditModule // ---------------------------------------------------------------------------- @@ -1692,7 +2054,7 @@ bool wxRichEditModule::OnInit() void wxRichEditModule::OnExit() { - for ( int i = 0; i < WXSIZEOF(ms_hRichEdit); i++ ) + for ( size_t i = 0; i < WXSIZEOF(ms_hRichEdit); i++ ) { if ( ms_hRichEdit[i] ) {