~ubuntu-branches/ubuntu/utopic/thunar/utopic-proposed

« back to all changes in this revision

Viewing changes to .pc/menu-icon-tweaks.patch/thunar/thunar-launcher.c

  • Committer: Package Import Robot
  • Author(s): Jackson Doak
  • Date: 2014-04-05 08:22:40 UTC
  • Revision ID: package-import@ubuntu.com-20140405082240-b3iiyypyyo3uliy3
Tags: 1.6.3-1ubuntu5
* Add git-save-keyboard-shortcuts.patch. LP: #1186846
  - Keyboard shortcuts save when changed rather than when thunar is closed
* Add menu-icon-tweaks.patch. LP: #1271861
  - Don't use generic folder icons for open with default application
  - Add icons for open in new tab, new window
* Add git-force-toolbr-icons.patch
  - Fix for "Home" and "Open Parent" icons going missing from toolbar

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id$ */
 
2
/*-
 
3
 * Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org>
 
4
 * Copyright (c) 2009 Jannis Pohlmann <jannis@xfce.org>
 
5
 *
 
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)
 
9
 * any later version.
 
10
 *
 
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
 
14
 * more details.
 
15
 *
 
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
 
19
 */
 
20
 
 
21
#ifdef HAVE_CONFIG_H
 
22
#include <config.h>
 
23
#endif
 
24
 
 
25
#ifdef HAVE_MEMORY_H
 
26
#include <memory.h>
 
27
#endif
 
28
#ifdef HAVE_STRING_H
 
29
#include <string.h>
 
30
#endif
 
31
 
 
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>
 
46
 
 
47
 
 
48
 
 
49
typedef struct _ThunarLauncherMountData ThunarLauncherMountData;
 
50
typedef struct _ThunarLauncherPokeData ThunarLauncherPokeData;
 
51
 
 
52
 
 
53
 
 
54
/* Property identifiers */
 
55
enum
 
56
{
 
57
  PROP_0,
 
58
  PROP_CURRENT_DIRECTORY,
 
59
  PROP_SELECTED_FILES,
 
60
  PROP_UI_MANAGER,
 
61
  PROP_WIDGET,
 
62
  N_PROPERTIES
 
63
};
 
64
 
 
65
 
 
66
 
 
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,
 
72
                                                                           guint                     prop_id,
 
73
                                                                           GValue                   *value,
 
74
                                                                           GParamSpec               *pspec);
 
75
static void                    thunar_launcher_set_property               (GObject                  *object,
 
76
                                                                           guint                     prop_id,
 
77
                                                                           const GValue             *value,
 
78
                                                                           GParamSpec               *pspec);
 
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,
 
89
                                                                           GList                    *files);
 
90
static void                    thunar_launcher_open_files                 (ThunarLauncher           *launcher,
 
91
                                                                           GList                    *files);
 
92
static void                    thunar_launcher_open_paths                 (GAppInfo                 *app_info,
 
93
                                                                           GList                    *file_list,
 
94
                                                                           ThunarLauncher           *launcher);
 
95
static void                    thunar_launcher_open_windows               (ThunarLauncher           *launcher,
 
96
                                                                           GList                    *directories);
 
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,
 
111
                                                                           GtkWidget                *widget);
 
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,
 
118
                                                                           ThunarFile               *file,
 
119
                                                                           ThunarFile               *target_file,
 
120
                                                                           GError                   *error,
 
121
                                                                           gpointer                  user_data);
 
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);
 
125
 
 
126
 
 
127
struct _ThunarLauncherClass
 
128
{
 
129
  GObjectClass __parent__;
 
130
};
 
131
 
 
132
struct _ThunarLauncher
 
133
{
 
134
  GObject __parent__;
 
135
 
 
136
  ThunarFile             *current_directory;
 
137
  GList                  *selected_files;
 
138
 
 
139
  guint                   launcher_idle_id;
 
140
 
 
141
  GtkIconFactory         *icon_factory;
 
142
  GtkActionGroup         *action_group;
 
143
  GtkUIManager           *ui_manager;
 
144
  guint                   ui_merge_id;
 
145
  guint                   ui_addons_merge_id;
 
146
 
 
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;
 
152
 
 
153
  GtkWidget              *widget;
 
154
 
 
155
  ThunarDeviceMonitor    *device_monitor;
 
156
  ThunarSendtoModel      *sendto_model;
 
157
  guint                   sendto_idle_id;
 
158
};
 
159
 
 
160
struct _ThunarLauncherMountData
 
161
{
 
162
  ThunarLauncher *launcher;
 
163
  GList          *files;
 
164
};
 
165
 
 
166
struct _ThunarLauncherPokeData
 
167
{
 
168
  GList *files;
 
169
  GList *resolved_files;
 
170
  guint  directories_in_tabs : 1;
 
171
};
 
172
 
 
173
 
 
174
 
 
175
static const GtkActionEntry action_entries[] =
 
176
{
 
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), },
 
184
};
 
185
 
 
186
static GQuark thunar_launcher_handler_quark;
 
187
 
 
188
 
 
189
 
 
190
static GParamSpec *launcher_props[N_PROPERTIES] = { NULL, };
 
191
 
 
192
 
 
193
 
 
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))
 
198
 
 
199
 
 
200
 
 
201
static void
 
202
thunar_launcher_class_init (ThunarLauncherClass *klass)
 
203
{
 
204
  GObjectClass *gobject_class;
 
205
  gpointer      g_iface;
 
206
 
 
207
  /* determine the "thunar-launcher-handler" quark */
 
208
  thunar_launcher_handler_quark = g_quark_from_static_string ("thunar-launcher-handler");
 
209
 
 
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;
 
215
 
 
216
  /**
 
217
   * ThunarLauncher:widget:
 
218
   *
 
219
   * The #GtkWidget with which this launcher is associated.
 
220
   **/
 
221
  launcher_props[PROP_WIDGET] =
 
222
      g_param_spec_object ("widget",
 
223
                           "widget",
 
224
                           "widget",
 
225
                           GTK_TYPE_WIDGET,
 
226
                           EXO_PARAM_READWRITE);
 
227
 
 
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"));
 
233
 
 
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"));
 
239
 
 
240
  launcher_props[PROP_UI_MANAGER] =
 
241
      g_param_spec_override ("ui-manager",
 
242
                             g_object_interface_find_property (g_iface, "ui-manager"));
 
243
 
 
244
  /* install properties */
 
245
  g_object_class_install_properties (gobject_class, N_PROPERTIES, launcher_props);
 
246
}
 
247
 
 
248
 
 
249
 
 
250
static void
 
251
thunar_launcher_component_init (ThunarComponentIface *iface)
 
252
{
 
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;
 
257
}
 
258
 
 
259
 
 
260
 
 
261
static void
 
262
thunar_launcher_navigator_init (ThunarNavigatorIface *iface)
 
263
{
 
264
  iface->get_current_directory = thunar_launcher_get_current_directory;
 
265
  iface->set_current_directory = thunar_launcher_set_current_directory;
 
266
}
 
267
 
 
268
 
 
269
 
 
270
static void
 
271
thunar_launcher_init (ThunarLauncher *launcher)
 
272
{
 
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);
 
277
 
 
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");
 
284
 
 
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);
 
288
 
 
289
  /* setup the "Send To" support */
 
290
  launcher->sendto_model = thunar_sendto_model_get_default ();
 
291
 
 
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);
 
296
}
 
297
 
 
298
 
 
299
 
 
300
static void
 
301
thunar_launcher_dispose (GObject *object)
 
302
{
 
303
  ThunarLauncher *launcher = THUNAR_LAUNCHER (object);
 
304
 
 
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);
 
309
 
 
310
  /* disconnect from the currently selected files */
 
311
  thunar_g_file_list_free (launcher->selected_files);
 
312
  launcher->selected_files = NULL;
 
313
 
 
314
  (*G_OBJECT_CLASS (thunar_launcher_parent_class)->dispose) (object);
 
315
}
 
316
 
 
317
 
 
318
 
 
319
static void
 
320
thunar_launcher_finalize (GObject *object)
 
321
{
 
322
  ThunarLauncher *launcher = THUNAR_LAUNCHER (object);
 
323
 
 
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);
 
327
 
 
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);
 
331
 
 
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);
 
335
 
 
336
  /* release the reference on the action group */
 
337
  g_object_unref (launcher->action_group);
 
338
 
 
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);
 
342
 
 
343
  /* release the reference on the sendto model */
 
344
  g_object_unref (launcher->sendto_model);
 
345
 
 
346
  (*G_OBJECT_CLASS (thunar_launcher_parent_class)->finalize) (object);
 
347
}
 
348
 
 
349
 
 
350
 
 
351
static void
 
352
thunar_launcher_get_property (GObject    *object,
 
353
                              guint       prop_id,
 
354
                              GValue     *value,
 
355
                              GParamSpec *pspec)
 
356
{
 
357
  switch (prop_id)
 
358
    {
 
359
    case PROP_CURRENT_DIRECTORY:
 
360
      g_value_set_object (value, thunar_navigator_get_current_directory (THUNAR_NAVIGATOR (object)));
 
361
      break;
 
362
 
 
363
    case PROP_SELECTED_FILES:
 
364
      g_value_set_boxed (value, thunar_component_get_selected_files (THUNAR_COMPONENT (object)));
 
365
      break;
 
366
 
 
367
    case PROP_UI_MANAGER:
 
368
      g_value_set_object (value, thunar_component_get_ui_manager (THUNAR_COMPONENT (object)));
 
369
      break;
 
370
 
 
371
    case PROP_WIDGET:
 
372
      g_value_set_object (value, thunar_launcher_get_widget (THUNAR_LAUNCHER (object)));
 
373
      break;
 
374
 
 
375
    default:
 
376
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
377
      break;
 
378
    }
 
379
}
 
380
 
 
381
 
 
382
 
 
383
static void
 
384
thunar_launcher_set_property (GObject      *object,
 
385
                              guint         prop_id,
 
386
                              const GValue *value,
 
387
                              GParamSpec   *pspec)
 
388
{
 
389
  switch (prop_id)
 
390
    {
 
391
    case PROP_CURRENT_DIRECTORY:
 
392
      thunar_navigator_set_current_directory (THUNAR_NAVIGATOR (object), g_value_get_object (value));
 
393
      break;
 
394
 
 
395
    case PROP_SELECTED_FILES:
 
396
      thunar_component_set_selected_files (THUNAR_COMPONENT (object), g_value_get_boxed (value));
 
397
      break;
 
398
 
 
399
    case PROP_UI_MANAGER:
 
400
      thunar_component_set_ui_manager (THUNAR_COMPONENT (object), g_value_get_object (value));
 
401
      break;
 
402
 
 
403
    case PROP_WIDGET:
 
404
      thunar_launcher_set_widget (THUNAR_LAUNCHER (object), g_value_get_object (value));
 
405
      break;
 
406
 
 
407
    default:
 
408
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
409
      break;
 
410
    }
 
411
}
 
412
 
 
413
 
 
414
 
 
415
static ThunarFile*
 
416
thunar_launcher_get_current_directory (ThunarNavigator *navigator)
 
417
{
 
418
  return THUNAR_LAUNCHER (navigator)->current_directory;
 
419
}
 
420
 
 
421
 
 
422
 
 
423
static void
 
424
thunar_launcher_set_current_directory (ThunarNavigator *navigator,
 
425
                                       ThunarFile      *current_directory)
 
426
{
 
427
  ThunarLauncher *launcher = THUNAR_LAUNCHER (navigator);
 
428
 
 
429
  /* disconnect from the previous directory */
 
430
  if (G_LIKELY (launcher->current_directory != NULL))
 
431
    g_object_unref (G_OBJECT (launcher->current_directory));
 
432
 
 
433
  /* activate the new directory */
 
434
  launcher->current_directory = current_directory;
 
435
 
 
436
  /* connect to the new directory */
 
437
  if (G_LIKELY (current_directory != NULL))
 
438
    g_object_ref (G_OBJECT (current_directory));
 
439
 
 
440
  /* notify listeners */
 
441
  g_object_notify_by_pspec (G_OBJECT (launcher), launcher_props[PROP_CURRENT_DIRECTORY]);
 
442
}
 
443
 
 
444
 
 
445
 
 
446
static GList*
 
447
thunar_launcher_get_selected_files (ThunarComponent *component)
 
448
{
 
449
  return THUNAR_LAUNCHER (component)->selected_files;
 
450
}
 
451
 
 
452
 
 
453
 
 
454
static void
 
455
thunar_launcher_set_selected_files (ThunarComponent *component,
 
456
                                    GList           *selected_files)
 
457
{
 
458
  ThunarLauncher *launcher = THUNAR_LAUNCHER (component);
 
459
  GList          *np;
 
460
  GList          *op;
 
461
 
 
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))
 
465
      break;
 
466
 
 
467
  /* check if the list of selected files really changed */
 
468
  if (G_UNLIKELY (np != NULL || op != NULL))
 
469
    {
 
470
      /* disconnect from the previously selected files */
 
471
      thunar_g_file_list_free (launcher->selected_files);
 
472
 
 
473
      /* connect to the new selected files list */
 
474
      launcher->selected_files = thunar_g_file_list_copy (selected_files);
 
475
 
 
476
      /* update the launcher actions */
 
477
      thunar_launcher_update (launcher);
 
478
 
 
479
      /* notify listeners */
 
480
      g_object_notify_by_pspec (G_OBJECT (launcher), launcher_props[PROP_SELECTED_FILES]);
 
481
    }
 
482
}
 
483
 
 
484
 
 
485
 
 
486
static GtkUIManager*
 
487
thunar_launcher_get_ui_manager (ThunarComponent *component)
 
488
{
 
489
  return THUNAR_LAUNCHER (component)->ui_manager;
 
490
}
 
491
 
 
492
 
 
493
 
 
494
static void
 
495
thunar_launcher_set_ui_manager (ThunarComponent *component,
 
496
                                GtkUIManager    *ui_manager)
 
