Merging upstream version 0.7.0 (Closes: #493243, #510888, #567617, #699414, #709777...
[debian/lxpanel.git] / plugins / wincmd.c
1 /**
2 * Copyright (c) 2006-2014 LxDE Developers, see the file AUTHORS for details.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #include <stdlib.h>
20
21 #include <glib/gi18n.h>
22
23 #include "misc.h"
24 #include "plugin.h"
25
26 /* Commands that can be issued. */
27 typedef enum {
28 WC_NONE,
29 WC_ICONIFY,
30 WC_SHADE
31 } WindowCommand;
32
33 /* Private context for window command plugin. */
34 typedef struct {
35 config_setting_t *settings; /* Settings array */
36 char * image; /* Main icon */
37 WindowCommand button_1_command; /* Command for mouse button 1 */
38 WindowCommand button_2_command; /* Command for mouse button 2 */
39 gboolean toggle_preference; /* User preference: toggle iconify/shade and map */
40 gboolean toggle_state; /* State of toggle */
41 } WinCmdPlugin;
42
43 static const char *wincmd_names[] = {
44 "none",
45 "iconify",
46 "shade"
47 };
48
49 static void wincmd_destructor(gpointer user_data);
50
51 /* Adjust the toggle state after a window command. */
52 static void wincmd_adjust_toggle_state(WinCmdPlugin * wc)
53 {
54 /* Ensure that if the user changes the preference from "unconditional" to "toggle", we do a raise on the next click. */
55 if (wc->toggle_preference)
56 wc->toggle_state = ! wc->toggle_state;
57 else wc->toggle_state = TRUE;
58 }
59
60 /* Execute a window command. */
61 static void wincmd_execute(WinCmdPlugin * wc, WindowCommand command)
62 {
63 /* Get the list of all windows. */
64 int client_count;
65 Window * client_list = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_CLIENT_LIST, XA_WINDOW, &client_count);
66 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
67 if (client_list != NULL)
68 {
69 /* Loop over all windows. */
70 int current_desktop = get_net_current_desktop();
71 int i;
72 for (i = 0; i < client_count; i++)
73 {
74 /* Get the desktop and window type properties. */
75 NetWMWindowType nwwt;
76 int task_desktop = get_net_wm_desktop(client_list[i]);
77 get_net_wm_window_type(client_list[i], &nwwt);
78
79 /* If the task is visible on the current desktop and it is an ordinary window,
80 * execute the requested Iconify or Shade change. */
81 if (((task_desktop == -1) || (task_desktop == current_desktop))
82 && (( ! nwwt.dock) && ( ! nwwt.desktop) && ( ! nwwt.splash)))
83 {
84 switch (command)
85 {
86 case WC_NONE:
87 break;
88
89 case WC_ICONIFY:
90 if (( ! wc->toggle_preference) || ( ! wc->toggle_state))
91 XIconifyWindow(xdisplay, client_list[i], DefaultScreen(xdisplay));
92 else
93 XMapWindow (xdisplay, client_list[i]);
94 break;
95
96 case WC_SHADE:
97 Xclimsg(client_list[i], a_NET_WM_STATE,
98 ((( ! wc->toggle_preference) || ( ! wc->toggle_state)) ? a_NET_WM_STATE_ADD : a_NET_WM_STATE_REMOVE),
99 a_NET_WM_STATE_SHADED, 0, 0, 0);
100 break;
101 }
102 }
103 }
104 XFree(client_list);
105
106 /* Adjust toggle state. */
107 wincmd_adjust_toggle_state(wc);
108 }
109 }
110
111 /* Handler for "clicked" signal on main widget. */
112 static gboolean wincmd_button_clicked(GtkWidget * widget, GdkEventButton * event, LXPanel * panel)
113 {
114 WinCmdPlugin * wc = lxpanel_plugin_get_data(widget);
115
116 /* Left-click to iconify. */
117 if (event->button == 1)
118 {
119 GdkScreen* screen = gtk_widget_get_screen(widget);
120 static GdkAtom atom = 0;
121 if( G_UNLIKELY(0 == atom) )
122 atom = gdk_atom_intern("_NET_SHOWING_DESKTOP", FALSE);
123
124 /* If window manager supports _NET_SHOWING_DESKTOP, use it.
125 * Otherwise, fall back to iconifying windows individually. */
126 if (gdk_x11_screen_supports_net_wm_hint(screen, atom))
127 {
128 int showing_desktop = ((( ! wc->toggle_preference) || ( ! wc->toggle_state)) ? 1 : 0);
129 Xclimsg(DefaultRootWindow(GDK_DISPLAY_XDISPLAY(gdk_display_get_default())),
130 a_NET_SHOWING_DESKTOP, showing_desktop, 0, 0, 0, 0);
131 wincmd_adjust_toggle_state(wc);
132 }
133 else
134 wincmd_execute(wc, WC_ICONIFY);
135 }
136
137 /* Middle-click to shade. */
138 else if (event->button == 2)
139 wincmd_execute(wc, WC_SHADE);
140
141 return TRUE;
142 }
143
144 /* Plugin constructor. */
145 static GtkWidget *wincmd_constructor(LXPanel *panel, config_setting_t *settings)
146 {
147 /* Allocate plugin context and set into Plugin private data pointer. */
148 WinCmdPlugin * wc = g_new0(WinCmdPlugin, 1);
149 GtkWidget * p;
150 const char *str;
151 int tmp_int;
152
153 /* Initialize to defaults. */
154 wc->button_1_command = WC_ICONIFY;
155 wc->button_2_command = WC_SHADE;
156
157 /* Load parameters from the configuration file. */
158 if (config_setting_lookup_string(settings, "Button1", &str))
159 {
160 if (g_ascii_strcasecmp(str, "shade") == 0)
161 wc->button_1_command = WC_SHADE;
162 else if (g_ascii_strcasecmp(str, "none") == 0)
163 wc->button_1_command = WC_NONE;
164 /* default is WC_ICONIFY */
165 }
166 if (config_setting_lookup_string(settings, "Button2", &str))
167 {
168 if (g_ascii_strcasecmp(str, "iconify") == 0)
169 wc->button_2_command = WC_ICONIFY;
170 else if (g_ascii_strcasecmp(str, "none") == 0)
171 wc->button_2_command = WC_NONE;
172 }
173 if (config_setting_lookup_string(settings, "image", &str))
174 wc->image = expand_tilda(str);
175 if (config_setting_lookup_int(settings, "Toggle", &tmp_int))
176 wc->toggle_preference = tmp_int != 0;
177
178 /* Default the image if unspecified. */
179 if (wc->image == NULL)
180 wc->image = g_strdup("window-manager");
181
182 /* Save construction pointers */
183 wc->settings = settings;
184
185 /* Allocate top level widget and set into Plugin widget pointer. */
186 p = lxpanel_button_new_for_icon(panel, wc->image, NULL, NULL);
187 lxpanel_plugin_set_data(p, wc, wincmd_destructor);
188 gtk_container_set_border_width(GTK_CONTAINER(p), 0);
189 gtk_widget_set_tooltip_text(p, _("Left click to iconify all windows. Middle click to shade them."));
190
191 /* Show the widget and return. */
192 return p;
193 }
194
195 /* Plugin destructor. */
196 static void wincmd_destructor(gpointer user_data)
197 {
198 WinCmdPlugin * wc = user_data;
199 g_free(wc->image);
200 g_free(wc);
201 }
202
203 /* Callback when the configuration dialog has recorded a configuration change. */
204 static gboolean wincmd_apply_configuration(gpointer user_data)
205 {
206 GtkWidget * p = user_data;
207 WinCmdPlugin * wc = lxpanel_plugin_get_data(p);
208
209 /* Just save settings */
210 config_group_set_string(wc->settings, "image", wc->image);
211 config_group_set_string(wc->settings, "Button1",
212 wincmd_names[wc->button_1_command]);
213 config_group_set_string(wc->settings, "Button2",
214 wincmd_names[wc->button_2_command]);
215 config_group_set_int(wc->settings, "Toggle", wc->toggle_preference);
216 return FALSE;
217 }
218
219 /* Callback when the configuration dialog is to be shown. */
220 static GtkWidget *wincmd_configure(LXPanel *panel, GtkWidget *p)
221 {
222 WinCmdPlugin * wc = lxpanel_plugin_get_data(p);
223 return lxpanel_generic_config_dlg(_("Minimize All Windows"),
224 panel, wincmd_apply_configuration, p,
225 _("Alternately iconify/shade and raise"), &wc->toggle_preference, CONF_TYPE_BOOL,
226 /* FIXME: configure buttons 1 and 2 */
227 NULL);
228 }
229
230
231 /* Callback when panel configuration changes. */
232 static void wincmd_panel_reconfigure(LXPanel *panel, GtkWidget *p)
233 {
234 WinCmdPlugin * wc = lxpanel_plugin_get_data(p);
235
236 lxpanel_button_set_icon(p, wc->image, panel_get_icon_size(panel));
237 }
238
239 /* Plugin descriptor. */
240 LXPanelPluginInit lxpanel_static_plugin_wincmd = {
241 .name = N_("Minimize All Windows"),
242 .description = N_("Sends commands to all desktop windows.\nSupported commands are 1) iconify and 2) shade"),
243
244 .new_instance = wincmd_constructor,
245 .config = wincmd_configure,
246 .reconfigure = wincmd_panel_reconfigure,
247 .button_press_event = wincmd_button_clicked
248 };