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 <stdlib.h>
23
24 #include <gtk/gtk.h>
25
26 #include "gldi-config.h"
27 #include "cairo-dock-draw.h" // cairo_dock_erase_cairo_context
28 #include "cairo-dock-draw-opengl.h"
29 #include "cairo-dock-surface-factory.h"
30 #include "cairo-dock-module-instance-manager.h" // GldiModuleInstance
31 #include "cairo-dock-log.h"
32 #include "cairo-dock-utils.h" // cairo_dock_cut_string
33 #include "cairo-dock-applications-manager.h" // myTaskbarParam.iAppliMaxNameLength
34 #include "cairo-dock-separator-manager.h" // GLDI_OBJECT_IS_SEPARATOR_ICON
35 #include "cairo-dock-dock-facility.h" // cairo_dock_update_dock_size
36 #include "cairo-dock-backends-manager.h" // cairo_dock_get_icon_container_renderer
37 #include "cairo-dock-icon-facility.h"
38 #include "cairo-dock-data-renderer.h"
39 #include "cairo-dock-overlay.h"
40 #include "cairo-dock-icon-factory.h"
41
42 extern CairoDockImageBuffer g_pIconBackgroundBuffer;
43 //extern gboolean g_bUseOpenGL;
44
45 const gchar *s_cRendererNames[4] = {NULL, "Emblem", "Stack", "Box"}; // c'est juste pour realiser la transition entre le chiffre en conf, et un nom (limitation du panneau de conf). On garde le numero pour savoir rapidement sur laquelle on set.
46
47
gldi_icon_new(void)48 Icon *gldi_icon_new (void)
49 {
50 Icon *_icon = (Icon*)gldi_object_new (&myIconObjectMgr, NULL);
51 return _icon;
52 }
53
cairo_dock_create_dummy_launcher(gchar * cName,gchar * cFileName,gchar * cCommand,gchar * cQuickInfo,double fOrder)54 Icon * cairo_dock_create_dummy_launcher (gchar *cName, gchar *cFileName, gchar *cCommand, gchar *cQuickInfo, double fOrder)
55 {
56 //\____________ On cree l'icone.
57 Icon *pIcon = gldi_icon_new ();
58 pIcon->iGroup = CAIRO_DOCK_LAUNCHER;
59 pIcon->cName = cName;
60 pIcon->cFileName = cFileName;
61 pIcon->cQuickInfo = cQuickInfo;
62 pIcon->fOrder = fOrder;
63 pIcon->fScale = 1.;
64 pIcon->fAlpha = 1.;
65 pIcon->fWidthFactor = 1.;
66 pIcon->fHeightFactor = 1.;
67 pIcon->cCommand = cCommand;
68
69 return pIcon;
70 }
71
72
73 //////////////
74 /// LOADER ///
75 //////////////
76
cairo_dock_apply_icon_background_opengl(Icon * icon)77 gboolean cairo_dock_apply_icon_background_opengl (Icon *icon)
78 {
79 if (cairo_dock_begin_draw_icon (icon, 1)) // 1 => don't clear current image
80 {
81 _cairo_dock_enable_texture ();
82 glBlendFunc (GL_ONE_MINUS_DST_ALPHA, GL_ONE); // dest_over = src * (1 - dst.a) + dst
83 _cairo_dock_set_alpha (1.);
84 _cairo_dock_apply_texture_at_size (g_pIconBackgroundBuffer.iTexture,
85 icon->image.iWidth,
86 icon->image.iHeight);
87 cairo_dock_end_draw_icon (icon);
88 return TRUE;
89 }
90 return FALSE;
91 }
92
cairo_dock_load_icon_image(Icon * icon,G_GNUC_UNUSED GldiContainer * pContainer)93 void cairo_dock_load_icon_image (Icon *icon, G_GNUC_UNUSED GldiContainer *pContainer)
94 {
95 if (icon->pContainer == NULL)
96 {
97 cd_warning ("/!\\ Icon %s is not inside a container !!!", icon->cName); // it's ok if this happens, but it should be rare, and I'd like to know when, so be noisy.
98 return;
99 }
100 GldiModuleInstance *pInstance = icon->pModuleInstance; // this is the only function where we destroy/create the icon's surface, so we must handle the cairo-context here.
101 if (pInstance && pInstance->pDrawContext != NULL)
102 {
103 cairo_destroy (pInstance->pDrawContext);
104 pInstance->pDrawContext = NULL;
105 }
106
107 //g_print ("%s (%s, %dx%d)\n", __func__, icon->cName, (int)icon->fWidth, (int)icon->fHeight);
108 // the renderer of the container must have set the size beforehand, when the icon has been inserted into the container.
109 if (cairo_dock_icon_get_allocated_width (icon) <= 0 || cairo_dock_icon_get_allocated_height (icon) <= 0) // we don't want a surface/texture.
110 {
111 cairo_dock_unload_image_buffer (&icon->image);
112 return;
113 }
114 g_return_if_fail (icon->fWidth > 0); // should never happen; if it does, it's an error, so be noisy.
115
116 //\______________ keep the current buffer on the icon so that the 'load' can use it (for instance, applis may draw emblems).
117 cairo_surface_t *pPrevSurface = icon->image.pSurface;
118 GLuint iPrevTexture = icon->image.iTexture;
119
120 //\______________ load the image buffer (surface + texture).
121 if (icon->iface.load_image)
122 icon->iface.load_image (icon);
123
124 //\______________ if nothing has changed or no image was loaded, set a default image.
125 if ((icon->image.pSurface == pPrevSurface || icon->image.pSurface == NULL)
126 && (icon->image.iTexture == iPrevTexture || icon->image.iTexture == 0))
127 {
128 gchar *cIconPath = cairo_dock_search_image_s_path (CAIRO_DOCK_DEFAULT_ICON_NAME);
129 if (cIconPath == NULL) // fichier non trouve.
130 {
131 cIconPath = g_strdup (GLDI_SHARE_DATA_DIR"/icons/"CAIRO_DOCK_DEFAULT_ICON_NAME);
132 }
133 int w = cairo_dock_icon_get_allocated_width (icon);
134 int h = cairo_dock_icon_get_allocated_height (icon);
135 cairo_surface_t *pSurface = cairo_dock_create_surface_from_image_simple (cIconPath,
136 w,
137 h);
138 cairo_dock_load_image_buffer_from_surface (&icon->image, pSurface, w, h);
139 g_free (cIconPath);
140 }
141
142 //\_____________ set the background if needed.
143 icon->bNeedApplyBackground = FALSE;
144 if (g_pIconBackgroundBuffer.pSurface != NULL && ! GLDI_OBJECT_IS_SEPARATOR_ICON (icon))
145 {
146 if (icon->image.iTexture != 0 && g_pIconBackgroundBuffer.iTexture != 0)
147 {
148 if (! cairo_dock_apply_icon_background_opengl (icon)) // couldn't draw on the texture
149 {
150 icon->bDamaged = FALSE; // it's not a big deal, since we can draw under the existing image easily; so we don't need to damage the icon (it's expensive especially if it's an applet).
151 icon->bNeedApplyBackground = TRUE; // just postpone it until drawing is possible.
152 }
153 }
154 else if (icon->image.pSurface != NULL)
155 {
156 cairo_t *pCairoIconBGContext = cairo_create (icon->image.pSurface);
157 cairo_set_operator (pCairoIconBGContext, CAIRO_OPERATOR_DEST_OVER);
158 cairo_dock_apply_image_buffer_surface_at_size (&g_pIconBackgroundBuffer, pCairoIconBGContext,
159 icon->image.iWidth, icon->image.iHeight,
160 0, 0, 1);
161 cairo_destroy (pCairoIconBGContext);
162 }
163 }
164
165 //\______________ free the previous buffers.
166 if (pPrevSurface != NULL)
167 cairo_surface_destroy (pPrevSurface);
168 if (iPrevTexture != 0)
169 _cairo_dock_delete_texture (iPrevTexture);
170
171 if (pInstance && icon->image.pSurface != NULL)
172 {
173 pInstance->pDrawContext = cairo_create (icon->image.pSurface);
174 if (!pInstance->pDrawContext || cairo_status (pInstance->pDrawContext) != CAIRO_STATUS_SUCCESS)
175 {
176 cd_warning ("couldn't initialize drawing context, applet won't be able to draw itself !");
177 pInstance->pDrawContext = NULL;
178 }
179 }
180 }
181
cairo_dock_load_icon_text(Icon * icon)182 void cairo_dock_load_icon_text (Icon *icon)
183 {
184 cairo_dock_unload_image_buffer (&icon->label);
185
186 if (icon->cName == NULL || (myIconsParam.iconTextDescription.iSize == 0))
187 return ;
188
189 gchar *cTruncatedName = NULL;
190 if (CAIRO_DOCK_IS_APPLI (icon) && myTaskbarParam.iAppliMaxNameLength > 0)
191 {
192 cTruncatedName = cairo_dock_cut_string (icon->cName, myTaskbarParam.iAppliMaxNameLength);
193 }
194
195 int iWidth, iHeight;
196 cairo_surface_t *pSurface = cairo_dock_create_surface_from_text ((cTruncatedName != NULL ? cTruncatedName : icon->cName),
197 &myIconsParam.iconTextDescription,
198 &iWidth,
199 &iHeight);
200 cairo_dock_load_image_buffer_from_surface (&icon->label, pSurface, iWidth, iHeight);
201 g_free (cTruncatedName);
202 }
203
cairo_dock_load_icon_quickinfo(Icon * icon)204 void cairo_dock_load_icon_quickinfo (Icon *icon)
205 {
206 if (icon->cQuickInfo == NULL) // no more quick-info -> remove any previous one
207 {
208 cairo_dock_remove_overlay_at_position (icon, CAIRO_OVERLAY_BOTTOM, (gpointer)"quick-info");
209 }
210 else // add an overlay at the bottom with the text surface; any previous "quick-info" overlay will be removed.
211 {
212 int iWidth, iHeight;
213 cairo_dock_get_icon_extent (icon, &iWidth, &iHeight);
214 double fMaxScale = cairo_dock_get_icon_max_scale (icon);
215 if (iHeight / (myIconsParam.quickInfoTextDescription.iSize * fMaxScale) > 5) // if the icon is very height (the text occupies less than 20% of the icon)
216 fMaxScale = MIN ((double)iHeight / (myIconsParam.quickInfoTextDescription.iSize * 5), MAX (1., 16./myIconsParam.quickInfoTextDescription.iSize) * fMaxScale); // let's make it use 20% of the icon's height, limited to 16px
217 int w, h;
218 cairo_surface_t *pSurface = cairo_dock_create_surface_from_text_full (icon->cQuickInfo,
219 &myIconsParam.quickInfoTextDescription,
220 fMaxScale,
221 iWidth, // limit the text to the width of the icon
222 &w, &h);
223 CairoOverlay *pOverlay = cairo_dock_add_overlay_from_surface (icon, pSurface, w, h, CAIRO_OVERLAY_BOTTOM, (gpointer)"quick-info"); // the constant string "quick-info" is used as a unique identifier for all quick-infos; the surface is taken by the overlay.
224 if (pOverlay)
225 cairo_dock_set_overlay_scale (pOverlay, 0);
226 }
227 }
228
229
cairo_dock_load_icon_buffers(Icon * pIcon,GldiContainer * pContainer)230 void cairo_dock_load_icon_buffers (Icon *pIcon, GldiContainer *pContainer)
231 {
232 gboolean bLoadText = TRUE;
233 if (pIcon->iSidLoadImage != 0) // if a load was sheduled, cancel it and do it now (we need to load the applets' buffer before initializing the module).
234 {
235 //g_print (" load %s immediately\n", pIcon->cName);
236 g_source_remove (pIcon->iSidLoadImage);
237 pIcon->iSidLoadImage = 0;
238 bLoadText = FALSE; // has been done in cairo_dock_trigger_load_icon_buffers(), the only function to schedule the image loading.
239 }
240
241 if (cairo_dock_icon_get_allocated_width (pIcon) > 0)
242 {
243 cairo_dock_load_icon_image (pIcon, pContainer);
244
245 if (bLoadText)
246 cairo_dock_load_icon_text (pIcon);
247
248 cairo_dock_load_icon_quickinfo (pIcon);
249 }
250 }
251
_load_icon_buffer_idle(Icon * pIcon)252 static gboolean _load_icon_buffer_idle (Icon *pIcon)
253 {
254 //g_print ("%s (%s; %dx%d; %.2fx%.2f; %x)\n", __func__, pIcon->cName, pIcon->iAllocatedWidth, pIcon->iAllocatedHeight, pIcon->fWidth, pIcon->fHeight, pIcon->pContainer);
255 pIcon->iSidLoadImage = 0;
256
257 GldiContainer *pContainer = pIcon->pContainer;
258 if (pContainer)
259 {
260 cairo_dock_load_icon_image (pIcon, pContainer);
261
262 if (cairo_dock_get_icon_data_renderer (pIcon) != NULL)
263 cairo_dock_refresh_data_renderer (pIcon, pContainer);
264
265 cairo_dock_load_icon_quickinfo (pIcon);
266
267 cairo_dock_redraw_icon (pIcon);
268 //g_print ("icon-factory: do 1 main loop iteration\n");
269 //gtk_main_iteration_do (FALSE); /// "unforseen consequences" : if _redraw_subdock_content_idle is planned just after, the container-icon stays blank in opengl only. couldn't figure why exactly :-/
270 }
271 return FALSE;
272 }
cairo_dock_trigger_load_icon_buffers(Icon * pIcon)273 void cairo_dock_trigger_load_icon_buffers (Icon *pIcon)
274 {
275 if (pIcon->iSidLoadImage == 0)
276 {
277 cairo_dock_load_icon_text (pIcon); // la vue peut avoir besoin de connaitre la taille du texte.
278 pIcon->iSidLoadImage = g_idle_add ((GSourceFunc)_load_icon_buffer_idle, pIcon);
279 }
280 }
281
282
283
284 ///////////////////////
285 /// CONTAINER ICONS ///
286 ///////////////////////
287
cairo_dock_draw_subdock_content_on_icon(Icon * pIcon,CairoDock * pDock)288 void cairo_dock_draw_subdock_content_on_icon (Icon *pIcon, CairoDock *pDock)
289 {
290 g_return_if_fail (pIcon != NULL && pIcon->pSubDock != NULL && (pIcon->image.pSurface != NULL || pIcon->image.iTexture != 0));
291
292 CairoIconContainerRenderer *pRenderer = cairo_dock_get_icon_container_renderer (pIcon->cClass != NULL ? "Stack" : s_cRendererNames[pIcon->iSubdockViewType]);
293 if (pRenderer == NULL)
294 return;
295 cd_debug ("%s (%s)", __func__, pIcon->cName);
296
297 int w, h;
298 cairo_dock_get_icon_extent (pIcon, &w, &h);
299
300 if (pIcon->image.iTexture != 0 && pRenderer->render_opengl) // dessin opengl
301 {
302 //\______________ On efface le dessin existant.
303 if (! cairo_dock_begin_draw_icon (pIcon, 0)) // 0 <=> erase the current texture.
304 return ;
305
306 _cairo_dock_set_blend_alpha ();
307 _cairo_dock_set_alpha (1.);
308 _cairo_dock_enable_texture ();
309
310 //\______________ On dessine les 3 ou 4 premieres icones du sous-dock.
311 pRenderer->render_opengl (pIcon, CAIRO_CONTAINER (pDock), w, h);
312
313 //\______________ On finit le dessin.
314 _cairo_dock_disable_texture ();
315 cairo_dock_end_draw_icon (pIcon);
316 }
317 else if (pIcon->image.pSurface != NULL && pRenderer->render != NULL) // dessin cairo
318 {
319 //\______________ On efface le dessin existant.
320 cairo_t *pCairoContext = cairo_dock_begin_draw_icon_cairo (pIcon, 0, NULL); // 0 <=> erase
321 g_return_if_fail (pCairoContext != NULL);
322
323 //\______________ On dessine les 3 ou 4 premieres icones du sous-dock.
324 pRenderer->render (pIcon, CAIRO_CONTAINER (pDock), w, h, pCairoContext);
325
326 //\______________ On finit le dessin.
327 cairo_dock_end_draw_icon_cairo (pIcon);
328 cairo_destroy (pCairoContext);
329 }
330 }
331
332
gldi_icon_detach(Icon * pIcon)333 void gldi_icon_detach (Icon *pIcon)
334 {
335 GldiContainer *pContainer = cairo_dock_get_icon_container (pIcon);
336 if (! pContainer) // the icon is not in a container -> nothing to do
337 return;
338 pContainer->iface.detach_icon (pContainer, pIcon);
339 cairo_dock_set_icon_container (pIcon, NULL);
340 }
341
gldi_icon_insert_in_container(Icon * pIcon,GldiContainer * pContainer,gboolean bAnimateIcon)342 void gldi_icon_insert_in_container (Icon *pIcon, GldiContainer *pContainer, gboolean bAnimateIcon)
343 {
344 g_return_if_fail (pContainer->iface.insert_icon != NULL); // the container must handle icons
345 if (cairo_dock_get_icon_container (pIcon) != NULL) // the icon must not be in a container yet
346 {
347 cd_warning ("This icon (%s) is already inside a container !", pIcon->cName);
348 return;
349 }
350 cairo_dock_set_icon_container (pIcon, pContainer); // set the container already, the icon might need it to set its size.
351 pContainer->iface.insert_icon (pContainer, pIcon, bAnimateIcon);
352 }
353