497
{
 
498
  ThunarLauncher *launcher = THUNAR_LAUNCHER (component);
 
499
  GError         *error = NULL;
 
500
 
 
501
  /* disconnect from the previous UI manager */
 
502
  if (G_UNLIKELY (launcher->ui_manager != NULL))
 
503
    {
 
504
      /* drop our action group from the previous UI manager */
 
505
      gtk_ui_manager_remove_action_group (launcher->ui_manager, launcher->action_group);
 
506
 
 
507
      /* unmerge our addons ui controls from the previous UI manager */
 
508
      if (G_LIKELY (launcher->ui_addons_merge_id != 0))
 
509
        {
 
510
          gtk_ui_manager_remove_ui (launcher->ui_manager, launcher->ui_addons_merge_id);
 
511
          launcher->ui_addons_merge_id = 0;
 
512
        }
 
513
 
 
514
      /* unmerge our ui controls from the previous UI manager */
 
515
      gtk_ui_manager_remove_ui (launcher->ui_manager, launcher->ui_merge_id);
 
516
 
 
517
      /* drop the reference on the previous UI manager */
 
518
      g_object_unref (G_OBJECT (launcher->ui_manager));
 
519
    }
 
520
 
 
521
  /* activate the new UI manager */
 
522
  launcher->ui_manager = ui_manager;
 
523
 
 
524
  /* connect to the new UI manager */
 
525
  if (G_LIKELY (ui_manager != NULL))
 
526
    {
 
527
      /* we keep a reference on the new manager */
 
528
      g_object_ref (G_OBJECT (ui_manager));
 
529
 
 
530
      /* add our action group to the new manager */
 
531
      gtk_ui_manager_insert_action_group (ui_manager, launcher->action_group, -1);
 
532
 
 
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))
 
536
        {
 
537
          g_error ("Failed to merge ThunarLauncher menus: %s", error->message);
 
538
          g_error_free (error);
 
539
        }
 
540
 
 
541
      /* update the user interface */
 
542
      thunar_launcher_update (launcher);
 
543
    }
 
544
 
 
545
  /* notify listeners */
 
546
  g_object_notify_by_pspec (G_OBJECT (launcher), launcher_props[PROP_UI_MANAGER]);
 
547
}
 
548
 
 
549
 
 
550
 
 
551
static void
 
552
thunar_launcher_execute_files (ThunarLauncher *launcher,
 
553
                               GList          *files)
 
554
{
 
555
  GError *error = NULL;
 
556
  GFile  *working_directory;
 
557
  GList  *lp;
 
558
 
 
559
  /* execute all selected files */
 
560
  for (lp = files; lp != NULL; lp = lp->next)
 
561
    {
 
562
      working_directory = thunar_file_get_file (launcher->current_directory);
 
563
 
 
564
      if (!thunar_file_execute (lp->data, working_directory, launcher->widget, NULL, &error))
 
565
        {
 
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);
 
569
          break;
 
570
        }
 
571
    }
 
572
}
 
573
 
 
574
 
 
575
 
 
576
static guint
 
577
thunar_launcher_g_app_info_hash (gconstpointer app_info)
 
578
{
 
579
  return 0;
 
580
}
 
581
 
 
582
 
 
583
 
 
584
static void
 
585
thunar_launcher_open_files (ThunarLauncher *launcher,
 
586
                            GList          *files)
 
587
{
 
588
  GHashTable *applications;
 
589
  GAppInfo   *app_info;
 
590
  GList      *file_list;
 
591
  GList      *lp;
 
592
 
 
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);
 
601
 
 
602
  for (lp = files; lp != NULL; lp = lp->next)
 
603
    {
 
604
      /* determine the default application for the MIME type */
 
605
      app_info = thunar_file_get_default_handler (lp->data);
 
606
 
 
607
      /* check if we have an application here */
 
608
      if (G_LIKELY (app_info != NULL))
 
609
        {
 
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))
 
613
            {
 
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);
 
616
            }
 
617
 
 
618
          /* append our new URI to the list */
 
619
          file_list = thunar_g_file_list_append (file_list, thunar_file_get_file (lp->data));
 
620
 
 
621
          /* (re)insert the URI list for the application */
 
622
          g_hash_table_insert (applications, app_info, file_list);
 
623
        }
 
624
      else
 
625
        {
 
626
          /* display a chooser dialog for the file and stop */
 
627
          thunar_show_chooser_dialog (launcher->widget, lp->data, TRUE);
 
628
          break;
 
629
        }
 
630
    }
 
631
 
 
632
  /* run all collected applications */
 
633
  g_hash_table_foreach (applications, (GHFunc) thunar_launcher_open_paths, launcher);
 
634
 
 
635
  /* drop the applications hash table */
 
636
  g_hash_table_destroy (applications);
 
637
}
 
638
 
 
639
 
 
640
 
 
641
static void
 
642
thunar_launcher_open_paths (GAppInfo       *app_info,
 
643
                            GList          *path_list,
 
644
                            ThunarLauncher *launcher)
 
645
{
 
646
  GdkAppLaunchContext *context;
 
647
  GdkScreen           *screen;
 
648
  GError              *error = NULL;
 
649
  GFile               *working_directory = NULL;
 
650
  gchar               *message;
 
651
  gchar               *name;
 
652
  guint                n;
 
653
 
 
654
  /* determine the screen on which to launch the application */
 
655
  screen = (launcher->widget != NULL) ? gtk_widget_get_screen (launcher->widget) : NULL;
 
656
 
 
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));
 
662
 
 
663
  /* determine the working directory */
 
664
  if (launcher->current_directory != NULL)
 
665
    working_directory = thunar_file_get_file (launcher->current_directory);
 
666
 
 
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))
 
669
    {
 
670
      /* figure out the appropriate error message */
 
671
      n = g_list_length (path_list);
 
672
      if (G_LIKELY (n == 1))
 
673
        {
 
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);
 
677
          g_free (name);
 
678
        }
 
679
      else
 
680
        {
 
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);
 
683
        }
 
684
 
 
685
      /* display an error dialog to the user */
 
686
      thunar_dialogs_show_error (launcher->widget, error, "%s", message);
 
687
      g_error_free (error);
 
688
      g_free (message);
 
689
    }
 
690
 
 
691
  /* destroy the launch context */
 
692
  g_object_unref (context);
 
693
}
 
694
 
 
695
 
 
696
 
 
697
static void
 
698
thunar_launcher_open_windows (ThunarLauncher *launcher,
 
699
                              GList          *directories)
 
700
{
 
701
  ThunarApplication *application;
 
702
  GtkWidget         *dialog;
 
703
  GtkWidget         *window;
 
704
  GdkScreen         *screen;
 
705
  gchar             *label;
 
706
  GList             *lp;
 
707
  gint               response = GTK_RESPONSE_YES;
 
708
  gint               n;
 
709
 
 
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))
 
713
    {
 
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
 
718
                                       | GTK_DIALOG_MODAL,
 
719
                                       GTK_MESSAGE_QUESTION,
 
720
                                       GTK_BUTTONS_NONE,
 
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.",
 
725
                                                          n),
 
