1 /*
2 * middle-click-window-close: Closes windows in window by middle-click
3 *
4 * Copyright 2012-2020 Stephan Haller <nomad@froevel.de>
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
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU 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., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 *
21 *
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include "middle-click-window-close.h"
29
30 #include <libxfdashboard/libxfdashboard.h>
31 #include <glib/gi18n-lib.h>
32 #include <gtk/gtk.h>
33 #include <math.h>
34
35
36 /* Define this class in GObject system */
37 struct _XfdashboardMiddleClickWindowClosePrivate
38 {
39 /* Instance related */
40 XfdashboardStage *stage;
41 guint stageActorCreatedSignalID;
42 guint stageDestroySignalID;
43
44 XfdashboardCssSelector *liveWindowSelector;
45 };
46
47 G_DEFINE_DYNAMIC_TYPE_EXTENDED(XfdashboardMiddleClickWindowClose,
48 xfdashboard_middle_click_window_close,
49 G_TYPE_OBJECT,
50 0,
51 G_ADD_PRIVATE_DYNAMIC(XfdashboardMiddleClickWindowClose))
52
53 /* Define this class in this plugin */
54 XFDASHBOARD_DEFINE_PLUGIN_TYPE(xfdashboard_middle_click_window_close);
55
56 /* IMPLEMENTATION: Private variables and methods */
57 #define DEFAULT_WINDOW_CLOSE_BUTTON XFDASHBOARD_CLICK_ACTION_MIDDLE_BUTTON
58 #define XFDASHBOARD_MIDDLE_CLICK_WINDOW_CLOSE_ACTION_NAME "middle-click-window-close-action"
59
60 /* A configured live window actor was clicked */
_xfdashboard_middle_click_window_close_on_clicked(XfdashboardMiddleClickWindowClose * self,ClutterActor * inActor,gpointer inUserData)61 static void _xfdashboard_middle_click_window_close_on_clicked(XfdashboardMiddleClickWindowClose *self,
62 ClutterActor *inActor,
63 gpointer inUserData)
64 {
65 XfdashboardLiveWindowSimple *liveWindow;
66 XfdashboardClickAction *action;
67 guint button;
68 XfdashboardWindowTrackerWindow *window;
69
70 g_return_if_fail(XFDASHBOARD_IS_MIDDLE_CLICK_WINDOW_CLOSE(self));
71 g_return_if_fail(XFDASHBOARD_IS_LIVE_WINDOW(inActor));
72 g_return_if_fail(XFDASHBOARD_IS_CLICK_ACTION(inUserData));
73
74 liveWindow=XFDASHBOARD_LIVE_WINDOW_SIMPLE(inActor);
75 action=XFDASHBOARD_CLICK_ACTION(inUserData);
76
77 /* Get button used for click action */
78 button=xfdashboard_click_action_get_button(action);
79 if(button==DEFAULT_WINDOW_CLOSE_BUTTON)
80 {
81 window=xfdashboard_live_window_simple_get_window(liveWindow);
82 xfdashboard_window_tracker_window_close(window);
83 }
84 }
85
86 /* An actor was created so check if we are interested at this one */
_xfdashboard_middle_click_window_close_on_actor_created(XfdashboardMiddleClickWindowClose * self,ClutterActor * inActor,gpointer inUserData)87 static void _xfdashboard_middle_click_window_close_on_actor_created(XfdashboardMiddleClickWindowClose *self,
88 ClutterActor *inActor,
89 gpointer inUserData)
90 {
91 XfdashboardMiddleClickWindowClosePrivate *priv;
92 gint score;
93 ClutterAction *action;
94
95 g_return_if_fail(XFDASHBOARD_IS_MIDDLE_CLICK_WINDOW_CLOSE(self));
96 g_return_if_fail(CLUTTER_IS_ACTOR(inActor));
97
98 priv=self->priv;
99
100 /* Check if we are interested in this newly created actor and set it up */
101 if(XFDASHBOARD_IS_STYLABLE(inActor))
102 {
103 score=xfdashboard_css_selector_score(priv->liveWindowSelector, XFDASHBOARD_STYLABLE(inActor));
104 if(score>0)
105 {
106 action=xfdashboard_click_action_new();
107 clutter_actor_add_action_with_name(inActor, XFDASHBOARD_MIDDLE_CLICK_WINDOW_CLOSE_ACTION_NAME, action);
108 g_signal_connect_swapped(action, "clicked", G_CALLBACK(_xfdashboard_middle_click_window_close_on_clicked), self);
109 }
110 }
111 }
112
113 /* Callback for traversal to setup live window for use with this plugin */
_xfdashboard_middle_click_window_close_traverse_acquire(ClutterActor * inActor,gpointer inUserData)114 static gboolean _xfdashboard_middle_click_window_close_traverse_acquire(ClutterActor *inActor,
115 gpointer inUserData)
116 {
117 XfdashboardMiddleClickWindowClose *self;
118 ClutterAction *action;
119
120 g_return_val_if_fail(XFDASHBOARD_IS_LIVE_WINDOW(inActor), XFDASHBOARD_TRAVERSAL_CONTINUE);
121 g_return_val_if_fail(XFDASHBOARD_IS_MIDDLE_CLICK_WINDOW_CLOSE(inUserData), XFDASHBOARD_TRAVERSAL_CONTINUE);
122
123 self=XFDASHBOARD_MIDDLE_CLICK_WINDOW_CLOSE(inUserData);
124
125 /* Set up live window */
126 action=xfdashboard_click_action_new();
127 clutter_actor_add_action_with_name(inActor, XFDASHBOARD_MIDDLE_CLICK_WINDOW_CLOSE_ACTION_NAME, action);
128 g_signal_connect_swapped(action, "clicked", G_CALLBACK(_xfdashboard_middle_click_window_close_on_clicked), self);
129
130 /* All done continue with traversal */
131 return(XFDASHBOARD_TRAVERSAL_CONTINUE);
132 }
133
134 /* Callback for traversal to deconfigure live window from use at this plugin */
_xfdashboard_middle_click_window_close_traverse_release(ClutterActor * inActor,gpointer inUserData)135 static gboolean _xfdashboard_middle_click_window_close_traverse_release(ClutterActor *inActor,
136 gpointer inUserData)
137 {
138 g_return_val_if_fail(XFDASHBOARD_IS_LIVE_WINDOW(inActor), XFDASHBOARD_TRAVERSAL_CONTINUE);
139 g_return_val_if_fail(XFDASHBOARD_IS_MIDDLE_CLICK_WINDOW_CLOSE(inUserData), XFDASHBOARD_TRAVERSAL_CONTINUE);
140
141 /* Set up live window */
142 clutter_actor_remove_action_by_name(inActor, XFDASHBOARD_MIDDLE_CLICK_WINDOW_CLOSE_ACTION_NAME);
143
144 /* All done continue with traversal */
145 return(XFDASHBOARD_TRAVERSAL_CONTINUE);
146 }
147
148 /* Stage is going to be destroyed */
_xfdashboard_middle_click_window_close_on_stage_destroyed(XfdashboardMiddleClickWindowClose * self,gpointer inUserData)149 static void _xfdashboard_middle_click_window_close_on_stage_destroyed(XfdashboardMiddleClickWindowClose *self,
150 gpointer inUserData)
151 {
152 XfdashboardMiddleClickWindowClosePrivate *priv;
153 XfdashboardStage *stage;
154
155 g_return_if_fail(XFDASHBOARD_IS_MIDDLE_CLICK_WINDOW_CLOSE(self));
156 g_return_if_fail(XFDASHBOARD_IS_STAGE(inUserData));
157
158 priv=self->priv;
159 stage=XFDASHBOARD_STAGE(inUserData);
160
161 /* Iterate through all existing live window actors that may still exist
162 * and deconfigure them from use at this plugin. We traverse the stage
163 * which is going to be destroyed and provided as function parameter
164 * regardless if it the stage we have set up initially or if it is any other.
165 */
166 xfdashboard_traverse_actor(CLUTTER_ACTOR(stage),
167 priv->liveWindowSelector,
168 _xfdashboard_middle_click_window_close_traverse_release,
169 self);
170
171 /* Disconnect signals from stage as it will be destroyed and reset variables
172 * but only if it the stage we are handling right now (this should always be
173 * the case!)
174 */
175 if(priv->stage==stage)
176 {
177 /* Disconnect signals */
178 if(priv->stageActorCreatedSignalID)
179 {
180 g_signal_handler_disconnect(priv->stage, priv->stageActorCreatedSignalID);
181 priv->stageActorCreatedSignalID=0;
182 }
183
184 if(priv->stageDestroySignalID)
185 {
186 g_signal_handler_disconnect(priv->stage, priv->stageDestroySignalID);
187 priv->stageDestroySignalID=0;
188 }
189
190 /* Release stage */
191 priv->stage=NULL;
192 }
193 }
194
195 /* IMPLEMENTATION: GObject */
196
197 /* Dispose this object */
_xfdashboard_middle_click_window_close_dispose(GObject * inObject)198 static void _xfdashboard_middle_click_window_close_dispose(GObject *inObject)
199 {
200 XfdashboardMiddleClickWindowClose *self=XFDASHBOARD_MIDDLE_CLICK_WINDOW_CLOSE(inObject);
201 XfdashboardMiddleClickWindowClosePrivate *priv=self->priv;
202
203 /* Release allocated resources */
204 if(priv->stage)
205 {
206 /* Iterate through all existing live window actors that may still exist
207 * and deconfigure them from use at this plugin.
208 */
209 xfdashboard_traverse_actor(CLUTTER_ACTOR(priv->stage),
210 priv->liveWindowSelector,
211 _xfdashboard_middle_click_window_close_traverse_release,
212 self);
213
214 /* Disconnect signals from stage */
215 if(priv->stageActorCreatedSignalID)
216 {
217 g_signal_handler_disconnect(priv->stage, priv->stageActorCreatedSignalID);
218 priv->stageActorCreatedSignalID=0;
219 }
220
221 if(priv->stageDestroySignalID)
222 {
223 g_signal_handler_disconnect(priv->stage, priv->stageDestroySignalID);
224 priv->stageDestroySignalID=0;
225 }
226
227 /* Release stage */
228 priv->stage=NULL;
229 }
230
231 if(priv->liveWindowSelector)
232 {
233 g_object_unref(priv->liveWindowSelector);
234 priv->liveWindowSelector=NULL;
235 }
236
237 /* Call parent's class dispose method */
238 G_OBJECT_CLASS(xfdashboard_middle_click_window_close_parent_class)->dispose(inObject);
239 }
240
241 /* Class initialization
242 * Override functions in parent classes and define properties
243 * and signals
244 */
xfdashboard_middle_click_window_close_class_init(XfdashboardMiddleClickWindowCloseClass * klass)245 void xfdashboard_middle_click_window_close_class_init(XfdashboardMiddleClickWindowCloseClass *klass)
246 {
247 GObjectClass *gobjectClass=G_OBJECT_CLASS(klass);
248
249 /* Override functions */
250 gobjectClass->dispose=_xfdashboard_middle_click_window_close_dispose;
251 }
252
253 /* Class finalization */
xfdashboard_middle_click_window_close_class_finalize(XfdashboardMiddleClickWindowCloseClass * klass)254 void xfdashboard_middle_click_window_close_class_finalize(XfdashboardMiddleClickWindowCloseClass *klass)
255 {
256 }
257
258 /* Object initialization
259 * Create private structure and set up default values
260 */
xfdashboard_middle_click_window_close_init(XfdashboardMiddleClickWindowClose * self)261 void xfdashboard_middle_click_window_close_init(XfdashboardMiddleClickWindowClose *self)
262 {
263 XfdashboardMiddleClickWindowClosePrivate *priv;
264
265 self->priv=priv=xfdashboard_middle_click_window_close_get_instance_private(self);
266
267 /* Set up default values */
268 priv->stage=xfdashboard_application_get_stage(NULL);
269 priv->stageActorCreatedSignalID=0;
270 priv->stageDestroySignalID=0;
271 priv->liveWindowSelector=xfdashboard_css_selector_new_from_string("XfdashboardWindowsView XfdashboardLiveWindow");
272
273 /* Iterate through all already existing live window actors and configure
274 * them for use with this plugin.
275 */
276 xfdashboard_traverse_actor(CLUTTER_ACTOR(priv->stage),
277 priv->liveWindowSelector,
278 _xfdashboard_middle_click_window_close_traverse_acquire,
279 self);
280
281 /* Connect signal to get notified about actor creations and filter out
282 * and set up the ones we are interested in.
283 */
284 priv->stageActorCreatedSignalID=
285 g_signal_connect_swapped(priv->stage,
286 "actor-created",
287 G_CALLBACK(_xfdashboard_middle_click_window_close_on_actor_created),
288 self);
289
290 /* Connect signal to get notified when stage is getting destoyed */
291 priv->stageDestroySignalID=
292 g_signal_connect_swapped(priv->stage,
293 "destroy",
294 G_CALLBACK(_xfdashboard_middle_click_window_close_on_stage_destroyed),
295 self);
296 }
297
298
299 /* IMPLEMENTATION: Public API */
300
301 /* Create new instance */
xfdashboard_middle_click_window_close_new(void)302 XfdashboardMiddleClickWindowClose* xfdashboard_middle_click_window_close_new(void)
303 {
304 GObject *middleClickWindowClose;
305
306 middleClickWindowClose=g_object_new(XFDASHBOARD_TYPE_MIDDLE_CLICK_WINDOW_CLOSE, NULL);
307 if(!middleClickWindowClose) return(NULL);
308
309 return(XFDASHBOARD_MIDDLE_CLICK_WINDOW_CLOSE(middleClickWindowClose));
310 }
311