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