726
                                                n);
 
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);
 
733
      g_free (label);
 
734
    }
 
735
 
 
736
  /* open n new windows if the user approved it */
 
737
  if (G_LIKELY (response == GTK_RESPONSE_YES))
 
738
    {
 
739
      /* query the application object */
 
740
      application = thunar_application_get ();
 
741
 
 
742
      /* determine the screen on which to open the new windows */
 
743
      screen = (launcher->widget != NULL) ? gtk_widget_get_screen (launcher->widget) : NULL;
 
744
 
 
745
      /* open all requested windows */
 
746
      for (lp = directories; lp != NULL; lp = lp->next)
 
747
        thunar_application_open_window (application, lp->data, screen, NULL);
 
748
 
 
749
      /* release the application object */
 
750
      g_object_unref (G_OBJECT (application));
 
751
    }
 
752
}
 
753
 
 
754
 
 
755
 
 
756
static gboolean
 
757
thunar_launcher_update_idle (gpointer data)
 
758
{
 
759
  ThunarLauncher *launcher = THUNAR_LAUNCHER (data);
 
760
  const gchar    *context_menu_path;
 
761
  const gchar    *file_menu_path;
 
762
  GtkAction      *action;
 
763
  gboolean        default_is_open_with_other = FALSE;
 
764
  GList          *applications;
 
765
  GList          *actions;
 
766
  GList          *lp;
 
767
  gchar          *tooltip;
 
768
  gchar          *label;
 
769
  gchar          *name;
 
770
  gint            n_directories = 0;
 
771
  gint            n_executables = 0;
 
772
  gint            n_regulars = 0;
 
773
  gint            n_selected_files = 0;
 
774
  gint            n;
 
775
 
 
776
  /* verify that we're connected to an UI manager */
 
777
  if (G_UNLIKELY (launcher->ui_manager == NULL))
 
778
    return FALSE;
 
779
 
 
780
  GDK_THREADS_ENTER ();
 
781
 
 
782
  /* drop the previous addons ui controls from the UI manager */
 
783
  if (G_LIKELY (launcher->ui_addons_merge_id != 0))
 
784
    {
 
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;
 
788
    }
 
789
 
 
790
  /* reset the application set for the "Open" action */
 
791
  g_object_set_qdata (G_OBJECT (launcher->action_open), thunar_launcher_handler_quark, NULL);
 
792
 
 
793
  /* determine the number of files/directories/executables */
 
794
  for (lp = launcher->selected_files; lp != NULL; lp = lp->next, ++n_selected_files)
 
795
    {
 
796
      if (thunar_file_is_directory (lp->data)
 
797
          || thunar_file_is_shortcut (lp->data)
 
798
          || thunar_file_is_mountable (lp->data))
 
799
        {
 
800
          ++n_directories;
 
801
        }
 
802
      else
 
803
        {
 
804
          if (thunar_file_is_executable (lp->data))
 
805
            ++n_executables;
 
806
          ++n_regulars;
 
807
        }
 
808
    }
 
809
 
 
810
  /* update the user interface depending on the current selection */
 
811
  if (G_LIKELY (n_selected_files == 0 || n_directories > 0))
 
812
    {
 
813
      /** CASE 1: nothing selected or atleast one directory in the selection
 
814
       **
 
815
       ** - "Open", "Open in n New Windows" and "Open in n New Tabs" actions
 
816
       **/
 
817
 
 
818
      /* Prepare "Open" label */
 
819
      gtk_action_set_label (launcher->action_open, _("_Open"));
 
820
 
 
821
      if (n_selected_files == n_directories && n_directories >= 1)
 
822
        {
 
823
          if (n_directories > 1)
 
824
            {
 
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),
 
831
                            "label", label,
 
832
                            "tooltip", tooltip,
 
833
                            NULL);
 
834
              g_free (tooltip);
 
835
              g_free (label);
 
836
 
 
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),
 
843
                            "label", label,
 
844
                            "tooltip", tooltip,
 
845
                            NULL);
 
846
              g_free (tooltip);
 
847
              g_free (label);
 
848
            }
 
849
          else if (n_directories == 1)
 
850
            {
 
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"),
 
855
                            NULL);
 
856
 
 
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"),
 
861
                            NULL);
 
862
 
 
863
              /* set tooltip that makes sence */
 
864
              gtk_action_set_tooltip (launcher->action_open, _("Open the selected directory"));
 
865
            }
 
866
 
 
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);
 
870
 
 
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);
 
874
        }
 
875
      else
 
876
        {
 
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);
 
880
 
 
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",
 
887
                                            n_selected_files));
 
888
        }
 
889
 
 
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);
 
893
    }
 
894
  else
 
895
    {
 
896
      /** CASE 2: one or more file in the selection
 
897
       **
 
898
       ** - "Execute" action if all selected files are executable
 
899
       ** - No "Open in n New Windows" action
 
900
       **/
 
901
 
 
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);
 
908
 
 
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);
 
911
 
 
912
      /* make the "Open" action sensitive */
 
913
      gtk_action_set_sensitive (launcher->action_open, TRUE);
 
914
 
 
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);
 
918
 
 
919
      /* determine the set of applications that work for all selected files */
 
920
      applications = thunar_file_list_get_applications (launcher->selected_files);
 
921
 
 
922
      /* reset the desktop actions list */
 
923
      actions = NULL;
 
924
 
 
925
      /* check if we have only executable files in the selection */
 
926
      if (G_UNLIKELY (n_executables == n_selected_files))
 
927
        {
 
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),
 
932
                        NULL);
 
933
        }
 
934
      else if (G_LIKELY (applications != NULL))
 
935
        {
 
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),
 
942
                        "label", label,
 
943
                        "tooltip", tooltip,
 
944
                        NULL);
 
945
          g_free (tooltip);
 
946
          g_free (label);
 
947
 
 
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);
 
950
 
 
951
          /* FIXME Add the desktop actions for this application.
 
952
           * Unfortunately this is not supported by GIO directly */
 
953
 
 
954
          /* drop the default application from the list */
 
955
          applications = g_list_delete_link (applications, applications);
 
956
        }
 
957
      else if (G_UNLIKELY (n_selected_files == 1))
 
958
        {
 
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"),
 
963
                        NULL);
 
964
          default_is_open_with_other = TRUE;
 
965
        }
 
966
      else
 
967
        {
 
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),
 
973
                        NULL);
 
974
        }
 
975
 
 
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
 
978
       */
 
979
      if (G_UNLIKELY (g_list_length (applications) > 2 || n_executables == n_selected_files))
 
980
        {
 
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";
 
984
 
 
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));
 
988
        }
 
989
      else
 
990
        {
 
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";
 
994
 
 
995
          /* add a separator if we have more than one additional application */
 
996
          if (G_LIKELY (applications != NULL))
 
997
            {
 
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);
 
1005
            }
 
1006
 
 
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);
 
1010
        }
 
1011
 
 
1012
      /* add actions for all remaining applications */
 
1013
      if (G_LIKELY (applications != NULL))
 
