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 <glib/gi18n.h>
23 
24 #include "applet-struct.h"
25 #include "applet-load-icons.h"
26 #include "applet-drives.h"
27 #include "applet-bookmarks.h"
28 
29 #define CD_SHORTCUT_DEFAULT_DIRECTORY_ICON_FILENAME "inode-directory"
30 
_get_custom_name_and_uri(gchar * cOneBookmark,gchar ** cURI)31 static const gchar * _get_custom_name_and_uri (gchar *cOneBookmark, gchar **cURI)
32 {
33 	const gchar *cUserName = NULL;
34 	// should not happen if we add bookmarks via the dock or Nautilus
35 	if (*cOneBookmark == '/')
36 	{
37 		// for 'gvfs_launch_uri':
38 		*cURI = g_strconcat ("file://", cOneBookmark, NULL);
39 		g_free (cOneBookmark);
40 	}
41 	else  // it's a valid URI but does it have a custom name?
42 	{
43 		*cURI = cOneBookmark;
44 		// a custom name is separated with a whitespace (no whitespace in the URI)
45 		gchar *str = strchr (cOneBookmark, ' ');
46 		if (str != NULL)
47 		{
48 			cUserName = str + 1;
49 			*str = '\0';
50 		}
51 	}
52 	return cUserName;
53 }
54 
_cd_shortcuts_get_icon(gchar * cFileName,const gchar * cUserName,double fCurrentOrder)55 static Icon * _cd_shortcuts_get_icon (gchar *cFileName, const gchar *cUserName, double fCurrentOrder)
56 {
57 	cd_debug ("New icon: %s, %s, %f", cFileName, cUserName, fCurrentOrder);
58 
59 	/* Nautilus adds custom prefixes which are not supported by gvfs...
60 	 * gvfs-integration plugin can read x-nautilus-desktop but not others, e.g.:
61 	 * x-nautilus-search://0/ => specific to Nautilus: open these URI with it.
62 	 * Note that all these URI have a user-name
63 	 */
64 	if (g_str_has_prefix (cFileName, "x-nautilus-")
65 	    && ! g_str_has_prefix (cFileName, "x-nautilus-desktop://"))
66 	{
67 		Icon *pNewIcon = cairo_dock_create_dummy_launcher (
68 			cUserName ? g_strdup (cUserName) : g_strdup (cFileName),
69 			cairo_dock_search_icon_s_path (
70 				CD_SHORTCUT_DEFAULT_DIRECTORY_ICON_FILENAME,
71 				CAIRO_DOCK_DEFAULT_ICON_SIZE),
72 			g_strdup_printf ("nautilus %s", cFileName),
73 			NULL,
74 			fCurrentOrder);
75 		pNewIcon->iGroup = CD_BOOKMARK_GROUP;
76 		pNewIcon->cBaseURI = cFileName;
77 		pNewIcon->iVolumeID = CD_VOLUME_ID_BOOKMARK_CMD;
78 		return pNewIcon;
79 	}
80 
81 	gchar *cName, *cRealURI, *cIconName;
82 	gboolean bIsDirectory;
83 	gint iVolumeID;
84 	gdouble fOrder;
85 	if (! cairo_dock_fm_get_file_info (cFileName, &cName, &cRealURI, &cIconName,
86 		&bIsDirectory, &iVolumeID, &fOrder, CAIRO_DOCK_FM_SORT_BY_NAME))
87 		return NULL;
88 	if (cUserName != NULL)
89 	{
90 		g_free (cName);
91 		if (cName == NULL)  // a bookmark on a unmounted system or a folder that doesn't exist any more
92 			cName = g_strdup_printf ("%s\n[%s]", cUserName, D_("Unmounted"));
93 		else
94 			cName = g_strdup (cUserName);
95 	}
96 	else if (cName == NULL)  // a bookmark on a unmounted system
97 	{
98 		gchar *cGuessedName = g_path_get_basename (cFileName);
99 		cairo_dock_remove_html_spaces (cGuessedName); // or: g_uri_unescape_string
100 		cName = g_strdup_printf ("%s\n[%s]", cGuessedName, D_("Unmounted"));
101 		g_free (cGuessedName);
102 	}
103 	if (cRealURI == NULL)
104 		cRealURI = g_strdup (cFileName);
105 	if (cIconName == NULL)
106 		cIconName = cairo_dock_search_icon_s_path (
107 			CD_SHORTCUT_DEFAULT_DIRECTORY_ICON_FILENAME,
108 			CAIRO_DOCK_DEFAULT_ICON_SIZE); // should be the default icon
109 
110 	Icon *pNewIcon = cairo_dock_create_dummy_launcher (cName,
111 		cIconName,
112 		cRealURI,
113 		NULL,
114 		fCurrentOrder);
115 	pNewIcon->iGroup = CD_BOOKMARK_GROUP;
116 	pNewIcon->cBaseURI = cFileName;
117 	pNewIcon->iVolumeID = iVolumeID;
118 	return pNewIcon;
119 }
120 
_get_item_with_base_uri_icon(GList * pIconList,const gchar * cBaseURI)121 static GList * _get_item_with_base_uri_icon (GList *pIconList, const gchar *cBaseURI)
122 {
123 	GList* ic;
124 	Icon *pIcon;
125 	for (ic = pIconList; ic != NULL; ic = ic->next)
126 	{
127 		pIcon = ic->data;
128 		if (pIcon->cBaseURI != NULL && strcmp (pIcon->cBaseURI, cBaseURI) == 0)
129 			return ic;
130 	}
131 	return NULL;
132 }
133 
_remove_old_icons_and_free_list(GList * pOldBookmarkList)134 static void _remove_old_icons_and_free_list (GList *pOldBookmarkList)
135 {
136 	GList* ic;
137 	Icon *pIcon;
138 	for (ic = pOldBookmarkList; ic != NULL; ic = ic->next)
139 	{
140 		pIcon = ic->data;
141 		CD_APPLET_REMOVE_ICON_FROM_MY_ICONS_LIST (pIcon);
142 	}
143 	g_list_free (pOldBookmarkList);
144 }
145 
cd_shortcuts_on_bookmarks_event(CairoDockFMEventType iEventType,const gchar * cURI,GldiModuleInstance * myApplet)146 void cd_shortcuts_on_bookmarks_event (CairoDockFMEventType iEventType, const gchar *cURI, GldiModuleInstance *myApplet)
147 {
148 	static int iTime = 0;
149 	iTime ++;
150 	CD_APPLET_ENTER;
151 	//g_print ("%s (%d)\n", __func__, iEventType);
152 	GList *pIconsList = CD_APPLET_MY_ICONS_LIST;
153 	GList *pOldBookmarkList;
154 	Icon *icon;
155 	GList *ic;
156 	// optimization: skip the disks and networks, and point on the first bookmark.
157 	for (ic = pIconsList; ic != NULL; ic = ic->next)
158 	{
159 		icon = ic->data;
160 		if (icon->iGroup == (CairoDockIconGroup) CD_BOOKMARK_GROUP)
161 			break;
162 	}
163 	/* Note that since the first bookmark is always the Home Folder,
164 	 * 'pIconsList' will never change when inserting/removing a bookmark.
165 	 */
166 	pIconsList = ic;
167 	GldiContainer *pContainer = CD_APPLET_MY_ICONS_LIST_CONTAINER;
168 	CD_APPLET_LEAVE_IF_FAIL (pContainer != NULL);
169 
170 	// split the list: items can have been removed
171 	pOldBookmarkList = pIconsList->next;
172 	pIconsList->next = NULL;
173 	pOldBookmarkList->prev = NULL;
174 
175 	// Bookmarks file has been modified
176 	if (iEventType == CAIRO_DOCK_FILE_CREATED || iEventType == CAIRO_DOCK_FILE_MODIFIED)
177 	{
178 		cd_message ("The bookmarks list has changed");
179 
180 		//\____________________ Read bookmarks file
181 		gchar *cContent = NULL;
182 		gsize length=0;
183 		GError *erreur = NULL;
184 		g_file_get_contents  (myData.cBookmarksURI, &cContent, &length, &erreur);
185 		if (erreur != NULL)
186 		{
187 			cd_warning ("when trying to get the bookmarks : %s", erreur->message);
188 			g_error_free (erreur);
189 		}
190 		else
191 		{
192 			gchar **cBookmarksList = g_strsplit (cContent, "\n", -1);
193 			g_free (cContent);
194 
195 			//\____________________ Read the content.
196 			/* Bookmarks are listed in the order of the file; we need to
197 			 * reorder each icon in case a bookmark has changed its place, or
198 			 * if a new one appeared (the first one is always the Home Folder).
199 			 */
200 			double fCurrentOrder = 1.;
201 			gchar *cOneBookmark;
202 			Icon *pNewIcon, *pExistingIcon;
203 			GList *pExistingIconNode;
204 			const gchar *cUserName;
205 			int i;
206 			for (i = 0; cBookmarksList[i] != NULL; i ++)
207 			{
208 				cOneBookmark = cBookmarksList[i];
209 				if (*cOneBookmark == '\0' || *cOneBookmark == '#')
210 				{
211 					g_free (cOneBookmark);
212 					continue;
213 				}
214 
215 				// Grab the custom name if any
216 				cUserName = _get_custom_name_and_uri (cBookmarksList[i], &cOneBookmark);
217 
218 				// Check if the icon already exists and has changed
219 				pExistingIconNode = _get_item_with_base_uri_icon (pOldBookmarkList, cOneBookmark);
220 				if (pExistingIconNode != NULL)
221 				{
222 					pExistingIcon = pExistingIconNode->data;
223 					// move this node to the subdock icons list
224 					pOldBookmarkList = g_list_delete_link (pOldBookmarkList, pExistingIconNode);
225 					pIconsList = g_list_insert (pIconsList, pExistingIcon, 1); // after the home, will be sorted later
226 					if (cUserName && g_strcmp0 (pExistingIcon->cName, cUserName) != 0)
227 					{
228 						CD_APPLET_REMOVE_ICON_FROM_MY_ICONS_LIST (pExistingIcon); // will destroy it
229 						pExistingIcon = NULL;
230 					}
231 					else
232 					{
233 						fCurrentOrder++;
234 						g_free (cOneBookmark);
235 					}
236 				}
237 				else
238 					pExistingIcon = NULL;
239 
240 				if (pExistingIcon == NULL)
241 				{
242 					pNewIcon = _cd_shortcuts_get_icon (cOneBookmark,
243 						cUserName, fCurrentOrder);
244 					if (pNewIcon)
245 					{
246 						CD_APPLET_ADD_ICON_IN_MY_ICONS_LIST (pNewIcon);
247 						fCurrentOrder++;
248 					}
249 					else
250 					{
251 						cd_warning ("couldn't get info on bookmark '%s'", cOneBookmark);
252 						g_free (cOneBookmark);
253 					}
254 				}
255 			}
256 			g_free (cBookmarksList);
257 
258 			_remove_old_icons_and_free_list (pOldBookmarkList);
259 
260 			/* Again, since 'Home Folder' is always the first bookmark,
261 			 * the head of the list won't change even if there are only bookmarks
262 			 * (so we don't need to re-assigne it to the container).
263 			 */
264 			cairo_dock_sort_icons_by_order (pIconsList);
265 		}
266 	}
267 	CD_APPLET_LEAVE();
268 }
269 
cd_shortcuts_remove_one_bookmark(const gchar * cURI,GldiModuleInstance * myApplet)270 void cd_shortcuts_remove_one_bookmark (const gchar *cURI, GldiModuleInstance *myApplet)
271 {
272 	g_return_if_fail (cURI != NULL);
273 	cd_message ("%s (%s)", __func__, cURI);
274 
275 	gchar *cContent = NULL;
276 	gsize length=0;
277 	GError *erreur = NULL;
278 	g_file_get_contents  (myData.cBookmarksURI, &cContent, &length, &erreur);
279 	if (erreur != NULL)
280 	{
281 		cd_warning ("while trying to read bookmarks file : %s", erreur->message);
282 		g_error_free (erreur);
283 	}
284 	else
285 	{
286 		gchar **cBookmarksList = g_strsplit (cContent, "\n", -1);
287 		g_free (cContent);
288 		gchar *cOneBookmark, *str;
289 		gboolean bFound = FALSE;
290 		int i = 0;
291 		for (i = 0; cBookmarksList[i] != NULL; i ++)
292 		{
293 			cOneBookmark = cBookmarksList[i];
294 			if (*cOneBookmark == '\0' || *cOneBookmark == '#')
295 				continue;
296 
297 			str = strchr (cOneBookmark, ' ');
298 			if ((str && strncmp (cOneBookmark, cURI, str - cOneBookmark) == 0)
299 			    || (!str && strcmp (cOneBookmark, cURI) == 0))
300 			{
301 				// remove this element from the array
302 				int j;
303 				for (j = i; cBookmarksList[j] != NULL; j ++)
304 				{
305 					cBookmarksList[j] = cBookmarksList[j+1];
306 				}
307 				// free the removed element.
308 				g_free (cOneBookmark);
309 				// quit the loop
310 				bFound = TRUE;
311 				break;
312 			}
313 		}
314 
315 		if (! bFound)
316 		{
317 			cd_warning ("bookmark '%s' not found", cURI);
318 		}
319 		else
320 		{
321 			cContent = g_strjoinv ("\n", cBookmarksList);
322 			g_file_set_contents (myData.cBookmarksURI, cContent, -1, &erreur);
323 			if (erreur != NULL)
324 			{
325 				cd_warning ("while trying to write bookmarks file : %s", erreur->message);
326 				g_error_free (erreur);
327 			}
328 			g_free (cContent);
329 		}
330 		g_strfreev (cBookmarksList);
331 	}
332 }
333 
cd_shortcuts_rename_one_bookmark(const gchar * cURI,const gchar * cName,GldiModuleInstance * myApplet)334 void cd_shortcuts_rename_one_bookmark (const gchar *cURI, const gchar *cName, GldiModuleInstance *myApplet)
335 {
336 	g_return_if_fail (cURI != NULL);
337 	cd_message ("%s (%s, %s)", __func__, cURI, cName);
338 
339 	gchar *cContent = NULL;
340 	gsize length=0;
341 	GError *erreur = NULL;
342 	g_file_get_contents  (myData.cBookmarksURI, &cContent, &length, &erreur);
343 	if (erreur != NULL)
344 	{
345 		cd_warning ("while trying to read bookmarks file : %s", erreur->message);
346 		g_error_free (erreur);
347 	}
348 	else
349 	{
350 		gchar **cBookmarksList = g_strsplit (cContent, "\n", -1);
351 		g_free (cContent);
352 		gchar *cOneBookmark, *str;
353 		int i = 0;
354 		for (i = 0; cBookmarksList[i] != NULL; i ++)
355 		{
356 			cOneBookmark = cBookmarksList[i];
357 			if (*cOneBookmark == '\0' || *cOneBookmark == '#')
358 				continue;
359 
360 			str = strchr (cOneBookmark, ' ');
361 			if ((str && strncmp (cOneBookmark, cURI, str - cOneBookmark) == 0) || (!str && strcmp (cOneBookmark, cURI) == 0))
362 			{
363 				cBookmarksList[i] = g_strdup_printf ("%s %s", cURI, cName);
364 				g_free (cOneBookmark);
365 				break;
366 			}
367 		}
368 
369 		if (cBookmarksList[i] == NULL)
370 		{
371 			cd_warning ("bookmark '%s' not found", cURI);
372 		}
373 		else
374 		{
375 			cContent = g_strjoinv ("\n", cBookmarksList);
376 			g_file_set_contents (myData.cBookmarksURI, cContent, -1, &erreur);
377 			if (erreur != NULL)
378 			{
379 				cd_warning ("while trying to write bookmarks file : %s", erreur->message);
380 				g_error_free (erreur);
381 			}
382 			g_free (cContent);
383 		}
384 		g_strfreev (cBookmarksList);
385 	}
386 }
387 
cd_shortcuts_add_one_bookmark(const gchar * cURI,GldiModuleInstance * myApplet)388 void cd_shortcuts_add_one_bookmark (const gchar *cURI, GldiModuleInstance *myApplet)
389 {
390 	g_return_if_fail (cURI != NULL);
391 	cd_message ("%s (%s)", __func__, cURI);
392 
393 	// see if we need to add a new line before the new URI.
394 	gchar *cContent = NULL;
395 	gsize length = 0;
396 	g_file_get_contents (myData.cBookmarksURI,
397 		&cContent,
398 		&length,
399 		NULL);
400 	gboolean bAddNewLine = (cContent && length > 0 && cContent[length-1] != '\n');
401 	g_free (cContent);
402 
403 	// append the new URI to the file.
404 	FILE *f = fopen (myData.cBookmarksURI, "a");
405 	if (f != NULL)
406 	{
407 		gchar *cNewLine = g_strdup_printf ("%s%s\n", bAddNewLine ? "\n" : "", cURI);
408 		fputs(cNewLine, f);
409 		g_free (cNewLine);
410 		fclose (f);
411 	}
412 }
413 
cd_shortcuts_list_bookmarks(gchar * cBookmarkFilePath,GldiModuleInstance * myApplet)414 GList *cd_shortcuts_list_bookmarks (gchar *cBookmarkFilePath, GldiModuleInstance *myApplet)
415 {
416 	GList *pBookmarkIconList = NULL;
417 	Icon *pNewIcon;
418 	double fCurrentOrder = 0.;
419 
420 	// Home
421 	gchar *cHome = g_strdup_printf ("file://%s", g_getenv ("HOME"));
422 	pNewIcon = _cd_shortcuts_get_icon (cHome, D_("Home Folder"), fCurrentOrder++);
423 	if (pNewIcon != NULL)
424 	{
425 		_init_disk_usage (pNewIcon, myApplet);
426 		CDDiskUsage *pDiskUsage = CD_APPLET_GET_MY_ICON_DATA (pNewIcon);
427 		if (pDiskUsage) // so that this bookmark will never be considered old, and therefore removed.
428 			pDiskUsage->iLastCheckTime = 1e9;
429 		pBookmarkIconList = g_list_append (pBookmarkIconList, pNewIcon);
430 	}
431 	else
432 		g_free (cHome);
433 
434 	gchar *cContent = NULL;
435 	gsize length = 0;
436 	GError *erreur = NULL;
437 	g_file_get_contents  (cBookmarkFilePath, &cContent, &length, &erreur);
438 	if (erreur != NULL)
439 	{
440 		cd_warning ("Attention: %s\n  no bookmark will be available", erreur->message);
441 		g_error_free (erreur);
442 	}
443 	else
444 	{
445 		gchar **cBookmarksList = g_strsplit (cContent, "\n", -1);
446 		g_free (cContent);
447 
448 		gchar *cOneBookmark;
449 		const gchar *cUserName;
450 		int i = 0;
451 		for (i = 0; cBookmarksList[i] != NULL; i ++)
452 		{
453 			cUserName = _get_custom_name_and_uri (cBookmarksList[i], &cOneBookmark);
454 			if (*cOneBookmark != '\0' && *cOneBookmark != '#')
455 			{
456 				cd_message (" + 1 bookmark : %s", cOneBookmark);
457 				pNewIcon = _cd_shortcuts_get_icon (cOneBookmark, cUserName, fCurrentOrder++);
458 				if (pNewIcon)
459 					pBookmarkIconList = g_list_append (pBookmarkIconList, pNewIcon);
460 				else
461 					g_free (cOneBookmark);
462 			}
463 			else
464 			{
465 				g_free (cOneBookmark);
466 			}
467 		}
468 		g_free (cBookmarksList);
469 	}
470 	return pBookmarkIconList;
471 }
472