[gnome-builder] open-with-external: Split from project-tree plugin to separate plugin



commit dc739667b72dc765dc12b28cddb775cc5318f360
Author: vanadiae <vanadiae35 gmail com>
Date:   Sun Jun 27 18:21:07 2021 +0200

    open-with-external: Split from project-tree plugin to separate plugin
    
    Currently it has a specific GAction which is only usable from the
    Open With => External Program context menu entry in the project tree.
    
    But by making it a separate plugin that handles opening through a
    workbench addin, it can have a specific very low priority to act as a
    fallback anywhere a file opening operation takes place: from the Ctrl+.
    search bar and from the project tree. This means in particular that
    files that can't be opened properly by Builder currently, like sounds,
    images and archives will instead be opened with an external program
    instead of the directory browser view (which is a bug anyway).

 src/plugins/ls/gbp-ls-workbench-addin.c            |   7 +-
 src/plugins/meson.build                            |   1 +
 .../open-with-external/gbp-owe-workbench-addin.c   | 184 +++++++++++++++++++++
 .../open-with-external/gbp-owe-workbench-addin.h   |  32 ++++
 src/plugins/open-with-external/gtk/menus.ui        |  17 ++
 src/plugins/open-with-external/meson.build         |  17 ++
 .../open-with-external/open-with-external-plugin.c |  34 ++++
 .../open-with-external.gresource.xml               |   7 +
 .../open-with-external/open-with-external.plugin   |   9 +
 .../project-tree/gbp-project-tree-pane-actions.c   |  42 -----
 src/plugins/project-tree/gtk/menus.ui              |   7 -
 src/plugins/project-tree/meson.build               |   1 -
 12 files changed, 307 insertions(+), 51 deletions(-)
---
diff --git a/src/plugins/ls/gbp-ls-workbench-addin.c b/src/plugins/ls/gbp-ls-workbench-addin.c
index 1188cdfda..308a05f9a 100644
--- a/src/plugins/ls/gbp-ls-workbench-addin.c
+++ b/src/plugins/ls/gbp-ls-workbench-addin.c
@@ -52,7 +52,12 @@ gbp_ls_workbench_addin_can_open (IdeWorkbenchAddin *addin,
       return TRUE;
     }
 
-  /* We can open, but super low priority */
+  /* We can open, but super low priority, to avoid needing a second menu entry
+   * in the project tree context menu, even if it doesn't fully make sense to
+   * make the ls plugin handle every file… Anyway, as the open-with-external
+   * plugin has slighter higher priority it'll be used instead as a fallback
+   * method, leaving this ls plugin for explicit use with the 'ls' hint.
+   */
   *priority = G_MAXINT;
   return TRUE;
 }
diff --git a/src/plugins/meson.build b/src/plugins/meson.build
index b8db03be4..ebaa2bd26 100644
--- a/src/plugins/meson.build
+++ b/src/plugins/meson.build
@@ -100,6 +100,7 @@ subdir('newcomers')
 subdir('notification')
 subdir('npm')
 subdir('omni-gutter')
+subdir('open-with-external')
 subdir('phpize')
 subdir('podman')
 subdir('project-tree')
