nautilus-3.6.3/libnautilus-private/nautilus-entry.c

No issues found

  1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
  2 
  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.
  6  *
  7  * Copyright (C) 2000 Eazel, Inc.
  8  *
  9  * Author: John Sullivan <sullivan@eazel.com>
 10  *
 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.
 15  *
 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.
 20  *
 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.
 25  */
 26 
 27 #include <config.h>
 28 #include "nautilus-entry.h"
 29 
 30 #include <string.h>
 31 #include "nautilus-global-preferences.h"
 32 #include <gdk/gdkkeysyms.h>
 33 #include <gtk/gtk.h>
 34 #include <glib/gi18n.h>
 35 
 36 struct NautilusEntryDetails {
 37 	gboolean special_tab_handling;
 38 
 39 	guint select_idle_id;
 40 };
 41 
 42 enum {
 43 	SELECTION_CHANGED,
 44 	LAST_SIGNAL
 45 };
 46 static guint signals[LAST_SIGNAL];
 47 
 48 static void nautilus_entry_editable_init (GtkEditableInterface *iface);
 49 
 50 G_DEFINE_TYPE_WITH_CODE (NautilusEntry, nautilus_entry, GTK_TYPE_ENTRY,
 51 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE,
 52 						nautilus_entry_editable_init));
 53 
 54 static GtkEditableInterface *parent_editable_interface = NULL;
 55 
 56 static void
 57 nautilus_entry_init (NautilusEntry *entry)
 58 {
 59 	entry->details = g_new0 (NautilusEntryDetails, 1);
 60 }
 61 
 62 GtkWidget *
 63 nautilus_entry_new (void)
 64 {
 65 	return gtk_widget_new (NAUTILUS_TYPE_ENTRY, NULL);
 66 }
 67 
 68 GtkWidget *
 69 nautilus_entry_new_with_max_length (guint16 max)
 70 {
 71 	GtkWidget *widget;
 72 
 73 	widget = gtk_widget_new (NAUTILUS_TYPE_ENTRY, NULL);
 74 	gtk_entry_set_max_length (GTK_ENTRY (widget), max);
 75 
 76 	return widget;
 77 }
 78 
 79 static void
 80 nautilus_entry_finalize (GObject *object)
 81 {
 82 	NautilusEntry *entry;
 83 
 84 	entry = NAUTILUS_ENTRY (object);
 85 
 86 	if (entry->details->select_idle_id != 0) {
 87 		g_source_remove (entry->details->select_idle_id);
 88 	}
 89 	
 90 	g_free (entry->details);
 91 
 92 	G_OBJECT_CLASS (nautilus_entry_parent_class)->finalize (object);
 93 }
 94 
 95 static gboolean
 96 nautilus_entry_key_press (GtkWidget *widget, GdkEventKey *event)
 97 {
 98 	NautilusEntry *entry;
 99 	GtkEditable *editable;
100 	int position;
101 	gboolean old_has, new_has;
102 	gboolean result;
103 	
104 	entry = NAUTILUS_ENTRY (widget);
105 	editable = GTK_EDITABLE (widget);
106 	
107 	if (!gtk_editable_get_editable (editable)) {
108 		return FALSE;
109 	}
110 
111 	switch (event->keyval) {
112 	case GDK_KEY_Tab:
113 		/* The location bar entry wants TAB to work kind of
114 		 * like it does in the shell for command completion,
115 		 * so if we get a tab and there's a selection, we
116 		 * should position the insertion point at the end of
117 		 * the selection.
118 		 */
119 		if (entry->details->special_tab_handling && gtk_editable_get_selection_bounds (editable, NULL, NULL)) {
120 			position = strlen (gtk_entry_get_text (GTK_ENTRY (editable)));
121 			gtk_editable_select_region (editable, position, position);
122 			return TRUE;
123 		}
124 		break;
125 
126 	default:
127 		break;
128 	}
129 	
130 	old_has = gtk_editable_get_selection_bounds (editable, NULL, NULL);
131 
132 	result = GTK_WIDGET_CLASS (nautilus_entry_parent_class)->key_press_event (widget, event);
133 
134 	/* Pressing a key usually changes the selection if there is a selection.
135 	 * If there is not selection, we can save work by not emitting a signal.
136 	 */
137 	if (result) {
138 		new_has = gtk_editable_get_selection_bounds (editable, NULL, NULL);
139 		if (old_has || new_has) {
140 			g_signal_emit (widget, signals[SELECTION_CHANGED], 0);
141 		}
142 	}
143 
144 	return result;
145 	
146 }
147 
148 static gboolean
149 nautilus_entry_motion_notify (GtkWidget *widget, GdkEventMotion *event)
150 {
151 	int result;
152 	gboolean old_had, new_had;
153 	int old_start, old_end, new_start, new_end;
154 	GtkEditable *editable;
155 
156 	editable = GTK_EDITABLE (widget);
157 
158 	old_had = gtk_editable_get_selection_bounds (editable, &old_start, &old_end);
159 
160 	result = GTK_WIDGET_CLASS (nautilus_entry_parent_class)->motion_notify_event (widget, event);
161 
162 	/* Send a signal if dragging the mouse caused the selection to change. */
163 	if (result) {
164 		new_had = gtk_editable_get_selection_bounds (editable, &new_start, &new_end);
165 		if (old_had != new_had || (old_had && (old_start != new_start || old_end != new_end))) {
166 			g_signal_emit (widget, signals[SELECTION_CHANGED], 0);
167 		}
168 	}
169 	
170 	return result;
171 }
172 
173 /**
174  * nautilus_entry_select_all
175  *
176  * Select all text, leaving the text cursor position at the end.
177  * 
178  * @entry: A NautilusEntry
179  **/
180 void
181 nautilus_entry_select_all (NautilusEntry *entry)
182 {
183 	g_return_if_fail (NAUTILUS_IS_ENTRY (entry));
184 
185 	gtk_editable_set_position (GTK_EDITABLE (entry), -1);
186 	gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
187 }
188 
189 static gboolean
190 select_all_at_idle (gpointer callback_data)
191 {
192 	NautilusEntry *entry;
193 
194 	entry = NAUTILUS_ENTRY (callback_data);
195 
196 	nautilus_entry_select_all (entry);
197 
198 	entry->details->select_idle_id = 0;
199 	
200 	return FALSE;
201 }
202 
203 /**
204  * nautilus_entry_select_all_at_idle
205  *
206  * Select all text at the next idle, not immediately.
207  * This is useful when reacting to a key press, because
208  * changing the selection and the text cursor position doesn't
209  * work in a key_press signal handler.
210  * 
211  * @entry: A NautilusEntry
212  **/
213 void
214 nautilus_entry_select_all_at_idle (NautilusEntry *entry)
215 {
216 	g_return_if_fail (NAUTILUS_IS_ENTRY (entry));
217 
218 	/* If the text cursor position changes in this routine
219 	 * then gtk_entry_key_press will unselect (and we want
220 	 * to move the text cursor position to the end).
221 	 */
222 
223 	if (entry->details->select_idle_id == 0) {
224 		entry->details->select_idle_id = g_idle_add (select_all_at_idle, entry);
225 	}
226 }
227 
228 /**
229  * nautilus_entry_set_text
230  *
231  * This function wraps gtk_entry_set_text.  It sets undo_registered
232  * to TRUE and preserves the old value for a later restore.  This is
233  * done so the programmatic changes to the entry do not register
234  * with the undo manager.
235  *  
236  * @entry: A NautilusEntry
237  * @test: The text to set
238  **/
239 
240 void
241 nautilus_entry_set_text (NautilusEntry *entry, const gchar *text)
242 {
243 	g_return_if_fail (NAUTILUS_IS_ENTRY (entry));
244 
245 	gtk_entry_set_text (GTK_ENTRY (entry), text);
246 	g_signal_emit (entry, signals[SELECTION_CHANGED], 0);
247 }
248 
249 static void
250 nautilus_entry_set_selection_bounds (GtkEditable *editable,
251 				     int start_pos,
252 				     int end_pos)
253 {
254 	parent_editable_interface->set_selection_bounds (editable, start_pos, end_pos);
255 
256 	g_signal_emit (editable, signals[SELECTION_CHANGED], 0);
257 }
258 
259 static gboolean
260 nautilus_entry_button_press (GtkWidget *widget,
261 			     GdkEventButton *event)
262 {
263 	gboolean result;
264 
265 	result = GTK_WIDGET_CLASS (nautilus_entry_parent_class)->button_press_event (widget, event);
266 
267 	if (result) {
268 		g_signal_emit (widget, signals[SELECTION_CHANGED], 0);
269 	}
270 
271 	return result;
272 }
273 
274 static gboolean
275 nautilus_entry_button_release (GtkWidget *widget,
276 			       GdkEventButton *event)
277 {
278 	gboolean result;
279 
280 	result = GTK_WIDGET_CLASS (nautilus_entry_parent_class)->button_release_event (widget, event);
281 
282 	if (result) {
283 		g_signal_emit (widget, signals[SELECTION_CHANGED], 0);
284 	}
285 
286 	return result;
287 }
288 
289 static void
290 nautilus_entry_insert_text (GtkEditable *editable, const gchar *text,
291 			    int length, int *position)
292 {
293 	parent_editable_interface->insert_text (editable, text, length, position);
294 
295 	g_signal_emit (editable, signals[SELECTION_CHANGED], 0);
296 }
297 			 		     
298 static void 
299 nautilus_entry_delete_text (GtkEditable *editable, int start_pos, int end_pos)
300 {
301 	parent_editable_interface->delete_text (editable, start_pos, end_pos);
302 
303 	g_signal_emit (editable, signals[SELECTION_CHANGED], 0);
304 }
305 
306 /* Overridden to work around GTK bug. The selection_clear_event is queued
307  * when the selection changes. Changing the selection to NULL and then
308  * back to the original selection owner still sends the event, so the
309  * selection owner then gets the selection ripped away from it. We ran into
310  * this with type-completion behavior in NautilusLocationBar (see bug 5313).
311  * There's a FIXME comment that seems to be about this same issue in
312  * gtk+/gtkselection.c, gtk_selection_clear.
313  */
314 static gboolean
315 nautilus_entry_selection_clear (GtkWidget *widget,
316 			        GdkEventSelection *event)
317 {
318 	g_assert (NAUTILUS_IS_ENTRY (widget));
319 	
320 	if (gdk_selection_owner_get (event->selection) == gtk_widget_get_window (widget)) {
321 		return FALSE;
322 	}
323 	
324 	return GTK_WIDGET_CLASS (nautilus_entry_parent_class)->selection_clear_event (widget, event);
325 }
326 
327 static void
328 nautilus_entry_editable_init (GtkEditableInterface *iface)
329 {
330 	parent_editable_interface = g_type_interface_peek_parent (iface);
331 
332 	iface->insert_text = nautilus_entry_insert_text;
333 	iface->delete_text = nautilus_entry_delete_text;
334 	iface->set_selection_bounds = nautilus_entry_set_selection_bounds;
335 
336 	/* Otherwise we might need some memcpy loving */
337 	g_assert (iface->do_insert_text != NULL);
338 	g_assert (iface->get_position != NULL);
339 	g_assert (iface->get_chars != NULL);
340 }
341 
342 static void
343 nautilus_entry_class_init (NautilusEntryClass *class)
344 {
345 	GtkWidgetClass *widget_class;
346 	GObjectClass *gobject_class;
347 
348 	widget_class = GTK_WIDGET_CLASS (class);
349 	gobject_class = G_OBJECT_CLASS (class);
350 		
351 	widget_class->button_press_event = nautilus_entry_button_press;
352 	widget_class->button_release_event = nautilus_entry_button_release;
353 	widget_class->key_press_event = nautilus_entry_key_press;
354 	widget_class->motion_notify_event = nautilus_entry_motion_notify;
355 	widget_class->selection_clear_event = nautilus_entry_selection_clear;
356 	
357 	gobject_class->finalize = nautilus_entry_finalize;
358 
359 	/* Set up signals */
360 	signals[SELECTION_CHANGED] = g_signal_new
361 		("selection_changed",
362 		 G_TYPE_FROM_CLASS (class),
363 		 G_SIGNAL_RUN_LAST,
364 		 G_STRUCT_OFFSET (NautilusEntryClass, selection_changed),
365 		 NULL, NULL,
366 		 g_cclosure_marshal_VOID__VOID,
367 		 G_TYPE_NONE, 0);
368 }
369 
370 void
371 nautilus_entry_set_special_tab_handling (NautilusEntry *entry,
372 					 gboolean special_tab_handling)
373 {
374 	g_return_if_fail (NAUTILUS_IS_ENTRY (entry));
375 
376 	entry->details->special_tab_handling = special_tab_handling;
377 }