*/
#include "config.h"
+#include "empathy-geometry.h"
#include <sys/stat.h>
-#include <glib.h>
-#include <gdk/gdk.h>
-
-#include "libempathy/empathy-utils.h"
-#include "empathy-geometry.h"
+#include "empathy-ui-utils.h"
+#include "empathy-utils.h"
#define DEBUG_FLAG EMPATHY_DEBUG_OTHER
-#include <libempathy/empathy-debug.h>
+#include "empathy-debug.h"
#define GEOMETRY_DIR_CREATE_MODE (S_IRUSR | S_IWUSR | S_IXUSR)
#define GEOMETRY_FILE_CREATE_MODE (S_IRUSR | S_IWUSR)
-#define GEOMETRY_KEY_FILENAME "geometry.ini"
-#define GEOMETRY_FORMAT "%d,%d,%d,%d"
-#define GEOMETRY_GROUP_NAME "geometry"
-#define GEOMETRY_MAXIMIZED_GROUP_NAME "maximized"
+/* geometry.ini file contains 2 groups:
+ * - one with position and size of each window
+ * - one with the maximized state of each window
+ * Windows are identified by a name. (e.g. "main-window") */
+#define GEOMETRY_FILENAME "geometry.ini"
+#define GEOMETRY_POSITION_FORMAT "%d,%d,%d,%d" /* "x,y,w,h" */
+#define GEOMETRY_POSITION_GROUP "geometry"
+#define GEOMETRY_MAXIMIZED_GROUP "maximized"
+
+/* Key used to keep window's name inside the object's qdata */
+#define GEOMETRY_NAME_KEY "geometry-name-key"
static guint store_id = 0;
}
filename = g_build_filename (g_get_user_config_dir (),
- PACKAGE_NAME, GEOMETRY_KEY_FILENAME, NULL);
+ PACKAGE_NAME, GEOMETRY_FILENAME, NULL);
if (!g_file_set_contents (filename, content, length, &error))
{
g_mkdir_with_parents (dir, GEOMETRY_DIR_CREATE_MODE);
}
- filename = g_build_filename (dir, GEOMETRY_KEY_FILENAME, NULL);
+ filename = g_build_filename (dir, GEOMETRY_FILENAME, NULL);
g_free (dir);
key_file = g_key_file_new ();
}
void
-empathy_geometry_save (GtkWindow *window,
- const gchar *name)
+empathy_geometry_save_values (GtkWindow *window,
+ gint x,
+ gint y,
+ gint w,
+ gint h,
+ gboolean maximized)
{
GKeyFile *key_file;
- GdkWindow *gdk_window;
- GdkWindowState window_state;
- gchar *escaped_name;
- gint x, y, w, h;
- gboolean maximized;
+ gchar *position_str = NULL;
+ GHashTable *names;
+ GHashTableIter iter;
+ const gchar *name;
- g_return_if_fail (GTK_IS_WINDOW (window));
- g_return_if_fail (!EMP_STR_EMPTY (name));
+ names = g_object_get_data (G_OBJECT (window), GEOMETRY_NAME_KEY);
- /* escape the name so that unwanted characters such as # are removed */
- escaped_name = g_uri_escape_string (name, NULL, TRUE);
-
- /* Get window geometry */
- gtk_window_get_position (window, &x, &y);
- gtk_window_get_size (window, &w, &h);
- gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
- window_state = gdk_window_get_state (gdk_window);
- maximized = (window_state & GDK_WINDOW_STATE_MAXIMIZED) != 0;
+ g_return_if_fail (GTK_IS_WINDOW (window));
+ g_return_if_fail (names != NULL);
/* Don't save off-screen positioning */
- if (x + w < 0 || y + h < 0 ||
- x > gdk_screen_width () ||
- y > gdk_screen_height ())
+ if (!EMPATHY_RECT_IS_ON_SCREEN (x, y, w, h))
return;
key_file = geometry_get_key_file ();
/* Save window size only if not maximized */
if (!maximized)
{
- gchar *str;
+ position_str = g_strdup_printf (GEOMETRY_POSITION_FORMAT, x, y, w, h);
+ }
+
+ g_hash_table_iter_init (&iter, names);
+ while (g_hash_table_iter_next (&iter, (gpointer) &name, NULL))
+ {
+ gchar *escaped_name;
+
+ /* escape the name so that unwanted characters such as # are removed */
+ escaped_name = g_uri_escape_string (name, NULL, TRUE);
+
+ g_key_file_set_boolean (key_file, GEOMETRY_MAXIMIZED_GROUP,
+ escaped_name, maximized);
- str = g_strdup_printf (GEOMETRY_FORMAT, x, y, w, h);
- g_key_file_set_string (key_file, GEOMETRY_GROUP_NAME, escaped_name, str);
- g_free (str);
+ if (position_str != NULL)
+ g_key_file_set_string (key_file, GEOMETRY_POSITION_GROUP,
+ escaped_name, position_str);
+
+ g_free (escaped_name);
}
- g_key_file_set_boolean (key_file, GEOMETRY_MAXIMIZED_GROUP_NAME,
- escaped_name, maximized);
geometry_schedule_store (key_file);
+ g_free (position_str);
}
-void
+static void
+empathy_geometry_save (GtkWindow *window)
+{
+ GdkWindow *gdk_window;
+ GdkWindowState window_state;
+ gboolean maximized;
+ gint x, y, w, h;
+
+ g_return_if_fail (GTK_IS_WINDOW (window));
+
+ if (!gtk_widget_get_visible (GTK_WIDGET (window)))
+ return;
+
+ /* Get window geometry */
+ gtk_window_get_position (window, &x, &y);
+ gtk_window_get_size (window, &w, &h);
+
+ gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
+ window_state = gdk_window_get_state (gdk_window);
+ maximized = (window_state & GDK_WINDOW_STATE_MAXIMIZED) != 0;
+
+ empathy_geometry_save_values (window, x, y, w, h, maximized);
+}
+
+static void
empathy_geometry_load (GtkWindow *window,
const gchar *name)
{
key_file = geometry_get_key_file ();
/* restore window size and position */
- str = g_key_file_get_string (key_file, GEOMETRY_GROUP_NAME, escaped_name,
- NULL);
+ str = g_key_file_get_string (key_file, GEOMETRY_POSITION_GROUP,
+ escaped_name, NULL);
if (str)
{
gint x, y, w, h;
- sscanf (str, GEOMETRY_FORMAT, &x, &y, &w, &h);
+ sscanf (str, GEOMETRY_POSITION_FORMAT, &x, &y, &w, &h);
gtk_window_move (window, x, y);
gtk_window_resize (window, w, h);
}
/* restore window maximized state */
- maximized = g_key_file_get_boolean (key_file,
- GEOMETRY_MAXIMIZED_GROUP_NAME,
+ maximized = g_key_file_get_boolean (key_file, GEOMETRY_MAXIMIZED_GROUP,
escaped_name, NULL);
if (maximized)
static gboolean
geometry_configure_event_cb (GtkWindow *window,
GdkEventConfigure *event,
- gchar *name)
+ gpointer user_data)
{
- empathy_geometry_save (window, name);
+ empathy_geometry_save (window);
+
return FALSE;
}
static gboolean
geometry_window_state_event_cb (GtkWindow *window,
GdkEventWindowState *event,
- gchar *name)
+ gpointer user_data)
{
if ((event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) != 0)
- empathy_geometry_save (window, name);
+ {
+ empathy_geometry_save (window);
+ }
return FALSE;
}
+static void
+geometry_map_cb (GtkWindow *window,
+ gpointer user_data)
+{
+ GHashTable *names;
+ GHashTableIter iter;
+ const gchar *name;
+
+ /* The WM will replace this window, restore its last position */
+ names = g_object_get_data (G_OBJECT (window), GEOMETRY_NAME_KEY);
+
+ g_assert (names != NULL);
+
+ /* Use the first name we get in the hash table */
+ g_hash_table_iter_init (&iter, names);
+ g_assert (g_hash_table_iter_next (&iter, (gpointer) &name, NULL));
+
+ empathy_geometry_load (window, name);
+}
+
void
empathy_geometry_bind (GtkWindow *window,
const gchar *name)
{
+ GHashTable *names;
+ gboolean connect_sigs = FALSE;
+
g_return_if_fail (GTK_IS_WINDOW (window));
g_return_if_fail (!EMP_STR_EMPTY (name));
- /* First load initial geometry */
+ /* Check if this window is already bound */
+ names = g_object_get_data (G_OBJECT (window), GEOMETRY_NAME_KEY);
+ if (names == NULL)
+ {
+ connect_sigs = TRUE;
+ names = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, NULL);
+
+ g_object_set_data_full (G_OBJECT (window), GEOMETRY_NAME_KEY, names,
+ (GDestroyNotify) g_hash_table_unref);
+ }
+ else if (g_hash_table_lookup (names, name) != NULL)
+ {
+ return;
+ }
+
+ /* Store the geometry name in the window's data */
+ g_hash_table_insert (names, g_strdup (name), GUINT_TO_POINTER (TRUE));
+
+ /* Load initial geometry */
empathy_geometry_load (window, name);
+ if (!connect_sigs)
+ return;
+
/* Track geometry changes */
- g_signal_connect_data (window, "configure-event",
- G_CALLBACK (geometry_configure_event_cb), g_strdup (name),
- (GClosureNotify) g_free, 0);
- g_signal_connect_data (window, "window-state-event",
- G_CALLBACK (geometry_window_state_event_cb), g_strdup (name),
- (GClosureNotify) g_free, 0);
+ g_signal_connect (window, "configure-event",
+ G_CALLBACK (geometry_configure_event_cb), NULL);
+ g_signal_connect (window, "window-state-event",
+ G_CALLBACK (geometry_window_state_event_cb), NULL);
+ g_signal_connect (window, "map",
+ G_CALLBACK (geometry_map_cb), NULL);
}
void
-empathy_geometry_unbind (GtkWindow *window)
+empathy_geometry_unbind (GtkWindow *window,
+ const gchar *name)
{
- g_signal_handlers_disconnect_matched (window,
- G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
+ GHashTable *names;
+
+ names = g_object_get_data (G_OBJECT (window), GEOMETRY_NAME_KEY);
+ if (names == NULL)
+ return;
+
+ g_hash_table_remove (names, name);
+
+ if (g_hash_table_size (names) > 0)
+ return;
+
+ g_signal_handlers_disconnect_by_func (window,
geometry_configure_event_cb, NULL);
- g_signal_handlers_disconnect_matched (window,
- G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
+ g_signal_handlers_disconnect_by_func (window,
geometry_window_state_event_cb, NULL);
+ g_signal_handlers_disconnect_by_func (window,
+ geometry_map_cb, NULL);
+
+ g_object_set_data (G_OBJECT (window), GEOMETRY_NAME_KEY, NULL);
}