don't call GetLastPosition() unnecessarily in SelectAll(), all platforms should suppo...
[wxWidgets.git] / src / gtk / textentry.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/textentry.cpp
3 // Purpose: wxTextEntry implementation for wxGTK
4 // Author: Vadim Zeitlin
5 // Created: 2007-09-24
6 // RCS-ID: $Id$
7 // Copyright: (c) 2007 Vadim Zeitlin <vadim@wxwindows.org>
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // for compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #if wxUSE_TEXTCTRL || wxUSE_COMBOBOX
27
28 #ifndef WX_PRECOMP
29 #include "wx/window.h"
30 #include "wx/textctrl.h"
31 #endif //WX_PRECOMP
32
33 #include "wx/textentry.h"
34
35 #include "wx/gtk/private.h"
36
37 // ============================================================================
38 // signal handlers implementation
39 // ============================================================================
40
41 extern "C"
42 {
43
44 // "insert_text" handler for GtkEntry
45 static void
46 wx_gtk_insert_text_callback(GtkEditable *editable,
47 const gchar * WXUNUSED(new_text),
48 gint WXUNUSED(new_text_length),
49 gint * WXUNUSED(position),
50 wxTextEntry *text)
51 {
52 // we should only be called if we have a max len limit at all
53 GtkEntry *entry = GTK_ENTRY (editable);
54
55 wxCHECK_RET( entry->text_max_length, _T("shouldn't be called") );
56
57 // check that we don't overflow the max length limit
58 //
59 // FIXME: this doesn't work when we paste a string which is going to be
60 // truncated
61 if ( entry->text_length == entry->text_max_length )
62 {
63 // we don't need to run the base class version at all
64 g_signal_stop_emission_by_name (editable, "insert_text");
65
66 text->SendMaxLenEvent();
67 }
68 }
69
70 } // extern "C"
71
72 // ============================================================================
73 // wxTextEntry implementation
74 // ============================================================================
75
76 // ----------------------------------------------------------------------------
77 // text operations
78 // ----------------------------------------------------------------------------
79
80 void wxTextEntry::WriteText(const wxString& value)
81 {
82 GtkEditable * const edit = GetEditable();
83
84 // remove the selection if there is one and suppress the text change event
85 // generated by this: we only want to generate one event for this change,
86 // not two
87 {
88 EventsSuppressor noevents(this);
89 gtk_editable_delete_selection(edit);
90 }
91
92 // insert new text at the cursor position
93 gint len = gtk_editable_get_position(edit);
94 gtk_editable_insert_text
95 (
96 edit,
97 wxGTK_CONV_FONT(value, GetEditableWindow()->GetFont()),
98 -1, // text: length: compute it using strlen()
99 &len // will be updated to position after the text end
100 );
101
102 // and move cursor to the end of new text
103 gtk_editable_set_position(edit, len);
104 }
105
106 wxString wxTextEntry::GetValue() const
107 {
108 const wxGtkString value(gtk_editable_get_chars(GetEditable(), 0, -1));
109
110 return wxGTK_CONV_BACK_FONT(value, GetEditableWindow()->GetFont());
111 }
112
113 void wxTextEntry::Remove(long from, long to)
114 {
115 gtk_editable_delete_text(GetEditable(), from, to);
116 }
117
118 // ----------------------------------------------------------------------------
119 // clipboard operations
120 // ----------------------------------------------------------------------------
121
122 void wxTextEntry::Copy()
123 {
124 gtk_editable_copy_clipboard(GetEditable());
125 }
126
127 void wxTextEntry::Cut()
128 {
129 gtk_editable_cut_clipboard(GetEditable());
130 }
131
132 void wxTextEntry::Paste()
133 {
134 gtk_editable_paste_clipboard(GetEditable());
135 }
136
137 // ----------------------------------------------------------------------------
138 // undo/redo
139 // ----------------------------------------------------------------------------
140
141 void wxTextEntry::Undo()
142 {
143 // TODO: not implemented
144 }
145
146 void wxTextEntry::Redo()
147 {
148 // TODO: not implemented
149 }
150
151 bool wxTextEntry::CanUndo() const
152 {
153 return false;
154 }
155
156 bool wxTextEntry::CanRedo() const
157 {
158 return false;
159 }
160
161 // ----------------------------------------------------------------------------
162 // insertion point
163 // ----------------------------------------------------------------------------
164
165 void wxTextEntry::SetInsertionPoint(long pos)
166 {
167 gtk_editable_set_position(GetEditable(), pos);
168 }
169
170 long wxTextEntry::GetInsertionPoint() const
171 {
172 return gtk_editable_get_position(GetEditable());
173 }
174
175 long wxTextEntry::GetLastPosition() const
176 {
177 // this can't be implemented for arbitrary GtkEditable so only do it for
178 // GtkEntries
179 GtkEntry * const entry = GTK_ENTRY(GetEditable());
180
181 return entry ? entry->text_length : - 1;
182 }
183
184 // ----------------------------------------------------------------------------
185 // selection
186 // ----------------------------------------------------------------------------
187
188 void wxTextEntry::SetSelection(long from, long to)
189 {
190 // in wx convention, (-1, -1) means the entire range but GTK+ translates -1
191 // (or any negative number for that matter) into last position so we need
192 // to translate manually
193 if ( from == -1 && to == -1 )
194 from = 0;
195
196 gtk_editable_select_region(GetEditable(), from, to);
197 }
198
199 void wxTextEntry::GetSelection(long *from, long *to) const
200 {
201 gint start, end;
202 if ( gtk_editable_get_selection_bounds(GetEditable(), &start, &end) )
203 {
204 // the output must always be in order, although in GTK+ it isn't
205 if ( start > end )
206 {
207 gint tmp = start;
208 start = end;
209 end = tmp;
210 }
211 }
212 else // no selection
213 {
214 // for compatibility with MSW return the empty selection at cursor
215 start =
216 end = GetInsertionPoint();
217 }
218
219 if ( from )
220 *from = start;
221
222 if ( to )
223 *to = end;
224 }
225
226 // ----------------------------------------------------------------------------
227 // auto completion
228 // ----------------------------------------------------------------------------
229
230 bool wxTextEntry::AutoComplete(const wxArrayString& choices)
231 {
232 GtkEntry * const entry = GTK_ENTRY(GetEditable());
233 wxCHECK_MSG(entry, false, "auto completion doesn't work with this control");
234
235 GtkListStore * const store = gtk_list_store_new(1, G_TYPE_STRING);
236 GtkTreeIter iter;
237
238 for ( wxArrayString::const_iterator i = choices.begin();
239 i != choices.end();
240 ++i )
241 {
242 gtk_list_store_append(store, &iter);
243 gtk_list_store_set(store, &iter,
244 0, (const gchar *)i->utf8_str(),
245 -1);
246 }
247
248 GtkEntryCompletion * const completion = gtk_entry_completion_new();
249 gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(store));
250 gtk_entry_completion_set_text_column(completion, 0);
251 gtk_entry_set_completion(entry, completion);
252 g_object_unref(completion);
253 return true;
254 }
255
256 // ----------------------------------------------------------------------------
257 // editable status
258 // ----------------------------------------------------------------------------
259
260 bool wxTextEntry::IsEditable() const
261 {
262 return gtk_editable_get_editable(GetEditable());
263 }
264
265 void wxTextEntry::SetEditable(bool editable)
266 {
267 gtk_editable_set_editable(GetEditable(), editable);
268 }
269
270 // ----------------------------------------------------------------------------
271 // max text length
272 // ----------------------------------------------------------------------------
273
274 void wxTextEntry::SetMaxLength(unsigned long len)
275 {
276 GtkEntry * const entry = GTK_ENTRY(GetEditable());
277 if ( !entry )
278 return;
279
280 gtk_entry_set_max_length(entry, len);
281
282 // there is a bug in GTK+ 1.2.x: "changed" signal is emitted even if we had
283 // tried to enter more text than allowed by max text length and the text
284 // wasn't really changed
285 //
286 // to detect this and generate TEXT_MAXLEN event instead of TEXT_CHANGED
287 // one in this case we also catch "insert_text" signal
288 //
289 // when max len is set to 0 we disconnect our handler as it means that we
290 // shouldn't check anything any more
291 if ( len )
292 {
293 g_signal_connect
294 (
295 entry,
296 "insert_text",
297 G_CALLBACK(wx_gtk_insert_text_callback),
298 this
299 );
300 }
301 else // no max length
302 {
303 g_signal_handlers_disconnect_by_func
304 (
305 entry,
306 (gpointer)wx_gtk_insert_text_callback,
307 this
308 );
309 }
310 }
311
312 void wxTextEntry::SendMaxLenEvent()
313 {
314 // remember that the next changed signal is to be ignored to avoid
315 // generating a dummy wxEVT_COMMAND_TEXT_UPDATED event
316 //IgnoreNextTextUpdate();
317
318 wxWindow * const win = const_cast<wxWindow *>(GetEditableWindow());
319 wxCommandEvent event(wxEVT_COMMAND_TEXT_MAXLEN, win->GetId());
320 event.SetEventObject(win);
321 event.SetString(GetValue());
322 win->HandleWindowEvent(event);
323 }
324
325 #endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX