3
* Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org>
4
* Copyright (c) 2009 Jannis Pohlmann <jannis@xfce.org>
6
* This program is free software; you can redistribute it and/or modify it
7
* under the terms of the GNU General Public License as published by the Free
8
* Software Foundation; either version 2 of the License, or (at your option)
11
* This program is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16
* You should have received a copy of the GNU General Public License along with
17
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
18
* Place, Suite 330, Boston, MA 02111-1307 USA
32
#include <thunar/thunar-application.h>
33
#include <thunar/thunar-browser.h>
34
#include <thunar/thunar-chooser-dialog.h>
35
#include <thunar/thunar-dialogs.h>
36
#include <thunar/thunar-gio-extensions.h>
37
#include <thunar/thunar-gobject-extensions.h>
38
#include <thunar/thunar-gtk-extensions.h>
39
#include <thunar/thunar-launcher.h>
40
#include <thunar/thunar-launcher-ui.h>
41
#include <thunar/thunar-private.h>
42
#include <thunar/thunar-sendto-model.h>
43
#include <thunar/thunar-stock.h>
44
#include <thunar/thunar-device-monitor.h>
45
#include <thunar/thunar-window.h>
49
typedef struct _ThunarLauncherMountData ThunarLauncherMountData;
50
typedef struct _ThunarLauncherPokeData ThunarLauncherPokeData;
54
/* Property identifiers */
58
PROP_CURRENT_DIRECTORY,
67
static void thunar_launcher_component_init (ThunarComponentIface *iface);
68
static void thunar_launcher_navigator_init (ThunarNavigatorIface *iface);
69
static void thunar_launcher_dispose (GObject *object);
70
static void thunar_launcher_finalize (GObject *object);
71
static void thunar_launcher_get_property (GObject *object,
75
static void thunar_launcher_set_property (GObject *object,
79
static ThunarFile *thunar_launcher_get_current_directory (ThunarNavigator *navigator);
80
static void thunar_launcher_set_current_directory (ThunarNavigator *navigator,
81
ThunarFile *current_directory);
82
static GList *thunar_launcher_get_selected_files (ThunarComponent *component);
83
static void thunar_launcher_set_selected_files (ThunarComponent *component,
84
GList *selected_files);
85
static GtkUIManager *thunar_launcher_get_ui_manager (ThunarComponent *component);
86
static void thunar_launcher_set_ui_manager (ThunarComponent *component,
87
GtkUIManager *ui_manager);
88
static void thunar_launcher_execute_files (ThunarLauncher *launcher,
90
static void thunar_launcher_open_files (ThunarLauncher *launcher,
92
static void thunar_launcher_open_paths (GAppInfo *app_info,
94
ThunarLauncher *launcher);
95
static void thunar_launcher_open_windows (ThunarLauncher *launcher,
97
static void thunar_launcher_update (ThunarLauncher *launcher);
98
static void thunar_launcher_action_open (GtkAction *action,
99
ThunarLauncher *launcher);
100
static void thunar_launcher_action_open_with_other (GtkAction *action,
101
ThunarLauncher *launcher);
102
static void thunar_launcher_action_open_in_new_window (GtkAction *action,
103
ThunarLauncher *launcher);
104
static void thunar_launcher_action_open_in_new_tab (GtkAction *action,
105
ThunarLauncher *launcher);
106
static void thunar_launcher_action_sendto_desktop (GtkAction *action,
107
ThunarLauncher *launcher);
108
static void thunar_launcher_action_sendto_device (GtkAction *action,
109
ThunarLauncher *launcher);
110
static void thunar_launcher_widget_destroyed (ThunarLauncher *launcher,
112
static gboolean thunar_launcher_sendto_idle (gpointer user_data);
113
static void thunar_launcher_sendto_idle_destroy (gpointer user_data);
114
static void thunar_launcher_mount_data_free (ThunarLauncherMountData *data);
115
static void thunar_launcher_poke_files (ThunarLauncher *launcher,
116
ThunarLauncherPokeData *poke_data);
117
static void thunar_launcher_poke_files_finish (ThunarBrowser *browser,
119
ThunarFile *target_file,
122
static ThunarLauncherPokeData *thunar_launcher_poke_data_new (GList *files);
123
static void thunar_launcher_poke_data_free (ThunarLauncherPokeData *data);
124
static GtkWidget *thunar_launcher_get_widget (const ThunarLauncher *launcher);
127
struct _ThunarLauncherClass
129
GObjectClass __parent__;
132
struct _ThunarLauncher
136
ThunarFile *current_directory;
137
GList *selected_files;
139
guint launcher_idle_id;
141
GtkIconFactory *icon_factory;
142
GtkActionGroup *action_group;
143
GtkUIManager *ui_manager;
145
guint ui_addons_merge_id;
147
GtkAction *action_open;
148
GtkAction *action_open_with_other;
149
GtkAction *action_open_in_new_window;
150
GtkAction *action_open_in_new_tab;
151
GtkAction *action_open_with_other_in_menu;
155
ThunarDeviceMonitor *device_monitor;
156
ThunarSendtoModel *sendto_model;
157
guint sendto_idle_id;
160
struct _ThunarLauncherMountData
162
ThunarLauncher *launcher;
166
struct _ThunarLauncherPokeData
169
GList *resolved_files;
170
guint directories_in_tabs : 1;
175
static const GtkActionEntry action_entries[] =
177
{ "open", GTK_STOCK_OPEN, N_ ("_Open"), "<control>O", NULL, G_CALLBACK (thunar_launcher_action_open), },
178
{ "open-in-new-tab", NULL, N_ ("Open in New _Tab"), "<control><shift>P", NULL, G_CALLBACK (thunar_launcher_action_open_in_new_tab), },
179
{ "open-in-new-window", NULL, N_ ("Open in New _Window"), "<control><shift>O", NULL, G_CALLBACK (thunar_launcher_action_open_in_new_window), },
180
{ "open-with-other", NULL, N_ ("Open With Other _Application..."), NULL, N_ ("Choose another application with which to open the selected file"), G_CALLBACK (thunar_launcher_action_open_with_other), },
181
{ "open-with-menu", NULL, N_ ("Open With"), NULL, NULL, NULL, },
182
{ "open-with-other-in-menu", NULL, N_ ("Open With Other _Application..."), NULL, N_ ("Choose another application with which to open the selected file"), G_CALLBACK (thunar_launcher_action_open_with_other), },
183
{ "sendto-desktop", THUNAR_STOCK_DESKTOP, "", NULL, NULL, G_CALLBACK (thunar_launcher_action_sendto_desktop), },
186
static GQuark thunar_launcher_handler_quark;
190
static GParamSpec *launcher_props[N_PROPERTIES] = { NULL, };
194
G_DEFINE_TYPE_WITH_CODE (ThunarLauncher, thunar_launcher, G_TYPE_OBJECT,
195
G_IMPLEMENT_INTERFACE (THUNAR_TYPE_BROWSER, NULL)
196
G_IMPLEMENT_INTERFACE (THUNAR_TYPE_NAVIGATOR, thunar_launcher_navigator_init)
197
G_IMPLEMENT_INTERFACE (THUNAR_TYPE_COMPONENT, thunar_launcher_component_init))
202
thunar_launcher_class_init (ThunarLauncherClass *klass)
204
GObjectClass *gobject_class;
207
/* determine the "thunar-launcher-handler" quark */
208
thunar_launcher_handler_quark = g_quark_from_static_string ("thunar-launcher-handler");
210
gobject_class = G_OBJECT_CLASS (klass);
211
gobject_class->dispose = thunar_launcher_dispose;
212
gobject_class->finalize = thunar_launcher_finalize;
213
gobject_class->get_property = thunar_launcher_get_property;
214
gobject_class->set_property = thunar_launcher_set_property;
217
* ThunarLauncher:widget:
219
* The #GtkWidget with which this launcher is associated.
221
launcher_props[PROP_WIDGET] =
222
g_param_spec_object ("widget",
226
EXO_PARAM_READWRITE);
228
/* Override ThunarNavigator's properties */
229
g_iface = g_type_default_interface_peek (THUNAR_TYPE_NAVIGATOR);
230
launcher_props[PROP_CURRENT_DIRECTORY] =
231
g_param_spec_override ("current-directory",
232
g_object_interface_find_property (g_iface, "current-directory"));
234
/* Override ThunarComponent's properties */
235
g_iface = g_type_default_interface_peek (THUNAR_TYPE_COMPONENT);
236
launcher_props[PROP_SELECTED_FILES] =
237
g_param_spec_override ("selected-files",
238
g_object_interface_find_property (g_iface, "selected-files"));
240
launcher_props[PROP_UI_MANAGER] =
241
g_param_spec_override ("ui-manager",
242
g_object_interface_find_property (g_iface, "ui-manager"));
244
/* install properties */
245
g_object_class_install_properties (gobject_class, N_PROPERTIES, launcher_props);
251
thunar_launcher_component_init (ThunarComponentIface *iface)
253
iface->get_selected_files = thunar_launcher_get_selected_files;
254
iface->set_selected_files = thunar_launcher_set_selected_files;
255
iface->get_ui_manager = thunar_launcher_get_ui_manager;
256
iface->set_ui_manager = thunar_launcher_set_ui_manager;
262
thunar_launcher_navigator_init (ThunarNavigatorIface *iface)
264
iface->get_current_directory = thunar_launcher_get_current_directory;
265
iface->set_current_directory = thunar_launcher_set_current_directory;
271
thunar_launcher_init (ThunarLauncher *launcher)
273
/* setup the action group for the launcher actions */
274
launcher->action_group = gtk_action_group_new ("ThunarLauncher");
275
gtk_action_group_set_translation_domain (launcher->action_group, GETTEXT_PACKAGE);
276
gtk_action_group_add_actions (launcher->action_group, action_entries, G_N_ELEMENTS (action_entries), launcher);
278
/* determine references to our actions */
279
launcher->action_open = gtk_action_group_get_action (launcher->action_group, "open");
280
launcher->action_open_with_other = gtk_action_group_get_action (launcher->action_group, "open-with-other");
281
launcher->action_open_in_new_window = gtk_action_group_get_action (launcher->action_group, "open-in-new-window");
282
launcher->action_open_in_new_tab = gtk_action_group_get_action (launcher->action_group, "open-in-new-tab");
283
launcher->action_open_with_other_in_menu = gtk_action_group_get_action (launcher->action_group, "open-with-other-in-menu");
285
/* initialize and add our custom icon factory for the application/action icons */
286
launcher->icon_factory = gtk_icon_factory_new ();
287
gtk_icon_factory_add_default (launcher->icon_factory);
289
/* setup the "Send To" support */
290
launcher->sendto_model = thunar_sendto_model_get_default ();
292
/* the "Send To" menu also displays removable devices from the device monitor */
293
launcher->device_monitor = thunar_device_monitor_get ();
294
g_signal_connect_swapped (launcher->device_monitor, "device-added", G_CALLBACK (thunar_launcher_update), launcher);
295
g_signal_connect_swapped (launcher->device_monitor, "device-removed", G_CALLBACK (thunar_launcher_update), launcher);
301
thunar_launcher_dispose (GObject *object)
303
ThunarLauncher *launcher = THUNAR_LAUNCHER (object);
305
/* reset our properties */
306
thunar_navigator_set_current_directory (THUNAR_NAVIGATOR (launcher), NULL);
307
thunar_component_set_ui_manager (THUNAR_COMPONENT (launcher), NULL);
308
thunar_launcher_set_widget (THUNAR_LAUNCHER (launcher), NULL);
310
/* disconnect from the currently selected files */
311
thunar_g_file_list_free (launcher->selected_files);
312
launcher->selected_files = NULL;
314
(*G_OBJECT_CLASS (thunar_launcher_parent_class)->dispose) (object);
320
thunar_launcher_finalize (GObject *object)
322
ThunarLauncher *launcher = THUNAR_LAUNCHER (object);
324
/* be sure to cancel the sendto idle source */
325
if (G_UNLIKELY (launcher->sendto_idle_id != 0))
326
g_source_remove (launcher->sendto_idle_id);
328
/* be sure to cancel the launcher idle source */
329
if (G_UNLIKELY (launcher->launcher_idle_id != 0))
330
g_source_remove (launcher->launcher_idle_id);
332
/* drop our custom icon factory for the application/action icons */
333
gtk_icon_factory_remove_default (launcher->icon_factory);
334
g_object_unref (launcher->icon_factory);
336
/* release the reference on the action group */
337
g_object_unref (launcher->action_group);
339
/* disconnect from the device monitor used for the "Send To" menu */
340
g_signal_handlers_disconnect_by_func (launcher->device_monitor, thunar_launcher_update, launcher);
341
g_object_unref (launcher->device_monitor);
343
/* release the reference on the sendto model */
344
g_object_unref (launcher->sendto_model);
346
(*G_OBJECT_CLASS (thunar_launcher_parent_class)->finalize) (object);
352
thunar_launcher_get_property (GObject *object,
359
case PROP_CURRENT_DIRECTORY:
360
g_value_set_object (value, thunar_navigator_get_current_directory (THUNAR_NAVIGATOR (object)));
363
case PROP_SELECTED_FILES:
364
g_value_set_boxed (value, thunar_component_get_selected_files (THUNAR_COMPONENT (object)));
367
case PROP_UI_MANAGER:
368
g_value_set_object (value, thunar_component_get_ui_manager (THUNAR_COMPONENT (object)));
372
g_value_set_object (value, thunar_launcher_get_widget (THUNAR_LAUNCHER (object)));
376
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
384
thunar_launcher_set_property (GObject *object,
391
case PROP_CURRENT_DIRECTORY:
392
thunar_navigator_set_current_directory (THUNAR_NAVIGATOR (object), g_value_get_object (value));
395
case PROP_SELECTED_FILES:
396
thunar_component_set_selected_files (THUNAR_COMPONENT (object), g_value_get_boxed (value));
399
case PROP_UI_MANAGER:
400
thunar_component_set_ui_manager (THUNAR_COMPONENT (object), g_value_get_object (value));
404
thunar_launcher_set_widget (THUNAR_LAUNCHER (object), g_value_get_object (value));
408
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
416
thunar_launcher_get_current_directory (ThunarNavigator *navigator)
418
return THUNAR_LAUNCHER (navigator)->current_directory;
424
thunar_launcher_set_current_directory (ThunarNavigator *navigator,
425
ThunarFile *current_directory)
427
ThunarLauncher *launcher = THUNAR_LAUNCHER (navigator);
429
/* disconnect from the previous directory */
430
if (G_LIKELY (launcher->current_directory != NULL))
431
g_object_unref (G_OBJECT (launcher->current_directory));
433
/* activate the new directory */
434
launcher->current_directory = current_directory;
436
/* connect to the new directory */
437
if (G_LIKELY (current_directory != NULL))
438
g_object_ref (G_OBJECT (current_directory));
440
/* notify listeners */
441
g_object_notify_by_pspec (G_OBJECT (launcher), launcher_props[PROP_CURRENT_DIRECTORY]);
447
thunar_launcher_get_selected_files (ThunarComponent *component)
449
return THUNAR_LAUNCHER (component)->selected_files;
455
thunar_launcher_set_selected_files (ThunarComponent *component,
456
GList *selected_files)
458
ThunarLauncher *launcher = THUNAR_LAUNCHER (component);
462
/* compare the old and the new list of selected files */
463
for (np = selected_files, op = launcher->selected_files; np != NULL && op != NULL; np = np->next, op = op->next)
464
if (G_UNLIKELY (np->data != op->data))
467
/* check if the list of selected files really changed */
468
if (G_UNLIKELY (np != NULL || op != NULL))
470
/* disconnect from the previously selected files */
471
thunar_g_file_list_free (launcher->selected_files);
473
/* connect to the new selected files list */
474
launcher->selected_files = thunar_g_file_list_copy (selected_files);
476
/* update the launcher actions */
477
thunar_launcher_update (launcher);
479
/* notify listeners */
480
g_object_notify_by_pspec (G_OBJECT (launcher), launcher_props[PROP_SELECTED_FILES]);
487
thunar_launcher_get_ui_manager (ThunarComponent *component)
489
return THUNAR_LAUNCHER (component)->ui_manager;
495
thunar_launcher_set_ui_manager (ThunarComponent *component,
496
GtkUIManager *ui_manager)
498
ThunarLauncher *launcher = THUNAR_LAUNCHER (component);
499
GError *error = NULL;
501
/* disconnect from the previous UI manager */
502
if (G_UNLIKELY (launcher->ui_manager != NULL))
504
/* drop our action group from the previous UI manager */
505
gtk_ui_manager_remove_action_group (launcher->ui_manager, launcher->action_group);
507
/* unmerge our addons ui controls from the previous UI manager */
508
if (G_LIKELY (launcher->ui_addons_merge_id != 0))
510
gtk_ui_manager_remove_ui (launcher->ui_manager, launcher->ui_addons_merge_id);
511
launcher->ui_addons_merge_id = 0;
514
/* unmerge our ui controls from the previous UI manager */
515
gtk_ui_manager_remove_ui (launcher->ui_manager, launcher->ui_merge_id);
517
/* drop the reference on the previous UI manager */
518
g_object_unref (G_OBJECT (launcher->ui_manager));
521
/* activate the new UI manager */
522
launcher->ui_manager = ui_manager;
524
/* connect to the new UI manager */
525
if (G_LIKELY (ui_manager != NULL))
527
/* we keep a reference on the new manager */
528
g_object_ref (G_OBJECT (ui_manager));
530
/* add our action group to the new manager */
531
gtk_ui_manager_insert_action_group (ui_manager, launcher->action_group, -1);
533
/* merge our UI control items with the new manager */
534
launcher->ui_merge_id = gtk_ui_manager_add_ui_from_string (ui_manager, thunar_launcher_ui, thunar_launcher_ui_length, &error);
535
if (G_UNLIKELY (launcher->ui_merge_id == 0))
537
g_error ("Failed to merge ThunarLauncher menus: %s", error->message);
538
g_error_free (error);
541
/* update the user interface */
542
thunar_launcher_update (launcher);
545
/* notify listeners */
546
g_object_notify_by_pspec (G_OBJECT (launcher), launcher_props[PROP_UI_MANAGER]);
552
thunar_launcher_execute_files (ThunarLauncher *launcher,
555
GError *error = NULL;
556
GFile *working_directory;
559
/* execute all selected files */
560
for (lp = files; lp != NULL; lp = lp->next)
562
working_directory = thunar_file_get_file (launcher->current_directory);
564
if (!thunar_file_execute (lp->data, working_directory, launcher->widget, NULL, &error))
566
/* display an error message to the user */
567
thunar_dialogs_show_error (launcher->widget, error, _("Failed to execute file \"%s\""), thunar_file_get_display_name (lp->data));
568
g_error_free (error);
577
thunar_launcher_g_app_info_hash (gconstpointer app_info)
585
thunar_launcher_open_files (ThunarLauncher *launcher,
588
GHashTable *applications;
593
/* allocate a hash table to associate applications to URIs. since GIO allocates
594
* new GAppInfo objects every time, g_direct_hash does not work. we therefor use
595
* a fake hash function to always hit the collision list of the hash table and
596
* avoid storing multiple equal GAppInfos by means of g_app_info_equal(). */
597
applications = g_hash_table_new_full (thunar_launcher_g_app_info_hash,
598
(GEqualFunc) g_app_info_equal,
599
(GDestroyNotify) g_object_unref,
600
(GDestroyNotify) thunar_g_file_list_free);
602
for (lp = files; lp != NULL; lp = lp->next)
604
/* determine the default application for the MIME type */
605
app_info = thunar_file_get_default_handler (lp->data);
607
/* check if we have an application here */
608
if (G_LIKELY (app_info != NULL))
610
/* check if we have that application already */
611
file_list = g_hash_table_lookup (applications, app_info);
612
if (G_LIKELY (file_list != NULL))
614
/* take a copy of the list as the old one will be dropped by the insert */
615
file_list = thunar_g_file_list_copy (file_list);
618
/* append our new URI to the list */
619
file_list = thunar_g_file_list_append (file_list, thunar_file_get_file (lp->data));
621
/* (re)insert the URI list for the application */
622
g_hash_table_insert (applications, app_info, file_list);
626
/* display a chooser dialog for the file and stop */
627
thunar_show_chooser_dialog (launcher->widget, lp->data, TRUE);
632
/* run all collected applications */
633
g_hash_table_foreach (applications, (GHFunc) thunar_launcher_open_paths, launcher);
635
/* drop the applications hash table */
636
g_hash_table_destroy (applications);
642
thunar_launcher_open_paths (GAppInfo *app_info,
644
ThunarLauncher *launcher)
646
GdkAppLaunchContext *context;
648
GError *error = NULL;
649
GFile *working_directory = NULL;
654
/* determine the screen on which to launch the application */
655
screen = (launcher->widget != NULL) ? gtk_widget_get_screen (launcher->widget) : NULL;
657
/* create launch context */
658
context = gdk_app_launch_context_new ();
659
gdk_app_launch_context_set_screen (context, screen);
660
gdk_app_launch_context_set_timestamp (context, gtk_get_current_event_time ());
661
gdk_app_launch_context_set_icon (context, g_app_info_get_icon (app_info));
663
/* determine the working directory */
664
if (launcher->current_directory != NULL)
665
working_directory = thunar_file_get_file (launcher->current_directory);
667
/* try to execute the application with the given URIs */
668
if (!thunar_g_app_info_launch (app_info, working_directory, path_list, G_APP_LAUNCH_CONTEXT (context), &error))
670
/* figure out the appropriate error message */
671
n = g_list_length (path_list);
672
if (G_LIKELY (n == 1))
674
/* we can give a precise error message here */
675
name = g_filename_display_name (g_file_get_basename (path_list->data));
676
message = g_strdup_printf (_("Failed to open file \"%s\""), name);
681
/* we can just tell that n files failed to open */
682
message = g_strdup_printf (ngettext ("Failed to open %d file", "Failed to open %d files", n), n);
685
/* display an error dialog to the user */
686
thunar_dialogs_show_error (launcher->widget, error, "%s", message);
687
g_error_free (error);
691
/* destroy the launch context */
692
g_object_unref (context);
698
thunar_launcher_open_windows (ThunarLauncher *launcher,
701
ThunarApplication *application;
707
gint response = GTK_RESPONSE_YES;
710
/* ask the user if we would open more than one new window */
711
n = g_list_length (directories);
712
if (G_UNLIKELY (n > 1))
714
/* open a message dialog */
715
window = (launcher->widget != NULL) ? gtk_widget_get_toplevel (launcher->widget) : NULL;
716
dialog = gtk_message_dialog_new ((GtkWindow *) window,
717
GTK_DIALOG_DESTROY_WITH_PARENT
719
GTK_MESSAGE_QUESTION,
721
_("Are you sure you want to open all folders?"));
722
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
723
ngettext ("This will open %d separate file manager window.",
724
"This will open %d separate file manager windows.",
727
label = g_strdup_printf (ngettext ("Open %d New Window", "Open %d New Windows", n), n);
728
gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
729
gtk_dialog_add_button (GTK_DIALOG (dialog), label, GTK_RESPONSE_YES);
730
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
731
response = gtk_dialog_run (GTK_DIALOG (dialog));
732
gtk_widget_destroy (dialog);
736
/* open n new windows if the user approved it */
737
if (G_LIKELY (response == GTK_RESPONSE_YES))
739
/* query the application object */
740
application = thunar_application_get ();
742
/* determine the screen on which to open the new windows */
743
screen = (launcher->widget != NULL) ? gtk_widget_get_screen (launcher->widget) : NULL;
745
/* open all requested windows */
746
for (lp = directories; lp != NULL; lp = lp->next)
747
thunar_application_open_window (application, lp->data, screen, NULL);
749
/* release the application object */
750
g_object_unref (G_OBJECT (application));
757
thunar_launcher_update_idle (gpointer data)
759
ThunarLauncher *launcher = THUNAR_LAUNCHER (data);
760
const gchar *context_menu_path;
761
const gchar *file_menu_path;
763
gboolean default_is_open_with_other = FALSE;
770
gint n_directories = 0;
771
gint n_executables = 0;
773
gint n_selected_files = 0;
776
/* verify that we're connected to an UI manager */
777
if (G_UNLIKELY (launcher->ui_manager == NULL))
780
GDK_THREADS_ENTER ();
782
/* drop the previous addons ui controls from the UI manager */
783
if (G_LIKELY (launcher->ui_addons_merge_id != 0))
785
gtk_ui_manager_remove_ui (launcher->ui_manager, launcher->ui_addons_merge_id);
786
gtk_ui_manager_ensure_update (launcher->ui_manager);
787
launcher->ui_addons_merge_id = 0;
790
/* reset the application set for the "Open" action */
791
g_object_set_qdata (G_OBJECT (launcher->action_open), thunar_launcher_handler_quark, NULL);
793
/* determine the number of files/directories/executables */
794
for (lp = launcher->selected_files; lp != NULL; lp = lp->next, ++n_selected_files)
796
if (thunar_file_is_directory (lp->data)
797
|| thunar_file_is_shortcut (lp->data)
798
|| thunar_file_is_mountable (lp->data))
804
if (thunar_file_is_executable (lp->data))
810
/* update the user interface depending on the current selection */
811
if (G_LIKELY (n_selected_files == 0 || n_directories > 0))
813
/** CASE 1: nothing selected or atleast one directory in the selection
815
** - "Open", "Open in n New Windows" and "Open in n New Tabs" actions
818
/* Prepare "Open" label */
819
gtk_action_set_label (launcher->action_open, _("_Open"));
821
if (n_selected_files == n_directories && n_directories >= 1)
823
if (n_directories > 1)
825
/* turn "Open New Window" into "Open in n New Windows" */
826
label = g_strdup_printf (ngettext ("Open in %d New _Window", "Open in %d New _Windows", n_directories), n_directories);
827
tooltip = g_strdup_printf (ngettext ("Open the selected directory in %d new window",
828
"Open the selected directories in %d new windows",
829
n_directories), n_directories);
830
g_object_set (G_OBJECT (launcher->action_open_in_new_window),
837
/* turn "Open in New Tab" into "Open in x New Tabs" */
838
label = g_strdup_printf (ngettext ("Open in %d New _Tab", "Open in %d New _Tabs", n_directories), n_directories);
839
tooltip = g_strdup_printf (ngettext ("Open the selected directory in %d new tab",
840
"Open the selected directories in %d new tabs",
841
n_directories), n_directories);
842
g_object_set (G_OBJECT (launcher->action_open_in_new_tab),
849
else if (n_directories == 1)
851
/* prepare "Open in New Window" */
852
g_object_set (G_OBJECT (launcher->action_open_in_new_window),
853
"label", _("Open in New _Window"),
854
"tooltip", _("Open the selected directory in a new window"),
857
/* prepare "Open in New Tab" */
858
g_object_set (G_OBJECT (launcher->action_open_in_new_tab),
859
"label", _("Open in New _Tab"),
860
"tooltip", _("Open the selected directory in a new tab"),
863
/* set tooltip that makes sence */
864
gtk_action_set_tooltip (launcher->action_open, _("Open the selected directory"));
867
/* Show Window/Tab action if there are only directories selected */
868
gtk_action_set_visible (launcher->action_open_in_new_window, n_directories > 0);
869
gtk_action_set_visible (launcher->action_open_in_new_tab, n_directories > 0);
871
/* Show open if there is exactly 1 directory selected */
872
gtk_action_set_visible (launcher->action_open, n_directories == 1);
873
gtk_action_set_sensitive (launcher->action_open, TRUE);
877
/* Hide New Window and Tab action */
878
gtk_action_set_visible (launcher->action_open_in_new_window, FALSE);
879
gtk_action_set_visible (launcher->action_open_in_new_tab, FALSE);
881
/* Normal open action, because there are also directories included */
882
gtk_action_set_visible (launcher->action_open, TRUE);
883
gtk_action_set_sensitive (launcher->action_open, n_selected_files > 0);
884
gtk_action_set_tooltip (launcher->action_open,
885
ngettext ("Open the selected file",
886
"Open the selected files",
890
/* hide the "Open With Other Application" actions */
891
gtk_action_set_visible (launcher->action_open_with_other, FALSE);
892
gtk_action_set_visible (launcher->action_open_with_other_in_menu, FALSE);
896
/** CASE 2: one or more file in the selection
898
** - "Execute" action if all selected files are executable
899
** - No "Open in n New Windows" action
902
/* drop all previous addon actions from the action group */
903
actions = gtk_action_group_list_actions (launcher->action_group);
904
for (lp = actions; lp != NULL; lp = lp->next)
905
if (strncmp (gtk_action_get_name (lp->data), "thunar-launcher-addon-", 22) == 0)
906
gtk_action_group_remove_action (launcher->action_group, lp->data);
907
g_list_free (actions);
909
/* allocate a new merge id from the UI manager */
910
launcher->ui_addons_merge_id = gtk_ui_manager_new_merge_id (launcher->ui_manager);
912
/* make the "Open" action sensitive */
913
gtk_action_set_sensitive (launcher->action_open, TRUE);
915
/* hide the "Open in n New Windows/Tabs" action */
916
gtk_action_set_visible (launcher->action_open_in_new_window, FALSE);
917
gtk_action_set_visible (launcher->action_open_in_new_tab, FALSE);
919
/* determine the set of applications that work for all selected files */
920
applications = thunar_file_list_get_applications (launcher->selected_files);
922
/* reset the desktop actions list */
925
/* check if we have only executable files in the selection */
926
if (G_UNLIKELY (n_executables == n_selected_files))
928
/* turn the "Open" action into "Execute" */
929
g_object_set (G_OBJECT (launcher->action_open),
930
"label", _("_Execute"),
931
"tooltip", ngettext ("Execute the selected file", "Execute the selected files", n_selected_files),
934
else if (G_LIKELY (applications != NULL))
936
/* turn the "Open" action into "Open With DEFAULT" */
937
label = g_strdup_printf (_("_Open With \"%s\""), g_app_info_get_name (applications->data));
938
tooltip = g_strdup_printf (ngettext ("Use \"%s\" to open the selected file",
939
"Use \"%s\" to open the selected files",
940
n_selected_files), g_app_info_get_name (applications->data));
941
g_object_set (G_OBJECT (launcher->action_open),
948
/* remember the default application for the "Open" action */
949
g_object_set_qdata_full (G_OBJECT (launcher->action_open), thunar_launcher_handler_quark, applications->data, g_object_unref);
951
/* FIXME Add the desktop actions for this application.
952
* Unfortunately this is not supported by GIO directly */
954
/* drop the default application from the list */
955
applications = g_list_delete_link (applications, applications);
957
else if (G_UNLIKELY (n_selected_files == 1))
959
/* turn the "Open" action into "Open With Other Application" */
960
g_object_set (G_OBJECT (launcher->action_open),
961
"label", _("_Open With Other Application..."),
962
"tooltip", _("Choose another application with which to open the selected file"),
964
default_is_open_with_other = TRUE;
968
/* we can only show a generic "Open" action */
969
g_object_set (G_OBJECT (launcher->action_open),
970
"label", _("_Open With Default Applications"),
971
"tooltip", ngettext ("Open the selected file with the default application",
972
"Open the selected files with the default applications", n_selected_files),
976
/* place the other applications in the "Open With" submenu if we have more than 2 other applications, or the
977
* default action for the file is "Execute", in which case the "Open With" actions aren't that relevant either
979
if (G_UNLIKELY (g_list_length (applications) > 2 || n_executables == n_selected_files))
981
/* determine the base paths for the actions */
982
file_menu_path = "/main-menu/file-menu/placeholder-launcher/open-with-menu/placeholder-applications";
983
context_menu_path = "/file-context-menu/placeholder-launcher/open-with-menu/placeholder-applications";
985
/* show the "Open With Other Application" in the submenu and hide the toplevel one */
986
gtk_action_set_visible (launcher->action_open_with_other, FALSE);
987
gtk_action_set_visible (launcher->action_open_with_other_in_menu, (n_selected_files == 1));
991
/* determine the base paths for the actions */
992
file_menu_path = "/main-menu/file-menu/placeholder-launcher/placeholder-applications";
993
context_menu_path = "/file-context-menu/placeholder-launcher/placeholder-applications";
995
/* add a separator if we have more than one additional application */
996
if (G_LIKELY (applications != NULL))
998
/* add separator after the DEFAULT/execute action */
999
gtk_ui_manager_add_ui (launcher->ui_manager, launcher->ui_addons_merge_id,
1000
file_menu_path, "separator", NULL,
1001
GTK_UI_MANAGER_SEPARATOR, FALSE);
1002
gtk_ui_manager_add_ui (launcher->ui_manager, launcher->ui_addons_merge_id,
1003
context_menu_path, "separator", NULL,
1004
GTK_UI_MANAGER_SEPARATOR, FALSE);
1007
/* show the toplevel "Open With Other Application" (if not already done by the "Open" action) */
1008
gtk_action_set_visible (launcher->action_open_with_other, !default_is_open_with_other && (n_selected_files == 1));
1009
gtk_action_set_visible (launcher->action_open_with_other_in_menu, FALSE);
1012
/* add actions for all remaining applications */
1013
if (G_LIKELY (applications != NULL))
1015
/* process all applications and determine the desktop actions */
1016
for (lp = applications, n = 0; lp != NULL; lp = lp->next, ++n)
1018
/* FIXME Determine the desktop actions for this application.
1019
* Unfortunately this is not supported by GIO directly. */
1021
/* generate a unique label, unique id and tooltip for the application's action */
1022
name = g_strdup_printf ("thunar-launcher-addon-application%d-%p", n, launcher);
1023
label = g_strdup_printf (_("Open With \"%s\""), g_app_info_get_name (lp->data));
1024
tooltip = g_strdup_printf (ngettext ("Use \"%s\" to open the selected file",
1025
"Use \"%s\" to open the selected files",
1026
n_selected_files), g_app_info_get_name (lp->data));
1028
/* allocate a new action for the application */
1029
action = gtk_action_new (name, label, tooltip, NULL);
1030
gtk_action_set_gicon (action, g_app_info_get_icon (lp->data));
1031
gtk_action_group_add_action (launcher->action_group, action);
1032
g_object_set_qdata_full (G_OBJECT (action), thunar_launcher_handler_quark, lp->data, g_object_unref);
1033
g_signal_connect (G_OBJECT (action), "activate", G_CALLBACK (thunar_launcher_action_open), launcher);
1034
gtk_ui_manager_add_ui (launcher->ui_manager, launcher->ui_addons_merge_id,
1035
file_menu_path, name, name,
1036
GTK_UI_MANAGER_MENUITEM, FALSE);
1037
gtk_ui_manager_add_ui (launcher->ui_manager, launcher->ui_addons_merge_id,
1038
context_menu_path, name, name,
1039
GTK_UI_MANAGER_MENUITEM, FALSE);
1040
g_object_unref (G_OBJECT (action));
1049
g_list_free (applications);
1052
/* FIXME Add desktop actions here. Unfortunately they are not supported by
1053
* GIO, so we'll have to roll our own thing here */
1056
/* schedule an update of the "Send To" menu */
1057
if (G_LIKELY (launcher->sendto_idle_id == 0))
1059
launcher->sendto_idle_id = g_idle_add_full (G_PRIORITY_LOW, thunar_launcher_sendto_idle,
1060
launcher, thunar_launcher_sendto_idle_destroy);
1063
GDK_THREADS_LEAVE ();
1071
thunar_launcher_update_idle_destroy (gpointer data)
1073
THUNAR_LAUNCHER (data)->launcher_idle_id = 0;
1079
thunar_launcher_update_check (ThunarLauncher *launcher,
1082
_thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
1083
_thunar_return_if_fail (menu == NULL || GTK_IS_MENU (menu));
1085
/* check if the menu is in a dirty state */
1086
if (launcher->launcher_idle_id != 0)
1088
/* stop the timeout */
1089
g_source_remove (launcher->launcher_idle_id);
1091
/* force an update */
1092
thunar_launcher_update_idle (launcher);
1095
gtk_ui_manager_ensure_update (launcher->ui_manager);
1097
/* make sure the menu is positioned correctly after the
1098
* interface update */
1100
gtk_menu_reposition (GTK_MENU (menu));
1107
thunar_launcher_update (ThunarLauncher *launcher)
1109
GSList *proxies, *lp;
1111
gboolean instant_update;
1113
_thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
1115
proxies = gtk_action_get_proxies (launcher->action_open);
1116
instant_update = (proxies == NULL);
1117
for (lp = proxies; lp != NULL; lp = lp->next)
1119
menu = gtk_widget_get_ancestor (lp->data, GTK_TYPE_MENU);
1120
if (G_LIKELY (menu != NULL))
1122
/* instant update if a menu is visible */
1123
if (gtk_widget_get_visible (menu))
1124
instant_update = TRUE;
1126
/* watch menu changes */
1127
g_signal_handlers_disconnect_by_func (G_OBJECT (menu), G_CALLBACK (thunar_launcher_update_check), launcher);
1128
g_signal_connect_swapped (G_OBJECT (menu), "show", G_CALLBACK (thunar_launcher_update_check), launcher);
1132
/* stop pending timeouts */
1133
if (launcher->launcher_idle_id != 0)
1134
g_source_remove (launcher->launcher_idle_id);
1138
/* directly update without interruption */
1139
thunar_launcher_update_idle (launcher);
1143
/* assume all actions are working */
1144
gtk_action_set_sensitive (launcher->action_open, TRUE);
1145
gtk_action_set_visible (launcher->action_open_with_other, TRUE);
1146
gtk_action_set_visible (launcher->action_open_in_new_window, TRUE);
1147
gtk_action_set_visible (launcher->action_open_in_new_tab, TRUE);
1148
gtk_action_set_visible (launcher->action_open_with_other_in_menu, TRUE);
1150
/* delayed update */
1151
launcher->launcher_idle_id = g_timeout_add_seconds_full (G_PRIORITY_LOW, 5, thunar_launcher_update_idle,
1152
launcher, thunar_launcher_update_idle_destroy);
1159
thunar_launcher_open_file (ThunarLauncher *launcher,
1164
_thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
1165
_thunar_return_if_fail (THUNAR_IS_FILE (file));
1171
if (thunar_file_is_directory (file))
1173
/* check if we're in a regular view (i.e. current_directory is set) */
1174
if (G_LIKELY (launcher->current_directory != NULL))
1176
/* we want to open one directory, so just emit "change-directory" here */
1177
thunar_navigator_change_directory (THUNAR_NAVIGATOR (launcher), file);
1181
/* open the selected directory in a new window */
1182
thunar_launcher_open_windows (launcher, &files);
1187
if (thunar_file_is_executable (file))
1189
/* try to execute the file */
1190
thunar_launcher_execute_files (launcher, &files);
1194
/* try to open the file using its default application */
1195
thunar_launcher_open_files (launcher, &files);
1203
thunar_launcher_poke_file_finish (ThunarBrowser *browser,
1205
ThunarFile *target_file,
1211
thunar_launcher_open_file (THUNAR_LAUNCHER (browser), target_file);
1215
thunar_dialogs_show_error (THUNAR_LAUNCHER (browser)->widget, error,
1216
_("Failed to open \"%s\""),
1217
thunar_file_get_display_name (file));
1224
thunar_launcher_poke_files (ThunarLauncher *launcher,
1225
ThunarLauncherPokeData *poke_data)
1227
_thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
1228
_thunar_return_if_fail (poke_data != NULL);
1229
_thunar_return_if_fail (poke_data->files != NULL);
1231
thunar_browser_poke_file (THUNAR_BROWSER (launcher), poke_data->files->data,
1232
launcher->widget, thunar_launcher_poke_files_finish,
1239
thunar_launcher_poke_files_finish (ThunarBrowser *browser,
1241
ThunarFile *target_file,
1245
ThunarLauncherPokeData *poke_data = user_data;
1246
gboolean executable = TRUE;
1247
GList *directories = NULL;
1248
GList *files = NULL;
1251
_thunar_return_if_fail (THUNAR_IS_BROWSER (browser));
1252
_thunar_return_if_fail (THUNAR_IS_FILE (file));
1253
_thunar_return_if_fail (poke_data != NULL);
1254
_thunar_return_if_fail (poke_data->files != NULL);
1256
/* check if poking succeeded */
1259
/* add the resolved file to the list of file to be opened/executed later */
1260
poke_data->resolved_files = g_list_prepend (poke_data->resolved_files,
1261
g_object_ref (target_file));
1264
/* release and remove the just poked file from the list */
1265
g_object_unref (poke_data->files->data);
1266
poke_data->files = g_list_delete_link (poke_data->files, poke_data->files);
1268
if (poke_data->files == NULL)
1270
/* separate files and directories in the selected files list */
1271
for (lp = poke_data->resolved_files; lp != NULL; lp = lp->next)
1273
if (thunar_file_is_directory (lp->data))
1275
/* add to our directory list */
1276
directories = g_list_prepend (directories, lp->data);
1280
/* add to our file list */
1281
files = g_list_prepend (files, lp->data);
1283
/* check if the file is executable */
1284
executable = (executable && thunar_file_is_executable (lp->data));
1288
/* check if we have any directories to process */
1289
if (G_LIKELY (directories != NULL))
1291
if (poke_data->directories_in_tabs)
1294
for (lp = directories; lp != NULL; lp = lp->next)
1295
thunar_navigator_open_new_tab (THUNAR_NAVIGATOR (browser), lp->data);
1299
/* open new windows for all directories */
1300
thunar_launcher_open_windows (THUNAR_LAUNCHER (browser), directories);
1302
g_list_free (directories);
1305
/* check if we have any files to process */
1306
if (G_LIKELY (files != NULL))
1308
/* if all files are executable, we just run them here */
1309
if (G_UNLIKELY (executable))
1311
/* try to execute all given files */
1312
thunar_launcher_execute_files (THUNAR_LAUNCHER (browser), files);
1316
/* try to open all files using their default applications */
1317
thunar_launcher_open_files (THUNAR_LAUNCHER (browser), files);
1321
g_list_free (files);
1324
/* free all files allocated for the poke data */
1325
thunar_launcher_poke_data_free (poke_data);
1329
/* we need to continue this until all files have been resolved */
1330
thunar_launcher_poke_files (THUNAR_LAUNCHER (browser), poke_data);
1337
thunar_launcher_action_open (GtkAction *action,
1338
ThunarLauncher *launcher)
1340
ThunarLauncherPokeData *poke_data;
1342
GList *selected_paths;
1344
_thunar_return_if_fail (GTK_IS_ACTION (action));
1345
_thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
1347
/* force update if still dirty */
1348
thunar_launcher_update_check (launcher, NULL);
1349
if (!gtk_action_get_sensitive (action))
1352
/* check if we have a mime handler associated with the action */
1353
app_info = g_object_get_qdata (G_OBJECT (action), thunar_launcher_handler_quark);
1354
if (G_LIKELY (app_info != NULL))
1356
/* try to open the selected files using the given application */
1357
selected_paths = thunar_file_list_to_thunar_g_file_list (launcher->selected_files);
1358
thunar_launcher_open_paths (app_info, selected_paths, launcher);
1359
thunar_g_file_list_free (selected_paths);
1361
else if (launcher->selected_files != NULL)
1363
if (launcher->selected_files->next == NULL)
1365
thunar_browser_poke_file (THUNAR_BROWSER (launcher),
1366
launcher->selected_files->data, launcher->widget,
1367
thunar_launcher_poke_file_finish, NULL);
1371
/* resolve files one after another until none is left. Open/execute
1372
* the resolved files/directories when all this is done at a later
1374
poke_data = thunar_launcher_poke_data_new (launcher->selected_files);
1375
thunar_launcher_poke_files (launcher, poke_data);
1383
thunar_launcher_action_open_with_other (GtkAction *action,
1384
ThunarLauncher *launcher)
1386
_thunar_return_if_fail (GTK_IS_ACTION (action));
1387
_thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
1389
/* force update if still dirty */
1390
thunar_launcher_update_check (launcher, NULL);
1391
if (!gtk_action_get_visible (action))
1394
/* verify that we have atleast one selected file */
1395
if (G_LIKELY (launcher->selected_files != NULL))
1397
/* popup the chooser dialog for the first selected file */
1398
thunar_show_chooser_dialog (launcher->widget, launcher->selected_files->data, TRUE);
1405
thunar_launcher_action_open_in_new_window (GtkAction *action,
1406
ThunarLauncher *launcher)
1408
ThunarLauncherPokeData *poke_data;
1410
_thunar_return_if_fail (GTK_IS_ACTION (action));
1411
_thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
1413
/* force update if still dirty */
1414
thunar_launcher_update_check (launcher, NULL);
1415
if (!gtk_action_get_visible (action))
1418
/* open the selected directories in new windows */
1419
poke_data = thunar_launcher_poke_data_new (launcher->selected_files);
1420
thunar_launcher_poke_files (launcher, poke_data);
1426
thunar_launcher_action_open_in_new_tab (GtkAction *action,
1427
ThunarLauncher *launcher)
1429
ThunarLauncherPokeData *poke_data;
1431
_thunar_return_if_fail (GTK_IS_ACTION (action));
1432
_thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
1434
/* force update if still dirty */
1435
thunar_launcher_update_check (launcher, NULL);
1436
if (!gtk_action_get_visible (action))
1439
/* open all selected directories in a new tab */
1440
poke_data = thunar_launcher_poke_data_new (launcher->selected_files);
1441
poke_data->directories_in_tabs = TRUE;
1442
thunar_launcher_poke_files (launcher, poke_data);
1448
thunar_launcher_action_sendto_desktop (GtkAction *action,
1449
ThunarLauncher *launcher)
1451
ThunarApplication *application;
1452
GFile *desktop_file;
1455
_thunar_return_if_fail (GTK_IS_ACTION (action));
1456
_thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
1458
/* determine the source files */
1459
files = thunar_file_list_to_thunar_g_file_list (launcher->selected_files);
1460
if (G_UNLIKELY (files == NULL))
1463
/* determine the file to the ~/Desktop folder */
1464
desktop_file = thunar_g_file_new_for_desktop ();
1466
/* launch the link job */
1467
application = thunar_application_get ();
1468
thunar_application_link_into (application, launcher->widget, files, desktop_file, NULL);
1469
g_object_unref (G_OBJECT (application));
1472
g_object_unref (desktop_file);
1473
thunar_g_file_list_free (files);
1478
static ThunarLauncherMountData *
1479
thunar_launcher_mount_data_new (ThunarLauncher *launcher,
1482
ThunarLauncherMountData *data;
1484
_thunar_return_val_if_fail (THUNAR_IS_LAUNCHER (launcher), NULL);
1486
data = g_slice_new0 (ThunarLauncherMountData);
1487
data->launcher = g_object_ref (launcher);
1488
data->files = thunar_g_file_list_copy (files);
1496
thunar_launcher_mount_data_free (ThunarLauncherMountData *data)
1498
_thunar_return_if_fail (data != NULL);
1499
_thunar_return_if_fail (THUNAR_IS_LAUNCHER (data->launcher));
1501
g_object_unref (data->launcher);
1502
thunar_g_file_list_free (data->files);
1503
g_slice_free (ThunarLauncherMountData, data);
1508
static ThunarLauncherPokeData *
1509
thunar_launcher_poke_data_new (GList *files)
1511
ThunarLauncherPokeData *data;
1513
data = g_slice_new0 (ThunarLauncherPokeData);
1514
data->files = thunar_g_file_list_copy (files);
1515
data->resolved_files = NULL;
1516
data->directories_in_tabs = FALSE;
1524
thunar_launcher_poke_data_free (ThunarLauncherPokeData *data)
1526
_thunar_return_if_fail (data != NULL);
1528
thunar_g_file_list_free (data->files);
1529
thunar_g_file_list_free (data->resolved_files);
1530
g_slice_free (ThunarLauncherPokeData, data);
1536
thunar_launcher_sendto_device (ThunarLauncher *launcher,
1537
ThunarDevice *device,
1540
ThunarApplication *application;
1543
_thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
1544
_thunar_return_if_fail (THUNAR_IS_DEVICE (device));
1546
if (!thunar_device_is_mounted (device))
1549
mount_point = thunar_device_get_root (device);
1550
if (mount_point != NULL)
1552
/* copy the files onto the specified device */
1553
application = thunar_application_get ();
1554
thunar_application_copy_into (application, launcher->widget, files, mount_point, NULL);
1555
g_object_unref (application);
1557
g_object_unref (mount_point);
1564
thunar_launcher_sendto_mount_finish (ThunarDevice *device,
1565
const GError *error,
1568
ThunarLauncherMountData *data = user_data;
1571
_thunar_return_if_fail (THUNAR_IS_DEVICE (device));
1572
_thunar_return_if_fail (user_data != NULL);
1573
_thunar_return_if_fail (THUNAR_IS_LAUNCHER (data->launcher));
1577
/* tell the user that we were unable to mount the device, which is
1578
* required to send files to it */
1579
device_name = thunar_device_get_name (device);
1580
thunar_dialogs_show_error (data->launcher->widget, error, _("Failed to mount \"%s\""), device_name);
1581
g_free (device_name);
1585
thunar_launcher_sendto_device (data->launcher, device, data->files);
1588
thunar_launcher_mount_data_free (data);
1594
thunar_launcher_action_sendto_device (GtkAction *action,
1595
ThunarLauncher *launcher)
1597
ThunarLauncherMountData *data;
1598
GMountOperation *mount_operation;
1599
ThunarDevice *device;
1602
_thunar_return_if_fail (GTK_IS_ACTION (action));
1603
_thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
1605
/* determine the source paths */
1606
files = thunar_file_list_to_thunar_g_file_list (launcher->selected_files);
1607
if (G_UNLIKELY (files == NULL))
1610
/* determine the device to which to send */
1611
device = g_object_get_qdata (G_OBJECT (action), thunar_launcher_handler_quark);
1612
if (G_UNLIKELY (device == NULL))
1615
/* make sure to mount the device first, if it's not already mounted */
1616
if (!thunar_device_is_mounted (device))
1618
/* allocate mount data */
1619
data = thunar_launcher_mount_data_new (launcher, files);
1621
/* allocate a GTK+ mount operation */
1622
mount_operation = thunar_gtk_mount_operation_new (launcher->widget);
1624
/* try to mount the device and later start sending the files */
1625
thunar_device_mount (device,
1628
thunar_launcher_sendto_mount_finish,
1631
g_object_unref (mount_operation);
1635
thunar_launcher_sendto_device (launcher, device, files);
1639
thunar_g_file_list_free (files);
1645
thunar_launcher_widget_destroyed (ThunarLauncher *launcher,
1648
_thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
1649
_thunar_return_if_fail (launcher->widget == widget);
1650
_thunar_return_if_fail (GTK_IS_WIDGET (widget));
1652
/* just reset the widget property for the launcher */
1653
thunar_launcher_set_widget (launcher, NULL);
1659
thunar_launcher_sendto_idle (gpointer user_data)
1661
ThunarLauncher *launcher = THUNAR_LAUNCHER (user_data);
1664
gboolean linkable = TRUE;
1672
gint n_selected_files;
1674
gboolean got_devices = FALSE;
1675
const gchar *file_menu_path;
1676
const gchar *context_menu_path;
1678
/* verify that we have an UI manager */
1679
if (launcher->ui_manager == NULL)
1682
GDK_THREADS_ENTER ();
1684
/* determine the number of selected files and check whether atleast one of these
1685
* files is located in the trash (to en-/disable the "sendto-desktop" action).
1687
for (lp = launcher->selected_files, n_selected_files = 0; lp != NULL; lp = lp->next, ++n_selected_files)
1689
/* check if this file is in trash */
1690
if (G_UNLIKELY (linkable))
1691
linkable = !thunar_file_is_trashed (lp->data);
1694
/* update the "Desktop (Create Link)" sendto action */
1695
action = gtk_action_group_get_action (launcher->action_group, "sendto-desktop");
1696
g_object_set (G_OBJECT (action),
1697
"label", ngettext ("Desktop (Create Link)", "Desktop (Create Links)", n_selected_files),
1698
"tooltip", ngettext ("Create a link to the selected file on the desktop",
1699
"Create links to the selected files on the desktop",
1701
"visible", (linkable && n_selected_files > 0),
1704
/* re-add the content to "Send To" if we have any files */
1705
if (G_LIKELY (n_selected_files > 0))
1707
/* drop all previous sendto actions from the action group */
1708
handlers = gtk_action_group_list_actions (launcher->action_group);
1709
for (lp = handlers; lp != NULL; lp = lp->next)
1710
if (strncmp (gtk_action_get_name (lp->data), "thunar-launcher-sendto", 22) == 0)
1711
gtk_action_group_remove_action (launcher->action_group, lp->data);
1712
g_list_free (handlers);
1714
/* allocate a new merge id from the UI manager (if not already done) */
1715
if (G_UNLIKELY (launcher->ui_addons_merge_id == 0))
1716
launcher->ui_addons_merge_id = gtk_ui_manager_new_merge_id (launcher->ui_manager);
1718
/* determine the currently active devices */
1719
devices = thunar_device_monitor_get_devices (launcher->device_monitor);
1720
got_devices = (devices != NULL);
1723
file_menu_path = "/main-menu/file-menu/sendto-menu/placeholder-sendto-actions";
1724
context_menu_path = "/file-context-menu/sendto-menu/placeholder-sendto-actions";
1726
/* add removable (and writable) drives and media */
1727
for (lp = devices; lp != NULL; lp = lp->next, ++n)
1729
/* generate a unique name and tooltip for the device */
1730
device_name = thunar_device_get_name (lp->data);
1731
name = g_strdup_printf ("thunar-launcher-sendto%d-%p", n, launcher);
1732
tooltip = g_strdup_printf (ngettext ("Send the selected file to \"%s\"",
1733
"Send the selected files to \"%s\"",
1734
n_selected_files), device_name);
1736
/* allocate a new action for the device */
1737
action = gtk_action_new (name, device_name, tooltip, NULL);
1738
g_object_set_qdata_full (G_OBJECT (action), thunar_launcher_handler_quark, lp->data, g_object_unref);
1739
g_signal_connect (G_OBJECT (action), "activate", G_CALLBACK (thunar_launcher_action_sendto_device), launcher);
1740
gtk_action_group_add_action (launcher->action_group, action);
1741
gtk_ui_manager_add_ui (launcher->ui_manager, launcher->ui_addons_merge_id,
1742
file_menu_path, name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
1743
gtk_ui_manager_add_ui (launcher->ui_manager, launcher->ui_addons_merge_id,
1744
context_menu_path, name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
1745
g_object_unref (action);
1747
icon = thunar_device_get_icon (lp->data);
1748
if (G_LIKELY (icon != NULL))
1750
gtk_action_set_gicon (action, icon);
1751
g_object_unref (icon);
1757
g_free (device_name);
1760
/* free the devices list */
1761
g_list_free (devices);
1763
/* determine the sendto handlers for the selected files */
1764
handlers = thunar_sendto_model_get_matching (launcher->sendto_model, launcher->selected_files);
1765
if (G_LIKELY (handlers != NULL))
1769
/* add separator between the devices and actions action */
1770
gtk_ui_manager_add_ui (launcher->ui_manager, launcher->ui_addons_merge_id,
1771
file_menu_path, "separator", NULL,
1772
GTK_UI_MANAGER_SEPARATOR, FALSE);
1773
gtk_ui_manager_add_ui (launcher->ui_manager, launcher->ui_addons_merge_id,
1774
context_menu_path, "separator", NULL,
1775
GTK_UI_MANAGER_SEPARATOR, FALSE);
1778
/* add all handlers to the user interface */
1779
for (lp = handlers; lp != NULL; lp = lp->next, ++n)
1781
/* generate a unique name and tooltip for the handler */
1782
label = g_app_info_get_name (lp->data);
1783
name = g_strdup_printf ("thunar-launcher-sendto%d-%p", n, launcher);
1784
tooltip = g_strdup_printf (ngettext ("Send the selected file to \"%s\"",
1785
"Send the selected files to \"%s\"",
1786
n_selected_files), label);
1788
/* allocate a new action for the handler */
1789
action = gtk_action_new (name, label, tooltip, NULL);
1790
gtk_action_set_gicon (action, g_app_info_get_icon (lp->data));
1791
g_object_set_qdata_full (G_OBJECT (action), thunar_launcher_handler_quark, lp->data, g_object_unref);
1792
g_signal_connect (G_OBJECT (action), "activate", G_CALLBACK (thunar_launcher_action_open), launcher);
1793
gtk_action_group_add_action (launcher->action_group, action);
1794
gtk_ui_manager_add_ui (launcher->ui_manager, launcher->ui_addons_merge_id,
1795
file_menu_path, name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
1796
gtk_ui_manager_add_ui (launcher->ui_manager, launcher->ui_addons_merge_id,
1797
context_menu_path, name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
1798
g_object_unref (G_OBJECT (action));
1805
/* release the handler list */
1806
g_list_free (handlers);
1810
GDK_THREADS_LEAVE ();
1818
thunar_launcher_sendto_idle_destroy (gpointer user_data)
1820
THUNAR_LAUNCHER (user_data)->sendto_idle_id = 0;
1826
* thunar_launcher_new:
1828
* Allocates a new #ThunarLauncher instance.
1830
* Return value: the newly allocated #ThunarLauncher.
1833
thunar_launcher_new (void)
1835
return g_object_new (THUNAR_TYPE_LAUNCHER, NULL);
1841
* thunar_launcher_get_widget:
1842
* @launcher : a #ThunarLauncher.
1844
* Returns the #GtkWidget currently associated with @launcher.
1846
* Return value: the widget associated with @launcher.
1849
thunar_launcher_get_widget (const ThunarLauncher *launcher)
1851
_thunar_return_val_if_fail (THUNAR_IS_LAUNCHER (launcher), NULL);
1852
return launcher->widget;
1858
* thunar_launcher_set_widget:
1859
* @launcher : a #ThunarLauncher.
1860
* @widget : a #GtkWidget or %NULL.
1862
* Associates @launcher with @widget.
1865
thunar_launcher_set_widget (ThunarLauncher *launcher,
1868
_thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
1869
_thunar_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget));
1871
/* disconnect from the previous widget */
1872
if (G_UNLIKELY (launcher->widget != NULL))
1874
g_signal_handlers_disconnect_by_func (G_OBJECT (launcher->widget), thunar_launcher_widget_destroyed, launcher);
1875
g_object_unref (G_OBJECT (launcher->widget));
1878
/* activate the new widget */
1879
launcher->widget = widget;
1881
/* connect to the new widget */
1882
if (G_LIKELY (widget != NULL))
1884
g_object_ref (G_OBJECT (widget));
1885
g_signal_connect_swapped (G_OBJECT (widget), "destroy", G_CALLBACK (thunar_launcher_widget_destroyed), launcher);
1888
/* notify listeners */
1889
g_object_notify_by_pspec (G_OBJECT (launcher), launcher_props[PROP_WIDGET]);