Merge branch 'trivia'
[empathy-mirror.git] / libempathy-gtk / empathy-geometry.c
blob9cf63817ef0410718eb1855a3c29326cd66004ac
1 /*
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>
24 #include "config.h"
26 #include <sys/stat.h>
28 #include <glib.h>
29 #include <gdk/gdk.h>
31 #include "libempathy/empathy-utils.h"
32 #include "empathy-geometry.h"
33 #include "empathy-ui-utils.h"
35 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
36 #include <libempathy/empathy-debug.h>
38 #define GEOMETRY_DIR_CREATE_MODE (S_IRUSR | S_IWUSR | S_IXUSR)
39 #define GEOMETRY_FILE_CREATE_MODE (S_IRUSR | S_IWUSR)
41 /* geometry.ini file contains 2 groups:
42 * - one with position and size of each window
43 * - one with the maximized state of each window
44 * Windows are identified by a name. (e.g. "main-window") */
45 #define GEOMETRY_FILENAME "geometry.ini"
46 #define GEOMETRY_POSITION_FORMAT "%d,%d,%d,%d" /* "x,y,w,h" */
47 #define GEOMETRY_POSITION_GROUP "geometry"
48 #define GEOMETRY_MAXIMIZED_GROUP "maximized"
50 /* Key used to keep window's name inside the object's qdata */
51 #define GEOMETRY_NAME_KEY "geometry-name-key"
53 static guint store_id = 0;
55 static void
56 geometry_real_store (GKeyFile *key_file)
58 gchar *filename;
59 gchar *content;
60 gsize length;
61 GError *error = NULL;
63 content = g_key_file_to_data (key_file, &length, &error);
64 if (error != NULL)
66 DEBUG ("Error: %s", error->message);
67 g_error_free (error);
68 return;
71 filename = g_build_filename (g_get_user_config_dir (),
72 PACKAGE_NAME, GEOMETRY_FILENAME, NULL);
74 if (!g_file_set_contents (filename, content, length, &error))
76 DEBUG ("Error: %s", error->message);
77 g_error_free (error);
80 g_free (content);
81 g_free (filename);
84 static gboolean
85 geometry_store_cb (gpointer key_file)
87 geometry_real_store (key_file);
88 store_id = 0;
90 return FALSE;
93 static void
94 geometry_schedule_store (GKeyFile *key_file)
96 if (store_id != 0)
97 g_source_remove (store_id);
99 store_id = g_timeout_add_seconds (1, geometry_store_cb, key_file);
102 static GKeyFile *
103 geometry_get_key_file (void)
105 static GKeyFile *key_file = NULL;
106 gchar *dir;
107 gchar *filename;
109 if (key_file != NULL)
110 return key_file;
112 dir = g_build_filename (g_get_user_config_dir (), PACKAGE_NAME, NULL);
113 if (!g_file_test (dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
115 DEBUG ("Creating directory:'%s'", dir);
116 g_mkdir_with_parents (dir, GEOMETRY_DIR_CREATE_MODE);
119 filename = g_build_filename (dir, GEOMETRY_FILENAME, NULL);
120 g_free (dir);
122 key_file = g_key_file_new ();
123 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL);
124 g_free (filename);
126 return key_file;
129 void
130 empathy_geometry_save (GtkWindow *window,
131 const gchar *name)
133 GKeyFile *key_file;
134 GdkWindow *gdk_window;
135 GdkWindowState window_state;
136 gchar *escaped_name;
137 gint x, y, w, h;
138 gboolean maximized;
140 g_return_if_fail (GTK_IS_WINDOW (window));
141 g_return_if_fail (!EMP_STR_EMPTY (name));
143 if (!gtk_widget_get_visible (GTK_WIDGET (window)))
144 return;
146 /* escape the name so that unwanted characters such as # are removed */
147 escaped_name = g_uri_escape_string (name, NULL, TRUE);
149 /* Get window geometry */
150 gtk_window_get_position (window, &x, &y);
151 gtk_window_get_size (window, &w, &h);
152 gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
153 window_state = gdk_window_get_state (gdk_window);
154 maximized = (window_state & GDK_WINDOW_STATE_MAXIMIZED) != 0;
156 /* Don't save off-screen positioning */
157 if (!EMPATHY_RECT_IS_ON_SCREEN (x, y, w, h))
158 return;
160 key_file = geometry_get_key_file ();
162 /* Save window size only if not maximized */
163 if (!maximized)
165 gchar *str;
167 str = g_strdup_printf (GEOMETRY_POSITION_FORMAT, x, y, w, h);
168 g_key_file_set_string (key_file, GEOMETRY_POSITION_GROUP,
169 escaped_name, str);
170 g_free (str);
173 g_key_file_set_boolean (key_file, GEOMETRY_MAXIMIZED_GROUP,
174 escaped_name, maximized);
176 geometry_schedule_store (key_file);
177 g_free (escaped_name);
180 void
181 empathy_geometry_load (GtkWindow *window,
182 const gchar *name)
184 GKeyFile *key_file;
185 gchar *escaped_name;
186 gchar *str;
187 gboolean maximized;
189 g_return_if_fail (GTK_IS_WINDOW (window));
190 g_return_if_fail (!EMP_STR_EMPTY (name));
192 /* escape the name so that unwanted characters such as # are removed */
193 escaped_name = g_uri_escape_string (name, NULL, TRUE);
195 key_file = geometry_get_key_file ();
197 /* restore window size and position */
198 str = g_key_file_get_string (key_file, GEOMETRY_POSITION_GROUP,
199 escaped_name, NULL);
200 if (str)
202 gint x, y, w, h;
204 sscanf (str, GEOMETRY_POSITION_FORMAT, &x, &y, &w, &h);
205 gtk_window_move (window, x, y);
206 gtk_window_resize (window, w, h);
209 /* restore window maximized state */
210 maximized = g_key_file_get_boolean (key_file, GEOMETRY_MAXIMIZED_GROUP,
211 escaped_name, NULL);
213 if (maximized)
214 gtk_window_maximize (window);
215 else
216 gtk_window_unmaximize (window);
218 g_free (str);
219 g_free (escaped_name);
222 static gboolean
223 geometry_configure_event_cb (GtkWindow *window,
224 GdkEventConfigure *event,
225 gpointer user_data)
227 gchar *name;
229 name = g_object_get_data (G_OBJECT (window), GEOMETRY_NAME_KEY);
230 empathy_geometry_save (window, name);
232 return FALSE;
235 static gboolean
236 geometry_window_state_event_cb (GtkWindow *window,
237 GdkEventWindowState *event,
238 gpointer user_data)
240 if ((event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) != 0)
242 gchar *name;
244 name = g_object_get_data (G_OBJECT (window), GEOMETRY_NAME_KEY);
245 empathy_geometry_save (window, name);
248 return FALSE;
251 static void
252 geometry_map_cb (GtkWindow *window,
253 gpointer user_data)
255 gchar *name;
257 /* The WM will replace this window, restore its last position */
258 name = g_object_get_data (G_OBJECT (window), GEOMETRY_NAME_KEY);
259 empathy_geometry_load (window, name);
262 void
263 empathy_geometry_bind (GtkWindow *window,
264 const gchar *name)
266 gchar *str;
268 g_return_if_fail (GTK_IS_WINDOW (window));
269 g_return_if_fail (!EMP_STR_EMPTY (name));
271 /* Check if this window is already bound */
272 str = g_object_get_data (G_OBJECT (window), GEOMETRY_NAME_KEY);
273 if (str != NULL)
274 return;
276 /* Store the geometry name in the window's data */
277 str = g_strdup (name);
278 g_object_set_data_full (G_OBJECT (window), GEOMETRY_NAME_KEY, str, g_free);
280 /* Load initial geometry */
281 empathy_geometry_load (window, name);
283 /* Track geometry changes */
284 g_signal_connect (window, "configure-event",
285 G_CALLBACK (geometry_configure_event_cb), NULL);
286 g_signal_connect (window, "window-state-event",
287 G_CALLBACK (geometry_window_state_event_cb), NULL);
288 g_signal_connect (window, "map",
289 G_CALLBACK (geometry_map_cb), NULL);
292 void
293 empathy_geometry_unbind (GtkWindow *window)
295 g_signal_handlers_disconnect_by_func (window,
296 geometry_configure_event_cb, NULL);
297 g_signal_handlers_disconnect_by_func (window,
298 geometry_window_state_event_cb, NULL);
299 g_signal_handlers_disconnect_by_func (window,
300 geometry_map_cb, NULL);
302 g_object_set_data (G_OBJECT (window), GEOMETRY_NAME_KEY, NULL);