2008-04-30 A. Walton <awalton@gnome.org>
[nautilus.git] / libnautilus-private / nautilus-entry.c
blob664ba6e89d91f00c1f940068a4c70ce4271d7b66
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
3 /* NautilusEntry: one-line text editing widget. This consists of bug fixes
4 * and other improvements to GtkEntry, and all the changes could be rolled
5 * into GtkEntry some day.
7 * Copyright (C) 2000 Eazel, Inc.
9 * Author: John Sullivan <sullivan@eazel.com>
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Library General Public
13 * License as published by the Free Software Foundation; either
14 * version 2 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Library General Public License for more details.
21 * You should have received a copy of the GNU Library General Public
22 * License along with this library; if not, write to the
23 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 * Boston, MA 02111-1307, USA.
27 #include <config.h>
28 #include "nautilus-entry.h"
30 #include <string.h>
31 #include "nautilus-global-preferences.h"
32 #include "nautilus-undo-signal-handlers.h"
33 #include <eel/eel-gdk-extensions.h>
34 #include <eel/eel-gtk-macros.h>
35 #include <gdk/gdkkeysyms.h>
36 #include <gtk/gtkmain.h>
37 #include <gtk/gtksignal.h>
38 #include <glib/gi18n.h>
40 struct NautilusEntryDetails {
41 gboolean user_edit;
42 gboolean special_tab_handling;
44 guint select_idle_id;
47 enum {
48 USER_CHANGED,
49 SELECTION_CHANGED,
50 LAST_SIGNAL
52 static guint signals[LAST_SIGNAL];
54 static void nautilus_entry_init (NautilusEntry *entry);
55 static void nautilus_entry_class_init (NautilusEntryClass *class);
57 static GObjectClass *parent_class = NULL;
58 static GtkEditableClass *parent_editable_interface = NULL;
60 static void
61 nautilus_entry_init (NautilusEntry *entry)
63 GtkWidget *widget;
65 widget = GTK_WIDGET (entry);
66 entry->details = g_new0 (NautilusEntryDetails, 1);
68 entry->details->user_edit = TRUE;
70 nautilus_undo_set_up_nautilus_entry_for_undo (entry);
73 GtkWidget *
74 nautilus_entry_new (void)
76 return gtk_widget_new (NAUTILUS_TYPE_ENTRY, NULL);
79 GtkWidget *
80 nautilus_entry_new_with_max_length (guint16 max)
82 GtkWidget *widget;
84 widget = gtk_widget_new (NAUTILUS_TYPE_ENTRY, NULL);
85 GTK_ENTRY (widget)->text_max_length = max;
87 return widget;
90 static void
91 nautilus_entry_finalize (GObject *object)
93 NautilusEntry *entry;
95 entry = NAUTILUS_ENTRY (object);
97 if (entry->details->select_idle_id != 0) {
98 g_source_remove (entry->details->select_idle_id);
101 g_free (entry->details);
103 EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
106 static gboolean
107 nautilus_entry_key_press (GtkWidget *widget, GdkEventKey *event)
109 NautilusEntry *entry;
110 GtkEditable *editable;
111 int position;
112 gboolean old_has, new_has;
113 gboolean result;
115 entry = NAUTILUS_ENTRY (widget);
116 editable = GTK_EDITABLE (widget);
118 if (!gtk_editable_get_editable (editable)) {
119 return FALSE;
122 switch (event->keyval) {
123 case GDK_Tab:
124 /* The location bar entry wants TAB to work kind of
125 * like it does in the shell for command completion,
126 * so if we get a tab and there's a selection, we
127 * should position the insertion point at the end of
128 * the selection.
130 if (entry->details->special_tab_handling && gtk_editable_get_selection_bounds (editable, NULL, NULL)) {
131 position = strlen (gtk_entry_get_text (GTK_ENTRY (editable)));
132 gtk_editable_select_region (editable, position, position);
133 return TRUE;
135 break;
137 default:
138 break;
141 old_has = gtk_editable_get_selection_bounds (editable, NULL, NULL);
143 result = EEL_CALL_PARENT_WITH_RETURN_VALUE
144 (GTK_WIDGET_CLASS, key_press_event, (widget, event));
146 /* Pressing a key usually changes the selection if there is a selection.
147 * If there is not selection, we can save work by not emitting a signal.
149 if (result) {
150 new_has = gtk_editable_get_selection_bounds (editable, NULL, NULL);
151 if (old_has || new_has) {
152 g_signal_emit (widget, signals[SELECTION_CHANGED], 0);
156 return result;
160 static gboolean
161 nautilus_entry_motion_notify (GtkWidget *widget, GdkEventMotion *event)
163 int result;
164 gboolean old_had, new_had;
165 int old_start, old_end, new_start, new_end;
166 NautilusEntry *entry;
167 GtkEditable *editable;
169 entry = NAUTILUS_ENTRY (widget);
170 editable = GTK_EDITABLE (widget);
172 old_had = gtk_editable_get_selection_bounds (editable, &old_start, &old_end);
174 result = EEL_CALL_PARENT_WITH_RETURN_VALUE
175 (GTK_WIDGET_CLASS, motion_notify_event, (widget, event));
177 /* Send a signal if dragging the mouse caused the selection to change. */
178 if (result) {
179 new_had = gtk_editable_get_selection_bounds (editable, &new_start, &new_end);
180 if (old_had != new_had || (old_had && (old_start != new_start || old_end != new_end))) {
181 g_signal_emit (widget, signals[SELECTION_CHANGED], 0);
185 return result;
189 * nautilus_entry_select_all
191 * Select all text, leaving the text cursor position at the end.
193 * @entry: A NautilusEntry
195 void
196 nautilus_entry_select_all (NautilusEntry *entry)
198 g_return_if_fail (NAUTILUS_IS_ENTRY (entry));
200 gtk_editable_set_position (GTK_EDITABLE (entry), -1);
201 gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
204 static gboolean
205 select_all_at_idle (gpointer callback_data)
207 NautilusEntry *entry;
209 entry = NAUTILUS_ENTRY (callback_data);
211 nautilus_entry_select_all (entry);
213 entry->details->select_idle_id = 0;
215 return FALSE;
219 * nautilus_entry_select_all_at_idle
221 * Select all text at the next idle, not immediately.
222 * This is useful when reacting to a key press, because
223 * changing the selection and the text cursor position doesn't
224 * work in a key_press signal handler.
226 * @entry: A NautilusEntry
228 void
229 nautilus_entry_select_all_at_idle (NautilusEntry *entry)
231 g_return_if_fail (NAUTILUS_IS_ENTRY (entry));
233 /* If the text cursor position changes in this routine
234 * then gtk_entry_key_press will unselect (and we want
235 * to move the text cursor position to the end).
238 if (entry->details->select_idle_id == 0) {
239 entry->details->select_idle_id = g_idle_add (select_all_at_idle, entry);
244 * nautilus_entry_set_text
246 * This function wraps gtk_entry_set_text. It sets undo_registered
247 * to TRUE and preserves the old value for a later restore. This is
248 * done so the programmatic changes to the entry do not register
249 * with the undo manager.
251 * @entry: A NautilusEntry
252 * @test: The text to set
255 void
256 nautilus_entry_set_text (NautilusEntry *entry, const gchar *text)
258 g_return_if_fail (NAUTILUS_IS_ENTRY (entry));
260 entry->details->user_edit = FALSE;
261 gtk_entry_set_text (GTK_ENTRY (entry), text);
262 entry->details->user_edit = TRUE;
264 g_signal_emit (entry, signals[SELECTION_CHANGED], 0);
267 static void
268 nautilus_entry_set_selection_bounds (GtkEditable *editable,
269 int start_pos,
270 int end_pos)
272 parent_editable_interface->set_selection_bounds (editable, start_pos, end_pos);
274 g_signal_emit (editable, signals[SELECTION_CHANGED], 0);
277 static gboolean
278 nautilus_entry_button_press (GtkWidget *widget,
279 GdkEventButton *event)
281 gboolean result;
283 result = EEL_CALL_PARENT_WITH_RETURN_VALUE
284 (GTK_WIDGET_CLASS, button_press_event, (widget, event));
286 if (result) {
287 g_signal_emit (widget, signals[SELECTION_CHANGED], 0);
290 return result;
293 static gboolean
294 nautilus_entry_button_release (GtkWidget *widget,
295 GdkEventButton *event)
297 gboolean result;
299 result = EEL_CALL_PARENT_WITH_RETURN_VALUE
300 (GTK_WIDGET_CLASS, button_release_event, (widget, event));
302 if (result) {
303 g_signal_emit (widget, signals[SELECTION_CHANGED], 0);
306 return result;
309 static void
310 nautilus_entry_insert_text (GtkEditable *editable, const gchar *text,
311 int length, int *position)
313 NautilusEntry *entry;
315 entry = NAUTILUS_ENTRY(editable);
317 /* Fire off user changed signals */
318 if (entry->details->user_edit) {
319 g_signal_emit (editable, signals[USER_CHANGED], 0);
322 parent_editable_interface->insert_text (editable, text, length, position);
324 g_signal_emit (editable, signals[SELECTION_CHANGED], 0);
327 static void
328 nautilus_entry_delete_text (GtkEditable *editable, int start_pos, int end_pos)
330 NautilusEntry *entry;
332 entry = NAUTILUS_ENTRY (editable);
334 /* Fire off user changed signals */
335 if (entry->details->user_edit) {
336 g_signal_emit (editable, signals[USER_CHANGED], 0);
339 parent_editable_interface->delete_text (editable, start_pos, end_pos);
341 g_signal_emit (editable, signals[SELECTION_CHANGED], 0);
344 /* Overridden to work around GTK bug. The selection_clear_event is queued
345 * when the selection changes. Changing the selection to NULL and then
346 * back to the original selection owner still sends the event, so the
347 * selection owner then gets the selection ripped away from it. We ran into
348 * this with type-completion behavior in NautilusLocationBar (see bug 5313).
349 * There's a FIXME comment that seems to be about this same issue in
350 * gtk+/gtkselection.c, gtk_selection_clear.
352 static gboolean
353 nautilus_entry_selection_clear (GtkWidget *widget,
354 GdkEventSelection *event)
356 g_assert (NAUTILUS_IS_ENTRY (widget));
358 if (gdk_selection_owner_get (event->selection) == widget->window) {
359 return FALSE;
362 return EEL_CALL_PARENT_WITH_RETURN_VALUE
363 (GTK_WIDGET_CLASS, selection_clear_event, (widget, event));
366 static void
367 nautilus_entry_editable_init (GtkEditableClass *iface)
369 parent_editable_interface = g_type_interface_peek_parent (iface);
371 iface->insert_text = nautilus_entry_insert_text;
372 iface->delete_text = nautilus_entry_delete_text;
373 iface->set_selection_bounds = nautilus_entry_set_selection_bounds;
375 /* Otherwise we might need some memcpy loving */
376 g_assert (iface->do_insert_text != NULL);
377 g_assert (iface->get_position != NULL);
378 g_assert (iface->get_chars != NULL);
381 static void
382 nautilus_entry_class_init (NautilusEntryClass *class)
384 GtkWidgetClass *widget_class;
385 GtkObjectClass *object_class;
386 GObjectClass *gobject_class;
388 parent_class = g_type_class_peek_parent (class);
390 widget_class = GTK_WIDGET_CLASS (class);
391 gobject_class = G_OBJECT_CLASS (class);
392 object_class = GTK_OBJECT_CLASS (class);
394 widget_class->button_press_event = nautilus_entry_button_press;
395 widget_class->button_release_event = nautilus_entry_button_release;
396 widget_class->key_press_event = nautilus_entry_key_press;
397 widget_class->motion_notify_event = nautilus_entry_motion_notify;
398 widget_class->selection_clear_event = nautilus_entry_selection_clear;
400 gobject_class->finalize = nautilus_entry_finalize;
402 /* Set up signals */
403 signals[USER_CHANGED] = g_signal_new
404 ("user_changed",
405 G_TYPE_FROM_CLASS (object_class),
406 G_SIGNAL_RUN_LAST,
407 G_STRUCT_OFFSET (NautilusEntryClass,
408 user_changed),
409 NULL, NULL,
410 g_cclosure_marshal_VOID__VOID,
411 G_TYPE_NONE, 0);
412 signals[SELECTION_CHANGED] = g_signal_new
413 ("selection_changed",
414 G_TYPE_FROM_CLASS (object_class),
415 G_SIGNAL_RUN_LAST,
416 G_STRUCT_OFFSET (NautilusEntryClass,
417 selection_changed),
418 NULL, NULL,
419 g_cclosure_marshal_VOID__VOID,
420 G_TYPE_NONE, 0);
423 void
424 nautilus_entry_set_special_tab_handling (NautilusEntry *entry,
425 gboolean special_tab_handling)
427 g_return_if_fail (NAUTILUS_IS_ENTRY (entry));
429 entry->details->special_tab_handling = special_tab_handling;
433 GType
434 nautilus_entry_get_type (void)
436 static GType entry_type = 0;
438 if (entry_type == 0) {
439 const GInterfaceInfo editable_info =
441 (GInterfaceInitFunc) nautilus_entry_editable_init,
442 NULL,
443 NULL
446 const GTypeInfo object_info = {
447 sizeof (NautilusEntryClass),
448 NULL, /* base_init */
449 NULL, /* base_finalize */
450 (GClassInitFunc) nautilus_entry_class_init,
451 NULL, /* class_finalize */
452 NULL, /* class_data */
453 sizeof (NautilusEntry),
454 0, /* n_preallocs */
455 (GInstanceInitFunc) nautilus_entry_init
457 entry_type = g_type_register_static (
458 GTK_TYPE_ENTRY, "NautilusEntry",
459 &object_info, 0);
460 g_type_add_interface_static (
461 entry_type, GTK_TYPE_EDITABLE, &editable_info);
464 return entry_type;