[gnome-session] [capplet] Big rework of the code to clean it up, fix bugs, etc.
- From: Vincent Untz <vuntz src gnome org>
- To: svn-commits-list gnome org
- Subject: [gnome-session] [capplet] Big rework of the code to clean it up, fix bugs, etc.
- Date: Sun, 21 Jun 2009 11:16:45 -0400 (EDT)
commit fe9f11b9bfefb3f12b048118a003b1c5ca6a9d38
Author: Vincent Untz <vuntz gnome org>
Date: Sun Jun 21 17:09:21 2009 +0200
[capplet] Big rework of the code to clean it up, fix bugs, etc.
The core of this rework is to separate in a clean way the code that
tracks the autostart files from the UI itself.
Now it'd be possible to make an autostart file inherit the data from
the autostart files with the same basename but in other directories.
This work also fixes a few bugs:
+ monitor autostart files
http://bugzilla.gnome.org/show_bug.cgi?id=437204
+ make name field optional and autofill it instead
http://bugzilla.gnome.org/show_bug.cgi?id=502393
capplet/Makefile.am | 6 +
capplet/gsm-app-dialog.c | 96 +++-
capplet/gsm-app-dialog.h | 5 +
capplet/gsm-properties-dialog.c | 1203 +++++++--------------------------------
capplet/gsm-properties-dialog.h | 2 +
capplet/gsp-app-manager.c | 567 ++++++++++++++++++
capplet/gsp-app-manager.h | 81 +++
capplet/gsp-app.c | 1019 +++++++++++++++++++++++++++++++++
capplet/gsp-app.h | 96 ++++
capplet/gsp-keyfile.c | 149 +++++
capplet/gsp-keyfile.h | 63 ++
11 files changed, 2281 insertions(+), 1006 deletions(-)
---
diff --git a/capplet/Makefile.am b/capplet/Makefile.am
index a45d0dc..cf1af9c 100644
--- a/capplet/Makefile.am
+++ b/capplet/Makefile.am
@@ -26,6 +26,12 @@ gnome_session_properties_SOURCES = \
gsm-properties-dialog.c \
gsm-app-dialog.h \
gsm-app-dialog.c \
+ gsp-app.h \
+ gsp-app.c \
+ gsp-app-manager.h \
+ gsp-app-manager.c \
+ gsp-keyfile.h \
+ gsp-keyfile.c \
$(NULL)
-include $(top_srcdir)/git.mk
diff --git a/capplet/gsm-app-dialog.c b/capplet/gsm-app-dialog.c
index 64ab063..da5f709 100644
--- a/capplet/gsm-app-dialog.c
+++ b/capplet/gsm-app-dialog.c
@@ -20,17 +20,13 @@
#include "config.h"
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-
#include <glib.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include <glade/glade-xml.h>
-#include <gconf/gconf-client.h>
+
+#include "gsm-util.h"
#include "gsm-app-dialog.h"
@@ -447,3 +443,91 @@ gsm_app_dialog_new (const char *name,
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;
+ gboolean changed;
+
+ 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;
+ }
+
+ changed = FALSE;
+
+ 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
index 0093332..ced0628 100644
--- a/capplet/gsm-app-dialog.h
+++ b/capplet/gsm-app-dialog.h
@@ -52,6 +52,11 @@ 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);
diff --git a/capplet/gsm-properties-dialog.c b/capplet/gsm-properties-dialog.c
index c54eb60..c2606c0 100644
--- a/capplet/gsm-properties-dialog.c
+++ b/capplet/gsm-properties-dialog.c
@@ -23,15 +23,8 @@
#include "config.h"
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-
#include <glib.h>
#include <glib/gi18n.h>
-#include <glib/gstdio.h>
-#include <gio/gio.h>
#include <gtk/gtk.h>
#include <glade/glade-xml.h>
@@ -41,6 +34,8 @@
#include "gsm-app-dialog.h"
#include "eggdesktopfile.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))
@@ -70,18 +65,26 @@ struct GsmPropertiesDialogPrivate
{
GladeXML *xml;
GtkListStore *list_store;
+ GtkTreeModel *tree_filter;
+
+ GtkTreeView *treeview;
+ GtkWidget *add_button;
+ GtkWidget *delete_button;
+ GtkWidget *edit_button;
+
+ GtkWidget *remember_toggle;
+
+ GspAppManager *manager;
};
enum {
- STORE_COL_ENABLED = 0,
+ STORE_COL_VISIBLE = 0,
+ STORE_COL_ENABLED,
STORE_COL_ICON_NAME,
+ STORE_COL_GICON,
+ STORE_COL_PIXBUF,
STORE_COL_DESCRIPTION,
- STORE_COL_NAME,
- STORE_COL_COMMAND,
- STORE_COL_COMMENT,
- STORE_COL_DESKTOP_FILE,
- STORE_COL_ID,
- STORE_COL_ACTIVATABLE,
+ STORE_COL_APP,
NUMBER_OF_COLUMNS
};
@@ -91,594 +94,157 @@ static void gsm_properties_dialog_finalize (GObject *obj
G_DEFINE_TYPE (GsmPropertiesDialog, gsm_properties_dialog, GTK_TYPE_DIALOG)
-static void
-on_response (GsmPropertiesDialog *dialog,
- gint response_id)
-
-{
-}
-
static gboolean
-find_by_id (GtkListStore *store,
- const char *id)
+find_by_app (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GspApp *app)
{
- GtkTreeIter iter;
- char *iter_id = NULL;
+ GspApp *iter_app = NULL;
- if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) {
+ if (!gtk_tree_model_get_iter_first (model, iter)) {
return FALSE;
}
do {
- gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
- STORE_COL_ID, &iter_id,
+ gtk_tree_model_get (model, iter,
+ STORE_COL_APP, &iter_app,
-1);
- if (!strcmp (iter_id, id)) {
+ if (iter_app == app) {
+ g_object_unref (iter_app);
return TRUE;
}
- } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
+ } while (gtk_tree_model_iter_next (model, iter));
return FALSE;
}
-static char *
-get_app_description (const char *name,
- const char *comment)
-{
- return g_markup_printf_escaped ("<b>%s</b>\n%s", name,
- (!gsm_util_text_is_blank (comment) ?
- comment : _("No description")));
-}
-
-static gboolean
-append_app (GsmPropertiesDialog *dialog,
- EggDesktopFile *desktop_file)
-{
- GtkIconTheme *theme;
- GtkTreeIter iter;
- GFile *source;
- char *basename;
- char *description;
- char *name;
- char *comment;
- char *command;
- char *icon_name;
- gboolean enabled = TRUE;
-
- source = g_file_new_for_uri (egg_desktop_file_get_source (desktop_file));
-
- basename = g_file_get_basename (source);
-
- if (egg_desktop_file_has_key (desktop_file,
- G_KEY_FILE_DESKTOP_KEY_HIDDEN, NULL)) {
- if (egg_desktop_file_get_boolean (desktop_file,
- G_KEY_FILE_DESKTOP_KEY_HIDDEN,
- NULL))
- return FALSE;
- }
-
- /* Check for duplicate apps */
- if (find_by_id (dialog->priv->list_store, basename)) {
- return TRUE;
- }
-
- name = egg_desktop_file_get_locale_string (desktop_file,
- G_KEY_FILE_DESKTOP_KEY_NAME,
- NULL, NULL);
-
- comment = NULL;
-
- if (egg_desktop_file_has_key (desktop_file,
- "Comment", NULL)) {
- comment =
- egg_desktop_file_get_locale_string (desktop_file,
- G_KEY_FILE_DESKTOP_KEY_COMMENT,
- NULL, NULL);
- }
-
- description = get_app_description (name, comment);
-
- command = egg_desktop_file_get_string (desktop_file,
- G_KEY_FILE_DESKTOP_KEY_EXEC,
- NULL);
-
- icon_name = NULL;
-
- if (egg_desktop_file_has_key (desktop_file,
- G_KEY_FILE_DESKTOP_KEY_ICON, NULL)) {
- icon_name =
- egg_desktop_file_get_string (desktop_file,
- G_KEY_FILE_DESKTOP_KEY_ICON,
- NULL);
- }
-
- theme = gtk_icon_theme_get_default ();
-
- if (icon_name == NULL || *icon_name == '\0' ||
- !gtk_icon_theme_has_icon (theme, icon_name)) {
- if (icon_name) {
- g_free (icon_name);
+static void
+_fill_iter_from_app (GtkListStore *list_store,
+ GtkTreeIter *iter,
+ GspApp *app)
+{
+ gboolean hidden;
+ gboolean enabled;
+ GIcon *icon;
+ const char *icon_name;
+ GdkPixbuf *pixbuf;
+ const char *description;
+
+ hidden = gsp_app_get_hidden (app);
+ enabled = gsp_app_get_enabled (app);
+ icon_name = gsp_app_get_icon_name (app);
+ pixbuf = gsp_app_get_pixbuf (app);
+ description = gsp_app_get_description (app);
+
+ icon = NULL;
+ if (!pixbuf) {
+#if 1
+ GtkIconTheme *theme;
+ theme = gtk_icon_theme_get_default ();
+ if (icon_name == NULL ||
+ !gtk_icon_theme_has_icon (theme, icon_name)) {
+ icon_name = STARTUP_APP_ICON;
}
-
- icon_name = g_strdup (STARTUP_APP_ICON);
- }
-
- if (egg_desktop_file_has_key (desktop_file,
- "X-GNOME-Autostart-enabled", NULL)) {
- enabled = egg_desktop_file_get_boolean (desktop_file,
- "X-GNOME-Autostart-enabled",
- NULL);
+#else
+ /* the issue with this approach is that icons that live in
+ * hicolor are ignored and STARTUP_APP_ICON is nearly always
+ * used if it's in the main theme */
+ icon = g_themed_icon_new (STARTUP_APP_ICON);
+ if (icon_name != NULL) {
+ g_themed_icon_prepend_name (G_THEMED_ICON (icon),
+ icon_name);
+ }
+#endif
}
- gtk_list_store_append (dialog->priv->list_store, &iter);
-
- gtk_list_store_set (dialog->priv->list_store,
- &iter,
+ gtk_list_store_set (list_store, iter,
+ STORE_COL_VISIBLE, !hidden,
STORE_COL_ENABLED, enabled,
STORE_COL_ICON_NAME, icon_name,
+ STORE_COL_GICON, icon,
+ STORE_COL_PIXBUF, pixbuf,
STORE_COL_DESCRIPTION, description,
- STORE_COL_NAME, name,
- STORE_COL_COMMAND, command,
- STORE_COL_COMMENT, comment,
- STORE_COL_DESKTOP_FILE, desktop_file,
- STORE_COL_ID, basename,
- STORE_COL_ACTIVATABLE, TRUE,
+ STORE_COL_APP, app,
-1);
-
- g_object_unref (source);
- g_free (basename);
- g_free (name);
- g_free (comment);
- g_free (description);
- g_free (icon_name);
-
- return TRUE;
-}
-
-static int
-compare_app (gconstpointer a,
- gconstpointer b)
-{
- if (!strcmp (a, b)) {
- return 0;
- }
-
- return 1;
}
static void
-append_autostart_apps (GsmPropertiesDialog *dialog,
- const char *path,
- GList **removed_apps)
+_app_changed (GsmPropertiesDialog *dialog,
+ GspApp *app)
{
- GDir *dir;
- const char *name;
+ GtkTreeIter iter;
- dir = g_dir_open (path, 0, NULL);
- if (!dir) {
+ if (!find_by_app (GTK_TREE_MODEL (dialog->priv->list_store),
+ &iter, app)) {
return;
}
- while ((name = g_dir_read_name (dir))) {
- EggDesktopFile *desktop_file;
- GError *error;
- char *desktop_file_path;
-
- if (!g_str_has_suffix (name, ".desktop")) {
- continue;
- }
-
- if (removed_apps &&
- g_list_find_custom (*removed_apps, name, compare_app)) {
- continue;
- }
-
- desktop_file_path = g_build_filename (path, name, NULL);
-
- error = NULL;
- desktop_file = egg_desktop_file_new (desktop_file_path, &error);
- if (error == NULL) {
- if (!append_app (dialog, desktop_file)) {
- if (removed_apps) {
- *removed_apps = g_list_prepend (*removed_apps, g_strdup (name));
- }
- }
- } else {
- g_warning ("could not read %s: %s\n", desktop_file_path, error->message);
-
- g_error_free (error);
- error = NULL;
- }
-
- g_free (desktop_file_path);
- }
-
- g_dir_close (dir);
-}
-
-static void
-populate_model (GsmPropertiesDialog *dialog)
-{
- GList *removed_apps;
- char **autostart_dirs;
- int i;
-
- autostart_dirs = gsm_util_get_autostart_dirs ();
-
- removed_apps = NULL;
- for (i = 0; autostart_dirs[i]; i++) {
- append_autostart_apps (dialog, autostart_dirs[i], &removed_apps);
- }
-
- g_strfreev (autostart_dirs);
- g_list_foreach (removed_apps, (GFunc) g_free, NULL);
- g_list_free (removed_apps);
-}
-
-static void
-on_selection_changed (GtkTreeSelection *selection,
- GsmPropertiesDialog *dialog)
-{
- GtkWidget *edit_button;
- GtkWidget *delete_button;
- gboolean sel;
-
- edit_button = glade_xml_get_widget (dialog->priv->xml, CAPPLET_EDIT_WIDGET_NAME);
- delete_button = glade_xml_get_widget (dialog->priv->xml, CAPPLET_DELETE_WIDGET_NAME);
-
- sel = gtk_tree_selection_get_selected (selection, NULL, NULL);
-
- if (edit_button) {
- gtk_widget_set_sensitive (edit_button, sel);
- }
-
- if (delete_button) {
- gtk_widget_set_sensitive (delete_button, sel);
- }
-}
-
-
-static gboolean
-system_desktop_entry_exists (const char *basename,
- char **system_path)
-{
- char *path;
- char **autostart_dirs;
- int i;
-
- autostart_dirs = gsm_util_get_autostart_dirs ();
-
- for (i = 0; autostart_dirs[i]; i++) {
- path = g_build_filename (autostart_dirs[i], basename, NULL);
-
- if (g_str_has_prefix (autostart_dirs[i], g_get_user_config_dir ())) {
- continue;
- }
-
- if (g_file_test (path, G_FILE_TEST_EXISTS)) {
- if (system_path) {
- *system_path = path;
- } else {
- g_free (path);
- }
- g_strfreev (autostart_dirs);
-
- return TRUE;
- }
-
- g_free (path);
- }
-
- g_strfreev (autostart_dirs);
-
- return FALSE;
+ _fill_iter_from_app (dialog->priv->list_store, &iter, app);
}
static void
-update_desktop_file (GtkListStore *store,
- GtkTreeIter *iter,
- EggDesktopFile *new_desktop_file)
+append_app (GsmPropertiesDialog *dialog,
+ GspApp *app)
{
- EggDesktopFile *old_desktop_file;
-
- gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
- STORE_COL_DESKTOP_FILE, &old_desktop_file,
- -1);
+ GtkTreeIter iter;
- egg_desktop_file_free (old_desktop_file);
+ gtk_list_store_append (dialog->priv->list_store, &iter);
+ _fill_iter_from_app (dialog->priv->list_store, &iter, app);
- gtk_list_store_set (store, iter,
- STORE_COL_DESKTOP_FILE, new_desktop_file,
- -1);
+ g_signal_connect_swapped (app, "changed",
+ G_CALLBACK (_app_changed), dialog);
}
static void
-ensure_user_autostart_dir ()
+_app_added (GsmPropertiesDialog *dialog,
+ GspApp *app,
+ GspAppManager *manager)
{
- char *dir;
-
- dir = g_build_filename (g_get_user_config_dir (), "autostart", NULL);
- g_mkdir_with_parents (dir, S_IRWXU);
-
- g_free (dir);
+ append_app (dialog, app);
}
static void
-key_file_set_locale_string (GKeyFile *keyfile,
- const char *group,
- const char *key,
- const char *value)
-{
- const char *locale;
- const char * const *langs_pointer;
- int i;
-
- g_assert (key != NULL);
-
- 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,
- group,
- key,
- locale,
- value);
- } else {
- g_key_file_set_string (keyfile,
- G_KEY_FILE_DESKTOP_GROUP,
- key, value);
- }
-}
-
-static gboolean
-key_file_to_file (GKeyFile *keyfile,
- const char *file,
- GError **error)
+_app_removed (GsmPropertiesDialog *dialog,
+ GspApp *app,
+ GspAppManager *manager)
{
- GError *write_error;
- char *filename;
- char *data;
- gsize length;
- gboolean res;
-
- g_return_val_if_fail (keyfile != NULL, FALSE);
- g_return_val_if_fail (file != 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;
- }
-
- if (!g_path_is_absolute (file)) {
- filename = g_filename_from_uri (file, NULL, &write_error);
- } else {
- filename = g_filename_from_utf8 (file, -1, NULL, NULL, &write_error);
- }
-
- if (write_error) {
- g_propagate_error (error, write_error);
- g_free (data);
- return FALSE;
- }
-
- res = g_file_set_contents (filename, data, length, &write_error);
-
- g_free (filename);
+ GtkTreeIter iter;
- if (write_error) {
- g_propagate_error (error, write_error);
- g_free (data);
- return FALSE;
+ if (!find_by_app (GTK_TREE_MODEL (dialog->priv->list_store),
+ &iter, app)) {
+ return;
}
- g_free (data);
-
- return res;
+ g_signal_handlers_disconnect_by_func (app,
+ _app_changed,
+ dialog);
+ gtk_list_store_remove (dialog->priv->list_store, &iter);
}
static void
-write_desktop_file (EggDesktopFile *desktop_file,
- GtkListStore *store,
- GtkTreeIter *iter,
- gboolean enabled)
+populate_model (GsmPropertiesDialog *dialog)
{
- GKeyFile *keyfile;
- GFile *source;
- GError *error;
- char *path;
- char *name;
- char *command;
- char *comment;
- gboolean path_changed = FALSE;
-
- ensure_user_autostart_dir ();
-
- keyfile = g_key_file_new ();
-
- source = g_file_new_for_uri (egg_desktop_file_get_source (desktop_file));
-
- path = g_file_get_path (source);
-
- error = NULL;
- g_key_file_load_from_file (keyfile,
- path,
- G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
- &error);
- if (error != NULL) {
- goto out;
- }
-
- if (!g_str_has_prefix (path, g_get_user_config_dir ())) {
- /* It's a system-wide file, save it to the user's home */
- char *basename;
-
- basename = g_file_get_basename (source);
-
- g_free (path);
-
- path = g_build_filename (g_get_user_config_dir (),
- "autostart", basename, NULL);
-
- g_free (basename);
-
- path_changed = TRUE;
- }
-
- gtk_tree_model_get (GTK_TREE_MODEL (store),
- iter,
- STORE_COL_NAME, &name,
- STORE_COL_COMMAND, &command,
- STORE_COL_COMMENT, &comment,
- -1);
-
- key_file_set_locale_string (keyfile,
- G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_NAME,
- name);
-
- key_file_set_locale_string (keyfile,
- G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_COMMENT,
- comment);
-
- g_key_file_set_string (keyfile,
- G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_EXEC,
- command);
-
- g_key_file_set_boolean (keyfile,
- G_KEY_FILE_DESKTOP_GROUP,
- "X-GNOME-Autostart-enabled",
- enabled);
-
- if (!key_file_to_file (keyfile, path, &error)) {
- goto out;
- }
-
- if (path_changed) {
- EggDesktopFile *new_desktop_file;
-
- new_desktop_file = egg_desktop_file_new (path, &error);
-
- if (error) {
- goto out;
- }
+ GSList *apps;
+ GSList *l;
- update_desktop_file (store, iter, new_desktop_file);
+ apps = gsp_app_manager_get_apps (dialog->priv->manager);
+ for (l = apps; l != NULL; l = l->next) {
+ append_app (dialog, GSP_APP (l->data));
}
-
- out:
- if (error != NULL) {
- g_warning ("Error when writing desktop file %s: %s",
- path, error->message);
-
- g_error_free (error);
- }
-
- g_free (path);
- g_free (name);
- g_free (comment);
- g_free (command);
- g_object_unref (source);
- g_key_file_free (keyfile);
+ g_slist_free (apps);
}
-static gboolean
-toggle_app (GsmPropertiesDialog *dialog,
- GtkTreeIter *iter,
- gboolean enabled)
+static void
+on_selection_changed (GtkTreeSelection *selection,
+ GsmPropertiesDialog *dialog)
{
- EggDesktopFile *desktop_file;
- GFile *source;
- char *system_path;
- char *basename;
- char *path;
- char *name;
- char *comment;
-
- gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->list_store),
- iter,
- STORE_COL_NAME, &name,
- STORE_COL_COMMENT, &comment,
- STORE_COL_DESKTOP_FILE, &desktop_file,
- -1);
-
- source = g_file_new_for_uri (egg_desktop_file_get_source (desktop_file));
-
- path = g_file_get_path (source);
-
- basename = g_file_get_basename (source);
-
- if (system_desktop_entry_exists (basename, &system_path)) {
- EggDesktopFile *system_desktop_file;
- char *original_name;
- char *original_comment;
- gboolean original_enabled;
-
- system_desktop_file = egg_desktop_file_new (system_path, NULL);
-
- original_name = egg_desktop_file_get_locale_string (system_desktop_file,
- "Name", NULL, NULL);
-
- original_comment = egg_desktop_file_get_locale_string (system_desktop_file,
- "Comment", NULL, NULL);
-
- if (egg_desktop_file_has_key (system_desktop_file,
- "X-GNOME-Autostart-enabled", NULL)) {
- original_enabled = egg_desktop_file_get_boolean (system_desktop_file,
- "X-GNOME-Autostart-enabled", NULL);
- } else {
- original_enabled = TRUE;
- }
-
- if (REALLY_IDENTICAL_STRING (name, original_name) &&
- REALLY_IDENTICAL_STRING (comment, original_comment) &&
- (enabled == original_enabled)) {
- char *user_file =
- g_build_filename (g_get_user_config_dir (),
- "autostart", basename, NULL);
-
- if (g_file_test (user_file, G_FILE_TEST_EXISTS)) {
- g_remove (user_file);
- }
+ gboolean sel;
- g_free (user_file);
-
- update_desktop_file (dialog->priv->list_store, iter, system_desktop_file);
- } else {
- write_desktop_file (desktop_file, dialog->priv->list_store, iter, enabled);
- egg_desktop_file_free (system_desktop_file);
- }
-
- g_free (original_name);
- g_free (original_comment);
- } else {
- write_desktop_file (desktop_file, dialog->priv->list_store, iter, enabled);
- }
-
- g_free (name);
- g_free (comment);
- g_free (basename);
+ sel = gtk_tree_selection_get_selected (selection, NULL, NULL);
- return TRUE;
+ gtk_widget_set_sensitive (dialog->priv->edit_button, sel);
+ gtk_widget_set_sensitive (dialog->priv->delete_button, sel);
}
static void
@@ -687,216 +253,27 @@ on_startup_enabled_toggled (GtkCellRendererToggle *cell_renderer,
GsmPropertiesDialog *dialog)
{
GtkTreeIter iter;
- char *desktop_file_path;
+ GspApp *app;
gboolean active;
- if (!gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (dialog->priv->list_store),
+ if (!gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (dialog->priv->tree_filter),
&iter, path)) {
return;
}
- gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->list_store),
+ app = NULL;
+ gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->tree_filter),
&iter,
- STORE_COL_DESKTOP_FILE, &desktop_file_path,
+ STORE_COL_APP, &app,
-1);
active = gtk_cell_renderer_toggle_get_active (cell_renderer);
active = !active;
- gtk_cell_renderer_toggle_set_active (cell_renderer, active);
-
- if (toggle_app (dialog, &iter, active)) {
- gtk_list_store_set (dialog->priv->list_store,
- &iter,
- STORE_COL_ENABLED, active,
- -1);
- }
-}
-
-static void
-add_app (GtkListStore *store,
- GtkTreeIter *iter)
-{
- EggDesktopFile *desktop_file;
- GKeyFile *keyfile;
- char **argv;
- char *basename;
- char *orig_filename;
- char *filename;
- char *name;
- char *command;
- char *comment;
- char *description;
- char *icon;
- int argc;
- int i = 2;
-
- gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
- STORE_COL_NAME, &name,
- STORE_COL_COMMAND, &command,
- STORE_COL_COMMENT, &comment,
- STORE_COL_ICON_NAME, &icon,
- -1);
-
- g_assert (command != NULL);
- g_shell_parse_argv (command, &argc, &argv, NULL);
-
- basename = g_path_get_basename (argv[0]);
-
- orig_filename = g_build_filename (g_get_user_config_dir (),
- "autostart", basename, NULL);
-
- filename = g_strdup_printf ("%s.desktop", orig_filename);
- while (g_file_test (filename, G_FILE_TEST_EXISTS)) {
- char *tmp = g_strdup_printf ("%s-%d.desktop", orig_filename, i);
-
- g_free (filename);
- filename = tmp;
-
- i++;
- }
-
- g_free (orig_filename);
-
- ensure_user_autostart_dir ();
-
- keyfile = g_key_file_new ();
-
- g_key_file_set_string (keyfile,
- G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_TYPE,
- "Application");
-
- g_key_file_set_string (keyfile,
- G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_NAME,
- name);
-
- g_key_file_set_string (keyfile,
- G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_EXEC,
- command);
-
- if (icon == NULL) {
- icon = g_strdup (STARTUP_APP_ICON);
+ if (app) {
+ gsp_app_set_enabled (app, active);
+ g_object_unref (app);
}
-
- g_key_file_set_string (keyfile,
- G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_ICON,
- icon);
-
- if (comment) {
- g_key_file_set_string (keyfile,
- G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_COMMENT,
- comment);
- }
-
- description = get_app_description (name, comment);
-
- if (!key_file_to_file (keyfile, filename, NULL)) {
- g_warning ("Could not save %s file", filename);
- }
-
- desktop_file = egg_desktop_file_new_from_key_file (keyfile,
- filename,
- NULL);
-
- g_free (basename);
-
- basename = g_path_get_basename (filename);
-
- gtk_list_store_set (store, iter,
- STORE_COL_ENABLED, TRUE,
- STORE_COL_ICON_NAME, icon,
- STORE_COL_DESKTOP_FILE, desktop_file,
- STORE_COL_ID, basename,
- STORE_COL_ACTIVATABLE, TRUE,
- -1);
-
- g_key_file_free (keyfile);
- g_strfreev (argv);
- g_free (name);
- g_free (command);
- g_free (comment);
- g_free (description);
- g_free (basename);
- g_free (icon);
-}
-
-static gboolean
-add_from_desktop_file (GtkTreeView *treeview,
- char *filename)
-{
- EggDesktopFile *desktop_file;
- gboolean success = FALSE;
-
- /* Assume that the file is local */
- GFile *file = g_file_new_for_uri (filename);
- gchar *path = g_file_get_path (file);
-
- if (path != NULL) {
- desktop_file = egg_desktop_file_new (path, NULL);
-
- if (desktop_file != NULL) {
- GtkTreeIter iter;
- GtkTreeModel *model;
- const char *name;
- char *comment;
- char *description;
- char *command;
- char *icon;
-
- model = gtk_tree_view_get_model (treeview);
-
- gtk_list_store_append (GTK_LIST_STORE (model), &iter);
-
- name = egg_desktop_file_get_name (desktop_file);
-
- comment = egg_desktop_file_get_locale_string (desktop_file,
- EGG_DESKTOP_FILE_KEY_COMMENT,
- NULL, NULL);
- if (comment == NULL) {
- comment = egg_desktop_file_get_string (desktop_file,
- EGG_DESKTOP_FILE_KEY_COMMENT,
- NULL);
- }
-
- description = get_app_description (name, comment);
-
- command = egg_desktop_file_get_string (desktop_file,
- EGG_DESKTOP_FILE_KEY_EXEC,
- NULL);
-
- icon = egg_desktop_file_get_string (desktop_file,
- EGG_DESKTOP_FILE_KEY_ICON,
- NULL);
-
- if (name && comment && description && command) {
- gtk_list_store_set (GTK_LIST_STORE (model), &iter,
- STORE_COL_DESCRIPTION, description,
- STORE_COL_NAME, name,
- STORE_COL_COMMAND, command,
- STORE_COL_COMMENT, comment,
- STORE_COL_ICON_NAME, icon,
- STORE_COL_DESKTOP_FILE, desktop_file,
- -1);
-
- add_app (GTK_LIST_STORE (model), &iter);
- success = TRUE;
- }
-
- g_free (comment);
- g_free (description);
- g_free (command);
- g_free (icon);
- egg_desktop_file_free (desktop_file);
- }
- }
-
- g_free (path);
- return success;
}
static gboolean
@@ -921,7 +298,7 @@ on_drag_data (GtkWidget *widget,
for (i = 0; filenames[i] && filenames[i][0]; i++) {
/* Return success if at least one file succeeded */
gboolean file_success;
- file_success = add_from_desktop_file (GTK_TREE_VIEW (widget), filenames[i]);
+ file_success = gsp_app_copy_desktop_file (filenames[i]);
dnd_success = dnd_success || file_success;
}
@@ -932,266 +309,77 @@ on_drag_data (GtkWidget *widget,
return TRUE;
}
-static gboolean
-edit_app_dialog (GsmPropertiesDialog *dialog,
- GtkTreeIter *iter)
-{
- GtkWidget *dlg;
- char *c_name;
- char *c_command;
- char *c_comment;
-
- gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->list_store),
- iter,
- STORE_COL_NAME, &c_name,
- STORE_COL_COMMAND, &c_command,
- STORE_COL_COMMENT, &c_comment,
- -1);
-
- dlg = gsm_app_dialog_new (c_name, c_command, c_comment);
- gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (dialog));
- g_free (c_name);
- g_free (c_command);
- g_free (c_comment);
-
- while (gtk_dialog_run (GTK_DIALOG (dlg)) == GTK_RESPONSE_OK) {
- const char *name;
- const char *command;
- const char *comment;
- const char *error_msg;
- char **argv;
- char *description;
- int argc;
- GError *error;
-
- name = gsm_app_dialog_get_name (GSM_APP_DIALOG (dlg));
- command = gsm_app_dialog_get_command (GSM_APP_DIALOG (dlg));
- comment = gsm_app_dialog_get_comment (GSM_APP_DIALOG (dlg));
- error = NULL;
-
- error_msg = NULL;
- if (gsm_util_text_is_blank (name)) {
- error_msg = _("The name of the startup program cannot be empty");
- }
-
- if (gsm_util_text_is_blank (command)) {
- error_msg = _("The startup command cannot be empty");
- } else {
- if (!g_shell_parse_argv (command, &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;
-
- gtk_widget_show (dlg);
-
- msgbox = gtk_message_dialog_new (GTK_WINDOW (dlg),
- 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);
- } else {
- g_strfreev (argv);
-
- description = get_app_description (name, comment);
-
- gtk_list_store_set (GTK_LIST_STORE (dialog->priv->list_store), iter,
- STORE_COL_DESCRIPTION, description,
- STORE_COL_NAME, name,
- STORE_COL_COMMAND, command,
- STORE_COL_COMMENT, comment,
- -1);
-
- g_free (description);
-
- gtk_widget_destroy (dlg);
-
- return TRUE;
- }
- }
-
- gtk_widget_destroy (dlg);
-
- return FALSE;
-}
-
-
static void
on_add_app_clicked (GtkWidget *widget,
GsmPropertiesDialog *dialog)
{
- GtkTreeView *view;
- GtkTreeSelection *selection;
- GtkTreeModel *model;
- GtkTreeIter iter;
+ GtkWidget *add_dialog;
+ char *name;
+ char *exec;
+ char *comment;
- view = GTK_TREE_VIEW (glade_xml_get_widget (dialog->priv->xml,
- CAPPLET_TREEVIEW_WIDGET_NAME));
+ add_dialog = gsm_app_dialog_new (NULL, NULL, NULL);
+ gtk_window_set_transient_for (GTK_WINDOW (add_dialog),
+ GTK_WINDOW (dialog));
- selection = gtk_tree_view_get_selection (view);
- model = gtk_tree_view_get_model (view);
-
- gtk_list_store_append (GTK_LIST_STORE (model), &iter);
-
- if (edit_app_dialog (dialog, &iter)) {
- add_app (GTK_LIST_STORE (model), &iter);
- } else {
- gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+ 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
-delete_desktop_file (GtkListStore *store,
- GtkTreeIter *iter)
-{
- EggDesktopFile *desktop_file;
- GFile *source;
- char *basename;
- char *path;
-
- gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
- STORE_COL_DESKTOP_FILE, &desktop_file,
- -1);
-
- source = g_file_new_for_uri (egg_desktop_file_get_source (desktop_file));
-
- path = g_file_get_path (source);
- basename = g_file_get_basename (source);
-
- if (g_str_has_prefix (path, g_get_user_config_dir ()) &&
- !system_desktop_entry_exists (basename, NULL)) {
- if (g_file_test (path, G_FILE_TEST_EXISTS))
- g_remove (path);
- } else {
- /* Two possible cases:
- * a) We want to remove a system wide startup desktop file.
- * We can't do that, so we will create a user desktop file
- * with the hidden flag set.
- * b) We want to remove a startup desktop file that is both
- * system and user. So we have to mark it as hidden.
- */
- GKeyFile *keyfile;
- GError *error;
- char *user_path;
-
- ensure_user_autostart_dir ();
-
- keyfile = g_key_file_new ();
-
- error = NULL;
-
- g_key_file_load_from_file (keyfile, path,
- G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS,
- &error);
-
- if (error) {
- g_error_free (error);
- g_key_file_free (keyfile);
- }
-
- g_key_file_set_boolean (keyfile,
- G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_HIDDEN,
- TRUE);
-
- user_path = g_build_filename (g_get_user_config_dir (),
- "autostart", basename, NULL);
-
- if (!key_file_to_file (keyfile, user_path, NULL)) {
- g_warning ("Could not save %s file", user_path);
- }
-
- g_key_file_free (keyfile);
-
- g_free (user_path);
- }
-
- g_object_unref (source);
- g_free (path);
- g_free (basename);
-}
-
-static void
-delete_app (GtkListStore *store,
- GtkTreeIter *iter)
-{
- delete_desktop_file (store, iter);
-}
-
-static void
on_delete_app_clicked (GtkWidget *widget,
GsmPropertiesDialog *dialog)
{
- GtkTreeView *view;
GtkTreeSelection *selection;
- GtkTreeModel *model;
GtkTreeIter iter;
+ GspApp *app;
- view = GTK_TREE_VIEW (glade_xml_get_widget (dialog->priv->xml,
- CAPPLET_TREEVIEW_WIDGET_NAME));
-
- selection = gtk_tree_view_get_selection (view);
- model = gtk_tree_view_get_model (view);
+ selection = gtk_tree_view_get_selection (dialog->priv->treeview);
if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
return;
}
- delete_app (GTK_LIST_STORE (model), &iter);
-
- gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
-}
-
-static void
-update_app (GtkListStore *store,
- GtkTreeIter *iter)
-{
- EggDesktopFile *desktop_file;
- gboolean enabled;
-
- gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
- STORE_COL_ENABLED, &enabled,
- STORE_COL_DESKTOP_FILE, &desktop_file,
+ app = NULL;
+ gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->tree_filter),
+ &iter,
+ STORE_COL_APP, &app,
-1);
- write_desktop_file (desktop_file, store, iter, enabled);
+ if (app) {
+ gsp_app_delete (app);
+ g_object_unref (app);
+ }
}
static void
on_edit_app_clicked (GtkWidget *widget,
GsmPropertiesDialog *dialog)
{
- GtkTreeView *view;
GtkTreeSelection *selection;
- GtkTreeModel *model;
GtkTreeIter iter;
+ GspApp *app;
- view = GTK_TREE_VIEW (glade_xml_get_widget (dialog->priv->xml,
- CAPPLET_TREEVIEW_WIDGET_NAME));
-
- selection = gtk_tree_view_get_selection (view);
- model = gtk_tree_view_get_model (view);
+ selection = gtk_tree_view_get_selection (dialog->priv->treeview);
if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
return;
}
- if (edit_app_dialog (dialog, &iter)) {
- update_app (GTK_LIST_STORE (model), &iter);
+ app = NULL;
+ gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->tree_filter),
+ &iter,
+ STORE_COL_APP, &app,
+ -1);
+
+ if (app) {
+ gsp_app_edit (app, GTK_WINDOW (dialog));
+ g_object_unref (app);
}
}
@@ -1212,18 +400,12 @@ on_autosave_value_notify (GConfClient *client,
{
gboolean gval;
gboolean bval;
- GtkWidget *button;
-
- button = glade_xml_get_widget (dialog->priv->xml, CAPPLET_REMEMBER_WIDGET_NAME);
- if (button == NULL) {
- return;
- }
gval = gconf_value_get_bool (entry->value);
- bval = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+ bval = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->remember_toggle));
if (bval != gval) {
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), gval);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->priv->remember_toggle), gval);
}
}
@@ -1255,8 +437,9 @@ on_save_session_clicked (GtkWidget *widget,
static void
setup_dialog (GsmPropertiesDialog *dialog)
{
- GtkWidget *treeview;
+ GtkTreeView *treeview;
GtkWidget *button;
+ GtkTreeModel *tree_filter;
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
GtkTreeSelection *selection;
@@ -1266,32 +449,36 @@ setup_dialog (GsmPropertiesDialog *dialog)
gtk_dialog_add_buttons (GTK_DIALOG (dialog),
GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
NULL);
- g_signal_connect (dialog,
- "response",
- G_CALLBACK (on_response),
- dialog);
dialog->priv->list_store = gtk_list_store_new (NUMBER_OF_COLUMNS,
G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN,
G_TYPE_STRING,
+ G_TYPE_ICON,
+ GDK_TYPE_PIXBUF,
G_TYPE_STRING,
- G_TYPE_STRING,
- G_TYPE_STRING,
- G_TYPE_STRING,
- G_TYPE_POINTER,
- G_TYPE_STRING,
- G_TYPE_BOOLEAN);
+ G_TYPE_OBJECT);
+ 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 = glade_xml_get_widget (dialog->priv->xml, CAPPLET_TREEVIEW_WIDGET_NAME);
- gtk_tree_view_set_model (GTK_TREE_VIEW (treeview),
- GTK_TREE_MODEL (dialog->priv->list_store));
- gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
+ treeview = GTK_TREE_VIEW (glade_xml_get_widget (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 (GTK_TREE_VIEW (treeview));
+ selection = gtk_tree_view_get_selection (treeview);
gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
g_signal_connect (selection,
"changed",
@@ -1303,9 +490,8 @@ setup_dialog (GsmPropertiesDialog *dialog)
column = gtk_tree_view_column_new_with_attributes (_("Enabled"),
renderer,
"active", STORE_COL_ENABLED,
- "activatable", STORE_COL_ACTIVATABLE,
NULL);
- gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
+ gtk_tree_view_append_column (treeview, column);
g_signal_connect (renderer,
"toggled",
G_CALLBACK (on_startup_enabled_toggled),
@@ -1315,12 +501,14 @@ setup_dialog (GsmPropertiesDialog *dialog)
renderer = gtk_cell_renderer_pixbuf_new ();
column = gtk_tree_view_column_new_with_attributes (_("Icon"),
renderer,
- "icon-name", STORE_COL_ICON_NAME,
+ "icon_name", STORE_COL_ICON_NAME,
+ "gicon", STORE_COL_GICON,
+ "pixbuf", STORE_COL_PIXBUF,
NULL);
g_object_set (renderer,
- "stock-size", GTK_ICON_SIZE_LARGE_TOOLBAR,
+ "stock-size", GSM_PROPERTIES_ICON_SIZE,
NULL);
- gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
+ gtk_tree_view_append_column (treeview, column);
/* NAME COLUMN */
renderer = gtk_cell_renderer_text_new ();
@@ -1331,14 +519,14 @@ setup_dialog (GsmPropertiesDialog *dialog)
g_object_set (renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
NULL);
- gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
+ gtk_tree_view_append_column (treeview, column);
- gtk_tree_view_column_set_sort_column_id (column, STORE_COL_NAME);
- gtk_tree_view_set_search_column (GTK_TREE_VIEW (treeview), STORE_COL_NAME);
- gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), TRUE);
+ gtk_tree_view_column_set_sort_column_id (column, STORE_COL_DESCRIPTION);
+ gtk_tree_view_set_search_column (treeview, STORE_COL_DESCRIPTION);
+ gtk_tree_view_set_rules_hint (treeview, TRUE);
- gtk_drag_dest_set (treeview,
+ gtk_drag_dest_set (GTK_WIDGET (treeview),
GTK_DEST_DEFAULT_ALL,
drag_targets,
G_N_ELEMENTS (drag_targets),
@@ -1350,23 +538,26 @@ setup_dialog (GsmPropertiesDialog *dialog)
dialog);
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (dialog->priv->list_store),
- STORE_COL_NAME,
+ STORE_COL_DESCRIPTION,
GTK_SORT_ASCENDING);
button = glade_xml_get_widget (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 = glade_xml_get_widget (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 = glade_xml_get_widget (dialog->priv->xml, CAPPLET_EDIT_WIDGET_NAME);
+ dialog->priv->edit_button = button;
g_signal_connect (button,
"clicked",
G_CALLBACK (on_edit_app_clicked),
@@ -1374,6 +565,7 @@ setup_dialog (GsmPropertiesDialog *dialog)
client = gconf_client_get_default ();
button = glade_xml_get_widget (dialog->priv->xml, CAPPLET_REMEMBER_WIDGET_NAME);
+ dialog->priv->remember_toggle = button;
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
gconf_client_get_bool (client, SPC_GCONF_AUTOSAVE_KEY, NULL));
gconf_client_notify_add (client,
@@ -1395,6 +587,12 @@ setup_dialog (GsmPropertiesDialog *dialog)
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);
}
@@ -1427,6 +625,11 @@ gsm_properties_dialog_dispose (GObject *object)
dialog = GSM_PROPERTIES_DIALOG (object);
+ if (dialog->priv->manager != NULL) {
+ g_object_unref (dialog->priv->manager);
+ dialog->priv->manager = NULL;
+ }
+
if (dialog->priv->xml != NULL) {
g_object_unref (dialog->priv->xml);
dialog->priv->xml = NULL;
diff --git a/capplet/gsm-properties-dialog.h b/capplet/gsm-properties-dialog.h
index 6d7cd7c..df4915e 100644
--- a/capplet/gsm-properties-dialog.h
+++ b/capplet/gsm-properties-dialog.h
@@ -50,6 +50,8 @@ 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 0000000..201da48
--- /dev/null
+++ b/capplet/gsp-app-manager.c
@@ -0,0 +1,567 @@
+/* -*- 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_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->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_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->apps,
+ (GFunc) _gsp_app_manager_app_unref, manager);
+ g_slist_free (manager->priv->apps);
+ manager->priv->apps = NULL;
+
+ 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 (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 0000000..777f8d6
--- /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 0000000..165cf31
--- /dev/null
+++ b/capplet/gsp-app.c
@@ -0,0 +1,1019 @@
+/* -*- 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 <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_ALL 0xffff
+
+struct _GspAppPrivate {
+ char *basename;
+ char *path;
+
+ gboolean hidden;
+ gboolean enabled;
+
+ char *name;
+ char *exec;
+ char *comment;
+ char *icon_name;
+
+ GdkPixbuf *pixbuf;
+ 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_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->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_name) {
+ g_free (app->priv->icon_name);
+ app->priv->icon_name = NULL;
+ }
+
+ if (app->priv->pixbuf) {
+ g_object_unref (app->priv->pixbuf);
+ app->priv->pixbuf = 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_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->save_timeout) {
+ g_source_remove (app->priv->save_timeout);
+ app->priv->save_timeout = 0;
+
+ /* save now */
+ _gsp_app_save (app);
+ }
+
+ 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) {
+ 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;
+ }
+
+ 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;
+ }
+
+ 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_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_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;
+}
+
+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_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);
+}
+
+const char *
+gsp_app_get_icon_name (GspApp *app)
+{
+ g_return_val_if_fail (GSP_IS_APP (app), NULL);
+
+ return app->priv->icon_name;
+}
+
+GdkPixbuf *
+gsp_app_get_pixbuf (GspApp *app)
+{
+ g_return_val_if_fail (GSP_IS_APP (app), NULL);
+
+ return app->priv->pixbuf;
+}
+
+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_edit (GspApp *app,
+ GtkWindow *parent)
+{
+ GtkWidget *dialog;
+ char *name;
+ char *exec;
+ char *comment;
+ gboolean changed;
+
+ g_return_if_fail (GSP_IS_APP (app));
+
+ dialog = gsm_app_dialog_new (app->priv->name,
+ app->priv->exec,
+ app->priv->comment);
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
+
+ if (gsm_app_dialog_run (GSM_APP_DIALOG (dialog),
+ &name, &exec, &comment)) {
+ changed = FALSE;
+
+ if (!_gsp_str_equal (name, app->priv->name)) {
+ changed = TRUE;
+ g_free (app->priv->name);
+ app->priv->name = name;
+ app->priv->save_mask |= GSP_ASP_SAVE_MASK_NAME;
+ } else {
+ g_free (name);
+ }
+
+ if (!_gsp_str_equal (comment, app->priv->comment)) {
+ changed = TRUE;
+ g_free (app->priv->comment);
+ app->priv->comment = comment;
+ app->priv->save_mask |= GSP_ASP_SAVE_MASK_COMMENT;
+ } else {
+ g_free (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 = exec;
+ app->priv->save_mask |= GSP_ASP_SAVE_MASK_EXEC;
+ } else {
+ g_free (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);
+}
+
+static GdkPixbuf *
+_gsp_app_load_pixbuf (const char *path)
+{
+ int width, height;
+
+ if (gtk_icon_size_lookup (GSM_PROPERTIES_ICON_SIZE,
+ &width, &height)) {
+ return gdk_pixbuf_new_from_file_at_size (path,
+ width, height, NULL);
+ }
+
+ return NULL;
+}
+
+GspApp *
+gsp_app_new (const char *path,
+ unsigned int xdg_position)
+{
+ GspAppManager *manager;
+ GspApp *app;
+ GKeyFile *keyfile;
+ char *basename;
+ gboolean new;
+ char *icon;
+
+ 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->enabled = gsp_key_file_get_boolean (keyfile,
+ GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED,
+ TRUE);
+
+ 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);
+ }
+
+ icon = gsp_key_file_get_locale_string (keyfile,
+ G_KEY_FILE_DESKTOP_KEY_ICON);
+
+ if (icon) {
+ /* look at icon and see if it's a themed icon or not */
+ if (g_path_is_absolute (icon)) {
+ app->priv->pixbuf = _gsp_app_load_pixbuf (icon);
+ g_free (icon);
+ } else {
+ app->priv->icon_name = icon;
+ }
+ }
+
+ 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->enabled = 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_name = NULL;
+
+ app->priv->pixbuf = 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->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 0000000..9221147
--- /dev/null
+++ b/capplet/gsp-app.h
@@ -0,0 +1,96 @@
+/* -*- 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 <gtk/gtk.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);
+
+GspApp *gsp_app_new (const char *path,
+ unsigned int xdg_position);
+void gsp_app_create (const char *name,
+ const char *comment,
+ const char *exec);
+
+gboolean gsp_app_copy_desktop_file (const char *uri);
+
+void gsp_app_reload_at (GspApp *app,
+ const char *path,
+ unsigned int xdg_position);
+
+void gsp_app_edit (GspApp *app,
+ GtkWindow *parent);
+void gsp_app_delete (GspApp *app);
+
+const char *gsp_app_get_basename (GspApp *app);
+
+gboolean gsp_app_get_hidden (GspApp *app);
+
+gboolean gsp_app_get_enabled (GspApp *app);
+void gsp_app_set_enabled (GspApp *app,
+ gboolean enabled);
+
+const char *gsp_app_get_description (GspApp *app);
+const char *gsp_app_get_icon_name (GspApp *app);
+GdkPixbuf *gsp_app_get_pixbuf (GspApp *app);
+
+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 0000000..fb28230
--- /dev/null
+++ b/capplet/gsp-keyfile.c
@@ -0,0 +1,149 @@
+/*
+ * 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;
+}
+
+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 0000000..51a56e0
--- /dev/null
+++ b/capplet/gsp-keyfile.h
@@ -0,0 +1,63 @@
+/*
+ * 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);
+#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 */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]