X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/e9863f4ea1ab957e59b421d36e6199c7b472476d..8472511246c9160d4ff40ab86f635fc67c10b54a:/src/common/textentrycmn.cpp?ds=inline diff --git a/src/common/textentrycmn.cpp b/src/common/textentrycmn.cpp index 5ec6850708..0db04b25f8 100644 --- a/src/common/textentrycmn.cpp +++ b/src/common/textentrycmn.cpp @@ -31,25 +31,191 @@ #endif //WX_PRECOMP #include "wx/textentry.h" +#include "wx/textcompleter.h" #include "wx/clipbrd.h" +// ---------------------------------------------------------------------------- +// wxTextEntryHintData +// ---------------------------------------------------------------------------- + +class WXDLLIMPEXP_CORE wxTextEntryHintData wxBIND_OR_CONNECT_HACK_ONLY_BASE_CLASS +{ +public: + wxTextEntryHintData(wxTextEntryBase *entry, wxWindow *win) + : m_entry(entry), + m_win(win), + m_text(m_entry->GetValue()) + { + wxBIND_OR_CONNECT_HACK(win, wxEVT_SET_FOCUS, wxFocusEventHandler, + wxTextEntryHintData::OnSetFocus, this); + wxBIND_OR_CONNECT_HACK(win, wxEVT_KILL_FOCUS, wxFocusEventHandler, + wxTextEntryHintData::OnKillFocus, this); + wxBIND_OR_CONNECT_HACK(win, wxEVT_TEXT, + wxCommandEventHandler, + wxTextEntryHintData::OnTextChanged, this); + } + + // default dtor is ok + + // Get the real text of the control such as it was before we replaced it + // with the hint. + const wxString& GetText() const { return m_text; } + + // Set the hint to show, shouldn't be empty normally. + // + // This should be called after creating a new wxTextEntryHintData object + // and may be called more times in the future. + void SetHintString(const wxString& hint) + { + m_hint = hint; + + if ( !m_win->HasFocus() ) + ShowHintIfAppropriate(); + //else: The new hint will be shown later when we lose focus. + } + + const wxString& GetHintString() const { return m_hint; } + + // This is called whenever the text control contents changes. + // + // We call it ourselves when this change generates an event but it's also + // necessary to call it explicitly from wxTextEntry::ChangeValue() as it, + // by design, does not generate any events. + void HandleTextUpdate(const wxString& text) + { + m_text = text; + + // If we're called because of a call to Set or ChangeValue(), the + // control may still have the hint text colour, reset it in this case. + RestoreTextColourIfNecessary(); + } + +private: + // Show the hint in the window if we should do it, i.e. if the window + // doesn't have any text of its own. + void ShowHintIfAppropriate() + { + // Never overwrite existing window text. + if ( !m_text.empty() ) + return; + + // Save the old text colour and set a more inconspicuous one for the + // hint. + m_colFg = m_win->GetForegroundColour(); + m_win->SetForegroundColour(*wxLIGHT_GREY); + + m_entry->DoSetValue(m_hint, wxTextEntryBase::SetValue_NoEvent); + } + + // Restore the original text colour if we had changed it to show the hint + // and not restored it yet. + void RestoreTextColourIfNecessary() + { + if ( m_colFg.IsOk() ) + { + m_win->SetForegroundColour(m_colFg); + m_colFg = wxColour(); + } + } + + void OnSetFocus(wxFocusEvent& event) + { + // If we had been showing the hint before, remove it now and restore + // the normal colour. + if ( m_text.empty() ) + { + RestoreTextColourIfNecessary(); + + m_entry->DoSetValue(wxString(), wxTextEntryBase::SetValue_NoEvent); + } + + event.Skip(); + } + + void OnKillFocus(wxFocusEvent& event) + { + // Restore the hint if the user didn't enter anything. + ShowHintIfAppropriate(); + + event.Skip(); + } + + void OnTextChanged(wxCommandEvent& event) + { + // Update the stored window text. + // + // Notice that we can't use GetValue() nor wxCommandEvent::GetString() + // which uses it internally because this would just forward back to us + // so go directly to the private method which returns the real control + // contents. + HandleTextUpdate(m_entry->DoGetValue()); + + event.Skip(); + } + + + // the text control we're associated with (as its interface and its window) + wxTextEntryBase * const m_entry; + wxWindow * const m_win; + + // the original foreground colour of m_win before we changed it + wxColour m_colFg; + + // The hint passed to wxTextEntry::SetHint(), never empty. + wxString m_hint; + + // The real text of the window. + wxString m_text; + + + wxDECLARE_NO_COPY_CLASS(wxTextEntryHintData); +}; + // ============================================================================ // wxTextEntryBase implementation // ============================================================================ +wxTextEntryBase::~wxTextEntryBase() +{ + delete m_hintData; +} + +// ---------------------------------------------------------------------------- +// text accessors +// ---------------------------------------------------------------------------- + +wxString wxTextEntryBase::GetValue() const +{ + return m_hintData ? m_hintData->GetText() : DoGetValue(); +} + wxString wxTextEntryBase::GetRange(long from, long to) const { wxString sel; wxString value = GetValue(); - - if ((from < to) && ((long)value.length() >= to + from)) + + if ( from < to && (long)value.length() >= to ) { - sel = GetValue().substr(from, to - from); + sel = value.substr(from, to - from); } return sel; } +// ---------------------------------------------------------------------------- +// text operations +// ---------------------------------------------------------------------------- + +void wxTextEntryBase::ChangeValue(const wxString& value) +{ + DoSetValue(value, SetValue_NoEvent); + + // As we didn't generate any events for wxTextEntryHintData to catch, + // notify it explicitly about our changed contents. + if ( m_hintData ) + m_hintData->HandleTextUpdate(value); +} + void wxTextEntryBase::AppendText(const wxString& text) { SetInsertionPointEnd(); @@ -58,10 +224,22 @@ void wxTextEntryBase::AppendText(const wxString& text) void wxTextEntryBase::DoSetValue(const wxString& value, int flags) { - EventsSuppressor noeventsIf(this, !(flags & SetValue_SendEvent)); + if ( value != DoGetValue() ) + { + EventsSuppressor noeventsIf(this, !(flags & SetValue_SendEvent)); - SelectAll(); - WriteText(value); + SelectAll(); + WriteText(value); + + SetInsertionPoint(0); + } + else // Same value, no need to do anything. + { + // Except that we still need to generate the event for consistency with + // the normal case when the text does change. + if ( flags & SetValue_SendEvent ) + SendTextUpdatedEvent(GetEditableWindow()); + } } void wxTextEntryBase::Replace(long from, long to, const wxString& value) @@ -71,9 +249,14 @@ void wxTextEntryBase::Replace(long from, long to, const wxString& value) Remove(from, to); } + SetInsertionPoint(from); WriteText(value); } +// ---------------------------------------------------------------------------- +// selection +// ---------------------------------------------------------------------------- + bool wxTextEntryBase::HasSelection() const { long from, to; @@ -98,6 +281,10 @@ wxString wxTextEntryBase::GetStringSelection() const return GetRange(from, to); } +// ---------------------------------------------------------------------------- +// clipboard +// ---------------------------------------------------------------------------- + bool wxTextEntryBase::CanCopy() const { return HasSelection(); @@ -128,4 +315,103 @@ bool wxTextEntryBase::CanPaste() const return false; } +// ---------------------------------------------------------------------------- +// hints support +// ---------------------------------------------------------------------------- + +bool wxTextEntryBase::SetHint(const wxString& hint) +{ + if ( !hint.empty() ) + { + if ( !m_hintData ) + m_hintData = new wxTextEntryHintData(this, GetEditableWindow()); + + m_hintData->SetHintString(hint); + } + else if ( m_hintData ) + { + // Setting empty hint removes any currently set one. + delete m_hintData; + m_hintData = NULL; + } + //else: Setting empty hint when we don't have any doesn't do anything. + + return true; +} + +wxString wxTextEntryBase::GetHint() const +{ + return m_hintData ? m_hintData->GetHintString() : wxString(); +} + +// ---------------------------------------------------------------------------- +// margins support +// ---------------------------------------------------------------------------- + +bool wxTextEntryBase::DoSetMargins(const wxPoint& WXUNUSED(pt)) +{ + return false; +} + +wxPoint wxTextEntryBase::DoGetMargins() const +{ + return wxPoint(-1, -1); +} + +// ---------------------------------------------------------------------------- +// events +// ---------------------------------------------------------------------------- + +/* static */ +bool wxTextEntryBase::SendTextUpdatedEvent(wxWindow *win) +{ + wxCHECK_MSG( win, false, "can't send an event without a window" ); + + wxCommandEvent event(wxEVT_TEXT, win->GetId()); + + // do not do this as it could be very inefficient if the text control + // contains a lot of text and we're not using ref-counted wxString + // implementation -- instead, event.GetString() will query the control for + // its current text if needed + //event.SetString(win->GetValue()); + + event.SetEventObject(win); + return win->HandleWindowEvent(event); +} + +// ---------------------------------------------------------------------------- +// auto-completion stubs +// ---------------------------------------------------------------------------- + +wxTextCompleter::~wxTextCompleter() +{ +} + +bool wxTextCompleterSimple::Start(const wxString& prefix) +{ + m_index = 0; + m_completions.clear(); + GetCompletions(prefix, m_completions); + + return !m_completions.empty(); +} + +wxString wxTextCompleterSimple::GetNext() +{ + if ( m_index == m_completions.size() ) + return wxString(); + + return m_completions[m_index++]; +} + +bool wxTextEntryBase::DoAutoCompleteCustom(wxTextCompleter *completer) +{ + // We don't do anything here but we still need to delete the completer for + // consistency with the ports that do implement this method and take + // ownership of the pointer. + delete completer; + + return false; +} + #endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX