| 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 |