gnome-session r4674 - in trunk: . capplet data gnome-session po



Author: lucasr
Date: Sat May 10 18:47:24 2008
New Revision: 4674
URL: http://svn.gnome.org/viewvc/gnome-session?rev=4674&view=rev

Log:
2008-05-10  Lucas Rocha  <lucasr gnome org>

	Session properties capplet re-implemented for new code base.
	#529601, Karsten BrÃckelmann.

	* capplet/main.c, capplet/ui.[ch], capplet/commands.[ch]: session
	properties capplet code.
	* gnome-session/util.[ch]: utility functions shared among session
	manager and capplet.
	* gnome-session/session.c (gsm_session_new): use gsm_util_* functions
	instead of local ones.
	* data/session-properties.glade: capplet Glade file.


Added:
   trunk/capplet/
   trunk/capplet/Makefile.am
   trunk/capplet/commands.c
   trunk/capplet/commands.h
   trunk/capplet/main.c
   trunk/capplet/ui.c
   trunk/capplet/ui.h
   trunk/data/session-properties.glade
   trunk/gnome-session/util.c
   trunk/gnome-session/util.h
Modified:
   trunk/ChangeLog
   trunk/Makefile.am
   trunk/configure.in
   trunk/data/Makefile.am
   trunk/gnome-session/Makefile.am
   trunk/gnome-session/dbus.c
   trunk/gnome-session/gconf.c
   trunk/gnome-session/gsm.h
   trunk/gnome-session/main.c
   trunk/gnome-session/session.c
   trunk/gnome-session/xsmp.c
   trunk/po/POTFILES.in

Modified: trunk/Makefile.am
==============================================================================
--- trunk/Makefile.am	(original)
+++ trunk/Makefile.am	Sat May 10 18:47:24 2008
@@ -1,6 +1,7 @@
 SUBDIRS =			\
 	egg			\
 	gnome-session		\
+	capplet			\
 	compat			\
 	splash			\
 	po			\

Added: trunk/capplet/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/capplet/Makefile.am	Sat May 10 18:47:24 2008
@@ -0,0 +1,23 @@
+bin_PROGRAMS = gnome-session-properties
+
+INCLUDES =						\
+	$(WARN_CFLAGS)					\
+	$(SESSION_PROPERTIES_CFLAGS)			\
+	$(GCONF_CFLAGS)					\
+	-I$(top_srcdir)/egg				\
+	-I$(top_srcdir)/gnome-session			\
+	-DLOCALE_DIR=\""$(datadir)/locale"\"		\
+	-DDATA_DIR=\""$(datadir)/gnome-session"\"
+
+gnome_session_properties_LDADD =			\
+	$(SESSION_PROPERTIES_LIBS)			\
+	$(top_builddir)/egg/libeggdesktopfile.la 	\
+	$(top_builddir)/gnome-session/libgsmutil.la 	\
+	$(GCONF_LIBS)
+
+gnome_session_properties_SOURCES =			\
+	main.c						\
+	commands.h					\
+	commands.c					\
+	ui.h						\
+	ui.c

