1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        src/common/textentrycmn.cpp
3 // Purpose:     wxTextEntryBase implementation
4 // Author:      Vadim Zeitlin
5 // Created:     2007-09-26
6 // Copyright:   (c) 2007 Vadim Zeitlin <vadim@wxwindows.org>
7 // Licence:     wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
9 
10 // ============================================================================
11 // declarations
12 // ============================================================================
13 
14 // ----------------------------------------------------------------------------
15 // headers
16 // ----------------------------------------------------------------------------
17 
18 // for compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
20 
21 #ifdef __BORLANDC__
22     #pragma hdrstop
23 #endif
24 
25 #if wxUSE_TEXTCTRL || wxUSE_COMBOBOX
26 
27 #ifndef WX_PRECOMP
28     #include "wx/window.h"
29     #include "wx/dataobj.h"
30 #endif //WX_PRECOMP
31 
32 #include "wx/textentry.h"
33 #include "wx/textcompleter.h"
34 #include "wx/clipbrd.h"
35 
36 // ----------------------------------------------------------------------------
37 // wxTextEntryHintData
38 // ----------------------------------------------------------------------------
39 
40 class WXDLLIMPEXP_CORE wxTextEntryHintData wxBIND_OR_CONNECT_HACK_ONLY_BASE_CLASS
41 {
42 public:
wxTextEntryHintData(wxTextEntryBase * entry,wxWindow * win)43     wxTextEntryHintData(wxTextEntryBase *entry, wxWindow *win)
44         : m_entry(entry),
45           m_win(win),
46           m_text(m_entry->GetValue())
47     {
48         wxBIND_OR_CONNECT_HACK(win, wxEVT_SET_FOCUS, wxFocusEventHandler,
49                                 wxTextEntryHintData::OnSetFocus, this);
50         wxBIND_OR_CONNECT_HACK(win, wxEVT_KILL_FOCUS, wxFocusEventHandler,
51                                 wxTextEntryHintData::OnKillFocus, this);
52         wxBIND_OR_CONNECT_HACK(win, wxEVT_TEXT,
53                                 wxCommandEventHandler,
54                                 wxTextEntryHintData::OnTextChanged, this);
55     }
56 
57 #ifdef wxHAS_EVENT_BIND
~wxTextEntryHintData()58     ~wxTextEntryHintData()
59     {
60         m_win->Unbind(wxEVT_SET_FOCUS, &wxTextEntryHintData::OnSetFocus, this);
61         m_win->Unbind(wxEVT_KILL_FOCUS, &wxTextEntryHintData::OnKillFocus, this);
62         m_win->Unbind(wxEVT_TEXT, &wxTextEntryHintData::OnTextChanged, this);
63     }
64 #endif // wxHAS_EVENT_BIND
65 
66     // Get the real text of the control such as it was before we replaced it
67     // with the hint.
GetText() const68     const wxString& GetText() const { return m_text; }
69 
70     // Set the hint to show, shouldn't be empty normally.
71     //
72     // This should be called after creating a new wxTextEntryHintData object
73     // and may be called more times in the future.
SetHintString(const wxString & hint)74     void SetHintString(const wxString& hint)
75     {
76         m_hint = hint;
77 
78         if ( !m_win->HasFocus() )
79             ShowHintIfAppropriate();
80         //else: The new hint will be shown later when we lose focus.
81     }
82 
GetHintString() const83     const wxString& GetHintString() const { return m_hint; }
84 
85     // This is called whenever the text control contents changes.
86     //
87     // We call it ourselves when this change generates an event but it's also
88     // necessary to call it explicitly from wxTextEntry::ChangeValue() as it,
89     // by design, does not generate any events.
HandleTextUpdate(const wxString & text)90     void HandleTextUpdate(const wxString& text)
91     {
92         m_text = text;
93 
94         // If we're called because of a call to Set or ChangeValue(), the
95         // control may still have the hint text colour, reset it in this case.
96         RestoreTextColourIfNecessary();
97     }
98 
99 private:
100     // Show the hint in the window if we should do it, i.e. if the window
101     // doesn't have any text of its own.
ShowHintIfAppropriate()102     void ShowHintIfAppropriate()
103     {
104         // Never overwrite existing window text.
105         if ( !m_text.empty() )
106             return;
107 
108         // Save the old text colour and set a more inconspicuous one for the
109         // hint.
110         if (!m_colFg.IsOk())
111         {
112             m_colFg = m_win->GetForegroundColour();
113             m_win->SetForegroundColour(*wxLIGHT_GREY);
114         }
115 
116         m_entry->DoSetValue(m_hint, wxTextEntryBase::SetValue_NoEvent);
117     }
118 
119     // Restore the original text colour if we had changed it to show the hint
120     // and not restored it yet.
RestoreTextColourIfNecessary()121     void RestoreTextColourIfNecessary()
122     {
123         if ( m_colFg.IsOk() )
124         {
125             m_win->SetForegroundColour(m_colFg);
126             m_colFg = wxColour();
127         }
128     }
129 
OnSetFocus(wxFocusEvent & event)130     void OnSetFocus(wxFocusEvent& event)
131     {
132         // If we had been showing the hint before, remove it now and restore
133         // the normal colour.
134         if ( m_text.empty() )
135         {
136             RestoreTextColourIfNecessary();
137 
138             m_entry->DoSetValue(wxString(), wxTextEntryBase::SetValue_NoEvent);
139         }
140 
141         event.Skip();
142     }
143 
OnKillFocus(wxFocusEvent & event)144     void OnKillFocus(wxFocusEvent& event)
145     {
146         // Restore the hint if the user didn't enter anything.
147         ShowHintIfAppropriate();
148 
149         event.Skip();
150     }
151 
OnTextChanged(wxCommandEvent & event)152     void OnTextChanged(wxCommandEvent& event)
153     {
154         // Update the stored window text.
155         //
156         // Notice that we can't use GetValue() nor wxCommandEvent::GetString()
157         // which uses it internally because this would just forward back to us
158         // so go directly to the private method which returns the real control
159         // contents.
160         HandleTextUpdate(m_entry->DoGetValue());
161 
162         event.Skip();
163     }
164 
165 
166     // the text control we're associated with (as its interface and its window)
167     wxTextEntryBase * const m_entry;
168     wxWindow * const m_win;
169 
170     // the original foreground colour of m_win before we changed it
171     wxColour m_colFg;
172 
173     // The hint passed to wxTextEntry::SetHint(), never empty.
174     wxString m_hint;
175 
176     // The real text of the window.
177     wxString m_text;
178 
179 
180     wxDECLARE_NO_COPY_CLASS(wxTextEntryHintData);
181 };
182 
183 // ============================================================================
184 // wxTextEntryBase implementation
185 // ============================================================================
186 
~wxTextEntryBase()187 wxTextEntryBase::~wxTextEntryBase()
188 {
189     delete m_hintData;
190 }
191 
192 // ----------------------------------------------------------------------------
193 // text accessors
194 // ----------------------------------------------------------------------------
195 
GetValue() const196 wxString wxTextEntryBase::GetValue() const
197 {
198     return m_hintData ? m_hintData->GetText() : DoGetValue();
199 }
200 
GetRange(long from,long to) const201 wxString wxTextEntryBase::GetRange(long from, long to) const
202 {
203     wxString sel;
204     wxString value = GetValue();
205 
206     if ( from < to && (long)value.length() >= to )
207     {
208         sel = value.substr(from, to - from);
209     }
210 
211     return sel;
212 }
213 
214 // ----------------------------------------------------------------------------
215 // text operations
216 // ----------------------------------------------------------------------------
217 
ChangeValue(const wxString & value)218 void wxTextEntryBase::ChangeValue(const wxString& value)
219 {
220     DoSetValue(value, SetValue_NoEvent);
221 
222     // As we didn't generate any events for wxTextEntryHintData to catch,
223     // notify it explicitly about our changed contents.
224     if ( m_hintData )
225         m_hintData->HandleTextUpdate(value);
226 }
227 
AppendText(const wxString & text)228 void wxTextEntryBase::AppendText(const wxString& text)
229 {
230     SetInsertionPointEnd();
231     WriteText(text);
232 }
233 
DoSetValue(const wxString & value,int flags)234 void wxTextEntryBase::DoSetValue(const wxString& value, int flags)
235 {
236     if ( value != DoGetValue() )
237     {
238         EventsSuppressor noeventsIf(this, !(flags & SetValue_SendEvent));
239 
240         SelectAll();
241         WriteText(value);
242 
243         SetInsertionPoint(0);
244     }
245     else // Same value, no need to do anything.
246     {
247         // Except that we still need to generate the event for consistency with
248         // the normal case when the text does change.
249         if ( flags & SetValue_SendEvent )
250             SendTextUpdatedEvent(GetEditableWindow());
251     }
252 }
253 
Replace(long from,long to,const wxString & value)254 void wxTextEntryBase::Replace(long from, long to, const wxString& value)
255 {
256     {
257         EventsSuppressor noevents(this);
258         Remove(from, to);
259     }
260 
261     SetInsertionPoint(from);
262     WriteText(value);
263 }
264 
265 // ----------------------------------------------------------------------------
266 // selection
267 // ----------------------------------------------------------------------------
268 
HasSelection() const269 bool wxTextEntryBase::HasSelection() const
270 {
271     long from, to;
272     GetSelection(&from, &to);
273 
274     return from < to;
275 }
276 
RemoveSelection()277 void wxTextEntryBase::RemoveSelection()
278 {
279     long from, to;
280     GetSelection(& from, & to);
281     if (from != -1 && to != -1)
282         Remove(from, to);
283 }
284 
GetStringSelection() const285 wxString wxTextEntryBase::GetStringSelection() const
286 {
287     long from, to;
288     GetSelection(&from, &to);
289 
290     return GetRange(from, to);
291 }
292 
293 // ----------------------------------------------------------------------------
294 // clipboard
295 // ----------------------------------------------------------------------------
296 
CanCopy() const297 bool wxTextEntryBase::CanCopy() const
298 {
299     return HasSelection();
300 }
301 
CanCut() const302 bool wxTextEntryBase::CanCut() const
303 {
304     return CanCopy() && IsEditable();
305 }
306 
CanPaste() const307 bool wxTextEntryBase::CanPaste() const
308 {
309     if ( IsEditable() )
310     {
311 #if wxUSE_CLIPBOARD
312         // check if there is any text on the clipboard
313         if ( wxTheClipboard->IsSupported(wxDF_TEXT)
314 #if wxUSE_UNICODE
315                 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
316 #endif // wxUSE_UNICODE
317            )
318         {
319             return true;
320         }
321 #endif // wxUSE_CLIPBOARD
322     }
323 
324     return false;
325 }
326 
327 // ----------------------------------------------------------------------------
328 // hints support
329 // ----------------------------------------------------------------------------
330 
SetHint(const wxString & hint)331 bool wxTextEntryBase::SetHint(const wxString& hint)
332 {
333     if ( !hint.empty() )
334     {
335         if ( !m_hintData )
336             m_hintData = new wxTextEntryHintData(this, GetEditableWindow());
337 
338         m_hintData->SetHintString(hint);
339     }
340     else if ( m_hintData )
341     {
342         // Setting empty hint removes any currently set one.
343         delete m_hintData;
344         m_hintData = NULL;
345     }
346     //else: Setting empty hint when we don't have any doesn't do anything.
347 
348     return true;
349 }
350 
GetHint() const351 wxString wxTextEntryBase::GetHint() const
352 {
353     return m_hintData ? m_hintData->GetHintString() : wxString();
354 }
355 
356 // ----------------------------------------------------------------------------
357 // margins support
358 // ----------------------------------------------------------------------------
359 
DoSetMargins(const wxPoint & WXUNUSED (pt))360 bool wxTextEntryBase::DoSetMargins(const wxPoint& WXUNUSED(pt))
361 {
362     return false;
363 }
364 
DoGetMargins() const365 wxPoint wxTextEntryBase::DoGetMargins() const
366 {
367     return wxPoint(-1, -1);
368 }
369 
370 // ----------------------------------------------------------------------------
371 // events
372 // ----------------------------------------------------------------------------
373 
374 /* static */
SendTextUpdatedEvent(wxWindow * win)375 bool wxTextEntryBase::SendTextUpdatedEvent(wxWindow *win)
376 {
377     wxCHECK_MSG( win, false, "can't send an event without a window" );
378 
379     wxCommandEvent event(wxEVT_TEXT, win->GetId());
380 
381     // do not do this as it could be very inefficient if the text control
382     // contains a lot of text and we're not using ref-counted wxString
383     // implementation -- instead, event.GetString() will query the control for
384     // its current text if needed
385     //event.SetString(win->GetValue());
386 
387     event.SetEventObject(win);
388     return win->HandleWindowEvent(event);
389 }
390 
391 // ----------------------------------------------------------------------------
392 // auto-completion stubs
393 // ----------------------------------------------------------------------------
394 
~wxTextCompleter()395 wxTextCompleter::~wxTextCompleter()
396 {
397 }
398 
Start(const wxString & prefix)399 bool wxTextCompleterSimple::Start(const wxString& prefix)
400 {
401     m_index = 0;
402     m_completions.clear();
403     GetCompletions(prefix, m_completions);
404 
405     return !m_completions.empty();
406 }
407 
GetNext()408 wxString wxTextCompleterSimple::GetNext()
409 {
410     if ( m_index == m_completions.size() )
411         return wxString();
412 
413     return m_completions[m_index++];
414 }
415 
DoAutoCompleteCustom(wxTextCompleter * completer)416 bool wxTextEntryBase::DoAutoCompleteCustom(wxTextCompleter *completer)
417 {
418     // We don't do anything here but we still need to delete the completer for
419     // consistency with the ports that do implement this method and take
420     // ownership of the pointer.
421     delete completer;
422 
423     return false;
424 }
425 
426 #endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX
427