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.
28 #include "nautilus-entry.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
{
42 gboolean special_tab_handling
;
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
;
61 nautilus_entry_init (NautilusEntry
*entry
)
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
);
74 nautilus_entry_new (void)
76 return gtk_widget_new (NAUTILUS_TYPE_ENTRY
, NULL
);
80 nautilus_entry_new_with_max_length (guint16 max
)
84 widget
= gtk_widget_new (NAUTILUS_TYPE_ENTRY
, NULL
);
85 GTK_ENTRY (widget
)->text_max_length
= max
;
91 nautilus_entry_finalize (GObject
*object
)
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
));
107 nautilus_entry_key_press (GtkWidget
*widget
, GdkEventKey
*event
)
109 NautilusEntry
*entry
;
110 GtkEditable
*editable
;
112 gboolean old_has
, new_has
;
115 entry
= NAUTILUS_ENTRY (widget
);
116 editable
= GTK_EDITABLE (widget
);
118 if (!gtk_editable_get_editable (editable
)) {
122 switch (event
->keyval
) {
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
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
);
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.
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);
161 nautilus_entry_motion_notify (GtkWidget
*widget
, GdkEventMotion
*event
)
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. */
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);
189 * nautilus_entry_select_all
191 * Select all text, leaving the text cursor position at the end.
193 * @entry: A NautilusEntry
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);
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;
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
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
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);
268 nautilus_entry_set_selection_bounds (GtkEditable
*editable
,
272 parent_editable_interface
->set_selection_bounds (editable
, start_pos
, end_pos
);
274 g_signal_emit (editable
, signals
[SELECTION_CHANGED
], 0);
278 nautilus_entry_button_press (GtkWidget
*widget
,
279 GdkEventButton
*event
)
283 result
= EEL_CALL_PARENT_WITH_RETURN_VALUE
284 (GTK_WIDGET_CLASS
, button_press_event
, (widget
, event
));
287 g_signal_emit (widget
, signals
[SELECTION_CHANGED
], 0);
294 nautilus_entry_button_release (GtkWidget
*widget
,
295 GdkEventButton
*event
)
299 result
= EEL_CALL_PARENT_WITH_RETURN_VALUE
300 (GTK_WIDGET_CLASS
, button_release_event
, (widget
, event
));
303 g_signal_emit (widget
, signals
[SELECTION_CHANGED
], 0);
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);
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.
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
) {
362 return EEL_CALL_PARENT_WITH_RETURN_VALUE
363 (GTK_WIDGET_CLASS
, selection_clear_event
, (widget
, event
));
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
);
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
;
403 signals
[USER_CHANGED
] = g_signal_new
405 G_TYPE_FROM_CLASS (object_class
),
407 G_STRUCT_OFFSET (NautilusEntryClass
,
410 g_cclosure_marshal_VOID__VOID
,
412 signals
[SELECTION_CHANGED
] = g_signal_new
413 ("selection_changed",
414 G_TYPE_FROM_CLASS (object_class
),
416 G_STRUCT_OFFSET (NautilusEntryClass
,
419 g_cclosure_marshal_VOID__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
;
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
,
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
),
455 (GInstanceInitFunc
) nautilus_entry_init
457 entry_type
= g_type_register_static (
458 GTK_TYPE_ENTRY
, "NautilusEntry",
460 g_type_add_interface_static (
461 entry_type
, GTK_TYPE_EDITABLE
, &editable_info
);