|
|
|
From e799dc8f049da855bf05edd6f994d802cd59668f Mon Sep 17 00:00:00 2001
|
|
|
|
From: Ray Strode <rstrode@redhat.com>
|
|
|
|
Date: Fri, 8 May 2015 16:24:59 -0400
|
|
|
|
Subject: [PATCH 02/19] Revert "Remove gnome-session-properties"
|
|
|
|
|
|
|
|
This reverts commit ea285af9962313ee2675fff27d3a852bb61e936a.
|
|
|
|
---
|
|
|
|
capplet/gsm-app-dialog.c | 540 +++++++++
|
|
|
|
capplet/gsm-app-dialog.h | 66 ++
|
|
|
|
capplet/gsm-properties-dialog.c | 774 +++++++++++++
|
|
|
|
capplet/gsm-properties-dialog.h | 57 +
|
|
|
|
capplet/gsp-app-manager.c | 593 ++++++++++
|
|
|
|
capplet/gsp-app-manager.h | 81 ++
|
|
|
|
capplet/gsp-app.c | 1102 +++++++++++++++++++
|
|
|
|
capplet/gsp-app.h | 108 ++
|
|
|
|
capplet/gsp-keyfile.c | 201 ++++
|
|
|
|
capplet/gsp-keyfile.h | 65 ++
|
|
|
|
capplet/main.c | 108 ++
|
|
|
|
capplet/meson.build | 36 +
|
|
|
|
data/gnome-session-properties.desktop.in.in | 15 +
|
|
|
|
data/meson.build | 13 +
|
|
|
|
doc/man/gnome-session-properties.1 | 24 +
|
|
|
|
meson.build | 1 +
|
|
|
|
po/POTFILES.in | 5 +
|
|
|
|
17 files changed, 3789 insertions(+)
|
|
|
|
create mode 100644 capplet/gsm-app-dialog.c
|
|
|
|
create mode 100644 capplet/gsm-app-dialog.h
|
|
|
|
create mode 100644 capplet/gsm-properties-dialog.c
|
|
|
|
create mode 100644 capplet/gsm-properties-dialog.h
|
|
|
|
create mode 100644 capplet/gsp-app-manager.c
|
|
|
|
create mode 100644 capplet/gsp-app-manager.h
|
|
|
|
create mode 100644 capplet/gsp-app.c
|
|
|
|
create mode 100644 capplet/gsp-app.h
|
|
|
|
create mode 100644 capplet/gsp-keyfile.c
|
|
|
|
create mode 100644 capplet/gsp-keyfile.h
|
|
|
|
create mode 100644 capplet/main.c
|
|
|
|
create mode 100644 capplet/meson.build
|
|
|
|
create mode 100644 data/gnome-session-properties.desktop.in.in
|
|
|
|
create mode 100644 doc/man/gnome-session-properties.1
|
|
|
|
|
|
|
|
diff --git a/capplet/gsm-app-dialog.c b/capplet/gsm-app-dialog.c
|
|
|
|
new file mode 100644
|
|
|
|
index 00000000..e7369dda
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/capplet/gsm-app-dialog.c
|
|
|
|
@@ -0,0 +1,540 @@
|
|
|
|
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
|
|
|
+ *
|
|
|
|
+ * Copyright (C) 2008 William Jon McCann <jmccann@redhat.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 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
|
|
|
|
+ * 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, write to the Free Software
|
|
|
|
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#include "config.h"
|
|
|
|
+
|
|
|
|
+#include <glib.h>
|
|
|
|
+#include <glib/gi18n.h>
|
|
|
|
+#include <gtk/gtk.h>
|
|
|
|
+
|
|
|
|
+#include "gsm-util.h"
|
|
|
|
+
|
|
|
|
+#include "gsm-app-dialog.h"
|
|
|
|
+
|
|
|
|
+#define GTKBUILDER_FILE "session-properties.ui"
|
|
|
|
+
|
|
|
|
+#define CAPPLET_NAME_ENTRY_WIDGET_NAME "session_properties_name_entry"
|
|
|
|
+#define CAPPLET_COMMAND_ENTRY_WIDGET_NAME "session_properties_command_entry"
|
|
|
|
+#define CAPPLET_COMMENT_ENTRY_WIDGET_NAME "session_properties_comment_entry"
|
|
|
|
+#define CAPPLET_BROWSE_WIDGET_NAME "session_properties_browse_button"
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+#define GSM_APP_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_APP_DIALOG, GsmAppDialogPrivate))
|
|
|
|
+
|
|
|
|
+struct GsmAppDialogPrivate
|
|
|
|
+{
|
|
|
|
+ GtkWidget *name_entry;
|
|
|
|
+ GtkWidget *command_entry;
|
|
|
|
+ GtkWidget *comment_entry;
|
|
|
|
+ GtkWidget *browse_button;
|
|
|
|
+ char *name;
|
|
|
|
+ char *command;
|
|
|
|
+ char *comment;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static void gsm_app_dialog_class_init (GsmAppDialogClass *klass);
|
|
|
|
+static void gsm_app_dialog_init (GsmAppDialog *app_dialog);
|
|
|
|
+static void gsm_app_dialog_finalize (GObject *object);
|
|
|
|
+
|
|
|
|
+enum {
|
|
|
|
+ PROP_0,
|
|
|
|
+ PROP_NAME,
|
|
|
|
+ PROP_COMMAND,
|
|
|
|
+ PROP_COMMENT
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+G_DEFINE_TYPE (GsmAppDialog, gsm_app_dialog, GTK_TYPE_DIALOG)
|
|
|
|
+
|
|
|
|
+static char *
|
|
|
|
+make_exec_uri (const char *exec)
|
|
|
|
+{
|
|
|
|
+ GString *str;
|
|
|
|
+ const char *c;
|
|
|
|
+
|
|
|
|
+ if (exec == NULL) {
|
|
|
|
+ return g_strdup ("");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (strchr (exec, ' ') == NULL) {
|
|
|
|
+ return g_strdup (exec);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ str = g_string_new_len (NULL, strlen (exec));
|
|
|
|
+
|
|
|
|
+ str = g_string_append_c (str, '"');
|
|
|
|
+ for (c = exec; *c != '\0'; c++) {
|
|
|
|
+ /* FIXME: GKeyFile will add an additional backslach so we'll
|
|
|
|
+ * end up with toto\\" instead of toto\"
|
|
|
|
+ * We could use g_key_file_set_value(), but then we don't
|
|
|
|
+ * benefit from the other escaping that glib is doing...
|
|
|
|
+ */
|
|
|
|
+ if (*c == '"') {
|
|
|
|
+ str = g_string_append (str, "\\\"");
|
|
|
|
+ } else {
|
|
|
|
+ str = g_string_append_c (str, *c);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ str = g_string_append_c (str, '"');
|
|
|
|
+
|
|
|
|
+ return g_string_free (str, FALSE);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+on_browse_button_clicked (GtkWidget *widget,
|
|
|
|
+ GsmAppDialog *dialog)
|
|
|
|
+{
|
|
|
|
+ GtkWidget *chooser;
|
|
|
|
+ int response;
|
|
|
|
+
|
|
|
|
+ chooser = gtk_file_chooser_dialog_new ("",
|
|
|
|
+ GTK_WINDOW (dialog),
|
|
|
|
+ GTK_FILE_CHOOSER_ACTION_OPEN,
|
|
|
|
+ GTK_STOCK_CANCEL,
|
|
|
|
+ GTK_RESPONSE_CANCEL,
|
|
|
|
+ GTK_STOCK_OPEN,
|
|
|
|
+ GTK_RESPONSE_ACCEPT,
|
|
|
|
+ NULL);
|
|
|
|
+
|
|
|
|
+ gtk_window_set_transient_for (GTK_WINDOW (chooser),
|
|
|
|
+ GTK_WINDOW (dialog));
|
|
|
|
+
|
|
|
|
+ gtk_window_set_destroy_with_parent (GTK_WINDOW (chooser), TRUE);
|
|
|
|
+
|
|
|
|
+ gtk_window_set_title (GTK_WINDOW (chooser), _("Select Command"));
|
|
|
|
+
|
|
|
|
+ gtk_widget_show (chooser);
|
|
|
|
+
|
|
|
|
+ response = gtk_dialog_run (GTK_DIALOG (chooser));
|
|
|
|
+
|
|
|
|
+ if (response == GTK_RESPONSE_ACCEPT) {
|
|
|
|
+ char *text;
|
|
|
|
+ char *uri;
|
|
|
|
+
|
|
|
|
+ text = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
|
|
|
|
+
|
|
|
|
+ uri = make_exec_uri (text);
|
|
|
|
+
|
|
|
|
+ g_free (text);
|
|
|
|
+
|
|
|
|
+ gtk_entry_set_text (GTK_ENTRY (dialog->priv->command_entry), uri);
|
|
|
|
+
|
|
|
|
+ g_free (uri);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ gtk_widget_destroy (chooser);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+on_entry_activate (GtkEntry *entry,
|
|
|
|
+ GsmAppDialog *dialog)
|
|
|
|
+{
|
|
|
|
+ gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+setup_dialog (GsmAppDialog *dialog)
|
|
|
|
+{
|
|
|
|
+ GtkWidget *content_area;
|
|
|
|
+ GtkWidget *widget;
|
|
|
|
+ GtkBuilder *xml;
|
|
|
|
+ GError *error;
|
|
|
|
+
|
|
|
|
+ xml = gtk_builder_new ();
|
|
|
|
+ gtk_builder_set_translation_domain (xml, GETTEXT_PACKAGE);
|
|
|
|
+
|
|
|
|
+ error = NULL;
|
|
|
|
+ if (!gtk_builder_add_from_file (xml,
|
|
|
|
+ GTKBUILDER_DIR "/" GTKBUILDER_FILE,
|
|
|
|
+ &error)) {
|
|
|
|
+ if (error) {
|
|
|
|
+ g_warning ("Could not load capplet UI file: %s",
|
|
|
|
+ error->message);
|
|
|
|
+ g_error_free (error);
|
|
|
|
+ } else {
|
|
|
|
+ g_warning ("Could not load capplet UI file.");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
|
|
|
|
+ widget = GTK_WIDGET (gtk_builder_get_object (xml, "main-table"));
|
|
|
|
+ gtk_container_add (GTK_CONTAINER (content_area), widget);
|
|
|
|
+
|
|
|
|
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
|
|
|
|
+ gtk_window_set_icon_name (GTK_WINDOW (dialog), "session-properties");
|
|
|
|
+
|
|
|
|
+ g_object_set (dialog,
|
|
|
|
+ "allow-shrink", FALSE,
|
|
|
|
+ "allow-grow", FALSE,
|
|
|
|
+ NULL);
|
|
|
|
+
|
|
|
|
+ gtk_dialog_add_button (GTK_DIALOG (dialog),
|
|
|
|
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
|
|
|
|
+
|
|
|
|
+ if (dialog->priv->name == NULL
|
|
|
|
+ && dialog->priv->command == NULL
|
|
|
|
+ && dialog->priv->comment == NULL) {
|
|
|
|
+ gtk_window_set_title (GTK_WINDOW (dialog), _("Add Startup Program"));
|
|
|
|
+ gtk_dialog_add_button (GTK_DIALOG (dialog),
|
|
|
|
+ GTK_STOCK_ADD, GTK_RESPONSE_OK);
|
|
|
|
+ } else {
|
|
|
|
+ gtk_window_set_title (GTK_WINDOW (dialog), _("Edit Startup Program"));
|
|
|
|
+ gtk_dialog_add_button (GTK_DIALOG (dialog),
|
|
|
|
+ GTK_STOCK_SAVE, GTK_RESPONSE_OK);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ dialog->priv->name_entry = GTK_WIDGET (gtk_builder_get_object (xml, CAPPLET_NAME_ENTRY_WIDGET_NAME));
|
|
|
|
+ g_signal_connect (dialog->priv->name_entry,
|
|
|
|
+ "activate",
|
|
|
|
+ G_CALLBACK (on_entry_activate),
|
|
|
|
+ dialog);
|
|
|
|
+ if (dialog->priv->name != NULL) {
|
|
|
|
+ gtk_entry_set_text (GTK_ENTRY (dialog->priv->name_entry), dialog->priv->name);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ dialog->priv->browse_button = GTK_WIDGET (gtk_builder_get_object (xml, CAPPLET_BROWSE_WIDGET_NAME));
|
|
|
|
+ g_signal_connect (dialog->priv->browse_button,
|
|
|
|
+ "clicked",
|
|
|
|
+ G_CALLBACK (on_browse_button_clicked),
|
|
|
|
+ dialog);
|
|
|
|
+
|
|
|
|
+ dialog->priv->command_entry = GTK_WIDGET (gtk_builder_get_object (xml, CAPPLET_COMMAND_ENTRY_WIDGET_NAME));
|
|
|
|
+ g_signal_connect (dialog->priv->command_entry,
|
|
|
|
+ "activate",
|
|
|
|
+ G_CALLBACK (on_entry_activate),
|
|
|
|
+ dialog);
|
|
|
|
+ if (dialog->priv->command != NULL) {
|
|
|
|
+ gtk_entry_set_text (GTK_ENTRY (dialog->priv->command_entry), dialog->priv->command);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ dialog->priv->comment_entry = GTK_WIDGET (gtk_builder_get_object (xml, CAPPLET_COMMENT_ENTRY_WIDGET_NAME));
|
|
|
|
+ g_signal_connect (dialog->priv->comment_entry,
|
|
|
|
+ "activate",
|
|
|
|
+ G_CALLBACK (on_entry_activate),
|
|
|
|
+ dialog);
|
|
|
|
+ if (dialog->priv->comment != NULL) {
|
|
|
|
+ gtk_entry_set_text (GTK_ENTRY (dialog->priv->comment_entry), dialog->priv->comment);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (xml != NULL) {
|
|
|
|
+ g_object_unref (xml);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static GObject *
|
|
|
|
+gsm_app_dialog_constructor (GType type,
|
|
|
|
+ guint n_construct_app,
|
|
|
|
+ GObjectConstructParam *construct_app)
|
|
|
|
+{
|
|
|
|
+ GsmAppDialog *dialog;
|
|
|
|
+
|
|
|
|
+ dialog = GSM_APP_DIALOG (G_OBJECT_CLASS (gsm_app_dialog_parent_class)->constructor (type,
|
|
|
|
+ n_construct_app,
|
|
|
|
+ construct_app));
|
|
|
|
+
|
|
|
|
+ setup_dialog (dialog);
|
|
|
|
+
|
|
|
|
+ gtk_widget_show_all (GTK_WIDGET (dialog));
|
|
|
|
+
|
|
|
|
+ return G_OBJECT (dialog);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+gsm_app_dialog_dispose (GObject *object)
|
|
|
|
+{
|
|
|
|
+ GsmAppDialog *dialog;
|
|
|
|
+
|
|
|
|
+ g_return_if_fail (object != NULL);
|
|
|
|
+ g_return_if_fail (GSM_IS_APP_DIALOG (object));
|
|
|
|
+
|
|
|
|
+ dialog = GSM_APP_DIALOG (object);
|
|
|
|
+
|
|
|
|
+ g_free (dialog->priv->name);
|
|
|
|
+ dialog->priv->name = NULL;
|
|
|
|
+ g_free (dialog->priv->command);
|
|
|
|
+ dialog->priv->command = NULL;
|
|
|
|
+ g_free (dialog->priv->comment);
|
|
|
|
+ dialog->priv->comment = NULL;
|
|
|
|
+
|
|
|
|
+ G_OBJECT_CLASS (gsm_app_dialog_parent_class)->dispose (object);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+gsm_app_dialog_set_name (GsmAppDialog *dialog,
|
|
|
|
+ const char *name)
|
|
|
|
+{
|
|
|
|
+ g_return_if_fail (GSM_IS_APP_DIALOG (dialog));
|
|
|
|
+
|
|
|
|
+ g_free (dialog->priv->name);
|
|
|
|
+
|
|
|
|
+ dialog->priv->name = g_strdup (name);
|
|
|
|
+ g_object_notify (G_OBJECT (dialog), "name");
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+gsm_app_dialog_set_command (GsmAppDialog *dialog,
|
|
|
|
+ const char *name)
|
|
|
|
+{
|
|
|
|
+ g_return_if_fail (GSM_IS_APP_DIALOG (dialog));
|
|
|
|
+
|
|
|
|
+ g_free (dialog->priv->command);
|
|
|
|
+
|
|
|
|
+ dialog->priv->command = g_strdup (name);
|
|
|
|
+ g_object_notify (G_OBJECT (dialog), "command");
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+gsm_app_dialog_set_comment (GsmAppDialog *dialog,
|
|
|
|
+ const char *name)
|
|
|
|
+{
|
|
|
|
+ g_return_if_fail (GSM_IS_APP_DIALOG (dialog));
|
|
|
|
+
|
|
|
|
+ g_free (dialog->priv->comment);
|
|
|
|
+
|
|
|
|
+ dialog->priv->comment = g_strdup (name);
|
|
|
|
+ g_object_notify (G_OBJECT (dialog), "comment");
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const char *
|
|
|
|
+gsm_app_dialog_get_name (GsmAppDialog *dialog)
|
|
|
|
+{
|
|
|
|
+ g_return_val_if_fail (GSM_IS_APP_DIALOG (dialog), NULL);
|
|
|
|
+ return gtk_entry_get_text (GTK_ENTRY (dialog->priv->name_entry));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const char *
|
|
|
|
+gsm_app_dialog_get_command (GsmAppDialog *dialog)
|
|
|
|
+{
|
|
|
|
+ g_return_val_if_fail (GSM_IS_APP_DIALOG (dialog), NULL);
|
|
|
|
+ return gtk_entry_get_text (GTK_ENTRY (dialog->priv->command_entry));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const char *
|
|
|
|
+gsm_app_dialog_get_comment (GsmAppDialog *dialog)
|
|
|
|
+{
|
|
|
|
+ g_return_val_if_fail (GSM_IS_APP_DIALOG (dialog), NULL);
|
|
|
|
+ return gtk_entry_get_text (GTK_ENTRY (dialog->priv->comment_entry));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+gsm_app_dialog_set_property (GObject *object,
|
|
|
|
+ guint prop_id,
|
|
|
|
+ const GValue *value,
|
|
|
|
+ GParamSpec *pspec)
|
|
|
|
+{
|
|
|
|
+ GsmAppDialog *dialog = GSM_APP_DIALOG (object);
|
|
|
|
+
|
|
|
|
+ switch (prop_id) {
|
|
|
|
+ case PROP_NAME:
|
|
|
|
+ gsm_app_dialog_set_name (dialog, g_value_get_string (value));
|
|
|
|
+ break;
|
|
|
|
+ case PROP_COMMAND:
|
|
|
|
+ gsm_app_dialog_set_command (dialog, g_value_get_string (value));
|
|
|
|
+ break;
|
|
|
|
+ case PROP_COMMENT:
|
|
|
|
+ gsm_app_dialog_set_comment (dialog, g_value_get_string (value));
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+gsm_app_dialog_get_property (GObject *object,
|
|
|
|
+ guint prop_id,
|
|
|
|
+ GValue *value,
|
|
|
|
+ GParamSpec *pspec)
|
|
|
|
+{
|
|
|
|
+ GsmAppDialog *dialog = GSM_APP_DIALOG (object);
|
|
|
|
+
|
|
|
|
+ switch (prop_id) {
|
|
|
|
+ case PROP_NAME:
|
|
|
|
+ g_value_set_string (value, dialog->priv->name);
|
|
|
|
+ break;
|
|
|
|
+ case PROP_COMMAND:
|
|
|
|
+ g_value_set_string (value, dialog->priv->command);
|
|
|
|
+ break;
|
|
|
|
+ case PROP_COMMENT:
|
|
|
|
+ g_value_set_string (value, dialog->priv->comment);
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+gsm_app_dialog_class_init (GsmAppDialogClass *klass)
|
|
|
|
+{
|
|
|
|
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
+
|
|
|
|
+ object_class->get_property = gsm_app_dialog_get_property;
|
|
|
|
+ object_class->set_property = gsm_app_dialog_set_property;
|
|
|
|
+ object_class->constructor = gsm_app_dialog_constructor;
|
|
|
|
+ object_class->dispose = gsm_app_dialog_dispose;
|
|
|
|
+ object_class->finalize = gsm_app_dialog_finalize;
|
|
|
|
+
|
|
|
|
+ g_object_class_install_property (object_class,
|
|
|
|
+ PROP_NAME,
|
|
|
|
+ g_param_spec_string ("name",
|
|
|
|
+ "name",
|
|
|
|
+ "name",
|
|
|
|
+ NULL,
|
|
|
|
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
|
|
|
|
+ g_object_class_install_property (object_class,
|
|
|
|
+ PROP_COMMAND,
|
|
|
|
+ g_param_spec_string ("command",
|
|
|
|
+ "command",
|
|
|
|
+ "command",
|
|
|
|
+ NULL,
|
|
|
|
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
|
|
|
|
+ g_object_class_install_property (object_class,
|
|
|
|
+ PROP_COMMENT,
|
|
|
|
+ g_param_spec_string ("comment",
|
|
|
|
+ "comment",
|
|
|
|
+ "comment",
|
|
|
|
+ NULL,
|
|
|
|
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
|
|
|
|
+
|
|
|
|
+ g_type_class_add_private (klass, sizeof (GsmAppDialogPrivate));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+gsm_app_dialog_init (GsmAppDialog *dialog)
|
|
|
|
+{
|
|
|
|
+
|
|
|
|
+ dialog->priv = GSM_APP_DIALOG_GET_PRIVATE (dialog);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+gsm_app_dialog_finalize (GObject *object)
|
|
|
|
+{
|
|
|
|
+ GsmAppDialog *dialog;
|
|
|
|
+
|
|
|
|
+ g_return_if_fail (object != NULL);
|
|
|
|
+ g_return_if_fail (GSM_IS_APP_DIALOG (object));
|
|
|
|
+
|
|
|
|
+ dialog = GSM_APP_DIALOG (object);
|
|
|
|
+
|
|
|
|
+ g_return_if_fail (dialog->priv != NULL);
|
|
|
|
+
|
|
|
|
+ G_OBJECT_CLASS (gsm_app_dialog_parent_class)->finalize (object);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+GtkWidget *
|
|
|
|
+gsm_app_dialog_new (const char *name,
|
|
|
|
+ const char *command,
|
|
|
|
+ const char *comment)
|
|
|
|
+{
|
|
|
|
+ GObject *object;
|
|
|
|
+
|
|
|
|
+ object = g_object_new (GSM_TYPE_APP_DIALOG,
|
|
|
|
+ "name", name,
|
|
|
|
+ "command", command,
|
|
|
|
+ "comment", comment,
|
|
|
|
+ NULL);
|
|
|
|
+
|
|
|
|
+ return GTK_WIDGET (object);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+gboolean
|
|
|
|
+gsm_app_dialog_run (GsmAppDialog *dialog,
|
|
|
|
+ char **name_p,
|
|
|
|
+ char **command_p,
|
|
|
|
+ char **comment_p)
|
|
|
|
+{
|
|
|
|
+ gboolean retval;
|
|
|
|
+
|
|
|
|
+ retval = FALSE;
|
|
|
|
+
|
|
|
|
+ while (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
|
|
|
|
+ const char *name;
|
|
|
|
+ const char *exec;
|
|
|
|
+ const char *comment;
|
|
|
|
+ const char *error_msg;
|
|
|
|
+ GError *error;
|
|
|
|
+ char **argv;
|
|
|
|
+ int argc;
|
|
|
|
+
|
|
|
|
+ name = gsm_app_dialog_get_name (GSM_APP_DIALOG (dialog));
|
|
|
|
+ exec = gsm_app_dialog_get_command (GSM_APP_DIALOG (dialog));
|
|
|
|
+ comment = gsm_app_dialog_get_comment (GSM_APP_DIALOG (dialog));
|
|
|
|
+
|
|
|
|
+ error = NULL;
|
|
|
|
+ error_msg = NULL;
|
|
|
|
+
|
|
|
|
+ if (gsm_util_text_is_blank (exec)) {
|
|
|
|
+ error_msg = _("The startup command cannot be empty");
|
|
|
|
+ } else {
|
|
|
|
+ if (!g_shell_parse_argv (exec, &argc, &argv, &error)) {
|
|
|
|
+ if (error != NULL) {
|
|
|
|
+ error_msg = error->message;
|
|
|
|
+ } else {
|
|
|
|
+ error_msg = _("The startup command is not valid");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (error_msg != NULL) {
|
|
|
|
+ GtkWidget *msgbox;
|
|
|
|
+
|
|
|
|
+ msgbox = gtk_message_dialog_new (GTK_WINDOW (dialog),
|
|
|
|
+ GTK_DIALOG_MODAL,
|
|
|
|
+ GTK_MESSAGE_ERROR,
|
|
|
|
+ GTK_BUTTONS_CLOSE,
|
|
|
|
+ "%s", error_msg);
|
|
|
|
+
|
|
|
|
+ if (error != NULL) {
|
|
|
|
+ g_error_free (error);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ gtk_dialog_run (GTK_DIALOG (msgbox));
|
|
|
|
+
|
|
|
|
+ gtk_widget_destroy (msgbox);
|
|
|
|
+
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (gsm_util_text_is_blank (name)) {
|
|
|
|
+ name = argv[0];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (name_p) {
|
|
|
|
+ *name_p = g_strdup (name);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_strfreev (argv);
|
|
|
|
+
|
|
|
|
+ if (command_p) {
|
|
|
|
+ *command_p = g_strdup (exec);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (comment_p) {
|
|
|
|
+ *comment_p = g_strdup (comment);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ retval = TRUE;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ gtk_widget_destroy (GTK_WIDGET (dialog));
|
|
|
|
+
|
|
|
|
+ return retval;
|
|
|
|
+}
|
|
|
|
diff --git a/capplet/gsm-app-dialog.h b/capplet/gsm-app-dialog.h
|
|
|
|
new file mode 100644
|
|
|
|
index 00000000..ced0628c
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/capplet/gsm-app-dialog.h
|
|
|
|
@@ -0,0 +1,66 @@
|
|
|
|
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
|
|
|
+ *
|
|
|
|
+ * Copyright (C) 2008 William Jon McCann <jmccann@redhat.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 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
|
|
|
|
+ * 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, write to the Free Software
|
|
|
|
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#ifndef __GSM_APP_DIALOG_H
|
|
|
|
+#define __GSM_APP_DIALOG_H
|
|
|
|
+
|
|
|
|
+#include <glib-object.h>
|
|
|
|
+#include <gtk/gtk.h>
|
|
|
|
+
|
|
|
|
+G_BEGIN_DECLS
|
|
|
|
+
|
|
|
|
+#define GSM_TYPE_APP_DIALOG (gsm_app_dialog_get_type ())
|
|
|
|
+#define GSM_APP_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSM_TYPE_APP_DIALOG, GsmAppDialog))
|
|
|
|
+#define GSM_APP_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSM_TYPE_APP_DIALOG, GsmAppDialogClass))
|
|
|
|
+#define GSM_IS_APP_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSM_TYPE_APP_DIALOG))
|
|
|
|
+#define GSM_IS_APP_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSM_TYPE_APP_DIALOG))
|
|
|
|
+#define GSM_APP_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSM_TYPE_APP_DIALOG, GsmAppDialogClass))
|
|
|
|
+
|
|
|
|
+typedef struct GsmAppDialogPrivate GsmAppDialogPrivate;
|
|
|
|
+
|
|
|
|
+typedef struct
|
|
|
|
+{
|
|
|
|
+ GtkDialog parent;
|
|
|
|
+ GsmAppDialogPrivate *priv;
|
|
|
|
+} GsmAppDialog;
|
|
|
|
+
|
|
|
|
+typedef struct
|
|
|
|
+{
|
|
|
|
+ GtkDialogClass parent_class;
|
|
|
|
+} GsmAppDialogClass;
|
|
|
|
+
|
|
|
|
+GType gsm_app_dialog_get_type (void);
|
|
|
|
+
|
|
|
|
+GtkWidget * gsm_app_dialog_new (const char *name,
|
|
|
|
+ const char *command,
|
|
|
|
+ const char *comment);
|
|
|
|
+
|
|
|
|
+gboolean gsm_app_dialog_run (GsmAppDialog *dialog,
|
|
|
|
+ char **name_p,
|
|
|
|
+ char **command_p,
|
|
|
|
+ char **comment_p);
|
|
|
|
+
|
|
|
|
+const char * gsm_app_dialog_get_name (GsmAppDialog *dialog);
|
|
|
|
+const char * gsm_app_dialog_get_command (GsmAppDialog *dialog);
|
|
|
|
+const char * gsm_app_dialog_get_comment (GsmAppDialog *dialog);
|
|
|
|
+
|
|
|
|
+G_END_DECLS
|
|
|
|
+
|
|
|
|
+#endif /* __GSM_APP_DIALOG_H */
|
|
|
|
diff --git a/capplet/gsm-properties-dialog.c b/capplet/gsm-properties-dialog.c
|
|
|
|
new file mode 100644
|
|
|
|
index 00000000..33812b8b
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/capplet/gsm-properties-dialog.c
|
|
|
|
@@ -0,0 +1,774 @@
|
|
|
|
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
|
|
|
+ *
|
|
|
|
+ * Copyright (C) 1999 Free Software Foundation, Inc.
|
|
|
|
+ * Copyright (C) 2007 Vincent Untz.
|
|
|
|
+ * Copyright (C) 2008 Lucas Rocha.
|
|
|
|
+ * Copyright (C) 2008 William Jon McCann <jmccann@redhat.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 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
|
|
|
|
+ * 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, write to the Free Software
|
|
|
|
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#include "config.h"
|
|
|
|
+
|
|
|
|
+#include <glib.h>
|
|
|
|
+#include <glib/gi18n.h>
|
|
|
|
+#include <gtk/gtk.h>
|
|
|
|
+
|
|
|
|
+#include "gsm-properties-dialog.h"
|
|
|
|
+#include "gsm-app-dialog.h"
|
|
|
|
+#include "gsm-util.h"
|
|
|
|
+#include "gsp-app.h"
|
|
|
|
+#include "gsp-app-manager.h"
|
|
|
|
+
|
|
|
|
+#define GSM_PROPERTIES_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialogPrivate))
|
|
|
|
+
|
|
|
|
+#define GTKBUILDER_FILE "session-properties.ui"
|
|
|
|
+
|
|
|
|
+#define CAPPLET_TREEVIEW_WIDGET_NAME "session_properties_treeview"
|
|
|
|
+#define CAPPLET_ADD_WIDGET_NAME "session_properties_add_button"
|
|
|
|
+#define CAPPLET_DELETE_WIDGET_NAME "session_properties_delete_button"
|
|
|
|
+#define CAPPLET_EDIT_WIDGET_NAME "session_properties_edit_button"
|
|
|
|
+#define CAPPLET_SAVE_WIDGET_NAME "session_properties_save_button"
|
|
|
|
+#define CAPPLET_REMEMBER_WIDGET_NAME "session_properties_remember_toggle"
|
|
|
|
+
|
|
|
|
+#define STARTUP_APP_ICON "system-run"
|
|
|
|
+
|
|
|
|
+#define SPC_SETTINGS_SCHEMA "org.gnome.SessionManager"
|
|
|
|
+#define SPC_SETTINGS_AUTOSAVE_KEY "auto-save-session"
|
|
|
|
+
|
|
|
|
+struct GsmPropertiesDialogPrivate
|
|
|
|
+{
|
|
|
|
+ GtkBuilder *xml;
|
|
|
|
+ GtkListStore *list_store;
|
|
|
|
+ GtkTreeModel *tree_filter;
|
|
|
|
+
|
|
|
|
+ GtkTreeView *treeview;
|
|
|
|
+ GtkWidget *add_button;
|
|
|
|
+ GtkWidget *delete_button;
|
|
|
|
+ GtkWidget *edit_button;
|
|
|
|
+
|
|
|
|
+ GSettings *settings;
|
|
|
|
+
|
|
|
|
+ GspAppManager *manager;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+enum {
|
|
|
|
+ STORE_COL_VISIBLE = 0,
|
|
|
|
+ STORE_COL_ENABLED,
|
|
|
|
+ STORE_COL_GICON,
|
|
|
|
+ STORE_COL_DESCRIPTION,
|
|
|
|
+ STORE_COL_APP,
|
|
|
|
+ STORE_COL_SEARCH,
|
|
|
|
+ NUMBER_OF_COLUMNS
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static void gsm_properties_dialog_class_init (GsmPropertiesDialogClass *klass);
|
|
|
|
+static void gsm_properties_dialog_init (GsmPropertiesDialog *properties_dialog);
|
|
|
|
+static void gsm_properties_dialog_finalize (GObject *object);
|
|
|
|
+
|
|
|
|
+G_DEFINE_TYPE (GsmPropertiesDialog, gsm_properties_dialog, GTK_TYPE_DIALOG)
|
|
|
|
+
|
|
|
|
+static gboolean
|
|
|
|
+find_by_app (GtkTreeModel *model,
|
|
|
|
+ GtkTreeIter *iter,
|
|
|
|
+ GspApp *app)
|
|
|
|
+{
|
|
|
|
+ GspApp *iter_app = NULL;
|
|
|
|
+
|
|
|
|
+ if (!gtk_tree_model_get_iter_first (model, iter)) {
|
|
|
|
+ return FALSE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ do {
|
|
|
|
+ gtk_tree_model_get (model, iter,
|
|
|
|
+ STORE_COL_APP, &iter_app,
|
|
|
|
+ -1);
|
|
|
|
+
|
|
|
|
+ if (iter_app == app) {
|
|
|
|
+ g_object_unref (iter_app);
|
|
|
|
+ return TRUE;
|
|
|
|
+ }
|
|
|
|
+ } while (gtk_tree_model_iter_next (model, iter));
|
|
|
|
+
|
|
|
|
+ return FALSE;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+_fill_iter_from_app (GtkListStore *list_store,
|
|
|
|
+ GtkTreeIter *iter,
|
|
|
|
+ GspApp *app)
|
|
|
|
+{
|
|
|
|
+ gboolean hidden;
|
|
|
|
+ gboolean display;
|
|
|
|
+ gboolean enabled;
|
|
|
|
+ gboolean shown;
|
|
|
|
+ GIcon *icon;
|
|
|
|
+ const char *description;
|
|
|
|
+ const char *app_name;
|
|
|
|
+
|
|
|
|
+ hidden = gsp_app_get_hidden (app);
|
|
|
|
+ display = gsp_app_get_display (app);
|
|
|
|
+ enabled = gsp_app_get_enabled (app);
|
|
|
|
+ shown = gsp_app_get_shown (app);
|
|
|
|
+ icon = gsp_app_get_icon (app);
|
|
|
|
+ description = gsp_app_get_description (app);
|
|
|
|
+ app_name = gsp_app_get_name (app);
|
|
|
|
+
|
|
|
|
+ if (G_IS_THEMED_ICON (icon)) {
|
|
|
|
+ GtkIconTheme *theme;
|
|
|
|
+ const char * const *icon_names;
|
|
|
|
+
|
|
|
|
+ theme = gtk_icon_theme_get_default ();
|
|
|
|
+ icon_names = g_themed_icon_get_names (G_THEMED_ICON (icon));
|
|
|
|
+ if (icon_names[0] == NULL ||
|
|
|
|
+ !gtk_icon_theme_has_icon (theme, icon_names[0])) {
|
|
|
|
+ g_object_unref (icon);
|
|
|
|
+ icon = NULL;
|
|
|
|
+ }
|
|
|
|
+ } else if (G_IS_FILE_ICON (icon)) {
|
|
|
|
+ GFile *iconfile;
|
|
|
|
+
|
|
|
|
+ iconfile = g_file_icon_get_file (G_FILE_ICON (icon));
|
|
|
|
+ if (!g_file_query_exists (iconfile, NULL)) {
|
|
|
|
+ g_object_unref (icon);
|
|
|
|
+ icon = NULL;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (icon == NULL) {
|
|
|
|
+ icon = g_themed_icon_new (STARTUP_APP_ICON);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ gtk_list_store_set (list_store, iter,
|
|
|
|
+ STORE_COL_VISIBLE, !hidden && shown && display,
|
|
|
|
+ STORE_COL_ENABLED, enabled,
|
|
|
|
+ STORE_COL_GICON, icon,
|
|
|
|
+ STORE_COL_DESCRIPTION, description,
|
|
|
|
+ STORE_COL_APP, app,
|
|
|
|
+ STORE_COL_SEARCH, app_name,
|
|
|
|
+ -1);
|
|
|
|
+ g_object_unref (icon);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+_app_changed (GsmPropertiesDialog *dialog,
|
|
|
|
+ GspApp *app)
|
|
|
|
+{
|
|
|
|
+ GtkTreeIter iter;
|
|
|
|
+
|
|
|
|
+ if (!find_by_app (GTK_TREE_MODEL (dialog->priv->list_store),
|
|
|
|
+ &iter, app)) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ _fill_iter_from_app (dialog->priv->list_store, &iter, app);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+append_app (GsmPropertiesDialog *dialog,
|
|
|
|
+ GspApp *app)
|
|
|
|
+{
|
|
|
|
+ GtkTreeIter iter;
|
|
|
|
+
|
|
|
|
+ gtk_list_store_append (dialog->priv->list_store, &iter);
|
|
|
|
+ _fill_iter_from_app (dialog->priv->list_store, &iter, app);
|
|
|
|
+
|
|
|
|
+ g_signal_connect_swapped (app, "changed",
|
|
|
|
+ G_CALLBACK (_app_changed), dialog);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+_app_added (GsmPropertiesDialog *dialog,
|
|
|
|
+ GspApp *app,
|
|
|
|
+ GspAppManager *manager)
|
|
|
|
+{
|
|
|
|
+ append_app (dialog, app);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+_app_removed (GsmPropertiesDialog *dialog,
|
|
|
|
+ GspApp *app,
|
|
|
|
+ GspAppManager *manager)
|
|
|
|
+{
|
|
|
|
+ GtkTreeIter iter;
|
|
|
|
+
|
|
|
|
+ if (!find_by_app (GTK_TREE_MODEL (dialog->priv->list_store),
|
|
|
|
+ &iter, app)) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_signal_handlers_disconnect_by_func (app,
|
|
|
|
+ _app_changed,
|
|
|
|
+ dialog);
|
|
|
|
+ gtk_list_store_remove (dialog->priv->list_store, &iter);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+populate_model (GsmPropertiesDialog *dialog)
|
|
|
|
+{
|
|
|
|
+ GSList *apps;
|
|
|
|
+ GSList *l;
|
|
|
|
+
|
|
|
|
+ apps = gsp_app_manager_get_apps (dialog->priv->manager);
|
|
|
|
+ for (l = apps; l != NULL; l = l->next) {
|
|
|
|
+ append_app (dialog, GSP_APP (l->data));
|
|
|
|
+ }
|
|
|
|
+ g_slist_free (apps);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+on_selection_changed (GtkTreeSelection *selection,
|
|
|
|
+ GsmPropertiesDialog *dialog)
|
|
|
|
+{
|
|
|
|
+ gboolean sel;
|
|
|
|
+
|
|
|
|
+ sel = gtk_tree_selection_get_selected (selection, NULL, NULL);
|
|
|
|
+
|
|
|
|
+ gtk_widget_set_sensitive (dialog->priv->edit_button, sel);
|
|
|
|
+ gtk_widget_set_sensitive (dialog->priv->delete_button, sel);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+on_startup_enabled_toggled (GtkCellRendererToggle *cell_renderer,
|
|
|
|
+ char *path,
|
|
|
|
+ GsmPropertiesDialog *dialog)
|
|
|
|
+{
|
|
|
|
+ GtkTreeIter iter;
|
|
|
|
+ GspApp *app;
|
|
|
|
+ gboolean active;
|
|
|
|
+
|
|
|
|
+ if (!gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (dialog->priv->tree_filter),
|
|
|
|
+ &iter, path)) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ app = NULL;
|
|
|
|
+ gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->tree_filter),
|
|
|
|
+ &iter,
|
|
|
|
+ STORE_COL_APP, &app,
|
|
|
|
+ -1);
|
|
|
|
+
|
|
|
|
+ active = gtk_cell_renderer_toggle_get_active (cell_renderer);
|
|
|
|
+ active = !active;
|
|
|
|
+
|
|
|
|
+ if (app) {
|
|
|
|
+ gsp_app_set_enabled (app, active);
|
|
|
|
+ g_object_unref (app);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+on_drag_data_received (GtkWidget *widget,
|
|
|
|
+ GdkDragContext *drag_context,
|
|
|
|
+ gint x,
|
|
|
|
+ gint y,
|
|
|
|
+ GtkSelectionData *data,
|
|
|
|
+ guint info,
|
|
|
|
+ guint time,
|
|
|
|
+ GsmPropertiesDialog *dialog)
|
|
|
|
+{
|
|
|
|
+ gboolean dnd_success;
|
|
|
|
+
|
|
|
|
+ dnd_success = FALSE;
|
|
|
|
+
|
|
|
|
+ if (data != NULL) {
|
|
|
|
+ char **filenames;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ filenames = gtk_selection_data_get_uris (data);
|
|
|
|
+
|
|
|
|
+ for (i = 0; filenames[i] && filenames[i][0]; i++) {
|
|
|
|
+ /* Return success if at least one file succeeded */
|
|
|
|
+ gboolean file_success;
|
|
|
|
+ file_success = gsp_app_copy_desktop_file (filenames[i]);
|
|
|
|
+ dnd_success = dnd_success || file_success;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_strfreev (filenames);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ gtk_drag_finish (drag_context, dnd_success, FALSE, time);
|
|
|
|
+ g_signal_stop_emission_by_name (widget, "drag_data_received");
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+on_drag_begin (GtkWidget *widget,
|
|
|
|
+ GdkDragContext *context,
|
|
|
|
+ GsmPropertiesDialog *dialog)
|
|
|
|
+{
|
|
|
|
+ GtkTreePath *path;
|
|
|
|
+ GtkTreeIter iter;
|
|
|
|
+ GspApp *app;
|
|
|
|
+
|
|
|
|
+ gtk_tree_view_get_cursor (GTK_TREE_VIEW (widget), &path, NULL);
|
|
|
|
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (dialog->priv->tree_filter),
|
|
|
|
+ &iter, path);
|
|
|
|
+ gtk_tree_path_free (path);
|
|
|
|
+
|
|
|
|
+ gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->tree_filter),
|
|
|
|
+ &iter,
|
|
|
|
+ STORE_COL_APP, &app,
|
|
|
|
+ -1);
|
|
|
|
+
|
|
|
|
+ if (app) {
|
|
|
|
+ g_object_set_data_full (G_OBJECT (context), "gsp-app",
|
|
|
|
+ g_object_ref (app), g_object_unref);
|
|
|
|
+ g_object_unref (app);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+on_drag_data_get (GtkWidget *widget,
|
|
|
|
+ GdkDragContext *context,
|
|
|
|
+ GtkSelectionData *selection_data,
|
|
|
|
+ guint info,
|
|
|
|
+ guint time,
|
|
|
|
+ GsmPropertiesDialog *dialog)
|
|
|
|
+{
|
|
|
|
+ GspApp *app;
|
|
|
|
+
|
|
|
|
+ app = g_object_get_data (G_OBJECT (context), "gsp-app");
|
|
|
|
+ if (app) {
|
|
|
|
+ const char *uris[2];
|
|
|
|
+ char *uri;
|
|
|
|
+
|
|
|
|
+ uri = g_filename_to_uri (gsp_app_get_path (app), NULL, NULL);
|
|
|
|
+
|
|
|
|
+ uris[0] = uri;
|
|
|
|
+ uris[1] = NULL;
|
|
|
|
+ gtk_selection_data_set_uris (selection_data, (char **) uris);
|
|
|
|
+
|
|
|
|
+ g_free (uri);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+on_add_app_clicked (GtkWidget *widget,
|
|
|
|
+ GsmPropertiesDialog *dialog)
|
|
|
|
+{
|
|
|
|
+ GtkWidget *add_dialog;
|
|
|
|
+ char *name;
|
|
|
|
+ char *exec;
|
|
|
|
+ char *comment;
|
|
|
|
+
|
|
|
|
+ add_dialog = gsm_app_dialog_new (NULL, NULL, NULL);
|
|
|
|
+ gtk_window_set_transient_for (GTK_WINDOW (add_dialog),
|
|
|
|
+ GTK_WINDOW (dialog));
|
|
|
|
+
|
|
|
|
+ if (gsm_app_dialog_run (GSM_APP_DIALOG (add_dialog),
|
|
|
|
+ &name, &exec, &comment)) {
|
|
|
|
+ gsp_app_create (name, comment, exec);
|
|
|
|
+ g_free (name);
|
|
|
|
+ g_free (exec);
|
|
|
|
+ g_free (comment);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+on_delete_app_clicked (GtkWidget *widget,
|
|
|
|
+ GsmPropertiesDialog *dialog)
|
|
|
|
+{
|
|
|
|
+ GtkTreeSelection *selection;
|
|
|
|
+ GtkTreeIter iter;
|
|
|
|
+ GspApp *app;
|
|
|
|
+
|
|
|
|
+ selection = gtk_tree_view_get_selection (dialog->priv->treeview);
|
|
|
|
+
|
|
|
|
+ if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ app = NULL;
|
|
|
|
+ gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->tree_filter),
|
|
|
|
+ &iter,
|
|
|
|
+ STORE_COL_APP, &app,
|
|
|
|
+ -1);
|
|
|
|
+
|
|
|
|
+ if (app) {
|
|
|
|
+ gsp_app_delete (app);
|
|
|
|
+ g_object_unref (app);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+on_edit_app_clicked (GtkWidget *widget,
|
|
|
|
+ GsmPropertiesDialog *dialog)
|
|
|
|
+{
|
|
|
|
+ GtkTreeSelection *selection;
|
|
|
|
+ GtkTreeIter iter;
|
|
|
|
+ GspApp *app;
|
|
|
|
+
|
|
|
|
+ selection = gtk_tree_view_get_selection (dialog->priv->treeview);
|
|
|
|
+
|
|
|
|
+ if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ app = NULL;
|
|
|
|
+ gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->tree_filter),
|
|
|
|
+ &iter,
|
|
|
|
+ STORE_COL_APP, &app,
|
|
|
|
+ -1);
|
|
|
|
+
|
|
|
|
+ if (app) {
|
|
|
|
+ GtkWidget *edit_dialog;
|
|
|
|
+ char *name;
|
|
|
|
+ char *exec;
|
|
|
|
+ char *comment;
|
|
|
|
+
|
|
|
|
+ edit_dialog = gsm_app_dialog_new (gsp_app_get_name (app),
|
|
|
|
+ gsp_app_get_exec (app),
|
|
|
|
+ gsp_app_get_comment (app));
|
|
|
|
+ gtk_window_set_transient_for (GTK_WINDOW (edit_dialog),
|
|
|
|
+ GTK_WINDOW (dialog));
|
|
|
|
+
|
|
|
|
+ if (gsm_app_dialog_run (GSM_APP_DIALOG (edit_dialog),
|
|
|
|
+ &name, &exec, &comment)) {
|
|
|
|
+ gsp_app_update (app, name, comment, exec);
|
|
|
|
+ g_free (name);
|
|
|
|
+ g_free (exec);
|
|
|
|
+ g_free (comment);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_object_unref (app);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+on_row_activated (GtkTreeView *tree_view,
|
|
|
|
+ GtkTreePath *path,
|
|
|
|
+ GtkTreeViewColumn *column,
|
|
|
|
+ GsmPropertiesDialog *dialog)
|
|
|
|
+{
|
|
|
|
+ on_edit_app_clicked (NULL, dialog);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+on_save_session_clicked (GtkWidget *widget,
|
|
|
|
+ GsmPropertiesDialog *dialog)
|
|
|
|
+{
|
|
|
|
+ g_debug ("Session saving is not implemented yet!");
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+setup_dialog (GsmPropertiesDialog *dialog)
|
|
|
|
+{
|
|
|
|
+ GtkTreeView *treeview;
|
|
|
|
+ GtkWidget *button;
|
|
|
|
+ GtkTreeModel *tree_filter;
|
|
|
|
+ GtkTreeViewColumn *column;
|
|
|
|
+ GtkCellRenderer *renderer;
|
|
|
|
+ GtkTreeSelection *selection;
|
|
|
|
+ GtkTargetList *targetlist;
|
|
|
|
+
|
|
|
|
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog),
|
|
|
|
+ GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
|
|
|
|
+ NULL);
|
|
|
|
+
|
|
|
|
+ dialog->priv->list_store = gtk_list_store_new (NUMBER_OF_COLUMNS,
|
|
|
|
+ G_TYPE_BOOLEAN,
|
|
|
|
+ G_TYPE_BOOLEAN,
|
|
|
|
+ G_TYPE_ICON,
|
|
|
|
+ G_TYPE_STRING,
|
|
|
|
+ G_TYPE_OBJECT,
|
|
|
|
+ G_TYPE_STRING);
|
|
|
|
+ tree_filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (dialog->priv->list_store),
|
|
|
|
+ NULL);
|
|
|
|
+ g_object_unref (dialog->priv->list_store);
|
|
|
|
+ dialog->priv->tree_filter = tree_filter;
|
|
|
|
+
|
|
|
|
+ gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (tree_filter),
|
|
|
|
+ STORE_COL_VISIBLE);
|
|
|
|
+
|
|
|
|
+ treeview = GTK_TREE_VIEW (gtk_builder_get_object (dialog->priv->xml,
|
|
|
|
+ CAPPLET_TREEVIEW_WIDGET_NAME));
|
|
|
|
+ dialog->priv->treeview = treeview;
|
|
|
|
+
|
|
|
|
+ gtk_tree_view_set_model (treeview, tree_filter);
|
|
|
|
+ g_object_unref (tree_filter);
|
|
|
|
+
|
|
|
|
+ gtk_tree_view_set_headers_visible (treeview, FALSE);
|
|
|
|
+ g_signal_connect (treeview,
|
|
|
|
+ "row-activated",
|
|
|
|
+ G_CALLBACK (on_row_activated),
|
|
|
|
+ dialog);
|
|
|
|
+
|
|
|
|
+ selection = gtk_tree_view_get_selection (treeview);
|
|
|
|
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
|
|
|
|
+ g_signal_connect (selection,
|
|
|
|
+ "changed",
|
|
|
|
+ G_CALLBACK (on_selection_changed),
|
|
|
|
+ dialog);
|
|
|
|
+
|
|
|
|
+ /* CHECKBOX COLUMN */
|
|
|
|
+ renderer = gtk_cell_renderer_toggle_new ();
|
|
|
|
+ column = gtk_tree_view_column_new_with_attributes (_("Enabled"),
|
|
|
|
+ renderer,
|
|
|
|
+ "active", STORE_COL_ENABLED,
|
|
|
|
+ NULL);
|
|
|
|
+ gtk_tree_view_append_column (treeview, column);
|
|
|
|
+ g_signal_connect (renderer,
|
|
|
|
+ "toggled",
|
|
|
|
+ G_CALLBACK (on_startup_enabled_toggled),
|
|
|
|
+ dialog);
|
|
|
|
+
|
|
|
|
+ /* ICON COLUMN */
|
|
|
|
+ renderer = gtk_cell_renderer_pixbuf_new ();
|
|
|
|
+ column = gtk_tree_view_column_new_with_attributes (_("Icon"),
|
|
|
|
+ renderer,
|
|
|
|
+ "gicon", STORE_COL_GICON,
|
|
|
|
+ "sensitive", STORE_COL_ENABLED,
|
|
|
|
+ NULL);
|
|
|
|
+ g_object_set (renderer,
|
|
|
|
+ "stock-size", GSM_PROPERTIES_ICON_SIZE,
|
|
|
|
+ NULL);
|
|
|
|
+ gtk_tree_view_append_column (treeview, column);
|
|
|
|
+
|
|
|
|
+ /* NAME COLUMN */
|
|
|
|
+ renderer = gtk_cell_renderer_text_new ();
|
|
|
|
+ column = gtk_tree_view_column_new_with_attributes (_("Program"),
|
|
|
|
+ renderer,
|
|
|
|
+ "markup", STORE_COL_DESCRIPTION,
|
|
|
|
+ "sensitive", STORE_COL_ENABLED,
|
|
|
|
+ NULL);
|
|
|
|
+ g_object_set (renderer,
|
|
|
|
+ "ellipsize", PANGO_ELLIPSIZE_END,
|
|
|
|
+ NULL);
|
|
|
|
+ gtk_tree_view_append_column (treeview, column);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ gtk_tree_view_column_set_sort_column_id (column, STORE_COL_DESCRIPTION);
|
|
|
|
+ gtk_tree_view_set_search_column (treeview, STORE_COL_SEARCH);
|
|
|
|
+ gtk_tree_view_set_rules_hint (treeview, TRUE);
|
|
|
|
+
|
|
|
|
+ gtk_tree_view_enable_model_drag_source (treeview,
|
|
|
|
+ GDK_BUTTON1_MASK|GDK_BUTTON2_MASK,
|
|
|
|
+ NULL, 0,
|
|
|
|
+ GDK_ACTION_COPY);
|
|
|
|
+ gtk_drag_source_add_uri_targets (GTK_WIDGET (treeview));
|
|
|
|
+
|
|
|
|
+ gtk_drag_dest_set (GTK_WIDGET (treeview),
|
|
|
|
+ GTK_DEST_DEFAULT_ALL,
|
|
|
|
+ NULL, 0,
|
|
|
|
+ GDK_ACTION_COPY);
|
|
|
|
+
|
|
|
|
+ gtk_drag_dest_add_uri_targets (GTK_WIDGET (treeview));
|
|
|
|
+ /* we don't want to accept drags coming from this widget */
|
|
|
|
+ targetlist = gtk_drag_dest_get_target_list (GTK_WIDGET (treeview));
|
|
|
|
+ if (targetlist != NULL) {
|
|
|
|
+ GtkTargetEntry *targets;
|
|
|
|
+ gint n_targets;
|
|
|
|
+ gint i;
|
|
|
|
+
|
|
|
|
+ targets = gtk_target_table_new_from_list (targetlist, &n_targets);
|
|
|
|
+ for (i = 0; i < n_targets; i++)
|
|
|
|
+ targets[i].flags = GTK_TARGET_OTHER_WIDGET;
|
|
|
|
+
|
|
|
|
+ targetlist = gtk_target_list_new (targets, n_targets);
|
|
|
|
+ gtk_drag_dest_set_target_list (GTK_WIDGET (treeview), targetlist);
|
|
|
|
+ gtk_target_list_unref (targetlist);
|
|
|
|
+
|
|
|
|
+ gtk_target_table_free (targets, n_targets);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_signal_connect (treeview, "drag_begin",
|
|
|
|
+ G_CALLBACK (on_drag_begin),
|
|
|
|
+ dialog);
|
|
|
|
+ g_signal_connect (treeview, "drag_data_get",
|
|
|
|
+ G_CALLBACK (on_drag_data_get),
|
|
|
|
+ dialog);
|
|
|
|
+ g_signal_connect (treeview, "drag_data_received",
|
|
|
|
+ G_CALLBACK (on_drag_data_received),
|
|
|
|
+ dialog);
|
|
|
|
+
|
|
|
|
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (dialog->priv->list_store),
|
|
|
|
+ STORE_COL_DESCRIPTION,
|
|
|
|
+ GTK_SORT_ASCENDING);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml,
|
|
|
|
+ CAPPLET_ADD_WIDGET_NAME));
|
|
|
|
+ dialog->priv->add_button = button;
|
|
|
|
+ g_signal_connect (button,
|
|
|
|
+ "clicked",
|
|
|
|
+ G_CALLBACK (on_add_app_clicked),
|
|
|
|
+ dialog);
|
|
|
|
+
|
|
|
|
+ button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml,
|
|
|
|
+ CAPPLET_DELETE_WIDGET_NAME));
|
|
|
|
+ dialog->priv->delete_button = button;
|
|
|
|
+ g_signal_connect (button,
|
|
|
|
+ "clicked",
|
|
|
|
+ G_CALLBACK (on_delete_app_clicked),
|
|
|
|
+ dialog);
|
|
|
|
+
|
|
|
|
+ button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml,
|
|
|
|
+ CAPPLET_EDIT_WIDGET_NAME));
|
|
|
|
+ dialog->priv->edit_button = button;
|
|
|
|
+ g_signal_connect (button,
|
|
|
|
+ "clicked",
|
|
|
|
+ G_CALLBACK (on_edit_app_clicked),
|
|
|
|
+ dialog);
|
|
|
|
+
|
|
|
|
+ button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml,
|
|
|
|
+ CAPPLET_REMEMBER_WIDGET_NAME));
|
|
|
|
+ g_settings_bind (dialog->priv->settings, SPC_SETTINGS_AUTOSAVE_KEY,
|
|
|
|
+ button, "active", G_SETTINGS_BIND_DEFAULT);
|
|
|
|
+
|
|
|
|
+ button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml,
|
|
|
|
+ CAPPLET_SAVE_WIDGET_NAME));
|
|
|
|
+ g_signal_connect (button,
|
|
|
|
+ "clicked",
|
|
|
|
+ G_CALLBACK (on_save_session_clicked),
|
|
|
|
+ dialog);
|
|
|
|
+
|
|
|
|
+ dialog->priv->manager = gsp_app_manager_get ();
|
|
|
|
+ gsp_app_manager_fill (dialog->priv->manager);
|
|
|
|
+ g_signal_connect_swapped (dialog->priv->manager, "added",
|
|
|
|
+ G_CALLBACK (_app_added), dialog);
|
|
|
|
+ g_signal_connect_swapped (dialog->priv->manager, "removed",
|
|
|
|
+ G_CALLBACK (_app_removed), dialog);
|
|
|
|
+
|
|
|
|
+ populate_model (dialog);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static GObject *
|
|
|
|
+gsm_properties_dialog_constructor (GType type,
|
|
|
|
+ guint n_construct_properties,
|
|
|
|
+ GObjectConstructParam *construct_properties)
|
|
|
|
+{
|
|
|
|
+ GsmPropertiesDialog *dialog;
|
|
|
|
+
|
|
|
|
+ dialog = GSM_PROPERTIES_DIALOG (G_OBJECT_CLASS (gsm_properties_dialog_parent_class)->constructor (type,
|
|
|
|
+ n_construct_properties,
|
|
|
|
+ construct_properties));
|
|
|
|
+
|
|
|
|
+ setup_dialog (dialog);
|
|
|
|
+
|
|
|
|
+ gtk_widget_show (GTK_WIDGET (dialog));
|
|
|
|
+
|
|
|
|
+ return G_OBJECT (dialog);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+gsm_properties_dialog_dispose (GObject *object)
|
|
|
|
+{
|
|
|
|
+ GsmPropertiesDialog *dialog;
|
|
|
|
+
|
|
|
|
+ g_return_if_fail (object != NULL);
|
|
|
|
+ g_return_if_fail (GSM_IS_PROPERTIES_DIALOG (object));
|
|
|
|
+
|
|
|
|
+ dialog = GSM_PROPERTIES_DIALOG (object);
|
|
|
|
+
|
|
|
|
+ if (dialog->priv->xml != NULL) {
|
|
|
|
+ g_object_unref (dialog->priv->xml);
|
|
|
|
+ dialog->priv->xml = NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (dialog->priv->settings != NULL) {
|
|
|
|
+ g_object_unref (dialog->priv->settings);
|
|
|
|
+ dialog->priv->settings = NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ G_OBJECT_CLASS (gsm_properties_dialog_parent_class)->dispose (object);
|
|
|
|
+
|
|
|
|
+ /* it's important to do this after chaining to the parent dispose
|
|
|
|
+ * method because we want to make sure the treeview has been disposed
|
|
|
|
+ * and removed all its references to GspApp objects */
|
|
|
|
+ if (dialog->priv->manager != NULL) {
|
|
|
|
+ g_object_unref (dialog->priv->manager);
|
|
|
|
+ dialog->priv->manager = NULL;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+gsm_properties_dialog_class_init (GsmPropertiesDialogClass *klass)
|
|
|
|
+{
|
|
|
|
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
+
|
|
|
|
+ object_class->constructor = gsm_properties_dialog_constructor;
|
|
|
|
+ object_class->dispose = gsm_properties_dialog_dispose;
|
|
|
|
+ object_class->finalize = gsm_properties_dialog_finalize;
|
|
|
|
+
|
|
|
|
+ g_type_class_add_private (klass, sizeof (GsmPropertiesDialogPrivate));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+gsm_properties_dialog_init (GsmPropertiesDialog *dialog)
|
|
|
|
+{
|
|
|
|
+ GtkWidget *content_area;
|
|
|
|
+ GtkWidget *widget;
|
|
|
|
+ GError *error;
|
|
|
|
+
|
|
|
|
+ dialog->priv = GSM_PROPERTIES_DIALOG_GET_PRIVATE (dialog);
|
|
|
|
+
|
|
|
|
+ dialog->priv->settings = g_settings_new (SPC_SETTINGS_SCHEMA);
|
|
|
|
+
|
|
|
|
+ dialog->priv->xml = gtk_builder_new ();
|
|
|
|
+ gtk_builder_set_translation_domain (dialog->priv->xml, GETTEXT_PACKAGE);
|
|
|
|
+
|
|
|
|
+ error = NULL;
|
|
|
|
+ if (!gtk_builder_add_from_file (dialog->priv->xml,
|
|
|
|
+ GTKBUILDER_DIR "/" GTKBUILDER_FILE,
|
|
|
|
+ &error)) {
|
|
|
|
+ if (error) {
|
|
|
|
+ g_warning ("Could not load capplet UI file: %s",
|
|
|
|
+ error->message);
|
|
|
|
+ g_error_free (error);
|
|
|
|
+ } else {
|
|
|
|
+ g_warning ("Could not load capplet UI file.");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
|
|
|
|
+ widget = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml,
|
|
|
|
+ "main-notebook"));
|
|
|
|
+ gtk_box_pack_start (GTK_BOX (content_area), widget, TRUE, TRUE, 0);
|
|
|
|
+
|
|
|
|
+ gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 450);
|
|
|
|
+ gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
|
|
|
|
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
|
|
|
|
+ gtk_box_set_spacing (GTK_BOX (content_area), 2);
|
|
|
|
+ gtk_window_set_icon_name (GTK_WINDOW (dialog), "session-properties");
|
|
|
|
+ gtk_window_set_title (GTK_WINDOW (dialog), _("Startup Applications Preferences"));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+gsm_properties_dialog_finalize (GObject *object)
|
|
|
|
+{
|
|
|
|
+ GsmPropertiesDialog *dialog;
|
|
|
|
+
|
|
|
|
+ g_return_if_fail (object != NULL);
|
|
|
|
+ g_return_if_fail (GSM_IS_PROPERTIES_DIALOG (object));
|
|
|
|
+
|
|
|
|
+ dialog = GSM_PROPERTIES_DIALOG (object);
|
|
|
|
+
|
|
|
|
+ g_return_if_fail (dialog->priv != NULL);
|
|
|
|
+
|
|
|
|
+ G_OBJECT_CLASS (gsm_properties_dialog_parent_class)->finalize (object);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+GtkWidget *
|
|
|
|
+gsm_properties_dialog_new (void)
|
|
|
|
+{
|
|
|
|
+ GObject *object;
|
|
|
|
+
|
|
|
|
+ object = g_object_new (GSM_TYPE_PROPERTIES_DIALOG,
|
|
|
|
+ NULL);
|
|
|
|
+
|
|
|
|
+ return GTK_WIDGET (object);
|
|
|
|
+}
|
|
|
|
diff --git a/capplet/gsm-properties-dialog.h b/capplet/gsm-properties-dialog.h
|
|
|
|
new file mode 100644
|
|
|
|
index 00000000..df4915ea
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/capplet/gsm-properties-dialog.h
|
|
|
|
@@ -0,0 +1,57 @@
|
|
|
|
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
|
|
|
+ *
|
|
|
|
+ * Copyright (C) 2008 William Jon McCann <jmccann@redhat.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 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
|
|
|
|
+ * 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, write to the Free Software
|
|
|
|
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#ifndef __GSM_PROPERTIES_DIALOG_H
|
|
|
|
+#define __GSM_PROPERTIES_DIALOG_H
|
|
|
|
+
|
|
|
|
+#include <glib-object.h>
|
|
|
|
+#include <gtk/gtk.h>
|
|
|
|
+
|
|
|
|
+G_BEGIN_DECLS
|
|
|
|
+
|
|
|
|
+#define GSM_TYPE_PROPERTIES_DIALOG (gsm_properties_dialog_get_type ())
|
|
|
|
+#define GSM_PROPERTIES_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialog))
|
|
|
|
+#define GSM_PROPERTIES_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialogClass))
|
|
|
|
+#define GSM_IS_PROPERTIES_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSM_TYPE_PROPERTIES_DIALOG))
|
|
|
|
+#define GSM_IS_PROPERTIES_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSM_TYPE_PROPERTIES_DIALOG))
|
|
|
|
+#define GSM_PROPERTIES_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialogClass))
|
|
|
|
+
|
|
|
|
+typedef struct GsmPropertiesDialogPrivate GsmPropertiesDialogPrivate;
|
|
|
|
+
|
|
|
|
+typedef struct
|
|
|
|
+{
|
|
|
|
+ GtkDialog parent;
|
|
|
|
+ GsmPropertiesDialogPrivate *priv;
|
|
|
|
+} GsmPropertiesDialog;
|
|
|
|
+
|
|
|
|
+typedef struct
|
|
|
|
+{
|
|
|
|
+ GtkDialogClass parent_class;
|
|
|
|
+} GsmPropertiesDialogClass;
|
|
|
|
+
|
|
|
|
+GType gsm_properties_dialog_get_type (void);
|
|
|
|
+
|
|
|
|
+GtkWidget * gsm_properties_dialog_new (void);
|
|
|
|
+
|
|
|
|
+#define GSM_PROPERTIES_ICON_SIZE GTK_ICON_SIZE_LARGE_TOOLBAR
|
|
|
|
+
|
|
|
|
+G_END_DECLS
|
|
|
|
+
|
|
|
|
+#endif /* __GSM_PROPERTIES_DIALOG_H */
|
|
|
|
diff --git a/capplet/gsp-app-manager.c b/capplet/gsp-app-manager.c
|
|
|
|
new file mode 100644
|
|
|
|
index 00000000..bcd6d402
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/capplet/gsp-app-manager.c
|
|
|
|
@@ -0,0 +1,593 @@
|
|
|
|
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
|
|
|
+ *
|
|
|
|
+ * Copyright (C) 1999 Free Software Foundation, Inc.
|
|
|
|
+ * Copyright (C) 2007, 2009 Vincent Untz.
|
|
|
|
+ * Copyright (C) 2008 Lucas Rocha.
|
|
|
|
+ * Copyright (C) 2008 William Jon McCann <jmccann@redhat.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 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
|
|
|
|
+ * 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, write to the Free Software
|
|
|
|
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#include <string.h>
|
|
|
|
+
|
|
|
|
+#include "gsm-util.h"
|
|
|
|
+#include "gsp-app.h"
|
|
|
|
+
|
|
|
|
+#include "gsp-app-manager.h"
|
|
|
|
+
|
|
|
|
+static GspAppManager *manager = NULL;
|
|
|
|
+
|
|
|
|
+typedef struct {
|
|
|
|
+ char *dir;
|
|
|
|
+ int index;
|
|
|
|
+ GFileMonitor *monitor;
|
|
|
|
+} GspXdgDir;
|
|
|
|
+
|
|
|
|
+struct _GspAppManagerPrivate {
|
|
|
|
+ GSList *apps;
|
|
|
|
+ GSList *dirs;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+#define GSP_APP_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSP_TYPE_APP_MANAGER, GspAppManagerPrivate))
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+enum {
|
|
|
|
+ ADDED,
|
|
|
|
+ REMOVED,
|
|
|
|
+ LAST_SIGNAL
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static guint gsp_app_manager_signals[LAST_SIGNAL] = { 0 };
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+G_DEFINE_TYPE (GspAppManager, gsp_app_manager, G_TYPE_OBJECT)
|
|
|
|
+
|
|
|
|
+static void gsp_app_manager_dispose (GObject *object);
|
|
|
|
+static void gsp_app_manager_finalize (GObject *object);
|
|
|
|
+static void _gsp_app_manager_app_unref (GspApp *app,
|
|
|
|
+ GspAppManager *manager);
|
|
|
|
+static void _gsp_app_manager_app_removed (GspAppManager *manager,
|
|
|
|
+ GspApp *app);
|
|
|
|
+
|
|
|
|
+static GspXdgDir *
|
|
|
|
+_gsp_xdg_dir_new (const char *dir,
|
|
|
|
+ int index)
|
|
|
|
+{
|
|
|
|
+ GspXdgDir *xdgdir;
|
|
|
|
+
|
|
|
|
+ xdgdir = g_slice_new (GspXdgDir);
|
|
|
|
+
|
|
|
|
+ xdgdir->dir = g_strdup (dir);
|
|
|
|
+ xdgdir->index = index;
|
|
|
|
+ xdgdir->monitor = NULL;
|
|
|
|
+
|
|
|
|
+ return xdgdir;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+_gsp_xdg_dir_free (GspXdgDir *xdgdir)
|
|
|
|
+{
|
|
|
|
+ if (xdgdir->dir) {
|
|
|
|
+ g_free (xdgdir->dir);
|
|
|
|
+ xdgdir->dir = NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (xdgdir->monitor) {
|
|
|
|
+ g_file_monitor_cancel (xdgdir->monitor);
|
|
|
|
+ g_object_unref (xdgdir->monitor);
|
|
|
|
+ xdgdir->monitor = NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_slice_free (GspXdgDir, xdgdir);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+gsp_app_manager_class_init (GspAppManagerClass *class)
|
|
|
|
+{
|
|
|
|
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
|
|
|
|
+
|
|
|
|
+ gobject_class->dispose = gsp_app_manager_dispose;
|
|
|
|
+ gobject_class->finalize = gsp_app_manager_finalize;
|
|
|
|
+
|
|
|
|
+ gsp_app_manager_signals[ADDED] =
|
|
|
|
+ g_signal_new ("added",
|
|
|
|
+ G_TYPE_FROM_CLASS (gobject_class),
|
|
|
|
+ G_SIGNAL_RUN_LAST,
|
|
|
|
+ G_STRUCT_OFFSET (GspAppManagerClass,
|
|
|
|
+ added),
|
|
|
|
+ NULL,
|
|
|
|
+ NULL,
|
|
|
|
+ g_cclosure_marshal_VOID__OBJECT,
|
|
|
|
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
|
|
|
|
+
|
|
|
|
+ gsp_app_manager_signals[REMOVED] =
|
|
|
|
+ g_signal_new ("removed",
|
|
|
|
+ G_TYPE_FROM_CLASS (gobject_class),
|
|
|
|
+ G_SIGNAL_RUN_LAST,
|
|
|
|
+ G_STRUCT_OFFSET (GspAppManagerClass,
|
|
|
|
+ removed),
|
|
|
|
+ NULL,
|
|
|
|
+ NULL,
|
|
|
|
+ g_cclosure_marshal_VOID__OBJECT,
|
|
|
|
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
|
|
|
|
+
|
|
|
|
+ g_type_class_add_private (class, sizeof (GspAppManagerPrivate));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+gsp_app_manager_init (GspAppManager *manager)
|
|
|
|
+{
|
|
|
|
+ manager->priv = GSP_APP_MANAGER_GET_PRIVATE (manager);
|
|
|
|
+
|
|
|
|
+ memset (manager->priv, 0, sizeof (GspAppManagerPrivate));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+gsp_app_manager_dispose (GObject *object)
|
|
|
|
+{
|
|
|
|
+ GspAppManager *manager;
|
|
|
|
+
|
|
|
|
+ g_return_if_fail (object != NULL);
|
|
|
|
+ g_return_if_fail (GSP_IS_APP_MANAGER (object));
|
|
|
|
+
|
|
|
|
+ manager = GSP_APP_MANAGER (object);
|
|
|
|
+
|
|
|
|
+ /* we unref GspApp objects in dispose since they might need to
|
|
|
|
+ * reference us during their dispose/finalize */
|
|
|
|
+ g_slist_foreach (manager->priv->apps,
|
|
|
|
+ (GFunc) _gsp_app_manager_app_unref, manager);
|
|
|
|
+ g_slist_free (manager->priv->apps);
|
|
|
|
+ manager->priv->apps = NULL;
|
|
|
|
+
|
|
|
|
+ G_OBJECT_CLASS (gsp_app_manager_parent_class)->dispose (object);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+gsp_app_manager_finalize (GObject *object)
|
|
|
|
+{
|
|
|
|
+ GspAppManager *manager;
|
|
|
|
+
|
|
|
|
+ g_return_if_fail (object != NULL);
|
|
|
|
+ g_return_if_fail (GSP_IS_APP_MANAGER (object));
|
|
|
|
+
|
|
|
|
+ manager = GSP_APP_MANAGER (object);
|
|
|
|
+
|
|
|
|
+ g_slist_foreach (manager->priv->dirs,
|
|
|
|
+ (GFunc) _gsp_xdg_dir_free, NULL);
|
|
|
|
+ g_slist_free (manager->priv->dirs);
|
|
|
|
+ manager->priv->dirs = NULL;
|
|
|
|
+
|
|
|
|
+ G_OBJECT_CLASS (gsp_app_manager_parent_class)->finalize (object);
|
|
|
|
+
|
|
|
|
+ manager = NULL;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+_gsp_app_manager_emit_added (GspAppManager *manager,
|
|
|
|
+ GspApp *app)
|
|
|
|
+{
|
|
|
|
+ g_signal_emit (G_OBJECT (manager), gsp_app_manager_signals[ADDED],
|
|
|
|
+ 0, app);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+_gsp_app_manager_emit_removed (GspAppManager *manager,
|
|
|
|
+ GspApp *app)
|
|
|
|
+{
|
|
|
|
+ g_signal_emit (G_OBJECT (manager), gsp_app_manager_signals[REMOVED],
|
|
|
|
+ 0, app);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Directories
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+static int
|
|
|
|
+gsp_app_manager_get_dir_index (GspAppManager *manager,
|
|
|
|
+ const char *dir)
|
|
|
|
+{
|
|
|
|
+ GSList *l;
|
|
|
|
+ GspXdgDir *xdgdir;
|
|
|
|
+
|
|
|
|
+ g_return_val_if_fail (GSP_IS_APP_MANAGER (manager), -1);
|
|
|
|
+ g_return_val_if_fail (dir != NULL, -1);
|
|
|
|
+
|
|
|
|
+ for (l = manager->priv->dirs; l != NULL; l = l->next) {
|
|
|
|
+ xdgdir = l->data;
|
|
|
|
+ if (strcmp (dir, xdgdir->dir) == 0) {
|
|
|
|
+ return xdgdir->index;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return -1;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const char *
|
|
|
|
+gsp_app_manager_get_dir (GspAppManager *manager,
|
|
|
|
+ unsigned int index)
|
|
|
|
+{
|
|
|
|
+ GSList *l;
|
|
|
|
+ GspXdgDir *xdgdir;
|
|
|
|
+
|
|
|
|
+ g_return_val_if_fail (GSP_IS_APP_MANAGER (manager), NULL);
|
|
|
|
+
|
|
|
|
+ for (l = manager->priv->dirs; l != NULL; l = l->next) {
|
|
|
|
+ xdgdir = l->data;
|
|
|
|
+ if (index == xdgdir->index) {
|
|
|
|
+ return xdgdir->dir;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return NULL;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int
|
|
|
|
+_gsp_app_manager_find_dir_with_basename (GspAppManager *manager,
|
|
|
|
+ const char *basename,
|
|
|
|
+ int minimum_index)
|
|
|
|
+{
|
|
|
|
+ GSList *l;
|
|
|
|
+ GspXdgDir *xdgdir;
|
|
|
|
+ char *path;
|
|
|
|
+ GKeyFile *keyfile;
|
|
|
|
+ int result = -1;
|
|
|
|
+
|
|
|
|
+ path = NULL;
|
|
|
|
+ keyfile = g_key_file_new ();
|
|
|
|
+
|
|
|
|
+ for (l = manager->priv->dirs; l != NULL; l = l->next) {
|
|
|
|
+ xdgdir = l->data;
|
|
|
|
+
|
|
|
|
+ if (xdgdir->index <= minimum_index) {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_free (path);
|
|
|
|
+ path = g_build_filename (xdgdir->dir, basename, NULL);
|
|
|
|
+ if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!g_key_file_load_from_file (keyfile, path,
|
|
|
|
+ G_KEY_FILE_NONE, NULL)) {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* the file exists and is readable */
|
|
|
|
+ if (result == -1) {
|
|
|
|
+ result = xdgdir->index;
|
|
|
|
+ } else {
|
|
|
|
+ result = MIN (result, xdgdir->index);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_key_file_free (keyfile);
|
|
|
|
+ g_free (path);
|
|
|
|
+
|
|
|
|
+ return result;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+_gsp_app_manager_handle_delete (GspAppManager *manager,
|
|
|
|
+ GspApp *app,
|
|
|
|
+ const char *basename,
|
|
|
|
+ int index)
|
|
|
|
+{
|
|
|
|
+ unsigned int position;
|
|
|
|
+ unsigned int system_position;
|
|
|
|
+
|
|
|
|
+ position = gsp_app_get_xdg_position (app);
|
|
|
|
+ system_position = gsp_app_get_xdg_system_position (app);
|
|
|
|
+
|
|
|
|
+ if (system_position < index) {
|
|
|
|
+ /* it got deleted, but we don't even care about it */
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (index < position) {
|
|
|
|
+ /* it got deleted, but in a position earlier than the current
|
|
|
|
+ * one. This happens when the user file was changed and became
|
|
|
|
+ * identical to the system file; in this case, the user file is
|
|
|
|
+ * simply removed. */
|
|
|
|
+ g_assert (index == 0);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (position == index &&
|
|
|
|
+ (system_position == index || system_position == G_MAXUINT)) {
|
|
|
|
+ /* the file used by the user was deleted, and there's no other
|
|
|
|
+ * file in system directories. So it really got deleted. */
|
|
|
|
+ _gsp_app_manager_app_removed (manager, app);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (system_position == index) {
|
|
|
|
+ /* then we know that position != index; we just hae to tell
|
|
|
|
+ * GspApp if there's still a system directory containing this
|
|
|
|
+ * basename */
|
|
|
|
+ int new_system;
|
|
|
|
+
|
|
|
|
+ new_system = _gsp_app_manager_find_dir_with_basename (manager,
|
|
|
|
+ basename,
|
|
|
|
+ index);
|
|
|
|
+ if (new_system < 0) {
|
|
|
|
+ gsp_app_set_xdg_system_position (app, G_MAXUINT);
|
|
|
|
+ } else {
|
|
|
|
+ gsp_app_set_xdg_system_position (app, new_system);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (position == index) {
|
|
|
|
+ /* then we know that system_position != G_MAXUINT; we need to
|
|
|
|
+ * tell GspApp to change position to system_position */
|
|
|
|
+ const char *dir;
|
|
|
|
+
|
|
|
|
+ dir = gsp_app_manager_get_dir (manager, system_position);
|
|
|
|
+ if (dir) {
|
|
|
|
+ char *path;
|
|
|
|
+
|
|
|
|
+ path = g_build_filename (dir, basename, NULL);
|
|
|
|
+ gsp_app_reload_at (app, path,
|
|
|
|
+ (unsigned int) system_position);
|
|
|
|
+ g_free (path);
|
|
|
|
+ } else {
|
|
|
|
+ _gsp_app_manager_app_removed (manager, app);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_assert_not_reached ();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static gboolean
|
|
|
|
+gsp_app_manager_xdg_dir_monitor (GFileMonitor *monitor,
|
|
|
|
+ GFile *child,
|
|
|
|
+ GFile *other_file,
|
|
|
|
+ GFileMonitorEvent flags,
|
|
|
|
+ gpointer data)
|
|
|
|
+{
|
|
|
|
+ GspAppManager *manager;
|
|
|
|
+ GspApp *old_app;
|
|
|
|
+ GspApp *app;
|
|
|
|
+ GFile *parent;
|
|
|
|
+ char *basename;
|
|
|
|
+ char *dir;
|
|
|
|
+ char *path;
|
|
|
|
+ int index;
|
|
|
|
+
|
|
|
|
+ manager = GSP_APP_MANAGER (data);
|
|
|
|
+
|
|
|
|
+ basename = g_file_get_basename (child);
|
|
|
|
+ if (!g_str_has_suffix (basename, ".desktop")) {
|
|
|
|
+ /* not a desktop file, we can ignore */
|
|
|
|
+ g_free (basename);
|
|
|
|
+ return TRUE;
|
|
|
|
+ }
|
|
|
|
+ old_app = gsp_app_manager_find_app_with_basename (manager, basename);
|
|
|
|
+
|
|
|
|
+ parent = g_file_get_parent (child);
|
|
|
|
+ dir = g_file_get_path (parent);
|
|
|
|
+ g_object_unref (parent);
|
|
|
|
+
|
|
|
|
+ index = gsp_app_manager_get_dir_index (manager, dir);
|
|
|
|
+ if (index < 0) {
|
|
|
|
+ /* not a directory we know; should never happen, though */
|
|
|
|
+ g_free (dir);
|
|
|
|
+ return TRUE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ path = g_file_get_path (child);
|
|
|
|
+
|
|
|
|
+ switch (flags) {
|
|
|
|
+ case G_FILE_MONITOR_EVENT_CHANGED:
|
|
|
|
+ case G_FILE_MONITOR_EVENT_CREATED:
|
|
|
|
+ /* we just do as if it was a new file: GspApp is clever enough
|
|
|
|
+ * to do the right thing */
|
|
|
|
+ app = gsp_app_new (path, (unsigned int) index);
|
|
|
|
+
|
|
|
|
+ /* we didn't have this app before, so add it */
|
|
|
|
+ if (old_app == NULL && app != NULL) {
|
|
|
|
+ gsp_app_manager_add (manager, app);
|
|
|
|
+ g_object_unref (app);
|
|
|
|
+ }
|
|
|
|
+ /* else: it was just updated, GspApp took care of
|
|
|
|
+ * sending the event */
|
|
|
|
+ break;
|
|
|
|
+ case G_FILE_MONITOR_EVENT_DELETED:
|
|
|
|
+ if (!old_app) {
|
|
|
|
+ /* it got deleted, but we don't know about it, so
|
|
|
|
+ * nothing to do */
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ _gsp_app_manager_handle_delete (manager, old_app,
|
|
|
|
+ basename, index);
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_free (path);
|
|
|
|
+ g_free (dir);
|
|
|
|
+ g_free (basename);
|
|
|
|
+
|
|
|
|
+ return TRUE;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Initialization
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+_gsp_app_manager_fill_from_dir (GspAppManager *manager,
|
|
|
|
+ GspXdgDir *xdgdir)
|
|
|
|
+{
|
|
|
|
+ GFile *file;
|
|
|
|
+ GDir *dir;
|
|
|
|
+ const char *name;
|
|
|
|
+
|
|
|
|
+ file = g_file_new_for_path (xdgdir->dir);
|
|
|
|
+ xdgdir->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE,
|
|
|
|
+ NULL, NULL);
|
|
|
|
+ g_object_unref (file);
|
|
|
|
+
|
|
|
|
+ if (xdgdir->monitor) {
|
|
|
|
+ g_signal_connect (xdgdir->monitor, "changed",
|
|
|
|
+ G_CALLBACK (gsp_app_manager_xdg_dir_monitor),
|
|
|
|
+ manager);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ dir = g_dir_open (xdgdir->dir, 0, NULL);
|
|
|
|
+ if (!dir) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ while ((name = g_dir_read_name (dir))) {
|
|
|
|
+ GspApp *app;
|
|
|
|
+ char *desktop_file_path;
|
|
|
|
+
|
|
|
|
+ if (!g_str_has_suffix (name, ".desktop")) {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ desktop_file_path = g_build_filename (xdgdir->dir, name, NULL);
|
|
|
|
+ app = gsp_app_new (desktop_file_path, xdgdir->index);
|
|
|
|
+
|
|
|
|
+ if (app != NULL) {
|
|
|
|
+ gsp_app_manager_add (manager, app);
|
|
|
|
+ g_object_unref (app);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_free (desktop_file_path);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_dir_close (dir);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+gsp_app_manager_fill (GspAppManager *manager)
|
|
|
|
+{
|
|
|
|
+ char **autostart_dirs;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ if (manager->priv->apps != NULL)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ autostart_dirs = gsm_util_get_autostart_dirs ();
|
|
|
|
+ /* we always assume that the first directory is the user one */
|
|
|
|
+ g_assert (g_str_has_prefix (autostart_dirs[0],
|
|
|
|
+ g_get_user_config_dir ()));
|
|
|
|
+
|
|
|
|
+ for (i = 0; autostart_dirs[i] != NULL; i++) {
|
|
|
|
+ GspXdgDir *xdgdir;
|
|
|
|
+
|
|
|
|
+ if (gsp_app_manager_get_dir_index (manager,
|
|
|
|
+ autostart_dirs[i]) >= 0) {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ xdgdir = _gsp_xdg_dir_new (autostart_dirs[i], i);
|
|
|
|
+ manager->priv->dirs = g_slist_prepend (manager->priv->dirs,
|
|
|
|
+ xdgdir);
|
|
|
|
+
|
|
|
|
+ _gsp_app_manager_fill_from_dir (manager, xdgdir);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_strfreev (autostart_dirs);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * App handling
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+_gsp_app_manager_app_unref (GspApp *app,
|
|
|
|
+ GspAppManager *manager)
|
|
|
|
+{
|
|
|
|
+ g_signal_handlers_disconnect_by_func (app,
|
|
|
|
+ _gsp_app_manager_app_removed,
|
|
|
|
+ manager);
|
|
|
|
+ g_object_unref (app);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+_gsp_app_manager_app_removed (GspAppManager *manager,
|
|
|
|
+ GspApp *app)
|
|
|
|
+{
|
|
|
|
+ _gsp_app_manager_emit_removed (manager, app);
|
|
|
|
+ manager->priv->apps = g_slist_remove (manager->priv->apps, app);
|
|
|
|
+ _gsp_app_manager_app_unref (app, manager);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+gsp_app_manager_add (GspAppManager *manager,
|
|
|
|
+ GspApp *app)
|
|
|
|
+{
|
|
|
|
+ g_return_if_fail (GSP_IS_APP_MANAGER (manager));
|
|
|
|
+ g_return_if_fail (GSP_IS_APP (app));
|
|
|
|
+
|
|
|
|
+ manager->priv->apps = g_slist_prepend (manager->priv->apps,
|
|
|
|
+ g_object_ref (app));
|
|
|
|
+ g_signal_connect_swapped (app, "removed",
|
|
|
|
+ G_CALLBACK (_gsp_app_manager_app_removed),
|
|
|
|
+ manager);
|
|
|
|
+ _gsp_app_manager_emit_added (manager, app);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+GspApp *
|
|
|
|
+gsp_app_manager_find_app_with_basename (GspAppManager *manager,
|
|
|
|
+ const char *basename)
|
|
|
|
+{
|
|
|
|
+ GSList *l;
|
|
|
|
+ GspApp *app;
|
|
|
|
+
|
|
|
|
+ g_return_val_if_fail (GSP_IS_APP_MANAGER (manager), NULL);
|
|
|
|
+ g_return_val_if_fail (basename != NULL, NULL);
|
|
|
|
+
|
|
|
|
+ for (l = manager->priv->apps; l != NULL; l = l->next) {
|
|
|
|
+ app = GSP_APP (l->data);
|
|
|
|
+ if (strcmp (basename, gsp_app_get_basename (app)) == 0)
|
|
|
|
+ return app;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return NULL;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Singleton
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+GspAppManager *
|
|
|
|
+gsp_app_manager_get (void)
|
|
|
|
+{
|
|
|
|
+ if (manager == NULL) {
|
|
|
|
+ manager = g_object_new (GSP_TYPE_APP_MANAGER, NULL);
|
|
|
|
+ return manager;
|
|
|
|
+ } else {
|
|
|
|
+ return g_object_ref (manager);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+GSList *
|
|
|
|
+gsp_app_manager_get_apps (GspAppManager *manager)
|
|
|
|
+{
|
|
|
|
+ g_return_val_if_fail (GSP_IS_APP_MANAGER (manager), NULL);
|
|
|
|
+
|
|
|
|
+ return g_slist_copy (manager->priv->apps);
|
|
|
|
+}
|
|
|
|
diff --git a/capplet/gsp-app-manager.h b/capplet/gsp-app-manager.h
|
|
|
|
new file mode 100644
|
|
|
|
index 00000000..777f8d6c
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/capplet/gsp-app-manager.h
|
|
|
|
@@ -0,0 +1,81 @@
|
|
|
|
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
|
|
|
+ *
|
|
|
|
+ * Copyright (C) 1999 Free Software Foundation, Inc.
|
|
|
|
+ * Copyright (C) 2007, 2009 Vincent Untz.
|
|
|
|
+ * Copyright (C) 2008 Lucas Rocha.
|
|
|
|
+ * Copyright (C) 2008 William Jon McCann <jmccann@redhat.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 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
|
|
|
|
+ * 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, write to the Free Software
|
|
|
|
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#ifndef __GSP_APP_MANAGER_H
|
|
|
|
+#define __GSP_APP_MANAGER_H
|
|
|
|
+
|
|
|
|
+#include <glib-object.h>
|
|
|
|
+
|
|
|
|
+#include <gsp-app.h>
|
|
|
|
+
|
|
|
|
+G_BEGIN_DECLS
|
|
|
|
+
|
|
|
|
+#define GSP_TYPE_APP_MANAGER (gsp_app_manager_get_type ())
|
|
|
|
+#define GSP_APP_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSP_TYPE_APP_MANAGER, GspAppManager))
|
|
|
|
+#define GSP_APP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSP_TYPE_APP_MANAGER, GspAppManagerClass))
|
|
|
|
+#define GSP_IS_APP_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSP_TYPE_APP_MANAGER))
|
|
|
|
+#define GSP_IS_APP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSP_TYPE_APP_MANAGER))
|
|
|
|
+#define GSP_APP_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSP_TYPE_APP_MANAGER, GspAppManagerClass))
|
|
|
|
+
|
|
|
|
+typedef struct _GspAppManager GspAppManager;
|
|
|
|
+typedef struct _GspAppManagerClass GspAppManagerClass;
|
|
|
|
+
|
|
|
|
+typedef struct _GspAppManagerPrivate GspAppManagerPrivate;
|
|
|
|
+
|
|
|
|
+struct _GspAppManagerClass
|
|
|
|
+{
|
|
|
|
+ GObjectClass parent_class;
|
|
|
|
+
|
|
|
|
+ void (* added) (GspAppManager *manager,
|
|
|
|
+ GspApp *app);
|
|
|
|
+ void (* removed) (GspAppManager *manager,
|
|
|
|
+ GspApp *app);
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+struct _GspAppManager
|
|
|
|
+{
|
|
|
|
+ GObject parent_instance;
|
|
|
|
+
|
|
|
|
+ GspAppManagerPrivate *priv;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+GType gsp_app_manager_get_type (void);
|
|
|
|
+
|
|
|
|
+GspAppManager *gsp_app_manager_get (void);
|
|
|
|
+
|
|
|
|
+void gsp_app_manager_fill (GspAppManager *manager);
|
|
|
|
+
|
|
|
|
+GSList *gsp_app_manager_get_apps (GspAppManager *manager);
|
|
|
|
+
|
|
|
|
+GspApp *gsp_app_manager_find_app_with_basename (GspAppManager *manager,
|
|
|
|
+ const char *basename);
|
|
|
|
+
|
|
|
|
+const char *gsp_app_manager_get_dir (GspAppManager *manager,
|
|
|
|
+ unsigned int index);
|
|
|
|
+
|
|
|
|
+void gsp_app_manager_add (GspAppManager *manager,
|
|
|
|
+ GspApp *app);
|
|
|
|
+
|
|
|
|
+G_END_DECLS
|
|
|
|
+
|
|
|
|
+#endif /* __GSP_APP_MANAGER_H */
|
|
|
|
diff --git a/capplet/gsp-app.c b/capplet/gsp-app.c
|
|
|
|
new file mode 100644
|
|
|
|
index 00000000..c92b8dad
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/capplet/gsp-app.c
|
|
|
|
@@ -0,0 +1,1102 @@
|
|
|
|
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
|
|
|
+ *
|
|
|
|
+ * Copyright (C) 1999 Free Software Foundation, Inc.
|
|
|
|
+ * Copyright (C) 2007, 2009 Vincent Untz.
|
|
|
|
+ * Copyright (C) 2008 Lucas Rocha.
|
|
|
|
+ * Copyright (C) 2008 William Jon McCann <jmccann@redhat.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 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
|
|
|
|
+ * 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, write to the Free Software
|
|
|
|
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#ifdef HAVE_CONFIG_H
|
|
|
|
+#include <config.h>
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+#include <string.h>
|
|
|
|
+#include <sys/stat.h>
|
|
|
|
+
|
|
|
|
+#include <glib/gi18n.h>
|
|
|
|
+#include <glib/gstdio.h>
|
|
|
|
+
|
|
|
|
+#include "gsm-app-dialog.h"
|
|
|
|
+#include "gsm-properties-dialog.h"
|
|
|
|
+#include "gsm-util.h"
|
|
|
|
+#include "gsp-app-manager.h"
|
|
|
|
+#include "gsp-keyfile.h"
|
|
|
|
+
|
|
|
|
+#include "gsp-app.h"
|
|
|
|
+
|
|
|
|
+#define GSP_APP_SAVE_DELAY 2
|
|
|
|
+
|
|
|
|
+#define GSP_ASP_SAVE_MASK_HIDDEN 0x0001
|
|
|
|
+#define GSP_ASP_SAVE_MASK_ENABLED 0x0002
|
|
|
|
+#define GSP_ASP_SAVE_MASK_NAME 0x0004
|
|
|
|
+#define GSP_ASP_SAVE_MASK_EXEC 0x0008
|
|
|
|
+#define GSP_ASP_SAVE_MASK_COMMENT 0x0010
|
|
|
|
+#define GSP_ASP_SAVE_MASK_NO_DISPLAY 0x0020
|
|
|
|
+#define GSP_ASP_SAVE_MASK_ALL 0xffff
|
|
|
|
+
|
|
|
|
+struct _GspAppPrivate {
|
|
|
|
+ char *basename;
|
|
|
|
+ char *path;
|
|
|
|
+
|
|
|
|
+ gboolean hidden;
|
|
|
|
+ gboolean no_display;
|
|
|
|
+ gboolean enabled;
|
|
|
|
+ gboolean shown;
|
|
|
|
+
|
|
|
|
+ char *name;
|
|
|
|
+ char *exec;
|
|
|
|
+ char *comment;
|
|
|
|
+ char *icon;
|
|
|
|
+
|
|
|
|
+ GIcon *gicon;
|
|
|
|
+ char *description;
|
|
|
|
+
|
|
|
|
+ /* position of the directory in the XDG environment variable */
|
|
|
|
+ unsigned int xdg_position;
|
|
|
|
+ /* position of the first system directory in the XDG env var containing
|
|
|
|
+ * this autostart app too (G_MAXUINT means none) */
|
|
|
|
+ unsigned int xdg_system_position;
|
|
|
|
+
|
|
|
|
+ unsigned int save_timeout;
|
|
|
|
+ /* mask of what has changed */
|
|
|
|
+ unsigned int save_mask;
|
|
|
|
+ /* path that contains the original file that needs to be saved */
|
|
|
|
+ char *old_system_path;
|
|
|
|
+ /* after writing to file, we skip the next file monitor event of type
|
|
|
|
+ * CHANGED */
|
|
|
|
+ gboolean skip_next_monitor_event;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+#define GSP_APP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSP_TYPE_APP, GspAppPrivate))
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+enum {
|
|
|
|
+ CHANGED,
|
|
|
|
+ REMOVED,
|
|
|
|
+ LAST_SIGNAL
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static guint gsp_app_signals[LAST_SIGNAL] = { 0 };
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+G_DEFINE_TYPE (GspApp, gsp_app, G_TYPE_OBJECT)
|
|
|
|
+
|
|
|
|
+static void gsp_app_dispose (GObject *object);
|
|
|
|
+static void gsp_app_finalize (GObject *object);
|
|
|
|
+static gboolean _gsp_app_save (gpointer data);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static gboolean
|
|
|
|
+_gsp_str_equal (const char *a,
|
|
|
|
+ const char *b)
|
|
|
|
+{
|
|
|
|
+ if (g_strcmp0 (a, b) == 0) {
|
|
|
|
+ return TRUE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (a && !b && a[0] == '\0') {
|
|
|
|
+ return TRUE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (b && !a && b[0] == '\0') {
|
|
|
|
+ return TRUE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return FALSE;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+gsp_app_class_init (GspAppClass *class)
|
|
|
|
+{
|
|
|
|
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
|
|
|
|
+
|
|
|
|
+ gobject_class->dispose = gsp_app_dispose;
|
|
|
|
+ gobject_class->finalize = gsp_app_finalize;
|
|
|
|
+
|
|
|
|
+ gsp_app_signals[CHANGED] =
|
|
|
|
+ g_signal_new ("changed",
|
|
|
|
+ G_TYPE_FROM_CLASS (gobject_class),
|
|
|
|
+ G_SIGNAL_RUN_LAST,
|
|
|
|
+ G_STRUCT_OFFSET (GspAppClass,
|
|
|
|
+ changed),
|
|
|
|
+ NULL,
|
|
|
|
+ NULL,
|
|
|
|
+ g_cclosure_marshal_VOID__VOID,
|
|
|
|
+ G_TYPE_NONE, 0);
|
|
|
|
+
|
|
|
|
+ gsp_app_signals[REMOVED] =
|
|
|
|
+ g_signal_new ("removed",
|
|
|
|
+ G_TYPE_FROM_CLASS (gobject_class),
|
|
|
|
+ G_SIGNAL_RUN_LAST,
|
|
|
|
+ G_STRUCT_OFFSET (GspAppClass,
|
|
|
|
+ removed),
|
|
|
|
+ NULL,
|
|
|
|
+ NULL,
|
|
|
|
+ g_cclosure_marshal_VOID__VOID,
|
|
|
|
+ G_TYPE_NONE, 0);
|
|
|
|
+
|
|
|
|
+ g_type_class_add_private (class, sizeof (GspAppPrivate));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+gsp_app_init (GspApp *app)
|
|
|
|
+{
|
|
|
|
+ app->priv = GSP_APP_GET_PRIVATE (app);
|
|
|
|
+
|
|
|
|
+ memset (app->priv, 0, sizeof (GspAppPrivate));
|
|
|
|
+ app->priv->xdg_position = G_MAXUINT;
|
|
|
|
+ app->priv->xdg_system_position = G_MAXUINT;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+_gsp_app_free_reusable_data (GspApp *app)
|
|
|
|
+{
|
|
|
|
+ if (app->priv->path) {
|
|
|
|
+ g_free (app->priv->path);
|
|
|
|
+ app->priv->path = NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (app->priv->name) {
|
|
|
|
+ g_free (app->priv->name);
|
|
|
|
+ app->priv->name = NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (app->priv->exec) {
|
|
|
|
+ g_free (app->priv->exec);
|
|
|
|
+ app->priv->exec = NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (app->priv->comment) {
|
|
|
|
+ g_free (app->priv->comment);
|
|
|
|
+ app->priv->comment = NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (app->priv->icon) {
|
|
|
|
+ g_free (app->priv->icon);
|
|
|
|
+ app->priv->icon = NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (app->priv->gicon) {
|
|
|
|
+ g_object_unref (app->priv->gicon);
|
|
|
|
+ app->priv->gicon = NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (app->priv->description) {
|
|
|
|
+ g_free (app->priv->description);
|
|
|
|
+ app->priv->description = NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (app->priv->old_system_path) {
|
|
|
|
+ g_free (app->priv->old_system_path);
|
|
|
|
+ app->priv->old_system_path = NULL;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+gsp_app_dispose (GObject *object)
|
|
|
|
+{
|
|
|
|
+ GspApp *app;
|
|
|
|
+
|
|
|
|
+ g_return_if_fail (object != NULL);
|
|
|
|
+ g_return_if_fail (GSP_IS_APP (object));
|
|
|
|
+
|
|
|
|
+ app = GSP_APP (object);
|
|
|
|
+
|
|
|
|
+ /* we save in dispose since we might need to reference GspAppManager */
|
|
|
|
+ if (app->priv->save_timeout) {
|
|
|
|
+ g_source_remove (app->priv->save_timeout);
|
|
|
|
+ app->priv->save_timeout = 0;
|
|
|
|
+
|
|
|
|
+ /* save now */
|
|
|
|
+ _gsp_app_save (app);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ G_OBJECT_CLASS (gsp_app_parent_class)->dispose (object);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+gsp_app_finalize (GObject *object)
|
|
|
|
+{
|
|
|
|
+ GspApp *app;
|
|
|
|
+
|
|
|
|
+ g_return_if_fail (object != NULL);
|
|
|
|
+ g_return_if_fail (GSP_IS_APP (object));
|
|
|
|
+
|
|
|
|
+ app = GSP_APP (object);
|
|
|
|
+
|
|
|
|
+ if (app->priv->basename) {
|
|
|
|
+ g_free (app->priv->basename);
|
|
|
|
+ app->priv->basename = NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ _gsp_app_free_reusable_data (app);
|
|
|
|
+
|
|
|
|
+ G_OBJECT_CLASS (gsp_app_parent_class)->finalize (object);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+_gsp_app_emit_changed (GspApp *app)
|
|
|
|
+{
|
|
|
|
+ g_signal_emit (G_OBJECT (app), gsp_app_signals[CHANGED], 0);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+_gsp_app_emit_removed (GspApp *app)
|
|
|
|
+{
|
|
|
|
+ g_signal_emit (G_OBJECT (app), gsp_app_signals[REMOVED], 0);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+_gsp_app_update_description (GspApp *app)
|
|
|
|
+{
|
|
|
|
+ const char *primary;
|
|
|
|
+ const char *secondary;
|
|
|
|
+
|
|
|
|
+ if (!gsm_util_text_is_blank (app->priv->name)) {
|
|
|
|
+ primary = app->priv->name;
|
|
|
|
+ } else if (!gsm_util_text_is_blank (app->priv->exec)) {
|
|
|
|
+ primary = app->priv->exec;
|
|
|
|
+ } else {
|
|
|
|
+ primary = _("No name");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!gsm_util_text_is_blank (app->priv->comment)) {
|
|
|
|
+ secondary = app->priv->comment;
|
|
|
|
+ } else {
|
|
|
|
+ secondary = _("No description");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_free (app->priv->description);
|
|
|
|
+ app->priv->description = g_markup_printf_escaped ("<b>%s</b>\n%s",
|
|
|
|
+ primary,
|
|
|
|
+ secondary);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Saving
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+_gsp_ensure_user_autostart_dir (void)
|
|
|
|
+{
|
|
|
|
+ char *dir;
|
|
|
|
+
|
|
|
|
+ dir = g_build_filename (g_get_user_config_dir (), "autostart", NULL);
|
|
|
|
+ g_mkdir_with_parents (dir, S_IRWXU);
|
|
|
|
+
|
|
|
|
+ g_free (dir);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static gboolean
|
|
|
|
+_gsp_app_user_equal_system (GspApp *app,
|
|
|
|
+ char **system_path)
|
|
|
|
+{
|
|
|
|
+ GspAppManager *manager;
|
|
|
|
+ const char *system_dir;
|
|
|
|
+ char *path;
|
|
|
|
+ char *str;
|
|
|
|
+ GKeyFile *keyfile;
|
|
|
|
+
|
|
|
|
+ manager = gsp_app_manager_get ();
|
|
|
|
+ system_dir = gsp_app_manager_get_dir (manager,
|
|
|
|
+ app->priv->xdg_system_position);
|
|
|
|
+ g_object_unref (manager);
|
|
|
|
+ if (!system_dir) {
|
|
|
|
+ return FALSE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ path = g_build_filename (system_dir, app->priv->basename, NULL);
|
|
|
|
+
|
|
|
|
+ keyfile = g_key_file_new ();
|
|
|
|
+ if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) {
|
|
|
|
+ g_free (path);
|
|
|
|
+ g_key_file_free (keyfile);
|
|
|
|
+ return FALSE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (gsp_key_file_get_boolean (keyfile,
|
|
|
|
+ G_KEY_FILE_DESKTOP_KEY_HIDDEN,
|
|
|
|
+ FALSE) != app->priv->hidden ||
|
|
|
|
+ gsp_key_file_get_boolean (keyfile,
|
|
|
|
+ GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED,
|
|
|
|
+ TRUE) != app->priv->enabled ||
|
|
|
|
+ gsp_key_file_get_shown (keyfile,
|
|
|
|
+ gsm_util_get_current_desktop ()) != app->priv->shown) {
|
|
|
|
+ g_free (path);
|
|
|
|
+ g_key_file_free (keyfile);
|
|
|
|
+ return FALSE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (gsp_key_file_get_boolean (keyfile,
|
|
|
|
+ G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY,
|
|
|
|
+ FALSE) != app->priv->no_display) {
|
|
|
|
+ g_free (path);
|
|
|
|
+ g_key_file_free (keyfile);
|
|
|
|
+ return FALSE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ str = gsp_key_file_get_locale_string (keyfile,
|
|
|
|
+ G_KEY_FILE_DESKTOP_KEY_NAME);
|
|
|
|
+ if (!_gsp_str_equal (str, app->priv->name)) {
|
|
|
|
+ g_free (str);
|
|
|
|
+ g_free (path);
|
|
|
|
+ g_key_file_free (keyfile);
|
|
|
|
+ return FALSE;
|
|
|
|
+ }
|
|
|
|
+ g_free (str);
|
|
|
|
+
|
|
|
|
+ str = gsp_key_file_get_locale_string (keyfile,
|
|
|
|
+ G_KEY_FILE_DESKTOP_KEY_COMMENT);
|
|
|
|
+ if (!_gsp_str_equal (str, app->priv->comment)) {
|
|
|
|
+ g_free (str);
|
|
|
|
+ g_free (path);
|
|
|
|
+ g_key_file_free (keyfile);
|
|
|
|
+ return FALSE;
|
|
|
|
+ }
|
|
|
|
+ g_free (str);
|
|
|
|
+
|
|
|
|
+ str = gsp_key_file_get_string (keyfile,
|
|
|
|
+ G_KEY_FILE_DESKTOP_KEY_EXEC);
|
|
|
|
+ if (!_gsp_str_equal (str, app->priv->exec)) {
|
|
|
|
+ g_free (str);
|
|
|
|
+ g_free (path);
|
|
|
|
+ g_key_file_free (keyfile);
|
|
|
|
+ return FALSE;
|
|
|
|
+ }
|
|
|
|
+ g_free (str);
|
|
|
|
+
|
|
|
|
+ str = gsp_key_file_get_locale_string (keyfile,
|
|
|
|
+ G_KEY_FILE_DESKTOP_KEY_ICON);
|
|
|
|
+ if (!_gsp_str_equal (str, app->priv->icon)) {
|
|
|
|
+ g_free (str);
|
|
|
|
+ g_free (path);
|
|
|
|
+ g_key_file_free (keyfile);
|
|
|
|
+ return FALSE;
|
|
|
|
+ }
|
|
|
|
+ g_free (str);
|
|
|
|
+
|
|
|
|
+ g_key_file_free (keyfile);
|
|
|
|
+
|
|
|
|
+ *system_path = path;
|
|
|
|
+
|
|
|
|
+ return TRUE;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline void
|
|
|
|
+_gsp_app_save_done_success (GspApp *app)
|
|
|
|
+{
|
|
|
|
+ app->priv->save_mask = 0;
|
|
|
|
+
|
|
|
|
+ if (app->priv->old_system_path) {
|
|
|
|
+ g_free (app->priv->old_system_path);
|
|
|
|
+ app->priv->old_system_path = NULL;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static gboolean
|
|
|
|
+_gsp_app_save (gpointer data)
|
|
|
|
+{
|
|
|
|
+ GspApp *app;
|
|
|
|
+ char *use_path;
|
|
|
|
+ GKeyFile *keyfile;
|
|
|
|
+ GError *error;
|
|
|
|
+
|
|
|
|
+ app = GSP_APP (data);
|
|
|
|
+
|
|
|
|
+ /* first check if removing the data from the user dir and using the
|
|
|
|
+ * data from the system dir is enough -- this helps us keep clean the
|
|
|
|
+ * user config dir by removing unneeded files */
|
|
|
|
+ if (_gsp_app_user_equal_system (app, &use_path)) {
|
|
|
|
+ if (g_file_test (app->priv->path, G_FILE_TEST_EXISTS)) {
|
|
|
|
+ g_remove (app->priv->path);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_free (app->priv->path);
|
|
|
|
+ app->priv->path = use_path;
|
|
|
|
+
|
|
|
|
+ app->priv->xdg_position = app->priv->xdg_system_position;
|
|
|
|
+
|
|
|
|
+ _gsp_app_save_done_success (app);
|
|
|
|
+ return FALSE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (app->priv->old_system_path)
|
|
|
|
+ use_path = app->priv->old_system_path;
|
|
|
|
+ else
|
|
|
|
+ use_path = app->priv->path;
|
|
|
|
+
|
|
|
|
+ keyfile = g_key_file_new ();
|
|
|
|
+
|
|
|
|
+ error = NULL;
|
|
|
|
+ g_key_file_load_from_file (keyfile, use_path,
|
|
|
|
+ G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS,
|
|
|
|
+ &error);
|
|
|
|
+
|
|
|
|
+ if (error) {
|
|
|
|
+ g_error_free (error);
|
|
|
|
+ gsp_key_file_populate (keyfile);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (app->priv->save_mask & GSP_ASP_SAVE_MASK_HIDDEN) {
|
|
|
|
+ gsp_key_file_set_boolean (keyfile,
|
|
|
|
+ G_KEY_FILE_DESKTOP_KEY_HIDDEN,
|
|
|
|
+ app->priv->hidden);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (app->priv->save_mask & GSP_ASP_SAVE_MASK_NO_DISPLAY) {
|
|
|
|
+ gsp_key_file_set_boolean (keyfile,
|
|
|
|
+ G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY,
|
|
|
|
+ app->priv->no_display);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (app->priv->save_mask & GSP_ASP_SAVE_MASK_ENABLED) {
|
|
|
|
+ gsp_key_file_set_boolean (keyfile,
|
|
|
|
+ GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED,
|
|
|
|
+ app->priv->enabled);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (app->priv->save_mask & GSP_ASP_SAVE_MASK_NAME) {
|
|
|
|
+ gsp_key_file_set_locale_string (keyfile,
|
|
|
|
+ G_KEY_FILE_DESKTOP_KEY_NAME,
|
|
|
|
+ app->priv->name);
|
|
|
|
+ gsp_key_file_ensure_C_key (keyfile, G_KEY_FILE_DESKTOP_KEY_NAME);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (app->priv->save_mask & GSP_ASP_SAVE_MASK_COMMENT) {
|
|
|
|
+ gsp_key_file_set_locale_string (keyfile,
|
|
|
|
+ G_KEY_FILE_DESKTOP_KEY_COMMENT,
|
|
|
|
+ app->priv->comment);
|
|
|
|
+ gsp_key_file_ensure_C_key (keyfile, G_KEY_FILE_DESKTOP_KEY_COMMENT);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (app->priv->save_mask & GSP_ASP_SAVE_MASK_EXEC) {
|
|
|
|
+ gsp_key_file_set_string (keyfile,
|
|
|
|
+ G_KEY_FILE_DESKTOP_KEY_EXEC,
|
|
|
|
+ app->priv->exec);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ _gsp_ensure_user_autostart_dir ();
|
|
|
|
+ if (gsp_key_file_to_file (keyfile, app->priv->path, NULL)) {
|
|
|
|
+ app->priv->skip_next_monitor_event = TRUE;
|
|
|
|
+ _gsp_app_save_done_success (app);
|
|
|
|
+ } else {
|
|
|
|
+ g_warning ("Could not save %s file", app->priv->path);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_key_file_free (keyfile);
|
|
|
|
+
|
|
|
|
+ app->priv->save_timeout = 0;
|
|
|
|
+ return FALSE;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+_gsp_app_queue_save (GspApp *app)
|
|
|
|
+{
|
|
|
|
+ if (app->priv->save_timeout) {
|
|
|
|
+ g_source_remove (app->priv->save_timeout);
|
|
|
|
+ app->priv->save_timeout = 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* if the file was not in the user directory, then we'll create a copy
|
|
|
|
+ * there */
|
|
|
|
+ if (app->priv->xdg_position != 0) {
|
|
|
|
+ app->priv->xdg_position = 0;
|
|
|
|
+
|
|
|
|
+ if (app->priv->old_system_path == NULL) {
|
|
|
|
+ app->priv->old_system_path = app->priv->path;
|
|
|
|
+ /* if old_system_path was not NULL, then it means we
|
|
|
|
+ * tried to save and we failed; in that case, we want
|
|
|
|
+ * to try again and use the old file as a basis again */
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ app->priv->path = g_build_filename (g_get_user_config_dir (),
|
|
|
|
+ "autostart",
|
|
|
|
+ app->priv->basename, NULL);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ app->priv->save_timeout = g_timeout_add_seconds (GSP_APP_SAVE_DELAY,
|
|
|
|
+ _gsp_app_save,
|
|
|
|
+ app);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Accessors
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+const char *
|
|
|
|
+gsp_app_get_basename (GspApp *app)
|
|
|
|
+{
|
|
|
|
+ g_return_val_if_fail (GSP_IS_APP (app), NULL);
|
|
|
|
+
|
|
|
|
+ return app->priv->basename;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const char *
|
|
|
|
+gsp_app_get_path (GspApp *app)
|
|
|
|
+{
|
|
|
|
+ g_return_val_if_fail (GSP_IS_APP (app), NULL);
|
|
|
|
+
|
|
|
|
+ return app->priv->path;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+gboolean
|
|
|
|
+gsp_app_get_hidden (GspApp *app)
|
|
|
|
+{
|
|
|
|
+ g_return_val_if_fail (GSP_IS_APP (app), FALSE);
|
|
|
|
+
|
|
|
|
+ return app->priv->hidden;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+gboolean
|
|
|
|
+gsp_app_get_display (GspApp *app)
|
|
|
|
+{
|
|
|
|
+ g_return_val_if_fail (GSP_IS_APP (app), FALSE);
|
|
|
|
+
|
|
|
|
+ return !app->priv->no_display;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+gboolean
|
|
|
|
+gsp_app_get_enabled (GspApp *app)
|
|
|
|
+{
|
|
|
|
+ g_return_val_if_fail (GSP_IS_APP (app), FALSE);
|
|
|
|
+
|
|
|
|
+ return app->priv->enabled;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+gsp_app_set_enabled (GspApp *app,
|
|
|
|
+ gboolean enabled)
|
|
|
|
+{
|
|
|
|
+ g_return_if_fail (GSP_IS_APP (app));
|
|
|
|
+
|
|
|
|
+ if (enabled == app->priv->enabled) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ app->priv->enabled = enabled;
|
|
|
|
+ app->priv->save_mask |= GSP_ASP_SAVE_MASK_ENABLED;
|
|
|
|
+
|
|
|
|
+ _gsp_app_queue_save (app);
|
|
|
|
+ _gsp_app_emit_changed (app);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+gboolean
|
|
|
|
+gsp_app_get_shown (GspApp *app)
|
|
|
|
+{
|
|
|
|
+ g_return_val_if_fail (GSP_IS_APP (app), FALSE);
|
|
|
|
+
|
|
|
|
+ return app->priv->shown;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const char *
|
|
|
|
+gsp_app_get_name (GspApp *app)
|
|
|
|
+{
|
|
|
|
+ g_return_val_if_fail (GSP_IS_APP (app), NULL);
|
|
|
|
+
|
|
|
|
+ return app->priv->name;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const char *
|
|
|
|
+gsp_app_get_exec (GspApp *app)
|
|
|
|
+{
|
|
|
|
+ g_return_val_if_fail (GSP_IS_APP (app), NULL);
|
|
|
|
+
|
|
|
|
+ return app->priv->exec;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const char *
|
|
|
|
+gsp_app_get_comment (GspApp *app)
|
|
|
|
+{
|
|
|
|
+ g_return_val_if_fail (GSP_IS_APP (app), NULL);
|
|
|
|
+
|
|
|
|
+ return app->priv->comment;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+GIcon *
|
|
|
|
+gsp_app_get_icon (GspApp *app)
|
|
|
|
+{
|
|
|
|
+ g_return_val_if_fail (GSP_IS_APP (app), NULL);
|
|
|
|
+
|
|
|
|
+ if (app->priv->gicon) {
|
|
|
|
+ return g_object_ref (app->priv->gicon);
|
|
|
|
+ } else {
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+unsigned int
|
|
|
|
+gsp_app_get_xdg_position (GspApp *app)
|
|
|
|
+{
|
|
|
|
+ g_return_val_if_fail (GSP_IS_APP (app), G_MAXUINT);
|
|
|
|
+
|
|
|
|
+ return app->priv->xdg_position;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+unsigned int
|
|
|
|
+gsp_app_get_xdg_system_position (GspApp *app)
|
|
|
|
+{
|
|
|
|
+ g_return_val_if_fail (GSP_IS_APP (app), G_MAXUINT);
|
|
|
|
+
|
|
|
|
+ return app->priv->xdg_system_position;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+gsp_app_set_xdg_system_position (GspApp *app,
|
|
|
|
+ unsigned int position)
|
|
|
|
+{
|
|
|
|
+ g_return_if_fail (GSP_IS_APP (app));
|
|
|
|
+
|
|
|
|
+ app->priv->xdg_system_position = position;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const char *
|
|
|
|
+gsp_app_get_description (GspApp *app)
|
|
|
|
+{
|
|
|
|
+ g_return_val_if_fail (GSP_IS_APP (app), NULL);
|
|
|
|
+
|
|
|
|
+ return app->priv->description;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * High-level edition
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+gsp_app_update (GspApp *app,
|
|
|
|
+ const char *name,
|
|
|
|
+ const char *comment,
|
|
|
|
+ const char *exec)
|
|
|
|
+{
|
|
|
|
+ gboolean changed;
|
|
|
|
+
|
|
|
|
+ g_return_if_fail (GSP_IS_APP (app));
|
|
|
|
+
|
|
|
|
+ changed = FALSE;
|
|
|
|
+
|
|
|
|
+ if (!_gsp_str_equal (name, app->priv->name)) {
|
|
|
|
+ changed = TRUE;
|
|
|
|
+ g_free (app->priv->name);
|
|
|
|
+ app->priv->name = g_strdup (name);
|
|
|
|
+ app->priv->save_mask |= GSP_ASP_SAVE_MASK_NAME;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!_gsp_str_equal (comment, app->priv->comment)) {
|
|
|
|
+ changed = TRUE;
|
|
|
|
+ g_free (app->priv->comment);
|
|
|
|
+ app->priv->comment = g_strdup (comment);
|
|
|
|
+ app->priv->save_mask |= GSP_ASP_SAVE_MASK_COMMENT;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (changed) {
|
|
|
|
+ _gsp_app_update_description (app);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!_gsp_str_equal (exec, app->priv->exec)) {
|
|
|
|
+ changed = TRUE;
|
|
|
|
+ g_free (app->priv->exec);
|
|
|
|
+ app->priv->exec = g_strdup (exec);
|
|
|
|
+ app->priv->save_mask |= GSP_ASP_SAVE_MASK_EXEC;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (changed) {
|
|
|
|
+ _gsp_app_queue_save (app);
|
|
|
|
+ _gsp_app_emit_changed (app);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+gsp_app_delete (GspApp *app)
|
|
|
|
+{
|
|
|
|
+ g_return_if_fail (GSP_IS_APP (app));
|
|
|
|
+
|
|
|
|
+ if (app->priv->xdg_position == 0 &&
|
|
|
|
+ app->priv->xdg_system_position == G_MAXUINT) {
|
|
|
|
+ /* exists in user directory only */
|
|
|
|
+ if (app->priv->save_timeout) {
|
|
|
|
+ g_source_remove (app->priv->save_timeout);
|
|
|
|
+ app->priv->save_timeout = 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (g_file_test (app->priv->path, G_FILE_TEST_EXISTS)) {
|
|
|
|
+ g_remove (app->priv->path);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* for extra safety */
|
|
|
|
+ app->priv->hidden = TRUE;
|
|
|
|
+ app->priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN;
|
|
|
|
+
|
|
|
|
+ _gsp_app_emit_removed (app);
|
|
|
|
+ } else {
|
|
|
|
+ /* also exists in system directory, so we have to keep a file
|
|
|
|
+ * in the user directory */
|
|
|
|
+ app->priv->hidden = TRUE;
|
|
|
|
+ app->priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN;
|
|
|
|
+
|
|
|
|
+ _gsp_app_queue_save (app);
|
|
|
|
+ _gsp_app_emit_changed (app);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * New autostart app
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+gsp_app_reload_at (GspApp *app,
|
|
|
|
+ const char *path,
|
|
|
|
+ unsigned int xdg_position)
|
|
|
|
+{
|
|
|
|
+ g_return_if_fail (GSP_IS_APP (app));
|
|
|
|
+
|
|
|
|
+ app->priv->xdg_position = G_MAXUINT;
|
|
|
|
+ gsp_app_new (path, xdg_position);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+GspApp *
|
|
|
|
+gsp_app_new (const char *path,
|
|
|
|
+ unsigned int xdg_position)
|
|
|
|
+{
|
|
|
|
+ GspAppManager *manager;
|
|
|
|
+ GspApp *app;
|
|
|
|
+ GKeyFile *keyfile;
|
|
|
|
+ char *basename;
|
|
|
|
+ gboolean new;
|
|
|
|
+
|
|
|
|
+ basename = g_path_get_basename (path);
|
|
|
|
+
|
|
|
|
+ manager = gsp_app_manager_get ();
|
|
|
|
+ app = gsp_app_manager_find_app_with_basename (manager, basename);
|
|
|
|
+ g_object_unref (manager);
|
|
|
|
+
|
|
|
|
+ new = (app == NULL);
|
|
|
|
+
|
|
|
|
+ if (!new) {
|
|
|
|
+ if (app->priv->xdg_position == xdg_position) {
|
|
|
|
+ if (app->priv->skip_next_monitor_event) {
|
|
|
|
+ app->priv->skip_next_monitor_event = FALSE;
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+ /* else: the file got changed but not by us, we'll
|
|
|
|
+ * update our data from disk */
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (app->priv->xdg_position < xdg_position ||
|
|
|
|
+ app->priv->save_timeout != 0) {
|
|
|
|
+ /* we don't really care about this file, since we
|
|
|
|
+ * already have something with a higher priority, or
|
|
|
|
+ * we're going to write something in the user config
|
|
|
|
+ * anyway.
|
|
|
|
+ * Note: xdg_position >= 1 so it's a system dir */
|
|
|
|
+ app->priv->xdg_system_position = MIN (xdg_position,
|
|
|
|
+ app->priv->xdg_system_position);
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ keyfile = g_key_file_new ();
|
|
|
|
+ if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) {
|
|
|
|
+ g_key_file_free (keyfile);
|
|
|
|
+ g_free (basename);
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (new) {
|
|
|
|
+ app = g_object_new (GSP_TYPE_APP, NULL);
|
|
|
|
+ app->priv->basename = basename;
|
|
|
|
+ } else {
|
|
|
|
+ g_free (basename);
|
|
|
|
+ _gsp_app_free_reusable_data (app);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ app->priv->path = g_strdup (path);
|
|
|
|
+
|
|
|
|
+ app->priv->hidden = gsp_key_file_get_boolean (keyfile,
|
|
|
|
+ G_KEY_FILE_DESKTOP_KEY_HIDDEN,
|
|
|
|
+ FALSE);
|
|
|
|
+ app->priv->no_display = gsp_key_file_get_boolean (keyfile,
|
|
|
|
+ G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY,
|
|
|
|
+ FALSE);
|
|
|
|
+ app->priv->enabled = gsp_key_file_get_boolean (keyfile,
|
|
|
|
+ GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED,
|
|
|
|
+ TRUE);
|
|
|
|
+ app->priv->shown = gsp_key_file_get_shown (keyfile,
|
|
|
|
+ gsm_util_get_current_desktop ());
|
|
|
|
+
|
|
|
|
+ app->priv->name = gsp_key_file_get_locale_string (keyfile,
|
|
|
|
+ G_KEY_FILE_DESKTOP_KEY_NAME);
|
|
|
|
+ app->priv->exec = gsp_key_file_get_string (keyfile,
|
|
|
|
+ G_KEY_FILE_DESKTOP_KEY_EXEC);
|
|
|
|
+ app->priv->comment = gsp_key_file_get_locale_string (keyfile,
|
|
|
|
+ G_KEY_FILE_DESKTOP_KEY_COMMENT);
|
|
|
|
+
|
|
|
|
+ if (gsm_util_text_is_blank (app->priv->name)) {
|
|
|
|
+ g_free (app->priv->name);
|
|
|
|
+ app->priv->name = g_strdup (app->priv->exec);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ app->priv->icon = gsp_key_file_get_locale_string (keyfile,
|
|
|
|
+ G_KEY_FILE_DESKTOP_KEY_ICON);
|
|
|
|
+
|
|
|
|
+ if (app->priv->icon) {
|
|
|
|
+ /* look at icon and see if it's a themed icon or not */
|
|
|
|
+ if (g_path_is_absolute (app->priv->icon)) {
|
|
|
|
+ GFile *iconfile;
|
|
|
|
+
|
|
|
|
+ iconfile = g_file_new_for_path (app->priv->icon);
|
|
|
|
+ app->priv->gicon = g_file_icon_new (iconfile);
|
|
|
|
+ g_object_unref (iconfile);
|
|
|
|
+ } else {
|
|
|
|
+ app->priv->gicon = g_themed_icon_new (app->priv->icon);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ app->priv->gicon = NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_key_file_free (keyfile);
|
|
|
|
+
|
|
|
|
+ _gsp_app_update_description (app);
|
|
|
|
+
|
|
|
|
+ if (xdg_position > 0) {
|
|
|
|
+ g_assert (xdg_position <= app->priv->xdg_system_position);
|
|
|
|
+ app->priv->xdg_system_position = xdg_position;
|
|
|
|
+ }
|
|
|
|
+ /* else we keep the old value (which is G_MAXUINT if it wasn't set) */
|
|
|
|
+ app->priv->xdg_position = xdg_position;
|
|
|
|
+
|
|
|
|
+ g_assert (!new || app->priv->save_timeout == 0);
|
|
|
|
+ app->priv->save_timeout = 0;
|
|
|
|
+ app->priv->old_system_path = NULL;
|
|
|
|
+ app->priv->skip_next_monitor_event = FALSE;
|
|
|
|
+
|
|
|
|
+ if (!new) {
|
|
|
|
+ _gsp_app_emit_changed (app);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return app;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static char *
|
|
|
|
+_gsp_find_free_basename (const char *suggested_basename)
|
|
|
|
+{
|
|
|
|
+ GspAppManager *manager;
|
|
|
|
+ char *base_path;
|
|
|
|
+ char *filename;
|
|
|
|
+ char *basename;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ if (g_str_has_suffix (suggested_basename, ".desktop")) {
|
|
|
|
+ char *basename_no_ext;
|
|
|
|
+
|
|
|
|
+ basename_no_ext = g_strndup (suggested_basename,
|
|
|
|
+ strlen (suggested_basename) - strlen (".desktop"));
|
|
|
|
+ base_path = g_build_filename (g_get_user_config_dir (),
|
|
|
|
+ "autostart",
|
|
|
|
+ basename_no_ext, NULL);
|
|
|
|
+ g_free (basename_no_ext);
|
|
|
|
+ } else {
|
|
|
|
+ base_path = g_build_filename (g_get_user_config_dir (),
|
|
|
|
+ "autostart",
|
|
|
|
+ suggested_basename, NULL);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ filename = g_strdup_printf ("%s.desktop", base_path);
|
|
|
|
+ basename = g_path_get_basename (filename);
|
|
|
|
+
|
|
|
|
+ manager = gsp_app_manager_get ();
|
|
|
|
+
|
|
|
|
+ i = 1;
|
|
|
|
+#define _GSP_FIND_MAX_TRY 10000
|
|
|
|
+ while (gsp_app_manager_find_app_with_basename (manager,
|
|
|
|
+ basename) != NULL &&
|
|
|
|
+ g_file_test (filename, G_FILE_TEST_EXISTS) &&
|
|
|
|
+ i < _GSP_FIND_MAX_TRY) {
|
|
|
|
+ g_free (filename);
|
|
|
|
+ g_free (basename);
|
|
|
|
+
|
|
|
|
+ filename = g_strdup_printf ("%s-%d.desktop", base_path, i);
|
|
|
|
+ basename = g_path_get_basename (filename);
|
|
|
|
+
|
|
|
|
+ i++;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_object_unref (manager);
|
|
|
|
+
|
|
|
|
+ g_free (base_path);
|
|
|
|
+ g_free (filename);
|
|
|
|
+
|
|
|
|
+ if (i == _GSP_FIND_MAX_TRY) {
|
|
|
|
+ g_free (basename);
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return basename;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+gsp_app_create (const char *name,
|
|
|
|
+ const char *comment,
|
|
|
|
+ const char *exec)
|
|
|
|
+{
|
|
|
|
+ GspAppManager *manager;
|
|
|
|
+ GspApp *app;
|
|
|
|
+ char *basename;
|
|
|
|
+ char **argv;
|
|
|
|
+ int argc;
|
|
|
|
+
|
|
|
|
+ g_return_if_fail (!gsm_util_text_is_blank (exec));
|
|
|
|
+
|
|
|
|
+ if (!g_shell_parse_argv (exec, &argc, &argv, NULL)) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ basename = _gsp_find_free_basename (argv[0]);
|
|
|
|
+ g_strfreev (argv);
|
|
|
|
+ if (basename == NULL) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ app = g_object_new (GSP_TYPE_APP, NULL);
|
|
|
|
+
|
|
|
|
+ app->priv->basename = basename;
|
|
|
|
+ app->priv->path = g_build_filename (g_get_user_config_dir (),
|
|
|
|
+ "autostart",
|
|
|
|
+ app->priv->basename, NULL);
|
|
|
|
+
|
|
|
|
+ app->priv->hidden = FALSE;
|
|
|
|
+ app->priv->no_display = FALSE;
|
|
|
|
+ app->priv->enabled = TRUE;
|
|
|
|
+ app->priv->shown = TRUE;
|
|
|
|
+
|
|
|
|
+ if (!gsm_util_text_is_blank (name)) {
|
|
|
|
+ app->priv->name = g_strdup (name);
|
|
|
|
+ } else {
|
|
|
|
+ app->priv->name = g_strdup (exec);
|
|
|
|
+ }
|
|
|
|
+ app->priv->exec = g_strdup (exec);
|
|
|
|
+ app->priv->comment = g_strdup (comment);
|
|
|
|
+ app->priv->icon = NULL;
|
|
|
|
+
|
|
|
|
+ app->priv->gicon = NULL;
|
|
|
|
+ _gsp_app_update_description (app);
|
|
|
|
+
|
|
|
|
+ /* by definition */
|
|
|
|
+ app->priv->xdg_position = 0;
|
|
|
|
+ app->priv->xdg_system_position = G_MAXUINT;
|
|
|
|
+
|
|
|
|
+ app->priv->save_timeout = 0;
|
|
|
|
+ app->priv->save_mask |= GSP_ASP_SAVE_MASK_ALL;
|
|
|
|
+ app->priv->old_system_path = NULL;
|
|
|
|
+ app->priv->skip_next_monitor_event = FALSE;
|
|
|
|
+
|
|
|
|
+ _gsp_app_queue_save (app);
|
|
|
|
+
|
|
|
|
+ manager = gsp_app_manager_get ();
|
|
|
|
+ gsp_app_manager_add (manager, app);
|
|
|
|
+ g_object_unref (app);
|
|
|
|
+ g_object_unref (manager);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+gboolean
|
|
|
|
+gsp_app_copy_desktop_file (const char *uri)
|
|
|
|
+{
|
|
|
|
+ GspAppManager *manager;
|
|
|
|
+ GspApp *app;
|
|
|
|
+ GFile *src_file;
|
|
|
|
+ char *src_basename;
|
|
|
|
+ char *dst_basename;
|
|
|
|
+ char *dst_path;
|
|
|
|
+ GFile *dst_file;
|
|
|
|
+ gboolean changed;
|
|
|
|
+
|
|
|
|
+ g_return_val_if_fail (uri != NULL, FALSE);
|
|
|
|
+
|
|
|
|
+ src_file = g_file_new_for_uri (uri);
|
|
|
|
+ src_basename = g_file_get_basename (src_file);
|
|
|
|
+
|
|
|
|
+ if (src_basename == NULL) {
|
|
|
|
+ g_object_unref (src_file);
|
|
|
|
+ return FALSE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ dst_basename = _gsp_find_free_basename (src_basename);
|
|
|
|
+ g_free (src_basename);
|
|
|
|
+
|
|
|
|
+ if (dst_basename == NULL) {
|
|
|
|
+ g_object_unref (src_file);
|
|
|
|
+ return FALSE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ dst_path = g_build_filename (g_get_user_config_dir (),
|
|
|
|
+ "autostart",
|
|
|
|
+ dst_basename, NULL);
|
|
|
|
+ g_free (dst_basename);
|
|
|
|
+
|
|
|
|
+ dst_file = g_file_new_for_path (dst_path);
|
|
|
|
+
|
|
|
|
+ _gsp_ensure_user_autostart_dir ();
|
|
|
|
+ if (!g_file_copy (src_file, dst_file, G_FILE_COPY_NONE,
|
|
|
|
+ NULL, NULL, NULL, NULL)) {
|
|
|
|
+ g_object_unref (src_file);
|
|
|
|
+ g_object_unref (dst_file);
|
|
|
|
+ g_free (dst_path);
|
|
|
|
+ return FALSE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_object_unref (src_file);
|
|
|
|
+ g_object_unref (dst_file);
|
|
|
|
+
|
|
|
|
+ app = gsp_app_new (dst_path, 0);
|
|
|
|
+ if (!app) {
|
|
|
|
+ g_remove (dst_path);
|
|
|
|
+ g_free (dst_path);
|
|
|
|
+ return FALSE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_free (dst_path);
|
|
|
|
+
|
|
|
|
+ changed = FALSE;
|
|
|
|
+ if (app->priv->hidden) {
|
|
|
|
+ changed = TRUE;
|
|
|
|
+ app->priv->hidden = FALSE;
|
|
|
|
+ app->priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (app->priv->no_display) {
|
|
|
|
+ changed = TRUE;
|
|
|
|
+ app->priv->no_display = FALSE;
|
|
|
|
+ app->priv->save_mask |= GSP_ASP_SAVE_MASK_NO_DISPLAY;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!app->priv->enabled) {
|
|
|
|
+ changed = TRUE;
|
|
|
|
+ app->priv->enabled = TRUE;
|
|
|
|
+ app->priv->save_mask |= GSP_ASP_SAVE_MASK_ENABLED;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (changed) {
|
|
|
|
+ _gsp_app_queue_save (app);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ manager = gsp_app_manager_get ();
|
|
|
|
+ gsp_app_manager_add (manager, app);
|
|
|
|
+ g_object_unref (app);
|
|
|
|
+ g_object_unref (manager);
|
|
|
|
+
|
|
|
|
+ return TRUE;
|
|
|
|
+}
|
|
|
|
diff --git a/capplet/gsp-app.h b/capplet/gsp-app.h
|
|
|
|
new file mode 100644
|
|
|
|
index 00000000..a1997955
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/capplet/gsp-app.h
|
|
|
|
@@ -0,0 +1,108 @@
|
|
|
|
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
|
|
|
+ *
|
|
|
|
+ * Copyright (C) 1999 Free Software Foundation, Inc.
|
|
|
|
+ * Copyright (C) 2007, 2009 Vincent Untz.
|
|
|
|
+ * Copyright (C) 2008 Lucas Rocha.
|
|
|
|
+ * Copyright (C) 2008 William Jon McCann <jmccann@redhat.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 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
|
|
|
|
+ * 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, write to the Free Software
|
|
|
|
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#ifndef __GSP_APP_H
|
|
|
|
+#define __GSP_APP_H
|
|
|
|
+
|
|
|
|
+#include <glib-object.h>
|
|
|
|
+#include <gio/gio.h>
|
|
|
|
+
|
|
|
|
+G_BEGIN_DECLS
|
|
|
|
+
|
|
|
|
+#define GSP_TYPE_APP (gsp_app_get_type ())
|
|
|
|
+#define GSP_APP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSP_TYPE_APP, GspApp))
|
|
|
|
+#define GSP_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSP_TYPE_APP, GspAppClass))
|
|
|
|
+#define GSP_IS_APP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSP_TYPE_APP))
|
|
|
|
+#define GSP_IS_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSP_TYPE_APP))
|
|
|
|
+#define GSP_APP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSP_TYPE_APP, GspAppClass))
|
|
|
|
+
|
|
|
|
+typedef struct _GspApp GspApp;
|
|
|
|
+typedef struct _GspAppClass GspAppClass;
|
|
|
|
+
|
|
|
|
+typedef struct _GspAppPrivate GspAppPrivate;
|
|
|
|
+
|
|
|
|
+struct _GspAppClass
|
|
|
|
+{
|
|
|
|
+ GObjectClass parent_class;
|
|
|
|
+
|
|
|
|
+ void (* changed) (GspApp *app);
|
|
|
|
+ void (* removed) (GspApp *app);
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+struct _GspApp
|
|
|
|
+{
|
|
|
|
+ GObject parent_instance;
|
|
|
|
+
|
|
|
|
+ GspAppPrivate *priv;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+GType gsp_app_get_type (void);
|
|
|
|
+
|
|
|
|
+void gsp_app_create (const char *name,
|
|
|
|
+ const char *comment,
|
|
|
|
+ const char *exec);
|
|
|
|
+void gsp_app_update (GspApp *app,
|
|
|
|
+ const char *name,
|
|
|
|
+ const char *comment,
|
|
|
|
+ const char *exec);
|
|
|
|
+
|
|
|
|
+gboolean gsp_app_copy_desktop_file (const char *uri);
|
|
|
|
+
|
|
|
|
+void gsp_app_delete (GspApp *app);
|
|
|
|
+
|
|
|
|
+const char *gsp_app_get_basename (GspApp *app);
|
|
|
|
+const char *gsp_app_get_path (GspApp *app);
|
|
|
|
+
|
|
|
|
+gboolean gsp_app_get_hidden (GspApp *app);
|
|
|
|
+gboolean gsp_app_get_display (GspApp *app);
|
|
|
|
+
|
|
|
|
+gboolean gsp_app_get_enabled (GspApp *app);
|
|
|
|
+void gsp_app_set_enabled (GspApp *app,
|
|
|
|
+ gboolean enabled);
|
|
|
|
+
|
|
|
|
+gboolean gsp_app_get_shown (GspApp *app);
|
|
|
|
+
|
|
|
|
+const char *gsp_app_get_name (GspApp *app);
|
|
|
|
+const char *gsp_app_get_exec (GspApp *app);
|
|
|
|
+const char *gsp_app_get_comment (GspApp *app);
|
|
|
|
+
|
|
|
|
+const char *gsp_app_get_description (GspApp *app);
|
|
|
|
+GIcon *gsp_app_get_icon (GspApp *app);
|
|
|
|
+
|
|
|
|
+/* private interface for GspAppManager only */
|
|
|
|
+
|
|
|
|
+GspApp *gsp_app_new (const char *path,
|
|
|
|
+ unsigned int xdg_position);
|
|
|
|
+
|
|
|
|
+void gsp_app_reload_at (GspApp *app,
|
|
|
|
+ const char *path,
|
|
|
|
+ unsigned int xdg_position);
|
|
|
|
+
|
|
|
|
+unsigned int gsp_app_get_xdg_position (GspApp *app);
|
|
|
|
+unsigned int gsp_app_get_xdg_system_position (GspApp *app);
|
|
|
|
+void gsp_app_set_xdg_system_position (GspApp *app,
|
|
|
|
+ unsigned int position);
|
|
|
|
+
|
|
|
|
+G_END_DECLS
|
|
|
|
+
|
|
|
|
+#endif /* __GSP_APP_H */
|
|
|
|
diff --git a/capplet/gsp-keyfile.c b/capplet/gsp-keyfile.c
|
|
|
|
new file mode 100644
|
|
|
|
index 00000000..de9fac0e
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/capplet/gsp-keyfile.c
|
|
|
|
@@ -0,0 +1,201 @@
|
|
|
|
+/*
|
|
|
|
+ * gsp-keyfile.c: GKeyFile extensions
|
|
|
|
+ *
|
|
|
|
+ * Copyright (C) 2008, 2009 Novell, Inc.
|
|
|
|
+ *
|
|
|
|
+ * Based on code from panel-keyfile.c (from gnome-panel)
|
|
|
|
+ *
|
|
|
|
+ * 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 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, write to the Free Software
|
|
|
|
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
|
|
+ * 02111-1307, USA.
|
|
|
|
+ *
|
|
|
|
+ * Authors:
|
|
|
|
+ * Vincent Untz <vuntz@gnome.org>
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#include <string.h>
|
|
|
|
+
|
|
|
|
+#include <glib.h>
|
|
|
|
+
|
|
|
|
+#include "gsp-keyfile.h"
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+gsp_key_file_populate (GKeyFile *keyfile)
|
|
|
|
+{
|
|
|
|
+ gsp_key_file_set_string (keyfile,
|
|
|
|
+ G_KEY_FILE_DESKTOP_KEY_TYPE,
|
|
|
|
+ "Application");
|
|
|
|
+
|
|
|
|
+ gsp_key_file_set_string (keyfile,
|
|
|
|
+ G_KEY_FILE_DESKTOP_KEY_EXEC,
|
|
|
|
+ "/bin/false");
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+//FIXME: kill this when bug #309224 is fixed
|
|
|
|
+gboolean
|
|
|
|
+gsp_key_file_to_file (GKeyFile *keyfile,
|
|
|
|
+ const gchar *path,
|
|
|
|
+ GError **error)
|
|
|
|
+{
|
|
|
|
+ GError *write_error;
|
|
|
|
+ gchar *data;
|
|
|
|
+ gsize length;
|
|
|
|
+ gboolean res;
|
|
|
|
+
|
|
|
|
+ g_return_val_if_fail (keyfile != NULL, FALSE);
|
|
|
|
+ g_return_val_if_fail (path != NULL, FALSE);
|
|
|
|
+
|
|
|
|
+ write_error = NULL;
|
|
|
|
+ data = g_key_file_to_data (keyfile, &length, &write_error);
|
|
|
|
+
|
|
|
|
+ if (write_error) {
|
|
|
|
+ g_propagate_error (error, write_error);
|
|
|
|
+ return FALSE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ res = g_file_set_contents (path, data, length, &write_error);
|
|
|
|
+ g_free (data);
|
|
|
|
+
|
|
|
|
+ if (write_error) {
|
|
|
|
+ g_propagate_error (error, write_error);
|
|
|
|
+ return FALSE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return res;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+gboolean
|
|
|
|
+gsp_key_file_get_boolean (GKeyFile *keyfile,
|
|
|
|
+ const gchar *key,
|
|
|
|
+ gboolean default_value)
|
|
|
|
+{
|
|
|
|
+ GError *error;
|
|
|
|
+ gboolean retval;
|
|
|
|
+
|
|
|
|
+ error = NULL;
|
|
|
|
+ retval = g_key_file_get_boolean (keyfile, G_KEY_FILE_DESKTOP_GROUP,
|
|
|
|
+ key, &error);
|
|
|
|
+ if (error != NULL) {
|
|
|
|
+ retval = default_value;
|
|
|
|
+ g_error_free (error);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return retval;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+gboolean
|
|
|
|
+gsp_key_file_get_shown (GKeyFile *keyfile,
|
|
|
|
+ const char *current_desktop)
|
|
|
|
+{
|
|
|
|
+ char **only_show_in, **not_show_in;
|
|
|
|
+ gboolean found;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ if (!current_desktop)
|
|
|
|
+ return TRUE;
|
|
|
|
+
|
|
|
|
+ only_show_in = g_key_file_get_string_list (keyfile, G_KEY_FILE_DESKTOP_GROUP,
|
|
|
|
+ G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN,
|
|
|
|
+ NULL, NULL);
|
|
|
|
+
|
|
|
|
+ if (only_show_in) {
|
|
|
|
+ found = FALSE;
|
|
|
|
+ for (i = 0; only_show_in[i] != NULL; i++) {
|
|
|
|
+ if (g_strcmp0 (current_desktop, only_show_in[i]) == 0) {
|
|
|
|
+ found = TRUE;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_strfreev (only_show_in);
|
|
|
|
+
|
|
|
|
+ if (!found)
|
|
|
|
+ return FALSE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ not_show_in = g_key_file_get_string_list (keyfile, G_KEY_FILE_DESKTOP_GROUP,
|
|
|
|
+ G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN,
|
|
|
|
+ NULL, NULL);
|
|
|
|
+
|
|
|
|
+ if (not_show_in) {
|
|
|
|
+ found = FALSE;
|
|
|
|
+ for (i = 0; not_show_in[i] != NULL; i++) {
|
|
|
|
+ if (g_strcmp0 (current_desktop, not_show_in[i]) == 0) {
|
|
|
|
+ found = TRUE;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_strfreev (not_show_in);
|
|
|
|
+
|
|
|
|
+ if (found)
|
|
|
|
+ return FALSE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return TRUE;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+gsp_key_file_set_locale_string (GKeyFile *keyfile,
|
|
|
|
+ const gchar *key,
|
|
|
|
+ const gchar *value)
|
|
|
|
+{
|
|
|
|
+ const char *locale;
|
|
|
|
+ const char * const *langs_pointer;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ if (value == NULL) {
|
|
|
|
+ value = "";
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ locale = NULL;
|
|
|
|
+ langs_pointer = g_get_language_names ();
|
|
|
|
+ for (i = 0; langs_pointer[i] != NULL; i++) {
|
|
|
|
+ /* find first without encoding */
|
|
|
|
+ if (strchr (langs_pointer[i], '.') == NULL) {
|
|
|
|
+ locale = langs_pointer[i];
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (locale != NULL) {
|
|
|
|
+ g_key_file_set_locale_string (keyfile, G_KEY_FILE_DESKTOP_GROUP,
|
|
|
|
+ key, locale, value);
|
|
|
|
+ } else {
|
|
|
|
+ g_key_file_set_string (keyfile, G_KEY_FILE_DESKTOP_GROUP,
|
|
|
|
+ key, value);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+gsp_key_file_ensure_C_key (GKeyFile *keyfile,
|
|
|
|
+ const char *key)
|
|
|
|
+{
|
|
|
|
+ char *C_value;
|
|
|
|
+ char *buffer;
|
|
|
|
+
|
|
|
|
+ /* Make sure we set the "C" locale strings to the terms we set here.
|
|
|
|
+ * This is so that if the user logs into another locale they get their
|
|
|
|
+ * own description there rather then empty. It is not the C locale
|
|
|
|
+ * however, but the user created this entry herself so it's OK */
|
|
|
|
+ C_value = gsp_key_file_get_string (keyfile, key);
|
|
|
|
+ if (C_value == NULL || C_value [0] == '\0') {
|
|
|
|
+ buffer = gsp_key_file_get_locale_string (keyfile, key);
|
|
|
|
+ if (buffer) {
|
|
|
|
+ gsp_key_file_set_string (keyfile, key, buffer);
|
|
|
|
+ g_free (buffer);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ g_free (C_value);
|
|
|
|
+}
|
|
|
|
diff --git a/capplet/gsp-keyfile.h b/capplet/gsp-keyfile.h
|
|
|
|
new file mode 100644
|
|
|
|
index 00000000..d94f6672
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/capplet/gsp-keyfile.h
|
|
|
|
@@ -0,0 +1,65 @@
|
|
|
|
+/*
|
|
|
|
+ * gsp-keyfile.h: GKeyFile extensions
|
|
|
|
+ *
|
|
|
|
+ * Copyright (C) 2008, 2009 Novell, Inc.
|
|
|
|
+ *
|
|
|
|
+ * Based on code from panel-keyfile.h (from gnome-panel)
|
|
|
|
+ *
|
|
|
|
+ * 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 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, write to the Free Software
|
|
|
|
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
|
|
+ * 02111-1307, USA.
|
|
|
|
+ *
|
|
|
|
+ * Authors:
|
|
|
|
+ * Vincent Untz <vuntz@gnome.org>
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#ifndef GSP_KEYFILE_H
|
|
|
|
+#define GSP_KEYFILE_H
|
|
|
|
+
|
|
|
|
+#include "glib.h"
|
|
|
|
+
|
|
|
|
+G_BEGIN_DECLS
|
|
|
|
+
|
|
|
|
+#define GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED "X-GNOME-Autostart-enabled"
|
|
|
|
+
|
|
|
|
+void gsp_key_file_populate (GKeyFile *keyfile);
|
|
|
|
+
|
|
|
|
+gboolean gsp_key_file_to_file (GKeyFile *keyfile,
|
|
|
|
+ const gchar *path,
|
|
|
|
+ GError **error);
|
|
|
|
+
|
|
|
|
+gboolean gsp_key_file_get_boolean (GKeyFile *keyfile,
|
|
|
|
+ const gchar *key,
|
|
|
|
+ gboolean default_value);
|
|
|
|
+gboolean gsp_key_file_get_shown (GKeyFile *keyfile,
|
|
|
|
+ const char *current_desktop);
|
|
|
|
+#define gsp_key_file_get_string(key_file, key) \
|
|
|
|
+ g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, key, NULL)
|
|
|
|
+#define gsp_key_file_get_locale_string(key_file, key) \
|
|
|
|
+ g_key_file_get_locale_string(key_file, G_KEY_FILE_DESKTOP_GROUP, key, NULL, NULL)
|
|
|
|
+
|
|
|
|
+#define gsp_key_file_set_boolean(key_file, key, value) \
|
|
|
|
+ g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, key, value)
|
|
|
|
+#define gsp_key_file_set_string(key_file, key, value) \
|
|
|
|
+ g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP, key, value)
|
|
|
|
+void gsp_key_file_set_locale_string (GKeyFile *keyfile,
|
|
|
|
+ const gchar *key,
|
|
|
|
+ const gchar *value);
|
|
|
|
+
|
|
|
|
+void gsp_key_file_ensure_C_key (GKeyFile *keyfile,
|
|
|
|
+ const char *key);
|
|
|
|
+
|
|
|
|
+G_END_DECLS
|
|
|
|
+
|
|
|
|
+#endif /* GSP_KEYFILE_H */
|
|
|
|
diff --git a/capplet/main.c b/capplet/main.c
|
|
|
|
new file mode 100644
|
|
|
|
index 00000000..3c7177bc
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/capplet/main.c
|
|
|
|
@@ -0,0 +1,108 @@
|
|
|
|
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
|
|
|
+ * main.c
|
|
|
|
+ * Copyright (C) 1999 Free Software Foundation, Inc.
|
|
|
|
+ * Copyright (C) 2008 Lucas Rocha.
|
|
|
|
+ *
|
|
|
|
+ * 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 GNU
|
|
|
|
+ * Lesser General Public License for more details.
|
|
|
|
+ *
|
|
|
|
+ * You should have received a copy of the GNU General Public License
|
|
|
|
+ * along with this program; if not, write to the Free Software
|
|
|
|
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
|
|
+ * 02111-1307, USA.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#include <config.h>
|
|
|
|
+
|
|
|
|
+#include <unistd.h>
|
|
|
|
+#include <stdlib.h>
|
|
|
|
+
|
|
|
|
+#include <glib/gi18n.h>
|
|
|
|
+#include <gtk/gtk.h>
|
|
|
|
+
|
|
|
|
+#include "gsm-properties-dialog.h"
|
|
|
|
+
|
|
|
|
+static gboolean show_version = FALSE;
|
|
|
|
+
|
|
|
|
+static GOptionEntry options[] = {
|
|
|
|
+ { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, N_("Version of this application"), NULL },
|
|
|
|
+ { NULL, 0, 0, 0, NULL, NULL, NULL }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+dialog_response (GsmPropertiesDialog *dialog,
|
|
|
|
+ guint response_id,
|
|
|
|
+ gpointer data)
|
|
|
|
+{
|
|
|
|
+ GdkScreen *screen;
|
|
|
|
+ GError *error;
|
|
|
|
+
|
|
|
|
+ if (response_id == GTK_RESPONSE_HELP) {
|
|
|
|
+ screen = gtk_widget_get_screen (GTK_WIDGET (dialog));
|
|
|
|
+
|
|
|
|
+ error = NULL;
|
|
|
|
+ gtk_show_uri (screen, "ghelp:user-guide?gosstartsession-2",
|
|
|
|
+ gtk_get_current_event_time (), &error);
|
|
|
|
+
|
|
|
|
+ if (error != NULL) {
|
|
|
|
+ GtkWidget *d;
|
|
|
|
+ d = gtk_message_dialog_new (GTK_WINDOW (dialog),
|
|
|
|
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
|
|
|
|
+ GTK_MESSAGE_ERROR,
|
|
|
|
+ GTK_BUTTONS_CLOSE,
|
|
|
|
+ "%s",
|
|
|
|
+ _("Could not display help document"));
|
|
|
|
+ gtk_message_dialog_format_secondary_text (
|
|
|
|
+ GTK_MESSAGE_DIALOG (d),
|
|
|
|
+ "%s", error->message);
|
|
|
|
+ g_error_free (error);
|
|
|
|
+
|
|
|
|
+ gtk_dialog_run (GTK_DIALOG (d));
|
|
|
|
+ gtk_widget_destroy (d);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ gtk_widget_destroy (GTK_WIDGET (dialog));
|
|
|
|
+ gtk_main_quit ();
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int
|
|
|
|
+main (int argc, char *argv[])
|
|
|
|
+{
|
|
|
|
+ GError *error;
|
|
|
|
+ GtkWidget *dialog;
|
|
|
|
+
|
|
|
|
+ bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR);
|
|
|
|
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
|
|
|
|
+ textdomain (GETTEXT_PACKAGE);
|
|
|
|
+
|
|
|
|
+ error = NULL;
|
|
|
|
+ if (! gtk_init_with_args (&argc, &argv, " - GNOME Session Properties", options, GETTEXT_PACKAGE, &error)) {
|
|
|
|
+ g_warning ("Unable to start: %s", error->message);
|
|
|
|
+ g_error_free (error);
|
|
|
|
+ return 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (show_version) {
|
|
|
|
+ g_print ("%s %s\n", argv [0], VERSION);
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ dialog = gsm_properties_dialog_new ();
|
|
|
|
+ g_signal_connect (dialog,
|
|
|
|
+ "response",
|
|
|
|
+ G_CALLBACK (dialog_response),
|
|
|
|
+ NULL);
|
|
|
|
+ gtk_widget_show (dialog);
|
|
|
|
+
|
|
|
|
+ gtk_main ();
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
diff --git a/capplet/meson.build b/capplet/meson.build
|
|
|
|
new file mode 100644
|
|
|
|
index 00000000..8dad9c80
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/capplet/meson.build
|
|
|
|
@@ -0,0 +1,36 @@
|
|
|
|
+install_data(
|
|
|
|
+ install_dir: session_bindir
|
|
|
|
+)
|
|
|
|
+
|
|
|
|
+deps = session_deps + [
|
|
|
|
+ glib_dep,
|
|
|
|
+ gtk_dep,
|
|
|
|
+ x11_dep,
|
|
|
|
+ sm_dep,
|
|
|
|
+ ice_dep
|
|
|
|
+]
|
|
|
|
+
|
|
|
|
+cflags = [
|
|
|
|
+ '-DLOCALE_DIR="@0@"'.format(session_localedir),
|
|
|
|
+ '-DGTKBUILDER_DIR="@0@"'.format(session_pkgdatadir)
|
|
|
|
+]
|
|
|
|
+
|
|
|
|
+sources = files(
|
|
|
|
+ '../gnome-session/gsm-util.c',
|
|
|
|
+ 'gsm-app-dialog.c',
|
|
|
|
+ 'gsm-properties-dialog.c',
|
|
|
|
+ 'gsp-app.c',
|
|
|
|
+ 'gsp-app-manager.c',
|
|
|
|
+ 'gsp-keyfile.c',
|
|
|
|
+ 'main.c'
|
|
|
|
+)
|
|
|
|
+
|
|
|
|
+executable(
|
|
|
|
+ 'gnome-session-properties',
|
|
|
|
+ sources,
|
|
|
|
+ include_directories: [ top_inc, include_directories('../gnome-session') ],
|
|
|
|
+ dependencies: deps,
|
|
|
|
+ c_args: cflags,
|
|
|
|
+ install: true,
|
|
|
|
+ install_dir: session_bindir
|
|
|
|
+)
|
|
|
|
diff --git a/data/gnome-session-properties.desktop.in.in b/data/gnome-session-properties.desktop.in.in
|
|
|
|
new file mode 100644
|
|
|
|
index 00000000..3dc7b033
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/data/gnome-session-properties.desktop.in.in
|
|
|
|
@@ -0,0 +1,15 @@
|
|
|
|
+[Desktop Entry]
|
|
|
|
+_Name=Startup Applications
|
|
|
|
+_Comment=Choose what applications to start when you log in
|
|
|
|
+Exec=gnome-session-properties
|
|
|
|
+Icon=session-properties
|
|
|
|
+Terminal=false
|
|
|
|
+Type=Application
|
|
|
|
+StartupNotify=true
|
|
|
|
+Categories=GTK;GNOME;Settings;X-GNOME-PersonalSettings;
|
|
|
|
+OnlyShowIn=GNOME;Unity;
|
|
|
|
+NoDisplay=true
|
|
|
|
+X-GNOME-Bugzilla-Bugzilla=GNOME
|
|
|
|
+X-GNOME-Bugzilla-Product=gnome-session
|
|
|
|
+X-GNOME-Bugzilla-Component=gnome-session-properties
|
|
|
|
+X-GNOME-Bugzilla-Version=@VERSION@
|
|
|
|
diff --git a/data/meson.build b/data/meson.build
|
|
|
|
index b79a5fbd..c5b2b0f7 100644
|
|
|
|
--- a/data/meson.build
|
|
|
|
+++ b/data/meson.build
|
|
|
|
@@ -72,30 +72,43 @@ configure_file(
|
|
|
|
)
|
|
|
|
|
|
|
|
install_data(
|
|
|
|
'gnome-session.convert',
|
|
|
|
install_dir: join_paths(session_datadir, 'GConf', 'gsettings')
|
|
|
|
)
|
|
|
|
|
|
|
|
data = files('hardware-compatibility')
|
|
|
|
data += files('session-properties.ui')
|
|
|
|
|
|
|
|
if enable_session_selector
|
|
|
|
data += files('session-selector.ui')
|
|
|
|
|
|
|
|
desktop = 'gnome-custom-session.desktop'
|
|
|
|
|
|
|
|
i18n.merge_file(
|
|
|
|
desktop,
|
|
|
|
type: 'desktop',
|
|
|
|
input: desktop + '.in',
|
|
|
|
output: desktop,
|
|
|
|
po_dir: po_dir,
|
|
|
|
install: true,
|
|
|
|
install_dir: join_paths(session_datadir, 'xsessions')
|
|
|
|
)
|
|
|
|
endif
|
|
|
|
|
|
|
|
install_data(
|
|
|
|
data,
|
|
|
|
install_dir: session_pkgdatadir
|
|
|
|
)
|
|
|
|
+
|
|
|
|
+desktop = 'gnome-session-properties.desktop'
|
|
|
|
+
|
|
|
|
+i18n.merge_file(
|
|
|
|
+ desktop,
|
|
|
|
+ type: 'desktop',
|
|
|
|
+ input: desktop + '.in',
|
|
|
|
+ output: desktop,
|
|
|
|
+ po_dir: po_dir,
|
|
|
|
+ install: true,
|
|
|
|
+ install_dir: join_paths(session_datadir, 'applications')
|
|
|
|
+)
|
|
|
|
+
|
|
|
|
diff --git a/doc/man/gnome-session-properties.1 b/doc/man/gnome-session-properties.1
|
|
|
|
new file mode 100644
|
|
|
|
index 00000000..c7ef1af3
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/doc/man/gnome-session-properties.1
|
|
|
|
@@ -0,0 +1,24 @@
|
|
|
|
+.\"
|
|
|
|
+.\" gnome-session-properties manual page.
|
|
|
|
+.\" (C) 2009-2010 Vincent Untz (vuntz@gnome.org)
|
|
|
|
+.\"
|
|
|
|
+.TH GNOME-SESSION-PROPERTIES 1 "GNOME"
|
|
|
|
+.SH NAME
|
|
|
|
+gnome-session-properties \- Configure applications to start on login
|
|
|
|
+.SH SYNOPSIS
|
|
|
|
+.B gnome-session-properties
|
|
|
|
+.SH DESCRIPTION
|
|
|
|
+.PP
|
|
|
|
+The \fIgnome-session-properties\fP program enables the users to
|
|
|
|
+configure what applications should be started on login, in addition to
|
|
|
|
+the default startup applications configured on the system.
|
|
|
|
+.PP
|
|
|
|
+It also proposes an interface to save a snapshot of the currently
|
|
|
|
+running applications so that they can automatically be restored to
|
|
|
|
+their current state on your next GNOME session.
|
|
|
|
+.SH BUGS
|
|
|
|
+If you find bugs in the \fIgnome-session-properties\fP program, please report
|
|
|
|
+these on https://bugzilla.gnome.org.
|
|
|
|
+.SH SEE ALSO
|
|
|
|
+.BR gnome-session(1)
|
|
|
|
+.BR gnome-session-quit(1)
|
|
|
|
diff --git a/meson.build b/meson.build
|
|
|
|
index 0231360b..9a16d5b1 100644
|
|
|
|
--- a/meson.build
|
|
|
|
+++ b/meson.build
|
|
|
|
@@ -152,60 +152,61 @@ if enable_systemd or enable_consolekit
|
|
|
|
dbus_glib_dep = dependency('dbus-glib-1', version: '>= 0.76')
|
|
|
|
assert(dbus_glib_dep.found(), 'ConsoleKit support explicitly required, but dbus-glib not found')
|
|
|
|
|
|
|
|
session_bin_deps += dbus_glib_dep
|
|
|
|
|
|
|
|
if enable_systemd
|
|
|
|
session_tracking += ' (with fallback to ConsoleKit)'
|
|
|
|
else
|
|
|
|
session_tracking = 'ConsoleKit'
|
|
|
|
endif
|
|
|
|
endif
|
|
|
|
endif
|
|
|
|
config_h.set('HAVE_SYSTEMD', enable_systemd)
|
|
|
|
config_h.set('ENABLE_SYSTEMD_JOURNAL', enable_systemd_journal)
|
|
|
|
config_h.set('HAVE_CONSOLEKIT', enable_consolekit)
|
|
|
|
|
|
|
|
configure_file(
|
|
|
|
output: 'config.h',
|
|
|
|
configuration: config_h
|
|
|
|
)
|
|
|
|
|
|
|
|
gnome = import('gnome')
|
|
|
|
i18n = import('i18n')
|
|
|
|
pkg = import('pkgconfig')
|
|
|
|
|
|
|
|
po_dir = join_paths(meson.source_root(), 'po')
|
|
|
|
|
|
|
|
top_inc = include_directories('.')
|
|
|
|
|
|
|
|
subdir('gnome-session')
|
|
|
|
+subdir('capplet')
|
|
|
|
subdir('tools')
|
|
|
|
subdir('data')
|
|
|
|
|
|
|
|
enable_docbook = get_option('docbook')
|
|
|
|
enable_man = get_option('man')
|
|
|
|
if enable_docbook or enable_man
|
|
|
|
subdir('doc')
|
|
|
|
endif
|
|
|
|
|
|
|
|
subdir('po')
|
|
|
|
|
|
|
|
meson.add_install_script(
|
|
|
|
'meson_post_install.py',
|
|
|
|
session_datadir
|
|
|
|
)
|
|
|
|
|
|
|
|
output = '\n gnome-session ' + session_version + '\n'
|
|
|
|
output += ' ====================\n\n'
|
|
|
|
output += ' prefix: ' + session_prefix + '\n'
|
|
|
|
output += ' exec_prefix: ' + session_libexecdir + '\n'
|
|
|
|
output += ' libdir: ' + session_libdir + '\n'
|
|
|
|
output += ' bindir: ' + session_bindir + '\n'
|
|
|
|
output += ' sbindir: ' + session_sbindir + '\n'
|
|
|
|
output += ' sysconfdir: ' + session_sysconfdir + '\n'
|
|
|
|
output += ' localstatedir: ' + session_localstatedir + '\n'
|
|
|
|
output += ' datadir: ' + session_datadir + '\n'
|
|
|
|
output += ' source code location: ' + meson.source_root() + '\n'
|
|
|
|
output += ' compiler: ' + cc.get_id() + '\n'
|
|
|
|
output += ' cflags: ' + ' '.join(compiler_flags) + '\n'
|
|
|
|
output += ' Debug mode: ' + session_debug.to_string() + '\n'
|
|
|
|
diff --git a/po/POTFILES.in b/po/POTFILES.in
|
|
|
|
index f32040fc..87555685 100644
|
|
|
|
--- a/po/POTFILES.in
|
|
|
|
+++ b/po/POTFILES.in
|
|
|
|
@@ -1,19 +1,24 @@
|
|
|
|
# List of source files containing translatable strings.
|
|
|
|
# Please keep this file sorted alphabetically.
|
|
|
|
+capplet/gsm-app-dialog.c
|
|
|
|
+capplet/gsm-properties-dialog.c
|
|
|
|
+capplet/gsp-app.c
|
|
|
|
+capplet/main.c
|
|
|
|
data/gnome-custom-session.desktop.in
|
|
|
|
data/gnome.desktop.in
|
|
|
|
data/gnome-xorg.desktop.in
|
|
|
|
data/gnome-dummy.session.desktop.in.in
|
|
|
|
data/gnome.session.desktop.in.in
|
|
|
|
+data/gnome-session-properties.desktop.in.in
|
|
|
|
data/session-selector.ui
|
|
|
|
data/session-properties.ui
|
|
|
|
gnome-session/gsm-fail-whale-dialog.c
|
|
|
|
gnome-session/gsm-manager.c
|
|
|
|
gnome-session/gsm-process-helper.c
|
|
|
|
gnome-session/gsm-util.c
|
|
|
|
gnome-session/gsm-xsmp-client.c
|
|
|
|
gnome-session/gsm-xsmp-server.c
|
|
|
|
gnome-session/main.c
|
|
|
|
tools/gnome-session-inhibit.c
|
|
|
|
tools/gnome-session-selector.c
|
|
|
|
tools/gnome-session-quit.c
|
|
|
|
--
|
|
|
|
2.17.0
|
|
|
|
|