1 /*
2  * stage: Global stage of application
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 <libxfdashboard/stage.h>
29 
30 #include <glib/gi18n-lib.h>
31 #include <clutter/clutter.h>
32 #include <gtk/gtk.h>
33 #include <gdk/gdk.h>
34 #include <math.h>
35 
36 #include <libxfdashboard/application.h>
37 #include <libxfdashboard/viewpad.h>
38 #include <libxfdashboard/view-selector.h>
39 #include <libxfdashboard/text-box.h>
40 #include <libxfdashboard/quicklaunch.h>
41 #include <libxfdashboard/applications-view.h>
42 #include <libxfdashboard/windows-view.h>
43 #include <libxfdashboard/search-view.h>
44 #include <libxfdashboard/toggle-button.h>
45 #include <libxfdashboard/workspace-selector.h>
46 #include <libxfdashboard/collapse-box.h>
47 #include <libxfdashboard/tooltip-action.h>
48 #include <libxfdashboard/stylable.h>
49 #include <libxfdashboard/utils.h>
50 #include <libxfdashboard/focus-manager.h>
51 #include <libxfdashboard/enums.h>
52 #include <libxfdashboard/window-tracker.h>
53 #include <libxfdashboard/window-content.h>
54 #include <libxfdashboard/stage-interface.h>
55 #include <libxfdashboard/compat.h>
56 #include <libxfdashboard/debug.h>
57 
58 
59 /* Define this class in GObject system */
60 struct _XfdashboardStagePrivate
61 {
62 	/* Properties related */
63 	XfdashboardStageBackgroundImageType		backgroundType;
64 
65 	ClutterColor							*backgroundColor;
66 
67 	/* Actors */
68 	ClutterActor							*backgroundImageLayer;
69 	ClutterActor							*backgroundColorLayer;
70 
71 	gpointer								primaryInterface;
72 	gpointer								quicklaunch;
73 	gpointer								searchbox;
74 	gpointer								workspaces;
75 	gpointer								viewpad;
76 	gpointer								viewSelector;
77 	gpointer								notification;
78 	gpointer								tooltip;
79 
80 	/* Instance related */
81 	XfdashboardWindowTracker				*windowTracker;
82 	XfdashboardWindowTrackerWindow			*stageWindow;
83 
84 	gboolean								searchActive;
85 	gint									lastSearchTextLength;
86 	XfdashboardView							*viewBeforeSearch;
87 	gchar									*switchToView;
88 	gpointer								focusActorOnShow;
89 
90 	guint									notificationTimeoutID;
91 
92 	XfdashboardFocusManager					*focusManager;
93 };
94 
95 G_DEFINE_TYPE_WITH_PRIVATE(XfdashboardStage,
96 							xfdashboard_stage,
97 							CLUTTER_TYPE_STAGE)
98 
99 /* Properties */
100 enum
101 {
102 	PROP_0,
103 
104 	PROP_BACKGROUND_IMAGE_TYPE,
105 	PROP_BACKGROUND_COLOR,
106 
107 	PROP_SWITCH_TO_VIEW,
108 
109 	PROP_LAST
110 };
111 
112 static GParamSpec* XfdashboardStageProperties[PROP_LAST]={ 0, };
113 
114 /* Signals */
115 enum
116 {
117 	SIGNAL_ACTOR_CREATED,
118 
119 	SIGNAL_SEARCH_STARTED,
120 	SIGNAL_SEARCH_CHANGED,
121 	SIGNAL_SEARCH_ENDED,
122 
123 	SIGNAL_SHOW_TOOLTIP,
124 	SIGNAL_HIDE_TOOLTIP,
125 
126 	SIGNAL_LAST
127 };
128 
129 static guint XfdashboardStageSignals[SIGNAL_LAST]={ 0, };
130 
131 
132 /* Forward declaration */
133 static void _xfdashboard_stage_on_window_opened(XfdashboardStage *self,
134 													XfdashboardWindowTrackerWindow *inWindow,
135 													gpointer inUserData);
136 
137 
138 /* IMPLEMENTATION: Private variables and methods */
139 #define NOTIFICATION_TIMEOUT_XFCONF_PROP				"/min-notification-timeout"
140 #define DEFAULT_NOTIFICATION_TIMEOUT					3000
141 #define RESET_SEARCH_ON_RESUME_XFCONF_PROP				"/reset-search-on-resume"
142 #define DEFAULT_RESET_SEARCH_ON_RESUME					TRUE
143 #define SWITCH_VIEW_ON_RESUME_XFCONF_PROP				"/switch-to-view-on-resume"
144 #define DEFAULT_SWITCH_VIEW_ON_RESUME					NULL
145 #define RESELECT_THEME_FOCUS_ON_RESUME_XFCONF_PROP		"/reselect-theme-focus-on-resume"
146 #define DEFAULT_RESELECT_THEME_FOCUS_ON_RESUME			FALSE
147 #define XFDASHBOARD_THEME_LAYOUT_PRIMARY				"primary"
148 #define XFDASHBOARD_THEME_LAYOUT_SECONDARY				"secondary"
149 
150 typedef struct _XfdashboardStageThemeInterfaceData		XfdashboardStageThemeInterfaceData;
151 struct _XfdashboardStageThemeInterfaceData
152 {
153 	ClutterActor		*actor;
154 	GPtrArray			*focusables;
155 	ClutterActor		*focus;
156 };
157 
158 /* Handle an event */
_xfdashboard_stage_event(ClutterActor * inActor,ClutterEvent * inEvent)159 static gboolean _xfdashboard_stage_event(ClutterActor *inActor, ClutterEvent *inEvent)
160 {
161 	XfdashboardStage			*self;
162 	XfdashboardStagePrivate		*priv;
163 	gboolean					result;
164 
165 	g_return_val_if_fail(XFDASHBOARD_IS_STAGE(inActor), CLUTTER_EVENT_PROPAGATE);
166 
167 	self=XFDASHBOARD_STAGE(inActor);
168 	priv=self->priv;
169 
170 	/* Do only intercept any event if a focus manager is available */
171 	if(!priv->focusManager) return(CLUTTER_EVENT_PROPAGATE);
172 
173 	/* Do only intercept "key-press" and "key-release" events */
174 	if(clutter_event_type(inEvent)!=CLUTTER_KEY_PRESS &&
175 		clutter_event_type(inEvent)!=CLUTTER_KEY_RELEASE)
176 	{
177 		return(CLUTTER_EVENT_PROPAGATE);
178 	}
179 
180 	/* Handle key release event */
181 	if(clutter_event_type(inEvent)==CLUTTER_KEY_RELEASE)
182 	{
183 		/* Handle key */
184 		switch(inEvent->key.keyval)
185 		{
186 			/* Handle ESC key to clear search box or quit/suspend application */
187 			case CLUTTER_KEY_Escape:
188 			{
189 				/* If search is active then end search by clearing search box ... */
190 				if(priv->searchbox &&
191 					!xfdashboard_text_box_is_empty(XFDASHBOARD_TEXT_BOX(priv->searchbox)))
192 				{
193 					xfdashboard_text_box_set_text(XFDASHBOARD_TEXT_BOX(priv->searchbox), NULL);
194 					return(CLUTTER_EVENT_STOP);
195 				}
196 					/* ... otherwise quit application */
197 					else
198 					{
199 						xfdashboard_application_suspend_or_quit(NULL);
200 						return(CLUTTER_EVENT_STOP);
201 					}
202 			}
203 			break;
204 
205 			default:
206 				/* Fallthrough */
207 				break;
208 		}
209 	}
210 
211 	/* Ask focus manager to handle this event */
212 	result=xfdashboard_focus_manager_handle_key_event(priv->focusManager, inEvent, NULL);
213 	if(result==CLUTTER_EVENT_STOP) return(result);
214 
215 	/* If even focus manager did not handle this event send this event to searchbox */
216 	if(priv->searchbox &&
217 		XFDASHBOARD_IS_FOCUSABLE(priv->searchbox) &&
218 		xfdashboard_focus_manager_is_registered(priv->focusManager, XFDASHBOARD_FOCUSABLE(priv->searchbox)))
219 	{
220 		/* Ask search to handle this event if it has not the focus currently
221 		 * because in this case it has already handled the event and we do
222 		 * not to do this twice.
223 		 */
224 		if(xfdashboard_focus_manager_get_focus(priv->focusManager)!=XFDASHBOARD_FOCUSABLE(priv->searchbox))
225 		{
226 			result=xfdashboard_focus_manager_handle_key_event(priv->focusManager, inEvent, XFDASHBOARD_FOCUSABLE(priv->searchbox));
227 			if(result==CLUTTER_EVENT_STOP) return(result);
228 		}
229 	}
230 
231 	/* If we get here there was no searchbox or it could not handle the event
232 	 * so stop further processing.
233 	 */
234 	return(CLUTTER_EVENT_STOP);
235 }
236 
237 /* Get view to switch to by first looking upr temporary view ID set via command-line
238  * and if not found or not set then looking up view ID configured via settings.
239  */
_xfdashboard_stage_get_view_to_switch_to(XfdashboardStage * self)240 static XfdashboardView* _xfdashboard_stage_get_view_to_switch_to(XfdashboardStage *self)
241 {
242 	XfdashboardStagePrivate				*priv;
243 	XfdashboardView						*view;
244 
245 	g_return_val_if_fail(XFDASHBOARD_IS_STAGE(self), NULL);
246 
247 	priv=self->priv;
248 	view=NULL;
249 
250 	/* First lookup view at private variable 'switchToView' which has higher
251 	 * priority as it is a temporary value and is usually set via command-line.
252 	 */
253 	if(priv->switchToView)
254 	{
255 		view=xfdashboard_viewpad_find_view_by_id(XFDASHBOARD_VIEWPAD(priv->viewpad), priv->switchToView);
256 		if(!view) g_warning("Will not switch to unknown view '%s'", priv->switchToView);
257 
258 		/* Regardless if we could find view by its internal name or not
259 		 * reset variable because the switch should happen once only.
260 		 */
261 		g_free(priv->switchToView);
262 		priv->switchToView=NULL;
263 
264 		/* Notify about property change */
265 		g_object_notify_by_pspec(G_OBJECT(self), XfdashboardStageProperties[PROP_SWITCH_TO_VIEW]);
266 	}
267 
268 	/* If we have not to switch to a specific view or if this view cannot be found
269 	 * then lookup the configured view in settings by its internal name
270 	 */
271 	if(!view)
272 	{
273 		gchar							*resumeViewID;
274 
275 		/* Get view ID from settings and look up view */
276 		resumeViewID=xfconf_channel_get_string(xfdashboard_application_get_xfconf_channel(NULL),
277 												SWITCH_VIEW_ON_RESUME_XFCONF_PROP,
278 												DEFAULT_SWITCH_VIEW_ON_RESUME);
279 		if(resumeViewID)
280 		{
281 			/* Lookup view by its ID set configured settings */
282 			view=xfdashboard_viewpad_find_view_by_id(XFDASHBOARD_VIEWPAD(priv->viewpad), resumeViewID);
283 			if(!view) g_warning("Cannot switch to unknown view '%s'", resumeViewID);
284 
285 			/* Release allocated resources */
286 			g_free(resumeViewID);
287 		}
288 	}
289 
290 	/* Return view found */
291 	return(view);
292 }
293 
294 /* Set focus in stage */
_xfdashboard_stage_set_focus(XfdashboardStage * self)295 static void _xfdashboard_stage_set_focus(XfdashboardStage *self)
296 {
297 	XfdashboardStagePrivate		*priv;
298 	XfdashboardFocusable		*actor;
299 
300 	g_return_if_fail(XFDASHBOARD_IS_STAGE(self));
301 
302 	priv=self->priv;
303 
304 	/* Set focus if no focus is set */
305 	actor=xfdashboard_focus_manager_get_focus(priv->focusManager);
306 	if(!actor)
307 	{
308 		XfdashboardFocusable	*focusable;
309 
310 		/* First try to set focus to searchbox ... */
311 		if(XFDASHBOARD_IS_FOCUSABLE(priv->searchbox) &&
312 			xfdashboard_focusable_can_focus(XFDASHBOARD_FOCUSABLE(priv->searchbox)))
313 		{
314 			xfdashboard_focus_manager_set_focus(priv->focusManager, XFDASHBOARD_FOCUSABLE(priv->searchbox));
315 		}
316 			/* ... then lookup first focusable actor */
317 			else
318 			{
319 				focusable=xfdashboard_focus_manager_get_next_focusable(priv->focusManager, NULL);
320 				if(focusable) xfdashboard_focus_manager_set_focus(priv->focusManager, focusable);
321 			}
322 	}
323 }
324 
325 /* Stage got signal to show a tooltip */
_xfdashboard_stage_show_tooltip(XfdashboardStage * self,ClutterAction * inAction)326 static void _xfdashboard_stage_show_tooltip(XfdashboardStage *self, ClutterAction *inAction)
327 {
328 	XfdashboardStagePrivate		*priv;
329 	XfdashboardTooltipAction	*tooltipAction;
330 	const gchar					*tooltipText;
331 	gfloat						tooltipX, tooltipY;
332 	gfloat						tooltipWidth, tooltipHeight;
333 	guint						cursorSize;
334 	gfloat						x, y;
335 	gfloat						stageWidth, stageHeight;
336 
337 	g_return_if_fail(XFDASHBOARD_IS_STAGE(self));
338 	g_return_if_fail(XFDASHBOARD_IS_TOOLTIP_ACTION(inAction));
339 	g_return_if_fail(self->priv->tooltip);
340 
341 	priv=self->priv;
342 	tooltipAction=XFDASHBOARD_TOOLTIP_ACTION(inAction);
343 
344 	/* Hide tooltip while setup to avoid flicker */
345 	clutter_actor_hide(priv->tooltip);
346 
347 	/* Get tooltip text and update text in tooltip actor */
348 	tooltipText=xfdashboard_tooltip_action_get_text(tooltipAction);
349 	xfdashboard_text_box_set_text(XFDASHBOARD_TEXT_BOX(priv->tooltip), tooltipText);
350 
351 	/* Determine coordinates where to show tooltip at */
352 	xfdashboard_tooltip_action_get_position(tooltipAction, &tooltipX, &tooltipY);
353 	clutter_actor_get_size(priv->tooltip, &tooltipWidth, &tooltipHeight);
354 
355 	cursorSize=gdk_display_get_default_cursor_size(gdk_display_get_default());
356 
357 	clutter_actor_get_size(CLUTTER_ACTOR(self), &stageWidth, &stageHeight);
358 
359 	x=tooltipX+cursorSize;
360 	y=tooltipY+cursorSize;
361 	if((x+tooltipWidth)>stageWidth) x=tooltipX-tooltipWidth;
362 	if((y+tooltipHeight)>stageHeight) y=tooltipY-tooltipHeight;
363 
364 	clutter_actor_set_position(priv->tooltip, floor(x), floor(y));
365 
366 	/* Show tooltip */
367 	clutter_actor_show(priv->tooltip);
368 }
369 
370 /* Stage got signal to hide tooltip */
_xfdashboard_stage_hide_tooltip(XfdashboardStage * self,ClutterAction * inAction)371 static void _xfdashboard_stage_hide_tooltip(XfdashboardStage *self, ClutterAction *inAction)
372 {
373 	XfdashboardStagePrivate		*priv;
374 
375 	g_return_if_fail(XFDASHBOARD_IS_STAGE(self));
376 
377 	priv=self->priv;
378 
379 	/* Hide tooltip */
380 	clutter_actor_hide(priv->tooltip);
381 }
382 
383 /* Notification timeout has been reached */
_xfdashboard_stage_on_notification_timeout_destroyed(gpointer inUserData)384 static void _xfdashboard_stage_on_notification_timeout_destroyed(gpointer inUserData)
385 {
386 	XfdashboardStage			*self;
387 	XfdashboardStagePrivate		*priv;
388 
389 	g_return_if_fail(XFDASHBOARD_IS_STAGE(inUserData));
390 
391 	self=XFDASHBOARD_STAGE(inUserData);
392 	priv=self->priv;
393 
394 	/* Timeout source was destroy so just reset ID to 0 */
395 	priv->notificationTimeoutID=0;
396 }
397 
_xfdashboard_stage_on_notification_timeout(gpointer inUserData)398 static gboolean _xfdashboard_stage_on_notification_timeout(gpointer inUserData)
399 {
400 	XfdashboardStage			*self;
401 	XfdashboardStagePrivate		*priv;
402 
403 	g_return_val_if_fail(XFDASHBOARD_IS_STAGE(inUserData), G_SOURCE_REMOVE);
404 
405 	self=XFDASHBOARD_STAGE(inUserData);
406 	priv=self->priv;
407 
408 	/* Timeout reached so hide notification */
409 	clutter_actor_hide(priv->notification);
410 
411 	/* Tell main context to remove this source */
412 	return(G_SOURCE_REMOVE);
413 }
414 
415 /* App-button was toggled */
_xfdashboard_stage_on_quicklaunch_apps_button_toggled(XfdashboardStage * self,gpointer inUserData)416 static void _xfdashboard_stage_on_quicklaunch_apps_button_toggled(XfdashboardStage *self, gpointer inUserData)
417 {
418 	XfdashboardStagePrivate		*priv;
419 	XfdashboardToggleButton		*appsButton;
420 	gboolean					state;
421 	XfdashboardView				*view;
422 
423 	g_return_if_fail(XFDASHBOARD_IS_STAGE(self));
424 	g_return_if_fail(XFDASHBOARD_IS_TOGGLE_BUTTON(inUserData));
425 
426 	priv=self->priv;
427 	appsButton=XFDASHBOARD_TOGGLE_BUTTON(inUserData);
428 
429 	/* Get state of apps button */
430 	state=xfdashboard_toggle_button_get_toggle_state(appsButton);
431 
432 	/* Depending on state activate views */
433 	if(state==FALSE)
434 	{
435 		/* Find "windows-view" view and activate */
436 		view=xfdashboard_viewpad_find_view_by_type(XFDASHBOARD_VIEWPAD(priv->viewpad), XFDASHBOARD_TYPE_WINDOWS_VIEW);
437 		if(view) xfdashboard_viewpad_set_active_view(XFDASHBOARD_VIEWPAD(priv->viewpad), view);
438 	}
439 		else
440 		{
441 			/* Find "applications" or "search" view and activate */
442 			if(!priv->searchActive) view=xfdashboard_viewpad_find_view_by_type(XFDASHBOARD_VIEWPAD(priv->viewpad), XFDASHBOARD_TYPE_APPLICATIONS_VIEW);
443 				else view=xfdashboard_viewpad_find_view_by_type(XFDASHBOARD_VIEWPAD(priv->viewpad), XFDASHBOARD_TYPE_SEARCH_VIEW);
444 			if(view) xfdashboard_viewpad_set_active_view(XFDASHBOARD_VIEWPAD(priv->viewpad), view);
445 		}
446 }
447 
448 /* Text in search text-box has changed */
_xfdashboard_stage_on_searchbox_text_changed(XfdashboardStage * self,gchar * inText,gpointer inUserData)449 static void _xfdashboard_stage_on_searchbox_text_changed(XfdashboardStage *self,
450 															gchar *inText,
451 															gpointer inUserData)
452 {
453 	XfdashboardStagePrivate		*priv;
454 	XfdashboardTextBox			*textBox=XFDASHBOARD_TEXT_BOX(inUserData);
455 	XfdashboardView				*searchView;
456 	gint						textLength;
457 	const gchar					*text;
458 	XfdashboardToggleButton		*appsButton;
459 
460 	g_return_if_fail(XFDASHBOARD_IS_STAGE(self));
461 	g_return_if_fail(XFDASHBOARD_IS_TEXT_BOX(inUserData));
462 
463 	priv=self->priv;
464 
465 	/* Get search view */
466 	searchView=xfdashboard_viewpad_find_view_by_type(XFDASHBOARD_VIEWPAD(priv->viewpad), XFDASHBOARD_TYPE_SEARCH_VIEW);
467 	if(searchView==NULL)
468 	{
469 		g_critical("Cannot perform search because search view was not found in viewpad.");
470 		return;
471 	}
472 
473 	/* Get text and length of text in text-box */
474 	text=xfdashboard_text_box_get_text(textBox);
475 	textLength=xfdashboard_text_box_get_length(textBox);
476 
477 	/* Get apps button of quicklaunch */
478 	appsButton=xfdashboard_quicklaunch_get_apps_button(XFDASHBOARD_QUICKLAUNCH(priv->quicklaunch));
479 
480 	/* Check if current text length if greater than zero and previous text length
481 	 * was zero. If check is successful it marks the start of a search. Emit the
482 	 * "search-started" signal. There is no need to start a search a search over
483 	 * all search providers as it will be done later by updating search criteria.
484 	 * There is also no need to activate search view because we will ensure that
485 	 * search view is activate on any change in search text box but we enable that
486 	 * view to be able to activate it ;)
487 	 */
488 	if(textLength>0 && priv->lastSearchTextLength==0)
489 	{
490 		/* Remember current active view to restore it when search ended */
491 		priv->viewBeforeSearch=XFDASHBOARD_VIEW(g_object_ref(xfdashboard_viewpad_get_active_view(XFDASHBOARD_VIEWPAD(priv->viewpad))));
492 
493 		/* Enable search view and set focus to viewpad which will show the
494 		 * search view so this search view will get the focus finally
495 		 */
496 		xfdashboard_view_set_enabled(searchView, TRUE);
497 		if(priv->viewpad && priv->focusManager)
498 		{
499 			xfdashboard_focus_manager_set_focus(priv->focusManager, XFDASHBOARD_FOCUSABLE(priv->viewpad));
500 		}
501 
502 		/* Activate "clear" button on text box */
503 		xfdashboard_stylable_add_class(XFDASHBOARD_STYLABLE(priv->searchbox), "search-active");
504 
505 		/* Change apps button appearance */
506 		if(appsButton) xfdashboard_stylable_add_class(XFDASHBOARD_STYLABLE(appsButton), "search-active");
507 
508 		/* Emit "search-started" signal */
509 		g_signal_emit(self, XfdashboardStageSignals[SIGNAL_SEARCH_STARTED], 0);
510 		priv->searchActive=TRUE;
511 	}
512 
513 	/* Ensure that search view is active, emit signal for text changed,
514 	 * update search criteria and set active toggle state at apps button
515 	 */
516 	xfdashboard_viewpad_set_active_view(XFDASHBOARD_VIEWPAD(priv->viewpad), searchView);
517 	xfdashboard_search_view_update_search(XFDASHBOARD_SEARCH_VIEW(searchView), text);
518 	g_signal_emit(self, XfdashboardStageSignals[SIGNAL_SEARCH_CHANGED], 0, text);
519 
520 	if(appsButton) xfdashboard_toggle_button_set_toggle_state(appsButton, TRUE);
521 
522 	/* Check if current text length is zero and previous text length was greater
523 	 * than zero. If check is successful it marks the end of current search. Emit
524 	 * the "search-ended" signal, reactivate view before search was started and
525 	 * disable search view.
526 	 */
527 	if(textLength==0 && priv->lastSearchTextLength>0)
528 	{
529 		/* Reactivate active view before search has started */
530 		if(priv->viewBeforeSearch)
531 		{
532 			xfdashboard_viewpad_set_active_view(XFDASHBOARD_VIEWPAD(priv->viewpad), priv->viewBeforeSearch);
533 			g_object_unref(priv->viewBeforeSearch);
534 			priv->viewBeforeSearch=NULL;
535 		}
536 
537 		/* Deactivate "clear" button on text box */
538 		xfdashboard_stylable_remove_class(XFDASHBOARD_STYLABLE(priv->searchbox), "search-active");
539 
540 		/* Disable search view */
541 		xfdashboard_view_set_enabled(searchView, FALSE);
542 
543 		/* Change apps button appearance */
544 		if(appsButton) xfdashboard_stylable_remove_class(XFDASHBOARD_STYLABLE(appsButton), "search-active");
545 
546 		/* Emit "search-ended" signal */
547 		g_signal_emit(self, XfdashboardStageSignals[SIGNAL_SEARCH_ENDED], 0);
548 		priv->searchActive=FALSE;
549 	}
550 
551 	/* Trace text length changes */
552 	priv->lastSearchTextLength=textLength;
553 }
554 
555 /* Secondary icon ("clear") on text box was clicked */
_xfdashboard_stage_on_searchbox_secondary_icon_clicked(XfdashboardStage * self,gpointer inUserData)556 static void _xfdashboard_stage_on_searchbox_secondary_icon_clicked(XfdashboardStage *self, gpointer inUserData)
557 {
558 	XfdashboardTextBox			*textBox;
559 
560 	g_return_if_fail(XFDASHBOARD_IS_STAGE(self));
561 	g_return_if_fail(XFDASHBOARD_IS_TEXT_BOX(inUserData));
562 
563 	textBox=XFDASHBOARD_TEXT_BOX(inUserData);
564 
565 	/* Clear search text box */
566 	xfdashboard_text_box_set_text(textBox, NULL);
567 }
568 
569 /* Active view in viewpad has changed */
_xfdashboard_stage_on_view_activated(XfdashboardStage * self,XfdashboardView * inView,gpointer inUserData)570 static void _xfdashboard_stage_on_view_activated(XfdashboardStage *self, XfdashboardView *inView, gpointer inUserData)
571 {
572 	XfdashboardStagePrivate		*priv;
573 	XfdashboardViewpad			*viewpad G_GNUC_UNUSED;
574 	XfdashboardToggleButton		*appsButton;
575 
576 	g_return_if_fail(XFDASHBOARD_IS_STAGE(self));
577 	g_return_if_fail(XFDASHBOARD_IS_VIEWPAD(inUserData));
578 
579 	priv=self->priv;
580 	viewpad=XFDASHBOARD_VIEWPAD(inUserData);
581 
582 	/* If we have remembered a view "before-search" then a search is going on.
583 	 * If user switches between views while a search is going on remember the
584 	 * last one activated to restore it when search ends but do not remember
585 	 * the search view!
586 	 */
587 	if(priv->viewBeforeSearch &&
588 		G_OBJECT_TYPE(inView)!=XFDASHBOARD_TYPE_SEARCH_VIEW)
589 	{
590 		/* Release old remembered view */
591 		g_object_unref(priv->viewBeforeSearch);
592 
593 		/* Remember new active view */
594 		priv->viewBeforeSearch=XFDASHBOARD_VIEW(g_object_ref(inView));
595 	}
596 
597 	/* Toggle application button in quicklaunch */
598 	appsButton=xfdashboard_quicklaunch_get_apps_button(XFDASHBOARD_QUICKLAUNCH(priv->quicklaunch));
599 	if(appsButton)
600 	{
601 		/* Block our signal handler at stage which is called when apps button's
602 		 * state changes because it will enforce a specific view depending on its
603 		 * state which may not be the view which is going to be activated.
604 		 */
605 		g_signal_handlers_block_by_func(appsButton, _xfdashboard_stage_on_quicklaunch_apps_button_toggled, self);
606 
607 		/* Update toggle state of apps button */
608 		if(G_OBJECT_TYPE(inView)==XFDASHBOARD_TYPE_SEARCH_VIEW ||
609 			G_OBJECT_TYPE(inView)==XFDASHBOARD_TYPE_APPLICATIONS_VIEW)
610 		{
611 			xfdashboard_toggle_button_set_toggle_state(appsButton, TRUE);
612 		}
613 			else
614 			{
615 				xfdashboard_toggle_button_set_toggle_state(appsButton, FALSE);
616 			}
617 
618 		/* Unblock any handler we blocked before */
619 		g_signal_handlers_unblock_by_func(appsButton, _xfdashboard_stage_on_quicklaunch_apps_button_toggled, self);
620 	}
621 }
622 
623 /* A window was closed
624  * Check if stage window was closed then unset up window properties and reinstall
625  * signal handler to find new stage window.
626  */
_xfdashboard_stage_on_window_closed(XfdashboardStage * self,gpointer inUserData)627 static void _xfdashboard_stage_on_window_closed(XfdashboardStage *self,
628 												gpointer inUserData)
629 {
630 	XfdashboardStagePrivate				*priv;
631 	XfdashboardWindowTrackerWindow		*window;
632 
633 	g_return_if_fail(XFDASHBOARD_IS_STAGE(self));
634 	g_return_if_fail(XFDASHBOARD_IS_WINDOW_TRACKER_WINDOW(inUserData));
635 
636 	priv=self->priv;
637 	window=XFDASHBOARD_WINDOW_TRACKER_WINDOW(inUserData);
638 
639 	/* Check if window closed is this stage window */
640 	if(priv->stageWindow!=window) return;
641 
642 	/* Disconnect this signal handler as this stage window was closed*/
643 	XFDASHBOARD_DEBUG(self, ACTOR, "Stage window was closed. Removing signal handler");
644 	g_signal_handlers_disconnect_by_func(priv->stageWindow, G_CALLBACK(_xfdashboard_stage_on_window_closed), self);
645 
646 	/* Forget stage window as it was closed */
647 	priv->stageWindow=NULL;
648 
649 	/* Instead reconnect signal handler to find new stage window */
650 	XFDASHBOARD_DEBUG(self, ACTOR, "Reconnecting signal to find new stage window as this one as closed");
651 	g_signal_connect_swapped(priv->windowTracker, "window-opened", G_CALLBACK(_xfdashboard_stage_on_window_opened), self);
652 
653 	/* Set focus */
654 	_xfdashboard_stage_set_focus(self);
655 }
656 
657 
658 /* A window was created
659  * Check for stage window and set up window properties
660  */
_xfdashboard_stage_on_window_opened(XfdashboardStage * self,XfdashboardWindowTrackerWindow * inWindow,gpointer inUserData)661 static void _xfdashboard_stage_on_window_opened(XfdashboardStage *self,
662 													XfdashboardWindowTrackerWindow *inWindow,
663 													gpointer inUserData)
664 {
665 	XfdashboardStagePrivate				*priv;
666 	XfdashboardWindowTrackerWindow		*stageWindow;
667 
668 	g_return_if_fail(XFDASHBOARD_IS_STAGE(self));
669 	g_return_if_fail(XFDASHBOARD_IS_WINDOW_TRACKER_WINDOW(inWindow));
670 
671 	priv=self->priv;
672 
673 	/* Check if window opened is this stage window */
674 	stageWindow=xfdashboard_window_tracker_get_stage_window(priv->windowTracker, CLUTTER_STAGE(self));
675 	if(stageWindow!=inWindow) return;
676 
677 	/* Set up window for use as stage window */
678 	priv->stageWindow=inWindow;
679 	xfdashboard_window_tracker_window_show_stage(priv->stageWindow);
680 
681 	/* Disconnect this signal handler as this is a one-time setup of stage window */
682 	XFDASHBOARD_DEBUG(self, ACTOR, "Stage window was opened and set up. Removing signal handler");
683 	g_signal_handlers_disconnect_by_func(priv->windowTracker, G_CALLBACK(_xfdashboard_stage_on_window_opened), self);
684 
685 	/* Instead connect signal handler to get notified when this stage window was
686 	 * destroyed as we need to forget this window and to reinstall this signal
687 	 * handler again.
688 	 */
689 	XFDASHBOARD_DEBUG(self, ACTOR, "Connecting signal signal handler to get notified about destruction of stage window");
690 	g_signal_connect_swapped(priv->stageWindow,
691 								"closed",
692 								G_CALLBACK(_xfdashboard_stage_on_window_closed),
693 								self);
694 
695 	/* Set focus */
696 	_xfdashboard_stage_set_focus(self);
697 }
698 
699 /* A window was created
700  * Check if window opened is desktop background window
701  */
_xfdashboard_stage_on_desktop_window_opened(XfdashboardStage * self,XfdashboardWindowTrackerWindow * inWindow,gpointer inUserData)702 static void _xfdashboard_stage_on_desktop_window_opened(XfdashboardStage *self,
703 														XfdashboardWindowTrackerWindow *inWindow,
704 														gpointer inUserData)
705 {
706 	XfdashboardStagePrivate				*priv;
707 	XfdashboardWindowTrackerWindow		*desktopWindow;
708 	ClutterContent						*windowContent;
709 
710 	g_return_if_fail(XFDASHBOARD_IS_STAGE(self));
711 	g_return_if_fail(XFDASHBOARD_IS_WINDOW_TRACKER_WINDOW(inWindow));
712 
713 	priv=self->priv;
714 
715 	/* Get desktop background window and check if it is the new window opened */
716 	desktopWindow=xfdashboard_window_tracker_get_root_window(priv->windowTracker);
717 	if(desktopWindow)
718 	{
719 		windowContent=xfdashboard_window_tracker_window_get_content(desktopWindow);
720 		clutter_actor_set_content(priv->backgroundImageLayer, windowContent);
721 		clutter_actor_show(priv->backgroundImageLayer);
722 		g_object_unref(windowContent);
723 
724 		g_signal_handlers_disconnect_by_func(priv->windowTracker, G_CALLBACK(_xfdashboard_stage_on_desktop_window_opened), self);
725 		XFDASHBOARD_DEBUG(self, ACTOR, "Found desktop window with signal 'window-opened', so disconnecting signal handler");
726 	}
727 }
728 
729 /* The application will be suspended */
_xfdashboard_stage_on_application_suspend(XfdashboardStage * self,gpointer inUserData)730 static void _xfdashboard_stage_on_application_suspend(XfdashboardStage *self, gpointer inUserData)
731 {
732 	XfdashboardStagePrivate				*priv;
733 
734 	g_return_if_fail(XFDASHBOARD_IS_STAGE(self));
735 	g_return_if_fail(XFDASHBOARD_IS_APPLICATION(inUserData));
736 
737 	priv=self->priv;
738 
739 	/* Instead of hiding stage actor just hide stage's window. It should be safe
740 	 * to just hide the window as it should be listed on any task list and is not
741 	 * selectable by user. The advantage should be that the window is already mapped
742 	 * and its state is already set up like fullscreen, sticky and so on. This
743 	 * prevents that window will not be shown in fullscreen again (just maximized
744 	 * and flickers) if we use clutter_actor_show to show stage actor and its window
745 	 * again. But we can only do this if the window is known and set up ;)
746 	 */
747 	if(priv->stageWindow)
748 	{
749 		xfdashboard_window_tracker_window_hide_stage(priv->stageWindow);
750 	}
751 
752 	/* Hide tooltip */
753 	if(priv->tooltip) clutter_actor_hide(priv->tooltip);
754 }
755 
756 /* The application will be resumed */
_xfdashboard_stage_on_application_resume(XfdashboardStage * self,gpointer inUserData)757 static void _xfdashboard_stage_on_application_resume(XfdashboardStage *self, gpointer inUserData)
758 {
759 	XfdashboardStagePrivate				*priv;
760 
761 	g_return_if_fail(XFDASHBOARD_IS_STAGE(self));
762 	g_return_if_fail(XFDASHBOARD_IS_APPLICATION(inUserData));
763 
764 	priv=self->priv;
765 
766 	/* If stage window is known just show it again ... */
767 	if(priv->stageWindow)
768 	{
769 		gboolean						doResetSearch;
770 		XfdashboardView					*searchView;
771 		XfdashboardView					*resumeView;
772 
773 		/* Get configured options */
774 		doResetSearch=xfconf_channel_get_bool(xfdashboard_application_get_xfconf_channel(NULL),
775 												RESET_SEARCH_ON_RESUME_XFCONF_PROP,
776 												DEFAULT_RESET_SEARCH_ON_RESUME);
777 
778 		/* Find search view */
779 		searchView=xfdashboard_viewpad_find_view_by_type(XFDASHBOARD_VIEWPAD(priv->viewpad), XFDASHBOARD_TYPE_SEARCH_VIEW);
780 		if(!searchView) g_critical("Cannot find search view in viewpad to reset view.");
781 
782 		/* Find view to switch to if requested */
783 		resumeView=_xfdashboard_stage_get_view_to_switch_to(self);
784 
785 		/* If view to switch to is the search view behave like we did not find the view
786 		 * because it does not make sense to switch to a view which might be hidden,
787 		 * e.g. when resetting search on resume which causes the search view to be hidden
788 		 * and the previous view to be shown.
789 		 */
790 		if(resumeView &&
791 			searchView &&
792 			resumeView==searchView)
793 		{
794 			resumeView=NULL;
795 		}
796 
797 		/* If search is active then end search by clearing search box if requested ... */
798 		if(priv->searchbox &&
799 			doResetSearch &&
800 			!xfdashboard_text_box_is_empty(XFDASHBOARD_TEXT_BOX(priv->searchbox)))
801 		{
802 			/* If user wants to switch to a specific view set it as "previous" view now.
803 			 * It will be restored automatically when search box is cleared.
804 			 */
805 			if(resumeView)
806 			{
807 				/* Release old remembered view */
808 				if(priv->viewBeforeSearch) g_object_unref(priv->viewBeforeSearch);
809 
810 				/* Remember new active view */
811 				priv->viewBeforeSearch=XFDASHBOARD_VIEW(g_object_ref(resumeView));
812 			}
813 
814 			/* Reset search in search view */
815 			if(searchView) xfdashboard_search_view_reset_search(XFDASHBOARD_SEARCH_VIEW(searchView));
816 
817 			/* Reset text in search box */
818 			xfdashboard_text_box_set_text(XFDASHBOARD_TEXT_BOX(priv->searchbox), NULL);
819 		}
820 			/* ... otherwise just switch to view if requested */
821 			else if(resumeView)
822 			{
823 				xfdashboard_viewpad_set_active_view(XFDASHBOARD_VIEWPAD(priv->viewpad), resumeView);
824 			}
825 
826 		/* Now move focus to actor if user requested to refocus preselected actor
827 		 * as specified by theme.
828 		 */
829 		if(priv->focusActorOnShow)
830 		{
831 			gboolean				reselectFocusOnResume;
832 
833 			/* Determine if user (also) requests to reselect focus on resume */
834 			reselectFocusOnResume=xfconf_channel_get_bool(xfdashboard_application_get_xfconf_channel(NULL),
835 															RESELECT_THEME_FOCUS_ON_RESUME_XFCONF_PROP,
836 															DEFAULT_RESELECT_THEME_FOCUS_ON_RESUME);
837 			if(reselectFocusOnResume)
838 			{
839 				/* Move focus to actor */
840 				xfdashboard_focus_manager_set_focus(priv->focusManager, XFDASHBOARD_FOCUSABLE(priv->focusActorOnShow));
841 
842 				XFDASHBOARD_DEBUG(self, ACTOR,
843 									"Moved focus to actor %s because it should be reselected on resume",
844 									G_OBJECT_TYPE_NAME(priv->focusActorOnShow));
845 			}
846 				else
847 				{
848 					/* Forget actor to focus now the user did not requested to reselect
849 					 * this focus again and again when stage window is shown ;)
850 					 */
851 					g_object_remove_weak_pointer(G_OBJECT(priv->focusActorOnShow), &priv->focusActorOnShow);
852 					priv->focusActorOnShow=NULL;
853 				}
854 		}
855 
856 		/* Set up stage and show it */
857 		xfdashboard_window_tracker_window_show_stage(priv->stageWindow);
858 	}
859 		/* ... otherwise set it up by calling clutter_actor_show() etc. */
860 		else
861 		{
862 			/* Show stage and force window creation. It will also handle
863 			 * the switch to a specific view.
864 			 */
865 			clutter_actor_show(CLUTTER_ACTOR(self));
866 		}
867 
868 	/* In any case force a redraw */
869 	clutter_actor_queue_redraw(CLUTTER_ACTOR(self));
870 }
871 
872 /* Theme in application has changed */
_xfdashboard_stage_theme_interface_data_free(XfdashboardStageThemeInterfaceData * inData)873 static void _xfdashboard_stage_theme_interface_data_free(XfdashboardStageThemeInterfaceData *inData)
874 {
875 	g_return_if_fail(inData);
876 
877 	/* Release each data in data structure but do not unref the interface actor
878 	 * as it might be used at stage. The stage is responsible to destroy the
879 	 * interface actor in *any* case.
880 	 */
881 	if(inData->focusables) g_ptr_array_unref(inData->focusables);
882 	if(inData->focus) g_object_unref(inData->focus);
883 
884 	/* Release allocated memory */
885 	g_free(inData);
886 }
887 
_xfdashboard_stage_theme_interface_data_new(void)888 static XfdashboardStageThemeInterfaceData* _xfdashboard_stage_theme_interface_data_new(void)
889 {
890 	XfdashboardStageThemeInterfaceData   *data;
891 
892 	/* Allocate memory for data structure */
893 	data=g_new0(XfdashboardStageThemeInterfaceData, 1);
894 	if(!data) return(NULL);
895 
896 	/* Return newly create and initialized data structure */
897 	return(data);
898 }
899 
_xfdashboard_stage_on_application_theme_changed(XfdashboardStage * self,XfdashboardTheme * inTheme,gpointer inUserData)900 static void _xfdashboard_stage_on_application_theme_changed(XfdashboardStage *self,
901 															XfdashboardTheme *inTheme,
902 															gpointer inUserData)
903 {
904 	XfdashboardStagePrivate				*priv;
905 	XfdashboardThemeLayout				*themeLayout;
906 	GList								*interfaces;
907 	XfdashboardStageThemeInterfaceData	*interface;
908 	GList								*iter;
909 	GList								*monitors;
910 	XfdashboardWindowTrackerMonitor		*monitor;
911 	ClutterActorIter					childIter;
912 	ClutterActor						*child;
913 	GObject								*focusObject;
914 	guint								i;
915 	gboolean							reselectFocusOnResume;
916 
917 	g_return_if_fail(XFDASHBOARD_IS_STAGE(self));
918 	g_return_if_fail(XFDASHBOARD_IS_THEME(inTheme));
919 	g_return_if_fail(XFDASHBOARD_IS_APPLICATION(inUserData));
920 
921 	priv=self->priv;
922 
923 	/* Get theme layout */
924 	themeLayout=xfdashboard_theme_get_layout(inTheme);
925 
926 	/* Create interface for each monitor if multiple monitors are supported */
927 	interfaces=NULL;
928 	if(xfdashboard_window_tracker_supports_multiple_monitors(priv->windowTracker))
929 	{
930 		monitors=xfdashboard_window_tracker_get_monitors(priv->windowTracker);
931 		for(iter=monitors; iter; iter=g_list_next(iter))
932 		{
933 			/* Get monitor */
934 			monitor=XFDASHBOARD_WINDOW_TRACKER_MONITOR(iter->data);
935 
936 			/* Get interface  */
937 			if(xfdashboard_window_tracker_monitor_is_primary(monitor))
938 			{
939 				/* Get interface for primary monitor */
940 				interface=_xfdashboard_stage_theme_interface_data_new();
941 				interface->actor=xfdashboard_theme_layout_build_interface(themeLayout,
942 																			XFDASHBOARD_THEME_LAYOUT_PRIMARY,
943 																			XFDASHBOARD_THEME_LAYOUT_BUILD_GET_FOCUSABLES, &interface->focusables,
944 																			XFDASHBOARD_THEME_LAYOUT_BUILD_GET_SELECTED_FOCUS, &interface->focus,
945 																			-1);
946 				if(!interface->actor)
947 				{
948 					g_critical("Could not build interface '%s' from theme '%s'",
949 								XFDASHBOARD_THEME_LAYOUT_PRIMARY,
950 								xfdashboard_theme_get_theme_name(inTheme));
951 
952 					/* Release allocated resources */
953 					_xfdashboard_stage_theme_interface_data_free(interface);
954 					g_list_free_full(interfaces, (GDestroyNotify)_xfdashboard_stage_theme_interface_data_free);
955 
956 					return;
957 				}
958 
959 				if(!XFDASHBOARD_IS_STAGE_INTERFACE(interface->actor))
960 				{
961 					g_critical("Interface '%s' from theme '%s' must be an actor of type %s",
962 								XFDASHBOARD_THEME_LAYOUT_PRIMARY,
963 								xfdashboard_theme_get_theme_name(inTheme),
964 								g_type_name(XFDASHBOARD_TYPE_STAGE_INTERFACE));
965 
966 					/* Release allocated resources */
967 					_xfdashboard_stage_theme_interface_data_free(interface);
968 					g_list_free_full(interfaces, (GDestroyNotify)_xfdashboard_stage_theme_interface_data_free);
969 
970 					return;
971 				}
972 			}
973 				else
974 				{
975 					/* Get interface for non-primary monitors. If no interface
976 					 * is defined in theme then create an empty interface.
977 					 */
978 					interface=_xfdashboard_stage_theme_interface_data_new();
979 					interface->actor=xfdashboard_theme_layout_build_interface(themeLayout,
980 																				XFDASHBOARD_THEME_LAYOUT_SECONDARY,
981 																				XFDASHBOARD_THEME_LAYOUT_BUILD_GET_FOCUSABLES, &interface->focusables,
982 																				XFDASHBOARD_THEME_LAYOUT_BUILD_GET_SELECTED_FOCUS, &interface->focus,
983 																				-1);
984 					if(!interface->actor)
985 					{
986 						interface->actor=xfdashboard_stage_interface_new();
987 					}
988 
989 					if(!XFDASHBOARD_IS_STAGE_INTERFACE(interface->actor))
990 					{
991 						g_critical("Interface '%s' from theme '%s' must be an actor of type %s",
992 									XFDASHBOARD_THEME_LAYOUT_SECONDARY,
993 									xfdashboard_theme_get_theme_name(inTheme),
994 									g_type_name(XFDASHBOARD_TYPE_STAGE_INTERFACE));
995 
996 						/* Release allocated resources */
997 						_xfdashboard_stage_theme_interface_data_free(interface);
998 						g_list_free_full(interfaces, (GDestroyNotify)_xfdashboard_stage_theme_interface_data_free);
999 
1000 						return;
1001 					}
1002 				}
1003 
1004 			/* Set monitor at interface */
1005 			xfdashboard_stage_interface_set_monitor(XFDASHBOARD_STAGE_INTERFACE(interface->actor), monitor);
1006 
1007 			/* Add interface to list of interfaces */
1008 			interfaces=g_list_prepend(interfaces, interface);
1009 		}
1010 	}
1011 		/* Otherwise create only a primary stage interface and set no monitor
1012 		 * because no one is available if multiple monitors are not supported.
1013 		 */
1014 		else
1015 		{
1016 			/* Get interface for primary monitor */
1017 			interface=_xfdashboard_stage_theme_interface_data_new();
1018 			interface->actor=xfdashboard_theme_layout_build_interface(themeLayout,
1019 																		XFDASHBOARD_THEME_LAYOUT_PRIMARY,
1020 																		XFDASHBOARD_THEME_LAYOUT_BUILD_GET_FOCUSABLES, &interface->focusables,
1021 																		XFDASHBOARD_THEME_LAYOUT_BUILD_GET_SELECTED_FOCUS, &interface->focus,
1022 																		-1);
1023 			if(!interface->actor)
1024 			{
1025 				g_critical("Could not build interface '%s' from theme '%s'",
1026 							XFDASHBOARD_THEME_LAYOUT_PRIMARY,
1027 							xfdashboard_theme_get_theme_name(inTheme));
1028 
1029 				/* Release allocated resources */
1030 				_xfdashboard_stage_theme_interface_data_free(interface);
1031 				g_list_free_full(interfaces, (GDestroyNotify)_xfdashboard_stage_theme_interface_data_free);
1032 
1033 				return;
1034 			}
1035 
1036 			if(!XFDASHBOARD_IS_STAGE_INTERFACE(interface->actor))
1037 			{
1038 				g_critical("Interface '%s' from theme '%s' must be an actor of type %s",
1039 							XFDASHBOARD_THEME_LAYOUT_PRIMARY,
1040 							xfdashboard_theme_get_theme_name(inTheme),
1041 							g_type_name(XFDASHBOARD_TYPE_STAGE_INTERFACE));
1042 
1043 				/* Release allocated resources */
1044 				_xfdashboard_stage_theme_interface_data_free(interface);
1045 				g_list_free_full(interfaces, (GDestroyNotify)_xfdashboard_stage_theme_interface_data_free);
1046 
1047 				return;
1048 			}
1049 
1050 			/* Add interface to list of interfaces */
1051 			interfaces=g_list_prepend(interfaces, interface);
1052 
1053 			XFDASHBOARD_DEBUG(self, ACTOR, "Creating primary interface only because of no support for multiple monitors");
1054 		}
1055 
1056 	/* Destroy all interfaces from stage.
1057 	 * There is no need to reset pointer variables to quicklaunch, searchbox etc.
1058 	 * because they should be set NULL to by calling _xfdashboard_stage_reset_reference_on_destroy
1059 	 * when stage actor was destroyed.
1060 	 */
1061 	clutter_actor_iter_init(&childIter, CLUTTER_ACTOR(self));
1062 	while(clutter_actor_iter_next(&childIter, &child))
1063 	{
1064 		if(XFDASHBOARD_IS_STAGE_INTERFACE(child))
1065 		{
1066 			clutter_actor_iter_destroy(&childIter);
1067 		}
1068 	}
1069 
1070 	/* Add all new interfaces to stage */
1071 	for(iter=interfaces; iter; iter=g_list_next(iter))
1072 	{
1073 		/* Get interface to add to stage */
1074 		interface=(XfdashboardStageThemeInterfaceData*)(iter->data);
1075 		if(!interface) continue;
1076 
1077 		/* Check for interface actor to add to stage */
1078 		if(!interface->actor) continue;
1079 
1080 		/* Add interface to stage */
1081 		clutter_actor_add_child(CLUTTER_ACTOR(self), interface->actor);
1082 
1083 		/* Only check children, set up pointer variables to quicklaunch, searchbox etc.
1084 		 * and connect signals for primary monitor.
1085 		 */
1086 		monitor=xfdashboard_stage_interface_get_monitor(XFDASHBOARD_STAGE_INTERFACE(interface->actor));
1087 		if(!monitor || xfdashboard_window_tracker_monitor_is_primary(monitor))
1088 		{
1089 			/* Remember primary interface */
1090 			if(!priv->primaryInterface)
1091 			{
1092 				priv->primaryInterface=interface->actor;
1093 				g_object_add_weak_pointer(G_OBJECT(priv->primaryInterface), &priv->primaryInterface);
1094 			}
1095 				else g_critical("Invalid multiple stages for primary monitor");
1096 
1097 			/* Get children from built stage and connect signals */
1098 			priv->viewSelector=NULL;
1099 			child=xfdashboard_find_actor_by_name(CLUTTER_ACTOR(self), "view-selector");
1100 			if(child && XFDASHBOARD_IS_VIEW_SELECTOR(child))
1101 			{
1102 				priv->viewSelector=child;
1103 				g_object_add_weak_pointer(G_OBJECT(priv->viewSelector), &priv->viewSelector);
1104 
1105 				/* Register this focusable actor if it is focusable */
1106 				if(!interface->focusables && XFDASHBOARD_IS_FOCUSABLE(priv->viewSelector))
1107 				{
1108 					xfdashboard_focus_manager_register(priv->focusManager,
1109 														XFDASHBOARD_FOCUSABLE(priv->viewSelector));
1110 				}
1111 			}
1112 
1113 			priv->searchbox=NULL;
1114 			child=xfdashboard_find_actor_by_name(CLUTTER_ACTOR(self), "searchbox");
1115 			if(child && XFDASHBOARD_IS_TEXT_BOX(child))
1116 			{
1117 				priv->searchbox=child;
1118 				g_object_add_weak_pointer(G_OBJECT(priv->searchbox), &priv->searchbox);
1119 
1120 				/* If no hint-text was defined, set default one */
1121 				if(!xfdashboard_text_box_is_hint_text_set(XFDASHBOARD_TEXT_BOX(priv->searchbox)))
1122 				{
1123 					xfdashboard_text_box_set_hint_text(XFDASHBOARD_TEXT_BOX(priv->searchbox),
1124 														_("Just type to search..."));
1125 				}
1126 
1127 				/* Connect signals */
1128 				g_signal_connect_swapped(priv->searchbox,
1129 											"text-changed",
1130 											G_CALLBACK(_xfdashboard_stage_on_searchbox_text_changed),
1131 											self);
1132 				g_signal_connect_swapped(priv->searchbox,
1133 											"secondary-icon-clicked",
1134 											G_CALLBACK(_xfdashboard_stage_on_searchbox_secondary_icon_clicked),
1135 											self);
1136 
1137 				/* Register this focusable actor if it is focusable */
1138 				if(!interface->focusables && XFDASHBOARD_IS_FOCUSABLE(priv->searchbox))
1139 				{
1140 					xfdashboard_focus_manager_register(priv->focusManager,
1141 														XFDASHBOARD_FOCUSABLE(priv->searchbox));
1142 				}
1143 			}
1144 
1145 			priv->viewpad=NULL;
1146 			child=xfdashboard_find_actor_by_name(CLUTTER_ACTOR(self), "viewpad");
1147 			if(child && XFDASHBOARD_IS_VIEWPAD(child))
1148 			{
1149 				priv->viewpad=child;
1150 				g_object_add_weak_pointer(G_OBJECT(priv->viewpad), &priv->viewpad);
1151 
1152 				/* Connect signals */
1153 				g_signal_connect_swapped(priv->viewpad, "view-activated", G_CALLBACK(_xfdashboard_stage_on_view_activated), self);
1154 
1155 				/* Register this focusable actor if it is focusable */
1156 				if(!interface->focusables && XFDASHBOARD_IS_FOCUSABLE(priv->viewpad))
1157 				{
1158 					xfdashboard_focus_manager_register(priv->focusManager,
1159 														XFDASHBOARD_FOCUSABLE(priv->viewpad));
1160 
1161 					/* Check if viewpad can be focused to enforce all focusable views
1162 					 * will be registered too. We need to do it now to get all focusable
1163 					 * views registered before first use of any function of focus manager.
1164 					 */
1165 					xfdashboard_focusable_can_focus(XFDASHBOARD_FOCUSABLE(priv->viewpad));
1166 				}
1167 			}
1168 
1169 			priv->quicklaunch=NULL;
1170 			child=xfdashboard_find_actor_by_name(CLUTTER_ACTOR(self), "quicklaunch");
1171 			if(child && XFDASHBOARD_IS_QUICKLAUNCH(child))
1172 			{
1173 				XfdashboardToggleButton		*appsButton;
1174 
1175 				priv->quicklaunch=child;
1176 				g_object_add_weak_pointer(G_OBJECT(priv->quicklaunch), &priv->quicklaunch);
1177 
1178 				/* Connect signals */
1179 				appsButton=xfdashboard_quicklaunch_get_apps_button(XFDASHBOARD_QUICKLAUNCH(priv->quicklaunch));
1180 				if(appsButton)
1181 				{
1182 					g_signal_connect_swapped(appsButton,
1183 												"toggled",
1184 												G_CALLBACK(_xfdashboard_stage_on_quicklaunch_apps_button_toggled),
1185 												self);
1186 				}
1187 
1188 				/* Register this focusable actor if it is focusable */
1189 				if(!interface->focusables && XFDASHBOARD_IS_FOCUSABLE(priv->quicklaunch))
1190 				{
1191 					xfdashboard_focus_manager_register(priv->focusManager,
1192 														XFDASHBOARD_FOCUSABLE(priv->quicklaunch));
1193 				}
1194 			}
1195 
1196 			priv->workspaces=NULL;
1197 			child=xfdashboard_find_actor_by_name(CLUTTER_ACTOR(self), "workspace-selector");
1198 			if(child && XFDASHBOARD_IS_WORKSPACE_SELECTOR(child))
1199 			{
1200 				priv->workspaces=child;
1201 				g_object_add_weak_pointer(G_OBJECT(priv->workspaces), &priv->workspaces);
1202 
1203 				/* Register this focusable actor if it is focusable */
1204 				if(!interface->focusables && XFDASHBOARD_IS_FOCUSABLE(priv->workspaces))
1205 				{
1206 					xfdashboard_focus_manager_register(priv->focusManager,
1207 														XFDASHBOARD_FOCUSABLE(priv->workspaces));
1208 				}
1209 			}
1210 
1211 			priv->notification=NULL;
1212 			child=xfdashboard_find_actor_by_name(CLUTTER_ACTOR(self), "notification");
1213 			if(child && XFDASHBOARD_IS_TEXT_BOX(child))
1214 			{
1215 				priv->notification=child;
1216 				g_object_add_weak_pointer(G_OBJECT(priv->notification), &priv->notification);
1217 
1218 				/* Register this focusable actor if it is focusable */
1219 				if(!interface->focusables && XFDASHBOARD_IS_FOCUSABLE(priv->notification))
1220 				{
1221 					xfdashboard_focus_manager_register(priv->focusManager,
1222 														XFDASHBOARD_FOCUSABLE(priv->notification));
1223 				}
1224 
1225 				/* Hide notification by default */
1226 				clutter_actor_hide(priv->notification);
1227 				clutter_actor_set_reactive(priv->notification, FALSE);
1228 			}
1229 
1230 			priv->tooltip=NULL;
1231 			child=xfdashboard_find_actor_by_name(CLUTTER_ACTOR(self), "tooltip");
1232 			if(child && XFDASHBOARD_IS_TEXT_BOX(child))
1233 			{
1234 				priv->tooltip=child;
1235 				g_object_add_weak_pointer(G_OBJECT(priv->tooltip), &priv->tooltip);
1236 
1237 				/* Register this focusable actor if it is focusable */
1238 				if(!interface->focusables && XFDASHBOARD_IS_FOCUSABLE(priv->tooltip))
1239 				{
1240 					xfdashboard_focus_manager_register(priv->focusManager,
1241 														XFDASHBOARD_FOCUSABLE(priv->tooltip));
1242 				}
1243 
1244 				/* Hide tooltip by default */
1245 				clutter_actor_hide(priv->tooltip);
1246 				clutter_actor_set_reactive(priv->tooltip, FALSE);
1247 			}
1248 
1249 			/* Register focusable actors at focus manager */
1250 			if(interface->focusables)
1251 			{
1252 				for(i=0; i<interface->focusables->len; i++)
1253 				{
1254 					/* Get actor to register at focus manager */
1255 					focusObject=G_OBJECT(g_ptr_array_index(interface->focusables, i));
1256 					if(!focusObject) continue;
1257 
1258 					/* Check that actor is focusable */
1259 					if(!XFDASHBOARD_IS_FOCUSABLE(focusObject))
1260 					{
1261 						g_warning("Object %s is not focusable and cannot be registered.",
1262 									G_OBJECT_TYPE_NAME(focusObject));
1263 						continue;
1264 					}
1265 
1266 					/* Register actor at focus manager */
1267 					xfdashboard_focus_manager_register(priv->focusManager,
1268 														XFDASHBOARD_FOCUSABLE(focusObject));
1269 					XFDASHBOARD_DEBUG(self, ACTOR,
1270 										"Registering actor %s of interface with ID '%s' at focus manager",
1271 										G_OBJECT_TYPE_NAME(focusObject),
1272 										clutter_actor_get_name(interface->actor));
1273 				}
1274 			}
1275 
1276 			/* Move focus to selected actor or remember actor focus to set it later
1277 			 * but only if selected actor is a focusable actor and is registered
1278 			 * to focus manager.
1279 			 */
1280 			if(interface->focus &&
1281 				XFDASHBOARD_IS_FOCUSABLE(interface->focus) &&
1282 				xfdashboard_focus_manager_is_registered(priv->focusManager, XFDASHBOARD_FOCUSABLE(interface->focus)))
1283 			{
1284 				/* If actor can be focused then move focus to actor ... */
1285 				if(xfdashboard_focusable_can_focus(XFDASHBOARD_FOCUSABLE(interface->focus)))
1286 				{
1287 					xfdashboard_focus_manager_set_focus(priv->focusManager, XFDASHBOARD_FOCUSABLE(interface->focus));
1288 					XFDASHBOARD_DEBUG(self, ACTOR,
1289 										"Moved focus to actor %s of interface with ID '%s'",
1290 										G_OBJECT_TYPE_NAME(interface->focus),
1291 										clutter_actor_get_name(interface->actor));
1292 
1293 					/* Determine if user (also) requests to reselect focus on resume
1294 					 * because then remember the actor to focus to move the focus
1295 					 * each time the stage window gets shown after it was hidden.
1296 					 */
1297 					reselectFocusOnResume=xfconf_channel_get_bool(xfdashboard_application_get_xfconf_channel(NULL),
1298 																	RESELECT_THEME_FOCUS_ON_RESUME_XFCONF_PROP,
1299 																	DEFAULT_RESELECT_THEME_FOCUS_ON_RESUME);
1300 					if(reselectFocusOnResume)
1301 					{
1302 						priv->focusActorOnShow=XFDASHBOARD_FOCUSABLE(interface->focus);
1303 						g_object_add_weak_pointer(G_OBJECT(priv->focusActorOnShow), &priv->focusActorOnShow);
1304 
1305 						XFDASHBOARD_DEBUG(self, ACTOR,
1306 											"Will move focus to actor %s of interface with ID '%s' any time the stage gets visible",
1307 											G_OBJECT_TYPE_NAME(interface->focus),
1308 											clutter_actor_get_name(interface->actor));
1309 					}
1310 				}
1311 					/* ... otherwise if stage is not visible, remember the actor
1312 					 * to focus to move the focus to it as soon as stage is
1313 					 * visible ...
1314 					 */
1315 					else if(!clutter_actor_is_visible(CLUTTER_ACTOR(self)))
1316 					{
1317 						priv->focusActorOnShow=XFDASHBOARD_FOCUSABLE(interface->focus);
1318 						g_object_add_weak_pointer(G_OBJECT(priv->focusActorOnShow), &priv->focusActorOnShow);
1319 
1320 						XFDASHBOARD_DEBUG(self, ACTOR,
1321 											"Cannot move focus to actor %s of interface with ID '%s' but will try again when stage is visible",
1322 											G_OBJECT_TYPE_NAME(interface->focus),
1323 											clutter_actor_get_name(interface->actor));
1324 					}
1325 					/* ... otherwise just show a debug message */
1326 					else
1327 					{
1328 						XFDASHBOARD_DEBUG(self, ACTOR,
1329 											"Cannot move focus to actor %s of interface with ID '%s' because actor cannot be focused",
1330 											G_OBJECT_TYPE_NAME(interface->focus),
1331 											clutter_actor_get_name(interface->actor));
1332 					}
1333 			}
1334 				else
1335 				{
1336 					XFDASHBOARD_DEBUG(self, ACTOR, "Cannot move focus to any actor because no one was selected in theme");
1337 				}
1338 		}
1339 	}
1340 
1341 	/* Release allocated resources */
1342 	g_list_free_full(interfaces, (GDestroyNotify)_xfdashboard_stage_theme_interface_data_free);
1343 
1344 	/* Set focus */
1345 	_xfdashboard_stage_set_focus(self);
1346 }
1347 
1348 /* Primary monitor changed */
_xfdashboard_stage_on_primary_monitor_changed(XfdashboardStage * self,XfdashboardWindowTrackerMonitor * inOldMonitor,XfdashboardWindowTrackerMonitor * inNewMonitor,gpointer inUserData)1349 static void _xfdashboard_stage_on_primary_monitor_changed(XfdashboardStage *self,
1350 															XfdashboardWindowTrackerMonitor *inOldMonitor,
1351 															XfdashboardWindowTrackerMonitor *inNewMonitor,
1352 															gpointer inUserData)
1353 {
1354 	XfdashboardStagePrivate					*priv;
1355 	XfdashboardStageInterface				*oldStageInterface;
1356 	XfdashboardWindowTrackerMonitor			*oldPrimaryStageInterfaceMonitor;
1357 	ClutterActorIter						childIter;
1358 	ClutterActor							*child;
1359 
1360 	g_return_if_fail(XFDASHBOARD_IS_STAGE(self));
1361 	g_return_if_fail(!inOldMonitor || XFDASHBOARD_IS_WINDOW_TRACKER_MONITOR(inOldMonitor));
1362 	g_return_if_fail(XFDASHBOARD_IS_WINDOW_TRACKER_MONITOR(inNewMonitor));
1363 	g_return_if_fail(XFDASHBOARD_IS_WINDOW_TRACKER(inUserData));
1364 
1365 	priv=self->priv;
1366 
1367 	/* If we do not have a primary stage interface yet do nothing */
1368 	if(!priv->primaryInterface) return;
1369 
1370 	/* If primary stage interface has already new monitor set do nothing */
1371 	oldPrimaryStageInterfaceMonitor=xfdashboard_stage_interface_get_monitor(XFDASHBOARD_STAGE_INTERFACE(priv->primaryInterface));
1372 	if(oldPrimaryStageInterfaceMonitor==inNewMonitor) return;
1373 
1374 	/* Find stage interface currently using the new primary monitor */
1375 	oldStageInterface=NULL;
1376 
1377 	clutter_actor_iter_init(&childIter, CLUTTER_ACTOR(self));
1378 	while(!oldStageInterface && clutter_actor_iter_next(&childIter, &child))
1379 	{
1380 		XfdashboardStageInterface			*interface;
1381 
1382 		/* Check for stage interface */
1383 		if(!XFDASHBOARD_IS_STAGE_INTERFACE(child)) continue;
1384 
1385 		/* Get stage interface */
1386 		interface=XFDASHBOARD_STAGE_INTERFACE(child);
1387 
1388 		/* Check if stage interface is using new primary monitor then remember it */
1389 		if(xfdashboard_stage_interface_get_monitor(interface)==inNewMonitor)
1390 		{
1391 			oldStageInterface=interface;
1392 		}
1393 	}
1394 
1395 	/* Set old primary monitor at stage interface which is using new primary monitor */
1396 	if(oldStageInterface)
1397 	{
1398 		/* Set old monitor at found stage interface */
1399 		xfdashboard_stage_interface_set_monitor(oldStageInterface, oldPrimaryStageInterfaceMonitor);
1400 	}
1401 
1402 	/* Set new primary monitor at primary stage interface */
1403 	xfdashboard_stage_interface_set_monitor(XFDASHBOARD_STAGE_INTERFACE(priv->primaryInterface), inNewMonitor);
1404 	XFDASHBOARD_DEBUG(self, ACTOR,
1405 						"Primary monitor changed from %d to %d",
1406 						xfdashboard_window_tracker_monitor_get_number(oldPrimaryStageInterfaceMonitor),
1407 						xfdashboard_window_tracker_monitor_get_number(inNewMonitor));
1408 }
1409 
1410 /* A monitor was added */
_xfdashboard_stage_on_monitor_added(XfdashboardStage * self,XfdashboardWindowTrackerMonitor * inMonitor,gpointer inUserData)1411 static void _xfdashboard_stage_on_monitor_added(XfdashboardStage *self,
1412 												XfdashboardWindowTrackerMonitor *inMonitor,
1413 												gpointer inUserData)
1414 {
1415 	ClutterActor							*interface;
1416 	XfdashboardTheme						*theme;
1417 	XfdashboardThemeLayout					*themeLayout;
1418 
1419 	g_return_if_fail(XFDASHBOARD_IS_STAGE(self));
1420 	g_return_if_fail(XFDASHBOARD_IS_WINDOW_TRACKER_MONITOR(inMonitor));
1421 	g_return_if_fail(XFDASHBOARD_IS_WINDOW_TRACKER(inUserData));
1422 
1423 	/* Get theme and theme layout */
1424 	theme=xfdashboard_application_get_theme(NULL);
1425 	themeLayout=xfdashboard_theme_get_layout(theme);
1426 
1427 	/* Create interface for non-primary monitors. If no interface is defined in theme
1428 	 * then create an empty interface.
1429 	 */
1430 	interface=xfdashboard_theme_layout_build_interface(themeLayout, XFDASHBOARD_THEME_LAYOUT_SECONDARY);
1431 	if(!interface)
1432 	{
1433 		interface=xfdashboard_stage_interface_new();
1434 	}
1435 
1436 	if(!XFDASHBOARD_IS_STAGE_INTERFACE(interface))
1437 	{
1438 		g_critical("Interface '%s' from theme '%s' must be an actor of type %s",
1439 					XFDASHBOARD_THEME_LAYOUT_SECONDARY,
1440 					xfdashboard_theme_get_theme_name(theme),
1441 					g_type_name(XFDASHBOARD_TYPE_STAGE_INTERFACE));
1442 		return;
1443 	}
1444 
1445 	/* Set monitor at interface */
1446 	xfdashboard_stage_interface_set_monitor(XFDASHBOARD_STAGE_INTERFACE(interface), inMonitor);
1447 
1448 	/* Add interface to stage */
1449 	clutter_actor_add_child(CLUTTER_ACTOR(self), interface);
1450 	XFDASHBOARD_DEBUG(self, ACTOR,
1451 						"Added stage interface for new monitor %d",
1452 						xfdashboard_window_tracker_monitor_get_number(inMonitor));
1453 
1454 	/* If monitor added is the primary monitor then swap now the stage interfaces */
1455 	if(xfdashboard_window_tracker_monitor_is_primary(inMonitor))
1456 	{
1457 		_xfdashboard_stage_on_primary_monitor_changed(self, NULL, inMonitor, inUserData);
1458 	}
1459 }
1460 
1461 /* A monitor was removed */
_xfdashboard_stage_on_monitor_removed(XfdashboardStage * self,XfdashboardWindowTrackerMonitor * inMonitor,gpointer inUserData)1462 static void _xfdashboard_stage_on_monitor_removed(XfdashboardStage *self,
1463 													XfdashboardWindowTrackerMonitor *inMonitor,
1464 													gpointer inUserData)
1465 {
1466 	XfdashboardStagePrivate					*priv;
1467 	ClutterActorIter						childIter;
1468 	ClutterActor							*child;
1469 
1470 	g_return_if_fail(XFDASHBOARD_IS_STAGE(self));
1471 	g_return_if_fail(XFDASHBOARD_IS_WINDOW_TRACKER_MONITOR(inMonitor));
1472 	g_return_if_fail(XFDASHBOARD_IS_WINDOW_TRACKER(inUserData));
1473 
1474 	priv=self->priv;
1475 
1476 	/* If monitor removed is the primary monitor swap primary interface with first
1477 	 * stage interface to keep it alive. We should afterward receive a signal that
1478 	 * primary monitor has changed, then the primary interface will be set to its
1479 	 * right place.
1480 	 */
1481 	if(xfdashboard_window_tracker_monitor_is_primary(inMonitor))
1482 	{
1483 		XfdashboardWindowTrackerMonitor*	firstMonitor;
1484 
1485 		/* Get first monitor */
1486 		firstMonitor=xfdashboard_window_tracker_get_monitor_by_number(priv->windowTracker, 0);
1487 
1488 		/* Swp stage interfaces */
1489 		_xfdashboard_stage_on_primary_monitor_changed(self, inMonitor, firstMonitor, inUserData);
1490 	}
1491 
1492 	/* Look up stage interface for removed monitor and destroy it */
1493 	clutter_actor_iter_init(&childIter, CLUTTER_ACTOR(self));
1494 	while(clutter_actor_iter_next(&childIter, &child))
1495 	{
1496 		XfdashboardStageInterface			*interface;
1497 
1498 		/* Only check stage interfaces */
1499 		if(!XFDASHBOARD_IS_STAGE_INTERFACE(child)) continue;
1500 
1501 		/* If stage interface is the one for this monitor then destroy it */
1502 		interface=XFDASHBOARD_STAGE_INTERFACE(child);
1503 		if(xfdashboard_stage_interface_get_monitor(interface)==inMonitor)
1504 		{
1505 			clutter_actor_iter_destroy(&childIter);
1506 			XFDASHBOARD_DEBUG(self, ACTOR,
1507 								"Removed stage interface for removed monitor %d",
1508 								xfdashboard_window_tracker_monitor_get_number(inMonitor));
1509 		}
1510 	}
1511 }
1512 
1513 /* Screen size has changed */
_xfdashboard_stage_on_screen_size_changed(XfdashboardStage * self,gpointer inUserData)1514 static void _xfdashboard_stage_on_screen_size_changed(XfdashboardStage *self,
1515 														gpointer inUserData)
1516 {
1517 	XfdashboardWindowTracker	*windowTracker;
1518 	gint						screenWidth, screenHeight;
1519 	gfloat						stageWidth, stageHeight;
1520 
1521 	g_return_if_fail(XFDASHBOARD_IS_STAGE(self));
1522 	g_return_if_fail(XFDASHBOARD_IS_WINDOW_TRACKER(inUserData));
1523 
1524 	windowTracker=XFDASHBOARD_WINDOW_TRACKER(inUserData);
1525 
1526 	/* Get screen size */
1527 	xfdashboard_window_tracker_get_screen_size(windowTracker, &screenWidth, &screenHeight);
1528 
1529 	/* Get current size of stage */
1530 	clutter_actor_get_size(CLUTTER_ACTOR(self), &stageWidth, &stageHeight);
1531 
1532 	/* If either stage's width or height does not match screen's width or height
1533 	 * resize the stage.
1534 	 */
1535 	if((gint)stageWidth!=screenWidth ||
1536 		(gint)stageHeight!=screenHeight)
1537 	{
1538 		XFDASHBOARD_DEBUG(self, ACTOR,
1539 							"Screen resized to %dx%d but stage has size of %dx%d - resizing stage",
1540 							screenWidth, screenHeight,
1541 							(gint)stageWidth, (gint)stageHeight);
1542 
1543 		clutter_actor_set_size(CLUTTER_ACTOR(self), screenWidth, screenHeight);
1544 	}
1545 }
1546 
1547 /* IMPLEMENTATION: ClutterActor */
1548 
1549 /* The stage actor should be shown */
_xfdashboard_stage_show(ClutterActor * inActor)1550 static void _xfdashboard_stage_show(ClutterActor *inActor)
1551 {
1552 	XfdashboardStage			*self;
1553 	XfdashboardStagePrivate		*priv;
1554 	XfdashboardView				*switchView;
1555 
1556 	g_return_if_fail(XFDASHBOARD_IS_STAGE(inActor));
1557 
1558 	self=XFDASHBOARD_STAGE(inActor);
1559 	priv=self->priv;
1560 
1561 	/* Find view to switch to if requested and switch to this view */
1562 	switchView=_xfdashboard_stage_get_view_to_switch_to(self);
1563 	if(switchView)
1564 	{
1565 		xfdashboard_viewpad_set_active_view(XFDASHBOARD_VIEWPAD(priv->viewpad), switchView);
1566 	}
1567 
1568 	/* Set stage to fullscreen as it may will be a newly created window */
1569 	clutter_stage_set_fullscreen(CLUTTER_STAGE(self), TRUE);
1570 
1571 	/* If we do not know the stage window connect signal to find it */
1572 	if(!priv->stageWindow)
1573 	{
1574 		/* Connect signals */
1575 		XFDASHBOARD_DEBUG(self, ACTOR, "Connecting signal to find stage window");
1576 		g_signal_connect_swapped(priv->windowTracker, "window-opened", G_CALLBACK(_xfdashboard_stage_on_window_opened), self);
1577 	}
1578 
1579 	/* Call parent's show method */
1580 	if(CLUTTER_ACTOR_CLASS(xfdashboard_stage_parent_class)->show)
1581 	{
1582 		CLUTTER_ACTOR_CLASS(xfdashboard_stage_parent_class)->show(inActor);
1583 	}
1584 
1585 	/* Now move focus to actor is one was remembered when theme was loaded */
1586 	if(priv->focusActorOnShow)
1587 	{
1588 		gboolean				reselectFocusOnResume;
1589 
1590 		/* Determine if user (also) requests to reselect focus on resume */
1591 		reselectFocusOnResume=xfconf_channel_get_bool(xfdashboard_application_get_xfconf_channel(NULL),
1592 														RESELECT_THEME_FOCUS_ON_RESUME_XFCONF_PROP,
1593 														DEFAULT_RESELECT_THEME_FOCUS_ON_RESUME);
1594 
1595 		/* Move focus to actor */
1596 		xfdashboard_focus_manager_set_focus(priv->focusManager, XFDASHBOARD_FOCUSABLE(priv->focusActorOnShow));
1597 
1598 		XFDASHBOARD_DEBUG(self, ACTOR,
1599 							"Moved focus to actor %s %s",
1600 							G_OBJECT_TYPE_NAME(priv->focusActorOnShow),
1601 							!reselectFocusOnResume ? "now as it was delayed to when stage is visible" : "because it should be reselected on resume");
1602 
1603 		/* Forget actor to focus now if user did not requested to reselect
1604 		 * this focus again and again when stage window is shown ;)
1605 		 */
1606 		if(!reselectFocusOnResume)
1607 		{
1608 			g_object_remove_weak_pointer(G_OBJECT(priv->focusActorOnShow), &priv->focusActorOnShow);
1609 			priv->focusActorOnShow=NULL;
1610 		}
1611 	}
1612 }
1613 
1614 /* IMPLEMENTATION: GObject */
1615 
1616 /* Dispose this object */
_xfdashboard_stage_dispose(GObject * inObject)1617 static void _xfdashboard_stage_dispose(GObject *inObject)
1618 {
1619 	XfdashboardStage			*self=XFDASHBOARD_STAGE(inObject);
1620 	XfdashboardStagePrivate		*priv=self->priv;
1621 
1622 	/* Release allocated resources */
1623 	if(priv->stageWindow)
1624 	{
1625 		g_signal_handlers_disconnect_by_func(priv->stageWindow, G_CALLBACK(_xfdashboard_stage_on_window_closed), self);
1626 		xfdashboard_window_tracker_window_hide_stage(priv->stageWindow);
1627 		priv->stageWindow=NULL;
1628 	}
1629 
1630 	if(priv->focusManager)
1631 	{
1632 		g_object_unref(priv->focusManager);
1633 		priv->focusManager=NULL;
1634 	}
1635 
1636 	if(priv->notificationTimeoutID)
1637 	{
1638 		g_source_remove(priv->notificationTimeoutID);
1639 		priv->notificationTimeoutID=0;
1640 	}
1641 
1642 	if(priv->windowTracker)
1643 	{
1644 		g_signal_handlers_disconnect_by_data(priv->windowTracker, self);
1645 		g_object_unref(priv->windowTracker);
1646 		priv->windowTracker=NULL;
1647 	}
1648 
1649 	if(priv->backgroundColor)
1650 	{
1651 		clutter_color_free(priv->backgroundColor);
1652 		priv->backgroundColor=NULL;
1653 	}
1654 
1655 	if(priv->notification)
1656 	{
1657 		clutter_actor_destroy(CLUTTER_ACTOR(priv->notification));
1658 		priv->notification=NULL;
1659 	}
1660 
1661 	if(priv->tooltip)
1662 	{
1663 		clutter_actor_destroy(CLUTTER_ACTOR(priv->tooltip));
1664 		priv->tooltip=NULL;
1665 	}
1666 
1667 	if(priv->quicklaunch)
1668 	{
1669 		clutter_actor_destroy(priv->quicklaunch);
1670 		priv->quicklaunch=NULL;
1671 	}
1672 
1673 	if(priv->searchbox)
1674 	{
1675 		clutter_actor_destroy(priv->searchbox);
1676 		priv->searchbox=NULL;
1677 	}
1678 
1679 	if(priv->workspaces)
1680 	{
1681 		clutter_actor_destroy(priv->workspaces);
1682 		priv->workspaces=NULL;
1683 	}
1684 
1685 	if(priv->viewSelector)
1686 	{
1687 		clutter_actor_destroy(priv->viewSelector);
1688 		priv->viewSelector=NULL;
1689 	}
1690 
1691 	if(priv->viewpad)
1692 	{
1693 		clutter_actor_destroy(priv->viewpad);
1694 		priv->viewpad=NULL;
1695 	}
1696 
1697 	if(priv->primaryInterface)
1698 	{
1699 		clutter_actor_destroy(priv->primaryInterface);
1700 		priv->primaryInterface=NULL;
1701 	}
1702 
1703 	if(priv->viewBeforeSearch)
1704 	{
1705 		g_object_unref(priv->viewBeforeSearch);
1706 		priv->viewBeforeSearch=NULL;
1707 	}
1708 
1709 	if(priv->backgroundImageLayer)
1710 	{
1711 		clutter_actor_destroy(priv->backgroundImageLayer);
1712 		priv->backgroundImageLayer=NULL;
1713 	}
1714 
1715 	if(priv->backgroundColorLayer)
1716 	{
1717 		clutter_actor_destroy(priv->backgroundColorLayer);
1718 		priv->backgroundColorLayer=NULL;
1719 	}
1720 
1721 	if(priv->switchToView)
1722 	{
1723 		g_free(priv->switchToView);
1724 		priv->switchToView=NULL;
1725 	}
1726 
1727 	/* Call parent's class dispose method */
1728 	G_OBJECT_CLASS(xfdashboard_stage_parent_class)->dispose(inObject);
1729 }
1730 
1731 /* Set/get properties */
_xfdashboard_stage_set_property(GObject * inObject,guint inPropID,const GValue * inValue,GParamSpec * inSpec)1732 static void _xfdashboard_stage_set_property(GObject *inObject,
1733 											guint inPropID,
1734 											const GValue *inValue,
1735 											GParamSpec *inSpec)
1736 {
1737 	XfdashboardStage			*self=XFDASHBOARD_STAGE(inObject);
1738 
1739 	switch(inPropID)
1740 	{
1741 		case PROP_BACKGROUND_IMAGE_TYPE:
1742 			xfdashboard_stage_set_background_image_type(self, g_value_get_enum(inValue));
1743 			break;
1744 
1745 		case PROP_BACKGROUND_COLOR:
1746 			xfdashboard_stage_set_background_color(self, clutter_value_get_color(inValue));
1747 			break;
1748 
1749 		case PROP_SWITCH_TO_VIEW:
1750 			xfdashboard_stage_set_switch_to_view(self, g_value_get_string(inValue));
1751 			break;
1752 
1753 		default:
1754 			G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
1755 			break;
1756 	}
1757 }
1758 
_xfdashboard_stage_get_property(GObject * inObject,guint inPropID,GValue * outValue,GParamSpec * inSpec)1759 static void _xfdashboard_stage_get_property(GObject *inObject,
1760 											guint inPropID,
1761 											GValue *outValue,
1762 											GParamSpec *inSpec)
1763 {
1764 	XfdashboardStage			*self=XFDASHBOARD_STAGE(inObject);
1765 	XfdashboardStagePrivate		*priv=self->priv;
1766 
1767 	switch(inPropID)
1768 	{
1769 		case PROP_BACKGROUND_IMAGE_TYPE:
1770 			g_value_set_enum(outValue, priv->backgroundType);
1771 			break;
1772 
1773 		case PROP_BACKGROUND_COLOR:
1774 			clutter_value_set_color(outValue, priv->backgroundColor);
1775 			break;
1776 
1777 		case PROP_SWITCH_TO_VIEW:
1778 			g_value_set_string(outValue, priv->switchToView);
1779 			break;
1780 
1781 		default:
1782 			G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
1783 			break;
1784 	}
1785 }
1786 
1787 /* Class initialization
1788  * Override functions in parent classes and define properties
1789  * and signals
1790  */
xfdashboard_stage_class_init(XfdashboardStageClass * klass)1791 static void xfdashboard_stage_class_init(XfdashboardStageClass *klass)
1792 {
1793 	ClutterActorClass				*actorClass=CLUTTER_ACTOR_CLASS(klass);
1794 	GObjectClass					*gobjectClass=G_OBJECT_CLASS(klass);
1795 
1796 	/* Override functions */
1797 	klass->show_tooltip=_xfdashboard_stage_show_tooltip;
1798 	klass->hide_tooltip=_xfdashboard_stage_hide_tooltip;
1799 
1800 	actorClass->show=_xfdashboard_stage_show;
1801 	actorClass->event=_xfdashboard_stage_event;
1802 
1803 	gobjectClass->dispose=_xfdashboard_stage_dispose;
1804 	gobjectClass->set_property=_xfdashboard_stage_set_property;
1805 	gobjectClass->get_property=_xfdashboard_stage_get_property;
1806 
1807 	/* Define properties */
1808 	XfdashboardStageProperties[PROP_BACKGROUND_IMAGE_TYPE]=
1809 		g_param_spec_enum("background-image-type",
1810 							"Background image type",
1811 							"Background image type",
1812 							XFDASHBOARD_TYPE_STAGE_BACKGROUND_IMAGE_TYPE,
1813 							XFDASHBOARD_STAGE_BACKGROUND_IMAGE_TYPE_NONE,
1814 							G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1815 
1816 	XfdashboardStageProperties[PROP_BACKGROUND_COLOR]=
1817 		clutter_param_spec_color("background-color",
1818 									"Background color",
1819 									"Color of stage's background",
1820 									NULL,
1821 									G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1822 
1823 	XfdashboardStageProperties[PROP_SWITCH_TO_VIEW]=
1824 		g_param_spec_string("switch-to-view",
1825 							"Switch to view",
1826 							"Switch to this named view as soon as stage gets visible",
1827 							NULL,
1828 							G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1829 
1830 	g_object_class_install_properties(gobjectClass, PROP_LAST, XfdashboardStageProperties);
1831 
1832 	/* Define signals */
1833 	XfdashboardStageSignals[SIGNAL_ACTOR_CREATED]=
1834 		g_signal_new("actor-created",
1835 						G_TYPE_FROM_CLASS(klass),
1836 						G_SIGNAL_RUN_LAST,
1837 						G_STRUCT_OFFSET(XfdashboardStageClass, actor_created),
1838 						NULL,
1839 						NULL,
1840 						g_cclosure_marshal_VOID__OBJECT,
1841 						G_TYPE_NONE,
1842 						1,
1843 						CLUTTER_TYPE_ACTOR);
1844 
1845 	XfdashboardStageSignals[SIGNAL_SEARCH_STARTED]=
1846 		g_signal_new("search-started",
1847 						G_TYPE_FROM_CLASS(klass),
1848 						G_SIGNAL_RUN_LAST,
1849 						G_STRUCT_OFFSET(XfdashboardStageClass, search_started),
1850 						NULL,
1851 						NULL,
1852 						g_cclosure_marshal_VOID__VOID,
1853 						G_TYPE_NONE,
1854 						0);
1855 
1856 	XfdashboardStageSignals[SIGNAL_SEARCH_CHANGED]=
1857 		g_signal_new("search-changed",
1858 						G_TYPE_FROM_CLASS(klass),
1859 						G_SIGNAL_RUN_LAST,
1860 						G_STRUCT_OFFSET(XfdashboardStageClass, search_changed),
1861 						NULL,
1862 						NULL,
1863 						g_cclosure_marshal_VOID__STRING,
1864 						G_TYPE_NONE,
1865 						1,
1866 						G_TYPE_STRING);
1867 
1868 	XfdashboardStageSignals[SIGNAL_SEARCH_ENDED]=
1869 		g_signal_new("search-ended",
1870 						G_TYPE_FROM_CLASS(klass),
1871 						G_SIGNAL_RUN_LAST,
1872 						G_STRUCT_OFFSET(XfdashboardStageClass, search_ended),
1873 						NULL,
1874 						NULL,
1875 						g_cclosure_marshal_VOID__VOID,
1876 						G_TYPE_NONE,
1877 						0);
1878 
1879 	XfdashboardStageSignals[SIGNAL_SHOW_TOOLTIP]=
1880 		g_signal_new("show-tooltip",
1881 						G_TYPE_FROM_CLASS(klass),
1882 						G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1883 						G_STRUCT_OFFSET(XfdashboardStageClass, show_tooltip),
1884 						NULL,
1885 						NULL,
1886 						g_cclosure_marshal_VOID__OBJECT,
1887 						G_TYPE_NONE,
1888 						1,
1889 						CLUTTER_TYPE_ACTION);
1890 
1891 	XfdashboardStageSignals[SIGNAL_HIDE_TOOLTIP]=
1892 		g_signal_new("hide-tooltip",
1893 						G_TYPE_FROM_CLASS(klass),
1894 						G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1895 						G_STRUCT_OFFSET(XfdashboardStageClass, hide_tooltip),
1896 						NULL,
1897 						NULL,
1898 						g_cclosure_marshal_VOID__OBJECT,
1899 						G_TYPE_NONE,
1900 						1,
1901 						CLUTTER_TYPE_ACTION);
1902 }
1903 
1904 /* Object initialization
1905  * Create private structure and set up default values
1906  */
xfdashboard_stage_init(XfdashboardStage * self)1907 static void xfdashboard_stage_init(XfdashboardStage *self)
1908 {
1909 	XfdashboardStagePrivate		*priv;
1910 	XfdashboardApplication		*application;
1911 	ClutterConstraint			*widthConstraint;
1912 	ClutterConstraint			*heightConstraint;
1913 	ClutterColor				transparent;
1914 
1915 	priv=self->priv=xfdashboard_stage_get_instance_private(self);
1916 
1917 	/* Set default values */
1918 	priv->focusManager=xfdashboard_focus_manager_get_default();
1919 	priv->windowTracker=xfdashboard_window_tracker_get_default();
1920 	priv->stageWindow=NULL;
1921 	priv->primaryInterface=NULL;
1922 	priv->quicklaunch=NULL;
1923 	priv->searchbox=NULL;
1924 	priv->workspaces=NULL;
1925 	priv->viewpad=NULL;
1926 	priv->viewSelector=NULL;
1927 	priv->notification=NULL;
1928 	priv->tooltip=NULL;
1929 	priv->lastSearchTextLength=0;
1930 	priv->viewBeforeSearch=NULL;
1931 	priv->searchActive=FALSE;
1932 	priv->notificationTimeoutID=0;
1933 	priv->backgroundType=XFDASHBOARD_STAGE_BACKGROUND_IMAGE_TYPE_NONE;
1934 	priv->backgroundColor=NULL;
1935 	priv->backgroundColorLayer=NULL;
1936 	priv->backgroundImageLayer=NULL;
1937 	priv->switchToView=NULL;
1938 	priv->focusActorOnShow=NULL;
1939 
1940 	/* Create background actors but order of adding background children is important */
1941 	widthConstraint=clutter_bind_constraint_new(CLUTTER_ACTOR(self), CLUTTER_BIND_WIDTH, 0.0f);
1942 	heightConstraint=clutter_bind_constraint_new(CLUTTER_ACTOR(self), CLUTTER_BIND_HEIGHT, 0.0f);
1943 	priv->backgroundImageLayer=clutter_actor_new();
1944 	clutter_actor_hide(priv->backgroundImageLayer);
1945 	clutter_actor_add_constraint(priv->backgroundImageLayer, widthConstraint);
1946 	clutter_actor_add_constraint(priv->backgroundImageLayer, heightConstraint);
1947 	clutter_actor_add_child(CLUTTER_ACTOR(self), priv->backgroundImageLayer);
1948 
1949 	widthConstraint=clutter_bind_constraint_new(CLUTTER_ACTOR(self), CLUTTER_BIND_WIDTH, 0.0f);
1950 	heightConstraint=clutter_bind_constraint_new(CLUTTER_ACTOR(self), CLUTTER_BIND_HEIGHT, 0.0f);
1951 	priv->backgroundColorLayer=clutter_actor_new();
1952 	clutter_actor_hide(priv->backgroundColorLayer);
1953 	clutter_actor_add_constraint(priv->backgroundColorLayer, widthConstraint);
1954 	clutter_actor_add_constraint(priv->backgroundColorLayer, heightConstraint);
1955 	clutter_actor_add_child(CLUTTER_ACTOR(self), priv->backgroundColorLayer);
1956 
1957 	/* Set up stage and style it */
1958 	clutter_color_init(&transparent, 0, 0, 0, 0);
1959 	clutter_actor_set_background_color(CLUTTER_ACTOR(self), &transparent);
1960 
1961 	clutter_stage_set_use_alpha(CLUTTER_STAGE(self), TRUE);
1962 	clutter_stage_set_user_resizable(CLUTTER_STAGE(self), FALSE);
1963 	clutter_stage_set_fullscreen(CLUTTER_STAGE(self), TRUE);
1964 
1965 	/* Connect signals to window tracker */
1966 	g_signal_connect_swapped(priv->windowTracker,
1967 								"monitor-added",
1968 								G_CALLBACK(_xfdashboard_stage_on_monitor_added),
1969 								self);
1970 	g_signal_connect_swapped(priv->windowTracker,
1971 								"monitor-removed",
1972 								G_CALLBACK(_xfdashboard_stage_on_monitor_removed),
1973 								self);
1974 	g_signal_connect_swapped(priv->windowTracker,
1975 								"primary-monitor-changed",
1976 								G_CALLBACK(_xfdashboard_stage_on_primary_monitor_changed),
1977 								self);
1978 
1979 	/* Connect signal to application */
1980 	application=xfdashboard_application_get_default();
1981 	g_signal_connect_swapped(application,
1982 								"suspend",
1983 								G_CALLBACK(_xfdashboard_stage_on_application_suspend),
1984 								self);
1985 
1986 	g_signal_connect_swapped(application,
1987 								"resume",
1988 								G_CALLBACK(_xfdashboard_stage_on_application_resume),
1989 								self);
1990 
1991 	g_signal_connect_swapped(application,
1992 								"theme-changed",
1993 								G_CALLBACK(_xfdashboard_stage_on_application_theme_changed),
1994 								self);
1995 
1996 	/* Resize stage to match screen size and listen for futher screen size changes
1997 	 * to resize stage again.
1998 	 * This should only be needed when compiled against Clutter prior to 0.17.2
1999 	 * because this version or newer ones seem to handle window resizes correctly.
2000 	 */
2001 	if(clutter_major_version<1 ||
2002 		(clutter_major_version==1 && clutter_minor_version<17) ||
2003 		(clutter_major_version==1 && clutter_minor_version==17 && clutter_micro_version<2))
2004 	{
2005 		_xfdashboard_stage_on_screen_size_changed(self, priv->windowTracker);
2006 
2007 		g_signal_connect_swapped(priv->windowTracker,
2008 									"screen-size-changed",
2009 									G_CALLBACK(_xfdashboard_stage_on_screen_size_changed),
2010 									self);
2011 
2012 		XFDASHBOARD_DEBUG(self, ACTOR, "Tracking screen resizes to resize stage");
2013 	}
2014 }
2015 
2016 /* IMPLEMENTATION: Public API */
2017 
2018 /* Create new instance */
xfdashboard_stage_new(void)2019 ClutterActor* xfdashboard_stage_new(void)
2020 {
2021 	return(CLUTTER_ACTOR(g_object_new(XFDASHBOARD_TYPE_STAGE, NULL)));
2022 }
2023 
2024 /* Get/set background type */
xfdashboard_stage_get_background_image_type(XfdashboardStage * self)2025 XfdashboardStageBackgroundImageType xfdashboard_stage_get_background_image_type(XfdashboardStage *self)
2026 {
2027 	g_return_val_if_fail(XFDASHBOARD_IS_STAGE(self), XFDASHBOARD_STAGE_BACKGROUND_IMAGE_TYPE_NONE);
2028 
2029 	return(self->priv->backgroundType);
2030 }
2031 
xfdashboard_stage_set_background_image_type(XfdashboardStage * self,XfdashboardStageBackgroundImageType inType)2032 void xfdashboard_stage_set_background_image_type(XfdashboardStage *self, XfdashboardStageBackgroundImageType inType)
2033 {
2034 	XfdashboardStagePrivate		*priv;
2035 
2036 	g_return_if_fail(XFDASHBOARD_IS_STAGE(self));
2037 	g_return_if_fail(inType<=XFDASHBOARD_STAGE_BACKGROUND_IMAGE_TYPE_DESKTOP);
2038 
2039 	priv=self->priv;
2040 
2041 
2042 	/* Set value if changed */
2043 	if(priv->backgroundType!=inType)
2044 	{
2045 		/* Set value */
2046 		priv->backgroundType=inType;
2047 
2048 		/* Set up background actor depending on type */
2049 		if(priv->backgroundImageLayer)
2050 		{
2051 			switch(priv->backgroundType)
2052 			{
2053 				case XFDASHBOARD_STAGE_BACKGROUND_IMAGE_TYPE_DESKTOP:
2054 					{
2055 						XfdashboardWindowTrackerWindow	*backgroundWindow;
2056 
2057 						backgroundWindow=xfdashboard_window_tracker_get_root_window(priv->windowTracker);
2058 						if(backgroundWindow)
2059 						{
2060 							ClutterContent				*backgroundContent;
2061 
2062 							backgroundContent=xfdashboard_window_tracker_window_get_content(backgroundWindow);
2063 							clutter_actor_show(priv->backgroundImageLayer);
2064 							clutter_actor_set_content(priv->backgroundImageLayer, backgroundContent);
2065 							g_object_unref(backgroundContent);
2066 
2067 							XFDASHBOARD_DEBUG(self, ACTOR, "Desktop window was found and set up as background image for stage");
2068 						}
2069 							else
2070 							{
2071 								g_signal_connect_swapped(priv->windowTracker,
2072 															"window-opened",
2073 															G_CALLBACK(_xfdashboard_stage_on_desktop_window_opened),
2074 															self);
2075 								XFDASHBOARD_DEBUG(self, ACTOR, "Desktop window was not found. Setting up signal to get notified when desktop window might be opened.");
2076 							}
2077 					}
2078 					break;
2079 
2080 				default:
2081 					clutter_actor_hide(priv->backgroundImageLayer);
2082 					clutter_actor_set_content(priv->backgroundImageLayer, NULL);
2083 					break;
2084 			}
2085 		}
2086 
2087 		/* Notify about property change */
2088 		g_object_notify_by_pspec(G_OBJECT(self), XfdashboardStageProperties[PROP_BACKGROUND_IMAGE_TYPE]);
2089 	}
2090 }
2091 
2092 /* Get/set background color */
xfdashboard_stage_get_background_color(XfdashboardStage * self)2093 ClutterColor* xfdashboard_stage_get_background_color(XfdashboardStage *self)
2094 {
2095 	g_return_val_if_fail(XFDASHBOARD_IS_STAGE(self), NULL);
2096 
2097 	return(self->priv->backgroundColor);
2098 }
2099 
xfdashboard_stage_set_background_color(XfdashboardStage * self,const ClutterColor * inColor)2100 void xfdashboard_stage_set_background_color(XfdashboardStage *self, const ClutterColor *inColor)
2101 {
2102 	XfdashboardStagePrivate		*priv;
2103 
2104 	g_return_if_fail(XFDASHBOARD_IS_STAGE(self));
2105 
2106 	priv=self->priv;
2107 
2108 	/* Set value if changed */
2109 	if((priv->backgroundColor && !inColor) ||
2110 		(!priv->backgroundColor && inColor) ||
2111 		(inColor && clutter_color_equal(inColor, priv->backgroundColor)==FALSE))
2112 	{
2113 		/* Set value */
2114 		if(priv->backgroundColor)
2115 		{
2116 			clutter_color_free(priv->backgroundColor);
2117 			priv->backgroundColor=NULL;
2118 		}
2119 
2120 		if(inColor) priv->backgroundColor=clutter_color_copy(inColor);
2121 
2122 		/* If a color is provided set background color and show background actor
2123 		 * otherwise hide background actor
2124 		 */
2125 		if(priv->backgroundColorLayer)
2126 		{
2127 			if(priv->backgroundColor)
2128 			{
2129 				clutter_actor_set_background_color(priv->backgroundColorLayer,
2130 													priv->backgroundColor);
2131 				clutter_actor_show(priv->backgroundColorLayer);
2132 			}
2133 				else clutter_actor_hide(priv->backgroundColorLayer);
2134 		}
2135 
2136 		/* Notify about property change */
2137 		g_object_notify_by_pspec(G_OBJECT(self), XfdashboardStageProperties[PROP_BACKGROUND_COLOR]);
2138 	}
2139 }
2140 
2141 /* Set name of view to switch to at next resume */
xfdashboard_stage_get_switch_to_view(XfdashboardStage * self)2142 const gchar* xfdashboard_stage_get_switch_to_view(XfdashboardStage *self)
2143 {
2144 	g_return_val_if_fail(XFDASHBOARD_IS_STAGE(self), NULL);
2145 
2146 	return(self->priv->switchToView);
2147 }
2148 
xfdashboard_stage_set_switch_to_view(XfdashboardStage * self,const gchar * inViewInternalName)2149 void xfdashboard_stage_set_switch_to_view(XfdashboardStage *self, const gchar *inViewInternalName)
2150 {
2151 	XfdashboardStagePrivate		*priv;
2152 
2153 	g_return_if_fail(XFDASHBOARD_IS_STAGE(self));
2154 
2155 	priv=self->priv;
2156 
2157 	/* Set value if changed */
2158 	if(g_strcmp0(priv->switchToView, inViewInternalName)!=0)
2159 	{
2160 		if(priv->switchToView)
2161 		{
2162 			g_free(priv->switchToView);
2163 			priv->switchToView=NULL;
2164 		}
2165 
2166 		if(inViewInternalName) priv->switchToView=g_strdup(inViewInternalName);
2167 
2168 		/* Notify about property change */
2169 		g_object_notify_by_pspec(G_OBJECT(self), XfdashboardStageProperties[PROP_SWITCH_TO_VIEW]);
2170 	}
2171 }
2172 
2173 /* Show a notification on stage */
xfdashboard_stage_show_notification(XfdashboardStage * self,const gchar * inIconName,const gchar * inText)2174 void xfdashboard_stage_show_notification(XfdashboardStage *self, const gchar *inIconName, const gchar *inText)
2175 {
2176 	XfdashboardStagePrivate		*priv;
2177 	gint						interval;
2178 
2179 	g_return_if_fail(XFDASHBOARD_IS_STAGE(self));
2180 
2181 	priv=self->priv;
2182 
2183 	/* Stop current running timeout source because it would hide this
2184 	 * new notification to soon.
2185 	 */
2186 	if(priv->notificationTimeoutID)
2187 	{
2188 		g_source_remove(priv->notificationTimeoutID);
2189 		priv->notificationTimeoutID=0;
2190 	}
2191 
2192 	/* Only show notification if a notification box is known where the notification
2193 	 * could be shown at.
2194 	 */
2195 	if(!priv->notification)
2196 	{
2197 		XFDASHBOARD_DEBUG(self, ACTOR, "Cannot show notification because no notification box is available");
2198 		return;
2199 	}
2200 
2201 	/* Show notification on stage */
2202 	xfdashboard_text_box_set_text(XFDASHBOARD_TEXT_BOX(priv->notification), inText);
2203 	xfdashboard_text_box_set_primary_icon(XFDASHBOARD_TEXT_BOX(priv->notification), inIconName);
2204 	clutter_actor_show(CLUTTER_ACTOR(priv->notification));
2205 
2206 	/* Set up timeout source. The timeout interval differs and depends on the length
2207 	 * of the notification text to show but never drops below the minimum timeout configured.
2208 	 * The interval is calculated by one second for 30 characters.
2209 	 */
2210 	interval=xfconf_channel_get_uint(xfdashboard_application_get_xfconf_channel(NULL),
2211 										NOTIFICATION_TIMEOUT_XFCONF_PROP,
2212 										DEFAULT_NOTIFICATION_TIMEOUT);
2213 	interval=MAX((gint)((strlen(inText)/30.0f)*1000.0f), interval);
2214 
2215 	priv->notificationTimeoutID=clutter_threads_add_timeout_full(G_PRIORITY_DEFAULT,
2216 																	interval,
2217 																	_xfdashboard_stage_on_notification_timeout,
2218 																	self,
2219 																	_xfdashboard_stage_on_notification_timeout_destroyed);
2220 }
2221