1014
        {
 
1015
          /* process all applications and determine the desktop actions */
 
1016
          for (lp = applications, n = 0; lp != NULL; lp = lp->next, ++n)
 
1017
            {
 
1018
              /* FIXME Determine the desktop actions for this application.
 
1019
               * Unfortunately this is not supported by GIO directly. */
 
1020
 
 
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));
 
1027
 
 
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));
 
1041
 
 
1042
              /* cleanup */
 
1043
              g_free (tooltip);
 
1044
              g_free (label);
 
1045
              g_free (name);
 
1046
            }
 
1047
 
 
1048
          /* cleanup */
 
1049
          g_list_free (applications);
 
1050
        }
 
1051
 
 
1052
      /* FIXME Add desktop actions here. Unfortunately they are not supported by
 
1053
       * GIO, so we'll have to roll our own thing here */
 
1054
    }
 
1055
 
 
1056
  /* schedule an update of the "Send To" menu */
 
1057
  if (G_LIKELY (launcher->sendto_idle_id == 0))
 
1058
    {
 
1059
      launcher->sendto_idle_id = g_idle_add_full (G_PRIORITY_LOW, thunar_launcher_sendto_idle,
 
1060
                                                  launcher, thunar_launcher_sendto_idle_destroy);
 
1061
    }
 
1062
 
 
1063
  GDK_THREADS_LEAVE ();
 
1064
 
 
1065
  return FALSE;
 
1066
}
 
1067
 
 
1068
 
 
1069
 
 
1070
static void
 
1071
thunar_launcher_update_idle_destroy (gpointer data)
 
1072
{
 
1073
  THUNAR_LAUNCHER (data)->launcher_idle_id = 0;
 
1074
}
 
1075
 
 
1076
 
 
1077
 
 
1078
static void
 
1079
thunar_launcher_update_check (ThunarLauncher *launcher,
 
1080
                              GtkWidget      *menu)
 
1081
{
 
1082
  _thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
 
1083
  _thunar_return_if_fail (menu == NULL || GTK_IS_MENU (menu));
 
1084
 
 
1085
  /* check if the menu is in a dirty state */
 
1086
  if (launcher->launcher_idle_id != 0)
 
1087
    {
 
1088
      /* stop the timeout */
 
1089
      g_source_remove (launcher->launcher_idle_id);
 
1090
 
 
1091
      /* force an update */
 
1092
      thunar_launcher_update_idle (launcher);
 
1093
 
 
1094
      /* ui update */
 
1095
      gtk_ui_manager_ensure_update (launcher->ui_manager);
 
1096
 
 
1097
      /* make sure the menu is positioned correctly after the
 
1098
       * interface update */
 
1099
      if (menu != NULL)
 
1100
        gtk_menu_reposition (GTK_MENU (menu));
 
1101
    }
 
1102
}
 
1103
 
 
1104
 
 
1105
 
 
1106
static void
 
1107
thunar_launcher_update (ThunarLauncher *launcher)
 
1108
{
 
1109
  GSList    *proxies, *lp;
 
1110
  GtkWidget *menu;
 
1111
  gboolean   instant_update;
 
1112
 
 
1113
  _thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
 
1114
 
 
1115
  proxies = gtk_action_get_proxies (launcher->action_open);
 
1116
  instant_update = (proxies == NULL);
 
1117
  for (lp = proxies; lp != NULL; lp = lp->next)
 
1118
    {
 
1119
      menu = gtk_widget_get_ancestor (lp->data, GTK_TYPE_MENU);
 
1120
      if (G_LIKELY (menu != NULL))
 
1121
        {
 
1122
          /* instant update if a menu is visible */
 
1123
          if (gtk_widget_get_visible (menu))
 
1124
            instant_update = TRUE;
 
1125
 
 
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);
 
1129
        }
 
1130
    }
 
1131
 
 
1132
  /* stop pending timeouts */
 
1133
  if (launcher->launcher_idle_id != 0)
 
1134
    g_source_remove (launcher->launcher_idle_id);
 
1135
 
 
1136
  if (instant_update)
 
1137
    {
 
1138
      /* directly update without interruption */
 
1139
      thunar_launcher_update_idle (launcher);
 
1140
    }
 
1141
  else
 
1142
    {
 
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);
 
1149
 
 
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);
 
1153
    }
 
1154
}
 
1155
 
 
1156
 
 
1157
 
 
1158
static void
 
1159
thunar_launcher_open_file (ThunarLauncher *launcher,
 
1160
                           ThunarFile     *file)
 
1161
{
 
1162
  GList files;
 
1163
 
 
1164
  _thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
 
1165
  _thunar_return_if_fail (THUNAR_IS_FILE (file));
 
1166
 
 
1167
  files.data = file;
 
1168
  files.next = NULL;
 
1169
  files.prev = NULL;
 
1170
 
 
1171
  if (thunar_file_is_directory (file))
 
1172
    {
 
1173
      /* check if we're in a regular view (i.e. current_directory is set) */
 
1174
      if (G_LIKELY (launcher->current_directory != NULL))
 
1175
        {
 
1176
          /* we want to open one directory, so just emit "change-directory" here */
 
1177
          thunar_navigator_change_directory (THUNAR_NAVIGATOR (launcher), file);
 
1178
        }
 
1179
      else
 
1180
        {
 
1181
          /* open the selected directory in a new window */
 
1182
          thunar_launcher_open_windows (launcher, &files);
 
1183
        }
 
1184
    }
 
1185
  else
 
1186
    {
 
1187
      if (thunar_file_is_executable (file))
 
1188
        {
 
1189
          /* try to execute the file */
 
1190
          thunar_launcher_execute_files (launcher, &files);
 
1191
        }
 
1192
      else
 
1193
        {
 
1194
          /* try to open the file using its default application */
 
1195
          thunar_launcher_open_files (launcher, &files);
 
1196
        }
 
1197
    }
 
1198
}
 
1199
 
 
1200
 
 
1201
 
 
1202
static void
 
1203
thunar_launcher_poke_file_finish (ThunarBrowser *browser,
 
1204
                                  ThunarFile    *file,
 
1205
                                  ThunarFile    *target_file,
 
1206
                                  GError        *error,
 
1207
                                  gpointer       ignored)
 
1208
{
 
1209
  if (error == NULL)
 
1210
    {
 
1211
      thunar_launcher_open_file (THUNAR_LAUNCHER (browser), target_file);
 
1212
    }
 
1213
  else
 
1214
    {
 
1215
      thunar_dialogs_show_error (THUNAR_LAUNCHER (browser)->widget, error,
 
1216
                                 _("Failed to open \"%s\""),
 
1217
                                 thunar_file_get_display_name (file));
 
1218
    }
 
1219
}
 
1220
 
 
1221
 
 
1222
 
 
1223
static void
 
1224
thunar_launcher_poke_files (ThunarLauncher         *launcher,
 
1225
                            ThunarLauncherPokeData *poke_data)
 
1226
{
 
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);
 
1230
 
 
1231
  thunar_browser_poke_file (THUNAR_BROWSER (launcher), poke_data->files->data,
 
1232
                            launcher->widget, thunar_launcher_poke_files_finish,
 
1233
                            poke_data);
 
