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