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