1234
}
 
1235
 
 
1236
 
 
1237
 
 
1238
static void
 
1239
thunar_launcher_poke_files_finish (ThunarBrowser *browser,
 
1240
                                   ThunarFile    *file,
 
1241
                                   ThunarFile    *target_file,
 
1242
                                   GError        *error,
 
1243
                                   gpointer       user_data)
 
1244
{
 
1245
  ThunarLauncherPokeData *poke_data = user_data;
 
1246
  gboolean                executable = TRUE;
 
1247
  GList                  *directories = NULL;
 
1248
  GList                  *files = NULL;
 
1249
  GList                  *lp;
 
1250
 
 
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);
 
1255
 
 
1256
  /* check if poking succeeded */
 
1257
  if (error == NULL)
 
1258
    {
 
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));
 
1262
    }
 
1263
 
 
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);
 
1267
 
 
1268
  if (poke_data->files == NULL)
 
1269
    {
 
1270
      /* separate files and directories in the selected files list */
 
1271
      for (lp = poke_data->resolved_files; lp != NULL; lp = lp->next)
 
1272
        {
 
1273
          if (thunar_file_is_directory (lp->data))
 
1274
            {
 
1275
              /* add to our directory list */
 
1276
              directories = g_list_prepend (directories, lp->data);
 
1277
            }
 
1278
          else
 
1279
            {
 
1280
              /* add to our file list */
 
1281
              files = g_list_prepend (files, lp->data);
 
1282
 
 
1283
              /* check if the file is executable */
 
1284
              executable = (executable && thunar_file_is_executable (lp->data));
 
1285
            }
 
1286
        }
 
1287
 
 
1288
      /* check if we have any directories to process */
 
1289
      if (G_LIKELY (directories != NULL))
 
1290
        {
 
1291
          if (poke_data->directories_in_tabs)
 
1292
            {
 
1293
              /* open new tabs */
 
1294
              for (lp = directories; lp != NULL; lp = lp->next)
 
1295
                thunar_navigator_open_new_tab (THUNAR_NAVIGATOR (browser), lp->data);
 
1296
            }
 
1297
          else
 
1298
            {
 
1299
              /* open new windows for all directories */
 
1300
              thunar_launcher_open_windows (THUNAR_LAUNCHER (browser), directories);
 
1301
            }
 
1302
          g_list_free (directories);
 
1303
        }
 
1304
 
 
1305
      /* check if we have any files to process */
 
1306
      if (G_LIKELY (files != NULL))
 
1307
        {
 
1308
          /* if all files are executable, we just run them here */
 
1309
          if (G_UNLIKELY (executable))
 
1310
            {
 
1311
              /* try to execute all given files */
 
1312
              thunar_launcher_execute_files (THUNAR_LAUNCHER (browser), files);
 
1313
            }
 
1314
          else
 
1315
            {
 
1316
              /* try to open all files using their default applications */
 
1317
              thunar_launcher_open_files (THUNAR_LAUNCHER (browser), files);
 
1318
            }
 
1319
 
 
1320
          /* cleanup */
 
1321
          g_list_free (files);
 
1322
        }
 
1323
 
 
1324
      /* free all files allocated for the poke data */
 
1325
      thunar_launcher_poke_data_free (poke_data);
 
1326
    }
 
1327
  else
 
1328
    {
 
1329
      /* we need to continue this until all files have been resolved */
 
1330
      thunar_launcher_poke_files (THUNAR_LAUNCHER (browser), poke_data);
 
1331
    }
 
1332
}
 
1333
 
 
1334
 
 
1335
 
 
1336
static void
 
1337
thunar_launcher_action_open (GtkAction      *action,
 
1338
                             ThunarLauncher *launcher)
 
1339
{
 
1340
  ThunarLauncherPokeData *poke_data;
 
1341
  GAppInfo               *app_info;
 
1342
  GList                  *selected_paths;
 
1343
 
 
1344
  _thunar_return_if_fail (GTK_IS_ACTION (action));
 
1345
  _thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
 
1346
 
 
1347
  /* force update if still dirty */
 
1348
  thunar_launcher_update_check (launcher, NULL);
 
1349
  if (!gtk_action_get_sensitive (action))
 
1350
    return;
 
1351
 
 
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))
 
1355
    {
 
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);
 
1360
    }
 
1361
  else if (launcher->selected_files != NULL)
 
1362
    {
 
1363
      if (launcher->selected_files->next == NULL)
 
1364
        {
 
1365
          thunar_browser_poke_file (THUNAR_BROWSER (launcher),
 
1366
                                    launcher->selected_files->data, launcher->widget,
 
1367
                                    thunar_launcher_poke_file_finish, NULL);
 
1368
        }
 
1369
      else
 
1370
        {
 
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
 
1373
           * stage */
 
1374
           poke_data = thunar_launcher_poke_data_new (launcher->selected_files);
 
1375
           thunar_launcher_poke_files (launcher, poke_data);
 
1376
        }
 
1377
    }
 
1378
}
 
1379
 
 
1380
 
 
1381
 
 
1382
static void
 
1383
thunar_launcher_action_open_with_other (GtkAction      *action,
 
1384
                                        ThunarLauncher *launcher)
 
1385
{
 
1386
  _thunar_return_if_fail (GTK_IS_ACTION (action));
 
1387
  _thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
 
1388
 
 
1389
  /* force update if still dirty */
 
1390
  thunar_launcher_update_check (launcher, NULL);
 
1391
  if (!gtk_action_get_visible (action))
 
1392
    return;
 
1393
 
 
1394
  /* verify that we have atleast one selected file */
 
1395
  if (G_LIKELY (launcher->selected_files != NULL))
 
1396
    {
 
1397
      /* popup the chooser dialog for the first selected file */
 
1398
      thunar_show_chooser_dialog (launcher->widget, launcher->selected_files->data, TRUE);
 
1399
    }
 
1400
}
 
1401
 
 
1402
 
 
1403
 
 
1404
static void
 
1405
thunar_launcher_action_open_in_new_window (GtkAction      *action,
 
1406
                                           ThunarLauncher *launcher)
 
1407
{
 
1408
  ThunarLauncherPokeData *poke_data;
 
1409
 
 
1410
  _thunar_return_if_fail (GTK_IS_ACTION (action));
 
1411
  _thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
 
1412
 
 
1413
  /* force update if still dirty */
 
1414
  thunar_launcher_update_check (launcher, NULL);
 
1415
  if (!gtk_action_get_visible (action))
 
1416
    return;
 
1417
 
 
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);
 
1421
}
 
1422
 
 
1423
 
 
1424
 
 
1425
static void
 
1426
thunar_launcher_action_open_in_new_tab (GtkAction      *action,
 
1427
                                        ThunarLauncher *launcher)
 
