1 /* vi:set sw=2 sts=2 ts=2 et ai tw=100: */
2 /*-
3  * Copyright (c) 2008 Stephan Arts <stephan@xfce.org>
4  * Copyright (c) 2008 Jannis Pohlmann <jannis@xfce.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or (at
9  * your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., Inc., 51 Franklin Street, Fifth Floor, Boston,
19  * MA 02110-1301, USA.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 #ifdef HAVE_STDLIB_H
27 #include <stdlib.h>
28 #endif
29 
30 #ifdef HAVE_STRING_H
31 #include <string.h>
32 #endif
33 
34 #include <glib.h>
35 #include <glib-object.h>
36 
37 #include <gdk/gdk.h>
38 #include <gdk/gdkx.h>
39 #include <gtk/gtk.h>
40 #include <gtk/gtkx.h>
41 
42 #include <libxfce4util/libxfce4util.h>
43 #include <libxfce4ui/libxfce4ui.h>
44 #include <xfconf/xfconf.h>
45 #include <libxfce4kbd-private/xfce-shortcut-dialog.h>
46 #include <libxfce4kbd-private/xfce-shortcuts-provider.h>
47 #include <libxfce4kbd-private/xfce-shortcuts-xfwm4.h>
48 
49 #include <common/xfwm-common.h>
50 
51 #include "xfwm4-dialog_ui.h"
52 #include "xfwm4-settings.h"
53 #include "range-debouncer.h"
54 
55 
56 
57 #define DEFAULT_THEME                   "Greybird"
58 
59 #define SHORTCUTS_NAME_COLUMN           0
60 #define SHORTCUTS_FEATURE_COLUMN        1
61 #define SHORTCUTS_SHORTCUT_COLUMN       2
62 #define SHORTCUTS_SHORTCUT_LABEL_COLUMN 3
63 
64 
65 
66 typedef struct _MenuTemplate            MenuTemplate;
67 
68 
69 
70 /* Property identifiers */
71 enum
72 {
73   PROP_0,
74   PROP_GTK_BUILDER,
75 };
76 
77 
78 
79 static void       xfwm_settings_constructed                          (GObject              *object);
80 static void       xfwm_settings_finalize                             (GObject              *object);
81 static void       xfwm_settings_get_property                         (GObject              *object,
82                                                                       guint                 prop_id,
83                                                                       GValue               *value,
84                                                                       GParamSpec           *pspec);
85 static void       xfwm_settings_set_property                         (GObject              *object,
86                                                                       guint                 prop_id,
87                                                                       const GValue         *value,
88                                                                       GParamSpec           *pspec);
89 static gint       xfwm_settings_theme_sort_func                      (GtkTreeModel         *model,
90                                                                       GtkTreeIter          *iter1,
91                                                                       GtkTreeIter          *iter2,
92                                                                       gpointer              data);
93 static void       xfwm_settings_load_themes                          (XfwmSettings         *settings);
94 static void       xfwm_settings_theme_selection_changed              (GtkTreeSelection     *selection,
95                                                                       XfwmSettings         *settings);
96 static void       xfwm_settings_title_alignment_changed              (GtkComboBox          *combo,
97                                                                       XfwmSettings         *settings);
98 static void       xfwm_settings_active_frame_drag_data               (GtkWidget            *widget,
99                                                                       GdkDragContext       *drag_context,
100                                                                       gint                  x,
101                                                                       gint                  y,
102                                                                       GtkSelectionData     *data,
103                                                                       guint                 info,
104                                                                       guint                 time,
105                                                                       XfwmSettings         *settings);
106 static gboolean   xfwm_settings_active_frame_drag_motion             (GtkWidget            *widget,
107                                                                       GdkDragContext       *drag_context,
108                                                                       gint                  x,
109                                                                       gint                  y,
110                                                                       guint                 time,
111                                                                       XfwmSettings         *settings);
112 static void       xfwm_settings_active_frame_drag_leave              (GtkWidget            *widget,
113                                                                       GdkDragContext       *drag_context,
114                                                                       guint                 time,
115                                                                       XfwmSettings         *settings);
116 static void       xfwm_settings_hidden_frame_drag_data               (GtkWidget            *widget,
117                                                                       GdkDragContext       *drag_context,
118                                                                       gint                  x,
119                                                                       gint                  y,
120                                                                       GtkSelectionData     *data,
121                                                                       guint                 info,
122                                                                       guint                 timestamp,
123                                                                       XfwmSettings         *settings);
124 static gboolean   xfwm_settings_title_button_press_event             (GtkWidget            *widget);
125 static void       xfwm_settings_title_button_drag_data               (GtkWidget            *widget,
126                                                                       GdkDragContext       *drag_context,
127                                                                       GtkSelectionData     *data,
128                                                                       guint                 info,
129                                                                       guint                 timestamp);
130 static void       xfwm_settings_title_button_drag_begin              (GtkWidget            *widget,
131                                                                       GdkDragContext       *drag_context);
132 static void       xfwm_settings_title_button_drag_end                (GtkWidget            *widget,
133                                                                       GdkDragContext       *drag_context);
134 static gboolean   xfwm_settings_active_frame_draw                    (GtkWidget            *widget,
135                                                                       cairo_t              *cr,
136                                                                       XfwmSettings         *settings);
137 static gboolean   xfwm_settings_signal_blocker                       (GtkWidget            *widget);
138 
139 /* FIXME! */
140 #if 0
141 static GdkPixbuf *xfwm_settings_create_icon_from_widget              (GtkWidget            *widget);
142 #endif
143 
144 static void       xfwm_settings_save_button_layout                   (XfwmSettings          *settings,
145                                                                       GtkContainer          *container);
146 static void       xfwm_settings_double_click_action_changed          (GtkComboBox           *combo,
147                                                                       XfwmSettings          *settings);
148 static void       xfwm_settings_title_button_alignment_changed       (GtkComboBox           *combo,
149                                                                       GtkWidget             *button);
150 
151 static void       xfwm_settings_button_layout_property_changed       (XfconfChannel         *channel,
152                                                                       const gchar           *property,
153                                                                       const GValue          *value,
154                                                                       XfwmSettings          *settings);
155 static void       xfwm_settings_title_alignment_property_changed     (XfconfChannel         *channel,
156                                                                       const gchar           *property,
157                                                                       const GValue          *value,
158                                                                       XfwmSettings          *settings);
159 static void       xfwm_settings_double_click_action_property_changed (XfconfChannel         *channel,
160                                                                       const gchar           *property,
161                                                                       const GValue          *value,
162                                                                       XfwmSettings          *settings);
163 static void       xfwm_settings_click_to_focus_property_changed      (XfconfChannel         *channel,
164                                                                       const gchar           *property,
165                                                                       const GValue          *value,
166                                                                       XfwmSettings          *settings);
167 static void       xfwm_settings_initialize_shortcuts                 (XfwmSettings          *settings);
168 static void       xfwm_settings_reload_shortcuts                     (XfwmSettings          *settings);
169 static void       xfwm_settings_shortcut_added                       (XfceShortcutsProvider *provider,
170                                                                       const gchar           *shortcut,
171                                                                       XfwmSettings          *settings);
172 static void       xfwm_settings_shortcut_removed                     (XfceShortcutsProvider *provider,
173                                                                       const gchar           *shortcut,
174                                                                       XfwmSettings          *settings);
175 static gboolean   xfwm_settings_update_treeview_on_conflict_replace  (GtkTreeModel          *model,
176                                                                       GtkTreePath           *path,
177                                                                       GtkTreeIter           *iter,
178                                                                       gpointer               shortcut_to_erase);
179 static void       xfwm_settings_shortcut_edit_clicked                (GtkButton             *button,
180                                                                       XfwmSettings          *settings);
181 static void       xfwm_settings_shortcut_clear_clicked               (GtkButton             *button,
182                                                                       XfwmSettings          *settings);
183 static void       xfwm_settings_shortcut_reset_clicked               (GtkButton             *button,
184                                                                       XfwmSettings          *settings);
185 static void       xfwm_settings_shortcut_row_activated               (GtkTreeView           *tree_view,
186                                                                       GtkTreePath           *path,
187                                                                       GtkTreeViewColumn     *column,
188                                                                       XfwmSettings          *settings);
189 
190 
191 
192 struct _XfwmSettingsPrivate
193 {
194   GtkBuilder            *builder;
195   XfceShortcutsProvider *provider;
196   XfconfChannel         *wm_channel;
197 };
198 
199 G_DEFINE_TYPE_WITH_PRIVATE (XfwmSettings, xfwm_settings, G_TYPE_OBJECT)
200 
201 struct _MenuTemplate
202 {
203   const gchar *name;
204   const gchar *value;
205 };
206 
207 enum
208 {
209   COL_THEME_NAME,
210   COL_THEME_RC,
211   N_COLUMNS
212 };
213 
214 
215 static const MenuTemplate double_click_values[] = {
216   { N_("Shade window"), "shade" },
217   { N_("Hide window"), "hide" },
218   { N_("Maximize window"), "maximize" },
219   { N_("Fill window"), "fill" },
220   { N_("Always on top"), "above" },
221   { N_("Nothing"), "none" },
222   { NULL, NULL }
223 };
224 
225 static const MenuTemplate title_align_values[] = {
226   { N_("Left"), "left" },
227   { N_("Center"), "center" },
228   { N_("Right"), "right" },
229   { NULL, NULL },
230 };
231 
232 
233 
234 static gboolean           opt_version = FALSE;
235 static Window             opt_socket_id = 0;
236 static GOptionEntry       opt_entries[] = {
237   { "socket-id", 's', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_INT, &opt_socket_id,
238     N_("Settings manager socket"), N_("SOCKET ID") },
239   { "version", 'V', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE, &opt_version,
240     N_("Version information"), NULL },
241   { NULL }
242 };
243 
244 
245 
246 static void
xfwm_settings_class_init(XfwmSettingsClass * klass)247 xfwm_settings_class_init (XfwmSettingsClass *klass)
248 {
249   GObjectClass *gobject_class;
250 
251   gobject_class = G_OBJECT_CLASS (klass);
252   gobject_class->constructed = xfwm_settings_constructed;
253   gobject_class->finalize = xfwm_settings_finalize;
254   gobject_class->get_property = xfwm_settings_get_property;
255   gobject_class->set_property = xfwm_settings_set_property;
256 
257   g_object_class_install_property (gobject_class,
258                                    PROP_GTK_BUILDER,
259                                    g_param_spec_object ("gtk-builder",
260                                                         "gtk-builder",
261                                                         "gtk-builder",
262                                                         GTK_TYPE_BUILDER,
263                                                         G_PARAM_CONSTRUCT_ONLY |
264                                                         G_PARAM_WRITABLE));
265 }
266 
267 
268 
269 static void
xfwm_settings_init(XfwmSettings * settings)270 xfwm_settings_init (XfwmSettings *settings)
271 {
272   settings->priv = xfwm_settings_get_instance_private (settings);
273 
274   settings->priv->builder = NULL;
275   settings->priv->provider = xfce_shortcuts_provider_new ("xfwm4");
276   settings->priv->wm_channel = xfconf_channel_new ("xfwm4");
277 }
278 
279 
280 
281 static void
xfwm_settings_constructed(GObject * object)282 xfwm_settings_constructed (GObject *object)
283 {
284   XfwmSettings       *settings = XFWM_SETTINGS (object);
285   const MenuTemplate *template;
286   GtkTreeSelection   *selection;
287   GtkCellRenderer    *renderer;
288   GtkTargetEntry      target_entry[2];
289   GtkListStore       *list_store;
290   GtkTreeIter         iter;
291   GtkWidget          *theme_name_treeview;
292   GtkWidget          *title_font_button;
293   GtkWidget          *title_align_combo;
294   GtkWidget          *active_frame;
295   GtkWidget          *active_box;
296   GtkWidget          *hidden_frame;
297   GtkWidget          *hidden_box;
298   GtkWidget          *shortcuts_treeview;
299   GtkWidget          *shortcuts_edit_button;
300   GtkWidget          *shortcuts_clear_button;
301   GtkWidget          *shortcuts_reset_button;
302   GtkWidget          *focus_delay_scale;
303   GtkWidget          *focus_raise_delay_scale;
304   GtkWidget          *raise_on_click_check;
305   GtkWidget          *raise_on_focus_check;
306   GtkWidget          *click_to_focus_radio;
307   GtkWidget          *focus_new_check;
308   GtkWidget          *box_move_check;
309   GtkWidget          *box_resize_check;
310   GtkWidget          *wrap_workspaces_check;
311   GtkWidget          *wrap_windows_check;
312   GtkWidget          *snap_to_border_check;
313   GtkWidget          *snap_to_window_check;
314   GtkWidget          *double_click_action_combo;
315   GtkWidget          *snap_width_scale;
316   GtkWidget          *wrap_resistance_scale;
317   GtkWidget          *button;
318   GValue              value = { 0, };
319   GList              *children;
320   GList              *list_iter;
321   const gchar        *name;
322 
323   /* Style tab widgets */
324   theme_name_treeview = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "theme_name_treeview"));
325   title_font_button = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "title_font_button"));
326   title_align_combo = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "title_align_combo"));
327   active_frame = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "active-frame"));
328   active_box = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "active-box"));
329   hidden_frame = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "hidden-frame"));
330   hidden_box = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "hidden-box"));
331 
332   /* Style tab: theme name */
333   {
334     list_store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING);
335     gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (list_store), COL_THEME_NAME,
336                                      (GtkTreeIterCompareFunc) xfwm_settings_theme_sort_func,
337                                      NULL, NULL);
338     gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list_store), COL_THEME_NAME, GTK_SORT_ASCENDING);
339     gtk_tree_view_set_model (GTK_TREE_VIEW (theme_name_treeview), GTK_TREE_MODEL (list_store));
340     g_object_unref (G_OBJECT (list_store));
341 
342     renderer = gtk_cell_renderer_text_new ();
343     gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (theme_name_treeview),
344                                                  0, _("Theme"), renderer, "text", 0, NULL);
345 
346     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (theme_name_treeview));
347     g_signal_connect (selection, "changed", G_CALLBACK (xfwm_settings_theme_selection_changed),
348                       settings);
349     gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
350 
351     xfwm_settings_load_themes (settings);
352   }
353 
354   /* Style tab: font */
355   xfconf_g_property_bind (settings->priv->wm_channel, "/general/title_font", G_TYPE_STRING,
356                           title_font_button, "font-name");
357 
358   /* Style tab: title alignment */
359   {
360     gtk_cell_layout_clear (GTK_CELL_LAYOUT (title_align_combo));
361 
362     renderer = gtk_cell_renderer_text_new ();
363     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (title_align_combo), renderer, TRUE);
364     gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (title_align_combo), renderer, "text", 0);
365 
366     list_store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
367     gtk_combo_box_set_model (GTK_COMBO_BOX (title_align_combo), GTK_TREE_MODEL (list_store));
368 
369     for (template = title_align_values; template->name != NULL; ++template)
370       {
371         gtk_list_store_append (list_store, &iter);
372         gtk_list_store_set (list_store, &iter, 0, _(template->name), 1, template->value, -1);
373       }
374     g_object_unref (G_OBJECT (list_store));
375     xfconf_channel_get_property (settings->priv->wm_channel, "/general/title_alignment", &value);
376     xfwm_settings_title_alignment_property_changed (settings->priv->wm_channel,
377                                                     "/general/title_alignment", &value, settings);
378     g_value_unset (&value);
379 
380     g_signal_connect (title_align_combo, "changed",
381                       G_CALLBACK (xfwm_settings_title_alignment_changed), settings);
382     g_signal_connect (settings->priv->wm_channel, "property-changed::/general/title_alignment",
383                       G_CALLBACK (xfwm_settings_title_alignment_property_changed), settings);
384   }
385 
386   /* Style tab: button layout */
387   {
388     target_entry[0].target = "_xfwm4_button_layout";
389     target_entry[0].flags = 0;
390     target_entry[0].info = 2;
391 
392     target_entry[1].target = "_xfwm4_active_layout";
393     target_entry[1].flags = 0;
394     target_entry[1].info = 3;
395 
396     gtk_drag_dest_set (active_frame, GTK_DEST_DEFAULT_ALL, target_entry, 2, GDK_ACTION_MOVE);
397 
398     g_signal_connect (active_frame, "drag-data-received",
399                       G_CALLBACK (xfwm_settings_active_frame_drag_data), settings);
400     g_signal_connect (active_frame, "drag-motion",
401                       G_CALLBACK (xfwm_settings_active_frame_drag_motion), settings);
402     g_signal_connect (active_frame, "drag-leave",
403                       G_CALLBACK (xfwm_settings_active_frame_drag_leave), settings);
404 
405     gtk_drag_dest_set (hidden_frame, GTK_DEST_DEFAULT_ALL, target_entry, 1, GDK_ACTION_MOVE);
406 
407     g_signal_connect (hidden_frame, "drag-data-received",
408                       G_CALLBACK (xfwm_settings_hidden_frame_drag_data), settings);
409 
410     g_object_set_data (G_OBJECT (active_frame), "indicator-position", GINT_TO_POINTER (-1));
411     g_signal_connect (active_frame, "draw",
412                       G_CALLBACK (xfwm_settings_active_frame_draw), settings);
413 
414     children = gtk_container_get_children (GTK_CONTAINER (active_box));
415     for (list_iter = children; list_iter != NULL; list_iter = g_list_next (list_iter))
416       {
417         button = GTK_WIDGET (list_iter->data);
418         name = gtk_buildable_get_name (GTK_BUILDABLE (button));
419 
420         if (name[strlen (name) - 1] == '|')
421           {
422             g_signal_connect (title_align_combo, "changed",
423                               G_CALLBACK (xfwm_settings_title_button_alignment_changed), button);
424             xfwm_settings_title_button_alignment_changed (GTK_COMBO_BOX (title_align_combo),
425                                                           button);
426           }
427 
428         g_object_set_data (G_OBJECT (button), "key_char", (gpointer) &name[strlen (name) - 1]);
429         gtk_drag_source_set (button, GDK_BUTTON1_MASK, &target_entry[1], 1, GDK_ACTION_MOVE);
430 
431         g_signal_connect (button, "drag_data_get",
432                           G_CALLBACK (xfwm_settings_title_button_drag_data), NULL);
433         g_signal_connect (button, "drag_begin", G_CALLBACK (xfwm_settings_title_button_drag_begin),
434                           NULL);
435         g_signal_connect (button, "drag_end", G_CALLBACK (xfwm_settings_title_button_drag_end),
436                           NULL);
437         g_signal_connect (button, "button_press_event",
438                           G_CALLBACK (xfwm_settings_title_button_press_event), NULL);
439         g_signal_connect (button, "enter_notify_event",
440                           G_CALLBACK (xfwm_settings_signal_blocker), NULL);
441         g_signal_connect (button, "focus",  G_CALLBACK (xfwm_settings_signal_blocker), NULL);
442       }
443     g_list_free (children);
444 
445     children = gtk_container_get_children (GTK_CONTAINER (hidden_box));
446     for (list_iter = children; list_iter != NULL; list_iter = g_list_next (list_iter))
447       {
448         button = GTK_WIDGET (list_iter->data);
449         name = gtk_buildable_get_name (GTK_BUILDABLE (button));
450 
451         g_object_set_data (G_OBJECT (button), "key_char", (gpointer) &name[strlen (name) - 1]);
452         gtk_drag_source_set (button, GDK_BUTTON1_MASK, &target_entry[0], 1, GDK_ACTION_MOVE);
453 
454           g_signal_connect (button, "drag_data_get",
455                             G_CALLBACK (xfwm_settings_title_button_drag_data), NULL);
456           g_signal_connect (button, "drag_begin", G_CALLBACK (xfwm_settings_title_button_drag_begin),
457                             NULL);
458           g_signal_connect (button, "drag_end", G_CALLBACK (xfwm_settings_title_button_drag_end),
459                             NULL);
460           g_signal_connect (button, "button_press_event",
461                             G_CALLBACK (xfwm_settings_title_button_press_event), NULL);
462           g_signal_connect (button, "enter_notify_event",
463                             G_CALLBACK (xfwm_settings_signal_blocker), NULL);
464           g_signal_connect (button, "focus",  G_CALLBACK (xfwm_settings_signal_blocker), NULL);
465       }
466     g_list_free (children);
467 
468     xfconf_channel_get_property (settings->priv->wm_channel, "/general/button_layout", &value);
469     xfwm_settings_button_layout_property_changed (settings->priv->wm_channel,
470                                                   "/general/button_layout", &value, settings);
471     g_value_unset (&value);
472   }
473 
474   /* Keyboard tab widgets */
475   shortcuts_treeview = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "shortcuts_treeview"));
476   shortcuts_edit_button = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder,
477                                        "shortcuts_edit_button"));
478   shortcuts_clear_button = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder,
479                                        "shortcuts_clear_button"));
480   shortcuts_reset_button = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder,
481                                        "shortcuts_reset_button"));
482 
483   /* Set reset button icon */
484   gtk_button_set_image (GTK_BUTTON (shortcuts_reset_button),
485                         gtk_image_new_from_icon_name ("document-revert",
486                                                       GTK_ICON_SIZE_BUTTON));
487 
488   /* Keyboard tab: Shortcuts tree view */
489   {
490     GtkTreeViewColumn * column = NULL;
491     gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (shortcuts_treeview)),
492                                  GTK_SELECTION_MULTIPLE);
493 
494     list_store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
495     gtk_tree_view_set_model (GTK_TREE_VIEW (shortcuts_treeview), GTK_TREE_MODEL (list_store));
496     g_object_unref (G_OBJECT (list_store));
497 
498     renderer = gtk_cell_renderer_text_new ();
499     column = gtk_tree_view_column_new_with_attributes (_("Action"), renderer,
500                                                  "text", SHORTCUTS_NAME_COLUMN,
501                                                  NULL);
502     gtk_tree_view_insert_column (GTK_TREE_VIEW (shortcuts_treeview), column, 0);
503     gtk_tree_view_column_set_sort_column_id (column, SHORTCUTS_NAME_COLUMN);
504 
505     renderer = gtk_cell_renderer_text_new ();
506     column = gtk_tree_view_column_new_with_attributes (_("Shortcut"), renderer,
507                                                  "text", SHORTCUTS_SHORTCUT_LABEL_COLUMN,
508                                                  NULL);
509     gtk_tree_view_insert_column (GTK_TREE_VIEW (shortcuts_treeview), column, 1);
510     gtk_tree_view_column_set_sort_column_id (column, SHORTCUTS_SHORTCUT_LABEL_COLUMN);
511 
512     /* Initial sorting: By category; given by the model */
513 
514     g_signal_connect (shortcuts_treeview, "row-activated",
515                       G_CALLBACK (xfwm_settings_shortcut_row_activated), settings);
516   }
517 
518   /* Connect to shortcut buttons */
519   g_signal_connect (shortcuts_edit_button, "clicked",
520                     G_CALLBACK (xfwm_settings_shortcut_edit_clicked), settings);
521   g_signal_connect (shortcuts_clear_button, "clicked",
522                     G_CALLBACK (xfwm_settings_shortcut_clear_clicked), settings);
523   g_signal_connect (shortcuts_reset_button, "clicked",
524                     G_CALLBACK (xfwm_settings_shortcut_reset_clicked), settings);
525 
526   /* Focus tab widgets */
527   focus_delay_scale = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "focus_delay_scale"));
528   focus_raise_delay_scale = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "focus_raise_delay_scale"));
529   focus_new_check = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "focus_new_check"));
530   raise_on_focus_check = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "raise_on_focus_check"));
531   raise_on_click_check = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "raise_on_click_check"));
532   click_to_focus_radio = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "click_to_focus_radio"));
533 
534   /* Focus tab */
535   xfconf_g_property_bind (settings->priv->wm_channel, "/general/focus_delay", G_TYPE_INT,
536                           range_debouncer_bind (GTK_RANGE (focus_delay_scale)), "value");
537   xfconf_g_property_bind (settings->priv->wm_channel, "/general/raise_delay", G_TYPE_INT,
538                           range_debouncer_bind (GTK_RANGE (focus_raise_delay_scale)), "value");
539   xfconf_g_property_bind (settings->priv->wm_channel, "/general/raise_on_click", G_TYPE_BOOLEAN,
540                           raise_on_click_check, "active");
541   xfconf_g_property_bind (settings->priv->wm_channel, "/general/raise_on_focus", G_TYPE_BOOLEAN,
542                           raise_on_focus_check, "active");
543   xfconf_g_property_bind (settings->priv->wm_channel, "/general/focus_new", G_TYPE_BOOLEAN,
544                           focus_new_check, "active");
545   xfconf_g_property_bind (settings->priv->wm_channel, "/general/click_to_focus", G_TYPE_BOOLEAN,
546                           click_to_focus_radio, "active");
547 
548   g_signal_connect (settings->priv->wm_channel, "property-changed::/general/click_to_focus",
549                     G_CALLBACK (xfwm_settings_click_to_focus_property_changed), settings);
550 
551   xfconf_channel_get_property (settings->priv->wm_channel, "/general/click_to_focus", &value);
552   xfwm_settings_click_to_focus_property_changed (settings->priv->wm_channel,
553                                                  "/general/click_to_focus", &value, settings);
554   g_value_unset (&value);
555 
556   /* Advanced tab widgets */
557   box_move_check = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "box_move_check"));
558   box_resize_check = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "box_resize_check"));
559   wrap_workspaces_check = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder,
560                                       "wrap_workspaces_check"));
561   wrap_windows_check = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "wrap_windows_check"));
562   snap_to_border_check = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "snap_to_border_check"));
563   snap_to_window_check = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "snap_to_window_check"));
564   double_click_action_combo = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder,
565                                           "double_click_action_combo"));
566   snap_width_scale = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "snap_width_scale"));
567   wrap_resistance_scale = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder,
568                                       "wrap_resistance_scale"));
569 
570   /* Advanced tab: double click action */
571   {
572     gtk_cell_layout_clear (GTK_CELL_LAYOUT (double_click_action_combo));
573 
574     renderer = gtk_cell_renderer_text_new ();
575     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (double_click_action_combo), renderer, TRUE);
576     gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (double_click_action_combo), renderer,
577                                    "text", 0);
578 
579     list_store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
580     gtk_combo_box_set_model (GTK_COMBO_BOX (double_click_action_combo),
581                              GTK_TREE_MODEL (list_store));
582 
583     for (template = double_click_values; template->name != NULL; ++template)
584       {
585         gtk_list_store_append (list_store, &iter);
586         gtk_list_store_set (list_store, &iter, 0, _(template->name), 1, template->value, -1);
587       }
588     g_object_unref (G_OBJECT (list_store));
589     xfconf_channel_get_property (settings->priv->wm_channel, "/general/double_click_action",
590                                  &value);
591     xfwm_settings_double_click_action_property_changed (settings->priv->wm_channel,
592                                                         "/general/double_click_action",
593                                                         &value, settings);
594     g_value_unset (&value);
595 
596     g_signal_connect (double_click_action_combo, "changed",
597                       G_CALLBACK (xfwm_settings_double_click_action_changed),
598                       settings);
599     g_signal_connect (settings->priv->wm_channel, "property-changed::/general/double_click_action",
600                       G_CALLBACK (xfwm_settings_double_click_action_property_changed), settings);
601   }
602 
603   /* Advanced tab */
604   xfconf_g_property_bind (settings->priv->wm_channel, "/general/snap_width", G_TYPE_INT,
605                           range_debouncer_bind (GTK_RANGE (snap_width_scale)), "value");
606   xfconf_g_property_bind (settings->priv->wm_channel, "/general/wrap_resistance", G_TYPE_INT,
607                           range_debouncer_bind (GTK_RANGE (wrap_resistance_scale)), "value");
608   xfconf_g_property_bind (settings->priv->wm_channel, "/general/box_move", G_TYPE_BOOLEAN,
609                           box_move_check, "active");
610   xfconf_g_property_bind (settings->priv->wm_channel, "/general/box_resize", G_TYPE_BOOLEAN,
611                           box_resize_check, "active");
612   xfconf_g_property_bind (settings->priv->wm_channel, "/general/wrap_workspaces", G_TYPE_BOOLEAN,
613                           wrap_workspaces_check, "active");
614   xfconf_g_property_bind (settings->priv->wm_channel, "/general/wrap_windows", G_TYPE_BOOLEAN,
615                           wrap_windows_check, "active");
616   xfconf_g_property_bind (settings->priv->wm_channel, "/general/snap_to_border", G_TYPE_BOOLEAN,
617                           snap_to_border_check, "active");
618   xfconf_g_property_bind (settings->priv->wm_channel, "/general/snap_to_windows", G_TYPE_BOOLEAN,
619                           snap_to_window_check, "active");
620 
621   /* Load shortcuts */
622   xfwm_settings_initialize_shortcuts (settings);
623   xfwm_settings_reload_shortcuts (settings);
624 
625   /* Connect to shortcuts provider */
626   g_signal_connect (settings->priv->provider, "shortcut-added",
627                     G_CALLBACK (xfwm_settings_shortcut_added), settings);
628   g_signal_connect (settings->priv->provider, "shortcut-removed",
629                     G_CALLBACK (xfwm_settings_shortcut_removed), settings);
630 }
631 
632 
633 
634 static void
xfwm_settings_finalize(GObject * object)635 xfwm_settings_finalize (GObject *object)
636 {
637   XfwmSettings *settings = XFWM_SETTINGS (object);
638 
639   g_object_unref (settings->priv->wm_channel);
640   g_object_unref (settings->priv->provider);
641   g_object_unref (settings->priv->builder);
642 
643   (*G_OBJECT_CLASS (xfwm_settings_parent_class)->finalize) (object);
644 }
645 
646 
647 
648 static void
xfwm_settings_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)649 xfwm_settings_get_property (GObject    *object,
650                                   guint       prop_id,
651                                   GValue     *value,
652                                   GParamSpec *pspec)
653 {
654   XfwmSettings *settings = XFWM_SETTINGS (object);
655 
656   switch (prop_id)
657     {
658     case PROP_GTK_BUILDER:
659       g_value_set_object (value, settings->priv->builder);
660       break;
661     default:
662       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
663       break;
664     }
665 }
666 
667 
668 
669 static void
xfwm_settings_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)670 xfwm_settings_set_property (GObject      *object,
671                                   guint         prop_id,
672                                   const GValue *value,
673                                   GParamSpec   *pspec)
674 {
675   XfwmSettings *settings = XFWM_SETTINGS (object);
676 
677   switch (prop_id)
678     {
679     case PROP_GTK_BUILDER:
680       if (GTK_IS_BUILDER (settings->priv->builder))
681         g_object_unref (settings->priv->builder);
682       settings->priv->builder = g_value_get_object (value);
683       g_object_notify (object, "gtk-builder");
684       break;
685     default:
686       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
687       break;
688     }
689 }
690 
691 
692 
693 XfwmSettings *
xfwm_settings_new(void)694 xfwm_settings_new (void)
695 {
696   XfwmSettings *settings = NULL;
697   GtkBuilder   *builder;
698 
699   builder = gtk_builder_new ();
700 
701   gtk_builder_add_from_string (builder, xfwm4_dialog_ui, xfwm4_dialog_ui_length, NULL);
702 
703   if (G_LIKELY (builder != NULL))
704     settings = g_object_new (XFWM_TYPE_SETTINGS, "gtk-builder", builder, NULL);
705 
706   return settings;
707 }
708 
709 
710 static gint
xfwm_settings_theme_sort_func(GtkTreeModel * model,GtkTreeIter * iter1,GtkTreeIter * iter2,gpointer data)711 xfwm_settings_theme_sort_func (GtkTreeModel *model,
712                                GtkTreeIter  *iter1,
713                                GtkTreeIter  *iter2,
714                                gpointer      data)
715 {
716   gchar *str1 = NULL;
717   gchar *str2 = NULL;
718 
719   gtk_tree_model_get (model, iter1, 0, &str1, -1);
720   gtk_tree_model_get (model, iter2, 0, &str2, -1);
721 
722   if (str1 == NULL) str1 = g_strdup ("");
723   if (str2 == NULL) str2 = g_strdup ("");
724 
725   if (g_str_equal (str1, DEFAULT_THEME))
726     return -1;
727 
728   if (g_str_equal (str2, DEFAULT_THEME))
729     return 1;
730 
731   return g_utf8_collate (str1, str2);
732 }
733 
734 
735 
736 static void
xfwm_settings_load_themes(XfwmSettings * settings)737 xfwm_settings_load_themes (XfwmSettings *settings)
738 {
739   GtkTreeModel *model;
740   GtkTreeIter   iter;
741   GtkWidget    *view;
742   GHashTable   *themes;
743   GDir         *dir;
744   const gchar  *file;
745   gchar       **theme_dirs;
746   gchar        *filename;
747   gchar        *active_theme_name;
748   gint          i;
749 
750   themes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
751 
752   view = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "theme_name_treeview"));
753   model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
754 
755   xfce_resource_push_path (XFCE_RESOURCE_THEMES, DATADIR G_DIR_SEPARATOR_S "themes");
756   theme_dirs = xfce_resource_dirs (XFCE_RESOURCE_THEMES);
757   xfce_resource_pop_path (XFCE_RESOURCE_THEMES);
758 
759   active_theme_name = xfconf_channel_get_string (settings->priv->wm_channel, "/general/theme", DEFAULT_THEME);
760 
761   for (i = 0; theme_dirs[i] != NULL; ++i)
762     {
763       dir = g_dir_open (theme_dirs[i], 0, NULL);
764 
765       if (G_UNLIKELY (dir == NULL))
766         continue;
767 
768       while ((file = g_dir_read_name (dir)) != NULL)
769         {
770           filename = g_build_filename (theme_dirs[i], file, "xfwm4", "themerc", NULL);
771 
772           /* check if the theme rc exists and there is not already a theme with the
773            * same name in the database */
774           if (g_file_test (filename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR) &&
775               g_hash_table_lookup (themes, file) == NULL)
776             {
777               g_hash_table_insert (themes, g_strdup (file), GINT_TO_POINTER (1));
778 
779               /* insert in the list store */
780               gtk_list_store_append (GTK_LIST_STORE (model), &iter);
781               gtk_list_store_set (GTK_LIST_STORE (model), &iter,
782                                   COL_THEME_NAME, file,
783                                   COL_THEME_RC, filename, -1);
784 
785               if (G_UNLIKELY (g_str_equal (active_theme_name, file)))
786                 {
787                   GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
788 
789                   gtk_tree_selection_select_iter (gtk_tree_view_get_selection (GTK_TREE_VIEW (view)),
790                                                   &iter);
791                   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (view), path, NULL, TRUE, 0.5, 0.5);
792 
793                   gtk_tree_path_free (path);
794                 }
795             }
796 
797           g_free (filename);
798         }
799 
800       g_dir_close (dir);
801     }
802 
803   g_free (active_theme_name);
804   g_strfreev (theme_dirs);
805   g_hash_table_destroy (themes);
806 }
807 
808 
809 
810 static GtkWidget *
xfwm_settings_create_dialog(XfwmSettings * settings)811 xfwm_settings_create_dialog (XfwmSettings *settings)
812 {
813   g_return_val_if_fail (XFWM_IS_SETTINGS (settings), NULL);
814   return GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "main-dialog"));
815 }
816 
817 
818 
819 static GtkWidget *
xfwm_settings_create_plug(XfwmSettings * settings,Window socket_id)820 xfwm_settings_create_plug (XfwmSettings   *settings,
821                            Window          socket_id)
822 {
823   GtkWidget *plug;
824   GtkWidget *child;
825 
826   g_return_val_if_fail (XFWM_IS_SETTINGS (settings), NULL);
827 
828   plug = gtk_plug_new (socket_id);
829   gtk_widget_show (plug);
830 
831   child = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "plug-child"));
832   xfwm_widget_reparent (child, plug);
833   gtk_widget_show (child);
834 
835   return plug;
836 }
837 
838 
839 
840 static void
xfwm_settings_response(GtkWidget * dialog,gint response_id)841 xfwm_settings_response (GtkWidget *dialog,
842                         gint response_id)
843 {
844     if (response_id == GTK_RESPONSE_HELP)
845     {
846         xfce_dialog_show_help (GTK_WINDOW (dialog), "xfwm4",
847                                "preferences", NULL);
848     }
849     else
850     {
851         gtk_main_quit ();
852     }
853 }
854 
855 
856 
857 int
main(int argc,char ** argv)858 main (int    argc,
859       char **argv)
860 {
861   XfwmSettings *settings;
862   GtkWidget    *dialog;
863   GtkWidget    *plug;
864   GError       *error = NULL;
865   const gchar  *wm_name;
866 
867 #ifdef ENABLE_NLS
868   xfce_textdomain (GETTEXT_PACKAGE, LOCALEDIR, "UTF-8");
869 #endif
870 
871   if (G_UNLIKELY (!gtk_init_with_args (&argc, &argv, _("."), opt_entries, PACKAGE, &error)))
872     {
873       if (G_LIKELY (error != NULL))
874         {
875           g_print (_("%s: %s\nTry %s --help to see a full list of available command line options.\n"),
876                    PACKAGE, error->message, PACKAGE_NAME);
877           g_error_free (error);
878         }
879 
880       return EXIT_FAILURE;
881     }
882 
883   wm_name = gdk_x11_screen_get_window_manager_name (gdk_screen_get_default ());
884   if (G_UNLIKELY (g_ascii_strcasecmp (wm_name, "Xfwm4")))
885     {
886       g_print ("These settings cannot work with your current window manager (%s)\n", wm_name);
887       return EXIT_FAILURE;
888     }
889 
890   if (G_UNLIKELY (opt_version))
891     {
892       g_print ("%s\n", PACKAGE_STRING);
893       return EXIT_SUCCESS;
894     }
895 
896   if (G_UNLIKELY (!xfconf_init (&error)))
897     {
898       if (G_LIKELY (error != NULL))
899         {
900           g_error (_("Failed to initialize xfconf. Reason: %s"), error->message);
901           g_error_free (error);
902         }
903 
904       return EXIT_FAILURE;
905     }
906 
907   settings = xfwm_settings_new ();
908 
909   if (G_UNLIKELY (settings == NULL))
910     {
911       g_error (_("Could not create the settings dialog."));
912       xfconf_shutdown ();
913       return EXIT_FAILURE;
914     }
915 
916   if (G_UNLIKELY (opt_socket_id == 0))
917     {
918       dialog = xfwm_settings_create_dialog (settings);
919       gtk_widget_show (dialog);
920       g_signal_connect (dialog, "response", G_CALLBACK (xfwm_settings_response), NULL);
921 
922       /* To prevent the settings dialog to be saved in the session */
923       gdk_x11_set_sm_client_id ("FAKE ID");
924 
925       gtk_main ();
926 
927       gtk_widget_destroy (dialog);
928     }
929   else
930     {
931       plug = xfwm_settings_create_plug (settings, opt_socket_id);
932       g_signal_connect (plug, "delete-event", G_CALLBACK (gtk_main_quit), NULL);
933 
934       /* To prevent the settings dialog to be saved in the session */
935       gdk_x11_set_sm_client_id ("FAKE ID");
936 
937       /* Stop startup notification */
938       gdk_notify_startup_complete ();
939 
940       gtk_main ();
941     }
942 
943   g_object_unref (settings);
944 
945   xfconf_shutdown ();
946 
947   return EXIT_SUCCESS;
948 }
949 
950 
951 
952 static void
xfwm_settings_theme_selection_changed(GtkTreeSelection * selection,XfwmSettings * settings)953 xfwm_settings_theme_selection_changed (GtkTreeSelection *selection,
954                                        XfwmSettings     *settings)
955 {
956   GtkTreeModel *model;
957   GtkTreeIter   iter;
958   gchar        *theme, *filename;
959   XfceRc       *rc;
960   GtkWidget    *widget;
961   gboolean      button_layout = FALSE;
962   gboolean      title_alignment = FALSE;
963 
964   if (gtk_tree_selection_get_selected (selection, &model, &iter))
965     {
966       gtk_tree_model_get (model, &iter,
967                           COL_THEME_NAME, &theme,
968                           COL_THEME_RC, &filename, -1);
969 
970       /* set the theme name */
971       xfconf_channel_set_string (settings->priv->wm_channel, "/general/theme", theme);
972       g_free (theme);
973 
974       /* check in the rc if the theme supports a custom button layout and/or
975        * title alignement */
976       rc = xfce_rc_simple_open (filename, TRUE);
977       g_free (filename);
978 
979       if (G_LIKELY (rc != NULL))
980         {
981           button_layout = !xfce_rc_has_entry (rc, "button_layout");
982           title_alignment = !xfce_rc_has_entry (rc, "title_alignment");
983           xfce_rc_close (rc);
984         }
985     }
986 
987   widget = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "button_layout_box"));
988   gtk_widget_set_sensitive (widget, button_layout);
989 
990   widget = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "title_align_box"));
991   gtk_widget_set_sensitive (widget, title_alignment);
992 }
993 
994 
995 
996 static void
xfwm_settings_title_alignment_changed(GtkComboBox * combo,XfwmSettings * settings)997 xfwm_settings_title_alignment_changed (GtkComboBox  *combo,
998                                        XfwmSettings *settings)
999 {
1000   GtkTreeModel *model;
1001   GtkTreeIter   iter;
1002   gchar        *alignment;
1003 
1004   g_return_if_fail (XFWM_IS_SETTINGS (settings));
1005 
1006   model = gtk_combo_box_get_model (combo);
1007 
1008   gtk_combo_box_get_active_iter (combo, &iter);
1009   gtk_tree_model_get (model, &iter, 1, &alignment, -1);
1010 
1011   xfconf_channel_set_string (settings->priv->wm_channel, "/general/title_alignment", alignment);
1012 
1013   g_free (alignment);
1014 }
1015 
1016 
1017 
1018 static void
xfwm_settings_active_frame_drag_data(GtkWidget * widget,GdkDragContext * drag_context,gint x,gint y,GtkSelectionData * data,guint info,guint timestamp,XfwmSettings * settings)1019 xfwm_settings_active_frame_drag_data (GtkWidget        *widget,
1020                                       GdkDragContext   *drag_context,
1021                                       gint              x,
1022                                       gint              y,
1023                                       GtkSelectionData *data,
1024                                       guint             info,
1025                                       guint             timestamp,
1026                                       XfwmSettings     *settings)
1027 {
1028   GtkWidget     *source;
1029   GtkWidget     *parent;
1030   GtkWidget     *active_box;
1031   GList         *children;
1032   GList         *iter;
1033   GtkAllocation  allocation;
1034   gint           xoffset;
1035   gint           i;
1036 
1037   g_return_if_fail (XFWM_IS_SETTINGS (settings));
1038 
1039   source = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder,
1040                                  (const gchar *)gtk_selection_data_get_data (data)));
1041   parent = gtk_widget_get_parent (source);
1042 
1043   active_box = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "active-box"));
1044 
1045   g_object_ref (source);
1046   gtk_container_remove (GTK_CONTAINER (parent), source);
1047   gtk_box_pack_start (GTK_BOX (active_box), source, info == 3, info == 3, 0);
1048   g_object_unref (source);
1049 
1050   gtk_widget_get_allocation (widget, &allocation);
1051 
1052   xoffset = allocation.x;
1053 
1054   children = gtk_container_get_children (GTK_CONTAINER (active_box));
1055 
1056   for (i = 0, iter = children; iter != NULL; ++i, iter = g_list_next (iter))
1057     if (gtk_widget_get_visible (GTK_WIDGET (iter->data)))
1058       {
1059         gtk_widget_get_allocation (GTK_WIDGET (iter->data), &allocation);
1060 
1061         if (x < allocation.width / 2 + allocation.x - xoffset)
1062           break;
1063       }
1064 
1065   g_list_free (children);
1066 
1067   gtk_box_reorder_child (GTK_BOX (active_box), source, i);
1068 
1069   xfwm_settings_save_button_layout (settings, GTK_CONTAINER (active_box));
1070   gtk_widget_show (source);
1071 }
1072 
1073 
1074 
1075 static gboolean
xfwm_settings_active_frame_drag_motion(GtkWidget * widget,GdkDragContext * drag_context,gint x,gint y,guint timestamp,XfwmSettings * settings)1076 xfwm_settings_active_frame_drag_motion (GtkWidget      *widget,
1077                                         GdkDragContext *drag_context,
1078                                         gint            x,
1079                                         gint            y,
1080                                         guint           timestamp,
1081                                         XfwmSettings   *settings)
1082 {
1083   GtkWidget     *active_box;
1084   GList         *children;
1085   GList         *iter;
1086   GtkAllocation  allocation;
1087   gint           xoffset;
1088   gint           index = -1;
1089 
1090   g_return_val_if_fail (XFWM_IS_SETTINGS (settings), FALSE);
1091 
1092   gtk_widget_get_allocation (widget, &allocation);
1093 
1094   xoffset = allocation.x;
1095 
1096   active_box = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "active-box"));
1097   children = gtk_container_get_children (GTK_CONTAINER (active_box));
1098 
1099   /* Set a value so that the compiler does not (rightfully) complain */
1100   for (iter = children, index = 0; iter != NULL; iter = g_list_next (iter))
1101     {
1102       if (gtk_widget_get_visible (GTK_WIDGET (iter->data)))
1103         {
1104           gtk_widget_get_allocation (GTK_WIDGET (iter->data), &allocation);
1105 
1106           if (x < (allocation.width / 2 + allocation.x - xoffset))
1107             break;
1108 
1109           index++;
1110         }
1111     }
1112 
1113   g_list_free (children);
1114 
1115   g_object_set_data (G_OBJECT (widget), "indicator-position", GINT_TO_POINTER (index));
1116   gtk_widget_queue_draw (widget);
1117 
1118   return FALSE;
1119 }
1120 
1121 
1122 
1123 static void
xfwm_settings_active_frame_drag_leave(GtkWidget * widget,GdkDragContext * drag_context,guint timestamp,XfwmSettings * settings)1124 xfwm_settings_active_frame_drag_leave (GtkWidget      *widget,
1125                                        GdkDragContext *drag_context,
1126                                        guint           timestamp,
1127                                        XfwmSettings   *settings)
1128 {
1129   g_return_if_fail (XFWM_IS_SETTINGS (settings));
1130 
1131   g_object_set_data (G_OBJECT (widget), "indicator-position", GINT_TO_POINTER (-1));
1132   gtk_widget_queue_draw (widget);
1133 }
1134 
1135 
1136 
1137 static void
xfwm_settings_hidden_frame_drag_data(GtkWidget * widget,GdkDragContext * drag_context,gint x,gint y,GtkSelectionData * data,guint info,guint timestamp,XfwmSettings * settings)1138 xfwm_settings_hidden_frame_drag_data (GtkWidget        *widget,
1139                                       GdkDragContext   *drag_context,
1140                                       gint              x,
1141                                       gint              y,
1142                                       GtkSelectionData *data,
1143                                       guint             info,
1144                                       guint             timestamp,
1145                                       XfwmSettings     *settings)
1146 {
1147   GtkWidget *source;
1148   GtkWidget *parent;
1149   GtkWidget *hidden_box;
1150   GtkWidget *active_box;
1151 
1152   g_return_if_fail (XFWM_IS_SETTINGS (settings));
1153 
1154   source = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder,
1155                                  (const gchar *)gtk_selection_data_get_data (data)));
1156   parent = gtk_widget_get_parent (source);
1157 
1158   hidden_box = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "hidden-box"));
1159   active_box = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "active-box"));
1160 
1161   if (G_LIKELY (parent != hidden_box))
1162     {
1163       g_object_ref (source);
1164       gtk_container_remove (GTK_CONTAINER (parent), source);
1165       gtk_box_pack_start (GTK_BOX (hidden_box), source, FALSE, FALSE, 0);
1166       g_object_unref (source);
1167 
1168       xfwm_settings_save_button_layout (settings, GTK_CONTAINER (active_box));
1169     }
1170 
1171   gtk_widget_show (source);
1172 }
1173 
1174 
1175 
1176 static gboolean
xfwm_settings_title_button_press_event(GtkWidget * widget)1177 xfwm_settings_title_button_press_event (GtkWidget *widget)
1178 {
1179  /* FIXME! This crashes in cairo... xfce bug 14606 */
1180 #if 0
1181   GdkPixbuf *pixbuf;
1182 
1183   g_return_val_if_fail (GTK_IS_WIDGET (widget), TRUE);
1184   /* set pixbuf before drag begin cause it can be not displayed */
1185   pixbuf = xfwm_settings_create_icon_from_widget (widget);
1186   gtk_drag_source_set_icon_pixbuf (widget, pixbuf);
1187   g_object_unref (pixbuf);
1188 #endif
1189 
1190   return TRUE;
1191 }
1192 
1193 
1194 
1195 static void
xfwm_settings_title_button_drag_data(GtkWidget * widget,GdkDragContext * drag_context,GtkSelectionData * data,guint info,guint timestamp)1196 xfwm_settings_title_button_drag_data (GtkWidget        *widget,
1197                                       GdkDragContext   *drag_context,
1198                                       GtkSelectionData *data,
1199                                       guint             info,
1200                                       guint             timestamp)
1201 {
1202   const gchar *name;
1203 
1204   name = gtk_buildable_get_name (GTK_BUILDABLE (widget));
1205 
1206   gtk_selection_data_set (data, gdk_atom_intern ("_xfwm4_button_layout", FALSE), 8,
1207                           (const guchar *)name, strlen (name));
1208 }
1209 
1210 
1211 
1212 static void
xfwm_settings_title_button_drag_begin(GtkWidget * widget,GdkDragContext * drag_context)1213 xfwm_settings_title_button_drag_begin (GtkWidget      *widget,
1214                                        GdkDragContext *drag_context)
1215 {
1216   g_return_if_fail (GTK_IS_WIDGET (widget));
1217 
1218   gtk_widget_hide (widget);
1219 }
1220 
1221 
1222 
1223 static void
xfwm_settings_title_button_drag_end(GtkWidget * widget,GdkDragContext * drag_context)1224 xfwm_settings_title_button_drag_end (GtkWidget      *widget,
1225                                      GdkDragContext *drag_context)
1226 {
1227   gtk_widget_show (widget);
1228 }
1229 
1230 
1231 
1232 static gboolean
xfwm_settings_active_frame_draw(GtkWidget * widget,cairo_t * cr,XfwmSettings * settings)1233 xfwm_settings_active_frame_draw (GtkWidget    *widget,
1234                                  cairo_t      *cr,
1235                                  XfwmSettings *settings)
1236 {
1237   GtkWidget     *active_box;
1238   gint           position;
1239   GList         *children;
1240   GList         *iter;
1241   GtkAllocation  widget_allocation;
1242   GtkAllocation  box_allocation;
1243   gint           index;
1244   gint           x;
1245   gint           spacing;
1246   gint           hshift;
1247   GdkRGBA        color;
1248 
1249   active_box = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "active-box"));
1250 
1251   position = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "indicator-position"));
1252 
1253   if (position >= 0)
1254     {
1255       gtk_widget_get_allocation (active_box, &box_allocation);
1256 
1257       spacing = gtk_box_get_spacing (GTK_BOX (active_box));
1258       x = box_allocation.x - spacing;
1259 
1260       children = gtk_container_get_children (GTK_CONTAINER (active_box));
1261 
1262       for (iter = children, index = 1; iter != NULL; iter = g_list_next (iter))
1263         {
1264           if (gtk_widget_get_visible (GTK_WIDGET (iter->data)))
1265             {
1266               if (index == position)
1267                 {
1268                   gtk_widget_get_allocation (GTK_WIDGET (iter->data), &widget_allocation);
1269                   x = widget_allocation.x + widget_allocation.width;
1270                 }
1271 
1272               index++;
1273             }
1274         }
1275 
1276       g_list_free (children);
1277 
1278       gtk_widget_get_allocation (widget, &widget_allocation);
1279 
1280       gtk_style_context_get_color (gtk_widget_get_style_context (widget),
1281                                    GTK_STATE_FLAG_NORMAL, &color);
1282 
1283       cairo_translate (cr,
1284                        box_allocation.x - widget_allocation.x,
1285                        box_allocation.y - widget_allocation.y);
1286 
1287       hshift = spacing * 5 / 3;
1288       x -= box_allocation.x + (hshift - spacing) / 2;
1289 
1290       gdk_cairo_set_source_rgba (cr, &color);
1291       cairo_move_to (cr, x, -spacing);
1292       cairo_rel_line_to (cr, hshift, 0);
1293       cairo_rel_line_to (cr, -(hshift / 2 - 1), spacing);
1294       cairo_rel_line_to (cr, 0, box_allocation.height);
1295       cairo_rel_line_to (cr, hshift / 2 - 1, spacing);
1296       cairo_rel_line_to (cr, -hshift, 0);
1297       cairo_rel_line_to (cr, hshift / 2 - 1, -spacing);
1298       cairo_rel_line_to (cr, 0, -box_allocation.height);
1299       cairo_rel_line_to (cr, -(hshift / 2 - 1), -spacing);
1300       cairo_close_path (cr);
1301       cairo_fill (cr);
1302     }
1303 
1304   return FALSE;
1305 }
1306 
1307 
1308 
1309 static gboolean
xfwm_settings_signal_blocker(GtkWidget * widget)1310 xfwm_settings_signal_blocker (GtkWidget *widget)
1311 {
1312   return TRUE;
1313 }
1314 
1315 
1316 /* FIXME! This crashes in cairo... xfce bug 14606 */
1317 #if 0
1318 static GdkPixbuf *
1319 xfwm_settings_create_icon_from_widget (GtkWidget *widget)
1320 {
1321   GdkWindow     *window;
1322   GtkAllocation  allocation;
1323 
1324   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1325 
1326   gtk_widget_get_allocation (widget, &allocation);
1327   window = gtk_widget_get_parent_window (widget);
1328   return gdk_pixbuf_get_from_window (window,
1329                                      allocation.x, allocation.y,
1330                                      allocation.width, allocation.height);
1331 }
1332 #endif
1333 
1334 
1335 static void
xfwm_settings_button_layout_property_changed(XfconfChannel * channel,const gchar * property,const GValue * value,XfwmSettings * settings)1336 xfwm_settings_button_layout_property_changed (XfconfChannel *channel,
1337                                               const gchar   *property,
1338                                               const GValue  *value,
1339                                               XfwmSettings  *settings)
1340 {
1341   GtkWidget   *active_box;
1342   GtkWidget   *hidden_box;
1343   GtkWidget   *button;
1344   GList       *children;
1345   GList       *iter;
1346   const gchar *str_value;
1347   const gchar *key_char;
1348 
1349   g_return_if_fail (XFWM_IS_SETTINGS (settings));
1350 
1351   hidden_box = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "hidden-box"));
1352   active_box = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "active-box"));
1353 
1354   gtk_widget_set_app_paintable (active_box, FALSE);
1355   gtk_widget_set_app_paintable (hidden_box, FALSE);
1356 
1357   children = gtk_container_get_children (GTK_CONTAINER (active_box));
1358 
1359   /* Move all buttons to the hidden list, except for the title */
1360   for (iter = children; iter != NULL; iter = g_list_next (iter))
1361     {
1362       button = GTK_WIDGET (iter->data);
1363       key_char = (const gchar *) g_object_get_data (G_OBJECT (button), "key_char");
1364 
1365       if (G_LIKELY (key_char[0] != '|'))
1366         {
1367           g_object_ref (button);
1368           gtk_container_remove (GTK_CONTAINER (active_box), button);
1369           gtk_box_pack_start (GTK_BOX (hidden_box), button, FALSE, FALSE, 0);
1370           g_object_unref (button);
1371         }
1372     }
1373 
1374   g_list_free (children);
1375 
1376   children = g_list_concat (gtk_container_get_children (GTK_CONTAINER (active_box)),
1377                             gtk_container_get_children (GTK_CONTAINER (hidden_box)));
1378 
1379   /* Move buttons to the active list */
1380   for (str_value = g_value_get_string (value); str_value != NULL && *str_value != '\0'; ++str_value)
1381     for (iter = children; iter != NULL; iter = g_list_next (iter))
1382       {
1383         button = GTK_WIDGET (iter->data);
1384         key_char = (const gchar *) g_object_get_data (G_OBJECT (button), "key_char");
1385 
1386         if (g_str_has_prefix (str_value, key_char))
1387           {
1388             g_object_ref (button);
1389             gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (button)), button);
1390             gtk_box_pack_start (GTK_BOX (active_box), button,
1391                                 key_char[0] == '|', key_char[0] == '|', 0);
1392             g_object_unref (button);
1393           }
1394       }
1395 
1396   g_list_free (children);
1397 
1398   gtk_widget_set_app_paintable (active_box, TRUE);
1399   gtk_widget_set_app_paintable (hidden_box, TRUE);
1400 }
1401 
1402 
1403 
1404 static void
xfwm_settings_title_alignment_property_changed(XfconfChannel * channel,const gchar * property,const GValue * value,XfwmSettings * settings)1405 xfwm_settings_title_alignment_property_changed (XfconfChannel *channel,
1406                                                 const gchar   *property,
1407                                                 const GValue  *value,
1408                                                 XfwmSettings  *settings)
1409 {
1410   GtkTreeModel *model;
1411   GtkTreeIter   iter;
1412   GtkWidget    *combo;
1413   gchar        *alignment;
1414   const gchar  *new_value;
1415 
1416   g_return_if_fail (XFWM_IS_SETTINGS (settings));
1417 
1418   combo = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "title_align_combo"));
1419   model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
1420 
1421   if (gtk_tree_model_get_iter_first (model, &iter))
1422     {
1423       do
1424         {
1425           gtk_tree_model_get (model, &iter, 1, &alignment, -1);
1426 
1427           if (G_UNLIKELY (G_VALUE_TYPE (value) == G_TYPE_INVALID))
1428               new_value = "center";
1429           else
1430               new_value = g_value_get_string (value);
1431 
1432           if (G_UNLIKELY (g_str_equal (alignment, new_value)))
1433             {
1434               g_free (alignment);
1435               gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
1436               break;
1437             }
1438 
1439           g_free (alignment);
1440         }
1441       while (gtk_tree_model_iter_next (model, &iter));
1442     }
1443 }
1444 
1445 
1446 
1447 static void
xfwm_settings_save_button_layout(XfwmSettings * settings,GtkContainer * container)1448 xfwm_settings_save_button_layout (XfwmSettings *settings,
1449                                   GtkContainer *container)
1450 {
1451   GList        *children;
1452   GList        *iter;
1453   const gchar **key_chars;
1454   gchar        *value;
1455   gint          i;
1456 
1457   g_return_if_fail (XFWM_IS_SETTINGS (settings));
1458 
1459   children = gtk_container_get_children (container);
1460 
1461   key_chars = g_new0 (const char *, g_list_length (children) + 1);
1462 
1463   for (i = 0, iter = children; iter != NULL; ++i, iter = g_list_next (iter))
1464     key_chars[i] = (const gchar *) g_object_get_data (G_OBJECT (iter->data), "key_char");
1465 
1466   value = g_strjoinv ("", (gchar **) key_chars);
1467 
1468   xfconf_channel_set_string (settings->priv->wm_channel, "/general/button_layout", value);
1469 
1470   g_list_free (children);
1471   g_free (key_chars);
1472   g_free (value);
1473 }
1474 
1475 
1476 
1477 static void
xfwm_settings_double_click_action_changed(GtkComboBox * combo,XfwmSettings * settings)1478 xfwm_settings_double_click_action_changed (GtkComboBox  *combo,
1479                                            XfwmSettings *settings)
1480 {
1481   GtkTreeModel *model;
1482   GtkTreeIter   iter;
1483   gchar        *value;
1484 
1485   g_return_if_fail (XFWM_IS_SETTINGS (settings));
1486 
1487   model = gtk_combo_box_get_model (combo);
1488   gtk_combo_box_get_active_iter (combo, &iter);
1489   gtk_tree_model_get (model, &iter, 1, &value, -1);
1490 
1491   xfconf_channel_set_string (settings->priv->wm_channel, "/general/double_click_action", value);
1492 
1493   g_free (value);
1494 }
1495 
1496 
1497 
1498 static void
xfwm_settings_title_button_alignment_changed(GtkComboBox * combo,GtkWidget * button)1499 xfwm_settings_title_button_alignment_changed (GtkComboBox *combo,
1500                                               GtkWidget   *button)
1501 {
1502   GtkTreeModel *model;
1503   GtkTreeIter   iter;
1504   gchar        *value;
1505   float         align = 0.5f;
1506   GList        *children;
1507   GList        *citer;
1508 
1509   model = gtk_combo_box_get_model (combo);
1510   if (gtk_combo_box_get_active_iter (combo, &iter))
1511     {
1512       gtk_tree_model_get (model, &iter, 1, &value, -1);
1513 
1514       if (g_str_equal (value, "left"))
1515         align = 0.0f;
1516       else if (g_str_equal (value, "right"))
1517         align = 1.0f;
1518 
1519       g_free (value);
1520     }
1521 
1522   children = gtk_container_get_children (GTK_CONTAINER (button));
1523   for (citer = children; citer != NULL; citer = g_list_next (citer))
1524     {
1525       if (GTK_IS_LABEL (citer->data))
1526         {
1527           gtk_label_set_xalign (GTK_LABEL (citer->data), align);
1528           break;
1529         }
1530     }
1531   g_list_free (children);
1532 }
1533 
1534 
1535 
1536 static void
xfwm_settings_double_click_action_property_changed(XfconfChannel * channel,const gchar * property,const GValue * value,XfwmSettings * settings)1537 xfwm_settings_double_click_action_property_changed (XfconfChannel *channel,
1538                                                     const gchar   *property,
1539                                                     const GValue  *value,
1540                                                     XfwmSettings  *settings)
1541 {
1542   GtkTreeModel *model;
1543   GtkTreeIter   iter;
1544   GtkWidget    *combo;
1545   const gchar  *new_value;
1546   gchar        *current_value;
1547 
1548   g_return_if_fail (XFWM_IS_SETTINGS (settings));
1549 
1550   combo = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "double_click_action_combo"));
1551   model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
1552 
1553   if (G_UNLIKELY (G_VALUE_TYPE (value) == G_TYPE_INVALID))
1554     new_value = "maximize";
1555   else
1556     new_value = g_value_get_string (value);
1557 
1558   if (G_LIKELY (gtk_tree_model_get_iter_first (model, &iter)))
1559     {
1560       do
1561         {
1562           gtk_tree_model_get (model, &iter, 1, &current_value, -1);
1563 
1564           if (G_UNLIKELY (g_str_equal (current_value, new_value)))
1565             {
1566               g_free (current_value);
1567               gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
1568               break;
1569             }
1570 
1571           g_free (current_value);
1572         }
1573       while (gtk_tree_model_iter_next (model, &iter));
1574     }
1575 }
1576 
1577 
1578 
1579 static void
xfwm_settings_click_to_focus_property_changed(XfconfChannel * channel,const gchar * property,const GValue * value,XfwmSettings * settings)1580 xfwm_settings_click_to_focus_property_changed (XfconfChannel *channel,
1581                                                const gchar   *property,
1582                                                const GValue  *value,
1583                                                XfwmSettings  *settings)
1584 {
1585   GtkWidget *click_to_focus_radio;
1586   GtkWidget *focus_follows_mouse_radio;
1587   GtkWidget *focus_delay_hbox;
1588 
1589   g_return_if_fail (XFWM_IS_SETTINGS (settings));
1590   g_return_if_fail (GTK_IS_BUILDER (settings->priv->builder));
1591 
1592   click_to_focus_radio = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder,
1593                                      "click_to_focus_radio"));
1594   focus_follows_mouse_radio = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder,
1595                                           "focus_follows_mouse_radio"));
1596   focus_delay_hbox = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder,
1597                                  "focus_delay_hbox"));
1598 
1599   if (G_UNLIKELY (G_VALUE_TYPE (value) != G_TYPE_BOOLEAN))
1600     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (click_to_focus_radio), TRUE);
1601   else
1602     {
1603       if (g_value_get_boolean (value))
1604         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (click_to_focus_radio), TRUE);
1605       else
1606         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (focus_follows_mouse_radio), TRUE);
1607     }
1608   gtk_widget_set_sensitive (GTK_WIDGET (focus_delay_hbox),
1609                             gtk_toggle_button_get_active (
1610                                   GTK_TOGGLE_BUTTON (focus_follows_mouse_radio)));
1611 }
1612 
1613 
1614 
1615 
1616 static void
xfwm_settings_initialize_shortcuts(XfwmSettings * settings)1617 xfwm_settings_initialize_shortcuts (XfwmSettings *settings)
1618 {
1619   GtkTreeModel *model;
1620   GtkTreeIter   iter;
1621   GtkWidget    *view;
1622   GList        *feature_list;
1623 
1624   g_return_if_fail (XFWM_IS_SETTINGS (settings));
1625   g_return_if_fail (GTK_IS_BUILDER (settings->priv->builder));
1626 
1627   view = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "shortcuts_treeview"));
1628   model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
1629 
1630   gtk_list_store_clear (GTK_LIST_STORE (model));
1631 
1632   if ((feature_list = xfce_shortcuts_xfwm4_get_feature_list ()) != NULL)
1633     {
1634       GList *l;
1635 
1636       for (l = g_list_first (feature_list); l != NULL; l = g_list_next (l))
1637         {
1638           gtk_list_store_append (GTK_LIST_STORE (model), &iter);
1639           gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1640                               SHORTCUTS_NAME_COLUMN,
1641                               xfce_shortcuts_xfwm4_get_feature_name (l->data),
1642                               SHORTCUTS_FEATURE_COLUMN, l->data,
1643                               -1);
1644         }
1645 
1646       g_list_free (feature_list);
1647     }
1648 }
1649 
1650 
1651 
1652 static void
xfwm_settings_clear_shortcuts_view(XfwmSettings * settings)1653 xfwm_settings_clear_shortcuts_view (XfwmSettings *settings)
1654 {
1655   GtkTreeModel *model;
1656   GtkTreeIter   iter;
1657   GtkWidget    *view;
1658 
1659   g_return_if_fail (XFWM_IS_SETTINGS (settings));
1660   g_return_if_fail (GTK_IS_BUILDER (settings->priv->builder));
1661 
1662   view = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "shortcuts_treeview"));
1663   model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
1664 
1665   if (G_LIKELY (gtk_tree_model_get_iter_first (model, &iter)))
1666     {
1667       do
1668         {
1669           gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1670                               SHORTCUTS_SHORTCUT_COLUMN, NULL, -1);
1671         }
1672       while (gtk_tree_model_iter_next (model, &iter));
1673     }
1674 }
1675 
1676 
1677 
1678 static void
xfwm_settings_reload_shortcut(XfceShortcut * shortcut,GtkTreeModel * model)1679 xfwm_settings_reload_shortcut (XfceShortcut *shortcut,
1680                                GtkTreeModel *model)
1681 {
1682   GtkTreeIter iter;
1683   gchar      *feature;
1684 
1685   g_return_if_fail (GTK_IS_TREE_MODEL (model));
1686   g_return_if_fail (shortcut != NULL);
1687 
1688   if (G_LIKELY (gtk_tree_model_get_iter_first (model, &iter)))
1689     {
1690       do
1691         {
1692           gtk_tree_model_get (model, &iter, SHORTCUTS_FEATURE_COLUMN, &feature, -1);
1693 
1694           if (G_UNLIKELY (g_str_equal (feature, shortcut->command)))
1695             {
1696               GdkModifierType  modifiers;
1697               guint            keyval;
1698               gchar           *label;
1699 
1700               /* Get the shortcut label */
1701               gtk_accelerator_parse (shortcut->shortcut, &keyval, &modifiers);
1702               label = gtk_accelerator_get_label (keyval, modifiers);
1703 
1704               gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1705                                   SHORTCUTS_SHORTCUT_COLUMN, shortcut->shortcut,
1706                                   SHORTCUTS_SHORTCUT_LABEL_COLUMN, label, -1);
1707               g_free (label);
1708             }
1709 
1710           g_free (feature);
1711         }
1712       while (gtk_tree_model_iter_next (model, &iter));
1713     }
1714 }
1715 
1716 
1717 
1718 static void
xfwm_settings_reload_shortcuts(XfwmSettings * settings)1719 xfwm_settings_reload_shortcuts (XfwmSettings *settings)
1720 {
1721   GtkTreeModel *model;
1722   GtkWidget    *view;
1723   GList        *shortcuts;
1724 
1725   g_return_if_fail (XFWM_IS_SETTINGS (settings));
1726   g_return_if_fail (GTK_IS_BUILDER (settings->priv->builder));
1727   g_return_if_fail (XFCE_IS_SHORTCUTS_PROVIDER (settings->priv->provider));
1728 
1729   view = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "shortcuts_treeview"));
1730   model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
1731 
1732   xfwm_settings_clear_shortcuts_view (settings);
1733 
1734   shortcuts = xfce_shortcuts_provider_get_shortcuts (settings->priv->provider);
1735   g_list_foreach (shortcuts, (GFunc) xfwm_settings_reload_shortcut, model);
1736   xfce_shortcuts_free (shortcuts);
1737 }
1738 
1739 
1740 
1741 static void
xfwm_settings_shortcut_added(XfceShortcutsProvider * provider,const gchar * shortcut,XfwmSettings * settings)1742 xfwm_settings_shortcut_added (XfceShortcutsProvider *provider,
1743                               const gchar           *shortcut,
1744                               XfwmSettings          *settings)
1745 {
1746   g_return_if_fail (XFWM_IS_SETTINGS (settings));
1747 
1748   DBG ("Shortcut added signal: %s", shortcut);
1749 
1750   xfwm_settings_reload_shortcuts (settings);
1751 }
1752 
1753 
1754 
1755 static void
xfwm_settings_shortcut_removed(XfceShortcutsProvider * provider,const gchar * shortcut,XfwmSettings * settings)1756 xfwm_settings_shortcut_removed (XfceShortcutsProvider *provider,
1757                                 const gchar           *shortcut,
1758                                 XfwmSettings          *settings)
1759 {
1760   g_return_if_fail (XFWM_IS_SETTINGS (settings));
1761 
1762   DBG ("Shortcut removed signal: %s", shortcut);
1763 
1764   xfwm_settings_reload_shortcuts (settings);
1765 }
1766 
1767 
1768 
1769 static void
free_row(GtkTreeRowReference * reference,gpointer unused)1770 free_row (GtkTreeRowReference *reference, gpointer unused)
1771 {
1772   gtk_tree_row_reference_free (reference);
1773 }
1774 
1775 
1776 
1777 static void
free_path(GtkTreePath * path,gpointer unused)1778 free_path (GtkTreePath *path, gpointer unused)
1779 {
1780   gtk_tree_path_free (path);
1781 }
1782 
1783 
1784 
1785 static void
xfwm_settings_shortcut_edit_clicked(GtkButton * button,XfwmSettings * settings)1786 xfwm_settings_shortcut_edit_clicked (GtkButton    *button,
1787                                      XfwmSettings *settings)
1788 {
1789   GtkTreeSelection *selection;
1790   GtkTreeModel     *model;
1791   GtkTreePath      *path;
1792   GtkWidget        *view;
1793   GList            *rows;
1794   GList            *iter;
1795   GList            *row_references = NULL;
1796 
1797   g_return_if_fail (XFWM_IS_SETTINGS (settings));
1798   g_return_if_fail (GTK_IS_BUILDER (settings->priv->builder));
1799   g_return_if_fail (XFCE_IS_SHORTCUTS_PROVIDER (settings->priv->provider));
1800 
1801   view = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "shortcuts_treeview"));
1802   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
1803   rows = gtk_tree_selection_get_selected_rows (selection, &model);
1804 
1805   for (iter = g_list_first (rows); iter != NULL; iter = g_list_next (iter))
1806     {
1807       row_references = g_list_append (row_references,
1808                                       gtk_tree_row_reference_new (model, iter->data));
1809     }
1810 
1811   for (iter = g_list_first (row_references); iter != NULL; iter = g_list_next (iter))
1812     {
1813       path = gtk_tree_row_reference_get_path (iter->data);
1814 
1815       /* Use the row-activated callback to manage the shortcut editing */
1816       xfwm_settings_shortcut_row_activated (GTK_TREE_VIEW (view),
1817                                             path, NULL,
1818                                             settings);
1819 
1820       gtk_tree_path_free (path);
1821     }
1822 
1823   /* Free row reference list */
1824   g_list_foreach (row_references, (GFunc) free_row, NULL);
1825   g_list_free (row_references);
1826 
1827   /* Free row list */
1828   g_list_foreach (rows, (GFunc) free_path, NULL);
1829   g_list_free (rows);
1830 }
1831 
1832 
1833 
1834 static void
xfwm_settings_shortcut_clear_clicked(GtkButton * button,XfwmSettings * settings)1835 xfwm_settings_shortcut_clear_clicked (GtkButton    *button,
1836                                       XfwmSettings *settings)
1837 {
1838   GtkTreeSelection *selection;
1839   GtkTreeModel     *model;
1840   GtkTreePath      *path;
1841   GtkTreeIter       tree_iter;
1842   GtkWidget        *view;
1843   GList            *rows;
1844   GList            *iter;
1845   GList            *row_references = NULL;
1846   gchar            *shortcut;
1847 
1848   g_return_if_fail (XFWM_IS_SETTINGS (settings));
1849   g_return_if_fail (GTK_IS_BUILDER (settings->priv->builder));
1850   g_return_if_fail (XFCE_IS_SHORTCUTS_PROVIDER (settings->priv->provider));
1851 
1852   view = GTK_WIDGET (gtk_builder_get_object (settings->priv->builder, "shortcuts_treeview"));
1853   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
1854   rows = gtk_tree_selection_get_selected_rows (selection, &model);
1855 
1856   for (iter = g_list_first (rows); iter != NULL; iter = g_list_next (iter))
1857     {
1858       row_references = g_list_append (row_references,
1859                                       gtk_tree_row_reference_new (model, iter->data));
1860     }
1861 
1862   for (iter = g_list_first (row_references); iter != NULL; iter = g_list_next (iter))
1863     {
1864       path = gtk_tree_row_reference_get_path (iter->data);
1865 
1866       /* Convert tree path to tree iter */
1867       if (G_LIKELY (gtk_tree_model_get_iter (model, &tree_iter, path)))
1868         {
1869           /* Read shortcut */
1870           gtk_tree_model_get (model, &tree_iter, SHORTCUTS_SHORTCUT_COLUMN, &shortcut, -1);
1871 
1872           if (G_LIKELY (shortcut != NULL))
1873             {
1874               DBG ("clear shortcut %s", shortcut);
1875 
1876               /* Remove keyboard shortcut via xfconf */
1877               xfce_shortcuts_provider_reset_shortcut (settings->priv->provider, shortcut);
1878 
1879               gtk_list_store_set (GTK_LIST_STORE (model), &tree_iter,
1880                                   SHORTCUTS_SHORTCUT_COLUMN, NULL,
1881                                   SHORTCUTS_SHORTCUT_LABEL_COLUMN, NULL, -1);
1882 
1883               /* Free shortcut string */
1884               g_free (shortcut);
1885             }
1886         }
1887 
1888       gtk_tree_path_free (path);
1889     }
1890 
1891   /* Free row reference list */
1892   g_list_foreach (row_references, (GFunc) free_row, NULL);
1893   g_list_free (row_references);
1894 
1895   /* Free row list */
1896   g_list_foreach (rows, (GFunc) free_path, NULL);
1897   g_list_free (rows);
1898 }
1899 
1900 
1901 
1902 static void
xfwm_settings_shortcut_reset_clicked(GtkButton * button,XfwmSettings * settings)1903 xfwm_settings_shortcut_reset_clicked (GtkButton    *button,
1904                                       XfwmSettings *settings)
1905 {
1906   gint confirm;
1907 
1908   g_return_if_fail (XFWM_IS_SETTINGS (settings));
1909   g_return_if_fail (XFCE_IS_SHORTCUTS_PROVIDER (settings->priv->provider));
1910 
1911   confirm = xfce_dialog_confirm (NULL, "gtk-yes", NULL,
1912                                   _("This will reset all shortcuts to their default "
1913                                     "values. Do you really want to do this?"),
1914                                   _("Reset to Greybirds"));
1915 
1916   if (confirm)
1917     xfce_shortcuts_provider_reset_to_defaults (settings->priv->provider);
1918 }
1919 
1920 
1921 
1922 static gboolean
xfwm_settings_update_treeview_on_conflict_replace(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer shortcut_to_erase)1923 xfwm_settings_update_treeview_on_conflict_replace (GtkTreeModel *model,
1924                                                    GtkTreePath  *path,
1925                                                    GtkTreeIter  *iter,
1926                                                    gpointer      shortcut_to_erase)
1927 {
1928   gchar *shortcut;
1929 
1930   gtk_tree_model_get (model, iter, SHORTCUTS_SHORTCUT_COLUMN, &shortcut, -1);
1931 
1932   if (g_strcmp0 (shortcut_to_erase, shortcut) == 0)
1933     {
1934       /* We found the iter for which we want to erase the shortcut value */
1935       /* Let's do it! */
1936       gtk_list_store_set (GTK_LIST_STORE (model), iter,
1937                           SHORTCUTS_SHORTCUT_COLUMN, NULL,
1938                           SHORTCUTS_SHORTCUT_LABEL_COLUMN, NULL, -1);
1939 
1940       g_free (shortcut);
1941 
1942       return TRUE;
1943     }
1944 
1945   g_free (shortcut);
1946 
1947   return FALSE;
1948 }
1949 
1950 
1951 static gboolean
xfwm_settings_validate_shortcut(XfceShortcutDialog * dialog,const gchar * shortcut,XfwmSettings * settings)1952 xfwm_settings_validate_shortcut (XfceShortcutDialog  *dialog,
1953                                  const gchar         *shortcut,
1954                                  XfwmSettings        *settings)
1955 {
1956   XfceShortcutsProvider *other_provider = NULL;
1957   XfceShortcut          *other_shortcut = NULL;
1958   GList                 *providers;
1959   GList                 *iter;
1960   gboolean               accepted = TRUE;
1961   gint                   response;
1962 
1963   g_return_val_if_fail (XFCE_IS_SHORTCUT_DIALOG (dialog), FALSE);
1964   g_return_val_if_fail (XFWM_IS_SETTINGS (settings), FALSE);
1965   g_return_val_if_fail (shortcut != NULL, FALSE);
1966 
1967   /* Ignore empty shortcuts */
1968   if (G_UNLIKELY (g_utf8_strlen (shortcut, -1) == 0))
1969     return FALSE;
1970 
1971   /* Ignore raw 'Return' and 'space' since that may have been used to activate the shortcut row */
1972   if (G_UNLIKELY (g_utf8_collate (shortcut, "Return") == 0 ||
1973                   g_utf8_collate (shortcut, "space") == 0))
1974     {
1975       return FALSE;
1976     }
1977 
1978   providers = xfce_shortcuts_provider_get_providers ();
1979 
1980   if (G_UNLIKELY (providers == NULL))
1981     return TRUE;
1982 
1983   for (iter = providers; iter != NULL && other_shortcut == NULL; iter = g_list_next (iter))
1984     {
1985       if (G_UNLIKELY (xfce_shortcuts_provider_has_shortcut (iter->data, shortcut)))
1986         {
1987           other_provider = g_object_ref (iter->data);
1988           other_shortcut = xfce_shortcuts_provider_get_shortcut (iter->data, shortcut);
1989         }
1990     }
1991 
1992   xfce_shortcuts_provider_free_providers (providers);
1993 
1994   if (G_UNLIKELY (other_shortcut != NULL))
1995     {
1996       if (G_LIKELY (!g_str_equal (xfce_shortcut_dialog_get_action (dialog), other_shortcut->command)))
1997         {
1998           response = xfce_shortcut_conflict_dialog (GTK_WINDOW (dialog),
1999                                                     xfce_shortcuts_provider_get_name (settings->priv->provider),
2000                                                     xfce_shortcuts_provider_get_name (other_provider),
2001                                                     shortcut,
2002                                                     xfce_shortcut_dialog_get_action (dialog),
2003                                                     other_shortcut->command,
2004                                                     FALSE);
2005 
2006           if (G_UNLIKELY (response == GTK_RESPONSE_ACCEPT))
2007             {
2008               GObject *view;
2009 
2010               xfce_shortcuts_provider_reset_shortcut (other_provider, shortcut);
2011 
2012               /* We need to update the treeview to erase the shortcut value */
2013               view = gtk_builder_get_object (settings->priv->builder, "shortcuts_treeview");
2014               gtk_tree_model_foreach (gtk_tree_view_get_model (GTK_TREE_VIEW (view)),
2015                                       xfwm_settings_update_treeview_on_conflict_replace,
2016                                       (gpointer) shortcut);
2017             }
2018           else
2019             accepted = FALSE;
2020         }
2021 
2022       xfce_shortcut_free (other_shortcut);
2023       g_object_unref (other_provider);
2024     }
2025 
2026   return accepted;
2027 }
2028 
2029 
2030 
2031 static void
xfwm_settings_shortcut_row_activated(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column,XfwmSettings * settings)2032 xfwm_settings_shortcut_row_activated (GtkTreeView       *tree_view,
2033                                       GtkTreePath       *path,
2034                                       GtkTreeViewColumn *column,
2035                                       XfwmSettings      *settings)
2036 {
2037   GtkTreeModel *model;
2038   GtkTreeIter   iter;
2039   GtkWidget    *dialog;
2040   const gchar  *new_shortcut;
2041   gchar        *shortcut;
2042   gchar        *feature;
2043   gchar        *name;
2044   gint          response;
2045 
2046   g_return_if_fail (XFWM_IS_SETTINGS (settings));
2047   g_return_if_fail (XFCE_IS_SHORTCUTS_PROVIDER (settings->priv->provider));
2048 
2049   model = gtk_tree_view_get_model (tree_view);
2050 
2051   if (G_LIKELY (gtk_tree_model_get_iter (model, &iter, path)))
2052     {
2053       /* Read shortcut from the activated row */
2054       gtk_tree_model_get (model, &iter,
2055                           SHORTCUTS_NAME_COLUMN, &name,
2056                           SHORTCUTS_FEATURE_COLUMN, &feature,
2057                           SHORTCUTS_SHORTCUT_COLUMN, &shortcut, -1);
2058 
2059       /* Request a new shortcut from the user */
2060       dialog = xfce_shortcut_dialog_new ("xfwm4", name, feature);
2061       g_signal_connect (dialog, "validate-shortcut",
2062                         G_CALLBACK (xfwm_settings_validate_shortcut), settings);
2063       response = xfce_shortcut_dialog_run (XFCE_SHORTCUT_DIALOG (dialog), gtk_widget_get_toplevel (GTK_WIDGET (tree_view)));
2064 
2065       if (G_LIKELY (response == GTK_RESPONSE_OK))
2066         {
2067           /* Remove old shortcut from the settings */
2068           if (G_LIKELY (shortcut != NULL))
2069             xfce_shortcuts_provider_reset_shortcut (settings->priv->provider, shortcut);
2070 
2071           /* Get new shortcut entered by the user */
2072           new_shortcut = xfce_shortcut_dialog_get_shortcut (XFCE_SHORTCUT_DIALOG (dialog));
2073 
2074           /* Save new shortcut */
2075           xfce_shortcuts_provider_set_shortcut (settings->priv->provider, new_shortcut, feature, FALSE);
2076         }
2077       else if (G_UNLIKELY (response == GTK_RESPONSE_REJECT))
2078         {
2079           /* Remove old shortcut from the settings */
2080           if (G_LIKELY (shortcut != NULL))
2081             {
2082               xfce_shortcuts_provider_reset_shortcut (settings->priv->provider, shortcut);
2083               gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2084                                   SHORTCUTS_SHORTCUT_COLUMN, NULL,
2085                                   SHORTCUTS_SHORTCUT_LABEL_COLUMN, NULL, -1);
2086             }
2087         }
2088 
2089       /* Destroy the shortcut dialog */
2090       gtk_widget_destroy (dialog);
2091 
2092       /* Free strings */
2093       g_free (feature);
2094       g_free (name);
2095       g_free (shortcut);
2096     }
2097 }
2098