Added: trunk/capplet/commands.c
==============================================================================
--- (empty file)
+++ trunk/capplet/commands.c	Sat May 10 18:47:24 2008
@@ -0,0 +1,763 @@
+#include <string.h>
+
+#include <glib/gstdio.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+#include "eggdesktopfile.h"
+#include "commands.h"
+#include "util.h"
+
+#define DESKTOP_ENTRY_GROUP  "Desktop Entry"
+#define STARTUP_APP_ICON     "gnome-run"
+
+#define REALLY_IDENTICAL_STRING(a, b) \
+      ((a && b && !strcmp (a, b)) || (!a && !b))
+
+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;
+}
+
+static void
+update_desktop_file (GtkListStore *store, 
+                     GtkTreeIter *iter, 
+                     EggDesktopFile *new_desktop_file)
+{
+  EggDesktopFile *old_desktop_file;
+
+  gtk_tree_model_get (GTK_TREE_MODEL (store), iter, 
+                      STORE_COL_DESKTOP_FILE, &old_desktop_file,
+                      -1);
+
+  egg_desktop_file_free (old_desktop_file);
+
+  gtk_list_store_set (store, iter, 
+                      STORE_COL_DESKTOP_FILE, new_desktop_file,
+                      -1);
+}
+
+static gboolean 
+find_by_id (GtkListStore *store, const gchar *id)
+{
+  GtkTreeIter iter;
+  gchar *iter_id = NULL;
+	
+  if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter))
+    return FALSE;
+  
+  do
+    {
+      gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
+          		  STORE_COL_ID, &iter_id,
+          		  -1);
+
+      if (!strcmp (iter_id, id))
+        {
+          return TRUE;
+        }
+    } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
+
+  return FALSE;
+}
+
+static void
+ensure_user_autostart_dir ()
+{
+  gchar *dir;
+  
+  dir = g_build_filename (g_get_user_config_dir (), "autostart", NULL);
+  g_mkdir_with_parents (dir, S_IRWXU);
+
+  g_free (dir);
+}
+
+static gboolean
+key_file_to_file (GKeyFile *keyfile, 
+                  const gchar *file,
+                  GError **error)
+{
+  GError *write_error;
+  gchar *filename;
+  gchar *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);
+
+  if (write_error)
+    {
+      g_propagate_error (error, write_error);
+      g_free (data);
+      return FALSE;
+    }
+
+  g_free (data);
+
+  return res;
+}
+
+static void
+key_file_set_locale_string (GKeyFile *keyfile,
+                            const gchar *group,
+                            const gchar *key,
+                            const gchar *value)
+{
+  const char *locale;
+  const char * const *langs_pointer;
+  int i;
+
+  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)
+    g_key_file_set_locale_string (keyfile, group,
+                                  key, locale, value);
+  else
+    g_key_file_set_string (keyfile, "Desktop Entry", key, value);
+}
+
+static void
+delete_desktop_file (GtkListStore *store, GtkTreeIter *iter)
+{
+  EggDesktopFile *desktop_file;
+  GFile *source;
+  char *basename, *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, DESKTOP_ENTRY_GROUP,
+                              "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
+write_desktop_file (EggDesktopFile *desktop_file,
+                    GtkListStore   *store, 
+                    GtkTreeIter    *iter, 
+                    gboolean        enabled)
+{
+  GKeyFile *keyfile;
+  GFile *source;
+  GError *error;
+  gchar *path, *name, *command, *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)
+    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 */
+      gchar *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, DESKTOP_ENTRY_GROUP, 
+                              "Name", name);
+
+  key_file_set_locale_string (keyfile, DESKTOP_ENTRY_GROUP, 
+                              "Comment", comment);
+
+  g_key_file_set_string (keyfile, DESKTOP_ENTRY_GROUP,
+                         "Exec", command);
+
+  g_key_file_set_boolean (keyfile,
+                          DESKTOP_ENTRY_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;
+ 
+      update_desktop_file (store, iter, new_desktop_file);
+    }
+
+out:
+  if (error)
+    {
+      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);
+}
+
+static gboolean 
+append_app (GtkListStore *store, EggDesktopFile *desktop_file)
+{
+  GtkTreeIter iter;
+  GFile *source;
+  gchar *basename, *description, *name, *comment, *command, *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,
+				"Hidden", NULL))
+    {
+      if (egg_desktop_file_get_boolean (desktop_file, 
+                                        "Hidden", NULL))
+        return FALSE; 
+    }
+
+  /* Check for duplicate apps */
+  if (find_by_id (store, basename))
+    return TRUE;
+
+  name = egg_desktop_file_get_locale_string (desktop_file,
+                                             "Name", NULL, NULL);
+
+  comment = NULL;
+
+  if (egg_desktop_file_has_key (desktop_file,
+				"Comment", NULL))
+    {
+      comment = 
+         egg_desktop_file_get_locale_string (desktop_file,
+                                             "Comment", NULL, NULL); 
+    }
+
+  description = spc_command_get_app_description (name, comment);
+ 
+  command = egg_desktop_file_get_string (desktop_file,
+                                         "Exec", NULL);
+
+  icon_name = NULL;
+
+  if (egg_desktop_file_has_key (desktop_file,
+				"Icon", NULL))
+    {
+      icon_name = 
+         egg_desktop_file_get_string (desktop_file,
+                                      "Icon", NULL); 
+    }
+
+  if (icon_name == NULL || *icon_name == '\0')
+    {
+      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);
+    }
+
+  gtk_list_store_append (store, &iter);
+
+  gtk_list_store_set (store, &iter,
+                      STORE_COL_ENABLED, enabled,
+                      STORE_COL_ICON_NAME, icon_name,
+                      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,
+                      -1);
+
+  g_object_unref (source);
+  g_free (basename);
+  g_free (name);
+  g_free (comment);
+  g_free (description);
+  g_free (icon_name);
+
+  return TRUE;
+}
+
+static gint
+compare_app (gconstpointer a, gconstpointer b)
+{
+  if (!strcmp (a, b))
+    return 0;
+
+  return 1;
+}
+
+static void
+append_autostart_apps (GtkListStore *store, const char *path, GList **removed_apps)
+{
+  GDir *dir;
+  const char *name;
+
+  dir = g_dir_open (path, 0, NULL);
+  if (!dir)
+    return;
+
+  while ((name = g_dir_read_name (dir)))
+    {
+      EggDesktopFile *desktop_file;
+      GError *error = NULL;
+      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);
+
+      desktop_file = egg_desktop_file_new (desktop_file_path, &error);
+
+      if (!error)
+	{
+	  g_debug ("read %s", desktop_file_path);
+
+	  if (!append_app (store, 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);
+}
+
+GtkTreeModel *
+spc_command_get_store ()
+{
+  GtkListStore *store;
+  GList *removed_apps;
+  char **autostart_dirs;
+  gint i;
+
+  store = gtk_list_store_new (STORE_NUM_COLS,
+                              G_TYPE_BOOLEAN,
+                              G_TYPE_STRING,
+                              G_TYPE_STRING,
+                              G_TYPE_STRING,
+                              G_TYPE_STRING,
+                              G_TYPE_STRING,
+                              G_TYPE_POINTER,
+                              G_TYPE_STRING,
+                              G_TYPE_BOOLEAN);
+
+  autostart_dirs = gsm_util_get_autostart_dirs ();
+ 
+  removed_apps = NULL;
+ 
+  for (i = 0; autostart_dirs[i]; i++)
+    {
+      append_autostart_apps (store, autostart_dirs[i], &removed_apps);
+    }
+
+  g_strfreev (autostart_dirs);
+  g_list_foreach (removed_apps, (GFunc) g_free, NULL);
+  g_list_free (removed_apps);
+
+  return GTK_TREE_MODEL (store);
+}
+
+gboolean
+spc_command_enable_app (GtkListStore *store, 
+                                       GtkTreeIter  *iter)
+{
+  EggDesktopFile *desktop_file;
+  GFile *source;
+  char *system_path, *basename, *path; 
+  char *name, *comment;
+  gboolean enabled;
+
+  gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
+      		      STORE_COL_ENABLED, &enabled,
+      		      STORE_COL_NAME, &name,
+      		      STORE_COL_COMMENT, &comment,
+      		      STORE_COL_DESKTOP_FILE, &desktop_file,
+      		      -1);
+
+  if (enabled)
+    return TRUE;
+
+  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, *original_comment;
+
+      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 (REALLY_IDENTICAL_STRING (name, original_name) &&
+          REALLY_IDENTICAL_STRING (comment, original_comment))
+        {
+          gchar *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);
+
+          g_free (user_file);
+
+          update_desktop_file (store, iter, system_desktop_file);
+        }
+      else
+        {
+          write_desktop_file (desktop_file, store, iter, TRUE);
+          egg_desktop_file_free (system_desktop_file);
+        }
+
+      g_free (original_name);
+      g_free (original_comment);
+    }
+  else
+    {
+      write_desktop_file (desktop_file, store, iter, TRUE);
+    }
+
+  g_free (name);
+  g_free (comment);
+  g_free (basename);
+
+  return TRUE;
+}
+
+gboolean
+spc_command_disable_app (GtkListStore *store, 
+                                        GtkTreeIter  *iter)
+{
+  EggDesktopFile *desktop_file;
+  const gchar *source;
+  gchar *basename;
+  gboolean enabled;
+
+  gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
+      		      STORE_COL_ENABLED, &enabled,
+      		      STORE_COL_DESKTOP_FILE, &desktop_file,
+      		      -1);
+
+  if (!enabled)
+    return TRUE;
+
+  source = egg_desktop_file_get_source (desktop_file);
+
+  basename = g_path_get_basename (source);
+
+  write_desktop_file (desktop_file, store, iter, FALSE);
+
+  g_free (basename);
+
+  return TRUE;
+}
+
+void
+spc_command_add_app (GtkListStore *store,
+                                    GtkTreeIter *iter)
+{
+  EggDesktopFile *desktop_file;
+  GKeyFile *keyfile;
+  char **argv;
+  char *basename, *orig_filename, *filename;
+  char *name, *command, *comment, *description;
+  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,
+                      -1);
+
+  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);
+
+  keyfile = g_key_file_new ();
+
+  g_key_file_set_string (keyfile, DESKTOP_ENTRY_GROUP,
+                         "Type", "Application");  
+
+  g_key_file_set_string (keyfile, DESKTOP_ENTRY_GROUP,
+                         "Name", name);  
+
+  g_key_file_set_string (keyfile, DESKTOP_ENTRY_GROUP,
+                         "Exec", command);  
+
+  if (comment)
+    g_key_file_set_string (keyfile, DESKTOP_ENTRY_GROUP,
+                           "Comment", comment);  
+
+  description = spc_command_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, STARTUP_APP_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);
+}
+
+void
+spc_command_delete_app (GtkListStore *store, 
+                                       GtkTreeIter  *iter)
+{
+  delete_desktop_file (store, iter);
+}
+
+void
+spc_command_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,
+      		      -1);
+
+  write_desktop_file (desktop_file, store, iter, enabled);
+}
+
+char *
+spc_command_get_app_description (const char *name, 
+                                                const char *comment)
+{
+  return g_strdup_printf ("<b>%s</b>\n%s", name,
+                          (!gsm_util_text_is_blank (comment) ? 
+                           comment : _("No description")));
+}

