[glib] New gapplication(1) tool



commit 9defb6b1b1de18b6005148e036941e89b400dbd2
Author: Ryan Lortie <desrt desrt ca>
Date:   Sun Jul 14 19:43:19 2013 -0400

    New gapplication(1) tool
    
    This is essentially a commandline implementation of the client-side of
    the org.freedesktop.Application D-Bus interface.
    
    It includes support for tab-completion based on desktop files and their
    contents.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=704218

 docs/reference/gio/Makefile.am      |    1 +
 docs/reference/gio/gapplication.xml |  352 ++++++++++++++++++++++++++
 gio/.gitignore                      |    1 +
 gio/Makefile.am                     |    9 +
 gio/completion/gapplication         |   55 ++++
 gio/gapplication-tool.c             |  463 +++++++++++++++++++++++++++++++++++
 6 files changed, 881 insertions(+), 0 deletions(-)
---
diff --git a/docs/reference/gio/Makefile.am b/docs/reference/gio/Makefile.am
index 112626b..42907ca 100644
--- a/docs/reference/gio/Makefile.am
+++ b/docs/reference/gio/Makefile.am
@@ -146,6 +146,7 @@ man_MANS =
 if ENABLE_MAN
 
 man_MANS +=                    \
+       gapplication.1          \
        gio-querymodules.1      \
        glib-compile-schemas.1  \
        glib-compile-resources.1        \
