[pan: 2/15] Add a class which spawns an editor window




commit 51c114757be5a387c6742056d5507e1cae530c61
Author: Thomas Tanner <thosrtanner googlemail com>
Date:   Tue May 31 17:56:22 2022 +0100

    Add a class which spawns an editor window

 pan/general/Makefile.am       |   2 +
 pan/general/editor-spawner.cc | 133 ++++++++++++++++++++++++++++++++++++++++++
 pan/general/editor-spawner.h  |  64 ++++++++++++++++++++
 3 files changed, 199 insertions(+)
---
diff --git a/pan/general/Makefile.am b/pan/general/Makefile.am
index 3cf292c..ca0eb68 100644
--- a/pan/general/Makefile.am
+++ b/pan/general/Makefile.am
@@ -6,6 +6,7 @@ noinst_LIBRARIES = libgeneralutils.a
 libgeneralutils_a_SOURCES = \
  debug.cc \
  e-util.cc \
+ editor-spawner.cc \
  file-util.cc \
  log.cc \
  line-reader.cc \
@@ -22,6 +23,7 @@ noinst_HEADERS = \
  debug.h \
  defgroup.h \
  e-util.h \
+ editor-spawner.h \
  file-util.h \
  line-reader.h \
  locking.h \
diff --git a/pan/general/editor-spawner.cc b/pan/general/editor-spawner.cc
new file mode 100644
index 0000000..166ac7b
--- /dev/null
+++ b/pan/general/editor-spawner.cc
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Pan - A Newsreader for Gtk+
+ * Copyright (C) 2002-2006  Charles Kerr <charles rebelbase 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; version 2 of the License.
+ *
+ * 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/>.
+ *
+ */
+
+#include "editor-spawner.h"
+
+#include <pan/general/log.h>
+#include <pan/gui/prefs.h>
+#include <pan/gui/url.h> //For get_default_editors
+
+#include <glib/gi18n.h> // for _
+#include <gtk/gtk.h>
+
+#include <set>
+
+namespace pan {
+
+namespace
+{
+       typedef struct
+       {
+               char *fname;
+               EditorSpawner::Callback callback;
+       } se_data;
+
+       void child_watch_cb(GPid pid, gint status, gpointer data)
+       {
+               se_data * d{static_cast<se_data *>(data)};
+               g_spawn_close_pid(pid);
+               d->callback(status, d->fname);
+               delete d;
+       }
+}
+
+EditorSpawner::EditorSpawner(
+  char * fname,
+  Callback callback,
+  Prefs const & prefs)
+{
+  // Get the configured editor
+  std::set<std::string> editors;
+  URL :: get_default_editors(editors);
+  std::string const editor{prefs.get_string("editor", *editors.begin())};
+
+  //Create the command line
+  int argc{0};
+  char ** argv{nullptr};
+  GError * err{nullptr};
+  g_shell_parse_argv (editor.c_str(), &argc, &argv, &err);
+  if (err != nullptr) {
+    Log::add_err_va(
+      _("Error parsing \"external editor\" command line: %s (Command was: %s)"),
+      err->message,
+      editor.c_str());
+    g_clear_error (&err);
+    throw EditorSpawnerError();
+  }
+
+  // put temp file's name into the substitution
+  bool filename_added{false};
+  for (int i = 0; i < argc; i += 1) {
+    char * token{argv[i]};
+    char * sub{strstr (token, "%t")};
+    if (sub) {
+      GString * gstr{g_string_new (nullptr)};
+      g_string_append_len(gstr, token, sub - token);
+      g_string_append(gstr, fname);
+      g_string_append(gstr, sub + 2);
+      g_free(token);
+      argv[i] = g_string_free(gstr, false);
+      filename_added = true;
+    }
+  }
+
+  // no substitution field -- add the filename at the end
+  if (not filename_added) {
+    char ** v{g_new(char*, argc + 2)};
+    for (int i = 0; i < argc; i += 1) {
+      v[i] = argv[i];
+    }
+    v[argc] = g_strdup(fname);
+    argc += 1;
+    v[argc] = nullptr;
+    g_free(argv);
+    argv = v;
+  }
+
+  // spawn off the external editor
+  GPid pid;
+  g_spawn_async(nullptr, argv, nullptr,
+                (GSpawnFlags)(G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD),
+                nullptr, nullptr, &pid, &err);
+  g_strfreev(argv);
+
+  if (err != nullptr) {
+    Log::add_err_va(_("Error starting external editor: %s"), err->message);
+    g_clear_error(&err);
+    throw EditorSpawnerError();
+  }
+
+  se_data *data = new se_data;
+  data->fname = fname;
+  data->callback = callback;
+
+  _child_id = g_child_watch_add(pid, child_watch_cb, static_cast<gpointer>(data));
+}
+
+EditorSpawner::~EditorSpawner()
+{
+  g_source_remove(_child_id);
+}
+
+EditorSpawnerError::EditorSpawnerError() :
+  runtime_error("Error spawning editor - see log")
+{
+}
+
+}
diff --git a/pan/general/editor-spawner.h b/pan/general/editor-spawner.h
new file mode 100644
index 0000000..efa5b6c
--- /dev/null
+++ b/pan/general/editor-spawner.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Pan - A Newsreader for Gtk+
+ * Copyright (C) 2002-2006  Charles Kerr <charles rebelbase 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; version 2 of the License.
+ *
+ * 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/>.
+ *
+ */
+
+#ifndef __EditorSpawner_h__
+#define __EditorSpawner_h__
+
+#include <functional>
+#include <stdexcept>
+
+#include <glib.h>
+
+namespace pan {
+
+  class Prefs;
+
+  class EditorSpawner {
+    public:
+      typedef std::function<void(int, char *)> Callback;
+
+      /** Spawns an editor for the named file.
+       *
+       * The callback is actioned when the editor window is closed, and it
+       * should take care of popping the correct window back to the front if
+       * required.
+       *
+       * If there's an error, an exception is thrown and an error message is
+       * logged.
+       */
+      EditorSpawner(char *fname,
+                    Callback callback,
+                    Prefs const & prefs);
+
+      /** Destructor does some cleanup */
+      ~EditorSpawner();
+
+    private:
+      guint _child_id;
+  };
+
+  class EditorSpawnerError : public std::runtime_error
+  {
+    public:
+        EditorSpawnerError();
+  };
+
+}
+
+#endif


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