1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
3 /* nautilus-clipboard.c
5 * Nautilus Clipboard support. For now, routines to support component cut
8 * Copyright (C) 1999, 2000 Free Software Foundaton
9 * Copyright (C) 2000, 2001 Eazel, Inc.
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Library General Public License as
13 * published by the Free Software Foundation; either version 2 of the
14 * License, or (at your option) any later version.
16 * This program 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 program; if not, write to the
23 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 * Boston, MA 02111-1307, USA.
26 * Authors: Rebecca Schulman <rebecka@eazel.com>,
27 * Darin Adler <darin@bentspoon.com>
31 #include "nautilus-clipboard.h"
32 #include "nautilus-file-utilities.h"
34 #include <glib/gi18n.h>
35 #include <gtk/gtkclipboard.h>
36 #include <gtk/gtkstock.h>
37 #include <gtk/gtkinvisible.h>
38 #include <gtk/gtkmain.h>
39 #include <gtk/gtksignal.h>
40 #include <gtk/gtktext.h>
41 #include <gtk/gtktextview.h>
44 typedef struct _TargetCallbackData TargetCallbackData
;
46 typedef void (* SelectAllCallback
) (gpointer target
);
47 typedef void (* ConnectCallbacksFunc
) (GObject
*object
,
48 TargetCallbackData
*target_data
);
50 static void selection_changed_callback (GtkWidget
*widget
,
51 gpointer callback_data
);
52 static void owner_change_callback (GtkClipboard
*clipboard
,
53 GdkEventOwnerChange
*event
,
54 gpointer callback_data
);
55 struct _TargetCallbackData
{
56 GtkUIManager
*ui_manager
;
57 GtkActionGroup
*action_group
;
58 gboolean shares_selection_changes
;
60 SelectAllCallback select_all_callback
;
62 ConnectCallbacksFunc connect_callbacks
;
63 ConnectCallbacksFunc disconnect_callbacks
;
67 cut_callback (gpointer target
)
69 g_return_if_fail (target
!= NULL
);
71 g_signal_emit_by_name (target
, "cut-clipboard");
75 copy_callback (gpointer target
)
77 g_return_if_fail (target
!= NULL
);
79 g_signal_emit_by_name (target
, "copy-clipboard");
83 paste_callback (gpointer target
)
85 g_return_if_fail (target
!= NULL
);
87 g_signal_emit_by_name (target
, "paste-clipboard");
91 editable_select_all_callback (gpointer target
)
93 GtkEditable
*editable
;
95 editable
= GTK_EDITABLE (target
);
96 g_return_if_fail (editable
!= NULL
);
98 gtk_editable_set_position (editable
, -1);
99 gtk_editable_select_region (editable
, 0, -1);
103 text_view_select_all_callback (gpointer target
)
105 g_return_if_fail (GTK_IS_TEXT_VIEW (target
));
107 g_signal_emit_by_name (target
, "select-all", TRUE
);
111 action_cut_callback (GtkAction
*action
,
112 gpointer callback_data
)
114 cut_callback (callback_data
);
118 action_copy_callback (GtkAction
*action
,
119 gpointer callback_data
)
121 copy_callback (callback_data
);
125 action_paste_callback (GtkAction
*action
,
126 gpointer callback_data
)
128 paste_callback (callback_data
);
132 action_select_all_callback (GtkAction
*action
,
133 gpointer callback_data
)
135 TargetCallbackData
*target_data
;
137 g_return_if_fail (callback_data
!= NULL
);
139 target_data
= g_object_get_data (callback_data
, "Nautilus:clipboard_target_data");
140 g_return_if_fail (target_data
!= NULL
);
142 target_data
->select_all_callback (callback_data
);
146 received_clipboard_contents (GtkClipboard
*clipboard
,
147 GtkSelectionData
*selection_data
,
150 GtkActionGroup
*action_group
;
155 action
= gtk_action_group_get_action (action_group
,
157 if (action
!= NULL
) {
158 gtk_action_set_sensitive (action
,
159 gtk_selection_data_targets_include_text (selection_data
));
162 g_object_unref (action_group
);
167 set_paste_sensitive_if_clipboard_contains_data (GtkActionGroup
*action_group
)
170 if (gdk_display_supports_selection_notification (gdk_display_get_default ())) {
171 gtk_clipboard_request_contents (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD
),
172 gdk_atom_intern ("TARGETS", FALSE
),
173 received_clipboard_contents
,
174 g_object_ref (action_group
));
176 /* If selection notification isn't supported, always activate Paste */
177 action
= gtk_action_group_get_action (action_group
,
179 gtk_action_set_sensitive (action
, TRUE
);
184 set_clipboard_menu_items_sensitive (GtkActionGroup
*action_group
)
188 action
= gtk_action_group_get_action (action_group
,
190 gtk_action_set_sensitive (action
, TRUE
);
191 action
= gtk_action_group_get_action (action_group
,
193 gtk_action_set_sensitive (action
, TRUE
);
197 set_clipboard_menu_items_insensitive (GtkActionGroup
*action_group
)
201 action
= gtk_action_group_get_action (action_group
,
203 gtk_action_set_sensitive (action
, FALSE
);
204 action
= gtk_action_group_get_action (action_group
,
206 gtk_action_set_sensitive (action
, FALSE
);
210 clipboard_items_are_merged_in (GtkWidget
*widget
)
212 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget
),
213 "Nautilus:clipboard_menu_items_merged"));
217 set_clipboard_items_are_merged_in (GObject
*widget_as_object
,
220 g_object_set_data (widget_as_object
,
221 "Nautilus:clipboard_menu_items_merged",
222 GINT_TO_POINTER (merged_in
));
226 editable_connect_callbacks (GObject
*object
,
227 TargetCallbackData
*target_data
)
229 g_signal_connect_after (object
, "selection_changed",
230 G_CALLBACK (selection_changed_callback
), target_data
);
231 selection_changed_callback (GTK_WIDGET (object
),
236 editable_disconnect_callbacks (GObject
*object
,
237 TargetCallbackData
*target_data
)
239 g_signal_handlers_disconnect_matched (object
,
240 G_SIGNAL_MATCH_FUNC
| G_SIGNAL_MATCH_DATA
,
242 G_CALLBACK (selection_changed_callback
),
247 text_buffer_update_sensitivity (GtkTextBuffer
*buffer
,
248 TargetCallbackData
*target_data
)
250 g_assert (GTK_IS_TEXT_BUFFER (buffer
));
251 g_assert (target_data
!= NULL
);
253 if (gtk_text_buffer_get_selection_bounds (buffer
, NULL
, NULL
)) {
254 set_clipboard_menu_items_sensitive (target_data
->action_group
);
256 set_clipboard_menu_items_insensitive (target_data
->action_group
);
261 text_buffer_delete_range (GtkTextBuffer
*buffer
,
264 TargetCallbackData
*target_data
)
266 text_buffer_update_sensitivity (buffer
, target_data
);
270 text_buffer_mark_set (GtkTextBuffer
*buffer
,
273 TargetCallbackData
*target_data
)
275 /* anonymous marks with NULL names refer to cursor moves */
276 if (gtk_text_mark_get_name (mark
) != NULL
) {
277 text_buffer_update_sensitivity (buffer
, target_data
);
282 text_view_connect_callbacks (GObject
*object
,
283 TargetCallbackData
*target_data
)
285 GtkTextBuffer
*buffer
;
287 buffer
= gtk_text_view_get_buffer (GTK_TEXT_VIEW (object
));
290 g_signal_connect_after (buffer
, "mark-set",
291 G_CALLBACK (text_buffer_mark_set
), target_data
);
292 g_signal_connect_after (buffer
, "delete-range",
293 G_CALLBACK (text_buffer_delete_range
), target_data
);
294 text_buffer_update_sensitivity (buffer
, target_data
);
298 text_view_disconnect_callbacks (GObject
*object
,
299 TargetCallbackData
*target_data
)
301 GtkTextBuffer
*buffer
;
303 buffer
= gtk_text_view_get_buffer (GTK_TEXT_VIEW (object
));
306 g_signal_handlers_disconnect_matched (buffer
,
313 merge_in_clipboard_menu_items (GObject
*widget_as_object
,
314 TargetCallbackData
*target_data
)
316 gboolean add_selection_callback
;
318 g_assert (target_data
!= NULL
);
320 add_selection_callback
= target_data
->shares_selection_changes
;
322 gtk_ui_manager_insert_action_group (target_data
->ui_manager
,
323 target_data
->action_group
, 0);
325 set_paste_sensitive_if_clipboard_contains_data (target_data
->action_group
);
327 g_signal_connect (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD
), "owner_change",
328 G_CALLBACK (owner_change_callback
), target_data
);
330 if (add_selection_callback
) {
331 target_data
->connect_callbacks (widget_as_object
, target_data
);
333 /* If we don't use sensitivity, everything should be on */
334 set_clipboard_menu_items_sensitive (target_data
->action_group
);
336 set_clipboard_items_are_merged_in (widget_as_object
, TRUE
);
340 merge_out_clipboard_menu_items (GObject
*widget_as_object
,
341 TargetCallbackData
*target_data
)
344 gboolean selection_callback_was_added
;
346 g_assert (target_data
!= NULL
);
348 gtk_ui_manager_remove_action_group (target_data
->ui_manager
,
349 target_data
->action_group
);
351 g_signal_handlers_disconnect_matched (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD
),
352 G_SIGNAL_MATCH_FUNC
| G_SIGNAL_MATCH_DATA
,
354 G_CALLBACK (owner_change_callback
),
357 selection_callback_was_added
= target_data
->shares_selection_changes
;
359 if (selection_callback_was_added
) {
360 target_data
->disconnect_callbacks (widget_as_object
, target_data
);
362 set_clipboard_items_are_merged_in (widget_as_object
, FALSE
);
366 focus_changed_callback (GtkWidget
*widget
,
368 gpointer callback_data
)
370 /* Connect the component to the container if the widget has focus. */
371 if (GTK_WIDGET_HAS_FOCUS (widget
)) {
372 if (!clipboard_items_are_merged_in (widget
)) {
373 merge_in_clipboard_menu_items (G_OBJECT (widget
), callback_data
);
376 if (clipboard_items_are_merged_in (widget
)) {
377 merge_out_clipboard_menu_items (G_OBJECT (widget
), callback_data
);
385 selection_changed_callback (GtkWidget
*widget
,
386 gpointer callback_data
)
388 TargetCallbackData
*target_data
;
389 GtkEditable
*editable
;
392 target_data
= (TargetCallbackData
*) callback_data
;
393 g_assert (target_data
!= NULL
);
395 editable
= GTK_EDITABLE (widget
);
396 g_return_if_fail (editable
!= NULL
);
398 if (gtk_editable_get_selection_bounds (editable
, &start
, &end
) && start
!= end
) {
399 set_clipboard_menu_items_sensitive (target_data
->action_group
);
401 set_clipboard_menu_items_insensitive (target_data
->action_group
);
406 owner_change_callback (GtkClipboard
*clipboard
,
407 GdkEventOwnerChange
*event
,
408 gpointer callback_data
)
410 TargetCallbackData
*target_data
;
412 g_assert (callback_data
!= NULL
);
413 target_data
= callback_data
;
415 set_paste_sensitive_if_clipboard_contains_data (target_data
->action_group
);
419 target_destroy_callback (GtkObject
*object
,
420 gpointer callback_data
)
422 TargetCallbackData
*target_data
;
424 g_assert (callback_data
!= NULL
);
425 target_data
= callback_data
;
427 if (clipboard_items_are_merged_in (GTK_WIDGET(object
))) {
428 merge_out_clipboard_menu_items (G_OBJECT (object
), callback_data
);
433 target_data_free (TargetCallbackData
*target_data
)
435 g_object_unref (target_data
->action_group
);
436 g_free (target_data
);
439 static const GtkActionEntry clipboard_entries
[] = {
440 /* name, stock id */ { "Cut", GTK_STOCK_CUT
,
441 /* label, accelerator */ NULL
, NULL
,
442 /* tooltip */ N_("Cut the selected text to the clipboard"),
443 G_CALLBACK (action_cut_callback
) },
444 /* name, stock id */ { "Copy", GTK_STOCK_COPY
,
445 /* label, accelerator */ NULL
, NULL
,
446 /* tooltip */ N_("Copy the selected text to the clipboard"),
447 G_CALLBACK (action_copy_callback
) },
448 /* name, stock id */ { "Paste", GTK_STOCK_PASTE
,
449 /* label, accelerator */ NULL
, NULL
,
450 /* tooltip */ N_("Paste the text stored on the clipboard"),
451 G_CALLBACK (action_paste_callback
) },
452 /* name, stock id */ { "Select All", NULL
,
453 /* label, accelerator */ N_("Select _All"), "<control>A",
454 /* tooltip */ N_("Select all the text in a text field"),
455 G_CALLBACK (action_select_all_callback
) },
458 static TargetCallbackData
*
459 initialize_clipboard_component_with_callback_data (GtkEditable
*target
,
460 GtkUIManager
*ui_manager
,
461 gboolean shares_selection_changes
,
462 SelectAllCallback select_all_callback
,
463 ConnectCallbacksFunc connect_callbacks
,
464 ConnectCallbacksFunc disconnect_callbacks
)
466 GtkActionGroup
*action_group
;
467 TargetCallbackData
*target_data
;
469 action_group
= gtk_action_group_new ("ClipboardActions");
470 gtk_action_group_set_translation_domain (action_group
, GETTEXT_PACKAGE
);
471 gtk_action_group_add_actions (action_group
,
472 clipboard_entries
, G_N_ELEMENTS (clipboard_entries
),
475 /* Do the actual connection of the UI to the container at
476 * focus time, and disconnect at both focus and destroy
479 target_data
= g_new (TargetCallbackData
, 1);
480 target_data
->ui_manager
= ui_manager
;
481 target_data
->action_group
= action_group
;
482 target_data
->shares_selection_changes
= shares_selection_changes
;
483 target_data
->select_all_callback
= select_all_callback
;
484 target_data
->connect_callbacks
= connect_callbacks
;
485 target_data
->disconnect_callbacks
= disconnect_callbacks
;
491 nautilus_clipboard_real_set_up (gpointer target
,
492 GtkUIManager
*ui_manager
,
493 gboolean shares_selection_changes
,
494 SelectAllCallback select_all_callback
,
495 ConnectCallbacksFunc connect_callbacks
,
496 ConnectCallbacksFunc disconnect_callbacks
)
498 TargetCallbackData
*target_data
;
500 if (g_object_get_data (G_OBJECT (target
), "Nautilus:clipboard_target_data") != NULL
) {
504 target_data
= initialize_clipboard_component_with_callback_data
507 shares_selection_changes
,
510 disconnect_callbacks
);
512 g_signal_connect (target
, "focus_in_event",
513 G_CALLBACK (focus_changed_callback
), target_data
);
514 g_signal_connect (target
, "focus_out_event",
515 G_CALLBACK (focus_changed_callback
), target_data
);
516 g_signal_connect (target
, "destroy",
517 G_CALLBACK (target_destroy_callback
), target_data
);
519 g_object_set_data_full (G_OBJECT (target
), "Nautilus:clipboard_target_data",
520 target_data
, (GDestroyNotify
) target_data_free
);
522 /* Call the focus changed callback once to merge if the window is
525 focus_changed_callback (GTK_WIDGET (target
), NULL
, target_data
);
529 nautilus_clipboard_set_up_editable (GtkEditable
*target
,
530 GtkUIManager
*ui_manager
,
531 gboolean shares_selection_changes
)
533 g_return_if_fail (GTK_IS_EDITABLE (target
));
534 g_return_if_fail (GTK_IS_UI_MANAGER (ui_manager
));
536 nautilus_clipboard_real_set_up (target
, ui_manager
,
537 shares_selection_changes
,
538 editable_select_all_callback
,
539 editable_connect_callbacks
,
540 editable_disconnect_callbacks
);
544 nautilus_clipboard_set_up_text_view (GtkTextView
*target
,
545 GtkUIManager
*ui_manager
)
547 g_return_if_fail (GTK_IS_TEXT_VIEW (target
));
548 g_return_if_fail (GTK_IS_UI_MANAGER (ui_manager
));
550 nautilus_clipboard_real_set_up (target
, ui_manager
, TRUE
,
551 text_view_select_all_callback
,
552 text_view_connect_callbacks
,
553 text_view_disconnect_callbacks
);