1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3 /*
4 eel-open-with-dialog.c: an open-with dialog
5
6 Copyright (C) 2004 Novell, Inc.
7 Copyright (C) 2007 Red Hat, Inc.
8
9 The Mate Library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Library General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version.
13
14 The Mate Library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Library General Public License for more details.
18
19 You should have received a copy of the GNU Library General Public
20 License along with the Mate Library; see the file COPYING.LIB. If not,
21 write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
22 Boston, MA 02110-1301, USA.
23
24 Authors: Dave Camp <dave@novell.com>
25 Alexander Larsson <alexl@redhat.com>
26 */
27
28 #include <config.h>
29 #include <string.h>
30 #include <cairo-gobject.h>
31
32 #include <glib/gi18n-lib.h>
33 #include <gtk/gtk.h>
34 #include <gio/gio.h>
35
36 #include <eel/eel-stock-dialogs.h>
37
38 #include "caja-open-with-dialog.h"
39 #include "caja-signaller.h"
40
41 #define sure_string(s) ((const char *)((s)!=NULL?(s):""))
42
43 struct _CajaOpenWithDialogDetails
44 {
45 GAppInfo *selected_app_info;
46
47 char *content_type;
48 char *extension;
49
50 GtkWidget *label;
51 GtkWidget *entry;
52 GtkWidget *button;
53 GtkWidget *checkbox;
54
55 GtkWidget *desc_label;
56
57 GtkWidget *open_label;
58
59 GtkWidget *program_list;
60 GtkListStore *program_list_store;
61 GSList *add_icon_paths;
62 gint add_items_idle_id;
63 gint add_icons_idle_id;
64
65 gboolean add_mode;
66 };
67
68 enum
69 {
70 COLUMN_APP_INFO,
71 COLUMN_ICON,
72 COLUMN_GICON,
73 COLUMN_NAME,
74 COLUMN_COMMENT,
75 COLUMN_EXEC,
76 NUM_COLUMNS
77 };
78
79 enum
80 {
81 RESPONSE_OPEN,
82 RESPONSE_REMOVE
83 };
84
85 enum
86 {
87 APPLICATION_SELECTED,
88 LAST_SIGNAL
89 };
90
91 static guint signals[LAST_SIGNAL] = { 0 };
92 G_DEFINE_TYPE (CajaOpenWithDialog, caja_open_with_dialog, GTK_TYPE_DIALOG);
93
94 static void
caja_open_with_dialog_finalize(GObject * object)95 caja_open_with_dialog_finalize (GObject *object)
96 {
97 CajaOpenWithDialog *dialog;
98
99 dialog = CAJA_OPEN_WITH_DIALOG (object);
100
101 if (dialog->details->add_icons_idle_id)
102 {
103 g_source_remove (dialog->details->add_icons_idle_id);
104 }
105
106 if (dialog->details->add_items_idle_id)
107 {
108 g_source_remove (dialog->details->add_items_idle_id);
109 }
110
111 if (dialog->details->selected_app_info)
112 {
113 g_object_unref (dialog->details->selected_app_info);
114 }
115 g_free (dialog->details->content_type);
116 g_free (dialog->details->extension);
117
118 g_free (dialog->details);
119
120 G_OBJECT_CLASS (caja_open_with_dialog_parent_class)->finalize (object);
121 }
122
123 /* An application is valid if:
124 *
125 * 1) The file exists
126 * 2) The user has permissions to run the file
127 */
128 static gboolean
check_application(CajaOpenWithDialog * dialog)129 check_application (CajaOpenWithDialog *dialog)
130 {
131 char *command;
132 char *path = NULL;
133 char **argv = NULL;
134 int argc;
135 GError *error = NULL;
136 gint retval = TRUE;
137
138 command = NULL;
139 if (dialog->details->selected_app_info != NULL)
140 {
141 command = g_strdup (g_app_info_get_executable (dialog->details->selected_app_info));
142 }
143
144 if (command == NULL)
145 {
146 command = g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->details->entry)));
147 }
148
149 g_shell_parse_argv (command, &argc, &argv, &error);
150 if (error)
151 {
152 eel_show_error_dialog (_("Could not run application"),
153 error->message,
154 GTK_WINDOW (dialog));
155 g_error_free (error);
156 retval = FALSE;
157 goto cleanup;
158 }
159
160 path = g_find_program_in_path (argv[0]);
161 if (!path)
162 {
163 char *error_message;
164
165 error_message = g_strdup_printf (_("Could not find '%s'"),
166 argv[0]);
167
168 eel_show_error_dialog (_("Could not find application"),
169 error_message,
170 GTK_WINDOW (dialog));
171 g_free (error_message);
172 retval = FALSE;
173 goto cleanup;
174 }
175
176 cleanup:
177 g_strfreev (argv);
178 g_free (path);
179 g_free (command);
180
181 return retval;
182 }
183
184 /* Only called for non-desktop files */
185 static char *
get_app_name(const char * commandline,GError ** error)186 get_app_name (const char *commandline, GError **error)
187 {
188 char *basename;
189 char *unquoted;
190 char **argv;
191 int argc;
192
193 if (!g_shell_parse_argv (commandline,
194 &argc, &argv, error))
195 {
196 return NULL;
197 }
198
199 unquoted = g_shell_unquote (argv[0], NULL);
200 if (unquoted)
201 {
202 basename = g_path_get_basename (unquoted);
203 }
204 else
205 {
206 basename = g_strdup (argv[0]);
207 }
208
209 g_free (unquoted);
210 g_strfreev (argv);
211
212 return basename;
213 }
214
215 /* This will check if the application the user wanted exists will return that
216 * application. If it doesn't exist, it will create one and return that.
217 * It also sets the app info as the default for this type.
218 */
219 static GAppInfo *
add_or_find_application(CajaOpenWithDialog * dialog)220 add_or_find_application (CajaOpenWithDialog *dialog)
221 {
222 GAppInfo *app;
223 GError *error;
224 gboolean success, should_set_default;
225 char *message;
226
227 error = NULL;
228 app = NULL;
229 if (dialog->details->selected_app_info)
230 {
231 app = g_object_ref (dialog->details->selected_app_info);
232 }
233 else
234 {
235 char *app_name;
236 const char *commandline;
237
238 commandline = gtk_entry_get_text (GTK_ENTRY (dialog->details->entry));
239 app_name = get_app_name (commandline, &error);
240 if (app_name != NULL)
241 {
242 app = g_app_info_create_from_commandline (commandline,
243 app_name,
244 G_APP_INFO_CREATE_NONE,
245 &error);
246 g_free (app_name);
247 }
248 }
249
250 if (app == NULL)
251 {
252 message = g_strdup_printf (_("Could not add application to the application database: %s"), error ? error->message : _("Unknown error"));
253 eel_show_error_dialog (_("Could not add application"),
254 message,
255 GTK_WINDOW (dialog));
256 g_free (message);
257
258 if (error)
259 g_error_free (error);
260
261 return NULL;
262 }
263
264 should_set_default = (dialog->details->add_mode) ||
265 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->details->checkbox));
266 success = TRUE;
267
268 if (should_set_default)
269 {
270 if (dialog->details->content_type)
271 {
272 success = g_app_info_set_as_default_for_type (app,
273 dialog->details->content_type,
274 &error);
275 }
276 else
277 {
278 success = g_app_info_set_as_default_for_extension (app,
279 dialog->details->extension,
280 &error);
281 }
282 }
283 else
284 {
285 GList *applications;
286
287 applications = g_app_info_get_all_for_type (dialog->details->content_type);
288 if (dialog->details->content_type && applications != NULL)
289 {
290 /* we don't care about reporting errors here */
291 g_app_info_add_supports_type (app,
292 dialog->details->content_type,
293 NULL);
294 }
295
296 if (applications != NULL)
297 {
298 g_list_free_full (applications, g_object_unref);
299 }
300 }
301
302 if (!success && should_set_default)
303 {
304 message = g_strdup_printf (_("Could not set application as the default: %s"), error->message);
305 eel_show_error_dialog (_("Could not set as default application"),
306 message,
307 GTK_WINDOW (dialog));
308 g_free (message);
309 g_error_free (error);
310 }
311
312 g_signal_emit_by_name (caja_signaller_get_current (),
313 "mime_data_changed");
314 return app;
315 }
316
317 static void
emit_application_selected(CajaOpenWithDialog * dialog,GAppInfo * application)318 emit_application_selected (CajaOpenWithDialog *dialog,
319 GAppInfo *application)
320 {
321 g_signal_emit (G_OBJECT (dialog), signals[APPLICATION_SELECTED], 0,
322 application);
323 }
324
325 static void
response_cb(CajaOpenWithDialog * dialog,int response_id,gpointer data)326 response_cb (CajaOpenWithDialog *dialog,
327 int response_id,
328 gpointer data)
329 {
330 switch (response_id)
331 {
332 case RESPONSE_OPEN:
333 if (check_application (dialog))
334 {
335 GAppInfo *application;
336
337 application = add_or_find_application (dialog);
338
339 if (application)
340 {
341 emit_application_selected (dialog, application);
342 g_object_unref (application);
343
344 gtk_widget_destroy (GTK_WIDGET (dialog));
345 }
346 }
347
348 break;
349 case RESPONSE_REMOVE:
350 if (dialog->details->selected_app_info != NULL)
351 {
352 if (g_app_info_delete (dialog->details->selected_app_info))
353 {
354 GtkTreeModel *model;
355 GtkTreeIter iter;
356 GAppInfo *info, *selected;
357
358 selected = dialog->details->selected_app_info;
359 dialog->details->selected_app_info = NULL;
360
361 model = GTK_TREE_MODEL (dialog->details->program_list_store);
362 if (gtk_tree_model_get_iter_first (model, &iter))
363 {
364 do
365 {
366 gtk_tree_model_get (model, &iter,
367 COLUMN_APP_INFO, &info,
368 -1);
369 if (g_app_info_equal (selected, info))
370 {
371 gtk_list_store_remove (dialog->details->program_list_store, &iter);
372 break;
373 }
374 }
375 while (gtk_tree_model_iter_next (model, &iter));
376 }
377
378 g_object_unref (selected);
379 }
380 }
381 break;
382 case GTK_RESPONSE_NONE:
383 case GTK_RESPONSE_DELETE_EVENT:
384 case GTK_RESPONSE_CANCEL:
385 gtk_widget_destroy (GTK_WIDGET (dialog));
386 break;
387 default :
388 g_assert_not_reached ();
389 }
390
391 }
392
393
394 static void
caja_open_with_dialog_class_init(CajaOpenWithDialogClass * class)395 caja_open_with_dialog_class_init (CajaOpenWithDialogClass *class)
396 {
397 GObjectClass *gobject_class;
398
399 gobject_class = G_OBJECT_CLASS (class);
400 gobject_class->finalize = caja_open_with_dialog_finalize;
401
402 signals[APPLICATION_SELECTED] =
403 g_signal_new ("application_selected",
404 G_TYPE_FROM_CLASS (class),
405 G_SIGNAL_RUN_LAST,
406 G_STRUCT_OFFSET (CajaOpenWithDialogClass,
407 application_selected),
408 NULL, NULL,
409 g_cclosure_marshal_VOID__POINTER,
410 G_TYPE_NONE,
411 1, G_TYPE_POINTER);
412 }
413
414 static void
chooser_response_cb(GtkFileChooser * chooser,int response,gpointer user_data)415 chooser_response_cb (GtkFileChooser *chooser,
416 int response,
417 gpointer user_data)
418 {
419 CajaOpenWithDialog *dialog;
420
421 dialog = CAJA_OPEN_WITH_DIALOG (user_data);
422
423 if (response == GTK_RESPONSE_OK)
424 {
425 char *filename;
426
427 filename = gtk_file_chooser_get_filename (chooser);
428
429 if (filename)
430 {
431 char *quoted_text;
432
433 quoted_text = g_shell_quote (filename);
434
435 gtk_entry_set_text (GTK_ENTRY (dialog->details->entry),
436 quoted_text);
437 gtk_editable_set_position (GTK_EDITABLE (dialog->details->entry), -1);
438 g_free (quoted_text);
439 g_free (filename);
440 }
441 }
442
443 gtk_widget_destroy (GTK_WIDGET (chooser));
444 }
445
446 static void
browse_clicked_cb(GtkWidget * button,gpointer user_data)447 browse_clicked_cb (GtkWidget *button,
448 gpointer user_data)
449 {
450 CajaOpenWithDialog *dialog;
451 GtkWidget *chooser;
452
453 dialog = CAJA_OPEN_WITH_DIALOG (user_data);
454
455 chooser = eel_file_chooser_dialog_new (_("Select an Application"),
456 GTK_WINDOW (dialog),
457 GTK_FILE_CHOOSER_ACTION_OPEN,
458 "process-stop",
459 GTK_RESPONSE_CANCEL,
460 "document-open",
461 GTK_RESPONSE_OK,
462 NULL);
463 gtk_window_set_destroy_with_parent (GTK_WINDOW (chooser), TRUE);
464 g_signal_connect (chooser, "response",
465 G_CALLBACK (chooser_response_cb), dialog);
466 gtk_dialog_set_default_response (GTK_DIALOG (chooser),
467 GTK_RESPONSE_OK);
468 gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (chooser), TRUE);
469 gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (chooser),
470 FALSE);
471 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (chooser),
472 "/usr/bin");
473
474 gtk_widget_show (chooser);
475 }
476
477 static void
entry_changed_cb(GtkWidget * entry,CajaOpenWithDialog * dialog)478 entry_changed_cb (GtkWidget *entry,
479 CajaOpenWithDialog *dialog)
480 {
481 /* We are writing in the entry, so we are not using a known appinfo anymore */
482 if (dialog->details->selected_app_info != NULL)
483 {
484 g_object_unref (dialog->details->selected_app_info);
485 dialog->details->selected_app_info = NULL;
486 }
487
488 if (gtk_entry_get_text (GTK_ENTRY (dialog->details->entry))[0] == '\000')
489 {
490 gtk_widget_set_sensitive (dialog->details->button, FALSE);
491 }
492 else
493 {
494 gtk_widget_set_sensitive (dialog->details->button, TRUE);
495 }
496 }
497
498 #define CAJA_OPEN_WITH_DIALOG_ICON_SIZE 24
499 static cairo_surface_t *
get_surface_for_icon(GIcon * icon)500 get_surface_for_icon (GIcon *icon)
501 {
502 cairo_surface_t *surface;
503 gint icon_scale;
504
505 surface = NULL;
506 icon_scale = gdk_window_get_scale_factor (gdk_get_default_root_window ());
507
508 if (G_IS_FILE_ICON (icon))
509 {
510 char *filename;
511
512 filename = g_file_get_path (g_file_icon_get_file (G_FILE_ICON (icon)));
513 if (filename)
514 {
515 GdkPixbuf *pixbuf;
516 pixbuf = gdk_pixbuf_new_from_file_at_size (filename,
517 CAJA_OPEN_WITH_DIALOG_ICON_SIZE * icon_scale,
518 CAJA_OPEN_WITH_DIALOG_ICON_SIZE * icon_scale,
519 NULL);
520 surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, icon_scale, NULL);
521 g_object_unref (pixbuf);
522 }
523 g_free (filename);
524 }
525 else if (G_IS_THEMED_ICON (icon))
526 {
527 const char * const *names;
528
529 names = g_themed_icon_get_names (G_THEMED_ICON (icon));
530
531 if (names != NULL && names[0] != NULL)
532 {
533 char *icon_no_extension;
534 char *p;
535
536 icon_no_extension = g_strdup (names[0]);
537 p = strrchr (icon_no_extension, '.');
538 if (p &&
539 (strcmp (p, ".png") == 0 ||
540 strcmp (p, ".xpm") == 0 ||
541 strcmp (p, ".svg") == 0))
542 {
543 *p = 0;
544 }
545 surface = gtk_icon_theme_load_surface (gtk_icon_theme_get_default (),
546 icon_no_extension,
547 CAJA_OPEN_WITH_DIALOG_ICON_SIZE,
548 icon_scale,
549 NULL,
550 GTK_ICON_LOOKUP_FORCE_SIZE,
551 NULL);
552 g_free (icon_no_extension);
553 }
554 }
555 return surface;
556 }
557
558 static gboolean
caja_open_with_dialog_add_icon_idle(CajaOpenWithDialog * dialog)559 caja_open_with_dialog_add_icon_idle (CajaOpenWithDialog *dialog)
560 {
561 cairo_surface_t *surface;
562 GIcon *icon;
563 gboolean long_operation;
564 GtkTreeIter iter;
565 GtkTreePath *path = NULL;
566
567 long_operation = FALSE;
568 do
569 {
570 if (!dialog->details->add_icon_paths)
571 {
572 dialog->details->add_icons_idle_id = 0;
573 return FALSE;
574 }
575
576 path = dialog->details->add_icon_paths->data;
577 dialog->details->add_icon_paths->data = NULL;
578 dialog->details->add_icon_paths = g_slist_delete_link (dialog->details->add_icon_paths,
579 dialog->details->add_icon_paths);
580
581 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (dialog->details->program_list_store),
582 &iter, path))
583 {
584 gtk_tree_path_free (path);
585 continue;
586 }
587
588 gtk_tree_path_free (path);
589
590 gtk_tree_model_get (GTK_TREE_MODEL (dialog->details->program_list_store), &iter,
591 COLUMN_GICON, &icon, -1);
592
593 if (icon == NULL)
594 {
595 continue;
596 }
597
598 surface = get_surface_for_icon (icon);
599 if (surface)
600 {
601 long_operation = TRUE;
602 gtk_list_store_set (dialog->details->program_list_store, &iter, COLUMN_ICON, surface, -1);
603 cairo_surface_destroy (surface);
604 }
605
606 /* don't go back into the main loop if this wasn't very hard to do */
607 }
608 while (!long_operation);
609
610 return TRUE;
611 }
612
613
614 static gboolean
caja_open_with_search_equal_func(GtkTreeModel * model,int column,const char * key,GtkTreeIter * iter,gpointer user_data)615 caja_open_with_search_equal_func (GtkTreeModel *model,
616 int column,
617 const char *key,
618 GtkTreeIter *iter,
619 gpointer user_data)
620 {
621 char *name;
622 char *path;
623 gboolean ret;
624
625 if (key != NULL)
626 {
627 char *normalized_key;
628
629 normalized_key = g_utf8_casefold (key, -1);
630 g_assert (normalized_key != NULL);
631
632 ret = TRUE;
633
634 gtk_tree_model_get (model, iter,
635 COLUMN_NAME, &name,
636 COLUMN_EXEC, &path,
637 -1);
638
639 if (name != NULL)
640 {
641 char *normalized_name;
642
643 normalized_name = g_utf8_casefold (name, -1);
644 g_assert (normalized_name != NULL);
645
646 if (strncmp (normalized_name, normalized_key, strlen (normalized_key)) == 0)
647 {
648 ret = FALSE;
649 }
650
651 g_free (normalized_name);
652 }
653
654 if (ret && path != NULL)
655 {
656 char *normalized_path;
657 char *basename, *normalized_basename;
658
659 normalized_path = g_utf8_casefold (path, -1);
660 g_assert (normalized_path != NULL);
661
662 basename = g_path_get_basename (path);
663 g_assert (basename != NULL);
664
665 normalized_basename = g_utf8_casefold (basename, -1);
666 g_assert (normalized_basename != NULL);
667
668 if (strncmp (normalized_path, normalized_key, strlen (normalized_key)) == 0 ||
669 strncmp (normalized_basename, normalized_key, strlen (normalized_key)) == 0)
670 {
671 ret = FALSE;
672 }
673
674 g_free (basename);
675 g_free (normalized_basename);
676 g_free (normalized_path);
677 }
678
679 g_free (name);
680 g_free (path);
681 g_free (normalized_key);
682
683 return ret;
684 }
685 else
686 {
687 return TRUE;
688 }
689 }
690
691
692
693 static gboolean
caja_open_with_dialog_add_items_idle(CajaOpenWithDialog * dialog)694 caja_open_with_dialog_add_items_idle (CajaOpenWithDialog *dialog)
695 {
696 GtkCellRenderer *renderer;
697 GtkTreeViewColumn *column;
698 GtkTreeModel *sort;
699 GList *all_applications;
700 GList *l;
701
702 /* create list store */
703 dialog->details->program_list_store = gtk_list_store_new (NUM_COLUMNS,
704 G_TYPE_APP_INFO,
705 CAIRO_GOBJECT_TYPE_SURFACE,
706 G_TYPE_ICON,
707 G_TYPE_STRING,
708 G_TYPE_STRING,
709 G_TYPE_STRING);
710 sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (dialog->details->program_list_store));
711 all_applications = g_app_info_get_all ();
712
713 for (l = all_applications; l; l = l->next)
714 {
715 GAppInfo *app = l->data;
716 GtkTreeIter iter;
717 GtkTreePath *path;
718
719 if (!g_app_info_supports_uris (app) &&
720 !g_app_info_supports_files (app))
721 continue;
722
723 gtk_list_store_append (dialog->details->program_list_store, &iter);
724 gtk_list_store_set (dialog->details->program_list_store, &iter,
725 COLUMN_APP_INFO, app,
726 COLUMN_ICON, NULL,
727 COLUMN_GICON, g_app_info_get_icon (app),
728 COLUMN_NAME, g_app_info_get_display_name (app),
729 COLUMN_COMMENT, g_app_info_get_description (app),
730 COLUMN_EXEC, g_app_info_get_executable,
731 -1);
732
733 path = gtk_tree_model_get_path (GTK_TREE_MODEL (dialog->details->program_list_store), &iter);
734 if (path != NULL)
735 {
736 dialog->details->add_icon_paths = g_slist_prepend (dialog->details->add_icon_paths, path);
737 }
738 }
739 g_list_free (all_applications);
740
741 gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->details->program_list),
742 GTK_TREE_MODEL (sort));
743 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort),
744 COLUMN_NAME, GTK_SORT_ASCENDING);
745 gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (dialog->details->program_list),
746 caja_open_with_search_equal_func,
747 NULL, NULL);
748
749 renderer = gtk_cell_renderer_pixbuf_new ();
750 column = gtk_tree_view_column_new ();
751 gtk_tree_view_column_pack_start (column, renderer, FALSE);
752 gtk_tree_view_column_set_attributes (column, renderer,
753 "surface", COLUMN_ICON,
754 NULL);
755
756 renderer = gtk_cell_renderer_text_new ();
757 gtk_tree_view_column_pack_start (column, renderer, TRUE);
758 gtk_tree_view_column_set_attributes (column, renderer,
759 "text", COLUMN_NAME,
760 NULL);
761 gtk_tree_view_column_set_sort_column_id (column, COLUMN_NAME);
762 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->details->program_list), column);
763
764 dialog->details->add_icon_paths = g_slist_reverse (dialog->details->add_icon_paths);
765
766 if (!dialog->details->add_icons_idle_id)
767 {
768 dialog->details->add_icons_idle_id =
769 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, (GSourceFunc) caja_open_with_dialog_add_icon_idle,
770 dialog, NULL);
771 }
772
773 dialog->details->add_items_idle_id = 0;
774 return FALSE;
775 }
776
777 static void
program_list_selection_changed(GtkTreeSelection * selection,CajaOpenWithDialog * dialog)778 program_list_selection_changed (GtkTreeSelection *selection,
779 CajaOpenWithDialog *dialog)
780 {
781 GtkTreeModel *model;
782 GtkTreeIter iter;
783 GAppInfo *info;
784
785 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
786 {
787 gtk_widget_set_sensitive (dialog->details->button, FALSE);
788 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
789 RESPONSE_REMOVE,
790 FALSE);
791 return;
792 }
793
794 info = NULL;
795 gtk_tree_model_get (model, &iter,
796 COLUMN_APP_INFO, &info,
797 -1);
798
799 if (info == NULL)
800 {
801 return;
802 }
803
804 gtk_entry_set_text (GTK_ENTRY (dialog->details->entry),
805 sure_string (g_app_info_get_executable (info)));
806 gtk_label_set_text (GTK_LABEL (dialog->details->desc_label),
807 sure_string (g_app_info_get_description (info)));
808 gtk_widget_set_sensitive (dialog->details->button, TRUE);
809 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
810 RESPONSE_REMOVE,
811 g_app_info_can_delete (info));
812
813 if (dialog->details->selected_app_info)
814 {
815 g_object_unref (dialog->details->selected_app_info);
816 }
817
818 dialog->details->selected_app_info = info;
819 }
820
821 static void
program_list_selection_activated(GtkTreeView * view,GtkTreePath * path,GtkTreeViewColumn * column,CajaOpenWithDialog * dialog)822 program_list_selection_activated (GtkTreeView *view,
823 GtkTreePath *path,
824 GtkTreeViewColumn *column,
825 CajaOpenWithDialog *dialog)
826 {
827 GtkTreeSelection *selection;
828
829 /* update the entry with the info from the selection */
830 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->details->program_list));
831 program_list_selection_changed (selection, dialog);
832
833 gtk_dialog_response (GTK_DIALOG (&dialog->parent), RESPONSE_OPEN);
834 }
835
836 static void
expander_toggled(GtkWidget * expander,CajaOpenWithDialog * dialog)837 expander_toggled (GtkWidget *expander, CajaOpenWithDialog *dialog)
838 {
839 if (gtk_expander_get_expanded (GTK_EXPANDER (expander)) == TRUE)
840 {
841 gtk_widget_grab_focus (dialog->details->entry);
842 gtk_window_resize (GTK_WINDOW (dialog), 400, 1);
843 }
844 else
845 {
846 GtkTreeSelection *selection;
847
848 gtk_widget_grab_focus (dialog->details->program_list);
849 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->details->program_list));
850 program_list_selection_changed (selection, dialog);
851 }
852 }
853
854 static void
caja_open_with_dialog_init(CajaOpenWithDialog * dialog)855 caja_open_with_dialog_init (CajaOpenWithDialog *dialog)
856 {
857 GtkWidget *hbox;
858 GtkWidget *vbox;
859 GtkWidget *vbox2;
860 GtkWidget *label;
861 GtkWidget *scrolled_window;
862 GtkWidget *expander;
863 GtkTreeSelection *selection;
864
865 dialog->details = g_new0 (CajaOpenWithDialogDetails, 1);
866
867 gtk_window_set_title (GTK_WINDOW (dialog), _("Open With"));
868 gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
869 gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
870 gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
871
872 gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 2);
873
874 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
875 gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
876
877 vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
878 gtk_box_pack_start (GTK_BOX (vbox), vbox2, TRUE, TRUE, 0);
879
880 dialog->details->label = gtk_label_new ("");
881 gtk_label_set_xalign (GTK_LABEL (dialog->details->label), 0.0);
882 gtk_label_set_line_wrap (GTK_LABEL (dialog->details->label), TRUE);
883 gtk_box_pack_start (GTK_BOX (vbox2), dialog->details->label,
884 FALSE, FALSE, 0);
885 gtk_widget_show (dialog->details->label);
886
887
888 scrolled_window = gtk_scrolled_window_new (NULL, NULL);
889 gtk_widget_set_size_request (scrolled_window, 400, 300);
890
891 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
892 GTK_SHADOW_IN);
893 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
894 GTK_POLICY_AUTOMATIC,
895 GTK_POLICY_AUTOMATIC);
896 gtk_scrolled_window_set_overlay_scrolling (GTK_SCROLLED_WINDOW (scrolled_window), FALSE);
897
898 dialog->details->program_list = gtk_tree_view_new ();
899 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (dialog->details->program_list),
900 FALSE);
901 gtk_container_add (GTK_CONTAINER (scrolled_window), dialog->details->program_list);
902
903 gtk_box_pack_start (GTK_BOX (vbox2), scrolled_window, TRUE, TRUE, 0);
904
905 dialog->details->desc_label = gtk_label_new (_("Select an application to view its description."));
906 gtk_label_set_xalign (GTK_LABEL (dialog->details->desc_label), 0.0);
907 gtk_label_set_justify (GTK_LABEL (dialog->details->desc_label), GTK_JUSTIFY_LEFT);
908 gtk_label_set_max_width_chars (GTK_LABEL (dialog->details->desc_label), 54);
909 gtk_label_set_line_wrap (GTK_LABEL (dialog->details->desc_label), TRUE);
910 gtk_label_set_single_line_mode (GTK_LABEL (dialog->details->desc_label), FALSE);
911 gtk_box_pack_start (GTK_BOX (vbox2), dialog->details->desc_label, FALSE, FALSE, 0);
912
913 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->details->program_list));
914 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
915 g_signal_connect (selection, "changed",
916 G_CALLBACK (program_list_selection_changed),
917 dialog);
918 g_signal_connect (dialog->details->program_list, "row-activated",
919 G_CALLBACK (program_list_selection_activated),
920 dialog);
921
922 dialog->details->add_items_idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
923 (GSourceFunc) caja_open_with_dialog_add_items_idle,
924 dialog, NULL);
925
926
927 gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), vbox, TRUE, TRUE, 0);
928 gtk_widget_show_all (vbox);
929
930
931 expander = gtk_expander_new_with_mnemonic (_("_Use a custom command"));
932 gtk_box_pack_start (GTK_BOX (vbox), expander, FALSE, FALSE, 0);
933 g_signal_connect_after (expander, "activate", G_CALLBACK (expander_toggled), dialog);
934
935 gtk_widget_show (expander);
936
937 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
938 gtk_container_add (GTK_CONTAINER (expander), hbox);
939 gtk_widget_show (hbox);
940
941 dialog->details->entry = gtk_entry_new ();
942 gtk_entry_set_activates_default (GTK_ENTRY (dialog->details->entry), TRUE);
943
944 gtk_box_pack_start (GTK_BOX (hbox), dialog->details->entry,
945 TRUE, TRUE, 0);
946 gtk_widget_show (dialog->details->entry);
947
948 dialog->details->button = gtk_button_new_with_mnemonic (_("_Browse..."));
949 g_signal_connect (dialog->details->button, "clicked",
950 G_CALLBACK (browse_clicked_cb), dialog);
951 gtk_box_pack_start (GTK_BOX (hbox), dialog->details->button, FALSE, FALSE, 0);
952 gtk_widget_show (dialog->details->button);
953
954 /* Add remember this application checkbox - only visible in open mode */
955 dialog->details->checkbox = gtk_check_button_new ();
956 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->details->checkbox), TRUE);
957 gtk_button_set_use_underline (GTK_BUTTON (dialog->details->checkbox), TRUE);
958 gtk_widget_show (GTK_WIDGET (dialog->details->checkbox));
959 gtk_box_pack_start (GTK_BOX (vbox), dialog->details->checkbox, FALSE, FALSE, 0);
960
961 eel_dialog_add_button (GTK_DIALOG (dialog),
962 _("_Remove"),
963 "list-remove",
964 RESPONSE_REMOVE);
965
966 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
967 RESPONSE_REMOVE,
968 FALSE);
969
970 eel_dialog_add_button (GTK_DIALOG (dialog),
971 _("_Cancel"),
972 "process-stop",
973 GTK_RESPONSE_CANCEL);
974
975
976 /* Create a custom stock icon */
977 dialog->details->button = gtk_button_new ();
978
979 /* Hook up the entry to the button */
980 gtk_widget_set_sensitive (dialog->details->button, FALSE);
981 g_signal_connect (G_OBJECT (dialog->details->entry), "changed",
982 G_CALLBACK (entry_changed_cb), dialog);
983
984 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
985 gtk_widget_show (hbox);
986
987 label = gtk_label_new_with_mnemonic (_("_Open"));
988 gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (dialog->details->button));
989 gtk_widget_show (label);
990 dialog->details->open_label = label;
991
992 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, FALSE, 0);
993
994 gtk_widget_show (dialog->details->button);
995 gtk_widget_set_can_default (dialog->details->button, TRUE);
996
997 gtk_container_add (GTK_CONTAINER (dialog->details->button), hbox);
998
999 gtk_dialog_add_action_widget (GTK_DIALOG (dialog),
1000 dialog->details->button, RESPONSE_OPEN);
1001
1002
1003 gtk_dialog_set_default_response (GTK_DIALOG (dialog),
1004 RESPONSE_OPEN);
1005
1006 g_signal_connect (dialog, "response",
1007 G_CALLBACK (response_cb),
1008 dialog);
1009 }
1010
1011 static char *
get_extension(const char * basename)1012 get_extension (const char *basename)
1013 {
1014 char *p;
1015
1016 p = strrchr (basename, '.');
1017
1018 if (p && *(p + 1) != '\0')
1019 {
1020 return g_strdup (p + 1);
1021 }
1022 else
1023 {
1024 return NULL;
1025 }
1026 }
1027
1028 static void
set_uri_and_type(CajaOpenWithDialog * dialog,const char * uri,const char * mime_type,const char * passed_extension,gboolean add_mode)1029 set_uri_and_type (CajaOpenWithDialog *dialog,
1030 const char *uri,
1031 const char *mime_type,
1032 const char *passed_extension,
1033 gboolean add_mode)
1034 {
1035 char *label;
1036 char *emname;
1037 char *name, *extension;
1038 char *checkbox_text;
1039
1040 name = NULL;
1041 extension = NULL;
1042
1043 if (uri != NULL)
1044 {
1045 GFile *file;
1046
1047 file = g_file_new_for_uri (uri);
1048 name = g_file_get_basename (file);
1049 g_object_unref (file);
1050 }
1051 if (passed_extension == NULL && name != NULL)
1052 {
1053 extension = get_extension (name);
1054 }
1055 else
1056 {
1057 extension = g_strdup (passed_extension);
1058 }
1059
1060 if (extension != NULL &&
1061 g_content_type_is_unknown (mime_type))
1062 {
1063 dialog->details->extension = g_strdup (extension);
1064
1065 if (name != NULL)
1066 {
1067 emname = g_strdup_printf ("<i>%s</i>", name);
1068 if (add_mode)
1069 {
1070 /* Translators: first %s is a filename and second %s is a file extension */
1071 label = g_strdup_printf (_("Open %s and other %s document with:"),
1072 emname, dialog->details->extension);
1073 }
1074 else
1075 {
1076 /* Translators: the %s here is a file name */
1077 label = g_strdup_printf (_("Open %s with:"), emname);
1078 checkbox_text = g_strdup_printf (_("_Remember this application for %s documents"),
1079 dialog->details->extension);
1080
1081 gtk_button_set_label (GTK_BUTTON (dialog->details->checkbox), checkbox_text);
1082 g_free (checkbox_text);
1083 }
1084 g_free (emname);
1085 }
1086 else
1087 {
1088 /* Only in add mode - the %s here is a file extension */
1089 label = g_strdup_printf (_("Open all %s documents with:"),
1090 dialog->details->extension);
1091 }
1092 g_free (extension);
1093 }
1094 else
1095 {
1096 char *description;
1097
1098 dialog->details->content_type = g_strdup (mime_type);
1099 description = g_content_type_get_description (mime_type);
1100
1101 if (description == NULL)
1102 {
1103 description = g_strdup (_("Unknown"));
1104 }
1105
1106 if (name != NULL)
1107 {
1108 emname = g_strdup_printf ("<i>%s</i>", name);
1109 if (add_mode)
1110 {
1111 /* Translators: First %s is a filename, second is a description
1112 * of the type, eg "plain text document" */
1113 label = g_strdup_printf (_("Open %s and other \"%s\" files with:"),
1114 emname, description);
1115 }
1116 else
1117 {
1118 /* Translators: %s is a filename */
1119 label = g_strdup_printf (_("Open %s with:"), emname);
1120 /* Translators: %s is a file type description */
1121 checkbox_text = g_strdup_printf (_("_Remember this application for \"%s\" files"),
1122 description);
1123
1124 gtk_button_set_label (GTK_BUTTON (dialog->details->checkbox), checkbox_text);
1125 g_free (checkbox_text);
1126 }
1127 g_free (emname);
1128 }
1129 else
1130 {
1131 /* Only in add mode */
1132 label = g_strdup_printf (_("Open all \"%s\" files with:"), description);
1133 }
1134
1135 g_free (description);
1136 }
1137
1138 dialog->details->add_mode = add_mode;
1139 if (add_mode)
1140 {
1141 gtk_widget_hide (dialog->details->checkbox);
1142
1143 gtk_label_set_text_with_mnemonic (GTK_LABEL (dialog->details->open_label),
1144 _("_Add"));
1145 gtk_window_set_title (GTK_WINDOW (dialog), _("Add Application"));
1146 }
1147
1148 gtk_label_set_markup (GTK_LABEL (dialog->details->label), label);
1149
1150 g_free (label);
1151 g_free (name);
1152 }
1153
1154
1155 static GtkWidget *
real_caja_open_with_dialog_new(const char * uri,const char * mime_type,const char * extension,gboolean add_mode)1156 real_caja_open_with_dialog_new (const char *uri,
1157 const char *mime_type,
1158 const char *extension,
1159 gboolean add_mode)
1160 {
1161 GtkWidget *dialog;
1162
1163 dialog = gtk_widget_new (CAJA_TYPE_OPEN_WITH_DIALOG, NULL);
1164
1165 set_uri_and_type (CAJA_OPEN_WITH_DIALOG (dialog), uri, mime_type, extension, add_mode);
1166
1167 return dialog;
1168 }
1169
1170 GtkWidget *
caja_open_with_dialog_new(const char * uri,const char * mime_type,const char * extension)1171 caja_open_with_dialog_new (const char *uri,
1172 const char *mime_type,
1173 const char *extension)
1174 {
1175 return real_caja_open_with_dialog_new (uri, mime_type, extension, FALSE);
1176 }
1177
1178 GtkWidget *
caja_add_application_dialog_new(const char * uri,const char * mime_type)1179 caja_add_application_dialog_new (const char *uri,
1180 const char *mime_type)
1181 {
1182 CajaOpenWithDialog *dialog;
1183
1184 dialog = CAJA_OPEN_WITH_DIALOG (real_caja_open_with_dialog_new (uri, mime_type, NULL, TRUE));
1185
1186 return GTK_WIDGET (dialog);
1187 }
1188
1189 GtkWidget *
caja_add_application_dialog_new_for_multiple_files(const char * extension,const char * mime_type)1190 caja_add_application_dialog_new_for_multiple_files (const char *extension,
1191 const char *mime_type)
1192 {
1193 CajaOpenWithDialog *dialog;
1194
1195 dialog = CAJA_OPEN_WITH_DIALOG (real_caja_open_with_dialog_new (NULL, mime_type, extension, TRUE));
1196
1197 return GTK_WIDGET (dialog);
1198 }
1199
1200