diff --git a/src/plugins/open-with-external/gbp-owe-workbench-addin.c 
b/src/plugins/open-with-external/gbp-owe-workbench-addin.c
new file mode 100644
index 000000000..4d1588d9b
--- /dev/null
+++ b/src/plugins/open-with-external/gbp-owe-workbench-addin.c
@@ -0,0 +1,184 @@
+/* gbp-owe-workbench-addin.c
+ *
+ * Copyright 2021 vanadiae <vanadiae35 gmail com>
+ *
+ * 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 3 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
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-owe-workbench-addin"
+
+#include "gbp-owe-workbench-addin.h"
+
+#include <libportal/portal.h>
+#include <libportal/portal-gtk3.h>
+
+struct _GbpOweWorkbenchAddin
+{
+  IdeObject         parent_instance;
+
+  XdpPortal *portal;
+  IdeWorkbench *workbench;
+};
+
+static void addin_iface_init (IdeWorkbenchAddinInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GbpOweWorkbenchAddin,
+                         gbp_owe_workbench_addin,
+                         IDE_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_WORKBENCH_ADDIN, addin_iface_init))
+
+static gboolean
+gbp_owe_workbench_addin_can_open (IdeWorkbenchAddin     *addin,
+                                  GFile                 *file,
+                                  const gchar           *content_type,
+                                  gint                  *priority)
+{
+  GbpOweWorkbenchAddin *self = (GbpOweWorkbenchAddin *)addin;
+  GFileType filetype;
+
+  g_assert (GBP_IS_OWE_WORKBENCH_ADDIN (self));
+  g_assert (G_IS_FILE (file));
+  g_assert (priority != NULL);
+
+  filetype = g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, NULL);
+
+  /* We want the addin to be used only as last resort, when none satisfied better.
+   * See the ls plugin's workbench addin for an explanation on why not a full G_MAXINT.
+   */
+  *priority = G_MAXINT / 2;
+
+  // xdp_portal_open_uri() doesn't accept opening directories, and anyway there's
+  // the Open Containing Folder entry for that purpose.
+  return filetype != G_FILE_TYPE_DIRECTORY;
+}
+
+static void
+on_file_opened_cb (GObject      *source_object,
+                   GAsyncResult *res,
+                   gpointer      user_data)
+{
+  XdpPortal *portal = (XdpPortal *)source_object;
+  g_autoptr(IdeTask) task = (IdeTask *)user_data;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (XDP_IS_PORTAL (portal));
+  g_assert (IDE_IS_TASK (task));
+
+  if (!xdp_portal_open_uri_finish (portal, res, &error) &&
+      !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+    {
+      ide_task_return_new_error (task, error->domain, error->code,
+                                 "Couldn't open file with external program using libportal: %s", 
error->message);
+    }
+  else
+    ide_task_return_boolean (task, TRUE);
+}
+
+static void
+gbp_owe_workbench_addin_open_async (IdeWorkbenchAddin     *addin,
+                                    GFile                 *file,
+                                    const gchar           *content_type,
+                                    IdeBufferOpenFlags     flags,
+                                    GCancellable          *cancellable,
+                                    GAsyncReadyCallback    callback,
+                                    gpointer               user_data)
+{
+  GbpOweWorkbenchAddin *self = (GbpOweWorkbenchAddin *)addin;
+  g_autoptr(IdeTask) task = NULL;
+  g_autofree gchar *uri = NULL;
+  XdpParent *parent;
+  GtkWindow *current_window;
+
+  g_assert (GBP_IS_OWE_WORKBENCH_ADDIN (self));
+  g_assert (G_IS_FILE (file));
+
+  if (self->portal == NULL)
+    self->portal = xdp_portal_new ();
+
+  uri = g_file_get_uri (file);
+  task = ide_task_new (self, cancellable, callback, user_data);
+
+  current_window = GTK_WINDOW (ide_workbench_get_current_workspace (self->workbench));
+  parent = xdp_parent_new_gtk (current_window);
+  xdp_portal_open_uri (self->portal,
+                       parent,
+                       uri,
+                       XDP_OPEN_URI_FLAG_ASK | XDP_OPEN_URI_FLAG_WRITABLE,
+                       ide_task_get_cancellable (task),
+                       on_file_opened_cb,
+                       g_object_ref (task));
+  xdp_parent_free (parent);
+}
+
+static gboolean
+gbp_owe_workbench_addin_open_finish (IdeWorkbenchAddin     *addin,
+                                     GAsyncResult          *result,
+                                     GError               **error)
+{
+  GbpOweWorkbenchAddin *self = (GbpOweWorkbenchAddin *)addin;
+
+  g_assert (GBP_IS_OWE_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_TASK (result));
+
+  return ide_task_propagate_boolean (IDE_TASK (result), error);
+}
+
+static void
+gbp_owe_workbench_addin_load (IdeWorkbenchAddin *addin,
+                              IdeWorkbench      *workbench)
+{
+  GbpOweWorkbenchAddin *self = (GbpOweWorkbenchAddin *)addin;
+
+  g_assert (GBP_IS_OWE_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_WORKBENCH (workbench));
+
+  self->workbench = workbench;
+}
+
+static void
+gbp_owe_workbench_addin_unload (IdeWorkbenchAddin *addin,
+                                IdeWorkbench      *workbench)
+{
+  GbpOweWorkbenchAddin *self = (GbpOweWorkbenchAddin *)addin;
+
+  g_assert (GBP_IS_OWE_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_WORKBENCH (workbench));
+
+  if (self->portal != NULL)
+    g_clear_object (&self->portal);
+
+  self->workbench = NULL;
+}
+
+static void
+gbp_owe_workbench_addin_class_init (GbpOweWorkbenchAddinClass *klass)
+{
+}
+
+static void
+gbp_owe_workbench_addin_init (GbpOweWorkbenchAddin *self)
+{
+}
+
+static void
+addin_iface_init (IdeWorkbenchAddinInterface *iface)
+{
+  iface->load = gbp_owe_workbench_addin_load;
+  iface->unload = gbp_owe_workbench_addin_unload;
+  iface->can_open = gbp_owe_workbench_addin_can_open;
+  iface->open_finish = gbp_owe_workbench_addin_open_finish;
+  iface->open_async = gbp_owe_workbench_addin_open_async;
+}
diff --git a/src/plugins/open-with-external/gbp-owe-workbench-addin.h 
b/src/plugins/open-with-external/gbp-owe-workbench-addin.h
new file mode 100644
index 000000000..3e5bb952a
--- /dev/null
+++ b/src/plugins/open-with-external/gbp-owe-workbench-addin.h
@@ -0,0 +1,32 @@
+/* gbp-owe-workbench-addin.h
+ *
+ * Copyright 2021 vanadiae <vanadiae35 gmail com>
+ *
+ * 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 3 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
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-core.h>
+#include <libide-gui.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_OWE_WORKBENCH_ADDIN (gbp_owe_workbench_addin_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpOweWorkbenchAddin, gbp_owe_workbench_addin, GBP, OWE_WORKBENCH_ADDIN, IdeObject)
+
+G_END_DECLS
diff --git a/src/plugins/open-with-external/gtk/menus.ui b/src/plugins/open-with-external/gtk/menus.ui
new file mode 100644
index 000000000..ad31aece3
--- /dev/null
+++ b/src/plugins/open-with-external/gtk/menus.ui
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <menu id="project-tree-menu">
+    <section id="project-tree-menu-open-section">
+      <submenu id="project-tree-menu-open-with-menu">
+        <section id="project-tree-menu-open-with-section">
+          <item>
+            <attribute name="id">project-tree-menu-open-external</attribute>
+            <attribute name="label" translatable="yes">_External Program…</attribute>
+            <attribute name="action">project-tree.open-with-hint</attribute>
+            <attribute name="target" type="s">'open-with-external'</attribute>
+          </item>
+        </section>
+      </submenu>
+    </section>
+  </menu>
+</interface>
diff --git a/src/plugins/open-with-external/meson.build b/src/plugins/open-with-external/meson.build
new file mode 100644
index 000000000..441c5a2cf
--- /dev/null
+++ b/src/plugins/open-with-external/meson.build
@@ -0,0 +1,17 @@
+if libportal_dep.found()
+
+plugins_sources += files([
+  'open-with-external-plugin.c',
+  'gbp-owe-workbench-addin.c',
+])
+
+plugin_open_with_external_resources = gnome.compile_resources(
+  'open-with-external-resources',
+  'open-with-external.gresource.xml',
+  c_name: 'gbp_owe',
+)
+
+plugins_deps += [libportal_dep]
+plugins_sources += plugin_open_with_external_resources
+
+endif
diff --git a/src/plugins/open-with-external/open-with-external-plugin.c 
b/src/plugins/open-with-external/open-with-external-plugin.c
new file mode 100644
index 000000000..870f27eac
--- /dev/null
+++ b/src/plugins/open-with-external/open-with-external-plugin.c
@@ -0,0 +1,34 @@
+/* open-with-external-plugin.c
+ *
+ * Copyright 2021 vanadiae <vanadiae35 gmail com>
+ *
+ * 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 3 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
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include "config.h"
+
+#include <libpeas/peas.h>
+#include <libide-gui.h>
+
+#include "gbp-owe-workbench-addin.h"
+
+_IDE_EXTERN void
+_gbp_owe_register_types (PeasObjectModule *module)
+{
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_WORKBENCH_ADDIN,
+                                              GBP_TYPE_OWE_WORKBENCH_ADDIN);
+}
diff --git a/src/plugins/open-with-external/open-with-external.gresource.xml 
b/src/plugins/open-with-external/open-with-external.gresource.xml
new file mode 100644
index 000000000..7f722ee30
--- /dev/null
+++ b/src/plugins/open-with-external/open-with-external.gresource.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/plugins/open-with-external">
+    <file>open-with-external.plugin</file>
+    <file preprocess="xml-stripblanks">gtk/menus.ui</file>
+  </gresource>
+</gresources>
diff --git a/src/plugins/open-with-external/open-with-external.plugin 
b/src/plugins/open-with-external/open-with-external.plugin
new file mode 100644
index 000000000..629ef935f
--- /dev/null
+++ b/src/plugins/open-with-external/open-with-external.plugin
@@ -0,0 +1,9 @@
+[Plugin]
+Authors=vanadiae <vanadiae35 gmail com>
+Builtin=true
+Copyright=Copyright © 2021 vanadiae
+Description=Provides a fallback file opener and an Open in External Program menu entry
+Embedded=_gbp_owe_register_types
+Hidden=true
+Module=open-with-external
+Name=Open with External Program
diff --git a/src/plugins/project-tree/gbp-project-tree-pane-actions.c 
b/src/plugins/project-tree/gbp-project-tree-pane-actions.c
index acc3bac6d..a93bae7ce 100644
--- a/src/plugins/project-tree/gbp-project-tree-pane-actions.c
+++ b/src/plugins/project-tree/gbp-project-tree-pane-actions.c
@@ -26,10 +26,6 @@
 #include <libide-projects.h>
 #include <vte/vte.h>
 
-#ifdef ENABLE_LIBPORTAL
-# include <libportal/portal-gtk3.h>
-#endif
-
 #include "gbp-project-tree-private.h"
 #include "gbp-rename-file-popover.h"
 #include "gbp-new-file-popover.h"
@@ -467,36 +463,6 @@ DEFINE_ACTION_HANDLER (open_with_hint, {
                             NULL, NULL, NULL);
 });
 
-#ifdef ENABLE_LIBPORTAL
-DEFINE_ACTION_HANDLER (open_with_external, {
-  IdeProjectFile *project_file;
-  g_autoptr(XdpPortal) portal = NULL;
-  g_autoptr(GFile) file = NULL;
-  g_autofree gchar *uri = NULL;
-  IdeTreeNode *selected;
-  XdpParent *parent;
-  GtkWidget *toplevel;
-
-  if (!(selected = ide_tree_get_selected_node (self->tree)) ||
-      !ide_tree_node_holds (selected, IDE_TYPE_PROJECT_FILE) ||
-      !(project_file = ide_tree_node_get_item (selected)))
-    return;
-
-  toplevel = gtk_widget_get_ancestor (GTK_WIDGET (self->tree), GTK_TYPE_WINDOW);
-  file = ide_project_file_ref_file (project_file);
-  uri = g_file_get_uri (file);
-
-  portal = xdp_portal_new ();
-  parent = xdp_parent_new_gtk (GTK_WINDOW (toplevel));
-  xdp_portal_open_uri (portal,
-                       parent,
-                       uri,
-                       XDP_OPEN_URI_FLAG_ASK | XDP_OPEN_URI_FLAG_WRITABLE,
-                       NULL, NULL, NULL);
-  xdp_parent_free (parent);
-});
-#endif
-
 /* Based on gdesktopappinfo.c in GIO */
 static gchar *
 find_terminal_executable (void)
@@ -582,9 +548,6 @@ static const GActionEntry entries[] = {
   { "new-folder", gbp_project_tree_pane_actions_new_folder },
   { "open", gbp_project_tree_pane_actions_open },
   { "open-with-hint", gbp_project_tree_pane_actions_open_with_hint, "s" },
-#ifdef ENABLE_LIBPORTAL
-  { "open-with-external", gbp_project_tree_pane_actions_open_with_external },
-#endif
   { "open-containing-folder", gbp_project_tree_pane_actions_open_containing_folder },
   { "open-in-terminal", gbp_project_tree_pane_actions_open_in_terminal },
   { "rename", gbp_project_tree_pane_actions_rename },
@@ -661,11 +624,6 @@ _gbp_project_tree_pane_update_actions (GbpProjectTreePane *self)
   dzl_gtk_widget_action_set (GTK_WIDGET (self->tree), "project-tree", "open-with-hint",
                              "enabled", is_file,
                              NULL);
-#ifdef ENABLE_LIBPORTAL
-  dzl_gtk_widget_action_set (GTK_WIDGET (self->tree), "project-tree", "open-with-external",
-                             "enabled", is_file,
-                             NULL);
-#endif
   dzl_gtk_widget_action_set (GTK_WIDGET (self->tree), "project-tree", "open-containing-folder",
                              "enabled", is_file,
                              NULL);
diff --git a/src/plugins/project-tree/gtk/menus.ui b/src/plugins/project-tree/gtk/menus.ui
index 893f80abb..61f32b67f 100644
--- a/src/plugins/project-tree/gtk/menus.ui
+++ b/src/plugins/project-tree/gtk/menus.ui
@@ -24,13 +24,6 @@
       </item>
       <submenu id="project-tree-menu-open-with-menu">
         <attribute name="label" translatable="yes">Open With…</attribute>
-        <section id="project-tree-menu-open-with-external">
-          <item>
-            <attribute name="id">project-tree-menu-open-external</attribute>
-            <attribute name="label" translatable="yes">_External Program…</attribute>
-            <attribute name="action">project-tree.open-with-external</attribute>
-          </item>
-        </section>
         <section id="project-tree-menu-open-with-section">
           <item>
             <attribute name="id">project-tree-menu-open-editor</attribute>
diff --git a/src/plugins/project-tree/meson.build b/src/plugins/project-tree/meson.build
index 2c4095230..1532d6f00 100644
--- a/src/plugins/project-tree/meson.build
+++ b/src/plugins/project-tree/meson.build
@@ -16,5 +16,4 @@ plugin_project_tree_resources = gnome.compile_resources(
   c_name: 'gbp_project_tree',
 )
 
-plugins_deps += [libportal_dep]
 plugins_sources += plugin_project_tree_resources


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]