Added: trunk/capplet/commands.h
==============================================================================
--- (empty file)
+++ trunk/capplet/commands.h	Sat May 10 18:47:24 2008
@@ -0,0 +1,44 @@
+#ifndef __SESSION_PROPERTIES_COMMANDS_H__ 
+#define __SESSION_PROPERTIES_COMMANDS_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+enum
+{
+  STORE_COL_ENABLED = 0,
+  STORE_COL_ICON_NAME,
+  STORE_COL_DESCRIPTION,
+  STORE_COL_NAME,
+  STORE_COL_COMMAND,
+  STORE_COL_COMMENT,
+  STORE_COL_DESKTOP_FILE,
+  STORE_COL_ID,
+  STORE_COL_ACTIVATABLE, 
+  STORE_NUM_COLS              
+};
+
+GtkTreeModel*   spc_command_get_store            (void);
+
+gboolean        spc_command_enable_app           (GtkListStore *store, 
+                                                  GtkTreeIter  *iter);
+
+gboolean        spc_command_disable_app          (GtkListStore *store, 
+                                                  GtkTreeIter  *iter);
+
+void            spc_command_add_app              (GtkListStore *store,
+                                                  GtkTreeIter  *iter);
+
+void            spc_command_delete_app           (GtkListStore *store, 
+                                                  GtkTreeIter  *iter);
+
+void            spc_command_update_app           (GtkListStore *store,
+                                                  GtkTreeIter  *iter);
+
+char*           spc_command_get_app_description  (const char *name,
+                                                  const char *comment);
+
+G_END_DECLS
+
+#endif /* __SESSION_PROPERTIES_COMMANDS_H__ */

Added: trunk/capplet/main.c
==============================================================================
--- (empty file)
+++ trunk/capplet/main.c	Sat May 10 18:47:24 2008
@@ -0,0 +1,63 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+
+#include <libgnomeui/gnome-client.h>
+#include <libgnomeui/gnome-help.h>
+#include <libgnomeui/gnome-ui-init.h>
+
+#include <gconf/gconf-client.h>
+
+#include "ui.h"
+
+int
+main (int argc, char *argv[])
+{
+  GOptionContext *context;
+  GConfClient *client;
+  GnomeClient *master_client;
+
+  bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR);
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+  textdomain (GETTEXT_PACKAGE);
+
+  context = g_option_context_new (" - GNOME Session Preferences");
+
+  gnome_program_init ("gnome-session-properties", VERSION, 
+		      LIBGNOMEUI_MODULE, argc, argv, 
+		      GNOME_PARAM_GOPTION_CONTEXT, context,
+		      NULL);
+
+  /* Don't restart the capplet, it's the place where the session is saved. */
+  gnome_client_set_restart_style (gnome_master_client (), GNOME_RESTART_NEVER);
+
+  gtk_window_set_default_icon_name ("session-properties");
+
+  client = gconf_client_get_default ();
+
+  gconf_client_add_dir (client, 
+                        SPC_GCONF_CONFIG_PREFIX, 
+                        GCONF_CLIENT_PRELOAD_ONELEVEL, 
+                        NULL);
+
+  master_client = gnome_master_client ();
+
+  if (!GNOME_CLIENT_CONNECTED (master_client)) 
+    {
+      g_printerr (_("Could not connect to the session manager\n"));
+      return 1;
+    }
+
+  g_signal_connect (master_client, 
+                    "die", 
+                    G_CALLBACK (gtk_main_quit), 
+                    NULL);
+
+  spc_ui_build (client);
+
+  gtk_main ();
+
+  return 0;
+}