1428
{
 
1429
  ThunarLauncherPokeData *poke_data;
 
1430
 
 
1431
  _thunar_return_if_fail (GTK_IS_ACTION (action));
 
1432
  _thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
 
1433
 
 
1434
  /* force update if still dirty */
 
1435
  thunar_launcher_update_check (launcher, NULL);
 
1436
  if (!gtk_action_get_visible (action))
 
1437
    return;
 
1438
 
 
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);
 
1443
}
 
1444
 
 
1445
 
 
1446
 
 
1447
static void
 
1448
thunar_launcher_action_sendto_desktop (GtkAction      *action,
 
1449
                                       ThunarLauncher *launcher)
 
1450
{
 
1451
  ThunarApplication *application;
 
1452
  GFile             *desktop_file;
 
1453
  GList             *files;
 
1454
 
 
1455
  _thunar_return_if_fail (GTK_IS_ACTION (action));
 
1456
  _thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
 
1457
 
 
1458
  /* determine the source files */
 
1459
  files = thunar_file_list_to_thunar_g_file_list (launcher->selected_files);
 
1460
  if (G_UNLIKELY (files == NULL))
 
1461
    return;
 
1462
 
 
1463
  /* determine the file to the ~/Desktop folder */
 
1464
  desktop_file = thunar_g_file_new_for_desktop ();
 
1465
 
 
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));
 
1470
 
 
1471
  /* cleanup */
 
1472
  g_object_unref (desktop_file);
 
1473
  thunar_g_file_list_free (files);
 
1474
}
 
1475
 
 
1476
 
 
1477
 
 
1478
static ThunarLauncherMountData *
 
1479
thunar_launcher_mount_data_new (ThunarLauncher *launcher,
 
1480
                                GList          *files)
 
1481
{
 
1482
  ThunarLauncherMountData *data;
 
1483
 
 
1484
  _thunar_return_val_if_fail (THUNAR_IS_LAUNCHER (launcher), NULL);
 
1485
 
 
1486
  data = g_slice_new0 (ThunarLauncherMountData);
 
1487
  data->launcher = g_object_ref (launcher);
 
1488
  data->files = thunar_g_file_list_copy (files);
 
1489
 
 
1490
  return data;
 
1491
}
 
1492
 
 
1493
 
 
1494
 
 
1495
static void
 
1496
thunar_launcher_mount_data_free (ThunarLauncherMountData *data)
 
1497
{
 
1498
  _thunar_return_if_fail (data != NULL);
 
1499
  _thunar_return_if_fail (THUNAR_IS_LAUNCHER (data->launcher));
 
1500
 
 
1501
  g_object_unref (data->launcher);
 
1502
  thunar_g_file_list_free (data->files);
 
1503
  g_slice_free (ThunarLauncherMountData, data);
 
1504
}
 
1505
 
 
1506
 
 
1507
 
 
1508
static ThunarLauncherPokeData *
 
1509
thunar_launcher_poke_data_new (GList *files)
 
1510
{
 
1511
  ThunarLauncherPokeData *data;
 
1512
 
 
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;
 
1517
 
 
1518
  return data;
 
1519
}
 
1520
 
 
1521
 
 
1522
 
 
1523
static void
 
1524
thunar_launcher_poke_data_free (ThunarLauncherPokeData *data)
 
1525
{
 
1526
  _thunar_return_if_fail (data != NULL);
 
1527
 
 
1528
  thunar_g_file_list_free (data->files);
 
1529
  thunar_g_file_list_free (data->resolved_files);
 
1530
  g_slice_free (ThunarLauncherPokeData, data);
 
1531
}
 
1532
 
 
1533
 
 
1534
 
 
1535
static void
 
1536
thunar_launcher_sendto_device (ThunarLauncher *launcher,
 
1537
                               ThunarDevice   *device,
 
1538
                               GList          *files)
 
1539
{
 
1540
  ThunarApplication *application;
 
1541
  GFile             *mount_point;
 
1542
 
 
1543
  _thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
 
1544
  _thunar_return_if_fail (THUNAR_IS_DEVICE (device));
 
1545
 
 
1546
  if (!thunar_device_is_mounted (device))
 
1547
    return;
 
1548
 
 
1549
  mount_point = thunar_device_get_root (device);
 
1550
  if (mount_point != NULL)
 
1551
    {
 
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);
 
1556
 
 
1557
      g_object_unref (mount_point);
 
1558
    }
 
1559
}
 
1560
 
 
1561
 
 
1562
 
 
1563
static void
 
1564
thunar_launcher_sendto_mount_finish (ThunarDevice *device,
 
1565
                                     const GError *error,
 
1566
                                     gpointer      user_data)
 
1567
{
 
1568
  ThunarLauncherMountData *data = user_data;
 
1569
  gchar                   *device_name;
 
1570
 
 
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));
 
1574
 
 
1575
  if (error != NULL)
 
1576
    {
 
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);
 
1582
    }
 
1583
  else
 
1584
    {
 
1585
      thunar_launcher_sendto_device (data->launcher, device, data->files);
 
1586
    }
 
1587
 
 
1588
  thunar_launcher_mount_data_free (data);
 
1589
}
 
1590
 
 
1591
 
 
1592
 
 
1593
static void
 
1594
thunar_launcher_action_sendto_device (GtkAction      *action,
 
1595
                                      ThunarLauncher *launcher)
 
1596
{
 
1597
  ThunarLauncherMountData *data;
 
1598
  GMountOperation         *mount_operation;
 
1599
  ThunarDevice            *device;
 
1600
  GList                   *files;
 
1601
 
 
1602
  _thunar_return_if_fail (GTK_IS_ACTION (action));
 
1603
  _thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
 
1604
 
 
1605
  /* determine the source paths */
 
1606
  files = thunar_file_list_to_thunar_g_file_list (launcher->selected_files);
 
1607
  if (G_UNLIKELY (files == NULL))
 
1608
    return;
 
1609
 
 
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))
 
1613
    return;
 
1614
 
 
1615
  /* make sure to mount the device first, if it's not already mounted */
 
1616
  if (!thunar_device_is_mounted (device))
 
1617
    {
 
1618
      /* allocate mount data */
 
1619
      data = thunar_launcher_mount_data_new (launcher, files);
 
1620
 
 
1621
      /* allocate a GTK+ mount operation */
 
1622
      mount_operation = thunar_gtk_mount_operation_new (launcher->widget);
 
1623
 
 
1624
      /* try to mount the device and later start sending the files */
 
1625
      thunar_device_mount (device,
 
1626
                           mount_operation,
 
1627
                           NULL,
 
1628
                           thunar_launcher_sendto_mount_finish,
 
1629
                           data);
 
1630
 
 
1631
      g_object_unref (mount_operation);
 
1632
    }
 
1633
  else
 
1634
    {
 
1635
      thunar_launcher_sendto_device (launcher, device, files);
 
1636
    }
 
1637
 
 
1638
  /* cleanup */
 
1639
  thunar_g_file_list_free (files);
 
1640
}
 
1641
 
 
1642
 
 
1643
 
 
1644
static void
 
1645
thunar_launcher_widget_destroyed (ThunarLauncher *launcher,
 
1646
                                  GtkWidget      *widget)
 
1647
{
 
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));
 
1651
 
 
1652
  /* just reset the widget property for the launcher */
 