diff --git a/docs/reference/gio/gapplication.xml b/docs/reference/gio/gapplication.xml
new file mode 100644
index 0000000..13e3f23
--- /dev/null
+++ b/docs/reference/gio/gapplication.xml
@@ -0,0 +1,352 @@
+<refentry id="gapplication-tool" lang="en">
+  <refentryinfo>
+    <title>gapplication</title>
+    <productname>GIO</productname>
+    <authorgroup>
+      <author>
+        <contrib>Developer</contrib>
+        <firstname>Ryan</firstname>
+        <surname>Lortie</surname>
+      </author>
+    </authorgroup>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>gapplication</refentrytitle>
+    <manvolnum>1</manvolnum>
+    <refmiscinfo class="manual">User Commands</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>gapplication</refname>
+    <refpurpose>D-Bus application launcher</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>gapplication</command>
+      <arg choice="plain">help</arg>
+      <arg choice="opt"><replaceable>COMMAND</replaceable></arg>
+    </cmdsynopsis>
+    <cmdsynopsis>
+      <command>gapplication</command>
+      <arg choice="plain">version</arg>
+    </cmdsynopsis>
+    <cmdsynopsis>
+      <command>gapplication</command>
+      <arg choice="plain">list-apps</arg>
+    </cmdsynopsis>
+    <cmdsynopsis>
+      <command>gapplication</command>
+      <arg choice="plain">launch</arg>
+      <arg choice="plain"><replaceable>APPID</replaceable></arg>
+    </cmdsynopsis>
+    <cmdsynopsis>
+      <command>gapplication</command>
+      <arg choice="plain">launch</arg>
+      <arg choice="plain"><replaceable>APPID</replaceable></arg>
+      <arg choice="opt" rep="repeat"><replaceable>FILE</replaceable></arg>
+    </cmdsynopsis>
+    <cmdsynopsis>
+      <command>gapplication</command>
+      <arg choice="plain">list-actions</arg>
+      <arg choice="plain"><replaceable>APPID</replaceable></arg>
+    </cmdsynopsis>
+    <cmdsynopsis>
+      <command>gapplication</command>
+      <arg choice="plain">action</arg>
+      <arg choice="plain"><replaceable>APPID</replaceable></arg>
+      <arg choice="plain"><replaceable>ACTION</replaceable></arg>
+      <arg choice="opt"><replaceable>PARAMETER</replaceable></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>
+      <command>gapplication</command> is a commandline implementation of the client-side of the
+      <interfacename>org.freedesktop.Application</interfacename> interface as specified by the 
freedesktop.org
+      Desktop Entry Specification.
+    </para>
+
+    <para>
+      <command>gapplication</command> can be used to start applications that have
+      <varname>DBusActivatable</varname> set to <literal>true</literal> in their 
<filename>.desktop</filename>
+      files and can be used to send messages to already-running instances of other applications.
+    </para>
+
+    <para>
+      It is possible for applications to refer to <command>gapplication</command> in the 
<varname>Exec</varname>
+      line of their <filename class='extension'>.desktop</filename> file to maintain backwards compatibility
+      with implementations that do not directly support <varname>DBusActivatable</varname>.
+    </para>
+
+    <para>
+      <command>gapplication</command> ships as part of <application>GLib</application>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Commands</title>
+
+    <refsect2>
+      <title>Global commands</title>
+
+      <variablelist>
+        <varlistentry>
+          <term>
+            <command>help</command>
+            <arg choice="opt"><replaceable>COMMAND</replaceable></arg>
+          </term>
+          <listitem>
+            <para>
+              Displays a short synopsis of the available commands or provides detailed help on a specific
+              command.
+            </para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>
+            <command>version</command>
+          </term>
+          <listitem>
+            <para>
+              Prints the GLib version whence <command>gapplication</command> came.
+            </para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>
+            <command>list-apps</command>
+          </term>
+          <listitem>
+            <para>
+              Prints a list of all application IDs that are known to support D-Bus activation.  This list is
+              generated by scanning <filename class='extension'>.desktop</filename> files as per the current
+              <envar>XDG_DATA_DIRS</envar>.
+            </para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>
+            <command>launch</command>
+            <arg choice="plain"><replaceable>APPID</replaceable></arg>
+            <arg choice="opt" rep="repeat"><replaceable>FILE</replaceable></arg>
+          </term>
+          <listitem>
+            <para>
+              Launches an application.
+            </para>
+            <para> 
+              The first parameter is the application ID in the familiar "reverse DNS" style (eg:
+              '<literal>org.gnome.app</literal>') without the <filename class='extension'>.desktop</filename>
+              suffix.
+            </para>
+            <para>
+              Optionally, if additional parameters are given, they are treated as the names of files to open 
and
+              may be filenames or URIs.  If no files are given then the application is simply activated.
+            </para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>
+            <command>list-actions</command>
+            <arg choice="plain"><replaceable>APPID</replaceable></arg>
+          </term>
+          <listitem>
+            <para>
+              List the actions declared in the application's <filename class='extension'>.desktop</filename>
+              file.  The parameter is the application ID, as above.
+            </para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>
+            <command>action</command>
+            <arg choice="plain"><replaceable>APPID</replaceable></arg>
+            <arg choice="plain"><replaceable>ACTION</replaceable></arg>
+            <arg choice="opt"><replaceable>PARAMETER</replaceable></arg>
+          </term>
+          <listitem>
+            <para>
+              Invokes the named action (in the same way as would occur when activating an action specified in
+              the <filename class='extension'>.desktop</filename> file).
+            </para>
+            <para>
+              The application ID (as above) is the first parameter.  The action name follows.
+            </para>
+            <para>
+              Optionally, following the action name can be one parameter, in GVariant format, given as a 
single
+              argument.  Make sure to use sufficient quoting.
+            </para>
+          </listitem>
+        </varlistentry>
+
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <refsect2>
+      <title>From the commandline</title>
+
+      <para>
+        Launching an application:
+      </para>
+
+      <programlisting>
+        gapplication launch org.example.fooview
+      </programlisting>
+
+      <para>
+        Opening a file with an application:
+      </para>
+
+      <programlisting>
+        gapplication launch org.example.fooview ~/file.foo
+      </programlisting>
+
+      <para>
+        Opening many files with an application:
+      </para>
+
+      <programlisting>
+        gapplication launch org.example.fooview ~/foos/*.foo
+      </programlisting>
+
+      <para>
+        Invoking an action on an application:
+      </para>
+
+      <programlisting>
+        gapplication action org.example.fooview create
+      </programlisting>
+
+      <para>
+        Invoking an action on an application, with an action:
+      </para>
+
+      <programlisting>
+        gapplication action org.example.fooview show-item '"item_id_828739"'
+      </programlisting>
+    </refsect2>
+
+    <refsect2>
+      <title>
+        From the <varname>Exec</varname> lines of a <filename class='extension'>.desktop</filename> file
+      </title>
+
+      <para>
+        The commandline interface of <command>gapplication</command> was designed so that it could be used
+        directly from the <varname>Exec</varname> line of a <filename class='extension'>.desktop</filename>
+        file.
+      </para>
+
+      <para>
+        You might want to do this to allow for backwards compatibility with implementations of the 
specification
+        that do not understand how to do D-Bus activation, without having to install a separate utility 
program.
+      </para>
+
+      <para>
+        Consider the following example:
+      </para>
+
+      <programlisting>
+        [Desktop Entry]
+        Version=1.1
+        Type=Application
+        Name=Foo Viewer
+        DBusActivatable=true
+        MimeType=image/x-foo;
+        Exec=gapplication launch org.example.fooview %F
+        Actions=gallery;create;
+
+        [Desktop Action gallery]
+        Name=Browse Gallery
+        Exec=gapplication action org.example.fooview gallery
+
+        [Desktop Action create]
+        Name=Create a new Foo!
+        Exec=gapplication action org.example.fooview create
+      </programlisting>
+    </refsect2>
+
+    <refsect2>
+      <title>From a script</title>
+
+      <para>
+        If installing an application that supports D-Bus activation you may still want to put a file in
+        <filename class='directory'>/usr/bin</filename> so that your program can be started from a terminal.
+      </para>
+
+      <para>
+        It is possible for this file to be a shell script.  The script can handle arguments such as --help 
and
+        --version directly.  It can also parse other command line arguments and convert them to uses of
+        <command>gapplication</command> to activate the application, open files, or invoke actions.
+      </para>
+
+      <para>
+        Here is a simplified example, as may be installed in <filename>/usr/bin/fooview</filename>:
+      </para>
+
+      <programlisting>
+        #!/bin/sh
+
+        case "$1" in
+          --help)
+            echo "see 'man fooview' for more information"
+            ;;
+
+          --version)
+            echo "fooview 1.2"
+            ;;
+
+          --gallery)
+            gapplication action org.example.fooview gallery
+            ;;
+
+          --create)
+            gapplication action org.example.fooview create
+            ;;
+
+          -*)
+            echo "unrecognised commandline argument"
+            exit 1
+            ;;
+
+          *)
+            gapplication launch org.example.fooview "$@"
+            ;;
+        esac
+      </programlisting>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>See also</title>
+    <para>
+      <ulink url='http://standards.freedesktop.org/desktop-entry-spec/latest/'>Desktop Entry 
Specification</ulink>,
+      <citerefentry>
+        <refentrytitle>gdbus</refentrytitle>
+        <manvolnum>1</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>xdg-open</refentrytitle>
+        <manvolnum>1</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>desktop-file-validate</refentrytitle>
+        <manvolnum>1</manvolnum>
+      </citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
diff --git a/gio/.gitignore b/gio/.gitignore
index be25b26..5aa5588 100644
--- a/gio/.gitignore
+++ b/gio/.gitignore
@@ -14,3 +14,4 @@ gnetworking.h
 gresource
 gschema-compile
 gsettings
+gapplication
diff --git a/gio/Makefile.am b/gio/Makefile.am
index ba596e5..7d24966 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -720,8 +720,17 @@ gdbus_LDADD = libgio-2.0.la \
        $(top_builddir)/glib/libglib-2.0.la             \
        $(top_builddir)/gobject/libgobject-2.0.la
 
+# ------------------------------------------------------------------------
+# gapplication(1) tool
+bin_PROGRAMS += gapplication
+gapplication_SOURCES = gapplication-tool.c
+gapplication_LDADD = libgio-2.0.la \
+       $(top_builddir)/glib/libglib-2.0.la             \
+       $(top_builddir)/gobject/libgobject-2.0.la
+
 completiondir = $(datadir)/bash-completion/completions
 completion_DATA = \
+       completion/gapplication                         \
        completion/gdbus                                \
        completion/gsettings                            \
        completion/gresource
diff --git a/gio/completion/gapplication b/gio/completion/gapplication
new file mode 100644
index 0000000..565025b
--- /dev/null
+++ b/gio/completion/gapplication
@@ -0,0 +1,55 @@
+
+# Check for bash
+[ -z "$BASH_VERSION" ] && return
+
+####################################################################################################
+
+__app() {
+  case "${COMP_CWORD}" in
+    1)
+      COMPREPLY=($(compgen -W "help version list-apps launch action list-actions" -- "${COMP_WORDS[1]}"))
+      return 0
+      ;;
+
+    2)
+      case "${COMP_WORDS[1]}" in
+        launch|action|list-actions)
+          COMPREPLY=($(compgen -W "`gapplication list-apps`" -- "${COMP_WORDS[2]}"))
+          return 0
+          ;;
+
+        *)
+          COMPREPLY=()
+          return 0
+          ;;
+      esac
+      ;;
+  esac
+
+  # Otherwise, what we will do is based on the command in ${COMP_WORDS[1]}
+  case "${COMP_WORDS[1]}" in
+    action)
+      # Word 3 is the action name.  This is the only one we can help with.
+      if [ "${COMP_CWORD}" == 3 ]; then
+        COMPREPLY=($(compgen -W "`gapplication list-actions "${COMP_WORDS[2]}"`" -- "${COMP_WORDS[3]}"))
+        return 0
+      else
+        COMPREPLY=()
+        return 0
+      fi
+      ;;
+    launch)
+      # Filenames...
+      COMPREPLY=($(compgen -A file "${COMP_WORDS[COMP_CWORD]}"))
+      return 0
+      ;;
+    *)
+      # Nothing else should be out this far...
+      COMPREPLY=()
+      return 0
+  esac
+}
+
+####################################################################################################
+
+complete -F __app gapplication
diff --git a/gio/gapplication-tool.c b/gio/gapplication-tool.c
new file mode 100644
index 0000000..c152909
--- /dev/null
+++ b/gio/gapplication-tool.c
@@ -0,0 +1,463 @@
+/*
+ * Copyright © 2013 Canonical Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ */
+
+#include "config.h"
+
+#include <gio/gdesktopappinfo.h>
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include <string.h>
+#include <locale.h>
+
+struct help_topic
+{
+  const gchar *command;
+  const gchar *summary;
+  const gchar *description;
+  const gchar *synopsis;
+};
+
+struct help_substvar
+{
+  const gchar *var;
+  const gchar *description;
+};
+
+static const struct help_topic topics[] = {
+  { "help",         N_("Print help"),
+                    N_("Print help"),
+                    N_("[COMMAND]")
+  },
+  { "version",      N_("Print version"),
+                    N_("Print version information and exit")
+  },
+  { "list-apps",    N_("List applications"),
+                    N_("List the installed D-Bus activatable applications (by .desktop files)")
+  },
+  { "launch",       N_("Launch an application"),
+                    N_("Launch the application (with optional files to open)"),
+                    N_("APPID [FILE...]")
+  },
+  { "action",       N_("Activate an action"),
+                    N_("Invoke an action on the application"),
+                    N_("APPID ACTION [PARAMETER]")
+  },
+  { "list-actions", N_("List available actions"),
+                    N_("List static actions for an application (from .desktop file)"),
+                    N_("APPID")
+  }
+};
+
+static const struct help_substvar substvars[] = {
+  { N_("COMMAND"),   N_("The command to print detailed help for")                             },
+  { N_("APPID"),     N_("Application identifier in D-Bus format (eg: org.example.viewer)")    },
+  { N_("FILE"),      N_("Optional relative or relative filenames, or URIs to open")           },
+  { N_("ACTION"),    N_("The action name to invoke")                                          },
+  { N_("PARAMETER"), N_("Optional parameter to the action invocation, in GVariant format")    }
+};
+
+static int
+app_help (gboolean     requested,
+          const gchar *command)
+{
+  const struct help_topic *topic = NULL;
+  GString *string;
+
+  string = g_string_new (NULL);
+
+  if (command)
+    {
+      gint i;
+
+      for (i = 0; i < G_N_ELEMENTS (topics); i++)
+        if (g_str_equal (topics[i].command, command))
+          topic = &topics[i];
+
+      if (!topic)
+        {
+          g_string_printf (string, _("Unknown command %s\n\n"), command);
+          requested = FALSE;
+        }
+    }
+
+  g_string_append (string, _("Usage:\n"));
+
+  if (topic)
+    {
+      gint maxwidth;
+      gint i;
+
+      g_string_append_printf (string, "\n  %s %s %s\n\n", "gapplication",
+                              topic->command, topic->synopsis ? _(topic->synopsis) : "");
+      g_string_append_printf (string, "%s\n\n", _(topic->description));
+
+      if (topic->synopsis)
+        {
+          g_string_append (string, _("Arguments:\n"));
+
+          maxwidth = 0;
+          for (i = 0; i < G_N_ELEMENTS (substvars); i++)
+            if (strstr (topic->synopsis, substvars[i].var))
+              maxwidth = MAX(maxwidth, strlen (_(substvars[i].var)));
+
+          for (i = 0; i < G_N_ELEMENTS (substvars); i++)
+            if (strstr (topic->synopsis, substvars[i].var))
+              g_string_append_printf (string, "  %-*.*s   %s\n", maxwidth, maxwidth,
+                                      _(substvars[i].var), _(substvars[i].description));
+          g_string_append (string, "\n");
+        }
+    }
+  else
+    {
+      gint maxwidth;
+      gint i;
+
+      g_string_append_printf (string, "\n  %s %s %s\n\n", "gapplication", _("COMMAND"), _("[ARGS...]"));
+      g_string_append_printf (string, _("Commands:\n"));
+
+      maxwidth = 0;
+      for (i = 0; i < G_N_ELEMENTS (topics); i++)
+        maxwidth = MAX(maxwidth, strlen (topics[i].command));
+
+      for (i = 0; i < G_N_ELEMENTS (topics); i++)
+        g_string_append_printf (string, "  %-*.*s   %s\n", maxwidth, maxwidth,
+                                topics[i].command, _(topics[i].summary));
+
+      g_string_append (string, "\n");
+      /* Translators: do not translate 'help', but please translate 'COMMAND'. */
+      g_string_append_printf (string, _("Use '%s help COMMAND' to get detailed help.\n\n"), "gapplication");
+    }
+
+  if (requested)
+    g_print ("%s", string->str);
+  else
+    g_printerr ("%s\n", string->str);
+
+  g_string_free (string, TRUE);
+
+  return requested ? 0 : 1;
+}
+
+static gboolean
+app_check_name (gchar       **args,
+                const gchar  *command)
+{
+  if (args[0] == NULL)
+    {
+      g_printerr (_("%s command requires an application id to directly follow\n\n"), command);
+      return FALSE;
+    }
+
+  if (!g_dbus_is_name (args[0]))
+    {
+      g_printerr (_("invalid application id: '%s'\n"), args[0]);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static int
+app_no_args (const gchar *command)
+{
+  /* Translators: %s is replaced with a command name like 'list-actions' */
+  g_printerr (_("'%s' takes no arguments\n\n"), command);
+  return app_help (FALSE, command);
+}
+
+static int
+app_version (gchar **args)
+{
+  if (g_strv_length (args))
+    return app_no_args ("version");
+
+  g_print (PACKAGE_VERSION "\n");
+  return 0;
+}
+
+static int
+app_list (gchar **args)
+{
+  GList *apps;
+
+  if (g_strv_length (args))
+    return app_no_args ("list");
+
+  apps = g_app_info_get_all ();
+
+  while (apps)
+    {
+      GDesktopAppInfo *info = apps->data;
+
+      if (G_IS_DESKTOP_APP_INFO (info))
+        if (g_desktop_app_info_get_boolean (info, "DBusActivatable"))
+          {
+            const gchar *filename;
+
+            filename = g_app_info_get_id (G_APP_INFO (info));
+            if (g_str_has_suffix (filename, ".desktop"))
+              {
+                gchar *id;
+
+                id = g_strndup (filename, strlen (filename) - 8);
+                g_print ("%s\n", id);
+                g_free (id);
+              }
+          }
+
+      apps = g_list_delete_link (apps, apps);
+      g_object_unref (info);
+    }
+
+  return 0;
+}
+
+static gchar *
+app_path_for_id (const gchar *app_id)
+{
+  gchar *path;
+  gint i;
+
+  path = g_strconcat ("/", app_id, NULL);
+  for (i = 0; path[i]; i++)
+    if (path[i] == '.')
+      path[i] = '/';
+
+  return path;
+}
+
+static int
+app_call (const gchar *app_id,
+          const gchar *method_name,
+          GVariant    *parameters)
+{
+  GDBusConnection *session;
+  GError *error = NULL;
+  gchar *object_path;
+  GVariant *result;
+
+
+  session = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+  if (!session)
+    {
+      g_variant_unref (g_variant_ref_sink (parameters));
+      g_printerr (_("unable to connect to D-Bus: %s\n"), error->message);
+      g_error_free (error);
+      return 1;
+    }
+
+  object_path = app_path_for_id (app_id);
+
+  result = g_dbus_connection_call_sync (session, app_id, object_path, "org.freedesktop.Application",
+                                        method_name, parameters, G_VARIANT_TYPE_UNIT,
+                                        G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
+
+  g_free (object_path);
+
+  if (result)
+    {
+      g_variant_unref (result);
+      return 0;
+    }
+  else
+    {
+      g_printerr (_("error sending %s message to application: %s\n"), method_name, error->message);
+      g_error_free (error);
+      return 1;
+    }
+}
+
+static GVariant *
+app_get_platform_data (void)
+{
+  GVariantBuilder builder;
+  const gchar *startup_id;
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+
+  if ((startup_id = g_getenv ("DESKTOP_STARTUP_iD")))
+    g_variant_builder_add (&builder, "{sv}", "desktop-startup-id", g_variant_new_string (startup_id));
+
+  return g_variant_builder_end (&builder);
+}
+
+static int
+app_action (gchar **args)
+{
+  GVariantBuilder params;
+  const gchar *name;
+
+  if (!app_check_name (args, "action"))
+    return 1;
+
+  if (args[1] == NULL)
+    {
+      g_printerr (_("action name must be given after application id\n"));
+      return 1;
+    }
+
+  name = args[1];
+
+  if (!g_action_name_is_valid (name))
+    {
+      g_printerr (_("invalid action name: '%s'\n"
+                    "action names must consist of only alphanumerics, '-' and '.'\n"), name);
+      return 1;
+    }
+
+  g_variant_builder_init (&params, G_VARIANT_TYPE ("av"));
+
+  if (args[2])
+    {
+      GError *error = NULL;
+      GVariant *parameter;
+
+      parameter = g_variant_parse (NULL, args[2], NULL, NULL, &error);
+
+      if (!parameter)
+        {
+          g_printerr (_("error parsing action parameter: %s\n"), error->message);
+          g_variant_builder_clear (&params);
+          g_error_free (error);
+          return 1;
+        }
+
+      g_variant_builder_add (&params, "v", parameter);
+      g_variant_unref (parameter);
+
+      if (args[3])
+        {
+          g_printerr (_("actions accept a maximum of one parameter\n"));
+          g_variant_builder_clear (&params);
+          return 1;
+        }
+    }
+
+  return app_call (args[0], "ActivateAction", g_variant_new ("(sav a{sv})", name, &params, 
app_get_platform_data ()));
+}
+
+static int
+app_activate (const gchar *app_id)
+{
+  return app_call (app_id, "Activate", g_variant_new ("(@a{sv})", app_get_platform_data ()));
+}
+
+static int
+app_launch (gchar **args)
+{
+  GVariantBuilder files;
+  gint i;
+
+  if (!app_check_name (args, "launch"))
+    return 1;
+
+  if (args[1] == NULL)
+    return app_activate (args[0]);
+
+  g_variant_builder_init (&files, G_VARIANT_TYPE_STRING_ARRAY);
+
+  for (i = 1; args[i]; i++)
+    {
+      GFile *file;
+
+      /* "This operation never fails" */
+      file = g_file_new_for_commandline_arg (args[i]);
+      g_variant_builder_add_value (&files, g_variant_new_take_string (g_file_get_uri (file)));
+      g_object_unref (file);
+    }
+
+  return app_call (args[0], "Open", g_variant_new ("(as a{sv})", &files, app_get_platform_data ()));
+}
+
+static int
+app_list_actions (gchar **args)
+{
+  const gchar * const *actions;
+  GDesktopAppInfo *app_info;
+  gchar *filename;
+  gint i;
+
+  if (!app_check_name (args, "list-actions"))
+    return 1;
+
+  if (args[1])
+    {
+      g_printerr (_("list-actions command takes only the application id"));
+      app_help (FALSE, "list-actions");
+    }
+
+  filename = g_strconcat (args[0], ".desktop", NULL);
+  app_info = g_desktop_app_info_new (filename);
+  g_free (filename);
+
+  if (app_info == NULL)
+    {
+      g_printerr (_("unable to find desktop file for application %s\n"), args[0]);
+      return 1;
+    }
+
+  actions = g_desktop_app_info_list_actions (app_info);
+
+  for (i = 0; actions[i]; i++)
+    g_print ("%s\n", actions[i]);
+
+  g_object_unref (app_info);
+
+  return 0;
+}
+
+int
+main (int argc, char **argv)
+{
+  setlocale (LC_ALL, "");
+  textdomain (GETTEXT_PACKAGE);
+  bindtextdomain (GETTEXT_PACKAGE, GLIB_LOCALE_DIR);
+#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif
+
+  if (argc < 2)
+    return app_help (TRUE, NULL);
+
+  if (g_str_equal (argv[1], "help"))
+    return app_help (TRUE, argv[2]);
+
+  if (g_str_equal (argv[1], "version"))
+    return app_version (argv + 2);
+
+  if (g_str_equal (argv[1], "list-apps"))
+    return app_list (argv + 2);
+
+  if (g_str_equal (argv[1], "launch"))
+    return app_launch (argv + 2);
+
+  if (g_str_equal (argv[1], "action"))
+    return app_action (argv + 2);
+
+  if (g_str_equal (argv[1], "list-actions"))
+    return app_list_actions (argv + 2);
+
+  g_printerr (_("unrecognised command: %s\n\n"), argv[1]);
+
+  return app_help (FALSE, NULL);
+}


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