]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-geometry.c
Use double quotes for all internal headers
[empathy.git] / libempathy-gtk / empathy-geometry.c
1 /*
2  * Copyright (C) 2006-2007 Imendio AB
3  * Copyright (C) 2007-2008 Collabora Ltd.
4  *
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.
9  *
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.
14  *
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
19  *
20  * Authors: Martyn Russell <martyn@imendio.com>
21  *          Xavier Claessens <xclaesse@gmail.com>
22  */
23
24 #include "config.h"
25
26 #include <sys/stat.h>
27
28 #include "libempathy/empathy-utils.h"
29 #include "empathy-geometry.h"
30 #include "empathy-ui-utils.h"
31
32 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
33 #include "libempathy/empathy-debug.h"
34
35 #define GEOMETRY_DIR_CREATE_MODE  (S_IRUSR | S_IWUSR | S_IXUSR)
36 #define GEOMETRY_FILE_CREATE_MODE (S_IRUSR | S_IWUSR)
37
38 /* geometry.ini file contains 2 groups:
39  *  - one with position and size of each window
40  *  - one with the maximized state of each window
41  * Windows are identified by a name. (e.g. "main-window") */
42 #define GEOMETRY_FILENAME             "geometry.ini"
43 #define GEOMETRY_POSITION_FORMAT      "%d,%d,%d,%d" /* "x,y,w,h" */
44 #define GEOMETRY_POSITION_GROUP       "geometry"
45 #define GEOMETRY_MAXIMIZED_GROUP      "maximized"
46
47 /* Key used to keep window's name inside the object's qdata */
48 #define GEOMETRY_NAME_KEY             "geometry-name-key"
49
50 static guint store_id = 0;
51
52 static void
53 geometry_real_store (GKeyFile *key_file)
54 {
55   gchar *filename;
56   gchar *content;
57   gsize length;
58   GError *error = NULL;
59
60   content = g_key_file_to_data (key_file, &length, &error);
61   if (error != NULL)
62     {
63       DEBUG ("Error: %s", error->message);
64       g_error_free (error);
65       return;
66     }
67
68   filename = g_build_filename (g_get_user_config_dir (),
69     PACKAGE_NAME, GEOMETRY_FILENAME, NULL);
70
71   if (!g_file_set_contents (filename, content, length, &error))
72     {
73       DEBUG ("Error: %s", error->message);
74       g_error_free (error);
75     }
76
77   g_free (content);
78   g_free (filename);
79 }
80
81 static gboolean
82 geometry_store_cb (gpointer key_file)
83 {
84   geometry_real_store (key_file);
85   store_id = 0;
86
87   return FALSE;
88 }
89
90 static void
91 geometry_schedule_store (GKeyFile *key_file)
92 {
93   if (store_id != 0)
94     g_source_remove (store_id);
95
96   store_id = g_timeout_add_seconds (1, geometry_store_cb, key_file);
97 }
98
99 static GKeyFile *
100 geometry_get_key_file (void)
101 {
102   static GKeyFile *key_file = NULL;
103   gchar *dir;
104   gchar *filename;
105
106   if (key_file != NULL)
107     return key_file;
108
109   dir = g_build_filename (g_get_user_config_dir (), PACKAGE_NAME, NULL);
110   if (!g_file_test (dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
111     {
112       DEBUG ("Creating directory:'%s'", dir);
113       g_mkdir_with_parents (dir, GEOMETRY_DIR_CREATE_MODE);
114     }
115
116   filename = g_build_filename (dir, GEOMETRY_FILENAME, NULL);
117   g_free (dir);
118
119   key_file = g_key_file_new ();
120   g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL);
121   g_free (filename);
122
123   return key_file;
124 }
125
126 void
127 empathy_geometry_save_values (GtkWindow *window,
128     gint x,
129     gint y,
130     gint w,
131     gint h,
132     gboolean maximized)
133 {
134   GKeyFile *key_file;
135   gchar *position_str = NULL;
136   GHashTable *names;
137   GHashTableIter iter;
138   const gchar *name;
139
140   names = g_object_get_data (G_OBJECT (window), GEOMETRY_NAME_KEY);
141
142   g_return_if_fail (GTK_IS_WINDOW (window));
143   g_return_if_fail (names != NULL);
144
145   /* Don't save off-screen positioning */
146   if (!EMPATHY_RECT_IS_ON_SCREEN (x, y, w, h))
147     return;
148
149   key_file = geometry_get_key_file ();
150
151   /* Save window size only if not maximized */
152   if (!maximized)
153     {
154       position_str = g_strdup_printf (GEOMETRY_POSITION_FORMAT, x, y, w, h);
155     }
156
157   g_hash_table_iter_init (&iter, names);
158   while (g_hash_table_iter_next (&iter, (gpointer) &name, NULL))
159     {
160       gchar *escaped_name;
161
162       /* escape the name so that unwanted characters such as # are removed */
163       escaped_name = g_uri_escape_string (name, NULL, TRUE);
164
165       g_key_file_set_boolean (key_file, GEOMETRY_MAXIMIZED_GROUP,
166           escaped_name, maximized);
167
168       if (position_str != NULL)
169         g_key_file_set_string (key_file, GEOMETRY_POSITION_GROUP,
170             escaped_name, position_str);
171
172       g_free (escaped_name);
173     }
174
175
176   geometry_schedule_store (key_file);
177   g_free (position_str);
178 }
179
180 static void
181 empathy_geometry_save (GtkWindow *window)
182 {
183   GdkWindow *gdk_window;
184   GdkWindowState window_state;
185   gboolean maximized;
186   gint x, y, w, h;
187
188   g_return_if_fail (GTK_IS_WINDOW (window));
189
190   if (!gtk_widget_get_visible (GTK_WIDGET (window)))
191     return;
192
193   /* Get window geometry */
194   gtk_window_get_position (window, &x, &y);
195   gtk_window_get_size (window, &w, &h);
196
197   gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
198   window_state = gdk_window_get_state (gdk_window);
199   maximized = (window_state & GDK_WINDOW_STATE_MAXIMIZED) != 0;
200
201   empathy_geometry_save_values (window, x, y, w, h, maximized);
202 }
203
204 static void
205 empathy_geometry_load (GtkWindow *window,
206     const gchar *name)
207 {
208   GKeyFile *key_file;
209   gchar    *escaped_name;
210   gchar    *str;
211   gboolean  maximized;
212
213   g_return_if_fail (GTK_IS_WINDOW (window));
214   g_return_if_fail (!EMP_STR_EMPTY (name));
215
216   /* escape the name so that unwanted characters such as # are removed */
217   escaped_name = g_uri_escape_string (name, NULL, TRUE);
218
219   key_file = geometry_get_key_file ();
220
221   /* restore window size and position */
222   str = g_key_file_get_string (key_file, GEOMETRY_POSITION_GROUP,
223       escaped_name, NULL);
224   if (str)
225     {
226       gint x, y, w, h;
227
228       sscanf (str, GEOMETRY_POSITION_FORMAT, &x, &y, &w, &h);
229       gtk_window_move (window, x, y);
230       gtk_window_resize (window, w, h);
231     }
232
233   /* restore window maximized state */
234   maximized = g_key_file_get_boolean (key_file, GEOMETRY_MAXIMIZED_GROUP,
235       escaped_name, NULL);
236
237   if (maximized)
238     gtk_window_maximize (window);
239   else
240     gtk_window_unmaximize (window);
241
242   g_free (str);
243   g_free (escaped_name);
244 }
245
246 static gboolean
247 geometry_configure_event_cb (GtkWindow *window,
248     GdkEventConfigure *event,
249     gpointer user_data)
250 {
251   empathy_geometry_save (window);
252
253   return FALSE;
254 }
255
256 static gboolean
257 geometry_window_state_event_cb (GtkWindow *window,
258     GdkEventWindowState *event,
259     gpointer user_data)
260 {
261   if ((event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) != 0)
262     {
263       empathy_geometry_save (window);
264     }
265
266   return FALSE;
267 }
268
269 static void
270 geometry_map_cb (GtkWindow *window,
271     gpointer user_data)
272 {
273   GHashTable *names;
274   GHashTableIter iter;
275   const gchar *name;
276
277   /* The WM will replace this window, restore its last position */
278   names = g_object_get_data (G_OBJECT (window), GEOMETRY_NAME_KEY);
279
280   g_assert (names != NULL);
281
282   /* Use the first name we get in the hash table */
283   g_hash_table_iter_init (&iter, names);
284   g_assert (g_hash_table_iter_next (&iter, (gpointer) &name, NULL));
285
286   empathy_geometry_load (window, name);
287 }
288
289 void
290 empathy_geometry_bind (GtkWindow *window,
291     const gchar *name)
292 {
293   GHashTable *names;
294   gboolean connect_sigs = FALSE;
295
296   g_return_if_fail (GTK_IS_WINDOW (window));
297   g_return_if_fail (!EMP_STR_EMPTY (name));
298
299   /* Check if this window is already bound */
300   names = g_object_get_data (G_OBJECT (window), GEOMETRY_NAME_KEY);
301   if (names == NULL)
302     {
303       connect_sigs = TRUE;
304       names = g_hash_table_new_full (g_str_hash, g_str_equal,
305           g_free, NULL);
306
307       g_object_set_data_full (G_OBJECT (window), GEOMETRY_NAME_KEY, names,
308           (GDestroyNotify) g_hash_table_unref);
309     }
310   else if (g_hash_table_lookup (names, name) != NULL)
311     {
312       return;
313     }
314
315   /* Store the geometry name in the window's data */
316   g_hash_table_insert (names, g_strdup (name), GUINT_TO_POINTER (TRUE));
317
318   /* Load initial geometry */
319   empathy_geometry_load (window, name);
320
321   if (!connect_sigs)
322     return;
323
324   /* Track geometry changes */
325   g_signal_connect (window, "configure-event",
326     G_CALLBACK (geometry_configure_event_cb), NULL);
327   g_signal_connect (window, "window-state-event",
328     G_CALLBACK (geometry_window_state_event_cb), NULL);
329   g_signal_connect (window, "map",
330     G_CALLBACK (geometry_map_cb), NULL);
331 }
332
333 void
334 empathy_geometry_unbind (GtkWindow *window,
335     const gchar *name)
336 {
337   GHashTable *names;
338
339   names = g_object_get_data (G_OBJECT (window), GEOMETRY_NAME_KEY);
340   if (names == NULL)
341     return;
342
343   g_hash_table_remove (names, name);
344
345   if (g_hash_table_size (names) > 0)
346     return;
347
348   g_signal_handlers_disconnect_by_func (window,
349     geometry_configure_event_cb, NULL);
350   g_signal_handlers_disconnect_by_func (window,
351     geometry_window_state_event_cb, NULL);
352   g_signal_handlers_disconnect_by_func (window,
353     geometry_map_cb, NULL);
354
355   g_object_set_data (G_OBJECT (window), GEOMETRY_NAME_KEY, NULL);
356 }