2 * Copyright (C) 2006-2007 Imendio AB
3 * Copyright (C) 2007-2008 Collabora Ltd.
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301 USA
20 * Authors: Martyn Russell <martyn@imendio.com>
21 * Xavier Claessens <xclaesse@gmail.com>
25 #include "empathy-geometry.h"
28 #include <tp-account-widgets/tpaw-utils.h>
30 #include "empathy-ui-utils.h"
31 #include "empathy-utils.h"
33 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
34 #include "empathy-debug.h"
36 #define GEOMETRY_DIR_CREATE_MODE (S_IRUSR | S_IWUSR | S_IXUSR)
37 #define GEOMETRY_FILE_CREATE_MODE (S_IRUSR | S_IWUSR)
39 /* geometry.ini file contains 2 groups:
40 * - one with position and size of each window
41 * - one with the maximized state of each window
42 * Windows are identified by a name. (e.g. "main-window") */
43 #define GEOMETRY_FILENAME "geometry.ini"
44 #define GEOMETRY_POSITION_FORMAT "%d,%d,%d,%d" /* "x,y,w,h" */
45 #define GEOMETRY_POSITION_GROUP "geometry"
46 #define GEOMETRY_MAXIMIZED_GROUP "maximized"
48 /* Key used to keep window's name inside the object's qdata */
49 #define GEOMETRY_NAME_KEY "geometry-name-key"
51 static guint store_id = 0;
54 geometry_real_store (GKeyFile *key_file)
61 content = g_key_file_to_data (key_file, &length, &error);
64 DEBUG ("Error: %s", error->message);
69 filename = g_build_filename (g_get_user_config_dir (),
70 PACKAGE_NAME, GEOMETRY_FILENAME, NULL);
72 if (!g_file_set_contents (filename, content, length, &error))
74 DEBUG ("Error: %s", error->message);
83 geometry_store_cb (gpointer key_file)
85 geometry_real_store (key_file);
92 geometry_schedule_store (GKeyFile *key_file)
95 g_source_remove (store_id);
97 store_id = g_timeout_add_seconds (1, geometry_store_cb, key_file);
101 geometry_get_key_file (void)
103 static GKeyFile *key_file = NULL;
107 if (key_file != NULL)
110 dir = g_build_filename (g_get_user_config_dir (), PACKAGE_NAME, NULL);
111 if (!g_file_test (dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
113 DEBUG ("Creating directory:'%s'", dir);
114 g_mkdir_with_parents (dir, GEOMETRY_DIR_CREATE_MODE);
117 filename = g_build_filename (dir, GEOMETRY_FILENAME, NULL);
120 key_file = g_key_file_new ();
121 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL);
128 empathy_geometry_save_values (GtkWindow *window,
136 gchar *position_str = NULL;
141 names = g_object_get_data (G_OBJECT (window), GEOMETRY_NAME_KEY);
143 g_return_if_fail (GTK_IS_WINDOW (window));
144 g_return_if_fail (names != NULL);
146 /* Don't save off-screen positioning */
147 if (!EMPATHY_RECT_IS_ON_SCREEN (x, y, w, h))
150 key_file = geometry_get_key_file ();
152 /* Save window size only if not maximized */
155 position_str = g_strdup_printf (GEOMETRY_POSITION_FORMAT, x, y, w, h);
158 g_hash_table_iter_init (&iter, names);
159 while (g_hash_table_iter_next (&iter, (gpointer) &name, NULL))
163 /* escape the name so that unwanted characters such as # are removed */
164 escaped_name = g_uri_escape_string (name, NULL, TRUE);
166 g_key_file_set_boolean (key_file, GEOMETRY_MAXIMIZED_GROUP,
167 escaped_name, maximized);
169 if (position_str != NULL)
170 g_key_file_set_string (key_file, GEOMETRY_POSITION_GROUP,
171 escaped_name, position_str);
173 g_free (escaped_name);
177 geometry_schedule_store (key_file);
178 g_free (position_str);
182 empathy_geometry_save (GtkWindow *window)
184 GdkWindow *gdk_window;
185 GdkWindowState window_state;
189 g_return_if_fail (GTK_IS_WINDOW (window));
191 if (!gtk_widget_get_visible (GTK_WIDGET (window)))
194 /* Get window geometry */
195 gtk_window_get_position (window, &x, &y);
196 gtk_window_get_size (window, &w, &h);
198 gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
199 window_state = gdk_window_get_state (gdk_window);
200 maximized = (window_state & GDK_WINDOW_STATE_MAXIMIZED) != 0;
202 empathy_geometry_save_values (window, x, y, w, h, maximized);
206 empathy_geometry_load (GtkWindow *window,
214 g_return_if_fail (GTK_IS_WINDOW (window));
215 g_return_if_fail (!TPAW_STR_EMPTY (name));
217 /* escape the name so that unwanted characters such as # are removed */
218 escaped_name = g_uri_escape_string (name, NULL, TRUE);
220 key_file = geometry_get_key_file ();
222 /* restore window size and position */
223 str = g_key_file_get_string (key_file, GEOMETRY_POSITION_GROUP,
229 sscanf (str, GEOMETRY_POSITION_FORMAT, &x, &y, &w, &h);
230 gtk_window_move (window, x, y);
231 gtk_window_resize (window, w, h);
234 /* restore window maximized state */
235 maximized = g_key_file_get_boolean (key_file, GEOMETRY_MAXIMIZED_GROUP,
239 gtk_window_maximize (window);
241 gtk_window_unmaximize (window);
244 g_free (escaped_name);
248 geometry_configure_event_cb (GtkWindow *window,
249 GdkEventConfigure *event,
252 empathy_geometry_save (window);
258 geometry_window_state_event_cb (GtkWindow *window,
259 GdkEventWindowState *event,
262 if ((event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) != 0)
264 empathy_geometry_save (window);
271 geometry_map_cb (GtkWindow *window,
278 /* The WM will replace this window, restore its last position */
279 names = g_object_get_data (G_OBJECT (window), GEOMETRY_NAME_KEY);
281 g_assert (names != NULL);
283 /* Use the first name we get in the hash table */
284 g_hash_table_iter_init (&iter, names);
285 g_assert (g_hash_table_iter_next (&iter, (gpointer) &name, NULL));
287 empathy_geometry_load (window, name);
291 empathy_geometry_bind (GtkWindow *window,
295 gboolean connect_sigs = FALSE;
297 g_return_if_fail (GTK_IS_WINDOW (window));
298 g_return_if_fail (!TPAW_STR_EMPTY (name));
300 /* Check if this window is already bound */
301 names = g_object_get_data (G_OBJECT (window), GEOMETRY_NAME_KEY);
305 names = g_hash_table_new_full (g_str_hash, g_str_equal,
308 g_object_set_data_full (G_OBJECT (window), GEOMETRY_NAME_KEY, names,
309 (GDestroyNotify) g_hash_table_unref);
311 else if (g_hash_table_lookup (names, name) != NULL)
316 /* Store the geometry name in the window's data */
317 g_hash_table_insert (names, g_strdup (name), GUINT_TO_POINTER (TRUE));
319 /* Load initial geometry */
320 empathy_geometry_load (window, name);
325 /* Track geometry changes */
326 g_signal_connect (window, "configure-event",
327 G_CALLBACK (geometry_configure_event_cb), NULL);
328 g_signal_connect (window, "window-state-event",
329 G_CALLBACK (geometry_window_state_event_cb), NULL);
330 g_signal_connect (window, "map",
331 G_CALLBACK (geometry_map_cb), NULL);
335 empathy_geometry_unbind (GtkWindow *window,
340 names = g_object_get_data (G_OBJECT (window), GEOMETRY_NAME_KEY);
344 g_hash_table_remove (names, name);
346 if (g_hash_table_size (names) > 0)
349 g_signal_handlers_disconnect_by_func (window,
350 geometry_configure_event_cb, NULL);
351 g_signal_handlers_disconnect_by_func (window,
352 geometry_window_state_event_cb, NULL);
353 g_signal_handlers_disconnect_by_func (window,
354 geometry_map_cb, NULL);
356 g_object_set_data (G_OBJECT (window), GEOMETRY_NAME_KEY, NULL);