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 
23 #include "applet-struct.h"
24 #include "applet-notifications.h"
25 #include "applet-load-icons.h"
26 
27 static void _cd_folders_remove_all_icons (GldiModuleInstance *myApplet);
28 
cd_shortcuts_set_icon_order(Icon * pNewIcon,GList * pIconsList,GCompareFunc comp)29 void cd_shortcuts_set_icon_order (Icon *pNewIcon, GList *pIconsList, GCompareFunc comp)
30 {
31 	if (comp == NULL)
32 		return;
33 	cd_debug ("%s (%s)", __func__, pNewIcon->cName);
34 	// on cherche la 1ere icone du meme type.
35 	GList *ic;
36 	Icon *pIcon;
37 	for (ic = pIconsList; ic != NULL; ic = ic->next)
38 	{
39 		pIcon = ic->data;
40 		if (pIcon->iGroup == pNewIcon->iGroup)
41 			break;
42 	}
43 	GList *ic0 = ic;
44 	if (! ic0)  // si non trouve, on arrete la.
45 	{
46 		pNewIcon->fOrder = 0;
47 		return;
48 	}
49 
50 	pIcon = ic0->data;
51 	if (comp (pNewIcon, pIcon) <= 0)
52 	{
53 		pNewIcon->fOrder = pIcon->fOrder - 1;
54 		cd_debug ("name : %s <= %s -> %.2f", pNewIcon->cName, pIcon->cName, pNewIcon->fOrder);
55 		return;
56 	}
57 
58 	pNewIcon->fOrder = 0;
59 	for (ic = ic0; ic != NULL; ic = ic->next)
60 	{
61 		pIcon = ic->data;
62 		cd_debug ("  compare with %s (%.2f)", pIcon->cName, pIcon->fOrder);
63 		if (pIcon->iGroup != pNewIcon->iGroup)
64 		{
65 			cd_debug ("  type differ, break");
66 			break;
67 		}
68 		if (comp (pNewIcon, pIcon) < 0)
69 		{
70 			if (ic->prev == NULL)
71 				pNewIcon->fOrder = pIcon->fOrder - 1;
72 			else
73 			{
74 				Icon *pPrevIcon = ic->prev->data;
75 				pNewIcon->fOrder = (pIcon->fOrder + pPrevIcon->fOrder) / 2;
76 			}
77 			cd_debug ("  name : %s < %s -> %.2f", pNewIcon->cName, pIcon->cName, pNewIcon->fOrder);
78 			break;
79 		}
80 		pNewIcon->fOrder = pIcon->fOrder + 1;
81 		cd_debug ("  fOrder <- %.2f", pNewIcon->fOrder);
82 	}
83 }
84 
85 
_manage_event_on_file(CairoDockFMEventType iEventType,const gchar * cBaseURI,GList * pIconsList,GldiContainer * pContainer,GldiModuleInstance * myApplet)86 static void _manage_event_on_file (CairoDockFMEventType iEventType, const gchar *cBaseURI, GList *pIconsList, GldiContainer *pContainer, GldiModuleInstance *myApplet)
87 {
88 	if (!cBaseURI)
89 		return;
90 	gchar *cURI = g_strdup (cBaseURI);
91 	cairo_dock_remove_html_spaces (cURI);
92 	cd_debug (" * event %d on '%s'", iEventType, cURI);
93 
94 	if (!myConfig.bShowHiddenFiles)
95 	{
96 		gchar *str = strrchr (cBaseURI, '/');
97 		if (str && *(str+1) == '.')
98 			return;
99 	}
100 
101 	switch (iEventType)
102 	{
103 		case CAIRO_DOCK_FILE_DELETED :  // un fichier a ete supprime (ce peut etre du a un renommage).
104 		{
105 			if (strcmp (myConfig.cDirPath, cBaseURI) == 0)
106 			{
107 				cd_debug ("our folder has been removed");
108 				_cd_folders_remove_all_icons (myApplet);
109 				return;
110 			}
111 
112 			Icon *pConcernedIcon = cairo_dock_get_icon_with_base_uri (pIconsList, cURI);
113 			if (pConcernedIcon == NULL)  // on cherche par nom.
114 			{
115 				pConcernedIcon = cairo_dock_get_icon_with_name (pIconsList, cURI);
116 			}
117 			if (pConcernedIcon == NULL)
118 			{
119 				cd_warning ("  an unknown file was removed");
120 				return ;
121 			}
122 			cd_debug (" %s will be removed", pConcernedIcon->cName);
123 
124 			// on l'enleve du container.
125 			CD_APPLET_REMOVE_ICON_FROM_MY_ICONS_LIST (pConcernedIcon);  // detruit l'icone.
126 		}
127 		break ;
128 
129 		case CAIRO_DOCK_FILE_CREATED :  // un point de montage a ete connecte.
130 		{
131 			if (strcmp (myConfig.cDirPath, cBaseURI) == 0)
132 			{
133 				cd_debug ("our folder has been re-created");
134 				gldi_task_launch (myData.pTask);
135 				return;
136 			}
137 
138 			//\_______________________ on verifie qu'elle n'existe pas deja.
139 			Icon *pSameIcon = cairo_dock_get_icon_with_base_uri (pIconsList, cURI);
140 			if (pSameIcon != NULL)
141 			{
142 				cd_warning ("this file (%s) already exists", pSameIcon->cName);
143 				return;  // on decide de ne rien faire, c'est surement un signal inutile.
144 			}
145 
146 			//\_______________________ on cree une icone pour cette nouvelle URI.
147 			Icon *pNewIcon = cairo_dock_fm_create_icon_from_URI (cURI, pContainer, myConfig.iSortType);
148 			if (pNewIcon == NULL)
149 			{
150 				cd_warning ("couldn't create an icon for this file");
151 				return ;
152 			}
153 			pNewIcon->iGroup = (myConfig.bFoldersFirst && pNewIcon->iVolumeID == -1 ? 6 : 8);
154 
155 			//\_______________________ on la place au bon endroit suivant son nom.
156 			cd_shortcuts_set_icon_order (pNewIcon, pIconsList, myData.comp);
157 			cd_debug (" new file : %s, order = %.2f", pNewIcon->cName, pNewIcon->fOrder);
158 
159 			CD_APPLET_ADD_ICON_IN_MY_ICONS_LIST (pNewIcon);
160 		}
161 		break ;
162 
163 		case CAIRO_DOCK_FILE_MODIFIED :  // un point de montage a ete (de)monte
164 		{
165 			Icon *pConcernedIcon = cairo_dock_get_icon_with_base_uri (pIconsList, cURI);
166 			if (pConcernedIcon == NULL)  // on cherche par nom.
167 			{
168 				pConcernedIcon = cairo_dock_get_icon_with_name (pIconsList, cURI);
169 			}
170 			if (pConcernedIcon == NULL)
171 			{
172 				cd_warning ("  an unknown file was modified");
173 				return ;
174 			}
175 			cd_debug (" %s is modified", pConcernedIcon->cName);
176 
177 			//\_______________________ on recupere les infos actuelles.
178 			Icon *pNewIcon = cairo_dock_fm_create_icon_from_URI (cURI, pContainer, myConfig.iSortType);
179 			if (pNewIcon == NULL)
180 			{
181 				cd_warning ("couldn't create an icon for this file");
182 				return ;
183 			}
184 			pNewIcon->iGroup = (myConfig.bFoldersFirst && pNewIcon->iVolumeID == -1 ? 6 : 8);
185 			double fCurrentOrder = pConcernedIcon->fOrder;
186 			if (myConfig.iSortType == 1 || myConfig.iSortType == 2)  // sort by date or size.
187 				pConcernedIcon->fOrder = pNewIcon->fOrder;
188 
189 			//\_______________________ on gere le changement de nom.
190 			if (cairo_dock_strings_differ (pConcernedIcon->cName, pNewIcon->cName))  // le nom a change.
191 			{
192 				cd_debug ("  name changed : '%s' -> '%s'", pConcernedIcon->cName, pNewIcon->cName);
193 				gldi_icon_set_name (pConcernedIcon, pNewIcon->cName);
194 				cd_shortcuts_set_icon_order (pConcernedIcon, pIconsList, myData.comp);
195 			}
196 
197 			//\_______________________ on gere le changement d'image.
198 			if (cairo_dock_strings_differ (pConcernedIcon->cFileName, pNewIcon->cFileName))
199 			{
200 				cd_debug ("  image changed : '%s' -> '%s'", pConcernedIcon->cFileName, pNewIcon->cFileName);
201 				g_free (pConcernedIcon->cFileName);
202 				pConcernedIcon->cFileName = g_strdup (pNewIcon->cFileName);
203 
204 				if (pConcernedIcon->image.pSurface != NULL)
205 					cairo_dock_load_icon_image (pConcernedIcon, pContainer);
206 			}
207 
208 			//\_______________________ on gere le changement d'ordre (du au changement de nom, d'extension, ou de taille, suivant le classement utilise).
209 			if (pConcernedIcon->fOrder != fCurrentOrder)
210 			{
211 				cd_debug ("  order changed : %.2f -> %.2f", fCurrentOrder, pConcernedIcon->fOrder);
212 
213 				// on la detache.
214 				CD_APPLET_DETACH_ICON_FROM_MY_ICONS_LIST (pConcernedIcon);
215 				pIconsList = CD_APPLET_MY_ICONS_LIST;
216 
217 				CD_APPLET_ADD_ICON_IN_MY_ICONS_LIST (pConcernedIcon);
218 			}
219 			gldi_object_unref (GLDI_OBJECT (pNewIcon));
220 		}
221 		break ;
222 		case CAIRO_DOCK_NB_EVENT_ON_FILES :
223 		break ;
224 	}
225 	g_free (cURI);
226 }
227 
_cd_folders_on_file_event(CairoDockFMEventType iEventType,const gchar * cURI,GldiModuleInstance * myApplet)228 static void _cd_folders_on_file_event (CairoDockFMEventType iEventType, const gchar *cURI, GldiModuleInstance *myApplet)
229 {
230 	g_return_if_fail (cURI != NULL);
231 	CD_APPLET_ENTER;
232 
233 	//\________________ On gere l'evenement sur le fichier.
234 	GList *pIconsList = CD_APPLET_MY_ICONS_LIST;
235 	GldiContainer *pContainer = CD_APPLET_MY_ICONS_LIST_CONTAINER;
236 	CD_APPLET_LEAVE_IF_FAIL (pContainer != NULL);
237 
238 	_manage_event_on_file (iEventType, cURI, pIconsList, pContainer, myApplet);
239 
240 	CD_APPLET_LEAVE();
241 }
242 
243 
_cd_folders_get_data(CDSharedMemory * pSharedMemory)244 static void _cd_folders_get_data (CDSharedMemory *pSharedMemory)
245 {
246 	//\_______________________ On recupere les fichiers.
247 	gchar *cCommand = NULL;
248 	pSharedMemory->pIconList = cairo_dock_fm_list_directory (pSharedMemory->cDirPath, pSharedMemory->iSortType, 8, pSharedMemory->bShowHiddenFiles, 1e4, &cCommand);
249 	g_free (cCommand);
250 
251 	//\_______________________ on classe les icones.
252 	if (pSharedMemory->bFoldersFirst)
253 	{
254 		Icon *pIcon;
255 		GList *ic;
256 		for (ic = pSharedMemory->pIconList; ic != NULL; ic = ic->next)
257 		{
258 			pIcon = ic->data;
259 			if (pIcon->iVolumeID != 0)  // repertoire
260 				pIcon->iGroup = 6;
261 		}
262 	}
263 
264 	if (pSharedMemory->iSortType == 0)  // sort by name
265 	{
266 		pSharedMemory->pIconList = g_list_sort (pSharedMemory->pIconList, (GCompareFunc) cairo_dock_compare_icons_name);
267 	}
268 	else if (pSharedMemory->iSortType == 3)  // sort by type
269 	{
270 		pSharedMemory->pIconList = g_list_sort (pSharedMemory->pIconList, (GCompareFunc) cairo_dock_compare_icons_extension);
271 	}
272 	else  // sort by date or size
273 	{
274 		pSharedMemory->pIconList = g_list_sort (pSharedMemory->pIconList, (GCompareFunc) cairo_dock_compare_icons_order);
275 	}
276 
277 	//g_print ("=== files to display: ===\n");
278 	Icon *pIcon;
279 	int iOrder = 0;
280 	GList *ic;
281 	for (ic = pSharedMemory->pIconList; ic != NULL; ic = ic->next)
282 	{
283 		pIcon = ic->data;
284 		//g_print ("  %s (%d)\n", pIcon->cName, pIcon->iVolumeID);
285 		pIcon->fOrder = iOrder ++;
286 	}
287 }
288 
289 
_cd_folders_load_icons_from_data(CDSharedMemory * pSharedMemory)290 static gboolean _cd_folders_load_icons_from_data (CDSharedMemory *pSharedMemory)
291 {
292 	GldiModuleInstance *myApplet = pSharedMemory->pApplet;
293 	g_return_val_if_fail (myIcon != NULL, FALSE);  // paranoia
294 	CD_APPLET_ENTER;
295 
296 	//\_______________________ On efface l'ancienne liste.
297 	CD_APPLET_DELETE_MY_ICONS_LIST;
298 
299 	//\_______________________ On charge la nouvelle liste.
300 	CD_APPLET_LOAD_MY_ICONS_LIST (pSharedMemory->pIconList, myConfig.cRenderer, "Viewport", NULL);
301 	pSharedMemory->pIconList = NULL;
302 
303 	//\_______________________ On se place en ecoute.
304 	cairo_dock_fm_add_monitor_full (pSharedMemory->cDirPath, TRUE, NULL, (CairoDockFMMonitorCallback) _cd_folders_on_file_event, myApplet);
305 
306 	gldi_task_discard (myData.pTask);
307 	myData.pTask = NULL;
308 	CD_APPLET_LEAVE (TRUE);
309 }
310 
_free_shared_memory(CDSharedMemory * pSharedMemory)311 static void _free_shared_memory (CDSharedMemory *pSharedMemory)
312 {
313 	g_free (pSharedMemory->cDirPath);
314 	g_list_foreach (pSharedMemory->pIconList, (GFunc)g_free, NULL);
315 	g_list_free (pSharedMemory->pIconList);
316 	g_free (pSharedMemory);
317 }
318 
cd_folders_start(GldiModuleInstance * myApplet)319 void cd_folders_start (GldiModuleInstance *myApplet)
320 {
321 	if (myData.pTask != NULL)
322 	{
323 		gldi_task_discard (myData.pTask);
324 		myData.pTask = NULL;
325 	}
326 
327 	CDSharedMemory *pSharedMemory = g_new0 (CDSharedMemory, 1);
328 	pSharedMemory->cDirPath = g_strdup (myConfig.cDirPath);
329 	pSharedMemory->bShowFiles = myConfig.bShowFiles;
330 	pSharedMemory->iSortType = myConfig.iSortType;
331 	pSharedMemory->bFoldersFirst = myConfig.bFoldersFirst;
332 	pSharedMemory->bShowHiddenFiles = myConfig.bShowHiddenFiles;
333 	pSharedMemory->pApplet = myApplet;
334 
335 	myData.pTask = gldi_task_new_full (0,
336 		(GldiGetDataAsyncFunc) _cd_folders_get_data,
337 		(GldiUpdateSyncFunc) _cd_folders_load_icons_from_data,
338 		(GFreeFunc) _free_shared_memory,
339 		pSharedMemory);
340 	gldi_task_launch_delayed (myData.pTask, 0);  // le delai est la pour laisser le temps au backend gvfs de s'initialiser (sinon on a un "g_hash_table_lookup: assertion `hash_table != NULL' failed" lors du listing d'un repertoire, avec en consequence des icones non trouvees).
341 }
342 
343 
344 
345 
_cd_folders_remove_all_icons(GldiModuleInstance * myApplet)346 static void _cd_folders_remove_all_icons (GldiModuleInstance *myApplet)
347 {
348 	//\_______________________ On stoppe la tache.
349 	gldi_task_discard (myData.pTask);
350 	myData.pTask = NULL;
351 
352 	//\_______________________ On detruit ensuite les icones chargees dans le container.
353 	CD_APPLET_DELETE_MY_ICONS_LIST;  // si le container a change entre-temps, le ModuleManager se chargera de nettoyer derriere nous.
354 }
cd_folders_free_all_data(GldiModuleInstance * myApplet)355 void cd_folders_free_all_data (GldiModuleInstance *myApplet)
356 {
357 	//\_______________________ On arrete de surveiller le repertoire.
358 	cairo_dock_fm_remove_monitor_full (myConfig.cDirPath, TRUE, NULL);
359 
360 	_cd_folders_remove_all_icons (myApplet);
361 
362 	cd_folders_free_apps_list (myApplet);
363 }
364 
365 
_get_order(Icon * pIcon,gpointer data)366 static void _get_order (Icon *pIcon, gpointer data)
367 {
368 	CairoDockFMSortType iSortType = GPOINTER_TO_INT (data);
369 	gchar *cName = NULL, *cURI = NULL, *cIconName = NULL;
370 	gboolean bIsDirectory;
371 	int iVolumeID;
372 	double fOrder;
373 	cairo_dock_fm_get_file_info (pIcon->cBaseURI, &cName, &cURI, &cIconName, &bIsDirectory, &iVolumeID, &fOrder, iSortType);
374 	g_free (cName);
375 	g_free (cURI);
376 	g_free (cIconName);
377 	pIcon->fOrder = fOrder;
378 }
379 
cairo_dock_sort_icons_by_extension(GList * pIconList)380 GList *cairo_dock_sort_icons_by_extension (GList *pIconList)
381 {
382 	GList *pSortedIconList = g_list_sort (pIconList, (GCompareFunc) cairo_dock_compare_icons_extension);
383 
384 	guint iCurrentGroup = -1;
385 	double fCurrentOrder = 0.;
386 	Icon *icon;
387 	GList *ic;
388 	for (ic = pIconList; ic != NULL; ic = ic->next)
389 	{
390 		icon = ic->data;
391 		if (icon->iGroup != iCurrentGroup)
392 		{
393 			iCurrentGroup = icon->iGroup;
394 			fCurrentOrder = 0.;
395 		}
396 		icon->fOrder = fCurrentOrder++;
397 	}
398 	return pSortedIconList;
399 }
400 
cd_folders_sort_icons(GldiModuleInstance * myApplet,CairoDockFMSortType iSortType)401 void cd_folders_sort_icons (GldiModuleInstance *myApplet, CairoDockFMSortType iSortType)
402 {
403 	GList *pIconsList = CD_APPLET_MY_ICONS_LIST;
404 	GldiContainer *pContainer = CD_APPLET_MY_ICONS_LIST_CONTAINER;
405 	if (!pIconsList || !pContainer)  // nothing to do.
406 		return;
407 
408 	switch (iSortType)
409 	{
410 		case CAIRO_DOCK_FM_SORT_BY_NAME:
411 			pIconsList = cairo_dock_sort_icons_by_name (pIconsList);
412 		break;
413 		case CAIRO_DOCK_FM_SORT_BY_DATE:
414 			g_list_foreach (pIconsList, (GFunc)_get_order, GINT_TO_POINTER (CAIRO_DOCK_FM_SORT_BY_DATE));
415 			pIconsList = cairo_dock_sort_icons_by_order (pIconsList);
416 		break;
417 		case CAIRO_DOCK_FM_SORT_BY_SIZE:
418 			g_list_foreach (pIconsList, (GFunc)_get_order, GINT_TO_POINTER (CAIRO_DOCK_FM_SORT_BY_SIZE));
419 			pIconsList = cairo_dock_sort_icons_by_order (pIconsList);
420 		break;
421 		case CAIRO_DOCK_FM_SORT_BY_TYPE:
422 			pIconsList = cairo_dock_sort_icons_by_extension (pIconsList);
423 		break;
424 		default:
425 		break;
426 	}
427 
428 	if (myDock)
429 	{
430 		CairoDock *pSubDock = CAIRO_DOCK (pContainer);
431 		pSubDock->icons = pIconsList;
432 		cairo_dock_calculate_dock_icons (pSubDock);
433 		cairo_dock_update_dock_size (pSubDock);
434 	}
435 	else
436 	{
437 		myDesklet->icons = pIconsList;
438 		if (myDesklet->pRenderer && myDesklet->pRenderer->calculate_icons != NULL)
439 			myDesklet->pRenderer->calculate_icons (myDesklet);  // don't use cairo_dock_update_desklet_icons(), since the number of icons didn't change.
440 	}
441 
442 	// redraw
443 	cairo_dock_redraw_container (pContainer);
444 
445 	myConfig.iSortType = iSortType;  // we don't update the conf file, it's a temporary modification.
446 }
447