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