Added: trunk/capplet/ui.c
==============================================================================
--- (empty file)
+++ trunk/capplet/ui.c	Sat May 10 18:47:24 2008
@@ -0,0 +1,619 @@
+#include <glib/gi18n.h>
+
+#include <string.h>
+#include <gconf/gconf-client.h>
+#include <libgnomeui/gnome-help.h>
+#include <glade/glade.h>
+
+#include "eggdesktopfile.h"
+#include "util.h"
+#include "ui.h"
+#include "commands.h"
+
+#define CAPPLET_DIALOG_WIDGET_NAME        "session_properties_capplet"
+#define CAPPLET_TREEVIEW_WIDGET_NAME      "session_properties_treeview"
+#define CAPPLET_CLOSE_WIDGET_NAME         "session_properties_close_button"
+#define CAPPLET_HELP_WIDGET_NAME          "session_properties_help_button"
+#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 CAPPLET_EDIT_DIALOG_WIDGET_NAME   "session_properties_edit_dialog"
+#define CAPPLET_NAME_ENTRY_WIDGET_NAME    "session_properties_name_entry"
+#define CAPPLET_COMMAND_ENTRY_WIDGET_NAME "spc_command_entry"
+#define CAPPLET_COMMENT_ENTRY_WIDGET_NAME "session_properties_comment_entry"
+#define CAPPLET_BROWSE_WIDGET_NAME        "session_properties_browse_button"
+
+static char *
+make_exec_uri (const char *exec)
+{
+  GString    *str;
+  const char *c;
+
+  if (!exec)
+    return g_strdup ("");
+
+  if (!strchr (exec, ' '))
+    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
+cmd_browse_button_clicked_cb (GladeXML *xml)
+{
+  GtkWidget *chooser;
+  GtkWidget *dlg;
+  GtkWidget *cmd_entry;
+  int response;
+
+  dlg = glade_xml_get_widget (xml, CAPPLET_EDIT_DIALOG_WIDGET_NAME);
+
+  cmd_entry = glade_xml_get_widget (xml, CAPPLET_COMMAND_ENTRY_WIDGET_NAME);
+
+  chooser = gtk_file_chooser_dialog_new ("", GTK_WINDOW (dlg),
+                                         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 (dlg));
+
+  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);
+
+      g_assert (cmd_entry != NULL);
+
+      gtk_entry_set_text (GTK_ENTRY (cmd_entry), uri);
+
+      g_free (uri);
+    }
+
+  gtk_widget_destroy (chooser);
+}
+
+static void
+entry_activate_cb (GtkEntry *entry, void *data)
+{
+  gtk_dialog_response (GTK_DIALOG (data), GTK_RESPONSE_OK);
+}
+
+static gboolean
+edit_app_dialog (const char *title, 
+                 GtkListStore *store, 
+                 GtkTreeIter *iter, 
+                 GtkWidget *parent)
+{
+  GladeXML *xml;
+  GtkWidget *dlg;
+  GtkWidget *browse_button;
+  GtkWidget *name_entry;
+  GtkWidget *cmd_entry;
+  GtkWidget *comment_entry;
+  char *c_name, *c_command, *c_comment;
+
+  gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
+                      STORE_COL_NAME, &c_name,
+                      STORE_COL_COMMAND, &c_command,
+                      STORE_COL_COMMENT, &c_comment,
+                      -1);
+
+  /* FIXME: ugly to read same xml file twice */
+  xml = glade_xml_new (DATA_DIR "/session-properties.glade",
+                       CAPPLET_EDIT_DIALOG_WIDGET_NAME, NULL);
+
+  dlg = glade_xml_get_widget (xml, CAPPLET_EDIT_DIALOG_WIDGET_NAME);
+
+  name_entry = glade_xml_get_widget (xml, CAPPLET_NAME_ENTRY_WIDGET_NAME);
+
+  g_signal_connect (name_entry, "activate",
+                    G_CALLBACK (entry_activate_cb),
+                    dlg);
+
+  if (c_name)
+     gtk_entry_set_text (GTK_ENTRY (name_entry), c_name);
+
+  browse_button = glade_xml_get_widget (xml, CAPPLET_BROWSE_WIDGET_NAME);
+
+  g_signal_connect_swapped (browse_button, "clicked",
+                            G_CALLBACK (cmd_browse_button_clicked_cb),
+                            xml);
+
+  cmd_entry = glade_xml_get_widget (xml, CAPPLET_COMMAND_ENTRY_WIDGET_NAME);
+
+  g_signal_connect (cmd_entry, "activate",
+                    G_CALLBACK (entry_activate_cb),
+                    dlg);
+
+  if (c_command)
+     gtk_entry_set_text (GTK_ENTRY (cmd_entry), c_command);
+
+  comment_entry = glade_xml_get_widget (xml, CAPPLET_COMMENT_ENTRY_WIDGET_NAME);
+
+  g_signal_connect (comment_entry, "activate",
+                    G_CALLBACK (entry_activate_cb),
+                    dlg);
+
+  if (c_comment)
+     gtk_entry_set_text (GTK_ENTRY (comment_entry), c_comment);
+
+  gtk_window_set_title (GTK_WINDOW (dlg), title);
+
+  gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (parent));
+
+  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 = gtk_entry_get_text (GTK_ENTRY (name_entry));
+      const char *command = gtk_entry_get_text (GTK_ENTRY (cmd_entry));
+      const char *comment = gtk_entry_get_text (GTK_ENTRY (comment_entry));
+      const char *error_msg = NULL;
+      char **argv;
+      char *description;
+      int argc;
+      GError *error = 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");
+
+      if (!g_shell_parse_argv (command, &argc, &argv, &error))
+        {
+          if (error)
+            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_OK,
+                                           error_msg);
+
+          if (error)
+            g_error_free (error);
+
+          gtk_dialog_run (GTK_DIALOG (msgbox));
+
+          gtk_widget_destroy (msgbox);
+        }
+      else
+        {
+          g_strfreev (argv);
+
+          description = 
+            spc_command_get_app_description (name, comment);
+
+          gtk_list_store_set (GTK_LIST_STORE (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
+selection_changed_cb (GtkTreeSelection *selection, GladeXML *xml)
+{
+  GtkWidget *edit_button;
+  GtkWidget *delete_button;
+  gboolean sel;
+
+  edit_button = glade_xml_get_widget (xml, CAPPLET_EDIT_WIDGET_NAME);
+  delete_button = glade_xml_get_widget (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 void
+startup_enabled_toggled_cb (GtkCellRendererToggle *cell_renderer,
+                            gchar                 *path,
+                            GtkListStore          *store)
+{
+  GtkTreeIter iter;
+  gchar *desktop_file_path;
+  gboolean active;
+
+  if (!gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (store),
+                                            &iter, path))
+      return;
+
+  gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
+      		      STORE_COL_DESKTOP_FILE, &desktop_file_path,
+      		      -1);
+
+  active = gtk_cell_renderer_toggle_get_active (cell_renderer);
+  active = !active;
+  gtk_cell_renderer_toggle_set_active (cell_renderer, active);
+
+  if (active)
+    {
+      if (spc_command_enable_app (store, &iter))
+        gtk_list_store_set (store, &iter,
+                            STORE_COL_ENABLED, TRUE,
+                            -1);
+    }
+  else
+    {
+      if (spc_command_disable_app (store, &iter))
+        gtk_list_store_set (store, &iter,
+                            STORE_COL_ENABLED, FALSE,
+                            -1);
+    }
+}
+
+static void
+setup_treeview (GtkTreeView *treeview, GladeXML *xml)
+{
+  GtkTreeModel *store;
+  GtkCellRenderer *renderer;
+  GtkTreeSelection *selection;
+  GtkTreeViewColumn *column;
+
+  store = spc_command_get_store ();
+  gtk_tree_view_set_model (treeview, store);
+  gtk_tree_view_set_headers_visible (treeview, FALSE);
+
+  selection = gtk_tree_view_get_selection (treeview);
+
+  gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
+
+  g_signal_connect (selection, 
+                    "changed",
+                    G_CALLBACK (selection_changed_cb), 
+                    xml);
+
+  renderer = gtk_cell_renderer_toggle_new ();
+
+  column = gtk_tree_view_column_new_with_attributes (_("Enabled"), renderer,
+                                                     "active", STORE_COL_ENABLED,
+                                                     "activatable", STORE_COL_ACTIVATABLE,
+                                                     NULL);
+
+  gtk_tree_view_append_column (treeview, column);
+
+  g_signal_connect (renderer, 
+                    "toggled",
+                    G_CALLBACK (startup_enabled_toggled_cb), 
+                    store);
+
+  renderer = gtk_cell_renderer_pixbuf_new ();
+
+  column = gtk_tree_view_column_new_with_attributes (_("Icon"), renderer,
+                                                     "icon-name", STORE_COL_ICON_NAME,
+						     NULL);
+
+  g_object_set (renderer, 
+                "stock-size", GTK_ICON_SIZE_LARGE_TOOLBAR, 
+                NULL);
+
+  gtk_tree_view_append_column (treeview, column);
+
+  renderer = gtk_cell_renderer_text_new ();
+
+  column = gtk_tree_view_column_new_with_attributes (_("Program"), renderer,
+                                                     "markup", STORE_COL_DESCRIPTION,
+						     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_NAME);
+  gtk_tree_view_set_search_column (treeview, STORE_COL_NAME);
+  gtk_tree_view_set_rules_hint (treeview, TRUE);
+}
+
+static void
+add_app_cb (GtkWidget *widget,
+            GladeXML *xml)
+{
+  GtkWidget *parent;
+  GtkTreeView *view;
+  GtkTreeSelection *selection;
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+
+  parent = glade_xml_get_widget (xml, CAPPLET_DIALOG_WIDGET_NAME);
+ 
+  view = GTK_TREE_VIEW (glade_xml_get_widget (xml, 
+                        CAPPLET_TREEVIEW_WIDGET_NAME));
+
+  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 (_("Add Startup Program"), GTK_LIST_STORE (model), &iter, parent))
+    spc_command_add_app (GTK_LIST_STORE (model), &iter);  
+}
+
+static void
+delete_app_cb (GtkWidget *widget,
+               GladeXML *xml)
+{
+  GtkTreeView *view;
+  GtkTreeSelection *selection;
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+
+  view = GTK_TREE_VIEW (glade_xml_get_widget (xml, 
+                        CAPPLET_TREEVIEW_WIDGET_NAME));
+
+  selection = gtk_tree_view_get_selection (view);
+  model = gtk_tree_view_get_model (view);
+
+  if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) 
+    return;
+
+  spc_command_delete_app (GTK_LIST_STORE (model), &iter);  
+
+  gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+}
+
+static void
+edit_app_cb (GtkWidget *widget,
+             GladeXML *xml)
+{
+  GtkWidget *parent;
+  GtkTreeView *view;
+  GtkTreeSelection *selection;
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+
+  parent = glade_xml_get_widget (xml, CAPPLET_DIALOG_WIDGET_NAME);
+ 
+  view = GTK_TREE_VIEW (glade_xml_get_widget (xml, 
+                        CAPPLET_TREEVIEW_WIDGET_NAME));
+
+  selection = gtk_tree_view_get_selection (view);
+  model = gtk_tree_view_get_model (view);
+
+  if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) 
+    return;
+
+  if (edit_app_dialog (_("Edit Startup Program"), GTK_LIST_STORE (model), &iter, parent))
+    spc_command_update_app (GTK_LIST_STORE (model), &iter);  
+}
+
+static void
+autosave_value_notify (GConfClient *client, guint id, 
+                       GConfEntry *entry, gpointer tb)
+{
+  gboolean gval, bval;
+ 
+  gval = gconf_value_get_bool (entry->value);
+  bval = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (tb));
+ 
+  if (bval != gval) 
+    {
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tb), gval);
+    }
+}
+
+static void
+autosave_value_toggled (GtkToggleButton *tb, gpointer data)
+{
+  GConfClient *client;
+  gboolean gval, bval;
+  const gchar *key;
+
+  client = (GConfClient *) data;
+
+  key = g_object_get_data (G_OBJECT (tb), "key");
+
+  gval = gconf_client_get_bool (client, key, NULL);
+  bval = gtk_toggle_button_get_active (tb);
+
+  if (gval != bval) 
+    {
+      gconf_client_set_bool (client, key, bval, NULL);
+    }
+}
+
+static void
+save_session_cb (GtkWidget *widget,
+                 GladeXML *xml)
+{
+  g_debug ("Session saving is not implemented yet!");
+}
+
+static void
+help_cb (GtkWidget *widget,
+         GtkWidget *dialog)
+{
+  GError *error = NULL;
+
+  gnome_help_display_desktop (NULL, "user-guide", "user-guide.xml",
+                              "goscustsession-5", &error);
+
+  if (error) 
+    {
+      GtkWidget *error_dlg;
+
+      error_dlg = 
+        gtk_message_dialog_new (GTK_WINDOW (dialog),
+                                GTK_DIALOG_MODAL,
+                                GTK_MESSAGE_ERROR,
+                                GTK_BUTTONS_CLOSE,
+                                ("There was an error displaying help: \n%s"),
+                                error->message);
+
+      g_signal_connect (G_OBJECT (error_dlg), "response",
+                        G_CALLBACK (gtk_widget_destroy),
+                        NULL);
+
+      gtk_window_set_resizable (GTK_WINDOW (error_dlg), FALSE);
+      gtk_widget_show (error_dlg);
+
+      g_error_free (error);
+    }
+}
+
+static gboolean
+delete_cb (GtkWidget *dialog)
+{
+  gtk_main_quit ();
+  return FALSE;
+}
+
+static void
+close_cb (GtkWidget *widget,
+          GtkWidget *dialog)
+{
+  delete_cb (dialog);
+}
+
+void
+spc_ui_build (GConfClient *client)
+{
+  GladeXML *xml;
+  GtkWidget *dlg;
+  GtkWidget *treeview;
+  GtkWidget *button;
+
+  xml = glade_xml_new (DATA_DIR "/session-properties.glade",
+                       CAPPLET_DIALOG_WIDGET_NAME, NULL);
+
+  dlg = glade_xml_get_widget (xml, CAPPLET_DIALOG_WIDGET_NAME);
+
+  g_signal_connect (dlg, 
+                    "delete-event", 
+                    G_CALLBACK (delete_cb), 
+                    NULL);
+
+  treeview = glade_xml_get_widget (xml, CAPPLET_TREEVIEW_WIDGET_NAME);
+
+  setup_treeview (GTK_TREE_VIEW (treeview), xml);
+
+  button = glade_xml_get_widget (xml, CAPPLET_CLOSE_WIDGET_NAME);
+
+  gtk_dialog_set_default_response (GTK_DIALOG (dlg), GTK_RESPONSE_CLOSE);
+
+  g_signal_connect (G_OBJECT (button), 
+                    "clicked", 
+                    G_CALLBACK (close_cb), 
+                    dlg);
+
+  button = glade_xml_get_widget (xml, CAPPLET_HELP_WIDGET_NAME);
+
+  g_signal_connect (G_OBJECT (button), 
+                    "clicked", 
+                    G_CALLBACK (help_cb), 
+                    dlg);
+
+  button = glade_xml_get_widget (xml, CAPPLET_ADD_WIDGET_NAME);
+
+  g_signal_connect (G_OBJECT (button), 
+                    "clicked", 
+                    G_CALLBACK (add_app_cb), 
+                    xml);
+
+  button = glade_xml_get_widget (xml, CAPPLET_DELETE_WIDGET_NAME);
+
+  g_signal_connect (G_OBJECT (button), 
+                    "clicked", 
+                    G_CALLBACK (delete_app_cb), 
+                    xml);
+
+  button = glade_xml_get_widget (xml, CAPPLET_EDIT_WIDGET_NAME);
+
+  g_signal_connect (G_OBJECT (button), 
+                    "clicked", 
+                    G_CALLBACK (edit_app_cb), 
+                    xml);
+
+  button = glade_xml_get_widget (xml, CAPPLET_REMEMBER_WIDGET_NAME);
+
+  g_object_set_data (G_OBJECT (button), "key", SPC_GCONF_AUTOSAVE_KEY);
+
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+                                gconf_client_get_bool (client, SPC_GCONF_AUTOSAVE_KEY, NULL));
+
+  gconf_client_notify_add (client, SPC_GCONF_AUTOSAVE_KEY, 
+                           autosave_value_notify, 
+                           button, NULL, NULL);
+
+  g_signal_connect (G_OBJECT (button), 
+                    "toggled", 
+                    G_CALLBACK (autosave_value_toggled), 
+                    client);
+
+  button = glade_xml_get_widget (xml, CAPPLET_SAVE_WIDGET_NAME);
+
+  g_signal_connect (G_OBJECT (button), 
+                    "clicked", 
+                    G_CALLBACK (save_session_cb), 
+                    xml);
+
+  gtk_widget_show (dlg);
+}

