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