summaryrefslogtreecommitdiff
path: root/src/terminal-app.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/terminal-app.c')
-rw-r--r--src/terminal-app.c2116
1 files changed, 2116 insertions, 0 deletions
diff --git a/src/terminal-app.c b/src/terminal-app.c
new file mode 100644
index 0000000..aa3ede7
--- /dev/null
+++ b/src/terminal-app.c
@@ -0,0 +1,2116 @@
+/*
+ * Copyright © 2001, 2002 Havoc Pennington
+ * Copyright © 2002 Red Hat, Inc.
+ * Copyright © 2002 Sun Microsystems
+ * Copyright © 2003 Mariano Suarez-Alvarez
+ * Copyright © 2008 Christian Persch
+ *
+ * Mate-terminal 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mate-terminal 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <errno.h>
+
+#include <glib.h>
+
+#include "terminal-intl.h"
+
+#include "terminal-debug.h"
+#include "terminal-app.h"
+#include "terminal-accels.h"
+#include "terminal-screen.h"
+#include "terminal-screen-container.h"
+#include "terminal-window.h"
+#include "terminal-util.h"
+#include "profile-editor.h"
+#include "terminal-encoding.h"
+#include <mateconf/mateconf-client.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+
+#ifdef WITH_SMCLIENT
+#include "eggsmclient.h"
+#ifdef GDK_WINDOWING_X11
+#include "eggdesktopfile.h"
+#endif
+#endif
+
+#define FALLBACK_PROFILE_ID "Default"
+
+/* Settings storage works as follows:
+ * /apps/mate-terminal/global/
+ * /apps/mate-terminal/profiles/Foo/
+ *
+ * It's somewhat tricky to manage the profiles/ dir since we need to track
+ * the list of profiles, but mateconf doesn't have a concept of notifying that
+ * a directory has appeared or disappeared.
+ *
+ * Session state is stored entirely in the RestartCommand command line.
+ *
+ * The number one rule: all stored information is EITHER per-session,
+ * per-profile, or set from a command line option. THERE CAN BE NO
+ * OVERLAP. The UI and implementation totally break if you overlap
+ * these categories. See mate-terminal 1.x for why.
+ *
+ * Don't use this code as an example of how to use MateConf - it's hugely
+ * overcomplicated due to the profiles stuff. Most apps should not
+ * have to do scary things of this nature, and should not have
+ * a profiles feature.
+ *
+ */
+
+struct _TerminalAppClass {
+ GObjectClass parent_class;
+
+ void (* quit) (TerminalApp *app);
+ void (* profile_list_changed) (TerminalApp *app);
+ void (* encoding_list_changed) (TerminalApp *app);
+};
+
+struct _TerminalApp
+{
+ GObject parent_instance;
+
+ GList *windows;
+ GtkWidget *new_profile_dialog;
+ GtkWidget *manage_profiles_dialog;
+ GtkWidget *manage_profiles_list;
+ GtkWidget *manage_profiles_new_button;
+ GtkWidget *manage_profiles_edit_button;
+ GtkWidget *manage_profiles_delete_button;
+ GtkWidget *manage_profiles_default_menu;
+
+ MateConfClient *conf;
+ guint profile_list_notify_id;
+ guint default_profile_notify_id;
+ guint encoding_list_notify_id;
+ guint system_font_notify_id;
+ guint enable_mnemonics_notify_id;
+ guint enable_menu_accels_notify_id;
+
+ GHashTable *profiles;
+ char* default_profile_id;
+ TerminalProfile *default_profile;
+ gboolean default_profile_locked;
+
+ GHashTable *encodings;
+ gboolean encodings_locked;
+
+ PangoFontDescription *system_font_desc;
+ gboolean enable_mnemonics;
+ gboolean enable_menu_accels;
+};
+
+enum
+{
+ PROP_0,
+ PROP_DEFAULT_PROFILE,
+ PROP_ENABLE_MENU_BAR_ACCEL,
+ PROP_ENABLE_MNEMONICS,
+ PROP_SYSTEM_FONT,
+};
+
+enum
+{
+ QUIT,
+ PROFILE_LIST_CHANGED,
+ ENCODING_LIST_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+enum
+{
+ COL_PROFILE,
+ NUM_COLUMNS
+};
+
+enum
+{
+ SOURCE_DEFAULT = 0,
+ SOURCE_SESSION = 1
+};
+
+static TerminalApp *global_app = NULL;
+
+/* Evil hack alert: this is exported from libmateconf-2 but not in a public header */
+extern gboolean mateconf_spawn_daemon(GError** err);
+
+#define MONOSPACE_FONT_DIR "/desktop/mate/interface"
+#define MONOSPACE_FONT_KEY MONOSPACE_FONT_DIR "/monospace_font_name"
+#define DEFAULT_MONOSPACE_FONT ("Monospace 10")
+
+#define ENABLE_MNEMONICS_KEY CONF_GLOBAL_PREFIX "/use_mnemonics"
+#define DEFAULT_ENABLE_MNEMONICS (TRUE)
+
+#define ENABLE_MENU_BAR_ACCEL_KEY CONF_GLOBAL_PREFIX"/use_menu_accelerators"
+#define DEFAULT_ENABLE_MENU_BAR_ACCEL (TRUE)
+
+#define PROFILE_LIST_KEY CONF_GLOBAL_PREFIX "/profile_list"
+#define DEFAULT_PROFILE_KEY CONF_GLOBAL_PREFIX "/default_profile"
+
+#define ENCODING_LIST_KEY CONF_GLOBAL_PREFIX "/active_encodings"
+
+/* Helper functions */
+
+static GdkScreen*
+terminal_app_get_screen_by_display_name (const char *display_name,
+ int screen_number)
+{
+ GdkDisplay *display = NULL;
+ GdkScreen *screen = NULL;
+
+ /* --screen=screen_number overrides --display */
+
+ if (display_name == NULL)
+ display = gdk_display_get_default ();
+ else
+ {
+ GSList *displays, *l;
+ const char *period;
+
+ period = strrchr (display_name, '.');
+ if (period)
+ {
+ gulong n;
+ char *end;
+
+ errno = 0;
+ end = NULL;
+ n = g_ascii_strtoull (period + 1, &end, 0);
+ if (errno == 0 && (period + 1) != end)
+ screen_number = n;
+ }
+
+ displays = gdk_display_manager_list_displays (gdk_display_manager_get ());
+ for (l = displays; l != NULL; l = l->next)
+ {
+ GdkDisplay *disp = l->data;
+
+ /* compare without the screen number part, if present */
+ if ((period && strncmp (gdk_display_get_name (disp), display_name, period - display_name) == 0) ||
+ (period == NULL && strcmp (gdk_display_get_name (disp), display_name) == 0))
+ {
+ display = disp;
+ break;
+ }
+ }
+ g_slist_free (displays);
+
+ if (display == NULL)
+ display = gdk_display_open (display_name); /* FIXME we never close displays */
+ }
+
+ if (display == NULL)
+ return NULL;
+ if (screen_number >= 0)
+ screen = gdk_display_get_screen (display, screen_number);
+ if (screen == NULL)
+ screen = gdk_display_get_default_screen (display);
+
+ return screen;
+}
+
+/* Menubar mnemonics settings handling */
+
+static int
+profiles_alphabetic_cmp (gconstpointer pa,
+ gconstpointer pb)
+{
+ TerminalProfile *a = (TerminalProfile *) pa;
+ TerminalProfile *b = (TerminalProfile *) pb;
+ int result;
+
+ result = g_utf8_collate (terminal_profile_get_property_string (a, TERMINAL_PROFILE_VISIBLE_NAME),
+ terminal_profile_get_property_string (b, TERMINAL_PROFILE_VISIBLE_NAME));
+ if (result == 0)
+ result = strcmp (terminal_profile_get_property_string (a, TERMINAL_PROFILE_NAME),
+ terminal_profile_get_property_string (b, TERMINAL_PROFILE_NAME));
+
+ return result;
+}
+
+typedef struct
+{
+ TerminalProfile *result;
+ const char *target;
+} LookupInfo;
+
+static void
+profiles_lookup_by_visible_name_foreach (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ LookupInfo *info = data;
+ const char *name;
+
+ name = terminal_profile_get_property_string (value, TERMINAL_PROFILE_VISIBLE_NAME);
+ if (name && strcmp (info->target, name) == 0)
+ info->result = value;
+}
+
+static void
+terminal_window_destroyed (TerminalWindow *window,
+ TerminalApp *app)
+{
+ app->windows = g_list_remove (app->windows, window);
+
+ if (app->windows == NULL)
+ g_signal_emit (app, signals[QUIT], 0);
+}
+
+static TerminalProfile *
+terminal_app_create_profile (TerminalApp *app,
+ const char *name)
+{
+ TerminalProfile *profile;
+
+ g_assert (terminal_app_get_profile_by_name (app, name) == NULL);
+
+ profile = _terminal_profile_new (name);
+
+ g_hash_table_insert (app->profiles,
+ g_strdup (terminal_profile_get_property_string (profile, TERMINAL_PROFILE_NAME)),
+ profile /* adopts the refcount */);
+
+ if (app->default_profile == NULL &&
+ app->default_profile_id != NULL &&
+ strcmp (app->default_profile_id,
+ terminal_profile_get_property_string (profile, TERMINAL_PROFILE_NAME)) == 0)
+ {
+ /* We are the default profile */
+ app->default_profile = profile;
+ g_object_notify (G_OBJECT (app), TERMINAL_APP_DEFAULT_PROFILE);
+ }
+
+ return profile;
+}
+
+static void
+terminal_app_delete_profile (TerminalApp *app,
+ TerminalProfile *profile)
+{
+ GHashTableIter iter;
+ GSList *name_list;
+ const char *name, *profile_name;
+ char *mateconf_dir;
+ GError *error = NULL;
+ const char **nameptr = &name;
+
+ profile_name = terminal_profile_get_property_string (profile, TERMINAL_PROFILE_NAME);
+ mateconf_dir = mateconf_concat_dir_and_key (CONF_PREFIX "/profiles", profile_name);
+
+ name_list = NULL;
+ g_hash_table_iter_init (&iter, app->profiles);
+ while (g_hash_table_iter_next (&iter, (gpointer *) nameptr, NULL))
+ {
+ if (strcmp (name, profile_name) == 0)
+ continue;
+
+ name_list = g_slist_prepend (name_list, g_strdup (name));
+ }
+
+ mateconf_client_set_list (app->conf,
+ CONF_GLOBAL_PREFIX"/profile_list",
+ MATECONF_VALUE_STRING,
+ name_list,
+ NULL);
+
+ g_slist_foreach (name_list, (GFunc) g_free, NULL);
+ g_slist_free (name_list);
+
+ /* And remove the profile directory */
+ if (!mateconf_client_recursive_unset (app->conf, mateconf_dir, MATECONF_UNSET_INCLUDING_SCHEMA_NAMES, &error))
+ {
+ g_warning ("Failed to recursively unset %s: %s\n", mateconf_dir, error->message);
+ g_error_free (error);
+ }
+
+ g_free (mateconf_dir);
+}
+
+static void
+terminal_app_profile_cell_data_func (GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ TerminalProfile *profile;
+ GValue value = { 0, };
+
+ gtk_tree_model_get (tree_model, iter, (int) COL_PROFILE, &profile, (int) -1);
+
+ g_value_init (&value, G_TYPE_STRING);
+ g_object_get_property (G_OBJECT (profile), "visible-name", &value);
+ g_object_set_property (G_OBJECT (cell), "text", &value);
+ g_value_unset (&value);
+}
+
+static int
+terminal_app_profile_sort_func (GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
+{
+ TerminalProfile *profile_a, *profile_b;
+ int retval;
+
+ gtk_tree_model_get (model, a, (int) COL_PROFILE, &profile_a, (int) -1);
+ gtk_tree_model_get (model, b, (int) COL_PROFILE, &profile_b, (int) -1);
+
+ retval = profiles_alphabetic_cmp (profile_a, profile_b);
+
+ g_object_unref (profile_a);
+ g_object_unref (profile_b);
+
+ return retval;
+}
+
+static /* ref */ GtkTreeModel *
+terminal_app_get_profile_liststore (TerminalApp *app,
+ TerminalProfile *selected_profile,
+ GtkTreeIter *selected_profile_iter,
+ gboolean *selected_profile_iter_set)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GList *profiles, *l;
+ TerminalProfile *default_profile;
+
+ store = gtk_list_store_new (NUM_COLUMNS, TERMINAL_TYPE_PROFILE);
+
+ *selected_profile_iter_set = FALSE;
+
+ if (selected_profile &&
+ _terminal_profile_get_forgotten (selected_profile))
+ selected_profile = NULL;
+
+ profiles = terminal_app_get_profile_list (app);
+ default_profile = terminal_app_get_default_profile (app);
+
+ for (l = profiles; l != NULL; l = l->next)
+ {
+ TerminalProfile *profile = TERMINAL_PROFILE (l->data);
+
+ gtk_list_store_insert_with_values (store, &iter, 0,
+ (int) COL_PROFILE, profile,
+ (int) -1);
+
+ if (selected_profile_iter && profile == selected_profile)
+ {
+ *selected_profile_iter = iter;
+ *selected_profile_iter_set = TRUE;
+ }
+ }
+ g_list_free (profiles);
+
+ /* Now turn on sorting */
+ gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store),
+ COL_PROFILE,
+ terminal_app_profile_sort_func,
+ NULL, NULL);
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
+ COL_PROFILE, GTK_SORT_ASCENDING);
+
+ return GTK_TREE_MODEL (store);
+}
+
+static /* ref */ TerminalProfile*
+profile_combo_box_get_selected (GtkWidget *widget)
+{
+ GtkComboBox *combo = GTK_COMBO_BOX (widget);
+ TerminalProfile *profile = NULL;
+ GtkTreeIter iter;
+
+ if (gtk_combo_box_get_active_iter (combo, &iter))
+ gtk_tree_model_get (gtk_combo_box_get_model (combo), &iter,
+ (int) COL_PROFILE, &profile, (int) -1);
+
+ return profile;
+}
+
+static void
+profile_combo_box_refill (TerminalApp *app,
+ GtkWidget *widget)
+{
+ GtkComboBox *combo = GTK_COMBO_BOX (widget);
+ GtkTreeIter iter;
+ gboolean iter_set;
+ TerminalProfile *selected_profile;
+ GtkTreeModel *model;
+
+ selected_profile = profile_combo_box_get_selected (widget);
+ if (!selected_profile)
+ {
+ selected_profile = terminal_app_get_default_profile (app);
+ if (selected_profile)
+ g_object_ref (selected_profile);
+ }
+
+ model = terminal_app_get_profile_liststore (app,
+ selected_profile,
+ &iter,
+ &iter_set);
+ gtk_combo_box_set_model (combo, model);
+ g_object_unref (model);
+
+ if (iter_set)
+ gtk_combo_box_set_active_iter (combo, &iter);
+
+ if (selected_profile)
+ g_object_unref (selected_profile);
+}
+
+static GtkWidget*
+profile_combo_box_new (TerminalApp *app)
+{
+ GtkWidget *combo;
+ GtkCellRenderer *renderer;
+
+ combo = gtk_combo_box_new ();
+ terminal_util_set_atk_name_description (combo, NULL, _("Click button to choose profile"));
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE);
+ gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo), renderer,
+ (GtkCellLayoutDataFunc) terminal_app_profile_cell_data_func,
+ NULL, NULL);
+
+ profile_combo_box_refill (app, combo);
+ g_signal_connect (app, "profile-list-changed",
+ G_CALLBACK (profile_combo_box_refill), combo);
+
+ gtk_widget_show (combo);
+ return combo;
+}
+
+static void
+profile_combo_box_changed_cb (GtkWidget *widget,
+ TerminalApp *app)
+{
+ TerminalProfile *profile;
+
+ profile = profile_combo_box_get_selected (widget);
+ if (!profile)
+ return;
+
+ mateconf_client_set_string (app->conf,
+ CONF_GLOBAL_PREFIX "/default_profile",
+ terminal_profile_get_property_string (profile, TERMINAL_PROFILE_NAME),
+ NULL);
+
+ /* Even though the mateconf change notification does this, it happens too late.
+ * In some cases, the default profile changes twice in quick succession,
+ * and update_default_profile must be called in sync with those changes.
+ */
+ app->default_profile = profile;
+
+ g_object_notify (G_OBJECT (app), TERMINAL_APP_DEFAULT_PROFILE);
+
+ g_object_unref (profile);
+}
+
+static void
+profile_list_treeview_refill (TerminalApp *app,
+ GtkWidget *widget)
+{
+ GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
+ GtkTreeIter iter;
+ gboolean iter_set;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ TerminalProfile *selected_profile = NULL;
+
+ model = gtk_tree_view_get_model (tree_view);
+
+ selection = gtk_tree_view_get_selection (tree_view);
+ if (gtk_tree_selection_get_selected (selection, NULL, &iter))
+ gtk_tree_model_get (model, &iter, (int) COL_PROFILE, &selected_profile, (int) -1);
+
+ model = terminal_app_get_profile_liststore (terminal_app_get (),
+ selected_profile,
+ &iter,
+ &iter_set);
+ gtk_tree_view_set_model (tree_view, model);
+ g_object_unref (model);
+
+ if (!iter_set)
+ iter_set = gtk_tree_model_get_iter_first (model, &iter);
+
+ if (iter_set)
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ if (selected_profile)
+ g_object_unref (selected_profile);
+}
+
+static GtkWidget*
+profile_list_treeview_create (TerminalApp *app)
+{
+ GtkWidget *tree_view;
+ GtkTreeSelection *selection;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ tree_view = gtk_tree_view_new ();
+ terminal_util_set_atk_name_description (tree_view, _("Profile list"), NULL);
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
+ gtk_tree_selection_set_mode (GTK_TREE_SELECTION (selection),
+ GTK_SELECTION_BROWSE);
+
+ column = gtk_tree_view_column_new ();
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (column), renderer, TRUE);
+ gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column), renderer,
+ (GtkCellLayoutDataFunc) terminal_app_profile_cell_data_func,
+ NULL, NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view),
+ GTK_TREE_VIEW_COLUMN (column));
+
+ return tree_view;
+}
+
+static void
+profile_list_delete_confirm_response_cb (GtkWidget *dialog,
+ int response,
+ TerminalApp *app)
+{
+ TerminalProfile *profile;
+
+ profile = TERMINAL_PROFILE (g_object_get_data (G_OBJECT (dialog), "profile"));
+ g_assert (profile != NULL);
+
+ if (response == GTK_RESPONSE_ACCEPT)
+ terminal_app_delete_profile (app, profile);
+
+ gtk_widget_destroy (dialog);
+}
+
+static void
+profile_list_delete_button_clicked_cb (GtkWidget *button,
+ GtkWidget *widget)
+{
+ GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
+ TerminalApp *app = terminal_app_get ();
+ GtkTreeSelection *selection;
+ GtkWidget *dialog;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ TerminalProfile *selected_profile;
+ GtkWidget *transient_parent;
+
+ model = gtk_tree_view_get_model (tree_view);
+ selection = gtk_tree_view_get_selection (tree_view);
+
+ if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
+ return;
+
+ gtk_tree_model_get (model, &iter, (int) COL_PROFILE, &selected_profile, (int) -1);
+
+ transient_parent = gtk_widget_get_toplevel (widget);
+ dialog = gtk_message_dialog_new (GTK_WINDOW (transient_parent),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_NONE,
+ _("Delete profile “%s”?"),
+ terminal_profile_get_property_string (selected_profile, TERMINAL_PROFILE_VISIBLE_NAME));
+
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_REJECT,
+ GTK_STOCK_DELETE,
+ GTK_RESPONSE_ACCEPT,
+ NULL);
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_ACCEPT,
+ GTK_RESPONSE_REJECT,
+ -1);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog),
+ GTK_RESPONSE_ACCEPT);
+
+ gtk_window_set_title (GTK_WINDOW (dialog), _("Delete Profile"));
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+
+ /* Transfer refcount of |selected_profile|, so no unref below */
+ g_object_set_data_full (G_OBJECT (dialog), "profile", selected_profile, g_object_unref);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (profile_list_delete_confirm_response_cb),
+ app);
+
+ gtk_window_present (GTK_WINDOW (dialog));
+}
+
+static void
+profile_list_new_button_clicked_cb (GtkWidget *button,
+ gpointer data)
+{
+ TerminalApp *app;
+
+ app = terminal_app_get ();
+ terminal_app_new_profile (app, NULL, GTK_WINDOW (app->manage_profiles_dialog));
+}
+
+static void
+profile_list_edit_button_clicked_cb (GtkWidget *button,
+ GtkWidget *widget)
+{
+ GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ TerminalProfile *selected_profile;
+ TerminalApp *app;
+
+ app = terminal_app_get ();
+
+ model = gtk_tree_view_get_model (tree_view);
+ selection = gtk_tree_view_get_selection (tree_view);
+
+ if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
+ return;
+
+ gtk_tree_model_get (model, &iter, (int) COL_PROFILE, &selected_profile, (int) -1);
+
+ terminal_app_edit_profile (app, selected_profile,
+ GTK_WINDOW (app->manage_profiles_dialog),
+ NULL);
+ g_object_unref (selected_profile);
+}
+
+static void
+profile_list_row_activated_cb (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ gpointer data)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ TerminalProfile *selected_profile;
+ TerminalApp *app;
+
+ app = terminal_app_get ();
+
+ model = gtk_tree_view_get_model (tree_view);
+
+ if (!gtk_tree_model_get_iter (model, &iter, path))
+ return;
+
+ gtk_tree_model_get (model, &iter, (int) COL_PROFILE, &selected_profile, (int) -1);
+
+ terminal_app_edit_profile (app, selected_profile,
+ GTK_WINDOW (app->manage_profiles_dialog),
+ NULL);
+ g_object_unref (selected_profile);
+}
+
+static GList*
+find_profile_link (GList *profiles,
+ const char *name)
+{
+ GList *l;
+
+ for (l = profiles; l != NULL; l = l->next)
+ {
+ const char *profile_name;
+
+ profile_name = terminal_profile_get_property_string (TERMINAL_PROFILE (l->data), TERMINAL_PROFILE_NAME);
+ if (profile_name && strcmp (profile_name, name) == 0)
+ break;
+ }
+
+ return l;
+}
+
+static void
+terminal_app_profile_list_notify_cb (MateConfClient *conf,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data)
+{
+ TerminalApp *app = TERMINAL_APP (user_data);
+ GObject *object = G_OBJECT (app);
+ MateConfValue *val;
+ GSList *value_list, *sl;
+ GList *profiles_to_delete, *l;
+ gboolean need_new_default;
+ TerminalProfile *fallback;
+ guint count;
+
+ g_object_freeze_notify (object);
+
+ profiles_to_delete = terminal_app_get_profile_list (app);
+
+ val = mateconf_entry_get_value (entry);
+ if (val == NULL ||
+ val->type != MATECONF_VALUE_LIST ||
+ mateconf_value_get_list_type (val) != MATECONF_VALUE_STRING)
+ goto ensure_one_profile;
+
+ value_list = mateconf_value_get_list (val);
+
+ /* Add any new ones */
+ for (sl = value_list; sl != NULL; sl = sl->next)
+ {
+ MateConfValue *listvalue = (MateConfValue *) (sl->data);
+ const char *profile_name;
+ GList *link;
+
+ profile_name = mateconf_value_get_string (listvalue);
+ if (!profile_name)
+ continue;
+
+ link = find_profile_link (profiles_to_delete, profile_name);
+ if (link)
+ /* make profiles_to_delete point to profiles we didn't find in the list */
+ profiles_to_delete = g_list_delete_link (profiles_to_delete, link);
+ else
+ terminal_app_create_profile (app, profile_name);
+ }
+
+ensure_one_profile:
+
+ fallback = NULL;
+ count = g_hash_table_size (app->profiles);
+ if (count == 0 || count <= g_list_length (profiles_to_delete))
+ {
+ /* We are going to run out, so create the fallback
+ * to be sure we always have one. Must be done
+ * here before we emit "forgotten" signals so that
+ * screens have a profile to fall back to.
+ *
+ * If the profile with the FALLBACK_ID already exists,
+ * we aren't allowed to delete it, unless at least one
+ * other profile will still exist. And if you delete
+ * all profiles, the FALLBACK_ID profile returns as
+ * the living dead.
+ */
+ fallback = terminal_app_get_profile_by_name (app, FALLBACK_PROFILE_ID);
+ if (fallback == NULL)
+ fallback = terminal_app_create_profile (app, FALLBACK_PROFILE_ID);
+ g_assert (fallback != NULL);
+ }
+
+ /* Forget no-longer-existing profiles */
+ need_new_default = FALSE;
+ for (l = profiles_to_delete; l != NULL; l = l->next)
+ {
+ TerminalProfile *profile = TERMINAL_PROFILE (l->data);
+ const char *name;
+
+ name = terminal_profile_get_property_string (profile, TERMINAL_PROFILE_NAME);
+ if (strcmp (name, FALLBACK_PROFILE_ID) == 0)
+ continue;
+
+ if (profile == app->default_profile)
+ {
+ app->default_profile = NULL;
+ need_new_default = TRUE;
+ }
+
+ _terminal_profile_forget (profile);
+ g_hash_table_remove (app->profiles, name);
+
+ /* |profile| possibly isn't dead yet since the profiles dialogue's tree model holds a ref too... */
+ }
+ g_list_free (profiles_to_delete);
+
+ if (need_new_default)
+ {
+ TerminalProfile *new_default;
+ TerminalProfile **new_default_ptr = &new_default;
+
+ new_default = terminal_app_get_profile_by_name (app, FALLBACK_PROFILE_ID);
+ if (new_default == NULL)
+ {
+ GHashTableIter iter;
+
+ g_hash_table_iter_init (&iter, app->profiles);
+ if (!g_hash_table_iter_next (&iter, NULL, (gpointer *) new_default_ptr))
+ /* shouldn't really happen ever, but just to be safe */
+ new_default = terminal_app_create_profile (app, FALLBACK_PROFILE_ID);
+ }
+ g_assert (new_default != NULL);
+
+ app->default_profile = new_default;
+
+ g_object_notify (object, TERMINAL_APP_DEFAULT_PROFILE);
+ }
+
+ g_assert (g_hash_table_size (app->profiles) > 0);
+
+ g_signal_emit (app, signals[PROFILE_LIST_CHANGED], 0);
+
+ g_object_thaw_notify (object);
+}
+
+static void
+terminal_app_default_profile_notify_cb (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data)
+{
+ TerminalApp *app = TERMINAL_APP (user_data);
+ MateConfValue *val;
+ const char *name = NULL;
+
+ app->default_profile_locked = !mateconf_entry_get_is_writable (entry);
+
+ val = mateconf_entry_get_value (entry);
+ if (val != NULL &&
+ val->type == MATECONF_VALUE_STRING)
+ name = mateconf_value_get_string (val);
+ if (!name || !name[0])
+ name = FALLBACK_PROFILE_ID;
+ g_assert (name != NULL);
+
+ g_free (app->default_profile_id);
+ app->default_profile_id = g_strdup (name);
+
+ app->default_profile = terminal_app_get_profile_by_name (app, name);
+
+ g_object_notify (G_OBJECT (app), TERMINAL_APP_DEFAULT_PROFILE);
+}
+
+static int
+compare_encodings (TerminalEncoding *a,
+ TerminalEncoding *b)
+{
+ return g_utf8_collate (a->name, b->name);
+}
+
+static void
+encoding_mark_active (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ TerminalEncoding *encoding = (TerminalEncoding *) value;
+ guint active = GPOINTER_TO_UINT (data);
+
+ encoding->is_active = active;
+}
+
+static void
+terminal_app_encoding_list_notify_cb (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data)
+{
+ TerminalApp *app = TERMINAL_APP (user_data);
+ MateConfValue *val;
+ GSList *strings, *tmp;
+ TerminalEncoding *encoding;
+ const char *charset;
+
+ app->encodings_locked = !mateconf_entry_get_is_writable (entry);
+
+ /* Mark all as non-active, then re-enable the active ones */
+ g_hash_table_foreach (app->encodings, (GHFunc) encoding_mark_active, GUINT_TO_POINTER (FALSE));
+
+ /* First add the locale's charset */
+ encoding = g_hash_table_lookup (app->encodings, "current");
+ g_assert (encoding);
+ if (terminal_encoding_is_valid (encoding))
+ encoding->is_active = TRUE;
+
+ /* Also always make UTF-8 available */
+ encoding = g_hash_table_lookup (app->encodings, "UTF-8");
+ g_assert (encoding);
+ if (terminal_encoding_is_valid (encoding))
+ encoding->is_active = TRUE;
+
+ val = mateconf_entry_get_value (entry);
+ if (val != NULL &&
+ val->type == MATECONF_VALUE_LIST &&
+ mateconf_value_get_list_type (val) == MATECONF_VALUE_STRING)
+ strings = mateconf_value_get_list (val);
+ else
+ strings = NULL;
+
+ for (tmp = strings; tmp != NULL; tmp = tmp->next)
+ {
+ MateConfValue *v = (MateConfValue *) tmp->data;
+
+ charset = mateconf_value_get_string (v);
+ if (!charset)
+ continue;
+
+ encoding = terminal_app_ensure_encoding (app, charset);
+ if (!terminal_encoding_is_valid (encoding))
+ continue;
+
+ encoding->is_active = TRUE;
+ }
+
+ g_signal_emit (app, signals[ENCODING_LIST_CHANGED], 0);
+}
+
+static void
+terminal_app_system_font_notify_cb (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data)
+{
+ TerminalApp *app = TERMINAL_APP (user_data);
+ MateConfValue *mateconf_value;
+ const char *font = NULL;
+ PangoFontDescription *font_desc;
+
+ if (strcmp (mateconf_entry_get_key (entry), MONOSPACE_FONT_KEY) != 0)
+ return;
+
+ mateconf_value = mateconf_entry_get_value (entry);
+ if (mateconf_value &&
+ mateconf_value->type == MATECONF_VALUE_STRING)
+ font = mateconf_value_get_string (mateconf_value);
+ if (!font)
+ font = DEFAULT_MONOSPACE_FONT;
+ g_assert (font != NULL);
+
+ font_desc = pango_font_description_from_string (font);
+ if (app->system_font_desc &&
+ pango_font_description_equal (app->system_font_desc, font_desc))
+ {
+ pango_font_description_free (font_desc);
+ return;
+ }
+
+ if (app->system_font_desc)
+ pango_font_description_free (app->system_font_desc);
+
+ app->system_font_desc = font_desc;
+
+ g_object_notify (G_OBJECT (app), TERMINAL_APP_SYSTEM_FONT);
+}
+
+static void
+terminal_app_enable_mnemonics_notify_cb (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data)
+{
+ TerminalApp *app = TERMINAL_APP (user_data);
+ MateConfValue *mateconf_value;
+ gboolean enable;
+
+ if (strcmp (mateconf_entry_get_key (entry), ENABLE_MNEMONICS_KEY) != 0)
+ return;
+
+ mateconf_value = mateconf_entry_get_value (entry);
+ if (mateconf_value &&
+ mateconf_value->type == MATECONF_VALUE_BOOL)
+ enable = mateconf_value_get_bool (mateconf_value);
+ else
+ enable = TRUE;
+
+ if (enable == app->enable_mnemonics)
+ return;
+
+ app->enable_mnemonics = enable;
+ g_object_notify (G_OBJECT (app), TERMINAL_APP_ENABLE_MNEMONICS);
+}
+
+static void
+terminal_app_enable_menu_accels_notify_cb (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data)
+{
+ TerminalApp *app = TERMINAL_APP (user_data);
+ MateConfValue *mateconf_value;
+ gboolean enable;
+
+ if (strcmp (mateconf_entry_get_key (entry), ENABLE_MENU_BAR_ACCEL_KEY) != 0)
+ return;
+
+ mateconf_value = mateconf_entry_get_value (entry);
+ if (mateconf_value &&
+ mateconf_value->type == MATECONF_VALUE_BOOL)
+ enable = mateconf_value_get_bool (mateconf_value);
+ else
+ enable = TRUE;
+
+ if (enable == app->enable_menu_accels)
+ return;
+
+ app->enable_menu_accels = enable;
+ g_object_notify (G_OBJECT (app), TERMINAL_APP_ENABLE_MENU_BAR_ACCEL);
+}
+
+static void
+new_profile_response_cb (GtkWidget *new_profile_dialog,
+ int response_id,
+ TerminalApp *app)
+{
+ if (response_id == GTK_RESPONSE_ACCEPT)
+ {
+ GtkWidget *name_entry;
+ char *name;
+ const char *new_profile_name;
+ GtkWidget *base_option_menu;
+ TerminalProfile *base_profile = NULL;
+ TerminalProfile *new_profile;
+ GList *profiles;
+ GList *tmp;
+ GtkWindow *transient_parent;
+ GtkWidget *confirm_dialog;
+ gint retval;
+ GSList *list;
+
+ base_option_menu = g_object_get_data (G_OBJECT (new_profile_dialog), "base_option_menu");
+ base_profile = profile_combo_box_get_selected (base_option_menu);
+ if (!base_profile)
+ base_profile = terminal_app_get_default_profile (app);
+ if (!base_profile)
+ return; /* shouldn't happen ever though */
+
+ name_entry = g_object_get_data (G_OBJECT (new_profile_dialog), "name_entry");
+ name = gtk_editable_get_chars (GTK_EDITABLE (name_entry), 0, -1);
+ g_strstrip (name); /* name will be non empty after stripping */
+
+ profiles = terminal_app_get_profile_list (app);
+ for (tmp = profiles; tmp != NULL; tmp = tmp->next)
+ {
+ TerminalProfile *profile = tmp->data;
+ const char *visible_name;
+
+ visible_name = terminal_profile_get_property_string (profile, TERMINAL_PROFILE_VISIBLE_NAME);
+
+ if (visible_name && strcmp (name, visible_name) == 0)
+ break;
+ }
+ if (tmp)
+ {
+ confirm_dialog = gtk_message_dialog_new (GTK_WINDOW (new_profile_dialog),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_YES_NO,
+ _("You already have a profile called “%s”. Do you want to create another profile with the same name?"), name);
+ /* Alternative button order was set automatically by GtkMessageDialog */
+ retval = gtk_dialog_run (GTK_DIALOG (confirm_dialog));
+ gtk_widget_destroy (confirm_dialog);
+ if (retval == GTK_RESPONSE_NO)
+ goto cleanup;
+ }
+ g_list_free (profiles);
+
+ transient_parent = gtk_window_get_transient_for (GTK_WINDOW (new_profile_dialog));
+
+ new_profile = _terminal_profile_clone (base_profile, name);
+ new_profile_name = terminal_profile_get_property_string (new_profile, TERMINAL_PROFILE_NAME);
+ g_hash_table_insert (app->profiles,
+ g_strdup (new_profile_name),
+ new_profile /* adopts the refcount */);
+
+ /* And now save the list to mateconf */
+ list = mateconf_client_get_list (app->conf,
+ CONF_GLOBAL_PREFIX"/profile_list",
+ MATECONF_VALUE_STRING,
+ NULL);
+ list = g_slist_append (list, g_strdup (new_profile_name));
+ mateconf_client_set_list (app->conf,
+ CONF_GLOBAL_PREFIX"/profile_list",
+ MATECONF_VALUE_STRING,
+ list,
+ NULL);
+
+ terminal_profile_edit (new_profile, transient_parent, NULL);
+
+ cleanup:
+ g_free (name);
+ }
+
+ gtk_widget_destroy (new_profile_dialog);
+}
+
+static void
+new_profile_dialog_destroy_cb (GtkWidget *new_profile_dialog,
+ TerminalApp *app)
+{
+ GtkWidget *combo;
+
+ combo = g_object_get_data (G_OBJECT (new_profile_dialog), "base_option_menu");
+ g_signal_handlers_disconnect_by_func (app, G_CALLBACK (profile_combo_box_refill), combo);
+
+ app->new_profile_dialog = NULL;
+}
+
+static void
+new_profile_name_entry_changed_cb (GtkEntry *entry,
+ GtkDialog *dialog)
+{
+ const char *name;
+
+ name = gtk_entry_get_text (entry);
+
+ /* make the create button sensitive only if something other than space has been set */
+ while (*name != '\0' && g_ascii_isspace (*name))
+ ++name;
+
+ gtk_dialog_set_response_sensitive (dialog, GTK_RESPONSE_ACCEPT, name[0] != '\0');
+}
+
+void
+terminal_app_new_profile (TerminalApp *app,
+ TerminalProfile *default_base_profile,
+ GtkWindow *transient_parent)
+{
+ if (app->new_profile_dialog == NULL)
+ {
+ GtkWidget *create_button, *table, *name_label, *name_entry, *base_label, *combo;
+
+ if (!terminal_util_load_builder_file ("profile-new-dialog.ui",
+ "new-profile-dialog", &app->new_profile_dialog,
+ "new-profile-create-button", &create_button,
+ "new-profile-table", &table,
+ "new-profile-name-label", &name_label,
+ "new-profile-name-entry", &name_entry,
+ "new-profile-base-label", &base_label,
+ NULL))
+ return;
+
+ g_signal_connect (G_OBJECT (app->new_profile_dialog), "response", G_CALLBACK (new_profile_response_cb), app);
+ g_signal_connect (app->new_profile_dialog, "destroy", G_CALLBACK (new_profile_dialog_destroy_cb), app);
+
+ g_object_set_data (G_OBJECT (app->new_profile_dialog), "create_button", create_button);
+ gtk_widget_set_sensitive (create_button, FALSE);
+
+ /* the name entry */
+ g_object_set_data (G_OBJECT (app->new_profile_dialog), "name_entry", name_entry);
+ g_signal_connect (name_entry, "changed", G_CALLBACK (new_profile_name_entry_changed_cb), app->new_profile_dialog);
+ gtk_entry_set_activates_default (GTK_ENTRY (name_entry), TRUE);
+ gtk_widget_grab_focus (name_entry);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (name_label), name_entry);
+
+ /* the base profile option menu */
+ combo = profile_combo_box_new (app);
+ gtk_table_attach_defaults (GTK_TABLE (table), combo, 1, 2, 1, 2);
+ g_object_set_data (G_OBJECT (app->new_profile_dialog), "base_option_menu", combo);
+ terminal_util_set_atk_name_description (combo, NULL, _("Choose base profile"));
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (base_label), combo);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (app->new_profile_dialog),
+ GTK_RESPONSE_ACCEPT,
+ GTK_RESPONSE_CANCEL,
+ -1);
+ gtk_dialog_set_default_response (GTK_DIALOG (app->new_profile_dialog), GTK_RESPONSE_ACCEPT);
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (app->new_profile_dialog), GTK_RESPONSE_ACCEPT, FALSE);
+ }
+
+ gtk_window_set_transient_for (GTK_WINDOW (app->new_profile_dialog),
+ transient_parent);
+
+ gtk_window_present (GTK_WINDOW (app->new_profile_dialog));
+}
+
+static void
+profile_list_selection_changed_cb (GtkTreeSelection *selection,
+ TerminalApp *app)
+{
+ gboolean selected;
+
+ selected = gtk_tree_selection_get_selected (selection, NULL, NULL);
+
+ gtk_widget_set_sensitive (app->manage_profiles_edit_button, selected);
+ gtk_widget_set_sensitive (app->manage_profiles_delete_button,
+ selected &&
+ g_hash_table_size (app->profiles) > 1);
+}
+
+static void
+profile_list_response_cb (GtkWidget *dialog,
+ int id,
+ TerminalApp *app)
+{
+ g_assert (app->manage_profiles_dialog == dialog);
+
+ if (id == GTK_RESPONSE_HELP)
+ {
+ terminal_util_show_help ("mate-terminal-manage-profiles", GTK_WINDOW (dialog));
+ return;
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+static void
+profile_list_destroyed_cb (GtkWidget *manage_profiles_dialog,
+ TerminalApp *app)
+{
+ g_signal_handlers_disconnect_by_func (app, G_CALLBACK (profile_list_treeview_refill), app->manage_profiles_list);
+ g_signal_handlers_disconnect_by_func (app, G_CALLBACK (profile_combo_box_refill), app->manage_profiles_default_menu);
+
+ app->manage_profiles_dialog = NULL;
+ app->manage_profiles_list = NULL;
+ app->manage_profiles_new_button = NULL;
+ app->manage_profiles_edit_button = NULL;
+ app->manage_profiles_delete_button = NULL;
+ app->manage_profiles_default_menu = NULL;
+}
+
+void
+terminal_app_manage_profiles (TerminalApp *app,
+ GtkWindow *transient_parent)
+{
+ GObject *dialog;
+ GObject *tree_view_container, *new_button, *edit_button, *remove_button;
+ GObject *default_hbox, *default_label;
+ GtkTreeSelection *selection;
+
+ if (app->manage_profiles_dialog)
+ {
+ gtk_window_set_transient_for (GTK_WINDOW (app->manage_profiles_dialog), transient_parent);
+ gtk_window_present (GTK_WINDOW (app->manage_profiles_dialog));
+ return;
+ }
+
+ if (!terminal_util_load_builder_file ("profile-manager.ui",
+ "profile-manager", &dialog,
+ "profiles-treeview-container", &tree_view_container,
+ "new-profile-button", &new_button,
+ "edit-profile-button", &edit_button,
+ "delete-profile-button", &remove_button,
+ "default-profile-hbox", &default_hbox,
+ "default-profile-label", &default_label,
+ NULL))
+ return;
+
+ app->manage_profiles_dialog = GTK_WIDGET (dialog);
+ app->manage_profiles_new_button = GTK_WIDGET (new_button);
+ app->manage_profiles_edit_button = GTK_WIDGET (edit_button);
+ app->manage_profiles_delete_button = GTK_WIDGET (remove_button);
+
+ g_signal_connect (dialog, "response", G_CALLBACK (profile_list_response_cb), app);
+ g_signal_connect (dialog, "destroy", G_CALLBACK (profile_list_destroyed_cb), app);
+
+ app->manage_profiles_list = profile_list_treeview_create (app);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (app->manage_profiles_list));
+ g_signal_connect (selection, "changed", G_CALLBACK (profile_list_selection_changed_cb), app);
+
+ profile_list_treeview_refill (app, app->manage_profiles_list);
+ g_signal_connect (app, "profile-list-changed",
+ G_CALLBACK (profile_list_treeview_refill), app->manage_profiles_list);
+
+ g_signal_connect (app->manage_profiles_list, "row-activated",
+ G_CALLBACK (profile_list_row_activated_cb), app);
+
+ gtk_container_add (GTK_CONTAINER (tree_view_container), app->manage_profiles_list);
+ gtk_widget_show (app->manage_profiles_list);
+
+ g_signal_connect (new_button, "clicked",
+ G_CALLBACK (profile_list_new_button_clicked_cb),
+ app->manage_profiles_list);
+ g_signal_connect (edit_button, "clicked",
+ G_CALLBACK (profile_list_edit_button_clicked_cb),
+ app->manage_profiles_list);
+ g_signal_connect (remove_button, "clicked",
+ G_CALLBACK (profile_list_delete_button_clicked_cb),
+ app->manage_profiles_list);
+
+ app->manage_profiles_default_menu = profile_combo_box_new (app);
+ g_signal_connect (app->manage_profiles_default_menu, "changed",
+ G_CALLBACK (profile_combo_box_changed_cb), app);
+
+ gtk_box_pack_start (GTK_BOX (default_hbox), app->manage_profiles_default_menu, FALSE, FALSE, 0);
+ gtk_widget_show (app->manage_profiles_default_menu);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (default_label), app->manage_profiles_default_menu);
+
+ gtk_widget_grab_focus (app->manage_profiles_list);
+
+ gtk_window_set_transient_for (GTK_WINDOW (app->manage_profiles_dialog),
+ transient_parent);
+
+ gtk_window_present (GTK_WINDOW (app->manage_profiles_dialog));
+}
+
+#ifdef WITH_SMCLIENT
+
+static void
+terminal_app_save_state_cb (EggSMClient *client,
+ GKeyFile *key_file,
+ TerminalApp *app)
+{
+ terminal_app_save_config (app, key_file);
+}
+
+static void
+terminal_app_client_quit_cb (EggSMClient *client,
+ TerminalApp *app)
+{
+ g_signal_emit (app, signals[QUIT], 0);
+}
+
+#endif /* WITH_SMCLIENT */
+
+/* Class implementation */
+
+G_DEFINE_TYPE (TerminalApp, terminal_app, G_TYPE_OBJECT)
+
+static void
+terminal_app_init (TerminalApp *app)
+{
+ GError *error = NULL;
+
+ global_app = app;
+
+ /* If the mateconf daemon isn't available (e.g. because there's no dbus
+ * session bus running), we'd crash later on. Tell the user about it
+ * now, and exit. See bug #561663.
+ * Don't use mateconf_ping_daemon() here since the server may just not
+ * be running yet, but able to be started. See comments on bug #564649.
+ */
+ if (!mateconf_spawn_daemon (&error))
+ {
+ g_printerr ("Failed to summon the MateConf demon; exiting. %s\n", error->message);
+ g_error_free (error);
+
+ exit (EXIT_FAILURE);
+ }
+
+ gtk_window_set_default_icon_name (MATE_TERMINAL_ICON_NAME);
+
+ /* Initialise defaults */
+ app->enable_mnemonics = DEFAULT_ENABLE_MNEMONICS;
+ app->enable_menu_accels = DEFAULT_ENABLE_MENU_BAR_ACCEL;
+
+ app->profiles = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+
+ app->encodings = terminal_encodings_get_builtins ();
+
+ app->conf = mateconf_client_get_default ();
+
+ mateconf_client_add_dir (app->conf, CONF_GLOBAL_PREFIX,
+ MATECONF_CLIENT_PRELOAD_ONELEVEL,
+ NULL);
+ mateconf_client_add_dir (app->conf, MONOSPACE_FONT_DIR,
+ MATECONF_CLIENT_PRELOAD_ONELEVEL,
+ NULL);
+ mateconf_client_add_dir (app->conf, CONF_PROXY_PREFIX,
+ MATECONF_CLIENT_PRELOAD_ONELEVEL,
+ NULL);
+ mateconf_client_add_dir (app->conf, CONF_HTTP_PROXY_PREFIX,
+ MATECONF_CLIENT_PRELOAD_ONELEVEL,
+ NULL);
+
+ app->profile_list_notify_id =
+ mateconf_client_notify_add (app->conf, PROFILE_LIST_KEY,
+ terminal_app_profile_list_notify_cb,
+ app, NULL, NULL);
+
+ app->default_profile_notify_id =
+ mateconf_client_notify_add (app->conf,
+ DEFAULT_PROFILE_KEY,
+ terminal_app_default_profile_notify_cb,
+ app, NULL, NULL);
+
+ app->encoding_list_notify_id =
+ mateconf_client_notify_add (app->conf,
+ ENCODING_LIST_KEY,
+ terminal_app_encoding_list_notify_cb,
+ app, NULL, NULL);
+
+ app->system_font_notify_id =
+ mateconf_client_notify_add (app->conf,
+ MONOSPACE_FONT_KEY,
+ terminal_app_system_font_notify_cb,
+ app, NULL, NULL);
+
+ app->enable_mnemonics_notify_id =
+ mateconf_client_notify_add (app->conf,
+ ENABLE_MNEMONICS_KEY,
+ terminal_app_enable_mnemonics_notify_cb,
+ app, NULL, NULL);
+
+ app->enable_menu_accels_notify_id =
+ mateconf_client_notify_add (app->conf,
+ ENABLE_MENU_BAR_ACCEL_KEY,
+ terminal_app_enable_menu_accels_notify_cb,
+ app, NULL, NULL);
+
+ /* Load the settings */
+ mateconf_client_notify (app->conf, PROFILE_LIST_KEY);
+ mateconf_client_notify (app->conf, DEFAULT_PROFILE_KEY);
+ mateconf_client_notify (app->conf, ENCODING_LIST_KEY);
+ mateconf_client_notify (app->conf, MONOSPACE_FONT_KEY);
+ mateconf_client_notify (app->conf, ENABLE_MENU_BAR_ACCEL_KEY);
+ mateconf_client_notify (app->conf, ENABLE_MNEMONICS_KEY);
+
+ /* Ensure we have valid settings */
+ g_assert (app->default_profile_id != NULL);
+ g_assert (app->system_font_desc != NULL);
+
+ terminal_accels_init ();
+
+#ifdef WITH_SMCLIENT
+{
+ EggSMClient *sm_client;
+#ifdef GDK_WINDOWING_X11
+ char *desktop_file;
+
+ desktop_file = g_build_filename (TERM_DATADIR,
+ "applications",
+ PACKAGE ".desktop",
+ NULL);
+ egg_set_desktop_file_without_defaults (desktop_file);
+ g_free (desktop_file);
+#endif /* GDK_WINDOWING_X11 */
+
+ sm_client = egg_sm_client_get ();
+ g_signal_connect (sm_client, "save-state",
+ G_CALLBACK (terminal_app_save_state_cb), app);
+ g_signal_connect (sm_client, "quit",
+ G_CALLBACK (terminal_app_client_quit_cb), app);
+}
+#endif
+}
+
+static void
+terminal_app_finalize (GObject *object)
+{
+ TerminalApp *app = TERMINAL_APP (object);
+
+#ifdef WITH_SMCLIENT
+ EggSMClient *sm_client;
+
+ sm_client = egg_sm_client_get ();
+ g_signal_handlers_disconnect_matched (sm_client, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, app);
+#endif
+
+ if (app->profile_list_notify_id != 0)
+ mateconf_client_notify_remove (app->conf, app->profile_list_notify_id);
+ if (app->default_profile_notify_id != 0)
+ mateconf_client_notify_remove (app->conf, app->default_profile_notify_id);
+ if (app->encoding_list_notify_id != 0)
+ mateconf_client_notify_remove (app->conf, app->encoding_list_notify_id);
+ if (app->system_font_notify_id != 0)
+ mateconf_client_notify_remove (app->conf, app->system_font_notify_id);
+ if (app->enable_menu_accels_notify_id != 0)
+ mateconf_client_notify_remove (app->conf, app->enable_menu_accels_notify_id);
+ if (app->enable_mnemonics_notify_id != 0)
+ mateconf_client_notify_remove (app->conf, app->enable_mnemonics_notify_id);
+
+ mateconf_client_remove_dir (app->conf, CONF_GLOBAL_PREFIX, NULL);
+ mateconf_client_remove_dir (app->conf, MONOSPACE_FONT_DIR, NULL);
+
+ g_object_unref (app->conf);
+
+ g_free (app->default_profile_id);
+
+ g_hash_table_destroy (app->profiles);
+
+ g_hash_table_destroy (app->encodings);
+
+ pango_font_description_free (app->system_font_desc);
+
+ terminal_accels_shutdown ();
+
+ G_OBJECT_CLASS (terminal_app_parent_class)->finalize (object);
+
+ global_app = NULL;
+}
+
+static void
+terminal_app_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ TerminalApp *app = TERMINAL_APP (object);
+
+ switch (prop_id)
+ {
+ case PROP_SYSTEM_FONT:
+ if (app->system_font_desc)
+ g_value_set_boxed (value, app->system_font_desc);
+ else
+ g_value_take_boxed (value, pango_font_description_from_string (DEFAULT_MONOSPACE_FONT));
+ break;
+ case PROP_ENABLE_MENU_BAR_ACCEL:
+ g_value_set_boolean (value, app->enable_menu_accels);
+ break;
+ case PROP_ENABLE_MNEMONICS:
+ g_value_set_boolean (value, app->enable_mnemonics);
+ break;
+ case PROP_DEFAULT_PROFILE:
+ g_value_set_object (value, app->default_profile);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+terminal_app_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ TerminalApp *app = TERMINAL_APP (object);
+
+ switch (prop_id)
+ {
+ case PROP_ENABLE_MENU_BAR_ACCEL:
+ app->enable_menu_accels = g_value_get_boolean (value);
+ mateconf_client_set_bool (app->conf, ENABLE_MENU_BAR_ACCEL_KEY, app->enable_menu_accels, NULL);
+ break;
+ case PROP_ENABLE_MNEMONICS:
+ app->enable_mnemonics = g_value_get_boolean (value);
+ mateconf_client_set_bool (app->conf, ENABLE_MNEMONICS_KEY, app->enable_mnemonics, NULL);
+ break;
+ case PROP_DEFAULT_PROFILE:
+ case PROP_SYSTEM_FONT:
+ /* not writable */
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+terminal_app_real_quit (TerminalApp *app)
+{
+ gtk_main_quit();
+}
+
+static void
+terminal_app_class_init (TerminalAppClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = terminal_app_finalize;
+ object_class->get_property = terminal_app_get_property;
+ object_class->set_property = terminal_app_set_property;
+
+ klass->quit = terminal_app_real_quit;
+
+ signals[QUIT] =
+ g_signal_new (I_("quit"),
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (TerminalAppClass, quit),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[PROFILE_LIST_CHANGED] =
+ g_signal_new (I_("profile-list-changed"),
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (TerminalAppClass, profile_list_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[ENCODING_LIST_CHANGED] =
+ g_signal_new (I_("encoding-list-changed"),
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (TerminalAppClass, profile_list_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_object_class_install_property
+ (object_class,
+ PROP_ENABLE_MENU_BAR_ACCEL,
+ g_param_spec_boolean (TERMINAL_APP_ENABLE_MENU_BAR_ACCEL, NULL, NULL,
+ DEFAULT_ENABLE_MENU_BAR_ACCEL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+ g_object_class_install_property
+ (object_class,
+ PROP_ENABLE_MNEMONICS,
+ g_param_spec_boolean (TERMINAL_APP_ENABLE_MNEMONICS, NULL, NULL,
+ DEFAULT_ENABLE_MNEMONICS,
+ G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+ g_object_class_install_property
+ (object_class,
+ PROP_SYSTEM_FONT,
+ g_param_spec_boxed (TERMINAL_APP_SYSTEM_FONT, NULL, NULL,
+ PANGO_TYPE_FONT_DESCRIPTION,
+ G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+ g_object_class_install_property
+ (object_class,
+ PROP_DEFAULT_PROFILE,
+ g_param_spec_object (TERMINAL_APP_DEFAULT_PROFILE, NULL, NULL,
+ TERMINAL_TYPE_PROFILE,
+ G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+}
+
+/* Public API */
+
+TerminalApp*
+terminal_app_get (void)
+{
+ if (global_app == NULL) {
+ g_object_new (TERMINAL_TYPE_APP, NULL);
+ g_assert (global_app != NULL);
+ }
+
+ return global_app;
+}
+
+void
+terminal_app_shutdown (void)
+{
+ if (global_app == NULL)
+ return;
+
+ g_object_unref (global_app);
+ g_assert (global_app == NULL);
+}
+
+/**
+ * terminal_app_handle_options:
+ * @app:
+ * @options: a #TerminalOptions
+ * @allow_resume: whether to merge the terminal configuration from the
+ * saved session on resume
+ * @error: a #GError to fill in
+ *
+ * Processes @options. It loads or saves the terminal configuration, or
+ * opens the specified windows and tabs.
+ *
+ * Returns: %TRUE if @options could be successfully handled, or %FALSE on
+ * error
+ */
+gboolean
+terminal_app_handle_options (TerminalApp *app,
+ TerminalOptions *options,
+ gboolean allow_resume,
+ GError **error)
+{
+ GList *lw;
+ GdkScreen *gdk_screen;
+
+ gdk_screen = terminal_app_get_screen_by_display_name (options->display_name,
+ options->screen_number);
+
+ if (options->save_config)
+ {
+ if (options->remote_arguments)
+ return terminal_app_save_config_file (app, options->config_file, error);
+
+ g_set_error_literal (error, TERMINAL_OPTION_ERROR, TERMINAL_OPTION_ERROR_NOT_IN_FACTORY,
+ "Cannot use \"--save-config\" when starting the factory process");
+ return FALSE;
+ }
+
+ if (options->load_config)
+ {
+ GKeyFile *key_file;
+ gboolean result;
+
+ key_file = g_key_file_new ();
+ result = g_key_file_load_from_file (key_file, options->config_file, 0, error) &&
+ terminal_options_merge_config (options, key_file, SOURCE_DEFAULT, error);
+ g_key_file_free (key_file);
+
+ if (!result)
+ return FALSE;
+
+ /* fall-through on success */
+ }
+
+#ifdef WITH_SMCLIENT
+{
+ EggSMClient *sm_client;
+
+ sm_client = egg_sm_client_get ();
+
+ if (allow_resume && egg_sm_client_is_resumed (sm_client))
+ {
+ GKeyFile *key_file;
+
+ key_file = egg_sm_client_get_state_file (sm_client);
+ if (key_file != NULL &&
+ !terminal_options_merge_config (options, key_file, SOURCE_SESSION, error))
+ return FALSE;
+ }
+}
+#endif
+
+ /* Make sure we open at least one window */
+ terminal_options_ensure_window (options);
+
+ if (options->startup_id != NULL)
+ _terminal_debug_print (TERMINAL_DEBUG_FACTORY,
+ "Startup ID is '%s'\n",
+ options->startup_id);
+
+ for (lw = options->initial_windows; lw != NULL; lw = lw->next)
+ {
+ InitialWindow *iw = lw->data;
+ TerminalWindow *window;
+ GList *lt;
+
+ g_assert (iw->tabs);
+
+ /* Create & setup new window */
+ window = terminal_app_new_window (app, gdk_screen);
+
+ /* Restored windows shouldn't demand attention; see bug #586308. */
+ if (iw->source_tag == SOURCE_SESSION)
+ terminal_window_set_is_restored (window);
+
+ if (options->startup_id != NULL)
+ gtk_window_set_startup_id (GTK_WINDOW (window), options->startup_id);
+
+ /* Overwrite the default, unique window role set in terminal_window_init */
+ if (iw->role)
+ gtk_window_set_role (GTK_WINDOW (window), iw->role);
+
+ if (iw->force_menubar_state)
+ terminal_window_set_menubar_visible (window, iw->menubar_state);
+
+ if (iw->start_fullscreen)
+ gtk_window_fullscreen (GTK_WINDOW (window));
+ if (iw->start_maximized)
+ gtk_window_maximize (GTK_WINDOW (window));
+
+ /* Now add the tabs */
+ for (lt = iw->tabs; lt != NULL; lt = lt->next)
+ {
+ InitialTab *it = lt->data;
+ TerminalProfile *profile = NULL;
+ TerminalScreen *screen;
+ const char *profile_name;
+ gboolean profile_is_id;
+
+ if (it->profile)
+ {
+ profile_name = it->profile;
+ profile_is_id = it->profile_is_id;
+ }
+ else
+ {
+ profile_name = options->default_profile;
+ profile_is_id = options->default_profile_is_id;
+ }
+
+ if (profile_name)
+ {
+ if (profile_is_id)
+ profile = terminal_app_get_profile_by_name (app, profile_name);
+ else
+ profile = terminal_app_get_profile_by_visible_name (app, profile_name);
+
+ if (profile == NULL)
+ g_printerr (_("No such profile \"%s\", using default profile\n"), it->profile);
+ }
+ if (profile == NULL)
+ profile = terminal_app_get_profile_for_new_term (app);
+ g_assert (profile);
+
+ screen = terminal_app_new_terminal (app, window, profile,
+ it->exec_argv ? it->exec_argv : options->exec_argv,
+ it->title ? it->title : options->default_title,
+ it->working_dir ? it->working_dir : options->default_working_dir,
+ options->env,
+ it->zoom_set ? it->zoom : options->zoom);
+
+ if (it->active)
+ terminal_window_switch_screen (window, screen);
+ }
+
+ if (iw->geometry)
+ {
+ _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
+ "[window %p] applying geometry %s\n",
+ window, iw->geometry);
+
+ if (!gtk_window_parse_geometry (GTK_WINDOW (window), iw->geometry))
+ g_printerr (_("Invalid geometry string \"%s\"\n"), iw->geometry);
+ }
+
+ gtk_window_present (GTK_WINDOW (window));
+ }
+
+ return TRUE;
+}
+
+TerminalWindow *
+terminal_app_new_window (TerminalApp *app,
+ GdkScreen *screen)
+{
+ TerminalWindow *window;
+
+ window = terminal_window_new ();
+
+ app->windows = g_list_append (app->windows, window);
+ g_signal_connect (window, "destroy",
+ G_CALLBACK (terminal_window_destroyed), app);
+
+ if (screen)
+ gtk_window_set_screen (GTK_WINDOW (window), screen);
+
+ return window;
+}
+
+TerminalScreen *
+terminal_app_new_terminal (TerminalApp *app,
+ TerminalWindow *window,
+ TerminalProfile *profile,
+ char **override_command,
+ const char *title,
+ const char *working_dir,
+ char **child_env,
+ double zoom)
+{
+ TerminalScreen *screen;
+
+ g_return_val_if_fail (TERMINAL_IS_APP (app), NULL);
+ g_return_val_if_fail (TERMINAL_IS_WINDOW (window), NULL);
+
+ screen = terminal_screen_new (profile, override_command, title,
+ working_dir, child_env, zoom);
+
+ terminal_window_add_screen (window, screen, -1);
+ terminal_window_switch_screen (window, screen);
+ gtk_widget_grab_focus (GTK_WIDGET (screen));
+
+ return screen;
+}
+
+void
+terminal_app_edit_profile (TerminalApp *app,
+ TerminalProfile *profile,
+ GtkWindow *transient_parent,
+ const char *widget_name)
+{
+ terminal_profile_edit (profile, transient_parent, widget_name);
+}
+
+void
+terminal_app_edit_keybindings (TerminalApp *app,
+ GtkWindow *transient_parent)
+{
+ terminal_edit_keys_dialog_show (transient_parent);
+}
+
+void
+terminal_app_edit_encodings (TerminalApp *app,
+ GtkWindow *transient_parent)
+{
+ terminal_encoding_dialog_show (transient_parent);
+}
+
+TerminalWindow *
+terminal_app_get_current_window (TerminalApp *app)
+{
+ if (app->windows == NULL)
+ return NULL;
+
+ return g_list_last (app->windows)->data;
+}
+
+/**
+ * terminal_profile_get_list:
+ *
+ * Returns: a #GList containing all #TerminalProfile objects.
+ * The content of the list is owned by the backend and
+ * should not be modified or freed. Use g_list_free() when done
+ * using the list.
+ */
+GList*
+terminal_app_get_profile_list (TerminalApp *app)
+{
+ g_return_val_if_fail (TERMINAL_IS_APP (app), NULL);
+
+ return g_list_sort (g_hash_table_get_values (app->profiles), profiles_alphabetic_cmp);
+}
+
+TerminalProfile*
+terminal_app_get_profile_by_name (TerminalApp *app,
+ const char *name)
+{
+ g_return_val_if_fail (TERMINAL_IS_APP (app), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ return g_hash_table_lookup (app->profiles, name);
+}
+
+TerminalProfile*
+terminal_app_get_profile_by_visible_name (TerminalApp *app,
+ const char *name)
+{
+ LookupInfo info;
+
+ g_return_val_if_fail (TERMINAL_IS_APP (app), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ info.result = NULL;
+ info.target = name;
+
+ g_hash_table_foreach (app->profiles,
+ profiles_lookup_by_visible_name_foreach,
+ &info);
+ return info.result;
+}
+
+TerminalProfile*
+terminal_app_get_default_profile (TerminalApp *app)
+{
+ g_return_val_if_fail (TERMINAL_IS_APP (app), NULL);
+
+ return app->default_profile;
+}
+
+TerminalProfile*
+terminal_app_get_profile_for_new_term (TerminalApp *app)
+{
+ GHashTableIter iter;
+ TerminalProfile *profile = NULL;
+ TerminalProfile **profileptr = &profile;
+
+ g_return_val_if_fail (TERMINAL_IS_APP (app), NULL);
+
+ if (app->default_profile)
+ return app->default_profile;
+
+ g_hash_table_iter_init (&iter, app->profiles);
+ if (g_hash_table_iter_next (&iter, NULL, (gpointer *) profileptr))
+ return profile;
+
+ return NULL;
+}
+
+GHashTable *
+terminal_app_get_encodings (TerminalApp *app)
+{
+ return app->encodings;
+}
+
+/**
+ * terminal_app_ensure_encoding:
+ * @app:
+ * @charset:
+ *
+ * Ensures there's a #TerminalEncoding for @charset available.
+ */
+TerminalEncoding *
+terminal_app_ensure_encoding (TerminalApp *app,
+ const char *charset)
+{
+ TerminalEncoding *encoding;
+
+ encoding = g_hash_table_lookup (app->encodings, charset);
+ if (encoding == NULL)
+ {
+ encoding = terminal_encoding_new (charset,
+ _("User Defined"),
+ TRUE,
+ TRUE /* scary! */);
+ g_hash_table_insert (app->encodings,
+ (gpointer) terminal_encoding_get_id (encoding),
+ encoding);
+ }
+
+ return encoding;
+}
+
+/**
+ * terminal_app_get_active_encodings:
+ *
+ * Returns: a newly allocated list of newly referenced #TerminalEncoding objects.
+ */
+GSList*
+terminal_app_get_active_encodings (TerminalApp *app)
+{
+ GSList *list = NULL;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, app->encodings);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ TerminalEncoding *encoding = (TerminalEncoding *) value;
+
+ if (!encoding->is_active)
+ continue;
+
+ list = g_slist_prepend (list, terminal_encoding_ref (encoding));
+ }
+
+ return g_slist_sort (list, (GCompareFunc) compare_encodings);
+}
+
+void
+terminal_app_save_config (TerminalApp *app,
+ GKeyFile *key_file)
+{
+ GList *lw;
+ guint n = 0;
+ GPtrArray *window_names_array;
+ char **window_names;
+ gsize len;
+
+ g_key_file_set_comment (key_file, NULL, NULL, "Written by " PACKAGE_STRING, NULL);
+
+ g_key_file_set_integer (key_file, TERMINAL_CONFIG_GROUP, TERMINAL_CONFIG_PROP_VERSION, TERMINAL_CONFIG_VERSION);
+ g_key_file_set_integer (key_file, TERMINAL_CONFIG_GROUP, TERMINAL_CONFIG_PROP_COMPAT_VERSION, TERMINAL_CONFIG_COMPAT_VERSION);
+
+ window_names_array = g_ptr_array_sized_new (g_list_length (app->windows) + 1);
+
+ for (lw = app->windows; lw != NULL; lw = lw->next)
+ {
+ TerminalWindow *window = TERMINAL_WINDOW (lw->data);
+ char *group;
+
+ group = g_strdup_printf ("Window%u", n++);
+ g_ptr_array_add (window_names_array, group);
+
+ terminal_window_save_state (window, key_file, group);
+ }
+
+ len = window_names_array->len;
+ g_ptr_array_add (window_names_array, NULL);
+ window_names = (char **) g_ptr_array_free (window_names_array, FALSE);
+ g_key_file_set_string_list (key_file, TERMINAL_CONFIG_GROUP, TERMINAL_CONFIG_PROP_WINDOWS, (const char * const *) window_names, len);
+ g_strfreev (window_names);
+}
+
+gboolean
+terminal_app_save_config_file (TerminalApp *app,
+ const char *file_name,
+ GError **error)
+{
+ GKeyFile *key_file;
+ char *data;
+ gsize len;
+ gboolean result;
+
+ key_file = g_key_file_new ();
+ terminal_app_save_config (app, key_file);
+
+ data = g_key_file_to_data (key_file, &len, NULL);
+ result = g_file_set_contents (file_name, data, len, error);
+ g_free (data);
+
+ return result;
+}