Added: trunk/capplet/ui.h
==============================================================================
--- (empty file)
+++ trunk/capplet/ui.h	Sat May 10 18:47:24 2008
@@ -0,0 +1,17 @@
+#ifndef __SESSION_PROPERTIES_CAPPLET_H__ 
+#define __SESSION_PROPERTIES_CAPPLET_H__
+
+#include <gtk/gtk.h>
+
+#include <gconf/gconf-client.h>
+
+G_BEGIN_DECLS
+
+#define SPC_GCONF_CONFIG_PREFIX   "/apps/gnome-session/options"
+#define SPC_GCONF_AUTOSAVE_KEY    SPC_GCONF_CONFIG_PREFIX "/auto_save_session" 
+
+void   spc_ui_build   (GConfClient *client);
+
+G_END_DECLS
+
+#endif /* __SESSION_PROPERTIES_CAPPLET_H__ */

Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in	(original)
+++ trunk/configure.in	Sat May 10 18:47:24 2008
@@ -54,6 +54,7 @@
 GLIB_REQUIRED=2.16.0
 LIBGNOMEUI_REQUIRED=2.2.0
 GTK_REQUIRED=2.11.1
+GLADE_REQUIRED=2.6.3
 DBUS_GLIB_REQUIRED=0.35
 GNOME_KEYRING_REQUIRED=2.21.92
 
