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 <stdlib.h>
21 #include <string.h>
22 #include <math.h>
23 
24 #include "applet-rotation.h"
25 #include "applet-spot.h"
26 #include "applet-struct.h"
27 #include "applet-rays.h"
28 #include "applet-wobbly.h"
29 #include "applet-mesh-factory.h"
30 #include "applet-wave.h"
31 #include "applet-pulse.h"
32 #include "applet-bounce.h"
33 #include "applet-blink.h"
34 #include "applet-busy.h"
35 #include "applet-unfold.h"
36 #include "applet-notifications.h"
37 
38 #define _REFLECT_FADE_NB_STEP 12
39 
40 #define _set_new_data(icon) \
41 	CDAnimationData *pData = CD_APPLET_GET_MY_ICON_DATA (pIcon);\
42 	if (pData == NULL) {\
43 		pData = g_new0 (CDAnimationData, 1);\
44 		CD_APPLET_SET_MY_ICON_DATA (pIcon, pData); }\
45 	else {\
46 		g_free (pData->pBusyImage); pData->pBusyImage = NULL;\
47 		g_list_foreach (pData->pUsedAnimations, (GFunc)g_free, NULL);\
48 		g_list_free (pData->pUsedAnimations); pData->pUsedAnimations = NULL;\
49 		pData->iNumRound = 0;\
50 		pData->bIsUnfolding = FALSE;\
51 		}
52 
_compare_rendering_order(CDCurrentAnimation * pCurrentAnimation1,CDCurrentAnimation * pCurrentAnimation2)53 static int _compare_rendering_order (CDCurrentAnimation *pCurrentAnimation1, CDCurrentAnimation *pCurrentAnimation2)
54 {
55 	if (pCurrentAnimation1->pAnimation->iRenderingOrder < pCurrentAnimation2->pAnimation->iRenderingOrder)
56 		return -1;
57 	else
58 		return 1;
59 }
_cd_animations_start(gpointer pUserData,Icon * pIcon,CairoDock * pDock,CDAnimationsEffects * pAnimations,gboolean * bStartAnimation)60 static void _cd_animations_start (gpointer pUserData, Icon *pIcon, CairoDock *pDock, CDAnimationsEffects *pAnimations, gboolean *bStartAnimation)
61 {
62 	_set_new_data (pIcon);
63 
64 	gboolean bUseOpenGL = CAIRO_DOCK_CONTAINER_IS_OPENGL (CAIRO_CONTAINER (pDock));
65 	double dt = cairo_dock_get_animation_delta_t (CAIRO_CONTAINER (pDock));
66 
67 	// for each animation, check if it's required.
68 	CDAnimationsEffects a;
69 	CDAnimation *pAnimation;
70 	int i;
71 	for (i = 0; pAnimations[i] < CD_ANIMATIONS_NB_EFFECTS; i ++)
72 	{
73 		a = pAnimations[i];
74 		pAnimation = &myData.pAnimations[a];
75 		CDCurrentAnimation *pCurrentAnimation = g_new0 (CDCurrentAnimation, 1);
76 		pCurrentAnimation->pAnimation = pAnimation;
77 		pCurrentAnimation->bIsPlaying = TRUE;
78 		pData->pUsedAnimations = g_list_insert_sorted (pData->pUsedAnimations, pCurrentAnimation, (GCompareFunc)_compare_rendering_order);
79 
80 		if (pAnimation->init)
81 			pAnimation->init (pIcon, pDock, pData, dt, bUseOpenGL);
82 		*bStartAnimation = TRUE;
83 	}
84 }
85 
cd_animations_on_enter(gpointer pUserData,Icon * pIcon,CairoDock * pDock,gboolean * bStartAnimation)86 gboolean cd_animations_on_enter (gpointer pUserData, Icon *pIcon, CairoDock *pDock, gboolean *bStartAnimation)
87 {
88 	if (pIcon->bStatic || ! CAIRO_DOCK_CONTAINER_IS_OPENGL (CAIRO_CONTAINER (pDock)) || pIcon->iAnimationState > CAIRO_DOCK_STATE_MOUSE_HOVERED)
89 		return GLDI_NOTIFICATION_LET_PASS;
90 
91 	if (pIcon->pSubDock && pIcon->iSubdockViewType == 3 && !myDocksParam.bShowSubDockOnClick)  // icone de sous-dock avec rendu de type "box"-> on n'anime pas.
92 	{
93 		//cd_animations_free_data (pUserData, pIcon);
94 		return GLDI_NOTIFICATION_LET_PASS;
95 	}
96 
97 	_cd_animations_start (pUserData, pIcon, pDock, myConfig.iEffectsOnMouseOver, bStartAnimation);
98 
99 	if (bStartAnimation)
100 	{
101 		CDAnimationData *pData = CD_APPLET_GET_MY_ICON_DATA (pIcon);
102 		pData->iNumRound = 0;
103 		cairo_dock_mark_icon_as_hovered_by_mouse (pIcon);
104 	}
105 	return GLDI_NOTIFICATION_LET_PASS;
106 }
107 
cd_animations_on_click(gpointer pUserData,Icon * pIcon,CairoDock * pDock,gint iButtonState)108 gboolean cd_animations_on_click (gpointer pUserData, Icon *pIcon, CairoDock *pDock, gint iButtonState)
109 {
110 	if (! CAIRO_DOCK_IS_DOCK (pDock) || pIcon->iAnimationState > CAIRO_DOCK_STATE_CLICKED)
111 		return GLDI_NOTIFICATION_LET_PASS;
112 
113 	if (pIcon->pSubDock && pIcon->iSubdockViewType == 3)  // icone de sous-dock avec rendu de type "box" -> on arrete l'animation en cours.
114 	{
115 		CDAnimationData *pData = CD_APPLET_GET_MY_ICON_DATA (pIcon);
116 		if (pData && ! pData->bIsUnfolding)
117 			cd_animations_free_data (pUserData, pIcon);  // on arrete l'animation en cours.
118 		return GLDI_NOTIFICATION_LET_PASS;
119 	}
120 
121 	CairoDockIconGroup iType = cairo_dock_get_icon_type (pIcon);
122 	if (iType == CAIRO_DOCK_LAUNCHER && CAIRO_DOCK_IS_APPLI (pIcon) && ! (iButtonState & GDK_SHIFT_MASK))
123 		iType = CAIRO_DOCK_APPLI;
124 
125 	gboolean bStartAnimation = FALSE;
126 	_cd_animations_start (pUserData, pIcon, pDock, myConfig.iEffectsOnClick[iType], &bStartAnimation);
127 	if (bStartAnimation)
128 	{
129 		CDAnimationData *pData = CD_APPLET_GET_MY_ICON_DATA (pIcon);
130 		pData->iNumRound = myConfig.iNbRoundsOnClick[iType] - 1;
131 		cairo_dock_mark_icon_as_clicked (pIcon);
132 	}
133 	return GLDI_NOTIFICATION_LET_PASS;
134 }
135 
_get_animation_from_name(const gchar * cName)136 static inline CDAnimationsEffects _get_animation_from_name (const gchar *cName)
137 {
138 	guint iAnimationID = cairo_dock_get_animation_id (cName);
139 	CDAnimation *pAnimation;
140 	int i;
141 	for (i = 0; i < CD_ANIMATIONS_NB_EFFECTS; i ++)
142 	{
143 		pAnimation = &myData.pAnimations[i];
144 		if (pAnimation->iRegisteredId == iAnimationID)
145 		{
146 			return pAnimation->id;
147 		}
148 	}
149 	return -1;
150 }
cd_animations_on_request(gpointer pUserData,Icon * pIcon,CairoDock * pDock,const gchar * cAnimation,gint iNbRounds)151 gboolean cd_animations_on_request (gpointer pUserData, Icon *pIcon, CairoDock *pDock, const gchar *cAnimation, gint iNbRounds)
152 {
153 	if (cAnimation == NULL || pIcon == NULL || pIcon->iAnimationState > CAIRO_DOCK_STATE_CLICKED)
154 		return GLDI_NOTIFICATION_LET_PASS;
155 
156 	CDAnimationsEffects anim[2] = {0, -1};
157 	if (strcmp (cAnimation, "default") == 0)
158 	{
159 		CairoDockIconGroup iType = cairo_dock_get_icon_type (pIcon);
160 		anim[0] =  myConfig.iEffectsOnClick[iType][0];
161 	}
162 	else
163 	{
164 		anim[0] = _get_animation_from_name (cAnimation);
165 		if (anim[0] >= CD_ANIMATIONS_NB_EFFECTS)  // enums are unsigned int, so >= 0
166 			return GLDI_NOTIFICATION_LET_PASS;
167 	}
168 
169 	gboolean bStartAnimation = FALSE;
170 	_cd_animations_start (pUserData, pIcon, pDock, anim, &bStartAnimation);
171 	if (bStartAnimation)
172 	{
173 		CDAnimationData *pData = CD_APPLET_GET_MY_ICON_DATA (pIcon);
174 		pData->iNumRound = iNbRounds - 1;
175 		cairo_dock_mark_icon_as_hovered_by_mouse (pIcon);
176 	}
177 	return GLDI_NOTIFICATION_LET_PASS;
178 }
179 
180 
cd_animations_post_render_icon(gpointer pUserData,Icon * pIcon,CairoDock * pDock,gboolean * bHasBeenRendered,cairo_t * pCairoContext)181 gboolean cd_animations_post_render_icon (gpointer pUserData, Icon *pIcon, CairoDock *pDock, gboolean *bHasBeenRendered, cairo_t *pCairoContext)
182 {
183 	CDAnimationData *pData = CD_APPLET_GET_MY_ICON_DATA (pIcon);
184 	if (pData == NULL || pData->bIsUnfolding)
185 		return GLDI_NOTIFICATION_LET_PASS;
186 
187 	CDCurrentAnimation *pCurrentAnimation;
188 	CDAnimation *pAnimation;
189 	GList *a;
190 	for (a = pData->pUsedAnimations; a != NULL; a = a->next)
191 	{
192 		pCurrentAnimation = a->data;
193 		if (pCurrentAnimation->bIsPlaying)
194 		{
195 			pAnimation = pCurrentAnimation->pAnimation;
196 			if (pAnimation->post_render)
197 			{
198 				pAnimation->post_render (pIcon, pDock, pData, pCairoContext);
199 			}
200 		}
201 	}
202 
203 	return GLDI_NOTIFICATION_LET_PASS;
204 }
205 
cd_animations_render_icon(gpointer pUserData,Icon * pIcon,CairoDock * pDock,gboolean * bHasBeenRendered,cairo_t * pCairoContext)206 gboolean cd_animations_render_icon (gpointer pUserData, Icon *pIcon, CairoDock *pDock, gboolean *bHasBeenRendered, cairo_t *pCairoContext)
207 {
208 	CDAnimationData *pData = CD_APPLET_GET_MY_ICON_DATA (pIcon);
209 	if (pData == NULL)
210 		return GLDI_NOTIFICATION_LET_PASS;
211 
212 	if (pData->bIsUnfolding && pIcon->pSubDock)
213 	{
214 		if (pCairoContext != NULL)
215 			cd_animations_draw_unfolding_icon_cairo (pIcon, pDock, pData, pCairoContext);
216 		else
217 			cd_animations_draw_unfolding_icon (pIcon, pDock, pData);
218 		*bHasBeenRendered = TRUE;
219 		return GLDI_NOTIFICATION_LET_PASS;
220 	}
221 
222 	pData->bHasBeenPulsed = FALSE;
223 
224 	CDCurrentAnimation *pCurrentAnimation;
225 	CDAnimation *pAnimation;
226 	GList *a;
227 	for (a = pData->pUsedAnimations; a != NULL; a = a->next)
228 	{
229 		pCurrentAnimation = a->data;
230 		if (pCurrentAnimation->bIsPlaying)
231 		{
232 			pAnimation = pCurrentAnimation->pAnimation;
233 			if (pAnimation->render)
234 			{
235 				if (! pAnimation->bDrawIcon || ! (*bHasBeenRendered))  // if the animation draws the icon and the icon has already been drawn, skip.
236 				{
237 					pAnimation->render (pIcon, pDock, pData, pCairoContext);
238 					if (pAnimation->bDrawIcon)
239 						*bHasBeenRendered = TRUE;
240 				}
241 			}
242 		}
243 	}
244 	return GLDI_NOTIFICATION_LET_PASS;
245 }
246 
247 
248 #define _will_continue(bRepeat) ((pData->iNumRound > 0) || (pIcon->iAnimationState == CAIRO_DOCK_STATE_MOUSE_HOVERED && bRepeat && pIcon->bPointed && pDock->container.bInside) || (pIcon->iAnimationState == CAIRO_DOCK_STATE_CLICKED && myConfig.bOpeningAnimation && gldi_icon_is_launching (pIcon)))
249 
cd_animations_update_icon(gpointer pUserData,Icon * pIcon,CairoDock * pDock,gboolean * bContinueAnimation)250 gboolean cd_animations_update_icon (gpointer pUserData, Icon *pIcon, CairoDock *pDock, gboolean *bContinueAnimation)
251 {
252 	CDAnimationData *pData = CD_APPLET_GET_MY_ICON_DATA (pIcon);
253 	if (pData == NULL)
254 		return GLDI_NOTIFICATION_LET_PASS;
255 	gboolean bUseOpenGL = CAIRO_DOCK_CONTAINER_IS_OPENGL (CAIRO_CONTAINER (pDock));
256 	double dt = cairo_dock_get_animation_delta_t (CAIRO_CONTAINER (pDock));
257 
258 	if (pData->bIsUnfolding)
259 	{
260 		if (pIcon->pSubDock->fFoldingFactor == 1 || pIcon->pSubDock == NULL || pIcon->pSubDock->icons == NULL)  // fin du pliage.
261 			pData->bIsUnfolding = FALSE;
262 		else
263 			*bContinueAnimation = TRUE;
264 		cairo_dock_redraw_container (CAIRO_CONTAINER (pDock));  // un peu bourrin ...
265 		return GLDI_NOTIFICATION_LET_PASS;
266 	}
267 
268 	gboolean bIconDrawn = FALSE;
269 	CDCurrentAnimation *pCurrentAnimation;
270 	CDAnimation *pAnimation;
271 	GList *a;
272 	for (a = pData->pUsedAnimations; a != NULL; a = a->next)
273 	{
274 		pCurrentAnimation = a->data;
275 		if (pCurrentAnimation->bIsPlaying)
276 		{
277 			pAnimation = pCurrentAnimation->pAnimation;
278 			if (pCurrentAnimation->bIsPlaying && pAnimation->update && (!bIconDrawn || !pAnimation->bDrawIcon))
279 			{
280 				// make 1 step
281 				gboolean bRepeat = _will_continue (myConfig.bContinue[pAnimation->id]);
282 				pCurrentAnimation->bIsPlaying = pAnimation->update (pIcon, pDock, pData, dt, bUseOpenGL, bRepeat);
283 
284 				// go to next round if repeating
285 				if (! pCurrentAnimation->bIsPlaying && bRepeat)
286 				{
287 					pData->iNumRound --;
288 					pCurrentAnimation->bIsPlaying = TRUE;
289 				}
290 
291 				// continue animation if still playing
292 				if (pCurrentAnimation->bIsPlaying)
293 				{
294 					if (pAnimation->bDrawIcon)
295 						pData->iReflectShadeCount = 0;
296 					*bContinueAnimation = TRUE;
297 				}
298 				else if (bUseOpenGL && pAnimation->bDrawIcon && ! pAnimation->bDrawReflect)
299 				{
300 					pData->iReflectShadeCount = _REFLECT_FADE_NB_STEP;
301 				}
302 
303 				if (pAnimation->bDrawIcon)
304 					bIconDrawn = TRUE;
305 			}
306 		}
307 	}
308 
309 	if (pData->iReflectShadeCount != 0)
310 	{
311 		pData->iReflectShadeCount --;
312 		pIcon->fReflectShading = (double) pData->iReflectShadeCount / _REFLECT_FADE_NB_STEP;
313 		if (pData->iReflectShadeCount != 0)
314 			*bContinueAnimation = TRUE;
315 		cairo_dock_redraw_icon (pIcon);
316 	}
317 
318 	return GLDI_NOTIFICATION_LET_PASS;
319 }
320 
321 
cd_animations_unfold_subdock(gpointer pUserData,Icon * pIcon)322 gboolean cd_animations_unfold_subdock (gpointer pUserData, Icon *pIcon)  // called on start (un)folding.
323 {
324 	if (pIcon == NULL || pIcon->iSubdockViewType != 3)
325 		return GLDI_NOTIFICATION_LET_PASS;
326 
327 	///CairoDock *pDock = cairo_dock_search_dock_from_name (pIcon->cParentDockName);
328 	CairoDock *pDock = CAIRO_DOCK (cairo_dock_get_icon_container (pIcon));
329 	if (pDock != NULL)
330 	{
331 		_set_new_data (pIcon);
332 		pData->bIsUnfolding = TRUE;
333 		cairo_dock_launch_animation (CAIRO_CONTAINER (pDock));
334 	}
335 
336 	return GLDI_NOTIFICATION_LET_PASS;
337 }
338 
339 
cd_animations_free_data(gpointer pUserData,Icon * pIcon)340 gboolean cd_animations_free_data (gpointer pUserData, Icon *pIcon)
341 {
342 	CDAnimationData *pData = CD_APPLET_GET_MY_ICON_DATA (pIcon);
343 	if (pData == NULL)
344 		return GLDI_NOTIFICATION_LET_PASS;
345 
346 	cairo_dock_free_particle_system (pData->pRaysSystem);
347 
348 	g_free (pData->pBusyImage);  // don't delete the content as it is a copy of 'myData.pBusyImage'
349 
350 	pIcon->fReflectShading = 0.;
351 	pIcon->fDeltaYReflection = 0.;
352 
353 	g_free (pData);
354 	CD_APPLET_SET_MY_ICON_DATA (pIcon, NULL);
355 	return GLDI_NOTIFICATION_LET_PASS;
356 }
357 
358 
cd_animations_register_animation(CDAnimation * pAnimation)359 void cd_animations_register_animation (CDAnimation *pAnimation)
360 {
361 	static int n = 0;
362 	pAnimation->iRenderingOrder = n;
363 	pAnimation->iRegisteredId = cairo_dock_register_animation (pAnimation->cName, pAnimation->cDisplayedName, FALSE);
364 	n ++;
365 }
366