]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/textentrycmn.cpp
deal with Cocoa as we do with Carbon, see #15008
[wxWidgets.git] / src / common / textentrycmn.cpp
index cb81ea0779d33bd4f3d61077ae475936fbdb5824..0db04b25f8a07ce22cd6e95566b9b338c59c2a98 100644 (file)
@@ -31,6 +31,7 @@
 #endif //WX_PRECOMP
 
 #include "wx/textentry.h"
+#include "wx/textcompleter.h"
 #include "wx/clipbrd.h"
 
 // ----------------------------------------------------------------------------
@@ -42,48 +43,90 @@ class WXDLLIMPEXP_CORE wxTextEntryHintData wxBIND_OR_CONNECT_HACK_ONLY_BASE_CLAS
 public:
     wxTextEntryHintData(wxTextEntryBase *entry, wxWindow *win)
         : m_entry(entry),
-          m_win(win)
+          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);
-
-        // we don't have any hint yet
-        m_showsHint = false;
+        wxBIND_OR_CONNECT_HACK(win, wxEVT_TEXT,
+                                wxCommandEventHandler,
+                                wxTextEntryHintData::OnTextChanged, this);
     }
 
     // default dtor is ok
 
-    // are we showing the hint right now?
-    bool ShowsHint() const { return m_showsHint; }
+    // 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_showsHint )
-        {
-            // update it immediately
-            m_entry->ChangeValue(hint);
-        }
-        //else: the new hint will be shown later
+        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:
-    void OnSetFocus(wxFocusEvent& event)
+    // 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()
     {
-        // hide the hint if we were showing it
-        if ( m_showsHint )
+        if ( m_colFg.IsOk() )
         {
-            // Clear() would send an event which we don't want, so do it like
-            // this
-            m_entry->ChangeValue(wxString());
             m_win->SetForegroundColour(m_colFg);
+            m_colFg = wxColour();
+        }
+    }
 
-            m_showsHint = false;
+    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();
@@ -91,20 +134,26 @@ private:
 
     void OnKillFocus(wxFocusEvent& event)
     {
-        // restore the hint if the user didn't do anything in the control
-        if ( m_entry->IsEmpty() )
-        {
-            m_entry->ChangeValue(m_hint);
+        // Restore the hint if the user didn't enter anything.
+        ShowHintIfAppropriate();
 
-            m_colFg = m_win->GetForegroundColour();
-            m_win->SetForegroundColour(*wxLIGHT_GREY);
+        event.Skip();
+    }
 
-            m_showsHint = true;
-        }
+    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;
@@ -112,12 +161,12 @@ private:
     // the original foreground colour of m_win before we changed it
     wxColour m_colFg;
 
-    // the hint passed to wxTextEntry::SetHint()
+    // The hint passed to wxTextEntry::SetHint(), never empty.
     wxString m_hint;
 
-    // true if we're currently showing it, for this we must be empty and not
-    // have focus
-    bool m_showsHint;
+    // The real text of the window.
+    wxString m_text;
+
 
     wxDECLARE_NO_COPY_CLASS(wxTextEntryHintData);
 };
@@ -137,7 +186,7 @@ wxTextEntryBase::~wxTextEntryBase()
 
 wxString wxTextEntryBase::GetValue() const
 {
-    return m_hintData && m_hintData->ShowsHint() ? wxString() : DoGetValue();
+    return m_hintData ? m_hintData->GetText() : DoGetValue();
 }
 
 wxString wxTextEntryBase::GetRange(long from, long to) const
@@ -157,6 +206,16 @@ wxString wxTextEntryBase::GetRange(long from, long to) const
 // 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();
@@ -165,12 +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);
+        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)
@@ -252,10 +321,20 @@ bool wxTextEntryBase::CanPaste() const
 
 bool wxTextEntryBase::SetHint(const wxString& hint)
 {
-    if ( !m_hintData )
-        m_hintData = new wxTextEntryHintData(this, GetEditableWindow());
+    if ( !hint.empty() )
+    {
+        if ( !m_hintData )
+            m_hintData = new wxTextEntryHintData(this, GetEditableWindow());
 
-    m_hintData->SetHintString(hint);
+        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;
 }
@@ -265,4 +344,74 @@ 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