1 /**
2 * This file is a part of the Cairo-Dock project
3 *
4 * Copyright : (C) see the 'copyright' file.
5 * E-mail    : see the 'copyright' file.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 3
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include <math.h>
21 #include <string.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 
25 #include <cairo.h>
26 
27 #include "cairo-dock-icon-factory.h"
28 #include "cairo-dock-icon-facility.h"
29 #include "cairo-dock-surface-factory.h"
30 #include "cairo-dock-log.h"
31 #include "cairo-dock-utils.h"  // cairo_dock_remove_version_from_string
32 #include "cairo-dock-dock-manager.h"
33 #include "cairo-dock-applet-manager.h"
34 #include "cairo-dock-launcher-manager.h"
35 #include "cairo-dock-stack-icon-manager.h"
36 #include "cairo-dock-separator-manager.h"
37 #include "cairo-dock-class-icon-manager.h"
38 #include "cairo-dock-dock-factory.h"
39 #include "cairo-dock-desktop-manager.h"  // gldi_desktop_notify_startup
40 #include "cairo-dock-module-manager.h"  // GldiModule
41 #include "cairo-dock-module-instance-manager.h"  // GldiModuleInstance
42 #include "cairo-dock-dock-facility.h"
43 #include "cairo-dock-applications-manager.h"
44 #include "cairo-dock-draw.h"
45 #include "cairo-dock-image-buffer.h"
46 #include "cairo-dock-icon-manager.h"
47 #include "cairo-dock-indicator-manager.h"  // myIndicatorsParam.bUseClassIndic
48 #include "cairo-dock-container.h"
49 #include "cairo-dock-animations.h"
50 #include "cairo-dock-application-facility.h"
51 #include "cairo-dock-keyfile-utilities.h"
52 #include "cairo-dock-file-manager.h"
53 #include "cairo-dock-windows-manager.h"
54 #include "cairo-dock-class-manager.h"
55 
56 extern CairoDock *g_pMainDock;
57 extern CairoDockDesktopEnv g_iDesktopEnv;
58 
59 static GHashTable *s_hClassTable = NULL;
60 
61 
cairo_dock_free_class_appli(CairoDockClassAppli * pClassAppli)62 static void cairo_dock_free_class_appli (CairoDockClassAppli *pClassAppli)
63 {
64 	g_list_free (pClassAppli->pIconsOfClass);
65 	g_list_free (pClassAppli->pAppliOfClass);
66 	g_free (pClassAppli->cDesktopFile);
67 	g_free (pClassAppli->cCommand);
68 	g_free (pClassAppli->cName);
69 	g_free (pClassAppli->cIcon);
70 	g_free (pClassAppli->cStartupWMClass);
71 	g_free (pClassAppli->cWorkingDirectory);
72 	if (pClassAppli->pMimeTypes)
73 		g_strfreev (pClassAppli->pMimeTypes);
74 	g_list_foreach (pClassAppli->pMenuItems, (GFunc)g_strfreev, NULL);
75 	g_list_free (pClassAppli->pMenuItems);
76 	if (pClassAppli->iSidOpeningTimeout != 0)
77 		g_source_remove (pClassAppli->iSidOpeningTimeout);
78 	g_free (pClassAppli);
79 }
80 
_cairo_dock_lookup_class_appli(const gchar * cClass)81 static inline CairoDockClassAppli *_cairo_dock_lookup_class_appli (const gchar *cClass)
82 {
83 	return (cClass != NULL ? g_hash_table_lookup (s_hClassTable, cClass) : NULL);
84 }
85 
86 
_on_window_created(G_GNUC_UNUSED gpointer data,GldiWindowActor * actor)87 static gboolean _on_window_created (G_GNUC_UNUSED gpointer data, GldiWindowActor *actor)
88 {
89 	gldi_class_startup_notify_end (actor->cClass);
90 
91 	return GLDI_NOTIFICATION_LET_PASS;
92 }
_on_window_activated(G_GNUC_UNUSED gpointer data,GldiWindowActor * actor)93 static gboolean _on_window_activated (G_GNUC_UNUSED gpointer data, GldiWindowActor *actor)
94 {
95 	if (! actor)
96 		return GLDI_NOTIFICATION_LET_PASS;
97 
98 	gldi_class_startup_notify_end (actor->cClass);
99 
100 	return GLDI_NOTIFICATION_LET_PASS;
101 }
cairo_dock_initialize_class_manager(void)102 void cairo_dock_initialize_class_manager (void)
103 {
104 	if (s_hClassTable == NULL)
105 		s_hClassTable = g_hash_table_new_full (g_str_hash,
106 			g_str_equal,
107 			g_free,
108 			(GDestroyNotify) cairo_dock_free_class_appli);
109 	// register to events to detect the ending of a launching
110 	gldi_object_register_notification (&myWindowObjectMgr,
111 		NOTIFICATION_WINDOW_CREATED,
112 		(GldiNotificationFunc) _on_window_created,
113 		GLDI_RUN_AFTER, NULL);
114 	gldi_object_register_notification (&myWindowObjectMgr,
115 		NOTIFICATION_WINDOW_ACTIVATED,
116 		(GldiNotificationFunc) _on_window_activated,
117 		GLDI_RUN_AFTER, NULL);  // some applications don't open a new window, but rather take the focus;
118 }
119 
120 
cairo_dock_list_existing_appli_with_class(const gchar * cClass)121 const GList *cairo_dock_list_existing_appli_with_class (const gchar *cClass)
122 {
123 	g_return_val_if_fail (cClass != NULL, NULL);
124 
125 	CairoDockClassAppli *pClassAppli = _cairo_dock_lookup_class_appli (cClass);
126 	return (pClassAppli != NULL ? pClassAppli->pAppliOfClass : NULL);
127 }
128 
129 
cairo_dock_get_class(const gchar * cClass)130 static CairoDockClassAppli *cairo_dock_get_class (const gchar *cClass)
131 {
132 	g_return_val_if_fail (cClass != NULL, NULL);
133 
134 	CairoDockClassAppli *pClassAppli = _cairo_dock_lookup_class_appli (cClass);
135 	if (pClassAppli == NULL)
136 	{
137 		pClassAppli = g_new0 (CairoDockClassAppli, 1);
138 		g_hash_table_insert (s_hClassTable, g_strdup (cClass), pClassAppli);
139 	}
140 	return pClassAppli;
141 }
142 
_cairo_dock_add_inhibitor_to_class(const gchar * cClass,Icon * pIcon)143 static gboolean _cairo_dock_add_inhibitor_to_class (const gchar *cClass, Icon *pIcon)
144 {
145 	CairoDockClassAppli *pClassAppli = cairo_dock_get_class (cClass);
146 	g_return_val_if_fail (pClassAppli!= NULL, FALSE);
147 
148 	g_return_val_if_fail (g_list_find (pClassAppli->pIconsOfClass, pIcon) == NULL, TRUE);
149 	pClassAppli->pIconsOfClass = g_list_prepend (pClassAppli->pIconsOfClass, pIcon);
150 
151 	return TRUE;
152 }
153 
cairo_dock_get_class_subdock(const gchar * cClass)154 CairoDock *cairo_dock_get_class_subdock (const gchar *cClass)
155 {
156 	CairoDockClassAppli *pClassAppli = cairo_dock_get_class (cClass);
157 	g_return_val_if_fail (pClassAppli!= NULL, NULL);
158 
159 	return gldi_dock_get (pClassAppli->cDockName);
160 }
161 
cairo_dock_create_class_subdock(const gchar * cClass,CairoDock * pParentDock)162 CairoDock* cairo_dock_create_class_subdock (const gchar *cClass, CairoDock *pParentDock)
163 {
164 	CairoDockClassAppli *pClassAppli = cairo_dock_get_class (cClass);
165 	g_return_val_if_fail (pClassAppli!= NULL, NULL);
166 
167 	CairoDock *pDock = gldi_dock_get (pClassAppli->cDockName);
168 	if (pDock == NULL)  // cDockName not yet defined, or previous class subdock no longer exists
169 	{
170 		g_free (pClassAppli->cDockName);
171 		pClassAppli->cDockName = cairo_dock_get_unique_dock_name (cClass);
172 		pDock = gldi_subdock_new (pClassAppli->cDockName, NULL, pParentDock, NULL);
173 	}
174 
175 	return pDock;
176 }
177 
cairo_dock_destroy_class_subdock(const gchar * cClass)178 static void cairo_dock_destroy_class_subdock (const gchar *cClass)
179 {
180 	CairoDockClassAppli *pClassAppli = cairo_dock_get_class (cClass);
181 	g_return_if_fail (pClassAppli!= NULL);
182 
183 	CairoDock *pDock = gldi_dock_get (pClassAppli->cDockName);
184 	if (pDock)
185 	{
186 		gldi_object_unref (GLDI_OBJECT(pDock));
187 	}
188 
189 	g_free (pClassAppli->cDockName);
190 	pClassAppli->cDockName = NULL;
191 }
192 
cairo_dock_add_appli_icon_to_class(Icon * pIcon)193 gboolean cairo_dock_add_appli_icon_to_class (Icon *pIcon)
194 {
195 	g_return_val_if_fail (CAIRO_DOCK_ICON_TYPE_IS_APPLI (pIcon) && pIcon->pAppli, FALSE);
196 	cd_debug ("%s (%s)", __func__, pIcon->cClass);
197 
198 	if (pIcon->cClass == NULL)
199 	{
200 		cd_message (" %s doesn't have any class, not good!", pIcon->cName);
201 		return FALSE;
202 	}
203 	CairoDockClassAppli *pClassAppli = cairo_dock_get_class (pIcon->cClass);
204 	g_return_val_if_fail (pClassAppli!= NULL, FALSE);
205 
206 	///if (pClassAppli->iAge == 0)  // age is > 0, so it means we have never set it yet.
207 	if (pClassAppli->pAppliOfClass == NULL)  // the first appli of a class defines the age of the class.
208 		pClassAppli->iAge = pIcon->pAppli->iAge;
209 
210 	g_return_val_if_fail (g_list_find (pClassAppli->pAppliOfClass, pIcon) == NULL, TRUE);
211 	pClassAppli->pAppliOfClass = g_list_prepend (pClassAppli->pAppliOfClass, pIcon);
212 
213 	return TRUE;
214 }
215 
cairo_dock_remove_appli_from_class(Icon * pIcon)216 gboolean cairo_dock_remove_appli_from_class (Icon *pIcon)
217 {
218 	g_return_val_if_fail (pIcon!= NULL, FALSE);
219 	cd_debug ("%s (%s, %s)", __func__, pIcon->cClass, pIcon->cName);
220 
221 	CairoDockClassAppli *pClassAppli = cairo_dock_get_class (pIcon->cClass);
222 	g_return_val_if_fail (pClassAppli!= NULL, FALSE);
223 
224 	pClassAppli->pAppliOfClass = g_list_remove (pClassAppli->pAppliOfClass, pIcon);
225 
226 	return TRUE;
227 }
228 
cairo_dock_set_class_use_xicon(const gchar * cClass,gboolean bUseXIcon)229 gboolean cairo_dock_set_class_use_xicon (const gchar *cClass, gboolean bUseXIcon)
230 {
231 	CairoDockClassAppli *pClassAppli = cairo_dock_get_class (cClass);
232 	g_return_val_if_fail (pClassAppli!= NULL, FALSE);
233 
234 	if (pClassAppli->bUseXIcon == bUseXIcon)  // nothing to do.
235 		return FALSE;
236 
237 	GList *pElement;
238 	Icon *pAppliIcon;
239 	for (pElement = pClassAppli->pAppliOfClass; pElement != NULL; pElement = pElement->next)
240 	{
241 		pAppliIcon = pElement->data;
242 		if (bUseXIcon)
243 		{
244 			cd_message ("%s: take X icon", pAppliIcon->cName);
245 		}
246 		else
247 		{
248 			cd_message ("%s: doesn't use X icon", pAppliIcon->cName);
249 		}
250 
251 		cairo_dock_reload_icon_image (pAppliIcon, cairo_dock_get_icon_container (pAppliIcon));
252 	}
253 
254 	return TRUE;
255 }
256 
257 
_cairo_dock_set_same_indicator_on_sub_dock(Icon * pInhibhatorIcon)258 static void _cairo_dock_set_same_indicator_on_sub_dock (Icon *pInhibhatorIcon)
259 {
260 	CairoDock *pInhibatorDock = CAIRO_DOCK(cairo_dock_get_icon_container (pInhibhatorIcon));
261 	if (GLDI_OBJECT_IS_DOCK(pInhibatorDock) && pInhibatorDock->iRefCount > 0)  // the inhibitor is in a sub-dock.
262 	{
263 		gboolean bSubDockHasIndicator = FALSE;
264 		if (pInhibhatorIcon->bHasIndicator)
265 		{
266 			bSubDockHasIndicator = TRUE;
267 		}
268 		else
269 		{
270 			GList* ic;
271 			Icon *icon;
272 			for (ic = pInhibatorDock->icons ; ic != NULL; ic = ic->next)
273 			{
274 				icon = ic->data;
275 				if (icon->bHasIndicator)
276 				{
277 					bSubDockHasIndicator = TRUE;
278 					break;
279 				}
280 			}
281 		}
282 		CairoDock *pParentDock = NULL;
283 		Icon *pPointingIcon = cairo_dock_search_icon_pointing_on_dock (pInhibatorDock, &pParentDock);
284 		if (pPointingIcon != NULL && pPointingIcon->bHasIndicator != bSubDockHasIndicator)
285 		{
286 			cd_message ("  for the sub-dock %s : indicator <- %d", pPointingIcon->cName, bSubDockHasIndicator);
287 			pPointingIcon->bHasIndicator = bSubDockHasIndicator;
288 			if (pParentDock != NULL)
289 				cairo_dock_redraw_icon (pPointingIcon);
290 		}
291 	}
292 }
293 
_gldi_appli_icon_detach_of_class(const gchar * cClass)294 static GldiWindowActor *_gldi_appli_icon_detach_of_class (const gchar *cClass)
295 {
296 	g_return_val_if_fail (cClass != NULL, 0);
297 
298 	const GList *pList = cairo_dock_list_existing_appli_with_class (cClass);
299 	Icon *pIcon;
300 	const GList *pElement;
301 	///gboolean bNeedsRedraw = FALSE;
302 	CairoDock *pParentDock;
303 	GldiWindowActor *pFirstFoundActor = NULL;
304 	for (pElement = pList; pElement != NULL; pElement = pElement->next)
305 	{
306 		pIcon = pElement->data;
307 		pParentDock = CAIRO_DOCK(cairo_dock_get_icon_container (pIcon));
308 		if (pParentDock == NULL)  // not in a dock => nothing to do.
309 			continue;
310 
311 		cd_debug ("detachment of the icon %s (%p)", pIcon->cName, pFirstFoundActor);
312 		gldi_icon_detach (pIcon);
313 
314 		// if the icon was in the class sub-dock, check if it became empty
315 		if (pParentDock == cairo_dock_get_class_subdock (cClass))  // the icon was in the class sub-dock
316 		{
317 			if (pParentDock->icons == NULL)  // and it's now empty -> destroy it (and the class-icon pointing on it as well)
318 			{
319 				CairoDock *pMainDock = NULL;
320 				Icon *pPointingIcon = cairo_dock_search_icon_pointing_on_dock (pParentDock, &pMainDock);
321 				/// TODO: register to the destroy event of the class sub-dock...
322 				cairo_dock_destroy_class_subdock (cClass);  // destroy it before the class-icon, since it will destroy the sub-dock
323 				if (pMainDock && CAIRO_DOCK_ICON_TYPE_IS_CLASS_CONTAINER (pPointingIcon))
324 				{
325 					gldi_icon_detach (pPointingIcon);
326 					gldi_object_unref (GLDI_OBJECT(pPointingIcon));
327 				}
328 			}
329 		}
330 
331 		if (pFirstFoundActor == NULL)  // we grab the 1st app of this class.
332 		{
333 			pFirstFoundActor = pIcon->pAppli;
334 		}
335 	}
336 	return pFirstFoundActor;
337 }
cairo_dock_inhibite_class(const gchar * cClass,Icon * pInhibitorIcon)338 gboolean cairo_dock_inhibite_class (const gchar *cClass, Icon *pInhibitorIcon)
339 {
340 	g_return_val_if_fail (cClass != NULL, FALSE);
341 	cd_message ("%s (%s)", __func__, cClass);
342 
343 	// add inhibitor to class (first, so that applis can find it and take its surface if necessary)
344 	if (! _cairo_dock_add_inhibitor_to_class (cClass, pInhibitorIcon))
345 		return FALSE;
346 
347 	// set class name on the inhibitor if not already done.
348 	if (pInhibitorIcon && pInhibitorIcon->cClass != cClass)
349 	{
350 		g_free (pInhibitorIcon->cClass);
351 		pInhibitorIcon->cClass = g_strdup (cClass);
352 	}
353 
354 	// if launchers are mixed with applis, steal applis icons.
355 	if (!myTaskbarParam.bMixLauncherAppli)
356 		return TRUE;
357 	GldiWindowActor *pFirstFoundActor = _gldi_appli_icon_detach_of_class (cClass);  // detach existing applis, and then retach them to the inhibitor.
358 	if (pInhibitorIcon != NULL)
359 	{
360 		// inhibitor takes control of the first existing appli of the class.
361 		gldi_icon_set_appli (pInhibitorIcon, pFirstFoundActor);
362 		pInhibitorIcon->bHasIndicator = (pFirstFoundActor != NULL);
363 		_cairo_dock_set_same_indicator_on_sub_dock (pInhibitorIcon);
364 
365 		// other applis icons are retached to the inhibitor.
366 		const GList *pList = cairo_dock_list_existing_appli_with_class (cClass);
367 		Icon *pIcon;
368 		const GList *pElement;
369 		for (pElement = pList; pElement != NULL; pElement = pElement->next)
370 		{
371 			pIcon = pElement->data;
372 			cd_debug (" an app is detached (%s)", pIcon->cName);
373 			if (pIcon->pAppli != pFirstFoundActor && cairo_dock_get_icon_container (pIcon) == NULL)  // detached and has to be re-attached.
374 				gldi_appli_icon_insert_in_dock (pIcon, g_pMainDock, ! CAIRO_DOCK_ANIMATE_ICON);
375 		}
376 	}
377 
378 	return TRUE;
379 }
380 
cairo_dock_class_is_inhibited(const gchar * cClass)381 gboolean cairo_dock_class_is_inhibited (const gchar *cClass)
382 {
383 	CairoDockClassAppli *pClassAppli = _cairo_dock_lookup_class_appli (cClass);
384 	return (pClassAppli != NULL && pClassAppli->pIconsOfClass != NULL);
385 }
386 
cairo_dock_class_is_using_xicon(const gchar * cClass)387 gboolean cairo_dock_class_is_using_xicon (const gchar *cClass)
388 {
389 	CairoDockClassAppli *pClassAppli = _cairo_dock_lookup_class_appli (cClass);
390 	// if pClassAppli == NULL, there is no launcher able to give its icon but we can found one in icons theme of the system.
391 	return (pClassAppli != NULL && pClassAppli->bUseXIcon);
392 }
393 
cairo_dock_class_is_expanded(const gchar * cClass)394 gboolean cairo_dock_class_is_expanded (const gchar *cClass)
395 {
396 	CairoDockClassAppli *pClassAppli = _cairo_dock_lookup_class_appli (cClass);
397 	return (pClassAppli != NULL && pClassAppli->bExpand);
398 }
399 
cairo_dock_prevent_inhibited_class(Icon * pIcon)400 gboolean cairo_dock_prevent_inhibited_class (Icon *pIcon)
401 {
402 	g_return_val_if_fail (pIcon != NULL, FALSE);
403 	//g_print ("%s (%s)\n", __func__, pIcon->cClass);
404 
405 	gboolean bToBeInhibited = FALSE;
406 	CairoDockClassAppli *pClassAppli = _cairo_dock_lookup_class_appli (pIcon->cClass);
407 	if (pClassAppli != NULL)
408 	{
409 		Icon *pInhibitorIcon;
410 		GList *pElement;
411 		for (pElement = pClassAppli->pIconsOfClass; pElement != NULL; pElement = pElement->next)
412 		{
413 			pInhibitorIcon = pElement->data;
414 			if (pInhibitorIcon != NULL)  // an inhibitor is present.
415 			{
416 				if (pInhibitorIcon->pAppli == NULL && pInhibitorIcon->pSubDock == NULL)  // this icon inhibits this class but doesn't control any apps yet
417 				{
418 					gldi_icon_set_appli (pInhibitorIcon, pIcon->pAppli);
419 					cd_message (">>> %s will take an indicator during the next redraw ! (pAppli : %p)", pInhibitorIcon->cName, pInhibitorIcon->pAppli);
420 					pInhibitorIcon->bHasIndicator = TRUE;
421 					_cairo_dock_set_same_indicator_on_sub_dock (pInhibitorIcon);
422 				/**}
423 
424 				if (pInhibitorIcon->pAppli == pIcon->pAppli)  // this icon controls us.
425 				{*/
426 					CairoDock *pInhibatorDock = CAIRO_DOCK(cairo_dock_get_icon_container (pInhibitorIcon));
427 					//\______________ We place the icon for X.
428 					if (! bToBeInhibited)  // we put the thumbnail only on the 1st one.
429 					{
430 						if (pInhibatorDock != NULL)
431 						{
432 							//g_print ("we move the thumbnail on the inhibitor %s\n", pInhibitorIcon->cName);
433 							gldi_appli_icon_set_geometry_for_window_manager (pInhibitorIcon, pInhibatorDock);
434 						}
435 					}
436 					//\______________ We update inhibitor's label.
437 					if (pInhibatorDock != NULL && pIcon->cName != NULL)
438 					{
439 						if (pInhibitorIcon->cInitialName == NULL)
440 							pInhibitorIcon->cInitialName = pInhibitorIcon->cName;
441 						else
442 							g_free (pInhibitorIcon->cName);
443 						pInhibitorIcon->cName = NULL;
444 						gldi_icon_set_name (pInhibitorIcon, pIcon->cName);
445 					}
446 				}
447 				bToBeInhibited = (pInhibitorIcon->pAppli == pIcon->pAppli);
448 			}
449 		}
450 	}
451 	return bToBeInhibited;
452 }
453 
454 
_cairo_dock_remove_icon_from_class(Icon * pInhibitorIcon)455 static void _cairo_dock_remove_icon_from_class (Icon *pInhibitorIcon)
456 {
457 	g_return_if_fail (pInhibitorIcon != NULL);
458 	cd_message ("%s (%s)", __func__, pInhibitorIcon->cClass);
459 
460 	CairoDockClassAppli *pClassAppli = _cairo_dock_lookup_class_appli (pInhibitorIcon->cClass);
461 	if (pClassAppli != NULL)
462 	{
463 		pClassAppli->pIconsOfClass = g_list_remove (pClassAppli->pIconsOfClass, pInhibitorIcon);
464 	}
465 }
466 
cairo_dock_deinhibite_class(const gchar * cClass,Icon * pInhibitorIcon)467 void cairo_dock_deinhibite_class (const gchar *cClass, Icon *pInhibitorIcon)
468 {
469 	cd_message ("%s (%s)", __func__, cClass);
470 	_cairo_dock_remove_icon_from_class (pInhibitorIcon);
471 
472 	if (pInhibitorIcon != NULL && pInhibitorIcon->pSubDock != NULL && pInhibitorIcon->pSubDock == cairo_dock_get_class_subdock (cClass))  // the launcher is controlling several appli icons, place them back in the taskbar.
473 	{
474 		// first destroy the class sub-dock, so that the appli icons won't go inside again.
475 		// we empty the sub-dock then destroy it, then re-insert the appli icons
476 		GList *icons = pInhibitorIcon->pSubDock->icons;
477 		pInhibitorIcon->pSubDock->icons = NULL;  // empty the sub-dock
478 		cairo_dock_destroy_class_subdock (cClass);  // destroy the sub-dock without destroying its icons
479 		pInhibitorIcon->pSubDock = NULL;  // since the inhibitor can already be detached, the sub-dock can't find it
480 
481 		Icon *pAppliIcon;
482 		GList *ic;
483 		for (ic = icons; ic != NULL; ic = ic->next)
484 		{
485 			pAppliIcon = ic->data;
486 			cairo_dock_set_icon_container (pAppliIcon, NULL);  // manually "detach" it
487 		}
488 
489 		// then re-insert the appli icons.
490 		for (ic = icons; ic != NULL; ic = ic->next)
491 		{
492 			pAppliIcon = ic->data;
493 			gldi_appli_icon_insert_in_dock (pAppliIcon, g_pMainDock, ! CAIRO_DOCK_ANIMATE_ICON);
494 		}
495 		g_list_free (icons);
496 
497 		cairo_dock_trigger_load_icon_buffers (pInhibitorIcon);  // in case the inhibitor was drawn with an emblem or a stack of the applis
498 	}
499 
500 	if (pInhibitorIcon == NULL || pInhibitorIcon->pAppli != NULL)  // the launcher is controlling 1 appli icon, or we deinhibate all the inhibitors.
501 	{
502 		const GList *pList = cairo_dock_list_existing_appli_with_class (cClass);
503 		Icon *pIcon;
504 		///gboolean bNeedsRedraw = FALSE;
505 		///CairoDock *pParentDock;
506 		const GList *pElement;
507 		for (pElement = pList; pElement != NULL; pElement = pElement->next)
508 		{
509 			pIcon = pElement->data;
510 			if (pInhibitorIcon == NULL || pIcon->pAppli == pInhibitorIcon->pAppli)
511 			{
512 				cd_message ("re-add the icon previously inhibited (pAppli:%p)", pIcon->pAppli);
513 				pIcon->fInsertRemoveFactor = 0;
514 				pIcon->fScale = 1.;
515 				/**pParentDock = */
516 				gldi_appli_icon_insert_in_dock (pIcon, g_pMainDock, ! CAIRO_DOCK_ANIMATE_ICON);
517 				///bNeedsRedraw = (pParentDock != NULL && pParentDock->bIsMainDock);
518 			}
519 			///cairo_dock_reload_icon_image (pIcon, cairo_dock_get_icon_container (pIcon));  /// question : why should we do that for all icons?...
520 		}
521 		///if (bNeedsRedraw)
522 		///	gtk_widget_queue_draw (g_pMainDock->container.pWidget);  /// pDock->pRenderer->calculate_icons (pDock); ?...
523 	}
524 	if (pInhibitorIcon != NULL)
525 	{
526 		cd_message (" the inhibitor has lost everything");
527 		gldi_icon_unset_appli (pInhibitorIcon);
528 		pInhibitorIcon->bHasIndicator = FALSE;
529 		g_free (pInhibitorIcon->cClass);
530 		pInhibitorIcon->cClass = NULL;
531 		cd_debug ("  no more classes");
532 	}
533 }
534 
535 
gldi_window_detach_from_inhibitors(GldiWindowActor * pAppli)536 void gldi_window_detach_from_inhibitors (GldiWindowActor *pAppli)
537 {
538 	const gchar *cClass = pAppli->cClass;
539 	cd_message ("%s (%s)", __func__, cClass);
540 	CairoDockClassAppli *pClassAppli = _cairo_dock_lookup_class_appli (cClass);
541 	if (pClassAppli != NULL)
542 	{
543 		GldiWindowActor *pNextAppli = NULL;  // next window that will be inhibited.
544 		gboolean bFirstSearch = TRUE;
545 		Icon *pSameClassIcon = NULL;
546 		Icon *pIcon;
547 		GList *pElement;
548 		for (pElement = pClassAppli->pIconsOfClass; pElement != NULL; pElement = pElement->next)
549 		{
550 			pIcon = pElement->data;
551 			if (pIcon->pAppli == pAppli)  // this inhibitor controls the given window -> make it control another (possibly none).
552 			{
553 				// find the next inhibited appli
554 				if (bFirstSearch)  // we didn't search the next window yet, do it now.
555 				{
556 					bFirstSearch = FALSE;
557 					Icon *pOneIcon;
558 					GList *ic;
559 					for (ic = g_list_last (pClassAppli->pAppliOfClass); ic != NULL; ic = ic->prev)  // reverse order, to take the oldest window of this class.
560 					{
561 						pOneIcon = ic->data;
562 						if (pOneIcon != NULL
563 						&& pOneIcon->pAppli != NULL
564 						&& pOneIcon->pAppli != pAppli  // not the window we precisely want to avoid
565 						&& (! myTaskbarParam.bAppliOnCurrentDesktopOnly || gldi_window_is_on_current_desktop (pOneIcon->pAppli)))  // can actually be displayed
566 						{
567 							pSameClassIcon = pOneIcon;
568 							break ;
569 						}
570 					}
571 					pNextAppli = (pSameClassIcon != NULL ? pSameClassIcon->pAppli : NULL);
572 					if (pSameClassIcon != NULL)  // this icon will be inhibited, we need to detach it if needed
573 					{
574 						cd_message ("  it's %s which will replace it", pSameClassIcon->cName);
575 						gldi_icon_detach (pSameClassIcon);  // it can't be the class sub-dock, because pIcon had the window actor, so it doesn't hold the class sub-dock and the class is not grouped (otherwise they would all be in the class sub-dock).
576 					}
577 				}
578 
579 				// make the icon inhibits the next appli (possibly none)
580 				gldi_icon_set_appli (pIcon, pNextAppli);
581 				pIcon->bHasIndicator = (pNextAppli != NULL);
582 				_cairo_dock_set_same_indicator_on_sub_dock (pIcon);
583 				if (pNextAppli == NULL)
584 					gldi_icon_set_name (pIcon, pIcon->cInitialName);
585 				cd_message (" %s : bHasIndicator <- %d, pAppli <- %p", pIcon->cName, pIcon->bHasIndicator, pIcon->pAppli);
586 
587 				// redraw
588 				GldiContainer *pContainer = cairo_dock_get_icon_container (pIcon);
589 				if (pContainer)
590 					gtk_widget_queue_draw (pContainer->pWidget);
591 			}
592 		}
593 	}
594 }
595 
_cairo_dock_remove_all_applis_from_class(G_GNUC_UNUSED gchar * cClass,CairoDockClassAppli * pClassAppli,G_GNUC_UNUSED gpointer data)596 static void _cairo_dock_remove_all_applis_from_class (G_GNUC_UNUSED gchar *cClass, CairoDockClassAppli *pClassAppli, G_GNUC_UNUSED gpointer data)
597 {
598 	g_list_free (pClassAppli->pAppliOfClass);
599 	pClassAppli->pAppliOfClass = NULL;
600 
601 	Icon *pInhibitorIcon;
602 	GList *pElement;
603 	for (pElement = pClassAppli->pIconsOfClass; pElement != NULL; pElement = pElement->next)
604 	{
605 		pInhibitorIcon = pElement->data;
606 		pInhibitorIcon->bHasIndicator = FALSE;
607 		gldi_icon_unset_appli (pInhibitorIcon);
608 		_cairo_dock_set_same_indicator_on_sub_dock (pInhibitorIcon);
609 	}
610 }
cairo_dock_remove_all_applis_from_class_table(void)611 void cairo_dock_remove_all_applis_from_class_table (void)  // for the stop_application_manager
612 {
613 	g_hash_table_foreach (s_hClassTable, (GHFunc) _cairo_dock_remove_all_applis_from_class, NULL);
614 }
615 
cairo_dock_reset_class_table(void)616 void cairo_dock_reset_class_table (void)
617 {
618 	g_hash_table_remove_all (s_hClassTable);
619 }
620 
621 
cairo_dock_create_surface_from_class(const gchar * cClass,int iWidth,int iHeight)622 cairo_surface_t *cairo_dock_create_surface_from_class (const gchar *cClass, int iWidth, int iHeight)
623 {
624 	cd_debug ("%s (%s)", __func__, cClass);
625 	// first we try to get an icon from one of the inhibitor.
626 	CairoDockClassAppli *pClassAppli = cairo_dock_get_class (cClass);
627 	if (pClassAppli != NULL)
628 	{
629 		cd_debug ("bUseXIcon:%d", pClassAppli->bUseXIcon);
630 		if (pClassAppli->bUseXIcon)
631 			return NULL;
632 
633 		GList *pElement;
634 		Icon *pInhibitorIcon;
635 		for (pElement = pClassAppli->pIconsOfClass; pElement != NULL; pElement = pElement->next)
636 		{
637 			pInhibitorIcon = pElement->data;
638 			cd_debug ("  %s", pInhibitorIcon->cName);
639 			if (! CAIRO_DOCK_ICON_TYPE_IS_APPLET (pInhibitorIcon))
640 			{
641 				if (pInhibitorIcon->pSubDock == NULL || myIndicatorsParam.bUseClassIndic)  // in the case where a launcher has more than one instance of its class and which represents the stack, we doesn't take the icon.
642 				{
643 					cd_debug ("%s will give its surface", pInhibitorIcon->cName);
644 					return cairo_dock_duplicate_surface (pInhibitorIcon->image.pSurface,
645 						pInhibitorIcon->image.iWidth,
646 						pInhibitorIcon->image.iHeight,
647 						iWidth,
648 						iHeight);
649 				}
650 				else if (pInhibitorIcon->cFileName != NULL)
651 				{
652 					gchar *cIconFilePath = cairo_dock_search_icon_s_path (pInhibitorIcon->cFileName, MAX (iWidth, iHeight));
653 					if (cIconFilePath != NULL)
654 					{
655 						cd_debug ("we replace X icon by %s", cIconFilePath);
656 						cairo_surface_t *pSurface = cairo_dock_create_surface_from_image_simple (cIconFilePath,
657 							iWidth,
658 							iHeight);
659 						g_free (cIconFilePath);
660 						if (pSurface)
661 							return pSurface;
662 					}
663 				}
664 			}
665 		}
666 	}
667 
668 	// if we didn't find one, we use the icon defined in the class.
669 	if (pClassAppli != NULL && pClassAppli->cIcon != NULL)
670 	{
671 		cd_debug ("get the class icon (%s)", pClassAppli->cIcon);
672 		gchar *cIconFilePath = cairo_dock_search_icon_s_path (pClassAppli->cIcon, MAX (iWidth, iHeight));
673 		cairo_surface_t *pSurface = cairo_dock_create_surface_from_image_simple (cIconFilePath,
674 			iWidth,
675 			iHeight);
676 		g_free (cIconFilePath);
677 		if (pSurface)
678 			return pSurface;
679 	}
680 	else
681 	{
682 		cd_debug ("no icon for the class %s", cClass);
683 	}
684 
685 	// if not found or not defined, try to find an icon based on the name class.
686 	gchar *cIconFilePath = cairo_dock_search_icon_s_path (cClass, MAX (iWidth, iHeight));
687 	if (cIconFilePath != NULL)
688 	{
689 		cd_debug ("we replace the X icon by %s", cIconFilePath);
690 		cairo_surface_t *pSurface = cairo_dock_create_surface_from_image_simple (cIconFilePath,
691 			iWidth,
692 			iHeight);
693 		g_free (cIconFilePath);
694 		if (pSurface)
695 			return pSurface;
696 	}
697 
698 	cd_debug ("class %s will take the X icon", cClass);
699 	return NULL;
700 }
701 
702 /**
703 void cairo_dock_update_visibility_on_inhibitors (const gchar *cClass, GldiWindowActor *pAppli, gboolean bIsHidden)
704 {
705 	CairoDockClassAppli *pClassAppli = cairo_dock_get_class (cClass);
706 	if (pClassAppli != NULL)
707 	{
708 		GList *pElement;
709 		Icon *pInhibitorIcon;
710 		for (pElement = pClassAppli->pIconsOfClass; pElement != NULL; pElement = pElement->next)
711 		{
712 			pInhibitorIcon = pElement->data;
713 
714 			if (pInhibitorIcon->pAppli == pAppli)
715 			{
716 				cd_debug (" %s also %s itself", pInhibitorIcon->cName, (bIsHidden ? "hide" : "show"));
717 				if (! CAIRO_DOCK_ICON_TYPE_IS_APPLET (pInhibitorIcon) && myTaskbarParam.fVisibleAppliAlpha != 0)
718 				{
719 					pInhibitorIcon->fAlpha = 1;  // we cheat a bit.
720 					cairo_dock_redraw_icon (pInhibitorIcon);
721 				}
722 			}
723 		}
724 	}
725 }
726 
727 void cairo_dock_update_activity_on_inhibitors (const gchar *cClass, GldiWindowActor *pAppli)
728 {
729 	CairoDockClassAppli *pClassAppli = cairo_dock_get_class (cClass);
730 	if (pClassAppli != NULL)
731 	{
732 		GList *pElement;
733 		Icon *pInhibitorIcon;
734 		for (pElement = pClassAppli->pIconsOfClass; pElement != NULL; pElement = pElement->next)
735 		{
736 			pInhibitorIcon = pElement->data;
737 
738 			if (pInhibitorIcon->pAppli == pAppli)
739 			{
740 				cd_debug (" %s becomes active too", pInhibitorIcon->cName);
741 				CairoDock *pParentDock = CAIRO_DOCK(cairo_dock_get_icon_container (pInhibitorIcon));
742 				if (pParentDock != NULL)
743 					gldi_appli_icon_animate_on_active (pInhibitorIcon, pParentDock);
744 			}
745 		}
746 	}
747 }
748 
749 void cairo_dock_update_inactivity_on_inhibitors (const gchar *cClass, GldiWindowActor *pAppli)
750 {
751 	CairoDockClassAppli *pClassAppli = cairo_dock_get_class (cClass);
752 	if (pClassAppli != NULL)
753 	{
754 		GList *pElement;
755 		Icon *pInhibitorIcon;
756 		for (pElement = pClassAppli->pIconsOfClass; pElement != NULL; pElement = pElement->next)
757 		{
758 			pInhibitorIcon = pElement->data;
759 
760 			if (pInhibitorIcon->pAppli == pAppli)
761 			{
762 				cairo_dock_redraw_icon (pInhibitorIcon);
763 			}
764 		}
765 	}
766 }
767 
768 void cairo_dock_update_name_on_inhibitors (const gchar *cClass, GldiWindowActor *actor, gchar *cNewName)
769 {
770 	CairoDockClassAppli *pClassAppli = cairo_dock_get_class (cClass);
771 	if (pClassAppli != NULL)
772 	{
773 		GList *pElement;
774 		Icon *pInhibitorIcon;
775 		for (pElement = pClassAppli->pIconsOfClass; pElement != NULL; pElement = pElement->next)
776 		{
777 			pInhibitorIcon = pElement->data;
778 
779 			if (pInhibitorIcon->pAppli == actor)
780 			{
781 				if (! CAIRO_DOCK_ICON_TYPE_IS_APPLET (pInhibitorIcon))
782 				{
783 					cd_debug (" %s change its name to %s", pInhibitorIcon->cName, cNewName);
784 					if (pInhibitorIcon->cInitialName == NULL)
785 					{
786 						pInhibitorIcon->cInitialName = pInhibitorIcon->cName;
787 						cd_debug ("pInhibitorIcon->cInitialName <- %s", pInhibitorIcon->cInitialName);
788 					}
789 					else
790 						g_free (pInhibitorIcon->cName);
791 					pInhibitorIcon->cName = NULL;
792 
793 					gldi_icon_set_name (pInhibitorIcon, (cNewName != NULL ? cNewName : pInhibitorIcon->cInitialName));
794 				}
795 				cairo_dock_redraw_icon (pInhibitorIcon);
796 			}
797 		}
798 	}
799 }
800 */
gldi_window_foreach_inhibitor(GldiWindowActor * actor,GldiIconRFunc callback,gpointer data)801 void gldi_window_foreach_inhibitor (GldiWindowActor *actor, GldiIconRFunc callback, gpointer data)
802 {
803 	CairoDockClassAppli *pClassAppli = cairo_dock_get_class (actor->cClass);
804 	if (pClassAppli != NULL)
805 	{
806 		Icon *pInhibitorIcon;
807 		GList *ic;
808 		for (ic = pClassAppli->pIconsOfClass; ic != NULL; ic = ic->next)
809 		{
810 			pInhibitorIcon = ic->data;
811 			if (pInhibitorIcon->pAppli == actor)
812 			{
813 				if (! callback (pInhibitorIcon, data))
814 					break;
815 			}
816 		}
817 	}
818 }
819 
820 
cairo_dock_get_classmate(Icon * pIcon)821 Icon *cairo_dock_get_classmate (Icon *pIcon)  // gets an icon of the same class, that is inside a dock (or will be for an inhibitor), but not inside the class sub-dock
822 {
823 	cd_debug ("%s (%s)", __func__, pIcon->cClass);
824 	CairoDockClassAppli *pClassAppli = cairo_dock_get_class (pIcon->cClass);
825 	if (pClassAppli == NULL)
826 		return NULL;
827 
828 	Icon *pFriendIcon = NULL;
829 	GList *pElement;
830 	for (pElement = pClassAppli->pIconsOfClass; pElement != NULL; pElement = pElement->next)
831 	{
832 		pFriendIcon = pElement->data;
833 		/// TODO: this is ugly... maybe an inhibitor shouldn't inhibit when not yet in a dock...
834 		if (pFriendIcon == NULL || (cairo_dock_get_icon_container(pFriendIcon) == NULL && pFriendIcon->cParentDockName == NULL))  // if not inside a dock (for instance a detached applet, but not a hidden launcher), ignore.
835 			continue ;
836 		cd_debug (" friend : %s", pFriendIcon->cName);
837 		if (pFriendIcon->pAppli != NULL || pFriendIcon->pSubDock != NULL)  // is linked to a window, 1 or several times (window actor or class sub-dock).
838 			return pFriendIcon;
839 	}
840 
841 	GldiContainer *pClassSubDock = CAIRO_CONTAINER(cairo_dock_get_class_subdock (pIcon->cClass));
842 	for (pElement = pClassAppli->pAppliOfClass; pElement != NULL; pElement = pElement->next)
843 	{
844 		pFriendIcon = pElement->data;
845 		if (pFriendIcon == pIcon)  // skip ourselves
846 			continue ;
847 
848 		if (cairo_dock_get_icon_container (pFriendIcon) != NULL && cairo_dock_get_icon_container (pFriendIcon) != pClassSubDock)  // inside a dock, but not the class sub-dock
849 			return pFriendIcon;
850 	}
851 
852 	return NULL;
853 }
854 
855 
856 
cairo_dock_check_class_subdock_is_empty(CairoDock * pDock,const gchar * cClass)857 gboolean cairo_dock_check_class_subdock_is_empty (CairoDock *pDock, const gchar *cClass)
858 {
859 	cd_debug ("%s (%s, %d)", __func__, cClass, g_list_length (pDock->icons));
860 	if (pDock->iRefCount == 0)
861 		return FALSE;
862 	if (pDock->icons == NULL)  // shouldn't happen, handle this case and make some noise.
863 	{
864 		cd_warning ("the %s class sub-dock has no element, which is probably an error !\nit will be destroyed.", cClass);
865 		Icon *pFakeClassIcon = cairo_dock_search_icon_pointing_on_dock (pDock, NULL);
866 		cairo_dock_destroy_class_subdock (cClass);
867 		pFakeClassIcon->pSubDock = NULL;
868 		if (CAIRO_DOCK_ICON_TYPE_IS_CLASS_CONTAINER (pFakeClassIcon))
869 		{
870 			gldi_icon_detach (pFakeClassIcon);
871 			gldi_object_unref (GLDI_OBJECT(pFakeClassIcon));
872 		}
873 		return TRUE;
874 	}
875 	else if (pDock->icons->next == NULL)  // only 1 icon left in the sub-dock -> destroy it.
876 	{
877 		cd_debug ("   the sub-dock of the class %s has now only one item in it: will be destroyed", cClass);
878 		Icon *pLastClassIcon = pDock->icons->data;
879 
880 		CairoDock *pFakeParentDock = NULL;
881 		Icon *pFakeClassIcon = cairo_dock_search_icon_pointing_on_dock (pDock, &pFakeParentDock);
882 		g_return_val_if_fail (pFakeClassIcon != NULL, TRUE);
883 
884 		// detach the last icon from the class sub-dock
885 		gboolean bLastIconIsRemoving = cairo_dock_icon_is_being_removed (pLastClassIcon);  // keep the removing state because when we detach the icon, it returns to normal state.
886 		gldi_icon_detach (pLastClassIcon);
887 		pLastClassIcon->fOrder = pFakeClassIcon->fOrder;  // if re-inserted in a dock, insert at the same place
888 
889 		// destroy the class sub-dock
890 		cairo_dock_destroy_class_subdock (cClass);
891 		pFakeClassIcon->pSubDock = NULL;
892 
893 		if (CAIRO_DOCK_ICON_TYPE_IS_CLASS_CONTAINER (pFakeClassIcon))  // the class sub-dock is pointed by a class-icon
894 		{
895 			// destroy the class-icon
896 			gldi_icon_detach (pFakeClassIcon);
897 			gldi_object_unref (GLDI_OBJECT(pFakeClassIcon));
898 
899 			// re-insert the last icon in place of it, or destroy it if it was being removed
900 			if (! bLastIconIsRemoving)
901 			{
902 				gldi_icon_insert_in_container (pLastClassIcon, CAIRO_CONTAINER(pFakeParentDock), ! CAIRO_DOCK_ANIMATE_ICON);
903 			}
904 			else  // the last icon is being removed, no need to re-insert it (e.g. when we close all classes in one it)
905 			{
906 				cd_debug ("no need to re-insert the last icon");
907 				gldi_object_unref (GLDI_OBJECT(pLastClassIcon));
908 			}
909 		}
910 		else  // the class sub-dock is pointed by a launcher/applet
911 		{
912 			// re-inhibit the last icon or destroy it if it was being removed
913 			if (! bLastIconIsRemoving)
914 			{
915 				gldi_appli_icon_insert_in_dock (pLastClassIcon, g_pMainDock, ! CAIRO_DOCK_ANIMATE_ICON);  // Note that we could optimize and manually set the appli and the name...
916 				///cairo_dock_update_name_on_inhibitors (cClass, pLastClassIcon->pAppli, pLastClassIcon->cName);
917 			}
918 			else  // the last icon is being removed, no need to re-insert it
919 			{
920 				pFakeClassIcon->bHasIndicator = FALSE;
921 				gldi_object_unref (GLDI_OBJECT(pLastClassIcon));
922 			}
923 			cairo_dock_redraw_icon (pFakeClassIcon);
924 		}
925 		cd_debug ("no more dock");
926 		return TRUE;
927 	}
928 	return FALSE;
929 }
930 
931 
_cairo_dock_reset_overwrite_exceptions(G_GNUC_UNUSED gchar * cClass,CairoDockClassAppli * pClassAppli,G_GNUC_UNUSED gpointer data)932 static void _cairo_dock_reset_overwrite_exceptions (G_GNUC_UNUSED gchar *cClass, CairoDockClassAppli *pClassAppli, G_GNUC_UNUSED gpointer data)
933 {
934 	pClassAppli->bUseXIcon = FALSE;
935 }
cairo_dock_set_overwrite_exceptions(const gchar * cExceptions)936 void cairo_dock_set_overwrite_exceptions (const gchar *cExceptions)
937 {
938 	g_hash_table_foreach (s_hClassTable, (GHFunc) _cairo_dock_reset_overwrite_exceptions, NULL);
939 	if (cExceptions == NULL)
940 		return;
941 
942 	gchar **cClassList = g_strsplit (cExceptions, ";", -1);
943 	if (cClassList == NULL || cClassList[0] == NULL || *cClassList[0] == '\0')
944 	{
945 		g_strfreev (cClassList);
946 		return;
947 	}
948 	CairoDockClassAppli *pClassAppli;
949 	int i;
950 	for (i = 0; cClassList[i] != NULL; i ++)
951 	{
952 		pClassAppli = cairo_dock_get_class (cClassList[i]);
953 		pClassAppli->bUseXIcon = TRUE;
954 	}
955 
956 	g_strfreev (cClassList);
957 }
958 
_cairo_dock_reset_group_exceptions(G_GNUC_UNUSED gchar * cClass,CairoDockClassAppli * pClassAppli,G_GNUC_UNUSED gpointer data)959 static void _cairo_dock_reset_group_exceptions (G_GNUC_UNUSED gchar *cClass, CairoDockClassAppli *pClassAppli, G_GNUC_UNUSED gpointer data)
960 {
961 	pClassAppli->bExpand = FALSE;
962 }
cairo_dock_set_group_exceptions(const gchar * cExceptions)963 void cairo_dock_set_group_exceptions (const gchar *cExceptions)
964 {
965 	g_hash_table_foreach (s_hClassTable, (GHFunc) _cairo_dock_reset_group_exceptions, NULL);
966 	if (cExceptions == NULL)
967 		return;
968 
969 	gchar **cClassList = g_strsplit (cExceptions, ";", -1);
970 	if (cClassList == NULL || cClassList[0] == NULL || *cClassList[0] == '\0')
971 	{
972 		g_strfreev (cClassList);
973 		return;
974 	}
975 	CairoDockClassAppli *pClassAppli;
976 	int i;
977 	for (i = 0; cClassList[i] != NULL; i ++)
978 	{
979 		pClassAppli = cairo_dock_get_class (cClassList[i]);
980 		pClassAppli->bExpand = TRUE;
981 	}
982 
983 	g_strfreev (cClassList);
984 }
985 
986 
cairo_dock_get_prev_next_classmate_icon(Icon * pIcon,gboolean bNext)987 Icon *cairo_dock_get_prev_next_classmate_icon (Icon *pIcon, gboolean bNext)
988 {
989 	cd_debug ("%s (%s, %s)", __func__, pIcon->cClass, pIcon->cName);
990 	g_return_val_if_fail (pIcon->cClass != NULL, NULL);
991 
992 	Icon *pActiveIcon = cairo_dock_get_current_active_icon ();
993 	if (pActiveIcon == NULL || pActiveIcon->cClass == NULL || strcmp (pActiveIcon->cClass, pIcon->cClass) != 0)  // the active window is not from our class, we active the icon given in parameter.
994 	{
995 		cd_debug ("Active icon's class: %s", pIcon->cClass);
996 		return pIcon;
997 	}
998 
999 	//\________________ We are looking in the class of the active window and take the next or previous one.
1000 	Icon *pNextIcon = NULL;
1001 	CairoDockClassAppli *pClassAppli = cairo_dock_get_class (pIcon->cClass);
1002 	if (pClassAppli == NULL)
1003 		return NULL;
1004 
1005 	//\________________ We are looking in icons of apps.
1006 	Icon *pClassmateIcon;
1007 	GList *pElement, *ic;
1008 	for (pElement = pClassAppli->pAppliOfClass; pElement != NULL && pNextIcon == NULL; pElement = pElement->next)
1009 	{
1010 		pClassmateIcon = pElement->data;
1011 		cd_debug (" %s is it active?", pClassmateIcon->cName);
1012 		if (pClassmateIcon->pAppli == pActiveIcon->pAppli)  // the active window.
1013 		{
1014 			cd_debug ("  found an active window (%s; %p)", pClassmateIcon->cName, pClassmateIcon->pAppli);
1015 			if (bNext)  // take the 1st non null after that.
1016 			{
1017 				ic = pElement;
1018 				do
1019 				{
1020 					ic = cairo_dock_get_next_element (ic, pClassAppli->pAppliOfClass);
1021 					if (ic == pElement)
1022 					{
1023 						cd_debug ("  found nothing!");
1024 						break ;
1025 					}
1026 					pClassmateIcon = ic->data;
1027 					if (pClassmateIcon != NULL && pClassmateIcon->pAppli != NULL)
1028 					{
1029 						cd_debug ("  we take this one (%s; %p)", pClassmateIcon->cName, pClassmateIcon->pAppli);
1030 						pNextIcon = pClassmateIcon;
1031 						break ;
1032 					}
1033 				}
1034 				while (1);
1035 			}
1036 			else  // we take the first non null before it.
1037 			{
1038 				ic = pElement;
1039 				do
1040 				{
1041 					ic = cairo_dock_get_previous_element (ic, pClassAppli->pAppliOfClass);
1042 					if (ic == pElement)
1043 						break ;
1044 					pClassmateIcon = ic->data;
1045 					if (pClassmateIcon != NULL && pClassmateIcon->pAppli != NULL)
1046 					{
1047 						pNextIcon = pClassmateIcon;
1048 						break ;
1049 					}
1050 				}
1051 				while (1);
1052 			}
1053 			break ;
1054 		}
1055 	}
1056 	return pNextIcon;
1057 }
1058 
1059 
1060 
cairo_dock_get_inhibitor(Icon * pIcon,gboolean bOnlyInDock)1061 Icon *cairo_dock_get_inhibitor (Icon *pIcon, gboolean bOnlyInDock)
1062 {
1063 	CairoDockClassAppli *pClassAppli = cairo_dock_get_class (pIcon->cClass);
1064 	if (pClassAppli != NULL)
1065 	{
1066 		GList *pElement;
1067 		Icon *pInhibitorIcon;
1068 		for (pElement = pClassAppli->pIconsOfClass; pElement != NULL; pElement = pElement->next)
1069 		{
1070 			pInhibitorIcon = pElement->data;
1071 
1072 			if (pInhibitorIcon->pAppli == pIcon->pAppli)
1073 			{
1074 				if (! bOnlyInDock || cairo_dock_get_icon_container (pInhibitorIcon) != NULL)
1075 					return pInhibitorIcon;
1076 			}
1077 		}
1078 	}
1079 	return NULL;
1080 }
1081 
1082 /* Not used
1083 static gboolean _appli_is_older (Icon *pIcon1, Icon *pIcon2, CairoDockClassAppli *pClassAppli)  // TRUE if icon1 older than icon2
1084 {
1085 	Icon *pAppliIcon;
1086 	GList *ic;
1087 	for (ic = pClassAppli->pAppliOfClass; ic != NULL; ic = ic->next)
1088 	{
1089 		pAppliIcon = ic->data;
1090 		if (pAppliIcon == pIcon1)  // we found the icon1 first, so icon1 is more recent (prepend).
1091 			return FALSE;
1092 		if (pAppliIcon == pIcon2)  // we found the icon2 first, so icon2 is more recent (prepend).
1093 			return TRUE;
1094 	}
1095 	return FALSE;
1096 }
1097 */
_get_previous_order(GList * ic)1098 static inline double _get_previous_order (GList *ic)
1099 {
1100 	if (ic == NULL)
1101 		return 0;
1102 	double fOrder;
1103 	Icon *icon = ic->data;
1104 	Icon *prev_icon = (ic->prev ? ic->prev->data : NULL);
1105 	if (prev_icon != NULL && cairo_dock_get_icon_order (prev_icon) == cairo_dock_get_icon_order (icon))
1106 
1107 		fOrder = (icon->fOrder + prev_icon->fOrder) / 2;
1108 	else
1109 		fOrder = icon->fOrder - 1;
1110 	return fOrder;
1111 }
_get_next_order(GList * ic)1112 static inline double _get_next_order (GList *ic)
1113 {
1114 	if (ic == NULL)
1115 		return 0;
1116 	double fOrder;
1117 	Icon *icon = ic->data;
1118 	Icon *next_icon = (ic->next ? ic->next->data : NULL);
1119 	if (next_icon != NULL && cairo_dock_get_icon_order (next_icon) == cairo_dock_get_icon_order (icon))
1120 		fOrder = (icon->fOrder + next_icon->fOrder) / 2;
1121 	else
1122 		fOrder = icon->fOrder + 1;
1123 	return fOrder;
1124 }
_get_first_appli_order(CairoDock * pDock,GList * first_launcher_ic,GList * last_launcher_ic)1125 static inline double _get_first_appli_order (CairoDock *pDock, GList *first_launcher_ic, GList *last_launcher_ic)
1126 {
1127 	double fOrder;
1128 	switch (myTaskbarParam.iIconPlacement)
1129 	{
1130 		case CAIRO_APPLI_BEFORE_FIRST_ICON:
1131 			fOrder = _get_previous_order (pDock->icons);
1132 		break;
1133 
1134 		case CAIRO_APPLI_BEFORE_FIRST_LAUNCHER:
1135 			if (first_launcher_ic != NULL)
1136 			{
1137 				//g_print (" go just before the first launcher (%s)\n", ((Icon*)first_launcher_ic->data)->cName);
1138 				fOrder = _get_previous_order (first_launcher_ic);  // 'first_launcher_ic' includes the separators, so we can just take the previous order.
1139 			}
1140 			else  // no launcher, go to the beginning of the dock.
1141 			{
1142 				fOrder = _get_previous_order (pDock->icons);
1143 			}
1144 		break;
1145 
1146 		case CAIRO_APPLI_AFTER_ICON:
1147 		{
1148 			Icon *icon;
1149 			GList *ic = NULL;
1150 			for (ic = pDock->icons; ic != NULL; ic = ic->next)
1151 			{
1152 				icon = ic->data;
1153 				if ((icon->cDesktopFileName != NULL && g_strcmp0 (icon->cDesktopFileName, myTaskbarParam.cRelativeIconName) == 0)
1154 				|| (icon->pModuleInstance && g_strcmp0 (icon->pModuleInstance->cConfFilePath, myTaskbarParam.cRelativeIconName) == 0))
1155 					break;
1156 			}
1157 
1158 			if (ic != NULL)  // icon found
1159 			{
1160 				fOrder = _get_next_order (ic);
1161 				break;
1162 			}  // else don't break, and go to the 'CAIRO_APPLI_AFTER_LAST_LAUNCHER' case, which will be the fallback.
1163 		}
1164 
1165 		case CAIRO_APPLI_AFTER_LAST_LAUNCHER:
1166 		default:
1167 			if (last_launcher_ic != NULL)
1168 			{
1169 				//g_print (" go just after the last launcher (%s)\n", ((Icon*)last_launcher_ic->data)->cName);
1170 				fOrder = _get_next_order (last_launcher_ic);
1171 			}
1172 			else  // no launcher, go to the beginning of the dock.
1173 			{
1174 				fOrder = _get_previous_order (pDock->icons);
1175 			}
1176 		break;
1177 
1178 		case CAIRO_APPLI_AFTER_LAST_ICON:
1179 			fOrder = _get_next_order (g_list_last (pDock->icons));
1180 		break;
1181 	}
1182 	return fOrder;
1183 }
_get_class_age(CairoDockClassAppli * pClassAppli)1184 static inline int _get_class_age (CairoDockClassAppli *pClassAppli)
1185 {
1186 	if (pClassAppli->pAppliOfClass == NULL)
1187 		return 0;
1188 	return pClassAppli->iAge;
1189 }
1190 // Set the order of an appli when they are mixed amongst launchers and no class sub-dock exists (because either they are not grouped by class, or just it's the first appli of this class in the dock)
1191 // First try to see if an inhibitor is present in the dock; if not, see if an appli of the same class is present in the dock.
1192 // -> if yes, place it next to it, ordered by age (go to the right until our age is greater)
1193 // -> if no, place it amongst the other appli icons, ordered by age (search the last launcher, skip any automatic separator, and then go to the right until our age is greater or there is no more appli).
cairo_dock_set_class_order_in_dock(Icon * pIcon,CairoDock * pDock)1194 void cairo_dock_set_class_order_in_dock (Icon *pIcon, CairoDock *pDock)
1195 {
1196 	//g_print ("%s (%s, %d)\n", __func__, pIcon->cClass, pIcon->iAge);
1197 	CairoDockClassAppli *pClassAppli = cairo_dock_get_class (pIcon->cClass);
1198 	g_return_if_fail (pClassAppli != NULL);
1199 
1200 	// Look for an icon of the same class in the dock, to place ourself relatively to it.
1201 	Icon *pSameClassIcon = NULL;
1202 	GList *same_class_ic = NULL;
1203 
1204 	// First look for an inhibitor of this class, preferably a launcher.
1205 	CairoDock *pParentDock;
1206 	Icon *pInhibitorIcon;
1207 	GList *ic;
1208 	for (ic = pClassAppli->pIconsOfClass; ic != NULL; ic = ic->next)
1209 	{
1210 		pInhibitorIcon = ic->data;
1211 
1212 		pParentDock = CAIRO_DOCK(cairo_dock_get_icon_container (pInhibitorIcon));
1213 		if (! GLDI_OBJECT_IS_DOCK(pParentDock))  // not inside a dock, for instance a desklet (or a hidden launcher) -> skip
1214 			continue;
1215 		pSameClassIcon = pInhibitorIcon;
1216 		same_class_ic = ic;
1217 		//g_print (" found an inhibitor of this class: %s (%d)\n", pSameClassIcon->cName, pSameClassIcon->iAge);
1218 		if (CAIRO_DOCK_ICON_TYPE_IS_LAUNCHER (pSameClassIcon))  // it's a launcher, we wont't find better -> quit
1219 			break ;
1220 	}
1221 
1222 	// if no inhibitor found, look for an appli of this class in the dock.
1223 	if (pSameClassIcon == NULL)
1224 	{
1225 		Icon *pAppliIcon;
1226 		for (ic = g_list_last (pClassAppli->pAppliOfClass); ic != NULL; ic = ic->prev)  // check the older icons first (prepend), because then we'll place ourself to their right.
1227 		{
1228 			pAppliIcon = ic->data;
1229 			if (pAppliIcon == pIcon)  // skip ourself
1230 				continue;
1231 			pParentDock = CAIRO_DOCK(cairo_dock_get_icon_container (pAppliIcon));
1232 			if (pParentDock != NULL)
1233 			{
1234 				pSameClassIcon = pAppliIcon;
1235 				same_class_ic = ic;
1236 				//g_print (" found an appli of this class: %s (%d)\n", pSameClassIcon->cName, pSameClassIcon->iAge);
1237 				break ;
1238 			}
1239 		}
1240 		pIcon->iGroup = (myTaskbarParam.bSeparateApplis ? CAIRO_DOCK_APPLI : CAIRO_DOCK_LAUNCHER);  // no inhibitor, so we'll go in the taskbar group.
1241 	}
1242 	else  // an inhibitor is present, we'll go next to it, so we'll be in its group.
1243 	{
1244 		pIcon->iGroup = pSameClassIcon->iGroup;
1245 	}
1246 
1247 	// if we found one, place next to it, ordered by age amongst the other appli of this class already in the dock.
1248 	if (pSameClassIcon != NULL)
1249 	{
1250 		same_class_ic = g_list_find (pDock->icons, pSameClassIcon);
1251 		g_return_if_fail (same_class_ic != NULL);
1252 		Icon *pNextIcon = NULL;  // the next icon after all the icons of our class, or NULL if we reach the end of the dock.
1253 		for (ic = same_class_ic->next; ic != NULL; ic = ic->next)
1254 		{
1255 			pNextIcon = ic->data;
1256 			//g_print ("  next icon: %s (%d)\n", pNextIcon->cName, pNextIcon->iAge);
1257 			if (!pNextIcon->cClass || strcmp (pNextIcon->cClass, pIcon->cClass) != 0)  // not our class any more, quit.
1258 				break;
1259 
1260 			if (pIcon->pAppli->iAge > pNextIcon->pAppli->iAge)  // we are more recent than this icon -> place on its right -> continue
1261 			{
1262 				pSameClassIcon = pNextIcon;  // 'pSameClassIcon' will be the last icon of our class older than us.
1263 				pNextIcon = NULL;
1264 			}
1265 			else  // we are older than it -> go just before it -> quit
1266 			{
1267 				break;
1268 			}
1269 		}
1270 		//g_print (" pNextIcon: %s (%d)\n", pNextIcon?pNextIcon->cName:"none", pNextIcon?pNextIcon->iAge:-1);
1271 
1272 		if (pNextIcon != NULL && cairo_dock_get_icon_order (pNextIcon) == cairo_dock_get_icon_order (pSameClassIcon))  // the next icon is in thge09e same group as us: place it between this icon and pSameClassIcon.
1273 			pIcon->fOrder = (pNextIcon->fOrder + pSameClassIcon->fOrder) / 2;
1274 		else  // no icon after our class or in a different grou: we place just after pSameClassIcon.
1275 			pIcon->fOrder = pSameClassIcon->fOrder + 1;
1276 
1277 		return;
1278 	}
1279 
1280 	// if no icon of our class is present in the dock, place it amongst the other appli icons, after the first appli or after the launchers, and ordered by age.
1281 	// search the last launcher and the first appli.
1282 	Icon *icon;
1283 	Icon *pFirstLauncher = NULL;
1284 	GList *first_appli_ic = NULL, *last_launcher_ic = NULL, *first_launcher_ic = NULL;
1285 	for (ic = pDock->icons; ic != NULL; ic = ic->next)
1286 	{
1287 		icon = ic->data;
1288 		if (CAIRO_DOCK_ICON_TYPE_IS_LAUNCHER (icon)  // launcher, even without class
1289 		|| CAIRO_DOCK_ICON_TYPE_IS_CONTAINER (icon)  // container icon (likely to contain some launchers)
1290 		|| (CAIRO_DOCK_ICON_TYPE_IS_APPLET (icon) && icon->pModuleInstance->pModule->pVisitCard->bActAsLauncher)  // applet acting like a launcher
1291 		/**|| CAIRO_DOCK_ICON_TYPE_IS_SEPARATOR (icon)*/)  // separator (user or auto).
1292 		{
1293 			// pLastLauncher = icon;
1294 			last_launcher_ic = ic;
1295 			if (pFirstLauncher == NULL)
1296 			{
1297 				pFirstLauncher = icon;
1298 				first_launcher_ic = ic;
1299 			}
1300 		}
1301 		else if ((CAIRO_DOCK_ICON_TYPE_IS_APPLI (icon) || CAIRO_DOCK_ICON_TYPE_IS_CLASS_CONTAINER (icon))
1302 		&& ! cairo_dock_class_is_inhibited (icon->cClass))  // an appli not placed next to its inhibitor.
1303 		{
1304 			// pFirstAppli = icon;
1305 			first_appli_ic = ic;
1306 			break ;
1307 		}
1308 	}
1309 	//g_print (" last launcher: %s\n", pLastLauncher?pLastLauncher->cName:"none");
1310 	//g_print (" first appli: %s\n", pFirstAppli?pFirstAppli->cName:"none");
1311 
1312 	// place amongst the other applis, or after the last launcher.
1313 	if (first_appli_ic != NULL)  // if an appli exists in the dock, use it as an anchor.
1314 	{
1315 		int iAge = _get_class_age (pClassAppli);  // the age of our class.
1316 
1317 		GList *last_appli_ic = NULL;  // last appli whose class is older than ours => we'll go just after.
1318 		for (ic = first_appli_ic; ic != NULL; ic = ic->next)
1319 		{
1320 			icon = ic->data;
1321 			if (! CAIRO_DOCK_ICON_TYPE_IS_APPLI (icon) && ! CAIRO_DOCK_ICON_TYPE_IS_CLASS_CONTAINER (icon))
1322 				break;
1323 
1324 			// get the age of this class (= age of the oldest icon of this class)
1325 			CairoDockClassAppli *pOtherClassAppli = _cairo_dock_lookup_class_appli (icon->cClass);
1326 			if (! pOtherClassAppli || ! pOtherClassAppli->pAppliOfClass)  // should never happen
1327 				continue;
1328 
1329 			int iOtherClassAge = _get_class_age (pOtherClassAppli);
1330 			//g_print (" age of class %s: %d\n", icon->cClass, iOtherClassAge);
1331 
1332 			// compare to our class.
1333 			if (iOtherClassAge < iAge)  // it's older than our class -> skip this whole class, we'll go after.
1334 			{
1335 				Icon *next_icon;
1336 				while (ic->next != NULL)
1337 				{
1338 					next_icon = ic->next->data;
1339 					if (next_icon->cClass && strcmp (next_icon->cClass, icon->cClass) == 0)  // next icon is of the same class -> skip
1340 						ic = ic->next;
1341 					else
1342 						break;
1343 				}
1344 				last_appli_ic = ic;
1345 			}
1346 			else  // we are older -> discard and quit.
1347 			{
1348 				break;
1349 			}
1350 		}
1351 
1352 		if (last_appli_ic == NULL)  // we are the oldest class -> go just before the first appli
1353 		{
1354 			//g_print (" we are the oldest class\n");
1355 			pIcon->fOrder = _get_previous_order (first_appli_ic);
1356 		}
1357 		else  // go just after the last one
1358 		{
1359 			//g_print (" go just after %s\n", ((Icon*)last_appli_ic->data)->cName);
1360 			pIcon->fOrder = _get_next_order (last_appli_ic);
1361 		}
1362 	}
1363 	else  // no appli yet in the dock -> place it at the taskbar position defined in conf.
1364 	{
1365 		pIcon->fOrder = _get_first_appli_order (pDock, first_launcher_ic, last_launcher_ic);
1366 	}
1367 }
1368 
cairo_dock_set_class_order_amongst_applis(Icon * pIcon,CairoDock * pDock)1369 void cairo_dock_set_class_order_amongst_applis (Icon *pIcon, CairoDock *pDock)  // set the order of an appli amongst the other applis of a given dock (class sub-dock or main dock).
1370 {
1371 	CairoDockClassAppli *pClassAppli = cairo_dock_get_class (pIcon->cClass);
1372 	g_return_if_fail (pClassAppli != NULL);
1373 
1374 	// place the icon amongst the other appli icons of this class, or after the last appli if none.
1375 	if (myTaskbarParam.bSeparateApplis)
1376 		pIcon->iGroup = CAIRO_DOCK_APPLI;
1377 	else
1378 		pIcon->iGroup = CAIRO_DOCK_LAUNCHER;
1379 	Icon *icon;
1380 	GList *ic, *last_ic = NULL, *first_appli_ic = NULL;
1381 	GList *last_launcher_ic = NULL, *first_launcher_ic = NULL;
1382 	for (ic = pDock->icons; ic != NULL; ic = ic->next)
1383 	{
1384 		icon = ic->data;
1385 		if (CAIRO_DOCK_ICON_TYPE_IS_APPLI (icon))
1386 		{
1387 			if (! first_appli_ic)
1388 				first_appli_ic = ic;
1389 			if (icon->cClass && strcmp (icon->cClass, pIcon->cClass) == 0)  // this icon is in our class.
1390 			{
1391 				if (!icon->pAppli || icon->pAppli->iAge < pIcon->pAppli->iAge)  // it's older than us => we are more recent => go after => continue. (Note: icon->pAppli can be NULL if the icon in the dock is being removed)
1392 				{
1393 					last_ic = ic;  // remember the last item of our class.
1394 				}
1395 				else  // we are older than it => go just before it.
1396 				{
1397 					pIcon->fOrder = _get_previous_order (ic);
1398 					return ;
1399 				}
1400 			}
1401 		}
1402 		else if (CAIRO_DOCK_ICON_TYPE_IS_LAUNCHER (icon)  // launcher, even without class
1403 		|| CAIRO_DOCK_ICON_TYPE_IS_CONTAINER (icon)  // container icon (likely to contain some launchers)
1404 		|| (CAIRO_DOCK_ICON_TYPE_IS_APPLET (icon) && icon->cClass != NULL && icon->pModuleInstance->pModule->pVisitCard->bActAsLauncher)  // applet acting like a launcher
1405 		|| (CAIRO_DOCK_ICON_TYPE_IS_SEPARATOR (icon)))  // separator (user or auto).
1406 		{
1407 			last_launcher_ic = ic;
1408 			if (first_launcher_ic == NULL)
1409 			{
1410 				first_launcher_ic = ic;
1411 			}
1412 		}
1413 	}
1414 
1415 	if (last_ic != NULL)  // there are some applis of our class, but none are more recent than us, so we are the most recent => go just after the last one we found previously.
1416 	{
1417 		pIcon->fOrder = _get_next_order (last_ic);
1418 	}
1419 	else  // we didn't find a single icon of our class => place amongst the other applis from age.
1420 	{
1421 		if (first_appli_ic != NULL)  // if an appli exists in the dock, use it as an anchor.
1422 		{
1423 			Icon *pOldestAppli = g_list_last (pClassAppli->pAppliOfClass)->data;  // prepend
1424 			int iAge = pOldestAppli->pAppli->iAge;  // the age of our class.
1425 
1426 			GList *last_appli_ic = NULL;  // last appli whose class is older than ours => we'll go just after.
1427 			for (ic = first_appli_ic; ic != NULL; ic = ic->next)
1428 			{
1429 				icon = ic->data;
1430 				if (! CAIRO_DOCK_ICON_TYPE_IS_APPLI (icon) && ! CAIRO_DOCK_IS_MULTI_APPLI (icon))
1431 					break;
1432 
1433 				// get the age of this class (= age of the oldest icon of this class)
1434 				CairoDockClassAppli *pOtherClassAppli = _cairo_dock_lookup_class_appli (icon->cClass);
1435 				if (! pOtherClassAppli || ! pOtherClassAppli->pAppliOfClass)  // should never happen
1436 					continue;
1437 
1438 				Icon *pOldestAppli = g_list_last (pOtherClassAppli->pAppliOfClass)->data;  // prepend
1439 
1440 				// compare to our class.
1441 				if (pOldestAppli->pAppli->iAge < iAge)  // it's older than our class -> skip this whole class, we'll go after.
1442 				{
1443 					while (ic->next != NULL)
1444 					{
1445 						icon = ic->next->data;
1446 						if (CAIRO_DOCK_ICON_TYPE_IS_APPLI (icon) && icon->cClass && strcmp (icon->cClass, pOldestAppli->cClass) == 0)  // next icon is an appli of the same class -> skip
1447 							ic = ic->next;
1448 						else
1449 							break;
1450 					}
1451 					last_appli_ic = ic;
1452 				}
1453 				else  // we are older -> discard and quit.
1454 				{
1455 					break;
1456 				}
1457 			}
1458 
1459 			if (last_appli_ic == NULL)  // we are the oldest class -> go just before the first appli
1460 			{
1461 				pIcon->fOrder = _get_previous_order (first_appli_ic);
1462 			}
1463 			else  // go just after the last one
1464 			{
1465 				pIcon->fOrder = _get_next_order (last_appli_ic);
1466 			}
1467 		}
1468 		else  // no appli, use the defined placement.
1469 		{
1470 			pIcon->fOrder = _get_first_appli_order (pDock, first_launcher_ic, last_launcher_ic);
1471 		}
1472 	}
1473 }
1474 
1475 
_get_class_appli_with_attributes(const gchar * cClass)1476 static inline CairoDockClassAppli *_get_class_appli_with_attributes (const gchar *cClass)
1477 {
1478 	CairoDockClassAppli *pClassAppli = cairo_dock_get_class (cClass);
1479 	if (! pClassAppli->bSearchedAttributes)
1480 	{
1481 		gchar *cClass2 = cairo_dock_register_class (cClass);
1482 		g_free (cClass2);
1483 	}
1484 	return pClassAppli;
1485 }
cairo_dock_get_class_command(const gchar * cClass)1486 const gchar *cairo_dock_get_class_command (const gchar *cClass)
1487 {
1488 	g_return_val_if_fail (cClass != NULL, NULL);
1489 	CairoDockClassAppli *pClassAppli = _get_class_appli_with_attributes (cClass);
1490 	return pClassAppli->cCommand;
1491 }
1492 
cairo_dock_get_class_name(const gchar * cClass)1493 const gchar *cairo_dock_get_class_name (const gchar *cClass)
1494 {
1495 	g_return_val_if_fail (cClass != NULL, NULL);
1496 	CairoDockClassAppli *pClassAppli = _get_class_appli_with_attributes (cClass);
1497 	return pClassAppli->cName;
1498 }
1499 
cairo_dock_get_class_mimetypes(const gchar * cClass)1500 const gchar **cairo_dock_get_class_mimetypes (const gchar *cClass)
1501 {
1502 	g_return_val_if_fail (cClass != NULL, NULL);
1503 	CairoDockClassAppli *pClassAppli = _get_class_appli_with_attributes (cClass);
1504 	return (const gchar **)pClassAppli->pMimeTypes;
1505 }
1506 
cairo_dock_get_class_desktop_file(const gchar * cClass)1507 const gchar *cairo_dock_get_class_desktop_file (const gchar *cClass)
1508 {
1509 	g_return_val_if_fail (cClass != NULL, NULL);
1510 	CairoDockClassAppli *pClassAppli = _get_class_appli_with_attributes (cClass);
1511 	return pClassAppli->cDesktopFile;
1512 }
1513 
cairo_dock_get_class_icon(const gchar * cClass)1514 const gchar *cairo_dock_get_class_icon (const gchar *cClass)
1515 {
1516 	g_return_val_if_fail (cClass != NULL, NULL);
1517 	CairoDockClassAppli *pClassAppli = _get_class_appli_with_attributes (cClass);
1518 	return pClassAppli->cIcon;
1519 }
1520 
cairo_dock_get_class_menu_items(const gchar * cClass)1521 const GList *cairo_dock_get_class_menu_items (const gchar *cClass)
1522 {
1523 	g_return_val_if_fail (cClass != NULL, NULL);
1524 	CairoDockClassAppli *pClassAppli = _get_class_appli_with_attributes (cClass);
1525 	return pClassAppli->pMenuItems;
1526 }
1527 
cairo_dock_get_class_wm_class(const gchar * cClass)1528 const gchar *cairo_dock_get_class_wm_class (const gchar *cClass)
1529 {
1530 	g_return_val_if_fail (cClass != NULL, NULL);
1531 	CairoDockClassAppli *pClassAppli = _get_class_appli_with_attributes (cClass);
1532 
1533 	if (pClassAppli->cStartupWMClass == NULL)  // if the WMClass has not been retrieved beforehand, do it now
1534 	{
1535 		cd_debug ("retrieve WMClass for %s...", cClass);
1536 		Icon *pIcon;
1537 		GList *ic;
1538 		for (ic = pClassAppli->pAppliOfClass; ic != NULL; ic = ic->next)
1539 		{
1540 			pIcon = ic->data;
1541 			if (pIcon->pAppli && pIcon->pAppli->cWmClass)
1542 			{
1543 				pClassAppli->cStartupWMClass = g_strdup (pIcon->pAppli->cWmClass);
1544 				break;
1545 			}
1546 		}
1547 	}
1548 
1549 	return pClassAppli->cStartupWMClass;
1550 }
1551 
cairo_dock_get_class_image_buffer(const gchar * cClass)1552 const CairoDockImageBuffer *cairo_dock_get_class_image_buffer (const gchar *cClass)
1553 {
1554 	static CairoDockImageBuffer image;
1555 	g_return_val_if_fail (cClass != NULL, NULL);
1556 	CairoDockClassAppli *pClassAppli = cairo_dock_get_class (cClass);
1557 	Icon *pIcon;
1558 	GList *ic;
1559 	for (ic = pClassAppli->pIconsOfClass; ic != NULL; ic = ic->next)
1560 	{
1561 		pIcon = ic->data;
1562 		if (CAIRO_DOCK_ICON_TYPE_IS_LAUNCHER (pIcon) && pIcon->image.pSurface)  // avoid applets
1563 		{
1564 			memcpy (&image, &pIcon->image, sizeof (CairoDockImageBuffer));
1565 			return &image;
1566 		}
1567 	}
1568 	for (ic = pClassAppli->pAppliOfClass; ic != NULL; ic = ic->next)
1569 	{
1570 		pIcon = ic->data;
1571 		if (pIcon->image.pSurface)
1572 		{
1573 			memcpy (&image, &pIcon->image, sizeof (CairoDockImageBuffer));
1574 			return &image;
1575 		}
1576 	}
1577 
1578 	return NULL;
1579 }
1580 
1581 
_search_desktop_file(const gchar * cDesktopFile)1582 static gchar *_search_desktop_file (const gchar *cDesktopFile)  // file, path or even class
1583 {
1584 	if (cDesktopFile == NULL)
1585 		return NULL;
1586 	if (*cDesktopFile == '/' && g_file_test (cDesktopFile, G_FILE_TEST_EXISTS))  // it's a path and it exists.
1587 	{
1588 		return g_strdup (cDesktopFile);
1589 	}
1590 
1591 	gchar *cDesktopFileName = NULL;
1592 	if (*cDesktopFile == '/')
1593 		cDesktopFileName = g_path_get_basename (cDesktopFile);
1594 	else if (! g_str_has_suffix (cDesktopFile, ".desktop"))
1595 		cDesktopFileName = g_strdup_printf ("%s.desktop", cDesktopFile);
1596 
1597 	const gchar *cFileName = (cDesktopFileName ? cDesktopFileName : cDesktopFile);
1598 	gboolean bFound = TRUE;
1599 	GString *sDesktopFilePath = g_string_new ("");
1600 	g_string_printf (sDesktopFilePath, "/usr/local/share/applications/%s", cFileName);
1601 	if (! g_file_test (sDesktopFilePath->str, G_FILE_TEST_EXISTS))
1602 	{
1603 		g_string_printf (sDesktopFilePath, "/usr/local/share/applications/%c%s", g_ascii_toupper (*cFileName), cFileName+1);  // handle stupid cases like Thunar.desktop
1604 		if (! g_file_test (sDesktopFilePath->str, G_FILE_TEST_EXISTS))
1605 		{
1606 			g_string_printf (sDesktopFilePath, "/usr/local/share/applications/xfce4/%s", cFileName);
1607 			if (! g_file_test (sDesktopFilePath->str, G_FILE_TEST_EXISTS))
1608 			{
1609 				g_string_printf (sDesktopFilePath, "/usr/local/share/applications/kde4/%s", cFileName);
1610 				if (! g_file_test (sDesktopFilePath->str, G_FILE_TEST_EXISTS))
1611 				{
1612 					g_string_printf (sDesktopFilePath, "%s/.local/share/applications/%s", g_getenv ("HOME"), cFileName);
1613 					if (! g_file_test (sDesktopFilePath->str, G_FILE_TEST_EXISTS))
1614 					{
1615 						bFound = FALSE;
1616 					}
1617 				}
1618 			}
1619 		}
1620 	}
1621 	g_free (cDesktopFileName);
1622 
1623 	gchar *cResult;
1624 	if (bFound)
1625 	{
1626 		cResult = sDesktopFilePath->str;
1627 		g_string_free (sDesktopFilePath, FALSE);
1628 	}
1629 	else
1630 	{
1631 		cResult = NULL;
1632 		g_string_free (sDesktopFilePath, TRUE);
1633 	}
1634 	return cResult;
1635 }
1636 
cairo_dock_guess_class(const gchar * cCommand,const gchar * cStartupWMClass)1637 gchar *cairo_dock_guess_class (const gchar *cCommand, const gchar *cStartupWMClass)
1638 {
1639 	// Several cases are possible:
1640 	// Exec=toto
1641 	// Exec=toto-1.2
1642 	// Exec=toto -x -y
1643 	// Exec=/path/to/toto -x -y
1644 	// Exec=gksu nautilus /  (or kdesu)
1645 	// Exec=su-to-root -X -c /usr/sbin/synaptic
1646 	// Exec=gksu --description /usr/local/share/applications/synaptic.desktop /usr/sbin/synaptic
1647 	// Exec=wine "C:\Program Files\Starcraft\Starcraft.exe"
1648 	// Exec=wine "/path/to/prog.exe"
1649 	// Exec=env WINEPREFIX="/home/fab/.wine" wine "C:\Program Files\Starcraft\Starcraft.exe"
1650 
1651 	cd_debug ("%s (%s, '%s')", __func__, cCommand, cStartupWMClass);
1652 	gchar *cResult = NULL;
1653 	if (cStartupWMClass == NULL || *cStartupWMClass == '\0' || strcmp (cStartupWMClass, "Wine") == 0)  // special case for wine, because even if the class is defined as "Wine", this information is non-exploitable.
1654 	{
1655 		if (cCommand == NULL || *cCommand == '\0')
1656 			return NULL;
1657 		gchar *cDefaultClass = g_ascii_strdown (cCommand, -1);
1658 		gchar *str;
1659 		const gchar *cClass = cDefaultClass;  // pointer to the current class.
1660 
1661 		if (strncmp (cClass, "gksu", 4) == 0 || strncmp (cClass, "kdesu", 5) == 0 || strncmp (cClass, "su-to-root", 10) == 0)  // we take the end
1662 		{
1663 			str = (gchar*)cClass + strlen(cClass) - 1;  // last char.
1664 			while (*str == ' ')  // by security, we remove spaces at the end of the line.
1665 				*(str--) = '\0';
1666 			str = strchr (cClass, ' ');  // first whitespace.
1667 			if (str != NULL)  // we are looking after that.
1668 			{
1669 				while (*str == ' ')
1670 					str ++;
1671 				cClass = str;
1672 			}  // we remove gksu, kdesu, etc..
1673 			if (*cClass == '-')  // if it's an option: we need the last param.
1674 			{
1675 				str = strrchr (cClass, ' ');  // last whitespace.
1676 				if (str != NULL)  // we are looking after that.
1677 					cClass = str + 1;
1678 			}
1679 			else  // we can use the first param
1680 			{
1681 				str = strchr (cClass, ' ');  // first whitespace.
1682 				if (str != NULL)  // we remove everything after that
1683 					*str = '\0';
1684 			}
1685 
1686 			str = strrchr (cClass, '/');  // last '/'.
1687 			if (str != NULL)  // remove after that.
1688 				cClass = str + 1;
1689 		}
1690 		else if ((str = g_strstr_len (cClass, -1, "wine ")) != NULL)
1691 		{
1692 			cClass = str;  // class = wine, better than nothing.
1693 			*(str+4) = '\0';
1694 			str += 5;
1695 			while (*str == ' ')  // we remove extra whitespaces.
1696 				str ++;
1697 			// we try to find the executable which is used by wine as res_name.
1698 			gchar *exe = g_strstr_len (str, -1, ".exe");
1699 			if (!exe)
1700 				exe = g_strstr_len (str, -1, ".EXE");
1701 			if (exe)
1702 			{
1703 				*exe = '\0';  // remove the extension.
1704 				gchar *slash = strrchr (str, '\\');
1705 				if (slash)
1706 					cClass = slash+1;
1707 				else
1708 				{
1709 					slash = strrchr (str, '/');
1710 					if (slash)
1711 						cClass = slash+1;
1712 					else
1713 						cClass = str;
1714 				}
1715 			}
1716 			cd_debug ("  special case : wine application => class = '%s'", cClass);
1717 		}
1718 		else
1719 		{
1720 			while (*cClass == ' ')  // by security, remove extra whitespaces.
1721 				cClass ++;
1722 			str = strchr (cClass, ' ');  // first whitespace.
1723 			if (str != NULL)  // remove everything after that
1724 				*str = '\0';
1725 			str = strrchr (cClass, '/');  // last '/'.
1726 			if (str != NULL)  // we take after that.
1727 				cClass = str + 1;
1728 			str = strchr (cClass, '.');  // we remove all .xxx otherwise we can't detect the lack of extension when looking for an icon (openoffice.org) or it's a problem when looking for an icon (jbrout.py).
1729 			if (str != NULL && str != cClass)
1730 				*str = '\0';
1731 		}
1732 
1733 		// handle the cases of programs where command != class.
1734 		if (*cClass != '\0')
1735 		{
1736 			if (strncmp (cClass, "oo", 2) == 0)
1737 			{
1738 				if (strcmp (cClass, "ooffice") == 0 || strcmp (cClass, "oowriter") == 0 || strcmp (cClass, "oocalc") == 0 || strcmp (cClass, "oodraw") == 0 || strcmp (cClass, "ooimpress") == 0)  // openoffice poor design: there is no way to bind its windows to the launcher without this trick.
1739 					cClass = "openoffice";
1740 			}
1741 			else if (strncmp (cClass, "libreoffice", 11) == 0)  // libreoffice has different classes according to the launching option (--writer => libreoffice-writer, --calc => libreoffice-calc, etc)
1742 			{
1743 				gchar *str = strchr (cCommand, ' ');
1744 				if (str && *(str+1) == '-')
1745 				{
1746 					g_free (cDefaultClass);
1747 					cDefaultClass = g_strdup_printf ("%s%s", "libreoffice", str+2);
1748 					str = strchr (cDefaultClass, ' ');  // remove the additionnal params of the command.
1749 					if (str)
1750 						*str = '\0';
1751 					cClass = cDefaultClass;  // "libreoffice-writer"
1752 				}
1753 			}
1754 			cResult = g_strdup (cClass);
1755 		}
1756 		g_free (cDefaultClass);
1757 	}
1758 	else
1759 	{
1760 		cResult = g_ascii_strdown (cStartupWMClass, -1);
1761 		gchar *str = strchr (cResult, '.');  // we remove all .xxx otherwise we can't detect the lack of extension when looking for an icon (openoffice.org) or it's a problem when looking for an icon (jbrout.py).
1762 		if (str != NULL)
1763 			*str = '\0';
1764 	}
1765 	cairo_dock_remove_version_from_string (cResult);
1766 	cd_debug (" -> '%s'", cResult);
1767 
1768 	return cResult;
1769 }
1770 
_add_action_menus(GKeyFile * pKeyFile,CairoDockClassAppli * pClassAppli,const gchar * cGettextDomain,const gchar * cMenuListKey,const gchar * cMenuGroup,gboolean bActionFirstInGroupKey)1771 static void _add_action_menus (GKeyFile *pKeyFile, CairoDockClassAppli *pClassAppli, const gchar *cGettextDomain, const gchar *cMenuListKey, const gchar *cMenuGroup, gboolean bActionFirstInGroupKey)
1772 {
1773 	gsize length = 0;
1774 	gchar **pMenuList = g_key_file_get_string_list (pKeyFile, "Desktop Entry", cMenuListKey, &length, NULL);
1775 	if (pMenuList != NULL)
1776 	{
1777 		gchar *cGroup;
1778 		int i;
1779 		for (i = 0; pMenuList[i] != NULL; i++)
1780 		{
1781 			cGroup = g_strdup_printf ("%s %s",
1782 				bActionFirstInGroupKey ? pMenuList[i] : cMenuGroup,   // [NewWindow Shortcut Group]
1783 				bActionFirstInGroupKey ? cMenuGroup : pMenuList [i]); // [Desktop Action NewWindow]
1784 
1785 			if (g_key_file_has_group (pKeyFile, cGroup))
1786 			{
1787 				gchar **pMenuItem = g_new0 (gchar*, 4);
1788 				// for a few apps, the translations are directly available in the .desktop file (e.g. firefox)
1789 				gchar *cName = g_key_file_get_locale_string (pKeyFile, cGroup, "Name", NULL, NULL);
1790 				pMenuItem[0] = g_strdup (dgettext (cGettextDomain, cName)); // but most of the time, it's available in the .mo file
1791 				g_free (cName);
1792 				gchar *cCommand = g_key_file_get_string (pKeyFile, cGroup, "Exec", NULL);
1793 				if (cCommand != NULL)  // remove the launching options %x.
1794 				{
1795 					gchar *str = strchr (cCommand, '%');  // search the first one.
1796 					if (str != NULL)
1797 					{
1798 						if (str != cCommand && (*(str-1) == '"' || *(str-1) == '\''))  // take care of "" around the option.
1799 							str --;
1800 						*str = '\0';  // not a big deal if there are extras whitespaces at the end
1801 					}
1802 				}
1803 				pMenuItem[1] = cCommand;
1804 				pMenuItem[2] = g_key_file_get_string (pKeyFile, cGroup, "Icon", NULL);
1805 
1806 				pClassAppli->pMenuItems = g_list_append (pClassAppli->pMenuItems, pMenuItem);
1807 			}
1808 			g_free (cGroup);
1809 		}
1810 		g_strfreev (pMenuList);
1811 	}
1812 }
1813 
1814 /*
1815 register from desktop-file name/path (+class-name):
1816   if class-name: guess class -> lookup class -> if already registered => quit
1817   search complete path -> not found => abort
1818   get main info from file (Exec, StartupWMClass)
1819   if class-name NULL: guess class from Exec+StartupWMClass
1820   if already registered => quit
1821   make new class
1822   get additional params from file (MimeType, Icon, etc) and store them in the class
1823 
1824 register from class name (window or old launchers):
1825   guess class -> lookup class -> if already registered => quit
1826   search complete path -> not found => abort
1827   make new class
1828   get additional params from file (MimeType, Icon, etc) and store them in the class
1829 */
cairo_dock_register_class_full(const gchar * cDesktopFile,const gchar * cClassName,const gchar * cWmClass)1830 gchar *cairo_dock_register_class_full (const gchar *cDesktopFile, const gchar *cClassName, const gchar *cWmClass)
1831 {
1832 	g_return_val_if_fail (cDesktopFile != NULL || cClassName != NULL, NULL);
1833 	//g_print ("%s (%s, %s, %s)\n", __func__, cDesktopFile, cClassName, cWmClass);
1834 
1835 	//\__________________ if the class is already registered and filled, quit.
1836 	gchar *cClass = NULL;
1837 	if (cClassName != NULL)
1838 		cClass = cairo_dock_guess_class (NULL, cClassName);
1839 	CairoDockClassAppli *pClassAppli = _cairo_dock_lookup_class_appli (cClass?cClass:cDesktopFile);
1840 
1841 	if (pClassAppli != NULL && pClassAppli->bSearchedAttributes && pClassAppli->cDesktopFile)  // we already searched this class, and we did find its .desktop file, so let's end here.
1842 	{
1843 		//g_print ("class %s already known (%s)\n", cClass?cClass:cDesktopFile, pClassAppli->cDesktopFile);
1844 		if (pClassAppli->cStartupWMClass == NULL && cWmClass != NULL)  // if the cStartupWMClass was not defined in the .desktop file, store it now.
1845 			pClassAppli->cStartupWMClass = g_strdup (cWmClass);
1846 		//g_print ("%s --> %s\n", cClass, pClassAppli->cStartupWMClass);
1847 		return (cClass?cClass:g_strdup (cDesktopFile));
1848 	}
1849 
1850 	//\__________________ search the desktop file's path.
1851 	gchar *cDesktopFilePath = _search_desktop_file (cDesktopFile?cDesktopFile:cClass);
1852 	if (cDesktopFilePath == NULL)  // couldn't find the .desktop
1853 	{
1854 		if (cClass != NULL)  // make a class anyway to store the few info we have.
1855 		{
1856 			if (pClassAppli == NULL)
1857 				pClassAppli = cairo_dock_get_class (cClass);
1858 			if (pClassAppli != NULL)
1859 			{
1860 				if (pClassAppli->cStartupWMClass == NULL && cWmClass != NULL)
1861 					pClassAppli->cStartupWMClass = g_strdup (cWmClass);
1862 				//g_print ("%s ---> %s\n", cClass, pClassAppli->cStartupWMClass);
1863 				pClassAppli->bSearchedAttributes = TRUE;
1864 			}
1865 		}
1866 		cd_debug ("couldn't find the desktop file %s", cDesktopFile?cDesktopFile:cClass);
1867 		return cClass;  /// NULL
1868 	}
1869 
1870 	//\__________________ open it.
1871 	cd_debug ("+ parsing class desktop file %s...", cDesktopFilePath);
1872 	GKeyFile* pKeyFile = cairo_dock_open_key_file (cDesktopFilePath);
1873 	g_return_val_if_fail (pKeyFile != NULL, NULL);
1874 
1875 	//\__________________ guess the class name.
1876 	gchar *cCommand = g_key_file_get_string (pKeyFile, "Desktop Entry", "Exec", NULL);
1877 	gchar *cStartupWMClass = g_key_file_get_string (pKeyFile, "Desktop Entry", "StartupWMClass", NULL);
1878 	if (cStartupWMClass && *cStartupWMClass == '\0')
1879 	{
1880 		g_free (cStartupWMClass);
1881 		cStartupWMClass = NULL;
1882 	}
1883 	if (cClass == NULL)
1884 		cClass = cairo_dock_guess_class (cCommand, cStartupWMClass);
1885 	if (cClass == NULL)
1886 	{
1887 		cd_debug ("couldn't guess the class for %s", cDesktopFile);
1888 		g_free (cDesktopFilePath);
1889 		g_free (cCommand);
1890 		g_free (cStartupWMClass);
1891 		return NULL;
1892 	}
1893 
1894 	//\__________________ make a new class or get the existing one.
1895 	pClassAppli = cairo_dock_get_class (cClass);
1896 	g_return_val_if_fail (pClassAppli!= NULL, NULL);
1897 
1898 	//\__________________ if we already searched and found the attributes beforehand, quit.
1899 	if (pClassAppli->bSearchedAttributes && pClassAppli->cDesktopFile)
1900 	{
1901 		if (pClassAppli->cStartupWMClass == NULL && cWmClass != NULL)  // we already searched this class before, but we couldn't have its WM class.
1902 			pClassAppli->cStartupWMClass = g_strdup (cWmClass);
1903 		//g_print ("%s ----> %s\n", cClass, pClassAppli->cStartupWMClass);
1904 		g_free (cDesktopFilePath);
1905 		g_free (cCommand);
1906 		g_free (cStartupWMClass);
1907 		return cClass;
1908 	}
1909 	pClassAppli->bSearchedAttributes = TRUE;
1910 
1911 	//\__________________ get the attributes.
1912 	pClassAppli->cDesktopFile = cDesktopFilePath;
1913 
1914 	pClassAppli->cName = cairo_dock_get_locale_string_from_conf_file (pKeyFile, "Desktop Entry", "Name", NULL);
1915 
1916 	if (cCommand != NULL)  // remove the launching options %x.
1917 	{
1918 		gchar *str = strchr (cCommand, '%');  // search the first one.
1919 
1920 		if (str && *(str+1) == 'c')  // this one (caption) is the only one that is expected (ex.: kreversi -caption "%c"; if we let '-caption' with nothing after, the appli will melt down); others are either URL or icon that can be empty as per the freedesktop specs, so we can sefely remove them completely from the command line.
1921 		{
1922 			*str = '\0';
1923 			gchar *cmd2 = g_strdup_printf ("%s%s%s", cCommand, pClassAppli->cName, str+2);  // replace %c with the localized name.
1924 			g_free (cCommand);
1925 			cCommand = cmd2;
1926 			str = strchr (cCommand, '%');  // jump to the next one.
1927 		}
1928 
1929 		if (str != NULL)  // remove everything from the first option to the end.
1930 		{
1931 			if (str != cCommand && (*(str-1) == '"' || *(str-1) == '\''))  // take care of "" around the option.
1932 				str --;
1933 			*str = '\0';  // not a big deal if there are extras whitespaces at the end.
1934 		}
1935 	}
1936 	pClassAppli->cCommand = cCommand;
1937 
1938 	if (pClassAppli->cStartupWMClass == NULL)
1939 		pClassAppli->cStartupWMClass = (cStartupWMClass ? cStartupWMClass : g_strdup (cWmClass));
1940 	//g_print ("%s -> pClassAppli->cStartupWMClass: %s\n", cClass, pClassAppli->cStartupWMClass);
1941 
1942 	pClassAppli->cIcon = g_key_file_get_string (pKeyFile, "Desktop Entry", "Icon", NULL);
1943 	if (pClassAppli->cIcon != NULL && *pClassAppli->cIcon != '/')  // remove any extension.
1944 	{
1945 		gchar *str = strrchr (pClassAppli->cIcon, '.');
1946 		if (str && (strcmp (str+1, "png") == 0 || strcmp (str+1, "svg") == 0 || strcmp (str+1, "xpm") == 0))
1947 			*str = '\0';
1948 	}
1949 
1950 	gsize length = 0;
1951 	pClassAppli->pMimeTypes = g_key_file_get_string_list (pKeyFile, "Desktop Entry", "MimeType", &length, NULL);
1952 
1953 	pClassAppli->cWorkingDirectory = g_key_file_get_string (pKeyFile, "Desktop Entry", "Path", NULL);
1954 
1955 	pClassAppli->bHasStartupNotify = g_key_file_get_boolean (pKeyFile, "Desktop Entry", "StartupNotify", NULL);  // let's handle the case StartupNotify=false as if the key was absent (ie: rely on the window events to stop the launching)
1956 
1957 	//\__________________ Gettext domain
1958 	// The translations of the quicklist menus are generally available in a .mo file
1959 	gchar *cGettextDomain = g_key_file_get_string (pKeyFile, "Desktop Entry", "X-Ubuntu-Gettext-Domain", NULL);
1960 	if (cGettextDomain == NULL)
1961 		cGettextDomain = g_key_file_get_string (pKeyFile, "Desktop Entry", "X-GNOME-Gettext-Domain", NULL); // Yes, they like doing that :P
1962 		// a few time ago, it seems that it was 'X-Gettext-Domain'
1963 
1964 	//______________ Quicklist menus.
1965 	_add_action_menus (pKeyFile, pClassAppli, cGettextDomain, "X-Ayatana-Desktop-Shortcuts", "Shortcut Group", TRUE); // oh crap, with a name like that you can be sure it will change 25 times before they decide a definite name :-/
1966 	_add_action_menus (pKeyFile, pClassAppli, cGettextDomain, "Actions", "Desktop Action", FALSE); // yes, it's true ^^ => Ubuntu Quantal
1967 
1968 	g_free (cGettextDomain);
1969 
1970 	g_key_file_free (pKeyFile);
1971 	cd_debug (" -> class '%s'", cClass);
1972 	return cClass;
1973 }
1974 
cairo_dock_set_data_from_class(const gchar * cClass,Icon * pIcon)1975 void cairo_dock_set_data_from_class (const gchar *cClass, Icon *pIcon)
1976 {
1977 	g_return_if_fail (cClass != NULL && pIcon != NULL);
1978 	cd_debug ("%s (%s)", __func__, cClass);
1979 
1980 	CairoDockClassAppli *pClassAppli = _cairo_dock_lookup_class_appli (cClass);
1981 	if (pClassAppli == NULL || ! pClassAppli->bSearchedAttributes)
1982 	{
1983 		cd_debug ("no class %s or no attributes", cClass);
1984 		return;
1985 	}
1986 
1987 	if (pIcon->cCommand == NULL)
1988 		pIcon->cCommand = g_strdup (pClassAppli->cCommand);
1989 
1990 	if (pIcon->cWorkingDirectory == NULL)
1991 		pIcon->cWorkingDirectory = g_strdup (pClassAppli->cWorkingDirectory);
1992 
1993 	if (pIcon->cName == NULL)
1994 		pIcon->cName = g_strdup (pClassAppli->cName);
1995 
1996 	if (pIcon->cFileName == NULL)
1997 		pIcon->cFileName = g_strdup (pClassAppli->cIcon);
1998 
1999 	if (pIcon->pMimeTypes == NULL)
2000 		pIcon->pMimeTypes = g_strdupv ((gchar**)pClassAppli->pMimeTypes);
2001 }
2002 
2003 
2004 
_stop_opening_timeout(const gchar * cClass)2005 static gboolean _stop_opening_timeout (const gchar *cClass)
2006 {
2007 	CairoDockClassAppli *pClassAppli = cairo_dock_get_class (cClass);
2008 	g_return_val_if_fail (pClassAppli != NULL, FALSE);
2009 	pClassAppli->iSidOpeningTimeout = 0;
2010 	gldi_class_startup_notify_end (cClass);
2011 	return FALSE;
2012 }
gldi_class_startup_notify(Icon * pIcon)2013 void gldi_class_startup_notify (Icon *pIcon)
2014 {
2015 	const gchar *cClass = pIcon->cClass;
2016 	CairoDockClassAppli *pClassAppli = cairo_dock_get_class (cClass);
2017 	if (! pClassAppli || pClassAppli->bIsLaunching)
2018 		return;
2019 
2020 	// mark the class as launching and set a timeout
2021 	pClassAppli->bIsLaunching = TRUE;
2022 	if (pClassAppli->iSidOpeningTimeout == 0)
2023 		pClassAppli->iSidOpeningTimeout = g_timeout_add_seconds (15,  // 15 seconds, for applications that take a really long time to start
2024 		(GSourceFunc) _stop_opening_timeout, g_strdup (cClass));  /// TODO: there is a memory leak here...
2025 
2026 	// notify about the startup
2027 	gldi_desktop_notify_startup (cClass);
2028 
2029 	// mark the icon as launching (this is just for convenience for the animations)
2030 	gldi_icon_mark_as_launching (pIcon);
2031 }
2032 
gldi_class_startup_notify_end(const gchar * cClass)2033 void gldi_class_startup_notify_end (const gchar *cClass)
2034 {
2035 	CairoDockClassAppli *pClassAppli = _cairo_dock_lookup_class_appli (cClass);
2036 	if (! pClassAppli || ! pClassAppli->bIsLaunching)
2037 		return;
2038 
2039 	// unset the icons as launching
2040 	GList* ic;
2041 	Icon *icon;
2042 	for (ic = pClassAppli->pIconsOfClass; ic != NULL; ic = ic->next)
2043 	{
2044 		icon = ic->data;
2045 		gldi_icon_stop_marking_as_launching (icon);
2046 	}
2047 	for (ic = pClassAppli->pAppliOfClass; ic != NULL; ic = ic->next)
2048 	{
2049 		icon = ic->data;
2050 		gldi_icon_stop_marking_as_launching (icon);
2051 	}
2052 
2053 	// unset the class as launching and stop a timeout
2054 	pClassAppli->bIsLaunching = FALSE;
2055 	if (pClassAppli->iSidOpeningTimeout != 0)
2056 	{
2057 		g_source_remove (pClassAppli->iSidOpeningTimeout);
2058 		pClassAppli->iSidOpeningTimeout = 0;
2059 	}
2060 }
2061 
gldi_class_is_starting(const gchar * cClass)2062 gboolean gldi_class_is_starting (const gchar *cClass)
2063 {
2064 	CairoDockClassAppli *pClassAppli = _cairo_dock_lookup_class_appli (cClass);
2065 	return (pClassAppli != NULL && pClassAppli->iSidOpeningTimeout != 0);
2066 }
2067