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