[glibmm] Gio::Application: Add add_main_option_entry() taking a slot parameter
- From: Kjell Ahlstedt <kjellahl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glibmm] Gio::Application: Add add_main_option_entry() taking a slot parameter
- Date: Wed, 14 May 2014 13:35:50 +0000 (UTC)
commit 9dec09068d5afbe76839bfe7574b670b2a04fd27
Author: Kjell Ahlstedt <kjell ahlstedt bredband net>
Date: Wed May 14 15:27:25 2014 +0200
Gio::Application: Add add_main_option_entry() taking a slot parameter
* gio/src/application.[hg|ccg]: Add add_main_option_entry() and
add_main_option_entry_filename() taking slot parameters.
Add add_main_option_entry_private(). Bug #727822.
gio/src/application.ccg | 208 ++++++++++++++++++++++++++++++++++++++++++++++-
gio/src/application.hg | 47 +++++++++++-
2 files changed, 250 insertions(+), 5 deletions(-)
---
diff --git a/gio/src/application.ccg b/gio/src/application.ccg
index a04d9ba..53fc100 100644
--- a/gio/src/application.ccg
+++ b/gio/src/application.ccg
@@ -22,6 +22,8 @@
#include <giomm/actiongroup.h>
#include <giomm/init.h>
#include <cstring> // std::memset()
+#include <glibmm/threads.h>
+#include <map>
#include <vector>
namespace // anonymous
@@ -128,6 +130,130 @@ static const Glib::SignalProxyInfo Application_signal_open_info =
(GCallback) &Application_signal_open_notify_callback
};
+// The add_main_option_entry*() methods that take a slot parameter are handled
+// similarly to the corresponding Glib::OptionGroup::add_entry*() methods.
+// There is an important difference: In add_main_option_entry*() we can't pass
+// an Application pointer to the used GOptionGroup.
+// g_application_add_main_option_entries() creates a GOptionGroup with user_data == NULL.
+// Therefore Application_option_arg_callback() is called with data == NULL.
+// Application_option_arg_callback() does not know which Application instance
+// the command-line option belongs to. All Application instances (usually only one)
+// share a map, mapping the long command option name to an OptionArgCallbackData.
+class OptionArgCallbackData
+{
+public:
+ explicit OptionArgCallbackData(const Gio::Application* application, gchar short_name,
+ const Glib::OptionGroup::SlotOptionArgString& slot)
+ : application_(application), short_name_(short_name),
+ slot_string_(new Glib::OptionGroup::SlotOptionArgString(slot)), slot_filename_(0)
+ { }
+
+ explicit OptionArgCallbackData(const Gio::Application* application, gchar short_name,
+ const Glib::OptionGroup::SlotOptionArgFilename& slot)
+ : application_(application), short_name_(short_name),
+ slot_string_(0), slot_filename_(new Glib::OptionGroup::SlotOptionArgFilename(slot))
+ { }
+
+ const Gio::Application* get_application() const { return application_; }
+ gchar get_short_name() const { return short_name_; }
+ bool is_filename_option() const { return slot_filename_ != 0; }
+
+ const Glib::OptionGroup::SlotOptionArgString* get_slot_string() const
+ { return slot_string_; }
+
+ const Glib::OptionGroup::SlotOptionArgFilename* get_slot_filename() const
+ { return slot_filename_; }
+
+ ~OptionArgCallbackData()
+ {
+ delete slot_string_;
+ delete slot_filename_;
+ // Don't delete application_. It's not owned by this class.
+ }
+
+private:
+ const Gio::Application* application_;
+ gchar short_name_;
+ // One of these slot pointers is 0 and the other one points to a slot.
+ Glib::OptionGroup::SlotOptionArgString* slot_string_;
+ Glib::OptionGroup::SlotOptionArgFilename* slot_filename_;
+
+ // Not copyable
+ OptionArgCallbackData(const OptionArgCallbackData&);
+ OptionArgCallbackData& operator=(const OptionArgCallbackData&);
+};
+
+typedef std::map<Glib::ustring, OptionArgCallbackData*> OptionArgCallbackDataMap;
+OptionArgCallbackDataMap option_arg_callback_data;
+
+// Gio::Application instances may be used in different threads.
+// Accesses to option_arg_callback_data must be thread-safe.
+Glib::Threads::Mutex option_arg_callback_data_mutex;
+
+gboolean Application_option_arg_callback(const gchar* option_name, const gchar* value,
+ gpointer /* data */, GError** error)
+{
+ const Glib::ustring cpp_option_name(option_name);
+
+ // option_name is either a single dash followed by a single letter (for a
+ // short name) or two dashes followed by a long option name.
+ Glib::Threads::Mutex::Lock lock(option_arg_callback_data_mutex);
+ OptionArgCallbackDataMap::const_iterator iterFind = option_arg_callback_data.end();
+ if (option_name[1] == '-')
+ {
+ // Long option name.
+ const Glib::ustring long_option_name = Glib::ustring(option_name+2);
+ iterFind = option_arg_callback_data.find(long_option_name);
+ }
+ else
+ {
+ // Short option name.
+ const gchar short_option_name = option_name[1];
+ for (iterFind = option_arg_callback_data.begin();
+ iterFind != option_arg_callback_data.end(); ++iterFind)
+ {
+ if (iterFind->second->get_short_name() == short_option_name)
+ break;
+ }
+ }
+
+ if (iterFind == option_arg_callback_data.end())
+ {
+ Glib::OptionError(Glib::OptionError::UNKNOWN_OPTION, "Application_option_arg_callback(): "
+ "Unknown option " + cpp_option_name).propagate(error);
+ return false;
+ }
+
+ const bool has_value = (value != 0);
+ const OptionArgCallbackData* const option_arg = iterFind->second;
+ try
+ {
+ if (option_arg->is_filename_option())
+ {
+ const Glib::OptionGroup::SlotOptionArgFilename* the_slot = option_arg->get_slot_filename();
+ lock.release();
+ const std::string cpp_value(value ? value : "");
+ return (*the_slot)(cpp_option_name, cpp_value, has_value);
+ }
+ else
+ {
+ const Glib::OptionGroup::SlotOptionArgString* the_slot = option_arg->get_slot_string();
+ lock.release();
+ const Glib::ustring cpp_value(value ? value : "");
+ return (*the_slot)(cpp_option_name, cpp_value, has_value);
+ }
+ }
+ catch (Glib::Error& err)
+ {
+ err.propagate(error);
+ }
+ catch (...)
+ {
+ Glib::exception_handlers_invoke();
+ }
+ return false;
+}
+
} // anonymous namespace
namespace Gio
@@ -151,6 +277,23 @@ Application::Application(const Glib::ustring& application_id, ApplicationFlags f
}
+Application::~Application()
+{
+ // Delete all OptionArgCallbackData instances that belong to this application.
+ Glib::Threads::Mutex::Lock lock(option_arg_callback_data_mutex);
+ OptionArgCallbackDataMap::iterator iter = option_arg_callback_data.begin();
+ while (iter != option_arg_callback_data.end())
+ {
+ OptionArgCallbackDataMap::iterator saved_iter = iter;
+ ++iter;
+ if (saved_iter->second->get_application() == this)
+ {
+ delete saved_iter->second;
+ option_arg_callback_data.erase(saved_iter);
+ }
+ }
+}
+
//static
void Application::unset_default()
{
@@ -240,6 +383,49 @@ void Application::open(const Glib::RefPtr<Gio::File>& file, const Glib::ustring&
void Application::add_main_option_entry(OptionType arg_type, const Glib::ustring& long_name,
gchar short_name, const Glib::ustring& description, const Glib::ustring& arg_description, int flags)
{
+ add_main_option_entry_private((GOptionArg)arg_type, long_name, short_name,
+ description, arg_description, flags);
+}
+
+void Application::add_main_option_entry(
+ const Glib::OptionGroup::SlotOptionArgString& slot, const Glib::ustring& long_name,
+ gchar short_name, const Glib::ustring& description,
+ const Glib::ustring& arg_description, int flags)
+{
+ Glib::Threads::Mutex::Lock lock(option_arg_callback_data_mutex);
+ OptionArgCallbackDataMap::iterator iterFind = option_arg_callback_data.find(long_name);
+ if (iterFind != option_arg_callback_data.end())
+ return; // Ignore duplicates
+
+ OptionArgCallbackData* callback_data = new OptionArgCallbackData(this, short_name, slot);
+ option_arg_callback_data[long_name] = callback_data;
+ lock.release();
+
+ add_main_option_entry_private(G_OPTION_ARG_CALLBACK, long_name, short_name,
+ description, arg_description, flags & ~Glib::OptionEntry::FLAG_FILENAME);
+}
+
+void Application::add_main_option_entry_filename(
+ const Glib::OptionGroup::SlotOptionArgFilename& slot, const Glib::ustring& long_name,
+ gchar short_name, const Glib::ustring& description,
+ const Glib::ustring& arg_description, int flags)
+{
+ Glib::Threads::Mutex::Lock lock(option_arg_callback_data_mutex);
+ OptionArgCallbackDataMap::iterator iterFind = option_arg_callback_data.find(long_name);
+ if (iterFind != option_arg_callback_data.end())
+ return; // Ignore duplicates
+
+ OptionArgCallbackData* callback_data = new OptionArgCallbackData(this, short_name, slot);
+ option_arg_callback_data[long_name] = callback_data;
+ lock.release();
+
+ add_main_option_entry_private(G_OPTION_ARG_CALLBACK, long_name, short_name,
+ description, arg_description, flags | Glib::OptionEntry::FLAG_FILENAME);
+}
+
+void Application::add_main_option_entry_private(GOptionArg arg, const Glib::ustring& long_name,
+ gchar short_name, const Glib::ustring& description, const Glib::ustring& arg_description, int flags)
+{
// Create a temporary array, just so we can give the correct thing to
g_application_add_main_option_entries():
GOptionEntry array[2];
std::memset(array, 0, 2 * sizeof(GOptionEntry)); // null-termination
@@ -269,15 +455,29 @@ void Application::add_main_option_entry(OptionType arg_type, const Glib::ustring
extra_application_data->option_entry_strings.push_back(arg_desc);
// Fill in array[0].
- array[0].arg = (GOptionArg)arg_type;
+ array[0].arg = arg;
array[0].long_name = lname;
array[0].short_name = short_name;
array[0].description = desc;
array[0].arg_description = arg_desc;
array[0].flags = flags;
- // We ensure that this is null to ensure that it is not used,
- // telling GApplication to put the parsed value in the options VariantDict instead.
- array[0].arg_data = 0;
+
+ if (arg == G_OPTION_ARG_CALLBACK)
+ {
+ // GoptionEntry.arg_data is a function pointer, cast to void*.
+ // See Glib::OptionGroup::CppOptionEntry::allocate_c_arg() for a discussion
+ // of this hack.
+ union {
+ void* dp;
+ GOptionArgFunc fp;
+ } u;
+ u.fp = &Application_option_arg_callback;
+ array[0].arg_data = u.dp;
+ }
+ else
+ // We ensure that this is null to ensure that it is not used,
+ // telling GApplication to put the parsed value in the options VariantDict instead.
+ array[0].arg_data = 0;
g_application_add_main_option_entries(gobj(), array);
}
diff --git a/gio/src/application.hg b/gio/src/application.hg
index e926744..045d6cf 100644
--- a/gio/src/application.hg
+++ b/gio/src/application.hg
@@ -28,6 +28,7 @@
#include <giomm/file.h>
#include <glibmm/object.h>
#include <glibmm/optionentry.h>
+#include <glibmm/optiongroup.h>
#include <glibmm/variant.h>
#include <glibmm/variantdict.h>
#include <giomm/dbusconnection.h>
@@ -104,6 +105,8 @@ protected:
_IGNORE(g_application_new)
public:
+ _CUSTOM_DTOR()
+
/** The OptionType enum values determine the expected type of a command line option.
* If an option expects an extra argument, it can be specified in several ways;
* with a short option: "-x arg", with a long option: "--name arg" or combined
@@ -224,6 +227,42 @@ public:
const Glib::ustring& arg_description = Glib::ustring(), int flags = 0);
_IGNORE(g_application_add_main_option_entries)
+ /** Adds a main option entry to be handled by the Application.
+ *
+ * Adds a string option entry, but lets the callback @a slot parse the extra
+ * argument instead of having it packed in a Glib::VariantDict.
+ *
+ * If you create more than one Application instance (unusual),
+ * one Application instance can't add an option with the same name as
+ * another instance adds. This restriction does not apply to the
+ * add_main_option_entry() that takes an OptionType parameter.
+ *
+ * @see add_main_option_entry(OptionType, const Glib::ustring&,
+ * gchar, const Glib::ustring&, const Glib::ustring&, int)
+ */
+ void add_main_option_entry(const Glib::OptionGroup::SlotOptionArgString& slot,
+ const Glib::ustring& long_name,
+ gchar short_name = '\0', const Glib::ustring& description = Glib::ustring(),
+ const Glib::ustring& arg_description = Glib::ustring(), int flags = 0);
+
+ /** Adds a main option entry to be handled by the Application.
+ *
+ * Adds a filename option entry, but lets the callback @a slot parse the extra
+ * argument instead of having it packed in a Glib::VariantDict.
+ *
+ * If you create more than one Application instance (unusual),
+ * one Application instance can't add an option with the same name as
+ * another instance adds. This restriction does not apply to the
+ * add_main_option_entry() that takes an OptionType parameter.
+ *
+ * @see add_main_option_entry(OptionType, const Glib::ustring&,
+ * gchar, const Glib::ustring&, const Glib::ustring&, int)
+ */
+ void add_main_option_entry_filename(const Glib::OptionGroup::SlotOptionArgFilename& slot,
+ const Glib::ustring& long_name,
+ gchar short_name = '\0', const Glib::ustring& description = Glib::ustring(),
+ const Glib::ustring& arg_description = Glib::ustring(), int flags = 0);
+
// _WRAP_METHOD(void add_option_group(Glib::OptionGroup& group), g_application_add_option_group)
// add_option_group() is probably not very useful. If implemented, it must probably
// be custom-implemented. See https://bugzilla.gnome.org/show_bug.cgi?id=727822#c10
@@ -321,7 +360,8 @@ public:
//TODO: Remove no_default_handler when we can break ABI
//TODO: Avoid the use of the Variants in the VariantDict?
- //TODO: Should options definitely be non-const? Confirm that the handler is meant to modify it.
+ //options must be non-const. The handler is meant to modify it. See the description
+ //of add_main_option_entry(OptionType, ...).
#m4 _CONVERSION(`GVariantDict*',`const Glib::RefPtr<Glib::VariantDict>&',`Glib::wrap($3, true)')
_WRAP_SIGNAL(int handle_local_options(const Glib::RefPtr<Glib::VariantDict>& options),
"handle-local-options", no_default_handler)
@@ -359,6 +399,11 @@ private:
* See https://bugzilla.gnome.org/show_bug.cgi?id=639925
*/
const Glib::Class& custom_class_init();
+
+ // Code, common to the public add_main_option_entry*() methods.
+ void add_main_option_entry_private(GOptionArg arg, const Glib::ustring& long_name,
+ gchar short_name, const Glib::ustring& description,
+ const Glib::ustring& arg_description, int flags);
};
} // namespace Gio
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]