From 022e9b12e84460cc56a140ffbbd12e3bd1027ade Mon Sep 17 00:00:00 2001 From: Colin Clark Date: Sun, 3 Dec 2023 13:29:39 +0000 Subject: [PATCH] Open With feature The File menu has an Open With menu item, and there is an Open With plugin. In both cases the standard GTK application chooser dialog is called. --- plugins/meson.build | 1 + plugins/open-with/meson.build | 21 +++++ .../open-with/org.geeqie.open-with.desktop.in | 16 ++++ src/layout-util.cc | 90 +++++++++++++++++++ src/main.h | 1 + src/ui/menu.ui | 1 + 6 files changed, 130 insertions(+) create mode 100644 plugins/open-with/meson.build create mode 100644 plugins/open-with/org.geeqie.open-with.desktop.in diff --git a/plugins/meson.build b/plugins/meson.build index 65d55bb7..503c94c4 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -16,6 +16,7 @@ subdir('export-jpeg') subdir('geocode-parameters') subdir('image-crop') subdir('lens') +subdir('open-with') subdir('random-image') subdir('resize-image') subdir('rotate') diff --git a/plugins/open-with/meson.build b/plugins/open-with/meson.build new file mode 100644 index 00000000..4f533f5d --- /dev/null +++ b/plugins/open-with/meson.build @@ -0,0 +1,21 @@ +# This file is a part of Geeqie project (https://www.geeqie.org/). +# Copyright (C) 2008 - 2023 The Geeqie Team +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# + +i18n.merge_file( + input : 'org.geeqie.open-with.desktop.in', + output : 'org.geeqie.open-with.desktop', + type : 'desktop', + po_dir : podir, + install : true, + install_dir : desktopdir) + diff --git a/plugins/open-with/org.geeqie.open-with.desktop.in b/plugins/open-with/org.geeqie.open-with.desktop.in new file mode 100644 index 00000000..5e3677c9 --- /dev/null +++ b/plugins/open-with/org.geeqie.open-with.desktop.in @@ -0,0 +1,16 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Name=Open With +Comment=Call the gtk application chooser dialog + +Exec=geeqie --remote --action:OpenWith %f + +# Desktop files that are usable only in Geeqie should be marked like this: +Categories=X-Geeqie; +OnlyShowIn=X-Geeqie; + +# It can be made verbose +# X-Geeqie-Verbose=true + +Icon=geeqie diff --git a/src/layout-util.cc b/src/layout-util.cc index 8dfac9cc..062c4c4c 100644 --- a/src/layout-util.cc +++ b/src/layout-util.cc @@ -904,6 +904,95 @@ static void layout_menu_view_in_new_window_cb(GtkAction *, gpointer data) view_window_new(layout_image_get_fd(lw)); } +struct OpenWithData +{ + GAppInfo *application; + GList *g_file_list; + GtkWidget *app_chooser_dialog; +}; + +void open_with_response_cb(GtkDialog *, gint response_id, gpointer data) +{ + GError *error = NULL; + auto open_with_data = static_cast(data); + + if (response_id == GTK_RESPONSE_OK) + { + g_app_info_launch(open_with_data->application, open_with_data->g_file_list, nullptr, &error); + + if (error) + { + log_printf("Error launching app: %s\n", error->message); + g_error_free(error); + } + } + + g_object_unref(open_with_data->application); + g_object_unref(g_list_first(open_with_data->g_file_list)->data); + g_list_free(open_with_data->g_file_list); + gtk_widget_destroy(GTK_WIDGET(open_with_data->app_chooser_dialog)); + g_free(open_with_data); +} + +static void open_with_application_selected_cb(GtkAppChooserWidget *, GAppInfo *application, gpointer data) +{ + auto open_with_data = static_cast(data); + + g_object_unref(open_with_data->application); + + open_with_data->application = g_app_info_dup(application); +} + +static void open_with_application_activated_cb(GtkAppChooserWidget *, GAppInfo *application, gpointer data) +{ + GError *error = NULL; + auto open_with_data = static_cast(data); + + g_app_info_launch(application, open_with_data->g_file_list, nullptr, &error); + + if (error) + { + log_printf("Error launching app.: %s\n", error->message); + g_error_free(error); + } + + g_object_unref(open_with_data->application); + g_object_unref(g_list_first(open_with_data->g_file_list)->data); + g_list_free(open_with_data->g_file_list); + gtk_widget_destroy(GTK_WIDGET(open_with_data->app_chooser_dialog)); + g_free(open_with_data); +} + +static void layout_menu_open_with_cb(GtkAction *, gpointer data) +{ + auto lw = static_cast(data); + FileData *fd; + GtkWidget *widget; + OpenWithData *open_with_data; + + if (layout_selection_list(lw)) + { + open_with_data = g_new(OpenWithData, 1); + + fd = static_cast(g_list_first(layout_selection_list(lw))->data); + + open_with_data->g_file_list = g_list_append(nullptr, g_file_new_for_path(fd->path)); + + open_with_data->app_chooser_dialog = gtk_app_chooser_dialog_new(nullptr, GTK_DIALOG_DESTROY_WITH_PARENT, G_FILE(g_list_first(open_with_data->g_file_list)->data)); + + widget = gtk_app_chooser_dialog_get_widget(GTK_APP_CHOOSER_DIALOG(open_with_data->app_chooser_dialog)); + + open_with_data->application = gtk_app_chooser_get_app_info(GTK_APP_CHOOSER(open_with_data->app_chooser_dialog)); + + g_signal_connect(G_OBJECT(widget), "application-selected", G_CALLBACK(open_with_application_selected_cb), open_with_data); + g_signal_connect(G_OBJECT(widget), "application-activated", G_CALLBACK(open_with_application_activated_cb), open_with_data); + g_signal_connect(G_OBJECT(open_with_data->app_chooser_dialog), "response", G_CALLBACK(open_with_response_cb), open_with_data); + g_signal_connect(G_OBJECT(open_with_data->app_chooser_dialog), "close", G_CALLBACK(open_with_response_cb), open_with_data); + + gtk_widget_show(open_with_data->app_chooser_dialog); + } +} + static void layout_menu_open_archive_cb(GtkAction *, gpointer data) { auto lw = static_cast(data); @@ -2608,6 +2697,7 @@ static GtkActionEntry menu_entries[] = { { "OpenArchive", GQ_ICON_OPEN, N_("Open archive"), nullptr, N_("Open archive"), CB(layout_menu_open_archive_cb) }, { "OpenCollection", GQ_ICON_OPEN, N_("_Open collection..."), "O", N_("Open collection..."), nullptr }, { "OpenRecent", nullptr, N_("Open recen_t"), nullptr, N_("Open recent collection"), nullptr }, + { "OpenWith", GQ_ICON_OPEN_WITH, N_("Open With..."), nullptr, N_("Open With..."), CB(layout_menu_open_with_cb) }, { "OrientationMenu", nullptr, N_("_Orientation"), nullptr, nullptr, nullptr }, { "OverlayMenu", nullptr, N_("Image _Overlay"), nullptr, nullptr, nullptr }, { "PanView", PIXBUF_INLINE_ICON_PANORAMA, N_("Pa_n view"), "J", N_("Pan view"), CB(layout_menu_pan_cb) }, diff --git a/src/main.h b/src/main.h index 3ec5c70e..79fa7ccb 100644 --- a/src/main.h +++ b/src/main.h @@ -138,6 +138,7 @@ #define GQ_ICON_UNDO "edit-undo" #define GQ_ICON_REDO "edit-redo" #define GQ_ICON_OPEN "document-open" +#define GQ_ICON_OPEN_WITH "open-menu" #define GQ_ICON_SAVE "document-save" #define GQ_ICON_SAVE_AS "document-save-as" #define GQ_ICON_NEW "document-new" diff --git a/src/ui/menu.ui b/src/ui/menu.ui index 56e8a86d..a0f081ba 100644 --- a/src/ui/menu.ui +++ b/src/ui/menu.ui @@ -17,6 +17,7 @@ + -- 2.20.1