@@ -64,6 +65,8 @@
 
 PKG_CHECK_MODULES(GNOME_SESSION, glib-2.0 >= $GLIB_REQUIRED gio-2.0 >= $GLIB_REQUIRED gtk+-2.0 >= $GTK_REQUIRED libgnomeui-2.0 >= $LIBGNOMEUI_REQUIRED dbus-glib-1 >= $DBUS_GLIB_REQUIRED gnome-keyring-1 >= $GNOME_KEYRING_REQUIRED)
 
+PKG_CHECK_MODULES(SESSION_PROPERTIES, glib-2.0 >= $GLIB_REQUIRED gtk+-2.0 >= $GTK_REQUIRED libgnomeui-2.0 >= $LIBGNOMEUI_REQUIRED libglade-2.0 >= $GLADE_REQUIRED)
+
 PKG_CHECK_MODULES(GTK, gtk+-2.0 >= $GTK_REQUIRED)
 PKG_CHECK_MODULES(DBUS_GLIB, dbus-glib-1 >= $DBUS_GLIB_REQUIRED)
 PKG_CHECK_MODULES(GCONF, gconf-2.0)
@@ -244,6 +247,7 @@
 AC_OUTPUT([
 Makefile
 compat/Makefile
+capplet/Makefile
 gnome-session/Makefile
 egg/Makefile
 splash/Makefile

Modified: trunk/data/Makefile.am
==============================================================================
--- trunk/data/Makefile.am	(original)
+++ trunk/data/Makefile.am	Sat May 10 18:47:24 2008
@@ -1,5 +1,8 @@
 SUBDIRS = icons
 
+gladedir = $(datadir)/gnome-session
+glade_DATA = session-properties.glade 
+
 @INTLTOOL_DESKTOP_RULE@
 
 xsessiondir = $(datadir)/xsessions

Added: trunk/data/session-properties.glade
==============================================================================
--- (empty file)
+++ trunk/data/session-properties.glade	Sat May 10 18:47:24 2008
@@ -0,0 +1,419 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--Generated with glade3 3.4.2 on Sat May 10 20:27:19 2008 -->
+<glade-interface>
+  <widget class="GtkDialog" id="session_properties_capplet">
+    <property name="border_width">10</property>
+    <property name="title" translatable="yes">Sessions Preferences</property>
+    <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+    <property name="icon_name">session-properties</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="spacing">2</property>
+        <child>
+          <widget class="GtkNotebook" id="notebook2">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <child>
+              <widget class="GtkVBox" id="vbox1">
+                <property name="visible">True</property>
+                <property name="border_width">12</property>
+                <property name="spacing">3</property>
+                <child>
+                  <widget class="GtkLabel" id="label6">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="xpad">3</property>
+                    <property name="ypad">3</property>
+                    <property name="label" translatable="yes">Additional startup _programs:</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">session_properties_treeview</property>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkHBox" id="hbox1">
+                    <property name="visible">True</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <widget class="GtkScrolledWindow" id="scrolledwindow1">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+                        <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                        <child>
+                          <widget class="GtkTreeView" id="session_properties_treeview">
+                            <property name="height_request">210</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkVBox" id="vbox2">
+                        <property name="visible">True</property>
+                        <property name="spacing">5</property>
+                        <child>
+                          <widget class="GtkButton" id="session_properties_add_button">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">True</property>
+                            <property name="label" translatable="yes">gtk-add</property>
+                            <property name="use_stock">True</property>
+                            <property name="response_id">0</property>
+                          </widget>
+                          <packing>
+                            <property name="expand">False</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkButton" id="session_properties_delete_button">
+                            <property name="visible">True</property>
+                            <property name="sensitive">False</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">True</property>
+                            <property name="label" translatable="yes">gtk-remove</property>
+                            <property name="use_stock">True</property>
+                            <property name="response_id">0</property>
+                          </widget>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkButton" id="session_properties_edit_button">
+                            <property name="visible">True</property>
+                            <property name="sensitive">False</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">True</property>
+                            <property name="label" translatable="yes">gtk-edit</property>
+                            <property name="use_stock">True</property>
+                            <property name="response_id">0</property>
+                          </widget>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <placeholder/>
+                        </child>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label4">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Startup Programs</property>
+              </widget>
+              <packing>
+                <property name="type">tab</property>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkVBox" id="vbox3">
+                <property name="visible">True</property>
+                <property name="border_width">12</property>
+                <property name="spacing">4</property>
+                <child>
+                  <widget class="GtkCheckButton" id="session_properties_remember_toggle">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="label" translatable="yes">_Automatically remember running applications when logging out</property>
+                    <property name="use_underline">True</property>
+                    <property name="response_id">0</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkHBox" id="hbox2">
+                    <property name="visible">True</property>
+                    <property name="homogeneous">True</property>
+                    <child>
+                      <widget class="GtkButton" id="session_properties_save_button">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">True</property>
+                        <property name="response_id">0</property>
+                        <child>
+                          <widget class="GtkHBox" id="hbox2">
+                            <property name="visible">True</property>
+                            <property name="spacing">4</property>
+                            <child>
+                              <widget class="GtkImage" id="image1">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-save</property>
+                              </widget>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkLabel" id="label7">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">_Remember Currently Running Application</property>
+                                <property name="use_underline">True</property>
+                              </widget>
+                              <packing>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </widget>
+                        </child>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label5">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Options</property>
+              </widget>
+              <packing>
+                <property name="type">tab</property>
+                <property name="position">1</property>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area1">
+            <property name="visible">True</property>
+            <property name="layout_style">GTK_BUTTONBOX_END</property>
+            <child>
+              <widget class="GtkButton" id="session_properties_help_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="label" translatable="yes">gtk-help</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">0</property>
+              </widget>
+              <packing>
+                <property name="secondary">True</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkButton" id="session_properties_close_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="label" translatable="yes">gtk-close</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">0</property>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkDialog" id="session_properties_edit_dialog">
+    <property name="border_width">5</property>
+    <property name="modal">True</property>
+    <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox2">
+        <property name="visible">True</property>
+        <property name="spacing">2</property>
+        <child>
+          <widget class="GtkTable" id="table1">
+            <property name="visible">True</property>
+            <property name="border_width">5</property>
+            <property name="n_rows">3</property>
+            <property name="n_columns">2</property>
+            <property name="column_spacing">12</property>
+            <property name="row_spacing">6</property>
+            <child>
+              <widget class="GtkLabel" id="label1">
+                <property name="visible">True</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">_Name:</property>
+                <property name="use_underline">True</property>
+                <property name="mnemonic_widget">session_properties_name_entry</property>
+              </widget>
+              <packing>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options">GTK_FILL</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label2">
+                <property name="visible">True</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Co_mmand:</property>
+                <property name="use_underline">True</property>
+                <property name="mnemonic_widget">session_properties_command_entry</property>
+              </widget>
+              <packing>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options">GTK_FILL</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label3">
+                <property name="visible">True</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Comm_ent:</property>
+                <property name="use_underline">True</property>
+                <property name="mnemonic_widget">label2</property>
+              </widget>
+              <packing>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options">GTK_FILL</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkEntry" id="session_properties_name_entry">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+              </widget>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="y_options">GTK_FILL</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkEntry" id="session_properties_comment_entry">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+              </widget>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
+                <property name="y_options">GTK_FILL</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkHBox" id="hbox3">
+                <property name="visible">True</property>
+                <property name="spacing">12</property>
+                <child>
+                  <widget class="GtkEntry" id="session_properties_command_entry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkButton" id="session_properties_browse_button">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="label" translatable="yes">Browse...</property>
+                    <property name="response_id">0</property>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+                <property name="y_options">GTK_FILL</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area2">
+            <property name="visible">True</property>
+            <property name="layout_style">GTK_BUTTONBOX_END</property>
+            <child>
+              <widget class="GtkButton" id="cancel_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="label" translatable="yes">gtk-cancel</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">-6</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkButton" id="ok_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="label" translatable="yes">gtk-ok</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">-5</property>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+</glade-interface>

Modified: trunk/gnome-session/Makefile.am
==============================================================================
--- trunk/gnome-session/Makefile.am	(original)
+++ trunk/gnome-session/Makefile.am	Sat May 10 18:47:24 2008
@@ -1,3 +1,7 @@
+bin_PROGRAMS = gnome-session
+
+noinst_LTLIBRARIES = libgsmutil.la
+
 INCLUDES =					\
 	$(WARN_CFLAGS)				\
 	$(GNOME_SESSION_CFLAGS)			\
@@ -6,20 +10,18 @@
 	$(GTK_CFLAGS)				\
 	-I$(top_srcdir)/egg			\
 	-DLOCALE_DIR=\""$(datadir)/locale"\"	\
+	-DDATA_DIR=\""$(datadir)/gnome-session"\" \
 	-DDBUS_LAUNCH=\"dbus-launch\"		\
 	-DGCONF_SANITY_CHECK=\""$(GCONF_SANITY_CHECK)"\" \
 	-DGCONFTOOL_CMD=\"$(GCONFTOOL)\" 
 
-LDADD =						\
+gnome_session_LDADD =				\
 	-lSM -lICE				\
+	libgsmutil.la 				\
 	$(top_builddir)/egg/libeggdesktopfile.la \
 	$(GNOME_SESSION_LIBS)			\
 	$(DBUS_GLIB_LIBS)			\
-	$(GCONF_LIBS)				\
-	$(GTK_LIBS)
-
-bin_PROGRAMS =					\
-	gnome-session
+	$(GCONF_LIBS)
 
 gnome_session_SOURCES =				\
 	app-autostart.c				\
@@ -49,6 +51,13 @@
 	xsmp.c					\
 	xsmp.h
 
+libgsmutil_la_LIBADD = 				\
+	$(GNOME_SESSION_LIBS)
+
+libgsmutil_la_SOURCES =				\
+	util.c					\
+	util.h
+
 dbus.o dbus.lo: dbus-glue.h
 
 dbus-glue.h: org.gnome.SessionManagement.xml

Modified: trunk/gnome-session/dbus.c
==============================================================================
--- trunk/gnome-session/dbus.c	(original)
+++ trunk/gnome-session/dbus.c	Sat May 10 18:47:24 2008
@@ -77,7 +77,7 @@
   if (error)
     {
       gsm_initialization_error (TRUE, "Could not start dbus-daemon: %s",
-				error->message);
+	  			error->message);
       /* not reached */
     }
 

Modified: trunk/gnome-session/gconf.c
==============================================================================
--- trunk/gnome-session/gconf.c	(original)
+++ trunk/gnome-session/gconf.c	Sat May 10 18:47:24 2008
@@ -30,6 +30,7 @@
 
 #include "gconf.h"
 #include "gsm.h"
+#include "util.h"
 
 static pid_t gsc_pid;
 static GConfClient *client = NULL;

Modified: trunk/gnome-session/gsm.h
==============================================================================
--- trunk/gnome-session/gsm.h	(original)
+++ trunk/gnome-session/gsm.h	Sat May 10 18:47:24 2008
@@ -1,9 +1,9 @@
 #include <glib.h>
 #include "session.h"
 
-void gsm_initialization_error (gboolean fatal, const char *format, ...);
-
 #define GSM_GCONF_DEFAULT_SESSION_KEY           "/desktop/gnome/session/default-session"
 #define GSM_GCONF_REQUIRED_COMPONENTS_DIRECTORY "/desktop/gnome/session/required-components"
 
+void gsm_initialization_error (gboolean fatal, const char *format, ...);
+
 extern GsmSession *global_session;

Modified: trunk/gnome-session/main.c
==============================================================================
--- trunk/gnome-session/main.c	(original)
+++ trunk/gnome-session/main.c	Sat May 10 18:47:24 2008
@@ -22,6 +22,7 @@
 #include "gconf.h"
 #include "gsm.h"
 #include "session.h"
+#include "util.h"
 #include "xsmp.h"
 
 GsmSession *global_session;

Modified: trunk/gnome-session/session.c
==============================================================================
--- trunk/gnome-session/session.c	(original)
+++ trunk/gnome-session/session.c	Sat May 10 18:47:24 2008
@@ -31,6 +31,7 @@
 #include "gsm.h"
 #include "session.h"
 #include "xsmp.h"
+#include "util.h"
 
 static void append_default_apps       (GsmSession *session,
                                        char **autostart_dirs);
@@ -64,9 +65,6 @@
 static void client_disconnected        (GsmClient *client,
 					gpointer   data);
 
-static char **get_autostart_dirs       (void);
-static char **get_app_dirs             (void);
-
 struct _GsmSession {
   /* Startup/resumed apps */
   GSList *apps;
@@ -127,7 +125,7 @@
 
   session->apps_by_name = g_hash_table_new (g_str_hash, g_str_equal);
 
-  autostart_dirs = get_autostart_dirs ();
+  autostart_dirs = gsm_util_get_autostart_dirs ();
 
   append_default_apps (session, autostart_dirs);
 
@@ -168,71 +166,6 @@
   g_hash_table_insert (session->apps_by_name, g_strdup (basename), app);
 }
 
-static char **
-get_autostart_dirs ()
-{
-  GPtrArray *dirs;
-  const char * const *system_config_dirs;
-  const char * const *system_data_dirs;
-  gint i;
-
-  dirs = g_ptr_array_new ();
-
-  system_data_dirs = g_get_system_data_dirs ();
-  for (i = 0; system_data_dirs[i]; i++)
-    {
-      g_ptr_array_add (dirs, 
-                       g_build_filename (system_data_dirs[i], 
-                                         "gnome", "autostart", NULL));
-
-      g_ptr_array_add (dirs, 
-                       g_build_filename (system_data_dirs[i], 
-                                         "autostart", NULL));
-    }
-
-  system_config_dirs = g_get_system_config_dirs ();
-  for (i = 0; system_config_dirs[i]; i++)
-    {
-      g_ptr_array_add (dirs, 
-                       g_build_filename (system_config_dirs[i], 
-                                         "autostart", NULL));
-    }
-
-  g_ptr_array_add (dirs, 
-                   g_build_filename (g_get_user_config_dir (), 
-                                     "autostart", NULL));
-
-  g_ptr_array_add (dirs, NULL);
-
-  return (char **) g_ptr_array_free (dirs, FALSE);
-}
-
-static char **
-get_app_dirs ()
-{
-  GPtrArray *dirs;
-  const char * const *system_data_dirs;
-  gint i;
-
-  dirs = g_ptr_array_new ();
-
-  system_data_dirs = g_get_system_data_dirs ();
-  for (i = 0; system_data_dirs[i]; i++)
-    {
-      g_ptr_array_add (dirs, 
-                       g_build_filename (system_data_dirs[i], "applications", 
-                                         NULL));
-
-      g_ptr_array_add (dirs, 
-                       g_build_filename (system_data_dirs[i], "gnome", "wm-properties", 
-                                         NULL));
-    }
-
-  g_ptr_array_add (dirs, NULL);
-
-  return (char **) g_ptr_array_free (dirs, FALSE);
-}
-
 static void
 append_default_apps (GsmSession *session, char **autostart_dirs)
 {
@@ -241,7 +174,7 @@
 
   g_debug ("append_default_apps ()");
 
-  app_dirs = get_app_dirs (); 
+  app_dirs = gsm_util_get_app_dirs (); 
 
   default_apps = 
     gconf_client_get_list (gsm_gconf_get_client (),

Added: trunk/gnome-session/util.c
==============================================================================
--- (empty file)
+++ trunk/gnome-session/util.c	Sat May 10 18:47:24 2008
@@ -0,0 +1,111 @@
+/* util.h
+ * 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 <stdlib.h>
+#include <ctype.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/goption.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtkmessagedialog.h>
+
+#include "util.h"
+
+gchar **
+gsm_util_get_autostart_dirs ()
+{
+  GPtrArray *dirs;
+  const char * const *system_config_dirs;
+  const char * const *system_data_dirs;
+  gint i;
+
+  dirs = g_ptr_array_new ();
+
+  g_ptr_array_add (dirs, 
+                   g_build_filename (g_get_user_config_dir (), 
+                                     "autostart", NULL));
+
+  system_data_dirs = g_get_system_data_dirs ();
+  for (i = 0; system_data_dirs[i]; i++)
+    {
+      g_ptr_array_add (dirs, 
+                       g_build_filename (system_data_dirs[i], 
+                                         "gnome", "autostart", NULL));
+
+      g_ptr_array_add (dirs, 
+                       g_build_filename (system_data_dirs[i], 
+                                         "autostart", NULL));
+    }
+
+  system_config_dirs = g_get_system_config_dirs ();
+  for (i = 0; system_config_dirs[i]; i++)
+    {
+      g_ptr_array_add (dirs, 
+                       g_build_filename (system_config_dirs[i], 
+                                         "autostart", NULL));
+    }
+
+  g_ptr_array_add (dirs, NULL);
+
+  return (char **) g_ptr_array_free (dirs, FALSE);
+}
+
+gchar **
+gsm_util_get_app_dirs ()
+{
+  GPtrArray *dirs;
+  const char * const *system_data_dirs;
+  gint i;
+
+  dirs = g_ptr_array_new ();
+
+  system_data_dirs = g_get_system_data_dirs ();
+  for (i = 0; system_data_dirs[i]; i++)
+    {
+      g_ptr_array_add (dirs, 
+                       g_build_filename (system_data_dirs[i], "applications", 
+                                         NULL));
+
+      g_ptr_array_add (dirs, 
+                       g_build_filename (system_data_dirs[i], "gnome", "wm-properties", 
+                                         NULL));
+    }
+
+  g_ptr_array_add (dirs, NULL);
+
+  return (char **) g_ptr_array_free (dirs, FALSE);
+}
+
+gboolean
+gsm_util_text_is_blank (const gchar *str)
+{
+  if (str == NULL)
+    return TRUE;
+
+  while (*str) 
+    {
+      if (!isspace(*str))
+        return FALSE;
+
+      str++;
+    }
+
+  return TRUE;
+}

Added: trunk/gnome-session/util.h
==============================================================================
--- (empty file)
+++ trunk/gnome-session/util.h	Sat May 10 18:47:24 2008
@@ -0,0 +1,33 @@
+/* util.h
+ * 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.
+ */
+
+#ifndef __GSM_UTIL_H__ 
+#define __GSM_UTIL_H__
+
+G_BEGIN_DECLS
+
+gchar**   gsm_util_get_app_dirs         (void);
+
+gchar**   gsm_util_get_autostart_dirs   (void);
+
+gboolean  gsm_util_text_is_blank        (const char *str);
+
+G_END_DECLS
+
+#endif /* __GSM_UTIL_H__ */

Modified: trunk/gnome-session/xsmp.c
==============================================================================
--- trunk/gnome-session/xsmp.c	(original)
+++ trunk/gnome-session/xsmp.c	Sat May 10 18:47:24 2008
@@ -34,6 +34,7 @@
 
 #include "client-xsmp.h"
 #include "gsm.h"
+#include "util.h"
 #include "xsmp.h"
 
 #include <X11/ICE/ICElib.h>

Modified: trunk/po/POTFILES.in
==============================================================================
--- trunk/po/POTFILES.in	(original)
+++ trunk/po/POTFILES.in	Sat May 10 18:47:24 2008
@@ -4,6 +4,7 @@
 data/gnome-session.schemas.in
 data/gnome.desktop.in
 data/session-properties.desktop.in.in
+data/session-properties.glade
 egg/eggdesktopfile.c
 egg/eggsmclient-libgnomeui.c
 egg/eggsmclient.c
@@ -11,6 +12,8 @@
 gnome-session/logout-dialog.c
 gnome-session/main.c
 gnome-session/xsmp.c
+capplet/ui.c
+capplet/commands.c
 splash/gnome-login-sound.c
 splash/gnome-session-splash.c
 



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