1653
  thunar_launcher_set_widget (launcher, NULL);
 
1654
}
 
1655
 
 
1656
 
 
1657
 
 
1658
static gboolean
 
1659
thunar_launcher_sendto_idle (gpointer user_data)
 
1660
{
 
1661
  ThunarLauncher *launcher = THUNAR_LAUNCHER (user_data);
 
1662
  const gchar    *label;
 
1663
  GtkAction      *action;
 
1664
  gboolean        linkable = TRUE;
 
1665
  GIcon          *icon;
 
1666
  GList          *handlers;
 
1667
  GList          *devices;
 
1668
  GList          *lp;
 
1669
  gchar          *name;
 
1670
  gchar          *tooltip;
 
1671
  gchar          *device_name;
 
1672
  gint            n_selected_files;
 
1673
  gint            n = 0;
 
1674
  gboolean        got_devices = FALSE;
 
1675
  const gchar    *file_menu_path;
 
1676
  const gchar    *context_menu_path;
 
1677
 
 
1678
  /* verify that we have an UI manager */
 
1679
  if (launcher->ui_manager == NULL)
 
1680
    return FALSE;
 
1681
 
 
1682
  GDK_THREADS_ENTER ();
 
1683
 
 
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).
 
1686
   */
 
1687
  for (lp = launcher->selected_files, n_selected_files = 0; lp != NULL; lp = lp->next, ++n_selected_files)
 
1688
    {
 
1689
      /* check if this file is in trash */
 
1690
      if (G_UNLIKELY (linkable))
 
1691
        linkable = !thunar_file_is_trashed (lp->data);
 
1692
    }
 
1693
 
 
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",
 
1700
                                     n_selected_files),
 
1701
                "visible", (linkable && n_selected_files > 0),
 
1702
                NULL);
 
1703
 
 
1704
  /* re-add the content to "Send To" if we have any files */
 
1705
  if (G_LIKELY (n_selected_files > 0))
 
1706
    {
 
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);
 
1713
 
 
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);
 
1717
 
 
1718
      /* determine the currently active devices */
 
1719
      devices = thunar_device_monitor_get_devices (launcher->device_monitor);
 
1720
      got_devices = (devices != NULL);
 
1721
 
 
1722
      /* paths in ui */
 
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";
 
1725
 
 
1726
      /* add removable (and writable) drives and media */
 
1727
      for (lp = devices; lp != NULL; lp = lp->next, ++n)
 
1728
        {
 
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);
 
1735
 
 
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);
 
1746
 
 
1747
          icon = thunar_device_get_icon (lp->data);
 
1748
          if (G_LIKELY (icon != NULL))
 
1749
            {
 
1750
              gtk_action_set_gicon (action, icon);
 
1751
              g_object_unref (icon);
 
1752
            }
 
1753
 
 
1754
          /* cleanup */
 
1755
          g_free (name);
 
1756
          g_free (tooltip);
 
1757
          g_free (device_name);
 
1758
        }
 
1759
 
 
1760
      /* free the devices list */
 
1761
      g_list_free (devices);
 
1762
 
 
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))
 
1766
        {
 
1767
          if (got_devices)
 
1768
            {
 
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);
 
1776
            }
 
1777
 
 
1778
          /* add all handlers to the user interface */
 
1779
          for (lp = handlers; lp != NULL; lp = lp->next, ++n)
 
1780
            {
 
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);
 
1787
 
 
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));
 
1799
 
 
1800
              /* cleanup */
 
1801
              g_free (tooltip);
 
1802
              g_free (name);
 
1803
            }
 
1804
 
 
1805
          /* release the handler list */
 
1806
          g_list_free (handlers);
 
1807
        }
 
1808
    }
 
1809
 
 
1810
  GDK_THREADS_LEAVE ();
 
1811
 
 
1812
  return FALSE;
 
1813
}
 
1814
 
 
1815
 
 
1816
 
 
1817
static void
 
1818
thunar_launcher_sendto_idle_destroy (gpointer user_data)
 
1819
{
 
1820
  THUNAR_LAUNCHER (user_data)->sendto_idle_id = 0;
 
1821
}
 
1822
 
 
1823
 
 
1824
 
 
1825
/**
 
1826
 * thunar_launcher_new:
 
1827
 *
 
1828
 * Allocates a new #ThunarLauncher instance.
 
1829
 *
 
1830
 * Return value: the newly allocated #ThunarLauncher.
 
1831
 **/
 
1832
ThunarLauncher*
 
1833
thunar_launcher_new (void)
 
1834
{
 
1835
  return g_object_new (THUNAR_TYPE_LAUNCHER, NULL);
 
1836
}
 
1837
 
 
1838
 
 
1839
 
 
1840
/**
 
1841
 * thunar_launcher_get_widget:
 
1842
 * @launcher : a #ThunarLauncher.
 
1843
 *
 
1844
 * Returns the #GtkWidget currently associated with @launcher.
 
1845
 *
 
1846
 * Return value: the widget associated with @launcher.
 
1847
 **/
 
1848
static GtkWidget*
 
1849
thunar_launcher_get_widget (const ThunarLauncher *launcher)
 
1850
{
 
1851
  _thunar_return_val_if_fail (THUNAR_IS_LAUNCHER (launcher), NULL);
 
1852
  return launcher->widget;
 
1853
}
 
1854
 
 
1855
 
 
1856
 
 
1857
/**
 
1858
 * thunar_launcher_set_widget:
 
1859
 * @launcher : a #ThunarLauncher.
 
1860
 * @widget   : a #GtkWidget or %NULL.
 
1861
 *
 
1862
 * Associates @launcher with @widget.
 
1863
 **/
 
1864
void
 
1865
thunar_launcher_set_widget (ThunarLauncher *launcher,
 
1866
                            GtkWidget      *widget)
 
1867
{
 
1868
  _thunar_return_if_fail (THUNAR_IS_LAUNCHER (launcher));
 
1869
  _thunar_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget));
 
1870
 
 
1871
  /* disconnect from the previous widget */
 
1872
  if (G_UNLIKELY (launcher->widget != NULL))
 
1873
    {
 
1874
      g_signal_handlers_disconnect_by_func (G_OBJECT (launcher->widget), thunar_launcher_widget_destroyed, launcher);
 
1875
      g_object_unref (G_OBJECT (launcher->widget));
 
1876
    }
 
1877
 
 
1878
  /* activate the new widget */
 
1879
  launcher->widget = widget;
 
1880
 
 
1881
  /* connect to the new widget */
 
1882
  if (G_LIKELY (widget != NULL))
 
1883
    {
 
1884
      g_object_ref (G_OBJECT (widget));
 
1885
      g_signal_connect_swapped (G_OBJECT (widget), "destroy", G_CALLBACK (thunar_launcher_widget_destroyed), launcher);
 
1886
    }
 
1887
 
 
1888
  /* notify listeners */
 
1889
  g_object_notify_by_pspec (G_OBJECT (launcher), launcher_props[PROP_WIDGET]);
 
1890
}
 
1891
 
 
1892
 
 
1893