1 /* Gnome Music Player Client (GMPC)
2  * Copyright (C) 2004-2011 Qball Cow <qball@gmpclient.org>
3  * Project homepage: http://gmpclient.org/
4 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14 
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 
20 #include <gtk/gtk.h>
21 #include <gdk/gdkkeysyms.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include "main.h"
25 #include "misc.h"
26 #include "playlist3.h"
27 #include "playlist3-file-browser.h"
28 #include "playlist3-find2-browser.h"
29 #include "advanced-search.h"
30 #include "gmpc-mpddata-model.h"
31 #include "gmpc-mpddata-treeview.h"
32 #include "playlist3-playlist-editor.h"
33 #include "gmpc-extras.h"
34 
35 #define LOG_DOMAIN "FileBrowser"
36 
37 static void pl3_file_browser_plugin_init(void);
38 
39 static gboolean pl3_file_browser_is_field_supported(int tag);
40 static MpdData *pl3_file_browser_is_search(const int num_field, const gchar * search_string, GError ** error);
41 
42 static int pl3_file_browser_option_menu(GmpcMpdDataTreeview *tree, GtkMenu *menu);
43 
44 static void pl3_file_browser_destroy(void);
45 static void pl3_file_browser_add(GtkWidget * cat_tree);
46 static void pl3_file_browser_unselected(GtkWidget * container);
47 static void pl3_file_browser_selected(GtkWidget * container);
48 static void pl3_file_browser_fill_tree(GtkWidget * tree, GtkTreeIter * iter, GtkTreePath * tpath, gpointer user_data);
49 static void pl3_file_browser_collapse_row(GtkTreeView * tree, GtkTreeIter * iter, GtkTreePath * path,
50 										  gpointer user_data);
51 static int pl3_file_browser_cat_popup(GtkWidget * tree, GdkEventButton * event, gpointer user_data);
52 static gboolean pl3_file_browser_cat_key_press(GtkWidget * tree, GdkEventKey * event, gpointer data);
53 static void pl3_file_browser_delete_playlist_from_right(GtkMenuItem * bt);
54 /* testing */
55 static void pl3_file_browser_reupdate(void);
56 static void pl3_file_browser_save_myself(void);
57 static int pl3_file_browser_add_go_menu(GtkWidget * menu);
58 static void pl3_file_browser_activate(void);
59 static gboolean pl3_file_browser_button_release_event(GtkWidget * but, GdkEventButton * event);
60 static void pl3_file_browser_row_activated(GtkTreeView * tree, GtkTreePath * tp);
61 static void pl3_file_browser_add_selected(void);
62 static void pl3_file_browser_replace_selected(void);
63 static int pl3_file_browser_playlist_key_press(GtkWidget * tree, GdkEventKey * event);
64 static void pl3_file_browser_show_info(void);
65 static void pl3_file_browser_view_folder(GtkTreeSelection * selection, gpointer user_data);
66 static void pl3_file_browser_update_folder(void);
67 static void pl3_file_browser_add_folder(void);
68 static void pl3_file_browser_delete_playlist(GtkMenuItem * bt);
69 static void pl3_file_browser_connection_changed(MpdObj * mi, int connect, gpointer data);
70 static void pl3_file_browser_status_changed(MpdObj * mi, ChangedStatusType what, void *data);
71 static void pl3_file_browser_disconnect(void);
72 
73 GtkTreeRowReference *pl3_fb_tree_ref = NULL;
74 
75 enum
76 {
77 	PL3_FB_ICON = 0,
78 	PL3_FB_NAME = 1,
79 	PL3_FB_PATH = 2,
80 	PL3_FB_OPEN = 3
81 };
82 /**
83  * Get/Set enabled
84  */
pl3_file_browser_get_enabled(void)85 static int pl3_file_browser_get_enabled(void)
86 {
87 	return cfg_get_single_value_as_int_with_default(config, "file-browser", "enable", TRUE);
88 }
89 
pl3_file_browser_set_enabled(int enabled)90 static void pl3_file_browser_set_enabled(int enabled)
91 {
92 	cfg_set_single_value_as_int(config, "file-browser", "enable", enabled);
93 	if (enabled)
94 	{
95 		GtkTreeView *tree = playlist3_get_category_tree_view();
96 		pl3_file_browser_add((GtkWidget *) tree);
97 	} else if (!enabled)
98 	{
99 		pl3_file_browser_destroy();
100 	}
101 }
102 
103 /**
104  * Plugin structure
105  */
106 gmpcPlBrowserPlugin file_browser_gbp = {
107 	.add = pl3_file_browser_add,
108 	.selected = pl3_file_browser_selected,
109 	.unselected = pl3_file_browser_unselected,
110 	.add_go_menu = pl3_file_browser_add_go_menu,
111 	.integrate_search_field_supported = pl3_file_browser_is_field_supported,
112 	.integrate_search = pl3_file_browser_is_search,
113     .song_list_option_menu = pl3_file_browser_option_menu
114 };
115 
116 gmpcPlugin file_browser_plug = {
117 	.name = N_("Database"),
118 	.version = {1, 1, 1}
119 	,
120 	.plugin_type = GMPC_PLUGIN_PL_BROWSER | GMPC_INTERNALL,
121 	.init = pl3_file_browser_plugin_init,
122 	.destroy = pl3_file_browser_destroy,
123 	.browser = &file_browser_gbp,
124 	.mpd_status_changed = pl3_file_browser_status_changed,
125 	.mpd_connection_changed = pl3_file_browser_connection_changed,
126 	.get_enabled = pl3_file_browser_get_enabled,
127 	.set_enabled = pl3_file_browser_set_enabled,
128 	.save_yourself = pl3_file_browser_save_myself
129 };
130 
131 /* internal */
132 GtkWidget *pl3_fb_tree = NULL;
133 GtkWidget *pl3_fb_vbox = NULL;
134 GtkWidget *pl3_fb_tree_search = NULL;
135 GmpcMpdDataModel *pl3_fb_store2 = NULL;
136 static GtkTreeStore *pl3_fb_dir_store = NULL;
137 static GtkWidget *pl3_fb_dir_tree = NULL;
138 static GtkWidget *pl3_fb_warning_box = NULL;
139 
pl3_file_browser_dir_row_activated(GtkTreeView * tree,GtkTreePath * tp,GtkTreeViewColumn * col,gpointer user_data)140 static void pl3_file_browser_dir_row_activated(GtkTreeView * tree, GtkTreePath * tp, GtkTreeViewColumn * col,
141 											   gpointer user_data)
142 {
143 	if (!mpd_check_connected(connection))
144 		return;
145 	if (gtk_tree_view_row_expanded(tree, tp))
146 		gtk_tree_view_collapse_row(tree, tp);
147 	else
148 		gtk_tree_view_expand_row(tree, tp, FALSE);
149 }
150 
playtime_changed(GmpcMpdDataModel * model,gulong playtime)151 static void playtime_changed(GmpcMpdDataModel * model, gulong playtime)
152 {
153 	if (pl3_cat_get_selected_browser() == file_browser_plug.id)
154 	{
155 		playlist3_show_playtime(playtime);
156 	}
157 }
pl3_file_support_help_button_clicked(GObject * a)158 static void pl3_file_support_help_button_clicked(GObject *a)
159 {
160 	open_uri("ghelp:gmpc#ProblemSolving");
161 }
pl3_file_browser_init(void)162 static void pl3_file_browser_init(void)
163 {
164 	GtkCellRenderer *renderer;
165 	GtkTreeViewColumn *column;
166 	GtkWidget *pl3_fb_sw = NULL;
167 	GtkWidget *vbox, *sw, *tree,*label;
168     GtkWidget *misc, *button;
169 
170 	pl3_fb_store2 = gmpc_mpddata_model_new();
171 	gmpc_mpddata_model_disable_image(GMPC_MPDDATA_MODEL(pl3_fb_store2));
172 	g_signal_connect(G_OBJECT(pl3_fb_store2), "playtime_changed", G_CALLBACK(playtime_changed), NULL);
173 
174 	pl3_fb_vbox = gtk_hpaned_new();
175 	gmpc_paned_size_group_add_paned(GMPC_PANED_SIZE_GROUP(paned_size_group), GTK_PANED(pl3_fb_vbox));
176 	vbox = gtk_vbox_new(FALSE, 6);
177 
178 	/* icon id, name, full path */
179 	pl3_fb_dir_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
180 
181 	sw = gtk_scrolled_window_new(NULL, NULL);
182 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
183 	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN);
184 	pl3_fb_dir_tree = tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(pl3_fb_dir_store));
185 	gtk_tree_view_set_enable_tree_lines(GTK_TREE_VIEW(tree), TRUE);
186 	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree), TRUE);
187 	column = gtk_tree_view_column_new();
188 	gtk_tree_view_column_set_title(column, _("Directories"));
189 	renderer = gtk_cell_renderer_pixbuf_new();
190 	gtk_tree_view_column_pack_start(column, renderer, FALSE);
191 	gtk_tree_view_column_add_attribute(column, renderer, "icon-name", PL3_FB_ICON);
192 	gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
193 	renderer = gtk_cell_renderer_text_new();
194 	gtk_tree_view_column_pack_start(column, renderer, TRUE);
195 	gtk_tree_view_column_add_attribute(column, renderer, "text", PL3_FB_NAME);
196 	gtk_tree_view_insert_column(GTK_TREE_VIEW(tree), column, -1);
197 
198 	gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(tree), TRUE);
199 	gtk_tree_view_set_search_column(GTK_TREE_VIEW(pl3_fb_dir_tree), PL3_FB_NAME);
200 	/* set the search column */
201 	gtk_tree_view_set_search_column(GTK_TREE_VIEW(tree), PL3_FB_NAME);
202 
203 	g_signal_connect(G_OBJECT(tree), "row-expanded", G_CALLBACK(pl3_file_browser_fill_tree), NULL);
204 	g_signal_connect(G_OBJECT(tree), "row-collapsed", G_CALLBACK(pl3_file_browser_collapse_row), NULL);
205 	g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree))), "changed",
206 					 G_CALLBACK(pl3_file_browser_view_folder), NULL);
207 	g_signal_connect(G_OBJECT(tree), "button-release-event", G_CALLBACK(pl3_file_browser_cat_popup), NULL);
208 	g_signal_connect(G_OBJECT(tree), "key-press-event", G_CALLBACK(pl3_file_browser_cat_key_press), NULL);
209 	g_signal_connect(G_OBJECT(tree), "row-activated", G_CALLBACK(pl3_file_browser_dir_row_activated), NULL);
210 
211 	gtk_container_add(GTK_CONTAINER(sw), tree);
212 	gtk_widget_show_all(sw);
213 	gtk_paned_add1(GTK_PANED(pl3_fb_vbox), sw);
214 
215 	/* set up the tree */
216 	pl3_fb_tree = gmpc_mpddata_treeview_new("file-browser", TRUE, GTK_TREE_MODEL(pl3_fb_store2));
217 	gmpc_mpddata_treeview_enable_click_fix(GMPC_MPDDATA_TREEVIEW(pl3_fb_tree));
218 	gtk_tree_view_set_search_column(GTK_TREE_VIEW(pl3_fb_tree), MPDDATA_MODEL_COL_SONG_TITLE);
219 	/* setup signals */
220 	g_signal_connect(G_OBJECT(pl3_fb_tree), "row-activated", G_CALLBACK(pl3_file_browser_row_activated), NULL);
221 	g_signal_connect(G_OBJECT(pl3_fb_tree), "button-release-event", G_CALLBACK(pl3_file_browser_button_release_event),
222 					 NULL);
223 	g_signal_connect(G_OBJECT(pl3_fb_tree), "key-press-event", G_CALLBACK(pl3_file_browser_playlist_key_press), NULL);
224 
225 	/* set up the scrolled window */
226 	pl3_fb_sw = gtk_scrolled_window_new(NULL, NULL);
227 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(pl3_fb_sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
228 	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(pl3_fb_sw), GTK_SHADOW_ETCHED_IN);
229 	gtk_container_add(GTK_CONTAINER(pl3_fb_sw), pl3_fb_tree);
230 
231 	gtk_box_pack_start(GTK_BOX(vbox), pl3_fb_sw, TRUE, TRUE, 0);
232 	gtk_widget_show_all(pl3_fb_sw);
233 
234     /******************************************/
235 	/* Warning box for when there is no music */
236     /******************************************/
237 	pl3_fb_warning_box = gtk_vbox_new(FALSE, 6);
238     /* label */
239     label = gtk_label_new("");
240 	gtk_label_set_markup(GTK_LABEL(label),
241 						 _("It seems you have no music in your database.\n"
242 						   "To add music, copy the music to your <i>music_directory</i> as specified in your mpd config file.\n"
243 						   "Then update the database. (Server->Update Database)"));
244 	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
245     gtk_widget_show(label);
246     gtk_box_pack_start(GTK_BOX(pl3_fb_warning_box), label, FALSE, FALSE, 0);
247 
248     /* help button */
249     misc    = gtk_alignment_new(0, 0.5, 0, 0);
250     button  = gtk_button_new_from_stock(GTK_STOCK_HELP);
251     g_signal_connect(G_OBJECT(button), "clicked",
252             G_CALLBACK(pl3_file_support_help_button_clicked), NULL);
253     gtk_container_add(GTK_CONTAINER(misc), button);
254     gtk_widget_show(button);
255     gtk_widget_show_all(misc);
256     gtk_box_pack_start(GTK_BOX(pl3_fb_warning_box), misc, FALSE, FALSE, 0);
257     gtk_widget_set_no_show_all(pl3_fb_warning_box, TRUE);
258 
259 
260 	gtk_box_pack_end(GTK_BOX(vbox), pl3_fb_warning_box, FALSE, TRUE, 0);
261     gtk_paned_add2(GTK_PANED(pl3_fb_vbox), vbox);
262     /* set initial state */
263     gtk_widget_show(vbox);
264 	gtk_widget_show(pl3_fb_vbox);
265 	g_object_ref_sink(G_OBJECT(pl3_fb_vbox));
266 }
267 
pl3_file_browser_add_folder(void)268 static void pl3_file_browser_add_folder(void)
269 {
270 	GtkTreeSelection *selec = gtk_tree_view_get_selection(GTK_TREE_VIEW(pl3_fb_dir_tree));
271 	GtkTreeModel *model = GTK_TREE_MODEL(pl3_fb_dir_store);
272 	GtkTreeIter iter;
273 
274 	if (!mpd_check_connected(connection))
275 	{
276 		return;
277 	}
278 	if (gtk_tree_selection_get_selected(selec, &model, &iter))
279 	{
280 		char *path, *icon;
281 		char *message = NULL;
282 		gtk_tree_model_get(model, &iter, PL3_FB_PATH, &path, PL3_FB_ICON, &icon, -1);
283 
284 		message = g_strdup_printf(_("Added folder '%s' recursively"), path);
285 		pl3_push_statusbar_message(message);
286 		q_free(message);
287 		if (strcmp("media-playlist", icon))
288 		{
289 			mpd_playlist_queue_add(connection, path);
290 		} else
291 		{
292 			mpd_playlist_queue_load(connection, path);
293 		}
294 		mpd_playlist_queue_commit(connection);
295 		q_free(path);
296 		q_free(icon);
297 	}
298 }
299 
pl3_file_browser_update_folder(void)300 static void pl3_file_browser_update_folder(void)
301 {
302 	GtkTreeSelection *selec = gtk_tree_view_get_selection((GtkTreeView *) pl3_fb_dir_tree);
303 	GtkTreeModel *model = GTK_TREE_MODEL(pl3_fb_dir_store);
304 	GtkTreeIter iter;
305 
306 	if (!mpd_check_connected(connection))
307 	{
308 		return;
309 	}
310 	if (gtk_tree_selection_get_selected(selec, &model, &iter))
311 	{
312 		char *path;
313 		gtk_tree_model_get(model, &iter, PL3_FB_PATH, &path, -1);
314 		if (path)
315 		{
316 			mpd_database_update_dir(connection, path);
317 			q_free(path);
318 		}
319 	}
320 
321 }
322 
pl3_file_browser_update_folder_left_pane(void)323 static void pl3_file_browser_update_folder_left_pane(void)
324 {
325 	GtkTreeModel *model = GTK_TREE_MODEL(pl3_fb_store2);
326 	GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(pl3_fb_tree));
327 	GtkTreeIter iter;
328 
329 	if (!mpd_check_connected(connection))
330 		return;
331 
332 	if (gtk_tree_selection_count_selected_rows(selection) == 1)
333 	{
334 		GList *list = gtk_tree_selection_get_selected_rows(selection, &model);
335 		/* free list */
336 		if (gtk_tree_model_get_iter(model, &iter, list->data))
337 		{
338 			char *path;
339 			gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_PATH, &path, -1);
340 			if (path)
341 			{
342 				mpd_database_update_dir(connection, path);
343 				q_free(path);
344 			}
345 		}
346 		g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
347 		g_list_free(list);
348 	}
349 }
350 
pl3_file_browser_replace_folder(void)351 static void pl3_file_browser_replace_folder(void)
352 {
353 	mpd_playlist_clear(connection);
354 	if (mpd_check_connected(connection))
355 	{
356 		pl3_file_browser_add_folder();
357 		mpd_player_play(connection);
358 	}
359 }
360 
361 /* add's the toplevel entry for the file browser, it also add's a fantom child */
pl3_file_browser_add(GtkWidget * cat_tree)362 static void pl3_file_browser_add(GtkWidget * cat_tree)
363 {
364 	GtkTreeIter iter;
365 	GtkTreePath *path;
366 	gint pos;
367 	INIT_TIC_TAC()
368 	pos = cfg_get_single_value_as_int_with_default(config, "file-browser", "position", 2);
369 
370 	TEC("get pos ")
371 	playlist3_insert_browser(&iter, pos);
372 	TEC("insert browser")
373 	gtk_list_store_set(GTK_LIST_STORE(pl3_tree), &iter,
374 					   PL3_CAT_TYPE, file_browser_plug.id,
375 					   PL3_CAT_TITLE, _("Database"), PL3_CAT_ICON_ID, "gmpc-database", -1);
376 	TEC("set list store")
377 
378 	if (pl3_fb_tree_ref)
379 	{
380 		gtk_tree_row_reference_free(pl3_fb_tree_ref);
381 		pl3_fb_tree_ref = NULL;
382 	}
383 	TEC("Free references")
384 
385 	path = gtk_tree_model_get_path(GTK_TREE_MODEL(pl3_tree), &iter);
386 	if (path)
387 	{
388 		pl3_fb_tree_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(pl3_tree), path);
389 		gtk_tree_path_free(path);
390 	}
391 	TEC("New references")
392 
393 
394 }
395 
directory_sort_func(gpointer ppaa,gpointer ppbb,gpointer data)396 static int directory_sort_func(gpointer ppaa, gpointer ppbb, gpointer data)
397 {
398 	MpdData_real *a = *(MpdData_real **) ppaa;
399 	MpdData_real *b = *(MpdData_real **) ppbb;
400 	int val = 0;
401 	if (!(a && b))
402 		return val;
403 	if (a->type != b->type)
404 		return a->type - b->type;
405 	if ((a->type == MPD_DATA_TYPE_DIRECTORY) && (b->type == MPD_DATA_TYPE_DIRECTORY))
406 	{
407 		if (a->directory && b->directory)
408 		{
409 			gchar *sa, *sb;
410 			sa = g_utf8_strdown(a->directory, -1);
411 			sb = g_utf8_strdown(b->directory, -1);
412 			val = g_utf8_collate(sa, sb);
413 			g_free(sa);
414 			g_free(sb);
415 		}
416 	}
417 	return val;
418 }
419 
420 /* Reverse sort function, needed for prepending instead of appending */
directory_sort_func_inv(gpointer a,gpointer b,gpointer d)421 static int directory_sort_func_inv(gpointer a, gpointer b, gpointer d)
422 {
423 	return -directory_sort_func(a, b, d);
424 }
425 
pl3_file_browser_reupdate_folder(GtkTreeIter * iter)426 static void pl3_file_browser_reupdate_folder(GtkTreeIter * iter)
427 {
428 	MpdData *data = NULL;
429 	gchar *path = NULL;
430 	gboolean temp = FALSE;
431 	GtkTreeModel *model = GTK_TREE_MODEL(pl3_fb_dir_store);
432 	gtk_tree_model_get(GTK_TREE_MODEL(pl3_fb_dir_store), iter, 3, &temp, 2, &path, -1);
433 	if (path && temp)
434 	{
435 		GtkTreeIter child, child2, child3;
436 		data = mpd_database_get_directory(connection, path);
437 		data = misc_sort_mpddata(data, (GCompareDataFunc) directory_sort_func, NULL);
438 		g_free(path);
439 		if (gtk_tree_model_iter_children(model, &child, iter))
440 		{
441 			gchar *test_path = NULL;
442 			gboolean has_next = FALSE;
443 			do
444 			{
445 				gtk_tree_model_get(model, &child, 3, &temp, 2, &test_path, -1);
446 
447 				if (data == NULL)
448 				{
449 					/* if no more data, remove the subdir */
450 					has_next = gtk_tree_store_remove(pl3_fb_dir_store, &child);
451 				} else
452 				{
453 					int compare = 0;
454 					/* get the next directory */
455 					while (data && data->type != MPD_DATA_TYPE_DIRECTORY)
456 						data = mpd_data_get_next(data);
457 					if (data)
458 					{
459 						compare = strcmp(data->directory, test_path);
460 						if (compare < 0)
461 						{
462 							gchar *basename = g_path_get_basename(data->directory);
463 							gtk_tree_store_insert_before(pl3_fb_dir_store, &child2, iter, &child);
464 							gtk_tree_store_set(pl3_fb_dir_store, &child2,
465 											   PL3_FB_ICON, "gtk-open",
466 											   PL3_FB_NAME, basename,
467 											   PL3_FB_PATH, data->directory, PL3_FB_OPEN, FALSE, -1);
468 
469 							gtk_tree_store_append(pl3_fb_dir_store, &child3, &child2);
470 							q_free(basename);
471 
472 							/* if the new dir is smaller the temp, we add it. */
473 							data = mpd_data_get_next(data);
474 							has_next = TRUE;
475 						} else if (compare > 0)
476 						{
477 							/* if it's bigger, we delete the row */
478 							has_next = gtk_tree_store_remove(pl3_fb_dir_store, &child);
479 
480 						} else
481 						{
482 							/* if equal we process children if available */
483 							if (temp)
484 							{
485 								pl3_file_browser_reupdate_folder(&child);
486 							}
487 							/* move to next entry in both */
488 							has_next = gtk_tree_model_iter_next(model, &child);
489 							data = mpd_data_get_next(data);
490 						}
491 					}
492 				}
493 				g_free(test_path);
494 			} while (has_next);
495 			if (data)
496 			{
497 				do
498 				{
499 					if (data->type == MPD_DATA_TYPE_DIRECTORY)
500 					{
501 						gchar *basename = g_path_get_basename(data->directory);
502 						gtk_tree_store_append(pl3_fb_dir_store, &child2, iter);
503 						gtk_tree_store_set(pl3_fb_dir_store, &child2,
504 										   PL3_FB_ICON, "gtk-open",
505 										   PL3_FB_NAME, basename, PL3_FB_PATH, data->directory, PL3_FB_OPEN, FALSE, -1);
506 						gtk_tree_store_append(pl3_fb_dir_store, &child3, &child2);
507 						q_free(basename);
508 					}
509 				} while ((data = mpd_data_get_next(data)));
510 			}
511 		}
512 	}
513 }
514 
pl3_file_browser_reupdate(void)515 static void pl3_file_browser_reupdate(void)
516 {
517 
518 	if (pl3_fb_vbox)
519 	{
520 		GtkTreeIter iter;
521 
522 		GtkTreeModel *model = GTK_TREE_MODEL(pl3_fb_dir_store);
523 
524 		if (mpd_stats_get_total_songs(connection) == 0 && !mpd_status_db_is_updating(connection))
525 		{
526 			gtk_widget_show(pl3_fb_warning_box);
527 		} else
528 		{
529 			gtk_widget_hide(pl3_fb_warning_box);
530 		}
531 
532 		if (gtk_tree_model_get_iter_first(model, &iter))
533 		{
534 			GtkTreeIter child;
535 			if (!gtk_tree_model_iter_children(model, &child, &iter))
536 			{
537 				gtk_tree_store_set(GTK_TREE_STORE(model), &iter, PL3_FB_OPEN, FALSE, -1);
538 				gtk_tree_store_append(pl3_fb_dir_store, &child, &iter);
539 			}
540 			pl3_file_browser_reupdate_folder(&iter);
541 			pl3_file_browser_view_folder(gtk_tree_view_get_selection(GTK_TREE_VIEW(pl3_fb_dir_tree)), NULL);
542 		}
543 	}
544 }
545 
pl3_file_browser_view_folder(GtkTreeSelection * selection,gpointer user_data)546 static void pl3_file_browser_view_folder(GtkTreeSelection * selection, gpointer user_data)
547 {
548 	MpdData *data = NULL;
549 	char *path = NULL, *icon = NULL;
550 	GtkTreeModel *model = GTK_TREE_MODEL(pl3_fb_dir_store);
551 	GtkTreeIter iter_cat;
552 	/* Clear the view */
553 	gmpc_mpddata_model_set_mpd_data(pl3_fb_store2, NULL);
554 
555 	if (!gtk_tree_selection_get_selected(selection, &model, &iter_cat))
556 		return;
557 
558 	/* check the connection state and when its valid proceed */
559 	if (!mpd_check_connected(connection))
560 	{
561 		return;
562 	}
563 	gtk_tree_model_get(GTK_TREE_MODEL(pl3_fb_dir_store), &iter_cat, PL3_FB_PATH, &path, PL3_FB_ICON, &icon, -1);
564 	if (strcmp("media-playlist", icon))
565 	{
566 		data = mpd_database_get_directory(connection, path);
567 	} else
568 	{
569 		data = mpd_database_get_playlist_content(connection, path);
570 	}
571 	/* Check, and set the up arrow in the model */
572 	if (!strcmp(path, "/"))
573 		gmpc_mpddata_model_set_has_up(pl3_fb_store2, FALSE);
574 	else
575 		gmpc_mpddata_model_set_has_up(pl3_fb_store2, TRUE);
576 	gmpc_mpddata_model_set_mpd_data(pl3_fb_store2, data);
577 	q_free(path);
578 	q_free(icon);
579 	return;
580 }
581 
pl3_file_browser_collapse_row(GtkTreeView * tree,GtkTreeIter * iter,GtkTreePath * path,gpointer user_data)582 static void pl3_file_browser_collapse_row(GtkTreeView * tree, GtkTreeIter * iter, GtkTreePath * path,
583 										  gpointer user_data)
584 {
585 	GtkTreeIter child;
586 	int valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(pl3_fb_dir_store), &child, iter);
587 	if (!cfg_get_single_value_as_int_with_default(config, "file-browser", "extra-lazy", TRUE))
588 		return;
589 
590 	while (valid)
591 	{
592 		valid = gtk_tree_store_remove(pl3_fb_dir_store, &child);
593 	}
594 	/* set unopened */
595 	gtk_tree_store_set(pl3_fb_dir_store, iter, PL3_FB_OPEN, FALSE, -1);
596 	/* add phantom child */
597 	gtk_tree_store_append(pl3_fb_dir_store, &child, iter);
598 }
599 
pl3_file_browser_fill_tree(GtkWidget * tree,GtkTreeIter * iter,GtkTreePath * tpath,gpointer user_data)600 static void pl3_file_browser_fill_tree(GtkWidget * tree, GtkTreeIter * iter, GtkTreePath * tpath, gpointer user_data)
601 {
602 	char *path;
603 	MpdData *data = NULL;
604 	GtkTreeIter child, child2, dummy;
605 	gboolean open;
606 	gtk_tree_model_get(GTK_TREE_MODEL(pl3_fb_dir_store), iter, PL3_FB_PATH, &path, PL3_FB_OPEN, &open, -1);
607 	gtk_tree_store_set(pl3_fb_dir_store, iter, PL3_FB_OPEN, TRUE, -1);
608 	if (open == FALSE)
609 	{
610 		GTimer *tim = g_timer_new();
611 		data = mpd_database_get_directory(connection, path);
612 		/* Do a reverse sort, because adding it to the gtk view by prepending is faster
613 		 * then appending */
614 		data = misc_sort_mpddata(data, (GCompareDataFunc) directory_sort_func_inv, NULL);
615 		g_log(LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Elapsed time sorting before adding: %f\n", g_timer_elapsed(tim, NULL));
616 
617 		if (gtk_tree_model_iter_children(GTK_TREE_MODEL(pl3_fb_dir_store), &dummy, iter))
618 		{
619 			while (data != NULL)
620 			{
621 				if (data->type == MPD_DATA_TYPE_DIRECTORY)
622 				{
623 					gchar *basename = g_path_get_basename(data->directory);
624 					gtk_tree_store_prepend(pl3_fb_dir_store, &child, iter);
625 					gtk_tree_store_set(pl3_fb_dir_store, &child,
626 									   PL3_FB_ICON, "gtk-open",
627 									   PL3_FB_NAME, basename, PL3_FB_PATH, data->directory, PL3_FB_OPEN, FALSE, -1);
628 					gtk_tree_store_append(pl3_fb_dir_store, &child2, &child);
629 
630 					q_free(basename);
631 				}
632 				data = mpd_data_get_next(data);
633 			}
634 
635 			gtk_tree_store_remove(pl3_fb_dir_store, &dummy);
636 		}
637 
638 		g_log(LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Elapsed time sorting after adding: %f\n", g_timer_elapsed(tim, NULL));
639 		g_timer_destroy(tim);
640 
641 	}
642 
643 	q_free(path);
644 }
645 
pl3_file_browser_cat_popup(GtkWidget * wid,GdkEventButton * event,gpointer user_data)646 static int pl3_file_browser_cat_popup(GtkWidget * wid, GdkEventButton * event, gpointer user_data)
647 {
648 	GtkWidget *menu;
649 	if (event->button == 3)
650 	{
651 
652 		/* here we have:  Add. Replace, (update?) */
653 		GtkWidget *item;
654 		menu = gtk_menu_new();
655 		/* add the add widget */
656 		item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ADD, NULL);
657 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
658 		g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_file_browser_add_folder), NULL);
659 
660 		/* add the replace widget */
661 		item = gtk_image_menu_item_new_with_label(_("Replace"));
662 		gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
663 									  gtk_image_new_from_stock(GTK_STOCK_REDO, GTK_ICON_SIZE_MENU));
664 		gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), NULL);
665 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
666 		g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_file_browser_replace_folder), NULL);
667 
668 		{
669 			GtkTreeView *tree = GTK_TREE_VIEW(pl3_fb_dir_tree);
670 			GtkTreeModel *model = (GtkTreeModel *) pl3_fb_dir_store;
671 			GtkTreeSelection *selection = gtk_tree_view_get_selection(tree);
672 			GtkTreeIter iter;
673 			if (gtk_tree_selection_get_selected(selection, &model, &iter))
674 			{
675 				char *icon = NULL;
676 				gtk_tree_model_get(model, &iter, PL3_FB_ICON, &icon, -1);
677 				if (!strcmp("media-playlist", icon))
678 				{
679 					item = gtk_image_menu_item_new_from_stock(GTK_STOCK_DELETE, NULL);
680 					gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
681 					g_signal_connect(G_OBJECT(item), "activate",
682 									 G_CALLBACK(pl3_file_browser_delete_playlist_from_right), NULL);
683 				} else
684 				{
685 					/* add the update widget */
686 					item = gtk_image_menu_item_new_with_label(_("Update"));
687 					gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
688 												  gtk_image_new_from_stock(GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU));
689 					gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
690 					g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_file_browser_update_folder), NULL);
691 				}
692 				q_free(icon);
693 			}
694 		}
695 		gtk_widget_show_all(menu);
696 		gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, event->time);
697 		/* show everything and popup */
698 		return TRUE;
699 	}
700 	return FALSE;
701 }
702 
pl3_file_browser_cat_key_press(GtkWidget * tree,GdkEventKey * event,gpointer data)703 static gboolean pl3_file_browser_cat_key_press(GtkWidget * tree, GdkEventKey * event, gpointer data)
704 {
705 	if (event->state & GDK_CONTROL_MASK && (event->keyval == GDK_Insert || event->keyval == GDK_KP_Insert))
706 	{
707 		pl3_file_browser_replace_folder();
708 	} else if (event->keyval == GDK_Insert || event->keyval == GDK_KP_Insert)
709 	{
710 		pl3_file_browser_add_folder();
711 	}
712 	return FALSE;
713 }
714 
pl3_file_browser_playlist_key_press(GtkWidget * tree,GdkEventKey * event)715 static int pl3_file_browser_playlist_key_press(GtkWidget * tree, GdkEventKey * event)
716 {
717 	if (event->state & GDK_CONTROL_MASK && (event->keyval == GDK_Insert || event->keyval == GDK_KP_Insert))
718 	{
719 		pl3_file_browser_replace_selected();
720 	} else if (event->keyval == GDK_Insert || event->keyval == GDK_KP_Insert)
721 	{
722 		pl3_file_browser_add_selected();
723 	} else if (event->keyval == GDK_i && event->state & GDK_MOD1_MASK)
724 	{
725 		pl3_file_browser_show_info();
726 	}
727 	return FALSE;
728 }
729 
pl3_file_browser_selected(GtkWidget * container)730 static void pl3_file_browser_selected(GtkWidget * container)
731 {
732 	if (pl3_fb_tree == NULL)
733 	{
734 		GtkTreeIter iter,child;
735 		pl3_file_browser_init();
736 		gtk_tree_store_append(pl3_fb_dir_store, &iter, NULL);
737 		gtk_tree_store_set(pl3_fb_dir_store, &iter,
738 				PL3_FB_ICON, "gtk-open", PL3_FB_NAME, "/", PL3_FB_PATH, "/", PL3_FB_OPEN, FALSE, -1);
739 		gtk_tree_store_append(pl3_fb_dir_store, &child, &iter);
740 	}
741 
742 	gtk_container_add(GTK_CONTAINER(container), pl3_fb_vbox);
743 	gtk_widget_grab_focus(pl3_fb_tree);
744 	gtk_widget_show(pl3_fb_vbox);
745 
746 	playlist3_show_playtime(gmpc_mpddata_model_get_playtime(GMPC_MPDDATA_MODEL(pl3_fb_store2)));
747 }
748 
pl3_file_browser_unselected(GtkWidget * container)749 static void pl3_file_browser_unselected(GtkWidget * container)
750 {
751 	gtk_container_remove(GTK_CONTAINER(container), pl3_fb_vbox);
752 }
753 
pl3_file_browser_show_info(void)754 static void pl3_file_browser_show_info(void)
755 {
756 	GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(pl3_fb_tree));
757 	GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(pl3_fb_tree));
758 	if (!mpd_server_check_version(connection, 0, 12, 0))
759 	{
760 		return;
761 	}
762 	if (gtk_tree_selection_count_selected_rows(selection) > 0)
763 	{
764 		GList *list = NULL;
765 		list = gtk_tree_selection_get_selected_rows(selection, &model);
766 
767 		list = g_list_last(list);
768 		{
769 			mpd_Song *song = NULL;
770 			GtkTreeIter iter;
771 			int type;
772 			gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) list->data);
773 			gtk_tree_model_get(GTK_TREE_MODEL(pl3_fb_store2), &iter,
774 							   MPDDATA_MODEL_ROW_TYPE, &type, MPDDATA_MODEL_COL_MPDSONG, &song, -1);
775 			if (type == MPD_DATA_TYPE_SONG)
776 			{
777 				if (song)
778 				{
779 					info2_activate();
780 					info2_fill_song_view(song);
781 				}
782 			}
783 		}
784 		g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
785 		g_list_free(list);
786 	}
787 }
788 
pl3_file_browser_row_activated(GtkTreeView * tree,GtkTreePath * tp)789 static void pl3_file_browser_row_activated(GtkTreeView * tree, GtkTreePath * tp)
790 {
791 	GtkTreeIter iter;
792 	gchar *song_path;
793 	gint r_type;
794 
795 	gtk_tree_model_get_iter(gtk_tree_view_get_model(tree), &iter, tp);
796 	gtk_tree_model_get(gtk_tree_view_get_model(tree), &iter, MPDDATA_MODEL_COL_PATH, &song_path, MPDDATA_MODEL_ROW_TYPE,
797 					   &r_type, -1);
798 	if (song_path == NULL && r_type != -1)
799 	{
800 		return;
801 	}
802 	if (r_type == MPD_DATA_TYPE_PLAYLIST)
803 	{
804 		pl3_push_statusbar_message(_("Loaded playlist"));
805 		mpd_playlist_queue_load(connection, song_path);
806 		mpd_playlist_queue_commit(connection);
807 	} else if (r_type == MPD_DATA_TYPE_DIRECTORY)
808 	{
809 		GtkTreeSelection *selec = gtk_tree_view_get_selection(GTK_TREE_VIEW(pl3_fb_dir_tree));
810 		GtkTreeModel *model = GTK_TREE_MODEL(pl3_fb_dir_store);
811 
812 		if (gtk_tree_selection_get_selected(selec, &model, &iter))
813 		{
814 			GtkTreeIter citer;
815 			GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
816 			gtk_tree_view_expand_row(GTK_TREE_VIEW(pl3_fb_dir_tree), path, FALSE);
817 			gtk_tree_path_free(path);
818 			if (gtk_tree_model_iter_children(model, &citer, &iter))
819 			{
820 				do
821 				{
822 					char *name = NULL;
823 					char *type = NULL;
824 					gtk_tree_model_get(model, &citer, PL3_FB_PATH, &name, PL3_FB_ICON, &type, -1);
825 					if (strcmp(name, song_path) == 0 && strcmp(type, "gtk-open") == 0)
826 					{
827 						gtk_tree_selection_select_iter(selec, &citer);
828 						path = gtk_tree_model_get_path(model, &citer);
829 						gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(pl3_fb_dir_tree), path, NULL, TRUE, 0.5, 0);
830 						gtk_tree_path_free(path);
831 					}
832 					q_free(name);
833 					q_free(type);
834 				} while (gtk_tree_model_iter_next(model, &citer));
835 			}
836 
837 		}
838 	} else if (r_type == -1)
839 	{
840 		GtkTreeSelection *selec = gtk_tree_view_get_selection((GtkTreeView *) pl3_fb_dir_tree);
841 		GtkTreeModel *model = GTK_TREE_MODEL(pl3_fb_dir_store);
842 
843 		if (gtk_tree_selection_get_selected(selec, &model, &iter))
844 		{
845 			GtkTreeIter piter;
846 			if (gtk_tree_model_iter_parent(model, &piter, &iter))
847 			{
848 				GtkTreePath *path = NULL;
849 				gtk_tree_selection_select_iter(selec, &piter);
850 				path = gtk_tree_model_get_path(model, &piter);
851 				gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(pl3_fb_dir_tree), path, NULL, TRUE, 0.5, 0);
852 				gtk_tree_path_free(path);
853 			}
854 		}
855 	} else
856 	{
857 		play_path(song_path);
858 	}
859 
860 	q_free(song_path);
861 }
862 
pl3_file_browser_add_to_playlist(GtkWidget * menu,gpointer cb_data)863 static void pl3_file_browser_add_to_playlist(GtkWidget * menu, gpointer cb_data)
864 {
865 	GtkTreeModel *model = GTK_TREE_MODEL(pl3_fb_store2);
866 	GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(pl3_fb_tree));
867 	gchar *data = g_object_get_data(G_OBJECT(menu), "playlist");
868 	GList *iter, *list = gtk_tree_selection_get_selected_rows(selection, &model);
869 	if (list)
870 	{
871 		iter = g_list_first(list);
872 		do
873 		{
874 			GtkTreeIter giter;
875 			if (gtk_tree_model_get_iter(model, &giter, (GtkTreePath *) iter->data))
876 			{
877 				gchar *file = NULL;
878 				int type = 0;
879 				gtk_tree_model_get(model, &giter, MPDDATA_MODEL_COL_PATH, &file, MPDDATA_MODEL_ROW_TYPE, &type, -1);
880 				if (type == MPD_DATA_TYPE_SONG)
881 				{
882 					mpd_database_playlist_list_add(connection, data, file);
883 				} else if (type == MPD_DATA_TYPE_DIRECTORY)
884 				{
885 					MpdData *data2 = mpd_database_get_directory_recursive(connection, file);
886 					for (; data2; data2 = mpd_data_get_next(data2))
887 					{
888 						if (data2->type == MPD_DATA_TYPE_SONG)
889 						{
890 							mpd_database_playlist_list_add(connection, data, data2->song->file);
891 						}
892 					}
893 				}
894 				g_free(file);
895 			}
896 		} while ((iter = g_list_next(iter)));
897 
898 		g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
899 		g_list_free(list);
900 	}
901 }
902 
pl3_file_browser_button_release_event(GtkWidget * but,GdkEventButton * event)903 static gboolean pl3_file_browser_button_release_event(GtkWidget * but, GdkEventButton * event)
904 {
905 
906 	int has_item = 0;
907 	GtkWidget *item;
908 	GtkWidget *menu = NULL;
909 	GtkTreeSelection *sel = NULL;
910 	if (event->button != 3)
911 		return FALSE;
912 	menu = gtk_menu_new();
913 	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(pl3_fb_tree));
914 	/* don't show it when where listing custom streams...
915 	 * show always when version 12..  or when searching in playlist.
916 	 */
917 	if (gtk_tree_selection_count_selected_rows(sel) == 1)
918 	{
919 		mpd_Song *song = NULL;
920 		GtkTreeModel *model = GTK_TREE_MODEL(pl3_fb_store2);
921 		GList *list = gtk_tree_selection_get_selected_rows(sel, &model);
922 		if (list != NULL)
923 		{
924 			GtkTreeIter iter;
925 			int row_type;
926 			char *path;
927 			GtkTreePath *tree_path;
928 			list = g_list_first(list);
929 			gtk_tree_model_get_iter(model, &iter, list->data);
930 			gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_PATH, &path, MPDDATA_MODEL_ROW_TYPE, &row_type, -1);
931 			if (row_type == MPD_DATA_TYPE_SONG)
932 			{
933 				if (mpd_server_check_version(connection, 0, 12, 0))
934 				{
935 					item = gtk_image_menu_item_new_from_stock(GTK_STOCK_DIALOG_INFO, NULL);
936 					gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
937 					g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_file_browser_show_info), NULL);
938 					has_item = 1;
939 				}
940 			} else if (row_type == MPD_DATA_TYPE_PLAYLIST)
941 			{
942 				item = gtk_image_menu_item_new_from_stock(GTK_STOCK_DELETE, NULL);
943 				gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
944 				g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_file_browser_delete_playlist), NULL);
945 				has_item = 1;
946 			} else if (row_type == MPD_DATA_TYPE_DIRECTORY)
947 			{
948 				item = gtk_image_menu_item_new_with_label(_("Update"));
949 				gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
950 											  gtk_image_new_from_stock(GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU));
951 				gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
952 				g_signal_connect(G_OBJECT(item), "activate",
953 								 G_CALLBACK(pl3_file_browser_update_folder_left_pane), NULL);
954 				has_item = 1;
955 			}
956 			if (row_type != -1)
957 			{
958 				/* replace the replace widget */
959 				item = gtk_image_menu_item_new_with_label(_("Replace"));
960 				gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
961 											  gtk_image_new_from_stock(GTK_STOCK_REDO, GTK_ICON_SIZE_MENU));
962 				gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), item);
963 				g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_file_browser_replace_selected), NULL);
964 
965 				/* add the delete widget */
966 				item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ADD, NULL);
967 				gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), item);
968 				g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_file_browser_add_selected), NULL);
969 
970 				playlist_editor_right_mouse(menu, pl3_file_browser_add_to_playlist, NULL);
971 				has_item = 1;
972 			}
973 
974 			tree_path = list->data;
975 			if (tree_path && gtk_tree_model_get_iter(model, &iter, tree_path))
976 			{
977 				gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_MPDSONG, &song, -1);
978 				if (song)
979 				{
980 					submenu_for_song(menu, song);
981 				}
982 			}
983 			g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
984 			g_list_free(list);
985 			q_free(path);
986 		}
987 	} else
988 	{
989 		/* replace the replace widget */
990 		item = gtk_image_menu_item_new_with_label(_("Replace"));
991 		gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
992 									  gtk_image_new_from_stock(GTK_STOCK_REDO, GTK_ICON_SIZE_MENU));
993 		gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), item);
994 		g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_file_browser_replace_selected), NULL);
995 
996 		/* add the delete widget */
997 		item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ADD, NULL);
998 		gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), item);
999 		g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_file_browser_add_selected), NULL);
1000 		has_item = 1;
1001 	}
1002 	gmpc_mpddata_treeview_right_mouse_intergration(GMPC_MPDDATA_TREEVIEW(pl3_fb_tree), GTK_MENU(menu));
1003 	/*  if(has_item) */
1004 	{
1005 		/*
1006 		   item = gtk_image_menu_item_new_with_label(_("Edit Columns"));
1007 		   gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
1008 		   gtk_image_new_from_stock(GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU));
1009 		   gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1010 		   g_signal_connect(G_OBJECT(item), "activate",
1011 		   G_CALLBACK(pl3_file_browser_edit_columns), NULL);
1012 		 */
1013 		gtk_widget_show_all(menu);
1014 		gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, event->time);
1015 		return TRUE;
1016 	}
1017 	/*  else{
1018 	   gtk_widget_destroy(menu);
1019 	   }
1020 	 */
1021 	return FALSE;
1022 }
1023 
pl3_file_browser_replace_selected(void)1024 static void pl3_file_browser_replace_selected(void)
1025 {
1026 	mpd_playlist_clear(connection);
1027 	if (mpd_check_connected(connection))
1028 	{
1029 		pl3_file_browser_add_selected();
1030 		mpd_player_play(connection);
1031 	}
1032 }
1033 
pl3_file_browser_add_selected(void)1034 static void pl3_file_browser_add_selected(void)
1035 {
1036 	GtkTreeIter iter;
1037 	GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(pl3_fb_tree));
1038 	GtkTreeModel *model = GTK_TREE_MODEL(pl3_fb_store2);
1039 	GList *rows = gtk_tree_selection_get_selected_rows(selection, &model);
1040 	int songs = 0;
1041 	int dirs = 0;
1042 	int pl = 0;
1043 	/*gchar *message; */
1044 	if (rows != NULL)
1045 	{
1046 		gchar *name;
1047 		gint type;
1048 		GList *node = g_list_first(rows);
1049 		do
1050 		{
1051 			GtkTreePath *path = node->data;
1052 			gtk_tree_model_get_iter(model, &iter, path);
1053 			gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_PATH, &name, MPDDATA_MODEL_ROW_TYPE, &type, -1);
1054 			/* does this bitmask thingy works ok? I think it hsould */
1055 			if (type == MPD_DATA_TYPE_SONG || type == MPD_DATA_TYPE_DIRECTORY)
1056 			{
1057 				/* add them to the add list */
1058 				mpd_playlist_queue_add(connection, name);
1059 				if (type == MPD_DATA_TYPE_DIRECTORY)
1060 					dirs++;
1061 				if (type == MPD_DATA_TYPE_SONG)
1062 					songs++;
1063 			} else if (type == MPD_DATA_TYPE_PLAYLIST)
1064 			{
1065 				mpd_playlist_queue_load(connection, name);
1066 				pl++;
1067 			}
1068 			q_free(name);
1069 		} while ((node = g_list_next(node)) != NULL);
1070 	}
1071 	/* if there are items in the add list add them to the playlist */
1072 	mpd_playlist_queue_commit(connection);
1073 	if ((songs + dirs + pl) != 0)
1074 	{
1075 		GString *string = g_string_new(_("Added"));
1076 		if (songs)
1077 			g_string_append_printf(string, " %i %s%c", songs, ngettext("song", "songs", songs),
1078 								   (dirs + pl > 0) ? ',' : ' ');
1079 		if (dirs)
1080 			g_string_append_printf(string, " %i %s%c", dirs, ngettext("directory", "directories", dirs),
1081 								   (pl > 0) ? ',' : ' ');
1082 		if (pl)
1083 			g_string_append_printf(string, " %i %s", pl, ngettext("playlist", "playlists", pl));
1084 		pl3_push_statusbar_message(string->str);
1085 		g_string_free(string, TRUE);
1086 	}
1087 
1088 	g_list_foreach(rows, (GFunc) gtk_tree_path_free, NULL);
1089 	g_list_free(rows);
1090 }
1091 
pl3_file_browser_delete_playlist_from_right(GtkMenuItem * bt)1092 static void pl3_file_browser_delete_playlist_from_right(GtkMenuItem * bt)
1093 {
1094 	GtkTreeView *tree = GTK_TREE_VIEW(pl3_fb_dir_tree);
1095 	GtkTreeModel *model = (GtkTreeModel *) pl3_fb_dir_store;
1096 	GtkTreeSelection *selection = gtk_tree_view_get_selection(tree);
1097 	GtkTreeIter iter;
1098 	char *path = NULL;
1099 	/* create a warning message dialog */
1100 	GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(playlist3_get_window()),
1101 											   GTK_DIALOG_MODAL,
1102 											   GTK_MESSAGE_WARNING,
1103 											   GTK_BUTTONS_NONE,
1104 											   _("Are you sure you want to clear the selected playlist?"));
1105 
1106 	gtk_dialog_add_buttons(GTK_DIALOG(dialog), GTK_STOCK_NO, GTK_RESPONSE_CANCEL, GTK_STOCK_YES, GTK_RESPONSE_OK, NULL);
1107 	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
1108 
1109 	if (gtk_tree_selection_get_selected(selection, &model, &iter))
1110 	{
1111 		char *icon = NULL;
1112 		gtk_tree_model_get(model, &iter, 3, &icon, 2, &path, -1);
1113 		if (path && strcmp("media-playlist", icon))
1114 		{
1115 			if (path)
1116 				q_free(path);
1117 			path = NULL;
1118 		}
1119 		q_free(icon);
1120 	}
1121 
1122 	if (path == NULL)
1123 	{
1124 		gtk_widget_destroy(dialog);
1125 		return;
1126 	}
1127 
1128 	switch (gtk_dialog_run(GTK_DIALOG(dialog)))
1129 	{
1130 	case GTK_RESPONSE_OK:
1131 		mpd_database_delete_playlist(connection, path);
1132 		pl3_file_browser_reupdate();
1133 	default:
1134 		break;
1135 	}
1136 	gtk_widget_destroy(GTK_WIDGET(dialog));
1137 	q_free(path);
1138 }
1139 
pl3_file_browser_delete_playlist(GtkMenuItem * bt)1140 static void pl3_file_browser_delete_playlist(GtkMenuItem * bt)
1141 {
1142 	char *path = NULL;
1143 	GtkTreeSelection *sel = NULL;
1144 	/* create a warning message dialog */
1145 	GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(playlist3_get_window()),
1146 											   GTK_DIALOG_MODAL,
1147 											   GTK_MESSAGE_WARNING,
1148 											   GTK_BUTTONS_NONE,
1149 											   _("Are you sure you want to clear the selected playlist?"));
1150 
1151 	gtk_dialog_add_buttons(GTK_DIALOG(dialog), GTK_STOCK_NO, GTK_RESPONSE_CANCEL, GTK_STOCK_YES, GTK_RESPONSE_OK, NULL);
1152 	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
1153 
1154 	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(pl3_fb_tree));
1155 	if (gtk_tree_selection_count_selected_rows(sel) == 1)
1156 	{
1157 		GtkTreeModel *model = GTK_TREE_MODEL(pl3_fb_store2);
1158 		GList *list = gtk_tree_selection_get_selected_rows(sel, &model);
1159 		if (list != NULL)
1160 		{
1161 			GtkTreeIter iter;
1162 
1163 			list = g_list_first(list);
1164 			gtk_tree_model_get_iter(model, &iter, list->data);
1165 			gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_PATH, &path, -1);
1166 			g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
1167 			g_list_free(list);
1168 		}
1169 	}
1170 
1171 	if (path == NULL)
1172 	{
1173 		gtk_widget_destroy(dialog);
1174 		return;
1175 	}
1176 
1177 	switch (gtk_dialog_run(GTK_DIALOG(dialog)))
1178 	{
1179 	case GTK_RESPONSE_OK:
1180 		mpd_database_delete_playlist(connection, path);
1181 		pl3_file_browser_reupdate();
1182 	default:
1183 		break;
1184 	}
1185 	gtk_widget_destroy(GTK_WIDGET(dialog));
1186 	q_free(path);
1187 }
1188 
pl3_file_browser_disconnect(void)1189 static void pl3_file_browser_disconnect(void)
1190 {
1191 
1192 	if (pl3_fb_tree_ref)
1193 	{
1194 		GtkTreeIter iter;
1195 		GtkTreeIter child;
1196 		if (pl3_fb_dir_store != NULL && gtk_tree_model_get_iter_first(GTK_TREE_MODEL(pl3_fb_dir_store), &iter))
1197 		{
1198 			int valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(pl3_fb_dir_store), &child, &iter);
1199 
1200 			while (valid)
1201 			{
1202 				valid = gtk_tree_store_remove(pl3_fb_dir_store, &child);
1203 			}
1204 			/* set unopened */
1205 			gtk_tree_store_set(pl3_fb_dir_store, &iter, PL3_FB_OPEN, FALSE, -1);
1206 			/* add phantom child */
1207 			gtk_tree_store_append(pl3_fb_dir_store, &child, &iter);
1208 		}
1209 	}
1210 	if (pl3_fb_store2)
1211 	{
1212 		gmpc_mpddata_model_set_has_up(pl3_fb_store2, FALSE);
1213 		gmpc_mpddata_model_set_mpd_data(pl3_fb_store2, NULL);
1214 	}
1215 }
1216 
pl3_file_browser_activate(void)1217 static void pl3_file_browser_activate(void)
1218 {
1219 	GtkTreeSelection *selec = gtk_tree_view_get_selection((GtkTreeView *) playlist3_get_category_tree_view());
1220 
1221 	GtkTreePath *path = gtk_tree_row_reference_get_path(pl3_fb_tree_ref);
1222 	if (path)
1223 	{
1224 		gtk_tree_selection_select_path(selec, path);
1225 		gtk_tree_path_free(path);
1226 	}
1227 }
1228 
pl3_file_browser_add_go_menu(GtkWidget * menu)1229 static int pl3_file_browser_add_go_menu(GtkWidget * menu)
1230 {
1231 	GtkWidget *item = NULL;
1232 	if (!pl3_file_browser_get_enabled())
1233 		return 0;
1234 
1235 	item = gtk_image_menu_item_new_with_label(_("Database"));
1236 	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
1237 								  gtk_image_new_from_icon_name("gmpc-database", GTK_ICON_SIZE_MENU));
1238 	gtk_widget_add_accelerator(GTK_WIDGET(item),
1239 							   "activate", gtk_menu_get_accel_group(GTK_MENU(menu)), GDK_F2, 0, GTK_ACCEL_VISIBLE);
1240 
1241 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1242 	g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_file_browser_activate), NULL);
1243 	return 1;
1244 }
1245 
pl3_file_browser_connection_changed(MpdObj * mi,int connect,gpointer data)1246 static void pl3_file_browser_connection_changed(MpdObj * mi, int connect, gpointer data)
1247 {
1248 	if (connect)
1249 	{
1250 		if(pl3_fb_tree != NULL)
1251 		{
1252 			GtkTreePath *path = gtk_tree_path_new_from_string("0");
1253 			if (mpd_stats_get_total_songs(connection) == 0)
1254 			{
1255 				gtk_widget_show(pl3_fb_warning_box);
1256 			} else
1257 			{
1258 				gtk_widget_hide(pl3_fb_warning_box);
1259 			}
1260 
1261 			gtk_tree_view_expand_to_path(GTK_TREE_VIEW(pl3_fb_dir_tree), path);
1262 			gtk_tree_path_free(path);
1263 		}
1264 	} else
1265 		pl3_file_browser_disconnect();
1266 }
1267 
pl3_file_browser_status_changed(MpdObj * mi,ChangedStatusType what,void * data)1268 static void pl3_file_browser_status_changed(MpdObj * mi, ChangedStatusType what, void *data)
1269 {
1270 	if (what & MPD_CST_UPDATING)
1271 	{
1272 		if (pl3_fb_vbox != NULL && mpd_status_db_is_updating(connection))
1273 		{
1274 			gtk_widget_hide(pl3_fb_warning_box);
1275 		}
1276 	}
1277 	if (what & MPD_CST_DATABASE)
1278 	{
1279 		pl3_file_browser_reupdate();
1280 	}
1281     /* We might not be able to read the data.
1282      * because off insufficient permission.
1283      * So if permission failed, lets reload things (it is fairly cheap.
1284      */
1285     else if (what & MPD_CST_PERMISSION)
1286     {
1287 		pl3_file_browser_reupdate();
1288     }
1289 }
1290 
pl3_file_browser_destroy(void)1291 static void pl3_file_browser_destroy(void)
1292 {
1293 	if (pl3_fb_tree_ref)
1294 	{
1295 		GtkTreeIter iter;
1296 		GtkTreePath *path;
1297 		path = gtk_tree_row_reference_get_path(pl3_fb_tree_ref);
1298 		if (path)
1299 		{
1300 			if (gtk_tree_model_get_iter(GTK_TREE_MODEL(gtk_tree_row_reference_get_model(pl3_fb_tree_ref)), &iter, path))
1301 			{
1302 				gtk_list_store_remove(GTK_LIST_STORE(gtk_tree_row_reference_get_model(pl3_fb_tree_ref)), &iter);
1303 			}
1304 			gtk_tree_path_free(path);
1305 		}
1306 		gtk_tree_row_reference_free(pl3_fb_tree_ref);
1307 		pl3_fb_tree_ref = NULL;
1308 	}
1309 	if (pl3_fb_vbox)
1310 	{
1311 		gtk_widget_destroy(pl3_fb_vbox);
1312         pl3_fb_vbox = NULL;
1313 	}
1314 	if (pl3_fb_store2)
1315 	{
1316 		g_object_unref(pl3_fb_store2);
1317         pl3_fb_store2 = NULL;
1318 	}
1319     if(pl3_fb_tree) {
1320         pl3_fb_tree = NULL;
1321     }
1322 }
1323 
pl3_file_browser_save_myself(void)1324 static void pl3_file_browser_save_myself(void)
1325 {
1326 	if (pl3_fb_tree_ref)
1327 	{
1328 		GtkTreePath *path = gtk_tree_row_reference_get_path(pl3_fb_tree_ref);
1329 		if (path)
1330 		{
1331 			gint *indices = gtk_tree_path_get_indices(path);
1332 			g_log(LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Saving myself to position: %i\n", indices[0]);
1333 			cfg_set_single_value_as_int(config, "file-browser", "position", indices[0]);
1334 			gtk_tree_path_free(path);
1335 		}
1336 	}
1337 }
1338 
pl3_file_browser_open_path_real(gchar ** dirs,GtkTreeIter * parent)1339 static void pl3_file_browser_open_path_real(gchar ** dirs, GtkTreeIter * parent)
1340 {
1341 	GtkTreeIter iter;
1342 	if ((dirs[0]) == NULL)
1343 	{
1344 		/* found dir */
1345 		GtkTreePath *path = NULL;
1346 		gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(pl3_fb_dir_tree)), parent);
1347 
1348 		path = gtk_tree_model_get_path(GTK_TREE_MODEL(pl3_fb_dir_store), parent);
1349 		gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(pl3_fb_dir_tree), path, NULL, TRUE, 0.5, 0);
1350 		gtk_tree_path_free(path);
1351 		return;
1352 	}
1353 	if (gtk_tree_model_iter_children(GTK_TREE_MODEL(pl3_fb_dir_store), &iter, parent))
1354 	{
1355 		do
1356 		{
1357 			gchar *name = NULL;
1358 			gtk_tree_model_get(GTK_TREE_MODEL(pl3_fb_dir_store), &iter, PL3_FB_NAME, &name, -1);
1359 			if (name && g_utf8_collate(name, dirs[0]) == 0)
1360 			{
1361 				GtkTreePath *tpath = gtk_tree_model_get_path(GTK_TREE_MODEL(pl3_fb_dir_store), &iter);
1362 				gtk_tree_view_expand_row(GTK_TREE_VIEW(pl3_fb_dir_tree), tpath, FALSE);
1363 				gtk_tree_path_free(tpath);
1364 				pl3_file_browser_open_path_real(&dirs[1], &iter);
1365 				g_free(name);
1366 				return;
1367 			}
1368 			if (name)
1369 				g_free(name);
1370 		} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(pl3_fb_dir_store), &iter));
1371 	}
1372 
1373 }
1374 
pl3_file_browser_open_path(const gchar * path)1375 void pl3_file_browser_open_path(const gchar * path)
1376 {
1377 	pl3_file_browser_activate();
1378 	if (pl3_fb_dir_store)
1379 	{
1380 		gchar **dirs = g_strsplit(path, G_DIR_SEPARATOR_S, -1);
1381 		if (dirs)
1382 		{
1383 			GtkTreeIter iter;
1384 			if (gtk_tree_model_iter_children(GTK_TREE_MODEL(pl3_fb_dir_store), &iter, NULL))
1385 			{
1386 				GtkTreePath *tpath = gtk_tree_model_get_path(GTK_TREE_MODEL(pl3_fb_dir_store), &iter);
1387 				gtk_tree_view_expand_row(GTK_TREE_VIEW(pl3_fb_dir_tree), tpath, FALSE);
1388 				gtk_tree_path_free(tpath);
1389 				pl3_file_browser_open_path_real(dirs, &iter);
1390 			}
1391 			g_strfreev(dirs);
1392 		}
1393 	}
1394 }
1395 
pl3_file_browser_is_field_supported(int tag)1396 static gboolean pl3_file_browser_is_field_supported(int tag)
1397 {
1398 	if (tag == MPD_TAG_NUM_OF_ITEM_TYPES)
1399 		return TRUE;
1400 	return mpd_server_tag_supported(connection, tag);
1401 }
1402 
pl3_file_browser_is_search(const int num_field,const gchar * search_string,GError ** error)1403 static MpdData *pl3_file_browser_is_search(const int num_field, const gchar * search_string, GError ** error)
1404 {
1405 	MpdData *data_t = NULL;
1406 	if (num_field == MPD_TAG_NUM_OF_ITEM_TYPES)
1407 	{
1408 		data_t = advanced_search(search_string, FALSE);
1409 	} else
1410 	{
1411 		gchar **splitted = tokenize_string(search_string);
1412 		int i = 0;
1413 		gboolean found = FALSE;
1414 		for (i = 0; splitted && splitted[i]; i++)
1415 		{
1416 			if (!found)
1417 			{
1418 				mpd_database_search_start(connection, FALSE);
1419 				found = TRUE;
1420 			}
1421 			mpd_database_search_add_constraint(connection, num_field, splitted[i]);
1422 		}
1423 		if (splitted)
1424 			g_strfreev(splitted);
1425 		if (found)
1426 		{
1427 			data_t = mpd_database_search_commit(connection);
1428 		}
1429 	}
1430 	return data_t;
1431 }
1432 
pl3_find2_ec_database(gpointer user_data,const char * param)1433 void pl3_find2_ec_database(gpointer user_data, const char *param)
1434 {
1435 	pl3_find2_select_plugin_id(file_browser_plug.id);
1436 	pl3_find2_do_search_any(param);
1437 }
1438 
pl3_file_browser_plugin_init(void)1439 static void pl3_file_browser_plugin_init(void)
1440 {
1441 	gmpc_easy_command_add_entry(gmpc_easy_command,
1442 								_("search database"), ".*",
1443 								_("Search database <query>"), (GmpcEasyCommandCallback *) pl3_find2_ec_database, NULL);
1444 }
1445 
1446 /***  Integrates the file browser in the right mouse menu ****/
1447 
1448 /* Handle the click on the menu item */
pl3_file_browser_option_menu_activate(GtkMenuItem * item,gpointer data)1449 static void pl3_file_browser_option_menu_activate (GtkMenuItem *item, gpointer data)
1450 {
1451     /* Get previously stored path from item */
1452     const gchar *path = g_object_get_data(G_OBJECT(item), "path");
1453     /* if there is one, act */
1454     if(path != NULL)
1455     {
1456         /* This function calls the file browser and opens the path */
1457         pl3_file_browser_open_path(path);
1458     }
1459 }
1460 
1461 /* add item to menu */
pl3_file_browser_option_menu(GmpcMpdDataTreeview * tree,GtkMenu * menu)1462 static int pl3_file_browser_option_menu(GmpcMpdDataTreeview *tree, GtkMenu *menu)
1463 {
1464     int retv = 0;
1465     GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1466     /* Only works on 1 song */
1467     if(gtk_tree_selection_count_selected_rows(selection) == 1)
1468     {
1469         GList *list;
1470         GtkTreeModel *model;
1471         mpd_Song *song = NULL;
1472         /* Get a list of selected rows (1) */
1473         list = gtk_tree_selection_get_selected_rows(selection, &model);
1474         if(list) {
1475             GtkTreeIter iter;
1476             GtkTreePath *path  = (GtkTreePath *)list->data;
1477             /* Convert the path into an actual iter we can use to get values from the model */
1478             if(gtk_tree_model_get_iter(model, &iter,path))
1479             {
1480                 /* Get a pointer to the mpd_Song in the model. */
1481                 gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_MPDSONG, &song, -1);
1482                 /* Only show if song exists and has a path */
1483                 if(song && song->file)
1484                 {
1485                     gchar *scheme = g_uri_parse_scheme(song->file);
1486                     /* If path has a scheme it is not in our db */
1487                     if(!scheme)
1488                     {
1489                         GtkWidget *item = gtk_image_menu_item_new_with_label(_("Lookup directory in database"));
1490                         /* Add folder icon */
1491                         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
1492                                 gtk_image_new_from_stock("gtk-open", GTK_ICON_SIZE_MENU));
1493 
1494                         /* Attach a copy of the path to open, so we don't have to look it up again */
1495                         g_object_set_data_full(G_OBJECT(item), "path", g_path_get_dirname(song->file), (GDestroyNotify)g_free);
1496                         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1497 
1498                         /* Connect signal */
1499                         g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_file_browser_option_menu_activate), NULL);
1500                         retv++;
1501                     }
1502                     else g_free(scheme);
1503 
1504                 }
1505             }
1506         }
1507         /* Free the list of rows */
1508         g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
1509         g_list_free (list);
1510     }
1511 
1512     return retv;
1513 }
1514