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