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 <string.h>
21 #include <unistd.h>
22 #define __USE_XOPEN_EXTENDED
23 #include <glib/gstdio.h>
24 #include <glib/gi18n.h>
25 
26 #include "config.h"
27 #include "gldi-icon-names.h"
28 #include "cairo-dock-struct.h"
29 #include "cairo-dock-keyfile-utilities.h"
30 #include "cairo-dock-file-manager.h"  // cairo_dock_copy_file
31 #include "cairo-dock-packages.h"
32 #include "cairo-dock-surface-factory.h"
33 #include "cairo-dock-dialog-factory.h"  // gldi_dialog_show_and_wait
34 #include "cairo-dock-gui-factory.h"
35 #include "cairo-dock-log.h"
36 #include "cairo-dock-icon-facility.h"  // gldi_icons_get_any_without_dialog
37 #include "cairo-dock-task.h"
38 #include "cairo-dock-dock-manager.h"  // gldi_dock_get
39 #include "cairo-dock-applications-manager.h"  // cairo_dock_get_current_active_icon
40 #include "cairo-dock-themes-manager.h"  // cairo_dock_export_current_theme
41 #include "cairo-dock-config.h"  // cairo_dock_load_current_theme
42 #include "cairo-dock-menu.h"  // cairo_dock_add_in_menu_with_stock_and_data
43 #include "cairo-dock-gui-manager.h"  // cairo_dock_set_status_message
44 #include "cairo-dock-gui-backend.h"
45 #include "cairo-dock-widget-themes.h"
46 
47 extern gchar *g_cThemesDirPath;
48 extern CairoDock *g_pMainDock;
49 
50 static void _themes_widget_apply (CDWidget *pCdWidget);
51 static void _themes_widget_reset (CDWidget *pCdWidget);
52 static void _themes_widget_reload (CDWidget *pThemesWidget);
53 static void _fill_treeview_with_themes (ThemesWidget *pThemesWidget);
54 static void _fill_combo_with_user_themes (ThemesWidget *pThemesWidget);
55 
56 
_cairo_dock_build_temporary_themes_conf_file(void)57 static gchar *_cairo_dock_build_temporary_themes_conf_file (void)
58 {
59 	//\___________________ On cree un fichier de conf temporaire.
60 	const gchar *cTmpDir = g_get_tmp_dir ();
61 	gchar *cTmpConfFile = g_strdup_printf ("%s/cairo-dock-init.XXXXXX", cTmpDir);
62 	int fds = mkstemp (cTmpConfFile);
63 	if (fds == -1)
64 	{
65 		cd_warning ("can't create a temporary file in %s", cTmpDir);
66 		g_free (cTmpConfFile);
67 		return NULL;
68 	}
69 
70 	//\___________________ On copie le fichier de conf par defaut dedans.
71 	cairo_dock_copy_file (CAIRO_DOCK_SHARE_DATA_DIR"/themes.conf", cTmpConfFile);
72 
73 	close(fds);
74 	return cTmpConfFile;
75 }
76 
_load_theme(gboolean bSuccess,ThemesWidget * pThemesWidget)77 static void _load_theme (gboolean bSuccess, ThemesWidget *pThemesWidget)
78 {
79 	if (bSuccess)
80 	{
81 		cairo_dock_load_current_theme ();
82 
83 		cairo_dock_set_status_message (NULL, "");
84 
85 		_fill_treeview_with_themes (pThemesWidget);
86 		cairo_dock_reload_gui ();
87 	}
88 	else
89 		cairo_dock_set_status_message (NULL, _("Could not import the theme."));
90 	gtk_widget_destroy (pThemesWidget->pWaitingDialog);
91 	pThemesWidget->pWaitingDialog = NULL;
92 	gldi_task_discard (pThemesWidget->pImportTask);
93 	pThemesWidget->pImportTask = NULL;
94 }
95 
_cairo_dock_save_current_theme(GKeyFile * pKeyFile)96 static gchar * _cairo_dock_save_current_theme (GKeyFile* pKeyFile)
97 {
98 	const gchar *cGroupName = "Save";
99 	//\______________ On recupere le nom du theme.
100 	gchar *cNewThemeName = g_key_file_get_string (pKeyFile, cGroupName, "theme name", NULL);
101 	if (cNewThemeName != NULL && *cNewThemeName == '\0')
102 	{
103 		g_free (cNewThemeName);
104 		cNewThemeName = NULL;
105 	}
106 	cd_message ("cNewThemeName : %s", cNewThemeName);
107 	g_return_val_if_fail (cNewThemeName != NULL, FALSE);
108 
109 	//\___________________ On sauve le theme courant sous ce nom.
110 	cairo_dock_extract_package_type_from_name (cNewThemeName);
111 
112 	gboolean bSaveBehavior = g_key_file_get_boolean (pKeyFile, cGroupName, "save current behaviour", NULL);
113 	gboolean bSaveLaunchers = g_key_file_get_boolean (pKeyFile, cGroupName, "save current launchers", NULL);
114 
115 	gboolean bThemeSaved = cairo_dock_export_current_theme (cNewThemeName, bSaveBehavior, bSaveLaunchers);
116 
117 	if (g_key_file_get_boolean (pKeyFile, cGroupName, "package", NULL))
118 	{
119 		gchar *cDirPath = g_key_file_get_string (pKeyFile, cGroupName, "package dir", NULL);
120 		bThemeSaved |= cairo_dock_package_current_theme (cNewThemeName, cDirPath);
121 		g_free (cDirPath);
122 	}
123 
124 	if (bThemeSaved)
125 	{
126 		return cNewThemeName;
127 	}
128 	else
129 	{
130 		g_free (cNewThemeName);
131 		return NULL;
132 	}
133 }
134 
135 
on_cancel_dl(G_GNUC_UNUSED GtkButton * button,ThemesWidget * pThemesWidget)136 static void on_cancel_dl (G_GNUC_UNUSED GtkButton *button, ThemesWidget *pThemesWidget)
137 {
138 	gldi_task_discard (pThemesWidget->pImportTask);
139 	pThemesWidget->pImportTask = NULL;
140 	gtk_widget_destroy (pThemesWidget->pWaitingDialog);  // stop the pulse too
141 	pThemesWidget->pWaitingDialog = NULL;
142 }
_pulse_bar(GtkWidget * pBar)143 static gboolean _pulse_bar (GtkWidget *pBar)
144 {
145 	gtk_progress_bar_pulse (GTK_PROGRESS_BAR (pBar));
146 	return TRUE;
147 }
on_waiting_dialog_destroyed(G_GNUC_UNUSED GtkWidget * pWidget,ThemesWidget * pThemesWidget)148 static void on_waiting_dialog_destroyed (G_GNUC_UNUSED GtkWidget *pWidget, ThemesWidget *pThemesWidget)
149 {
150 	pThemesWidget->pWaitingDialog = NULL;
151 	g_source_remove (pThemesWidget->iSidPulse);
152 	pThemesWidget->iSidPulse = 0;
153 }
_cairo_dock_load_theme(GKeyFile * pKeyFile,ThemesWidget * pThemesWidget)154 static gboolean _cairo_dock_load_theme (GKeyFile* pKeyFile, ThemesWidget *pThemesWidget)
155 {
156 	GtkWindow *pMainWindow = pThemesWidget->pMainWindow;
157 	const gchar *cGroupName = "Load theme";
158 	//\___________________ On recupere le theme selectionne.
159 	gchar *cNewThemeName = g_key_file_get_string (pKeyFile, cGroupName, "chosen theme", NULL);
160 	if (cNewThemeName != NULL && *cNewThemeName == '\0')
161 	{
162 		g_free (cNewThemeName);
163 		cNewThemeName = NULL;
164 	}
165 
166 	if (cNewThemeName == NULL)
167 	{
168 		cNewThemeName = g_key_file_get_string (pKeyFile, cGroupName, "package", NULL);
169 		if (cNewThemeName != NULL && *cNewThemeName == '\0')
170 		{
171 			g_free (cNewThemeName);
172 			cNewThemeName = NULL;
173 		}
174 	}
175 
176 	g_return_val_if_fail (cNewThemeName != NULL, FALSE);
177 
178 	//\___________________ On recupere les options de chargement.
179 	gboolean bLoadBehavior = g_key_file_get_boolean (pKeyFile, cGroupName, "use theme behaviour", NULL);
180 	gboolean bLoadLaunchers = g_key_file_get_boolean (pKeyFile, cGroupName, "use theme launchers", NULL);
181 
182 	if (pThemesWidget->pImportTask != NULL)
183 	{
184 		gldi_task_discard (pThemesWidget->pImportTask);
185 		pThemesWidget->pImportTask = NULL;
186 	}
187 	//\___________________ On regarde si le theme courant est modifie.
188 	gboolean bNeedSave = cairo_dock_current_theme_need_save ();
189 	if (bNeedSave)
190 	{
191 		Icon *pIcon = cairo_dock_get_current_active_icon ();  // it's most probably the icon corresponding to the configuration window
192 		if (pIcon == NULL || cairo_dock_get_icon_container (pIcon) == NULL)  // if not available, get any icon
193 			pIcon = gldi_icons_get_any_without_dialog ();
194 		int iClickedButton = gldi_dialog_show_and_wait (_("You have made some changes to the current theme.\nYou will lose them if you don't save before choosing a new theme. Continue anyway?"),
195 			pIcon, CAIRO_CONTAINER (g_pMainDock),
196 			CAIRO_DOCK_SHARE_DATA_DIR"/"CAIRO_DOCK_ICON, NULL);
197 		if (iClickedButton != 0 && iClickedButton != -1)  // cancel button or Escape.
198 		{
199 			return FALSE;
200 		}
201 	}
202 
203 	//\___________________ On charge le nouveau theme choisi.
204 	gchar *tmp = g_strdup (cNewThemeName);
205 	CairoDockPackageType iType = cairo_dock_extract_package_type_from_name (tmp);
206 	g_free (tmp);
207 
208 	gboolean bThemeImported = FALSE;
209 	if (iType != CAIRO_DOCK_LOCAL_PACKAGE && iType != CAIRO_DOCK_USER_PACKAGE)
210 	{
211 		GtkWidget *pWaitingDialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
212 		pThemesWidget->pWaitingDialog = pWaitingDialog;
213 		gtk_window_set_decorated (GTK_WINDOW (pWaitingDialog), FALSE);
214 		gtk_window_set_skip_taskbar_hint (GTK_WINDOW (pWaitingDialog), TRUE);
215 		gtk_window_set_skip_pager_hint (GTK_WINDOW (pWaitingDialog), TRUE);
216 		gtk_window_set_transient_for (GTK_WINDOW (pWaitingDialog), pMainWindow);
217 		gtk_window_set_modal (GTK_WINDOW (pWaitingDialog), TRUE);
218 
219 		GtkWidget *pMainVBox = gtk_box_new (GTK_ORIENTATION_VERTICAL, CAIRO_DOCK_FRAME_MARGIN);
220 		gtk_container_add (GTK_CONTAINER (pWaitingDialog), pMainVBox);
221 
222 		GtkWidget *pLabel = gtk_label_new (_("Please wait while importing the theme..."));
223 		gtk_box_pack_start(GTK_BOX (pMainVBox), pLabel, FALSE, FALSE, 0);
224 
225 		GtkWidget *pBar = gtk_progress_bar_new ();
226 		gtk_progress_bar_pulse (GTK_PROGRESS_BAR (pBar));
227 		gtk_box_pack_start (GTK_BOX (pMainVBox), pBar, FALSE, FALSE, 0);
228 		pThemesWidget->iSidPulse = g_timeout_add (100, (GSourceFunc)_pulse_bar, pBar);
229 		g_signal_connect (G_OBJECT (pWaitingDialog),
230 			"destroy",
231 			G_CALLBACK (on_waiting_dialog_destroyed),
232 			pThemesWidget);
233 
234 		GtkWidget *pCancelButton = gtk_button_new_with_label (_("Cancel"));
235 		g_signal_connect (G_OBJECT (pCancelButton), "clicked", G_CALLBACK(on_cancel_dl), pWaitingDialog);
236 		gtk_box_pack_start (GTK_BOX (pMainVBox), pCancelButton, FALSE, FALSE, 0);
237 
238 		gtk_widget_show_all (pWaitingDialog);
239 
240 		cd_debug ("start importation...");
241 		pThemesWidget->pImportTask = cairo_dock_import_theme_async (cNewThemeName, bLoadBehavior, bLoadLaunchers, (GFunc)_load_theme, pThemesWidget);  // if 'pThemesWidget' is destroyed, the 'reset' callback will be called and will cancel the task.
242 	}
243 	else  // if the theme is already local and uptodate, there is really no need to show a progressbar, because only the download/unpacking is done asynchonously (and the copy of the files is fast enough).
244 	{
245 		bThemeImported = cairo_dock_import_theme (cNewThemeName, bLoadBehavior, bLoadLaunchers);
246 
247 		//\_______________ load it.
248 		if (bThemeImported)
249 		{
250 			cairo_dock_load_current_theme ();
251 			cairo_dock_reload_gui ();
252 		}
253 	}
254 	g_free (cNewThemeName);
255 	return bThemeImported;
256 }
257 
258 
259 #define CD_MAX_RATING 5
_render_rating(GtkCellRenderer * cell,GtkTreeModel * model,GtkTreeIter * iter,int iColumnIndex)260 static inline void _render_rating (GtkCellRenderer *cell, GtkTreeModel *model, GtkTreeIter *iter, int iColumnIndex)
261 {
262 	gint iRating = 0;
263 	gtk_tree_model_get (model, iter, iColumnIndex, &iRating, -1);
264 	if (iRating > CD_MAX_RATING)
265 		iRating = CD_MAX_RATING;
266 	if (iRating > 0)
267 	{
268 		GString *s = g_string_sized_new (CD_MAX_RATING*4+1);
269 		int i;
270 		for (i= 0; i < iRating; i ++)
271 			g_string_append (s, "★");
272 		for (;i < CD_MAX_RATING; i ++)
273 			g_string_append (s, "☆");
274 		g_object_set (cell, "text", s->str, NULL);  // markup
275 		g_string_free (s, TRUE);
276 	}
277 	else
278 	{
279 		if (iColumnIndex == CAIRO_DOCK_MODEL_ORDER)  // note, peut etre changee (la sobriete ne peut pas).
280 		{
281 			gchar *cRateMe = g_strconcat ("<small><i>", _("Rate me"), "</i></small>", NULL);
282 			g_object_set (cell, "markup", cRateMe, NULL);
283 			g_free (cRateMe);
284 		}
285 		else
286 			g_object_set (cell, "markup", "   -", NULL);  // pour la sobriete d'un theme utilisateur, plutot que d'avoir une case vide, on met un tiret dedans.
287 	}
288 }
_cairo_dock_render_sobriety(G_GNUC_UNUSED GtkTreeViewColumn * tree_column,GtkCellRenderer * cell,GtkTreeModel * model,GtkTreeIter * iter,G_GNUC_UNUSED gpointer data)289 static void _cairo_dock_render_sobriety (G_GNUC_UNUSED GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, GtkTreeModel *model,GtkTreeIter *iter, G_GNUC_UNUSED gpointer data)
290 {
291 	_render_rating (cell, model, iter, CAIRO_DOCK_MODEL_ORDER2);
292 }
_cairo_dock_render_rating(G_GNUC_UNUSED GtkTreeViewColumn * tree_column,GtkCellRenderer * cell,GtkTreeModel * model,GtkTreeIter * iter,G_GNUC_UNUSED gpointer data)293 static void _cairo_dock_render_rating (G_GNUC_UNUSED GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, GtkTreeModel *model,GtkTreeIter *iter, G_GNUC_UNUSED gpointer data)
294 {
295 	/// ignorer les themes "default" qui sont en lecture seule...
296 	_render_rating (cell, model, iter, CAIRO_DOCK_MODEL_ORDER);
297 }
298 
_make_rate_list_store(void)299 static GtkListStore *_make_rate_list_store (void)
300 {
301 	GString *s = g_string_sized_new (CD_MAX_RATING*4+1);
302 	GtkListStore *note_list = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
303 	GtkTreeIter iter;
304 	int i, j;
305 	for (i = 1; i <= 5; i ++)
306 	{
307 		g_string_assign (s, "");
308 		for (j= 0; j < i; j ++)
309 			g_string_append (s, "★");
310 		for (;j < CD_MAX_RATING; j ++)
311 			g_string_append (s, "☆");
312 
313 		memset (&iter, 0, sizeof (GtkTreeIter));
314 		gtk_list_store_append (GTK_LIST_STORE (note_list), &iter);
315 		gtk_list_store_set (GTK_LIST_STORE (note_list), &iter,
316 			0, i,
317 			1, s->str, -1);
318 	}
319 	g_string_free (s, TRUE);
320 	return note_list;
321 }
322 
_change_rating(G_GNUC_UNUSED GtkCellRendererText * cell,gchar * path_string,gchar * new_text,GtkTreeModel * model)323 static void _change_rating (G_GNUC_UNUSED GtkCellRendererText * cell, gchar * path_string, gchar * new_text, GtkTreeModel * model)
324 {
325 	//g_print ("%s (%s : %s)\n", __func__, path_string, new_text);
326 	g_return_if_fail (new_text != NULL && *new_text != '\0');
327 
328 	GtkTreeIter it;
329 	if (! gtk_tree_model_get_iter_from_string (model, &it, path_string))
330 		return ;
331 
332 	int iRating = 0;
333 	gchar *str = new_text;
334 	do
335 	{
336 		if (strncmp (str, "★", strlen ("★")) == 0)
337 		{
338 			str += strlen ("★");
339 			iRating ++;
340 		}
341 		else
342 			break ;
343 	} while (1);
344 	//g_print ("iRating : %d\n", iRating);
345 
346 	gchar *cThemeName = NULL;
347 	gint iState;
348 	gtk_tree_model_get (model, &it,
349 		CAIRO_DOCK_MODEL_RESULT, &cThemeName,
350 		CAIRO_DOCK_MODEL_STATE, &iState, -1);
351 	g_return_if_fail (cThemeName != NULL);
352 	cairo_dock_extract_package_type_from_name (cThemeName);
353 	//g_print ("theme : %s / %s\n", cThemeName, cDisplayedName);
354 
355 	gchar *cRatingDir = g_strdup_printf ("%s/.rating", g_cThemesDirPath);  // il y'a un probleme ici, on ne connait pas le repertoire utilisateur des themes. donc ce code ne marche que pour les themes du dock (et n'est utilise que pour ca)
356 	gchar *cRatingFile = g_strdup_printf ("%s/%s", cRatingDir, cThemeName);
357 	//g_print ("on ecrit dans %s\n", cRatingFile);
358 	if (iState == CAIRO_DOCK_USER_PACKAGE || iState == CAIRO_DOCK_LOCAL_PACKAGE || g_file_test (cRatingFile, G_FILE_TEST_EXISTS))  // ca n'est pas un theme distant, ou l'utilisateur a deja vote auparavant pour ce theme.
359 	{
360 		if (!g_file_test (cRatingDir, G_FILE_TEST_IS_DIR))
361 		{
362 			if (g_mkdir (cRatingDir, 7*8*8+7*8+5) != 0)
363 			{
364 				cd_warning ("couldn't create directory %s", cRatingDir);
365 				return ;
366 			}
367 		}
368 		gchar *cContent = g_strdup_printf ("%d", iRating);
369 		g_file_set_contents (cRatingFile,
370 			cContent,
371 			-1,
372 			NULL);
373 		g_free (cContent);
374 
375 		gtk_list_store_set (GTK_LIST_STORE (model), &it, CAIRO_DOCK_MODEL_ORDER, iRating, -1);
376 	}
377 	else
378 	{
379 		Icon *pIcon = cairo_dock_get_current_active_icon ();  // most probably the appli-icon representing the config window.
380 		GldiContainer *pContainer = (pIcon != NULL ? cairo_dock_get_icon_container (pIcon) : NULL);
381 		if (pContainer != NULL)
382 			gldi_dialog_show_temporary_with_icon (_("You must try the theme before you can rate it."), pIcon, pContainer, 3000, "same icon");
383 		else
384 			gldi_dialog_show_general_message (_("You must try the theme before you can rate it."), 3000);
385 	}
386 	g_free (cThemeName);
387 	g_free (cRatingFile);
388 	g_free (cRatingDir);
389 }
390 
_got_themes_list(GHashTable * pThemeTable,ThemesWidget * pThemesWidget)391 static void _got_themes_list (GHashTable *pThemeTable, ThemesWidget *pThemesWidget)
392 {
393 	if (pThemeTable == NULL)
394 	{
395 		cairo_dock_set_status_message (GTK_WIDGET (pThemesWidget->pMainWindow), "Couldn't list online themes (is connection alive ?)");
396 		return ;
397 	}
398 	else
399 		cairo_dock_set_status_message (GTK_WIDGET (pThemesWidget->pMainWindow), "");
400 
401 	gldi_task_discard (pThemesWidget->pListTask);
402 	pThemesWidget->pListTask = NULL;
403 
404 	GtkListStore *pModel = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (pThemesWidget->pTreeView)));
405 	g_return_if_fail (pModel != NULL);
406 
407 	gtk_list_store_clear (GTK_LIST_STORE (pModel));  // the 2nd time we pass here, the table is full of all themes, so we have to clear it to avoid double local and user themes.
408 	cairo_dock_fill_model_with_themes (pModel, pThemeTable, NULL);
409 }
410 
_on_delete_theme(G_GNUC_UNUSED GtkMenuItem * pMenuItem,ThemesWidget * pThemesWidget)411 static void _on_delete_theme (G_GNUC_UNUSED GtkMenuItem *pMenuItem, ThemesWidget *pThemesWidget)
412 {
413 	// get the selected theme
414 	GtkTreeSelection *pSelection = gtk_tree_view_get_selection (GTK_TREE_VIEW (pThemesWidget->pTreeView));
415 	GtkTreeModel *pModel;
416 	GtkTreeIter iter;
417 	if (! gtk_tree_selection_get_selected (pSelection, &pModel, &iter))
418 		return ;
419 	gchar *cThemeName = NULL;
420 	gtk_tree_model_get (pModel, &iter,
421 		CAIRO_DOCK_MODEL_RESULT, &cThemeName, -1);
422 	cairo_dock_extract_package_type_from_name (cThemeName);
423 
424 	// delete it
425 	gchar *cThemesList[2] = {cThemeName, NULL};
426 	gboolean bSuccess = cairo_dock_delete_themes (cThemesList);
427 
428 	// reload the themes list
429 	if (bSuccess)
430 	{
431 		cairo_dock_set_status_message (NULL, _("The theme has been deleted"));
432 
433 		_fill_treeview_with_themes (pThemesWidget);
434 
435 		_fill_combo_with_user_themes (pThemesWidget);
436 	}
437 	g_free (cThemeName);
438 }
_on_click_tree_view(GtkTreeView * pTreeView,GdkEventButton * pButton,ThemesWidget * pThemesWidget)439 static gboolean _on_click_tree_view (GtkTreeView *pTreeView, GdkEventButton* pButton, ThemesWidget *pThemesWidget)
440 {
441 	if ((pButton->button == 3 && pButton->type == GDK_BUTTON_RELEASE)  // right click
442 	|| (pButton->button == 1 && pButton->type == GDK_2BUTTON_PRESS))  // double click
443 	{
444 		GtkTreeSelection *pSelection = gtk_tree_view_get_selection (pTreeView);
445 		GtkTreeModel *pModel;
446 		GtkTreeIter iter;
447 		if (! gtk_tree_selection_get_selected (pSelection, &pModel, &iter))
448 			return FALSE;
449 
450 		if (pButton->button == 3)  // show the menu if needed (ie, if the theme can be deleted).
451 		{
452 			gchar *cThemeName = NULL;
453 			gtk_tree_model_get (pModel, &iter,
454 				CAIRO_DOCK_MODEL_RESULT, &cThemeName, -1);
455 			CairoDockPackageType iType = cairo_dock_extract_package_type_from_name (cThemeName);  // the type is encoded inside the result; one could also see if the theme folder is present on the disk.
456 			g_free (cThemeName);
457 			if (iType == CAIRO_DOCK_USER_PACKAGE || iType == CAIRO_DOCK_UPDATED_PACKAGE)
458 			{
459 				GtkWidget *pMenu = gtk_menu_new ();
460 
461 				cairo_dock_add_in_menu_with_stock_and_data (_("Delete this theme"), GLDI_ICON_NAME_DELETE, G_CALLBACK (_on_delete_theme), pMenu, pThemesWidget);
462 
463 				gtk_widget_show_all (pMenu);
464 				gtk_menu_popup (GTK_MENU (pMenu),
465 					NULL,
466 					NULL,
467 					NULL,
468 					NULL,
469 					1,
470 					gtk_get_current_event_time ());
471 			}
472 		}
473 		else  // load the theme
474 		{
475 
476 		}
477 	}
478 	return FALSE;
479 }
480 
_fill_treeview_with_themes(ThemesWidget * pThemesWidget)481 static void _fill_treeview_with_themes (ThemesWidget *pThemesWidget)
482 {
483 	const gchar *cShareThemesDir = CAIRO_DOCK_SHARE_DATA_DIR"/"CAIRO_DOCK_THEMES_DIR;
484 	const gchar *cUserThemesDir = g_cThemesDirPath;
485 	const gchar *cDistantThemesDir = CAIRO_DOCK_DISTANT_THEMES_DIR;
486 
487 	// list local packages first
488 	GHashTable *pThemeTable = cairo_dock_list_packages (cShareThemesDir, cUserThemesDir, NULL, NULL);
489 	_got_themes_list (pThemeTable, pThemesWidget);
490 
491 	// and list distant packages asynchronously.
492 	cairo_dock_set_status_message_printf (GTK_WIDGET (pThemesWidget->pMainWindow), _("Listing themes in '%s' ..."), cDistantThemesDir);
493 	pThemesWidget->pListTask = cairo_dock_list_packages_async (NULL, NULL, cDistantThemesDir, (CairoDockGetPackagesFunc) _got_themes_list, pThemesWidget, pThemeTable);  // the table will be freed along with the task.
494 }
495 
_make_tree_view_for_themes(ThemesWidget * pThemesWidget,GPtrArray * pDataGarbage,G_GNUC_UNUSED GKeyFile * pKeyFile)496 static void _make_tree_view_for_themes (ThemesWidget *pThemesWidget, GPtrArray *pDataGarbage, G_GNUC_UNUSED GKeyFile* pKeyFile)
497 {
498 	//\______________ get the group/key widget
499 	GSList *pWidgetList = pThemesWidget->widget.pWidgetList;
500 	CairoDockGroupKeyWidget *myWidget = cairo_dock_gui_find_group_key_widget_in_list (pWidgetList, "Load theme", "chosen theme");
501 	g_return_if_fail (myWidget != NULL);
502 
503 	//\______________ build the treeview.
504 	GtkWidget *pOneWidget = cairo_dock_gui_make_tree_view (TRUE);
505 	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (pOneWidget), TRUE);
506 	gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW (pOneWidget), TRUE);
507 	GtkTreeModel *pModel = gtk_tree_view_get_model (GTK_TREE_VIEW (pOneWidget));
508 	GtkTreeViewColumn* col;
509 	GtkCellRenderer *rend;
510 	// state
511 	rend = gtk_cell_renderer_pixbuf_new ();
512 	gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (pOneWidget), -1, NULL, rend, "pixbuf", CAIRO_DOCK_MODEL_ICON, NULL);
513 	// nom du theme
514 	rend = gtk_cell_renderer_text_new ();
515 	col = gtk_tree_view_column_new_with_attributes (_("Theme"), rend, "text", CAIRO_DOCK_MODEL_NAME, NULL);
516 	gtk_tree_view_column_set_sort_column_id (col, CAIRO_DOCK_MODEL_NAME);
517 	gtk_tree_view_append_column (GTK_TREE_VIEW (pOneWidget), col);
518 	// rating
519 	GtkListStore *note_list = _make_rate_list_store ();
520 	rend = gtk_cell_renderer_combo_new ();
521 	g_object_set (G_OBJECT (rend),
522 		"text-column", 1,
523 		"model", note_list,
524 		"has-entry", FALSE,
525 		"editable", TRUE,
526 		NULL);
527 	g_signal_connect (G_OBJECT (rend), "edited", (GCallback) _change_rating, pModel);
528 	col = gtk_tree_view_column_new_with_attributes (_("Rating"), rend, "text", CAIRO_DOCK_MODEL_ORDER, NULL);
529 	gtk_tree_view_column_set_sort_column_id (col, CAIRO_DOCK_MODEL_ORDER);
530 	gtk_tree_view_column_set_cell_data_func (col, rend, (GtkTreeCellDataFunc)_cairo_dock_render_rating, NULL, NULL);
531 	gtk_tree_view_append_column (GTK_TREE_VIEW (pOneWidget), col);
532 	// soberty
533 	rend = gtk_cell_renderer_text_new ();
534 	col = gtk_tree_view_column_new_with_attributes (_("Sobriety"), rend, "text", CAIRO_DOCK_MODEL_ORDER2, NULL);
535 	gtk_tree_view_column_set_sort_column_id (col, CAIRO_DOCK_MODEL_ORDER2);
536 	gtk_tree_view_column_set_cell_data_func (col, rend, (GtkTreeCellDataFunc)_cairo_dock_render_sobriety, NULL, NULL);
537 	gtk_tree_view_append_column (GTK_TREE_VIEW (pOneWidget), col);
538 	// vertical scrollbar
539 	pThemesWidget->pTreeView = pOneWidget;
540 	GtkWidget *pScrolledWindow = gtk_scrolled_window_new (NULL, NULL);
541 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (pScrolledWindow), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
542 	#if GTK_CHECK_VERSION (3, 8, 0)
543 	gtk_container_add (GTK_CONTAINER (pScrolledWindow), pOneWidget);
544 	#else
545 	gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (pScrolledWindow), pOneWidget);
546 	#endif
547 	// menu
548 	g_signal_connect (G_OBJECT (pOneWidget), "button-release-event", G_CALLBACK (_on_click_tree_view), pThemesWidget);
549 	g_signal_connect (G_OBJECT (pOneWidget), "button-press-event", G_CALLBACK (_on_click_tree_view), pThemesWidget);
550 
551 	//\______________ add a preview widget next to the treeview
552 	GtkWidget *pPreviewBox = cairo_dock_gui_make_preview_box (GTK_WIDGET (pThemesWidget->pMainWindow), pOneWidget, FALSE, 2, NULL, CAIRO_DOCK_SHARE_DATA_DIR"/images/"CAIRO_DOCK_LOGO, pDataGarbage);
553 	GtkWidget *pWidgetBox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, CAIRO_DOCK_GUI_MARGIN);
554 	gtk_box_pack_start (GTK_BOX (pWidgetBox), pScrolledWindow, TRUE, TRUE, 0);
555 	gtk_box_pack_start (GTK_BOX (pWidgetBox), pPreviewBox, TRUE, TRUE, 0);
556 
557 	//\______________ get the user, shared and distant themes.
558 	_fill_treeview_with_themes (pThemesWidget);
559 
560 	//\______________ insert the widget.
561 	myWidget->pSubWidgetList = g_slist_append (myWidget->pSubWidgetList, pOneWidget);
562 	gtk_box_pack_start (GTK_BOX (myWidget->pKeyBox), pWidgetBox, FALSE, FALSE, 0);
563 }
564 
_ignore_server_themes(G_GNUC_UNUSED const gchar * cThemeName,CairoDockPackage * pTheme,G_GNUC_UNUSED gpointer data)565 static gboolean _ignore_server_themes (G_GNUC_UNUSED const gchar *cThemeName, CairoDockPackage *pTheme, G_GNUC_UNUSED gpointer data)
566 {
567 	gchar *cVersionFile = g_strdup_printf ("%s/last-modif", pTheme->cPackagePath);
568 	gboolean bRemove = g_file_test (cVersionFile, G_FILE_TEST_EXISTS);
569 	g_free (cVersionFile);
570 	return bRemove;
571 }
_fill_combo_with_user_themes(ThemesWidget * pThemesWidget)572 static void _fill_combo_with_user_themes (ThemesWidget *pThemesWidget)
573 {
574 	const gchar *cUserThemesDir = g_cThemesDirPath;
575 	GHashTable *pThemeTable = cairo_dock_list_packages (NULL, cUserThemesDir, NULL, NULL);
576 	g_hash_table_foreach_remove (pThemeTable, (GHRFunc)_ignore_server_themes, NULL);  // ignore themes coming from the server
577 	GtkTreeModel *pModel = gtk_combo_box_get_model (GTK_COMBO_BOX (pThemesWidget->pCombo));
578 	gtk_list_store_clear (GTK_LIST_STORE (pModel));  // for the reload
579 	cairo_dock_fill_model_with_themes (GTK_LIST_STORE (pModel), pThemeTable, NULL);
580 	g_hash_table_destroy (pThemeTable);
581 }
_make_combo_for_user_themes(ThemesWidget * pThemesWidget,GPtrArray * pDataGarbage,G_GNUC_UNUSED GKeyFile * pKeyFile)582 static void _make_combo_for_user_themes (ThemesWidget *pThemesWidget, GPtrArray *pDataGarbage, G_GNUC_UNUSED GKeyFile* pKeyFile)
583 {
584 	//\______________ get the group/key widget
585 	GSList *pWidgetList = pThemesWidget->widget.pWidgetList;
586 	CairoDockGroupKeyWidget *myWidget = cairo_dock_gui_find_group_key_widget_in_list (pWidgetList, "Save", "theme name");
587 	g_return_if_fail (myWidget != NULL);
588 
589 	//\______________ build the combo-box.
590 	GtkWidget *pOneWidget = cairo_dock_gui_make_combo (TRUE);
591 	pThemesWidget->pCombo = pOneWidget;
592 
593 	//\______________ get the user themes.
594 	_fill_combo_with_user_themes (pThemesWidget);
595 
596 	//\______________ insert the widget.
597 	GtkWidget *pHBox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, CAIRO_DOCK_GUI_MARGIN);
598 	GtkWidget *pLabel = gtk_label_new (_("Save as:"));
599 
600 	//\______________ add a preview widget under to the combo
601 	GtkWidget *pPreviewBox = cairo_dock_gui_make_preview_box (GTK_WIDGET (pThemesWidget->pMainWindow), pOneWidget, FALSE, 1, NULL, NULL, pDataGarbage);
602 	GtkWidget *pWidgetBox = gtk_box_new (GTK_ORIENTATION_VERTICAL, CAIRO_DOCK_GUI_MARGIN);
603 
604 	GtkWidget *pAlign = gtk_alignment_new (0, 0, 1, 0);
605 	gtk_container_add (GTK_CONTAINER (pAlign), pLabel);
606 	gtk_box_pack_start (GTK_BOX (pHBox), pAlign, FALSE, FALSE, 0);
607 	gtk_box_pack_end (GTK_BOX (pHBox), pWidgetBox, FALSE, FALSE, 0);
608 	gtk_box_pack_start (GTK_BOX (pWidgetBox), pOneWidget, FALSE, FALSE, 0);
609 	gtk_box_pack_start (GTK_BOX (pWidgetBox), pPreviewBox, FALSE, FALSE, 0);
610 	gtk_box_pack_start (GTK_BOX (myWidget->pKeyBox), pHBox, TRUE, TRUE, 0);
611 
612 	myWidget->pSubWidgetList = g_slist_append (myWidget->pSubWidgetList, pOneWidget);
613 }
_build_themes_widget(ThemesWidget * pThemesWidget)614 static void _build_themes_widget (ThemesWidget *pThemesWidget)
615 {
616 	GtkWindow *pMainWindow = pThemesWidget->pMainWindow;
617 	GKeyFile* pKeyFile = cairo_dock_open_key_file (pThemesWidget->cInitConfFile);
618 
619 	GSList *pWidgetList = NULL;
620 	GPtrArray *pDataGarbage = g_ptr_array_new ();
621 	if (pKeyFile != NULL)
622 	{
623 		pThemesWidget->widget.pWidget = cairo_dock_build_key_file_widget (pKeyFile,
624 			NULL,  // gettext domain
625 			GTK_WIDGET (pMainWindow),  // main window
626 			&pWidgetList,
627 			pDataGarbage,
628 			NULL);
629 		if (cairo_dock_current_theme_need_save ())
630 			gtk_notebook_set_current_page (GTK_NOTEBOOK (pThemesWidget->widget.pWidget), 1);  // "Save"
631 	}
632 	pThemesWidget->widget.pWidgetList = pWidgetList;
633 	pThemesWidget->widget.pDataGarbage = pDataGarbage;
634 
635 	// complete the widget
636 	_make_tree_view_for_themes (pThemesWidget, pDataGarbage, pKeyFile);
637 	_make_combo_for_user_themes (pThemesWidget, pDataGarbage, pKeyFile);
638 
639 	g_key_file_free (pKeyFile);
640 }
641 
cairo_dock_themes_widget_new(GtkWindow * pMainWindow)642 ThemesWidget *cairo_dock_themes_widget_new (GtkWindow *pMainWindow)
643 {
644 	ThemesWidget *pThemesWidget = g_new0 (ThemesWidget, 1);
645 	pThemesWidget->widget.iType = WIDGET_THEMES;
646 	pThemesWidget->widget.apply = _themes_widget_apply;
647 	pThemesWidget->widget.reset = _themes_widget_reset;
648 	pThemesWidget->widget.reload = _themes_widget_reload;
649 	pThemesWidget->pMainWindow = pMainWindow;
650 
651 	// build the widget from a .conf file
652 	pThemesWidget->cInitConfFile = _cairo_dock_build_temporary_themes_conf_file ();  // sera supprime a la destruction de la fenetre.
653 
654 	_build_themes_widget (pThemesWidget);
655 
656 	return pThemesWidget;
657 }
658 
659 
_themes_widget_apply(CDWidget * pCdWidget)660 static void _themes_widget_apply (CDWidget *pCdWidget)
661 {
662 	ThemesWidget *pThemesWidget = THEMES_WIDGET (pCdWidget);
663 
664 	//\_______________ open and update the conf file.
665 	GKeyFile *pKeyFile = cairo_dock_open_key_file (pThemesWidget->cInitConfFile);
666 	g_return_if_fail (pKeyFile != NULL);
667 
668 	cairo_dock_update_keyfile_from_widget_list (pKeyFile, pThemesWidget->widget.pWidgetList);
669 
670 	cairo_dock_write_keys_to_file (pKeyFile, pThemesWidget->cInitConfFile);
671 
672 	//\_______________ take the actions relatively to the current page.
673 	int iNumPage = gtk_notebook_get_current_page (GTK_NOTEBOOK (pThemesWidget->widget.pWidget));
674 	gchar *cNewThemeName = NULL;
675 	switch (iNumPage)
676 	{
677 		case 0:  // load a theme
678 			cairo_dock_set_status_message (NULL, _("Importing theme ..."));
679 			_cairo_dock_load_theme (pKeyFile, pThemesWidget);  // we don't reload the window in this case, we'll do it once the theme has been imported successfully
680 		break;
681 
682 		case 1:  // save current theme
683 			cNewThemeName = _cairo_dock_save_current_theme (pKeyFile);
684 			if (cNewThemeName != NULL)
685 			{
686 				cairo_dock_set_status_message (NULL, _("Theme has been saved"));
687 
688 				_fill_treeview_with_themes (pThemesWidget);
689 
690 				_fill_combo_with_user_themes (pThemesWidget);
691 
692 				cairo_dock_gui_select_in_combo_full (pThemesWidget->pCombo, cNewThemeName, TRUE);
693 			}
694 		break;
695 	}
696 	g_key_file_free (pKeyFile);
697 }
698 
699 
_themes_widget_reset(CDWidget * pCdWidget)700 static void _themes_widget_reset (CDWidget *pCdWidget)
701 {
702 	ThemesWidget *pThemesWidget = THEMES_WIDGET (pCdWidget);
703 
704 	g_remove (pThemesWidget->cInitConfFile);
705 	g_free (pThemesWidget->cInitConfFile);
706 
707 	gldi_task_discard (pThemesWidget->pImportTask);
708 
709 	if (pThemesWidget->iSidPulse != 0)
710 		g_source_remove (pThemesWidget->iSidPulse);
711 
712 	if (pThemesWidget->pWaitingDialog != NULL)
713 		gtk_widget_destroy (pThemesWidget->pWaitingDialog);
714 
715 	memset (pCdWidget+1, 0, sizeof (ThemesWidget) - sizeof (CDWidget));  // reset all our parameters.
716 }
717 
718 
_themes_widget_reload(CDWidget * pCdWidget)719 static void _themes_widget_reload (CDWidget *pCdWidget)
720 {
721 	ThemesWidget *pThemesWidget = THEMES_WIDGET (pCdWidget);
722 
723 	cairo_dock_widget_destroy_widget (pCdWidget);
724 
725 	_build_themes_widget (pThemesWidget);
726 }
727