1 /*************************************************************************/
2 /* Copyright (C) 2007-2009 sujith <m.sujith@gmail.com>                   */
3 /* Copyright (C) 2009-2013 matias <mati86dl@gmail.com>                   */
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 3 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     */
16 /* along with this program.  If not, see <http://www.gnu.org/licenses/>. */
17 /*************************************************************************/
18 
19 #if HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22 
23 #include "pragha-library-pane.h"
24 
25 #if defined(GETTEXT_PACKAGE)
26 #include <glib/gi18n-lib.h>
27 #else
28 #include <glib/gi18n.h>
29 #endif
30 
31 #include <glib.h>
32 #include <glib/gstdio.h>
33 #include <gdk/gdkkeysyms.h>
34 #include <stdlib.h>
35 
36 #include "pragha-playback.h"
37 #include "pragha-menubar.h"
38 #include "pragha-utils.h"
39 #include "pragha-playlists-mgmt.h"
40 #include "pragha-search-entry.h"
41 #include "pragha-tagger.h"
42 #include "pragha-tags-dialog.h"
43 #include "pragha-tags-mgmt.h"
44 #include "pragha-musicobject-mgmt.h"
45 #include "pragha-dnd.h"
46 
47 #ifdef G_OS_WIN32
48 #include "../win32/win32dep.h"
49 #endif
50 
51 struct _PraghaLibraryPane {
52 	GtkBox           __parent__;
53 
54 	/* Global database and preferences instances */
55 	PraghaDatabase    *cdbase;
56 	PraghaPreferences *preferences;
57 
58 	/* Tree view */
59 	GtkTreeStore      *library_store;
60 	GtkWidget         *library_tree;
61 	GtkWidget         *search_entry;
62 	GtkWidget         *pane_title;
63 
64 	/* Tree view order. TODO: Rework and remove it. */
65 	GSList            *library_tree_nodes;
66 
67 	/* Useful flags */
68 	gboolean           dragging;
69 	gboolean           view_change;
70 
71 	/* Filter stuff */
72 	gchar             *filter_entry;
73 	guint              timeout_id;
74 
75 	/* Fixbuf used on library tree. */
76 	GdkPixbuf         *pixbuf_artist;
77 	GdkPixbuf         *pixbuf_album;
78 	GdkPixbuf         *pixbuf_track;
79 	GdkPixbuf         *pixbuf_genre;
80 	GdkPixbuf         *pixbuf_dir;
81 
82 	/* Menu */
83 	GtkUIManager      *library_pane_context_menu;
84 	GtkUIManager      *library_tree_context_menu;
85 };
86 
87 enum
88 {
89 	LIBRARY_APPEND_PLAYLIST,
90 	LIBRARY_REPLACE_PLAYLIST,
91 	LIBRARY_REPLACE_PLAYLIST_AND_PLAY,
92 	LAST_SIGNAL
93 };
94 
95 static int signals[LAST_SIGNAL] = { 0 };
96 
97 G_DEFINE_TYPE(PraghaLibraryPane, pragha_library_pane, GTK_TYPE_BOX)
98 
99 /* Node types in library view */
100 
101 typedef enum {
102 	NODE_CATEGORY,
103 	NODE_FOLDER,
104 	NODE_GENRE,
105 	NODE_ARTIST,
106 	NODE_ALBUM,
107 	NODE_TRACK,
108 	NODE_BASENAME,
109 	NODE_PLAYLIST,
110 	NODE_RADIO
111 } LibraryNodeType;
112 
113 /* Columns in Library view */
114 
115 enum library_columns {
116 	L_PIXBUF,
117 	L_NODE_DATA,
118 	L_NODE_BOLD,
119 	L_NODE_TYPE,
120 	L_LOCATION_ID,
121 	L_MACH,
122 	L_VISIBILE,
123 	N_L_COLUMNS
124 };
125 
126 typedef enum {
127 	PRAGHA_RESPONSE_SKIP,
128 	PRAGHA_RESPONSE_SKIP_ALL,
129 	PRAGHA_RESPONSE_DELETE_ALL
130 } PraghaDeleteResponseType;
131 
132 #define PRAGHA_BUTTON_SKIP       _("_Skip")
133 #define PRAGHA_BUTTON_SKIP_ALL   _("S_kip All")
134 #define PRAGHA_BUTTON_DELETE_ALL _("Delete _All")
135 
136 /*
137  * library_pane_context_menu calbacks
138  */
139 static void pragha_library_pane_expand_all_action                  (GtkAction *action, PraghaLibraryPane *library);
140 static void pragha_library_pane_collapse_all_action                (GtkAction *action, PraghaLibraryPane *library);
141 static void pragha_library_pane_set_folders_view_action            (GtkAction *action, PraghaLibraryPane *library);
142 static void pragha_library_pane_set_artist_view_action             (GtkAction *action, PraghaLibraryPane *library);
143 static void pragha_library_pane_set_album_view_action              (GtkAction *action, PraghaLibraryPane *library);
144 static void pragha_library_pane_set_genre_view_action              (GtkAction *action, PraghaLibraryPane *library);
145 static void pragha_library_pane_set_artist_album_view_action       (GtkAction *action, PraghaLibraryPane *library);
146 static void pragha_library_pane_set_genre_album_view_action        (GtkAction *action, PraghaLibraryPane *library);
147 static void pragha_library_pane_set_genre_artist_action            (GtkAction *action, PraghaLibraryPane *library);
148 static void pragha_library_pane_set_genre_artist_album_view_action (GtkAction *action, PraghaLibraryPane *library);
149 
150 /*
151  * library_tree_context_menu calbacks
152  */
153 static void library_tree_add_to_playlist_action                    (GtkAction *action, PraghaLibraryPane *library);
154 static void library_tree_replace_playlist_action                   (GtkAction *action, PraghaLibraryPane *library);
155 static void library_tree_replace_and_play                          (GtkAction *action, PraghaLibraryPane *library);
156 static void pragha_library_pane_rename_item_action                 (GtkAction *action, PraghaLibraryPane *library);
157 static void pragha_library_pane_remove_item_action                 (GtkAction *action, PraghaLibraryPane *library);
158 static void pragha_library_pane_export_playlist_action             (GtkAction *action, PraghaLibraryPane *library);
159 static void pragha_library_pane_edit_tags_action                   (GtkAction *action, PraghaLibraryPane *library);
160 static void pragha_library_pane_delete_from_hdd_action             (GtkAction *action, PraghaLibraryPane *library);
161 static void pragha_library_pane_delete_from_db_action              (GtkAction *action, PraghaLibraryPane *library);
162 
163 /*
164  * Menus definitions
165  *
166  **/
167 
168 gchar *library_pane_context_menu_xml = "<ui>			\
169 	<popup>							\
170 	<menuitem action=\"Expand library\"/>			\
171 	<menuitem action=\"Collapse library\"/>			\
172 	<separator/>						\
173 	<menuitem action=\"folders\"/>				\
174 	<separator/>						\
175 	<menuitem action=\"artist\"/>				\
176 	<menuitem action=\"album\"/>				\
177 	<menuitem action=\"genre\"/>				\
178 	<separator/>						\
179 	<menuitem action=\"artist_album\"/>			\
180 	<menuitem action=\"genre_artist\"/>			\
181 	<menuitem action=\"genre_album\"/>			\
182 	<separator/>						\
183 	<menuitem action=\"genre_artist_album\"/>		\
184 	</popup>						\
185 	</ui>";
186 
187 GtkActionEntry library_pane_context_aentries[] = {
188 	{"Expand library", NULL, N_("_Expand library"),
189 	 "", "Expand the library", G_CALLBACK(pragha_library_pane_expand_all_action)},
190 	{"Collapse library", NULL, N_("_Collapse library"),
191 	 "", "Collapse the library", G_CALLBACK(pragha_library_pane_collapse_all_action)},
192 	{"folders", NULL, N_("Folders structure"),
193 	 "", "Folders structure", G_CALLBACK(pragha_library_pane_set_folders_view_action)},
194 	{"artist", NULL, N_("Artist"),
195 	 "", "Artist", G_CALLBACK(pragha_library_pane_set_artist_view_action)},
196 	{"album", NULL, N_("Album"),
197 	 "", "Album", G_CALLBACK(pragha_library_pane_set_album_view_action)},
198 	{"genre", NULL, N_("Genre"),
199 	 "", "Genre", G_CALLBACK(pragha_library_pane_set_genre_view_action)},
200 	{"artist_album", NULL, N_("Artist / Album"),
201 	 "", "Artist / Album", G_CALLBACK(pragha_library_pane_set_artist_album_view_action)},
202 	{"genre_album", NULL, N_("Genre / Album"),
203 	 "", "Genre / Album", G_CALLBACK(pragha_library_pane_set_genre_album_view_action)},
204 	{"genre_artist", NULL, N_("Genre / Artist"),
205 	 "", "Genre / Artist", G_CALLBACK(pragha_library_pane_set_genre_artist_action)},
206 	{"genre_artist_album", NULL, N_("Genre / Artist / Album"),
207 	 "", "Genre / Artist / Album", G_CALLBACK(pragha_library_pane_set_genre_artist_album_view_action)}
208 };
209 
210 gchar *library_tree_context_menu_xml = "<ui>		\
211 	<popup name=\"PlaylistPopup\">				\
212 	<menuitem action=\"Add to current playlist\"/>		\
213 	<menuitem action=\"Replace current playlist\"/>		\
214 	<menuitem action=\"Replace and play\"/>			\
215 	<separator/>						\
216 	<menuitem action=\"Rename\"/>				\
217 	<menuitem action=\"Delete\"/>				\
218 	<menuitem action=\"Export\"/>				\
219 	</popup>						\
220 	<popup name=\"LibraryPopup\">				\
221 	<menuitem action=\"Add to current playlist\"/>		\
222 	<menuitem action=\"Replace current playlist\"/>		\
223 	<menuitem action=\"Replace and play\"/>			\
224 	<separator/>						\
225 	<menuitem action=\"Edit tags\"/>			\
226 	<separator/>						\
227 	<menuitem action=\"Move to trash\"/>			\
228 	<menuitem action=\"Delete from library\"/>		\
229 	</popup>						\
230 	<popup name=\"CategoriesPopup\">			\
231 	<menuitem action=\"Add to current playlist\"/>		\
232 	<menuitem action=\"Replace current playlist\"/>		\
233 	<menuitem action=\"Replace and play\"/>			\
234 	<separator/>						\
235 	<menuitem action=\"Rescan library\"/>			\
236 	<menuitem action=\"Update library\"/>			\
237 	</popup>						\
238 	</ui>";
239 
240 GtkActionEntry library_tree_context_aentries[] = {
241 	/* Playlist and Radio tree */
242 	{"Add to current playlist", "list-add", N_("_Add to current playlist"),
243 	 "", "Add to current playlist", G_CALLBACK(library_tree_add_to_playlist_action)},
244 	{"Replace current playlist", NULL, N_("_Replace current playlist"),
245 	 "", "Replace current playlist", G_CALLBACK(library_tree_replace_playlist_action)},
246 	{"Replace and play", "media-playback-start", N_("Replace and _play"),
247 	 "", "Replace and play", G_CALLBACK(library_tree_replace_and_play)},
248 	{"Rename", NULL, N_("Rename"),
249 	 "", "Rename", G_CALLBACK(pragha_library_pane_rename_item_action)},
250 	{"Delete", "list-remove", N_("Delete"),
251 	 "", "Delete", G_CALLBACK(pragha_library_pane_remove_item_action)},
252 	{"Export", "document-save", N_("Export"),
253 	 "", "Export", G_CALLBACK(pragha_library_pane_export_playlist_action)},
254 	{"Edit tags", NULL, N_("Edit tags"),
255 	 "", "Edit tags", G_CALLBACK(pragha_library_pane_edit_tags_action)},
256 	{"Move to trash", "user-trash", N_("Move to _trash"),
257 	 "", "Move to trash", G_CALLBACK(pragha_library_pane_delete_from_hdd_action)},
258 	{"Delete from library", "list-remove", N_("Delete from library"),
259 	 "", "Delete from library", G_CALLBACK(pragha_library_pane_delete_from_db_action)}/*,
260 	{"Rescan library", GTK_STOCK_EXECUTE, N_("_Rescan library"),
261 	 "", "Rescan library", G_CALLBACK(rescan_library_action)},
262 	{"Update library", GTK_STOCK_EXECUTE, N_("_Update library"),
263 	 "", "Update library", G_CALLBACK(update_library_action)}*/
264 };
265 
266 /*
267  * library_tree_context_menu calbacks
268  */
269 
270 static void
library_tree_add_to_playlist_action(GtkAction * action,PraghaLibraryPane * library)271 library_tree_add_to_playlist_action(GtkAction *action, PraghaLibraryPane *library)
272 {
273 	g_signal_emit (library, signals[LIBRARY_APPEND_PLAYLIST], 0);
274 }
275 
276 static void
library_tree_replace_playlist_action(GtkAction * action,PraghaLibraryPane * library)277 library_tree_replace_playlist_action(GtkAction *action, PraghaLibraryPane *library)
278 {
279 	g_signal_emit (library, signals[LIBRARY_REPLACE_PLAYLIST], 0);
280 }
281 
282 static void
library_tree_replace_and_play(GtkAction * action,PraghaLibraryPane * library)283 library_tree_replace_and_play(GtkAction *action, PraghaLibraryPane *library)
284 {
285 	g_signal_emit (library, signals[LIBRARY_REPLACE_PLAYLIST_AND_PLAY], 0);
286 }
287 
288 /* Returns TRUE if any of the childs of p_iter matches node_data. iter
289  * and p_iter must be created outside this function */
290 
find_child_node(const gchar * node_data,GtkTreeIter * iter,GtkTreeIter * p_iter,GtkTreeModel * model)291 static gboolean find_child_node(const gchar *node_data, GtkTreeIter *iter,
292 	GtkTreeIter *p_iter, GtkTreeModel *model)
293 {
294 	gchar *data = NULL;
295 	gboolean valid;
296 	gint cmp;
297 
298 	valid = gtk_tree_model_iter_children(model, iter, p_iter);
299 
300 	while (valid) {
301 		gtk_tree_model_get(model, iter, L_NODE_DATA, &data, -1);
302 		if (data) {
303 			cmp = g_ascii_strcasecmp (data, node_data);
304 			if (cmp == 0) {
305 				g_free(data);
306 				return TRUE;
307 			}
308 			else if (cmp > 0) {
309 				g_free(data);
310 				return FALSE;
311 			}
312 		g_free(data);
313 		}
314 		valid = gtk_tree_model_iter_next(model, iter);
315 	}
316 	return FALSE;
317 }
318 
319 /* Prepend a child (iter) to p_iter with given data. NOTE that iter
320  * and p_iter must be created outside this function */
321 
322 static void
library_store_prepend_node(GtkTreeModel * model,GtkTreeIter * iter,GtkTreeIter * p_iter,GdkPixbuf * pixbuf,const gchar * node_data,int node_type,int location_id)323 library_store_prepend_node(GtkTreeModel *model,
324                            GtkTreeIter *iter,
325                            GtkTreeIter *p_iter,
326                            GdkPixbuf *pixbuf,
327                            const gchar *node_data,
328                            int node_type,
329                            int location_id)
330 {
331 	gtk_tree_store_prepend(GTK_TREE_STORE(model), iter, p_iter);
332 
333 	gtk_tree_store_set (GTK_TREE_STORE(model), iter,
334 	                    L_PIXBUF, pixbuf,
335 	                    L_NODE_DATA, node_data,
336 	                    L_NODE_BOLD, PANGO_WEIGHT_NORMAL,
337 	                    L_NODE_TYPE, node_type,
338 	                    L_LOCATION_ID, location_id,
339 	                    L_MACH, FALSE,
340 	                    L_VISIBILE, TRUE,
341 	                    -1);
342 }
343 
344 static void
add_child_node_folder(GtkTreeModel * model,GtkTreeIter * iter,GtkTreeIter * p_iter,const gchar * node_data,PraghaLibraryPane * clibrary)345 add_child_node_folder(GtkTreeModel *model,
346 		      GtkTreeIter *iter,
347 		      GtkTreeIter *p_iter,
348 		      const gchar *node_data,
349 		      PraghaLibraryPane *clibrary)
350 {
351 	GtkTreeIter l_iter;
352 	gchar *data = NULL;
353 	gint l_node_type;
354 	gboolean valid;
355 
356 	/* Find position of the last directory that is a child of p_iter */
357 	valid = gtk_tree_model_iter_children(model, &l_iter, p_iter);
358 	while (valid) {
359 		gtk_tree_model_get(model, &l_iter, L_NODE_TYPE, &l_node_type, -1);
360 		if (l_node_type != NODE_FOLDER)
361 			break;
362 		gtk_tree_model_get(model, &l_iter, L_NODE_DATA, &data, -1);
363 		if (g_ascii_strcasecmp(data, node_data) >= 0) {
364 			g_free(data);
365 			break;
366 		}
367 		g_free(data);
368 
369 		valid = gtk_tree_model_iter_next(model, &l_iter);
370 	}
371 
372 	/* Insert the new folder after the last subdirectory by order */
373 	gtk_tree_store_insert_before (GTK_TREE_STORE(model), iter, p_iter, valid ? &l_iter : NULL);
374 	gtk_tree_store_set (GTK_TREE_STORE(model), iter,L_PIXBUF, clibrary->pixbuf_dir,
375 	                    L_NODE_DATA, node_data,
376 	                    L_NODE_BOLD, PANGO_WEIGHT_NORMAL,
377 	                    L_NODE_TYPE, NODE_FOLDER,
378 	                    L_LOCATION_ID, 0,
379 	                    L_MACH, FALSE,
380 	                    L_VISIBILE, TRUE,
381 	                    -1);
382 }
383 
384 /* Appends a child (iter) to p_iter with given data. NOTE that iter
385  * and p_iter must be created outside this function */
386 
387 static void
add_child_node_file(GtkTreeModel * model,GtkTreeIter * iter,GtkTreeIter * p_iter,const gchar * node_data,int location_id,PraghaLibraryPane * clibrary)388 add_child_node_file(GtkTreeModel *model,
389 		    GtkTreeIter *iter,
390 		    GtkTreeIter *p_iter,
391 		    const gchar *node_data,
392 		    int location_id,
393 		    PraghaLibraryPane *clibrary)
394 {
395 	GtkTreeIter l_iter;
396 	gchar *data = NULL;
397 	gint l_node_type;
398 	gboolean valid;
399 
400 	/* Find position of the last file that is a child of p_iter */
401 	valid = gtk_tree_model_iter_children(model, &l_iter, p_iter);
402 	while (valid) {
403 		gtk_tree_model_get(model, &l_iter, L_NODE_TYPE, &l_node_type, -1);
404 		gtk_tree_model_get(model, &l_iter, L_NODE_DATA, &data, -1);
405 
406 		if ((l_node_type == NODE_BASENAME) && (g_ascii_strcasecmp(data, node_data) >= 0)) {
407 			g_free(data);
408 			break;
409 		}
410 		g_free(data);
411 
412 		valid = gtk_tree_model_iter_next(model, &l_iter);
413 	}
414 
415 	/* Insert the new file after the last file by order */
416 	gtk_tree_store_insert_before (GTK_TREE_STORE(model), iter, p_iter, valid ? &l_iter : NULL);
417 	gtk_tree_store_set (GTK_TREE_STORE(model), iter,
418 	                    L_PIXBUF, clibrary->pixbuf_track,
419 	                    L_NODE_DATA, node_data,
420 	                    L_NODE_BOLD, PANGO_WEIGHT_NORMAL,
421 	                    L_NODE_TYPE, NODE_BASENAME,
422 	                    L_LOCATION_ID, location_id,
423 	                    L_MACH, FALSE,
424 	                    L_VISIBILE, TRUE,
425 	                    -1);
426 }
427 
428 /* Adds a file and its parent directories to the library tree */
429 
430 static void
add_folder_file(GtkTreeModel * model,const gchar * filepath,int location_id,GtkTreeIter * p_iter,PraghaLibraryPane * clibrary)431 add_folder_file(GtkTreeModel *model,
432                 const gchar *filepath,
433                 int location_id,
434                 GtkTreeIter *p_iter,
435                 PraghaLibraryPane *clibrary)
436 {
437 	gchar **subpaths = NULL;		/* To be freed */
438 
439 	GtkTreeIter iter, iter2, search_iter;
440 	int i = 0 , len = 0;
441 
442 	/* Point after library directory prefix */
443 
444 	subpaths = g_strsplit(filepath, G_DIR_SEPARATOR_S, -1);
445 
446 	len = g_strv_length (subpaths);
447 	len--;
448 
449 	/* Add all subdirectories and filename to the tree */
450 	for (i = 0; subpaths[i]; i++) {
451 		if (!find_child_node(subpaths[i], &search_iter, p_iter, model)) {
452 			if(i < len)
453 				add_child_node_folder(model, &iter, p_iter, subpaths[i], clibrary);
454 			else
455 				add_child_node_file(model, &iter, p_iter, subpaths[i], location_id, clibrary);
456 			p_iter = &iter;
457 		}
458 		else {
459 			iter2 = search_iter;
460 			p_iter = &iter2;
461 		}
462 	}
463 
464 	g_strfreev(subpaths);
465 }
466 
467 /* Adds an entry to the library tree by tag (genre, artist...) */
468 
469 static void
add_child_node_by_tags(GtkTreeModel * model,GtkTreeIter * p_iter,gint location_id,const gchar * location,const gchar * genre,const gchar * album,const gchar * year,const gchar * artist,const gchar * track,PraghaLibraryPane * clibrary)470 add_child_node_by_tags (GtkTreeModel *model,
471                        GtkTreeIter *p_iter,
472                        gint location_id,
473                        const gchar *location,
474                        const gchar *genre,
475                        const gchar *album,
476                        const gchar *year,
477                        const gchar *artist,
478                        const gchar *track,
479                        PraghaLibraryPane *clibrary)
480 {
481 	GtkTreeIter iter, iter2, search_iter;
482 	gchar *node_data = NULL;
483 	GdkPixbuf *node_pixbuf = NULL;
484 	LibraryNodeType node_type = 0;
485 	gint node_level = 0, tot_levels = 0;
486 	gboolean need_gfree = FALSE;
487 
488 	/* Iterate through library tree node types */
489 	tot_levels = g_slist_length(clibrary->library_tree_nodes);
490 	while (node_level < tot_levels) {
491 		/* Set data to be added to the tree node depending on the type of node */
492 		node_type = GPOINTER_TO_INT(g_slist_nth_data(clibrary->library_tree_nodes, node_level));
493 		switch (node_type) {
494 			case NODE_TRACK:
495 				node_pixbuf = clibrary->pixbuf_track;
496 				if (string_is_not_empty(track)) {
497 					node_data = (gchar *)track;
498 				}
499 				else {
500 					node_data = get_display_filename(location, FALSE);
501 					need_gfree = TRUE;
502 				}
503 				break;
504 			case NODE_ARTIST:
505 				node_pixbuf = clibrary->pixbuf_artist;
506 				node_data = string_is_not_empty(artist) ? (gchar *)artist : _("Unknown Artist");
507 				break;
508 			case NODE_ALBUM:
509 				node_pixbuf = clibrary->pixbuf_album;
510 				if (pragha_preferences_get_sort_by_year(clibrary->preferences)) {
511 					node_data = g_strconcat ((string_is_not_empty(year) && (atoi(year) > 0)) ? year : _("Unknown"),
512 					                          " - ",
513 					                          string_is_not_empty(album) ? album : _("Unknown Album"),
514 					                          NULL);
515 					need_gfree = TRUE;
516 				}
517 				else {
518 					node_data = string_is_not_empty(album) ? (gchar *)album : _("Unknown Album");
519 				}
520 				break;
521 			case NODE_GENRE:
522 				node_pixbuf = clibrary->pixbuf_genre;
523 				node_data = string_is_not_empty(genre) ? (gchar *)genre : _("Unknown Genre");
524 				break;
525 			case NODE_CATEGORY:
526 			case NODE_FOLDER:
527 			case NODE_PLAYLIST:
528 			case NODE_RADIO:
529 			case NODE_BASENAME:
530 			default:
531 				g_warning("add_by_tag: Bad node type.");
532 				break;
533 		}
534 
535 		/* Find / add child node if it's not already added */
536 		if (node_type != NODE_TRACK) {
537 			if (!find_child_node(node_data, &search_iter, p_iter, model)) {
538 				library_store_prepend_node(model,
539 				                           &iter,
540 				                           p_iter,
541 				                           node_pixbuf,
542 				                           node_data,
543 				                           node_type,
544 				                           0);
545 				p_iter = &iter;
546 			}
547 			else {
548 				iter2 = search_iter;
549 				p_iter = &iter2;
550 			}
551 		}
552 		else {
553 			library_store_prepend_node(model,
554 			                           &iter,
555 			                           p_iter,
556 			                           node_pixbuf,
557 			                           node_data,
558 			                           NODE_TRACK,
559 			                           location_id);
560 		}
561 
562 		/* Free node_data if needed */
563 		if (need_gfree) {
564 			need_gfree = FALSE;
565 			g_free(node_data);
566 		}
567 		node_level++;
568 	}
569 }
570 
571 GString *
append_pragha_uri_string_list(GtkTreeIter * r_iter,GString * list,GtkTreeModel * model)572 append_pragha_uri_string_list(GtkTreeIter *r_iter,
573                               GString *list,
574                               GtkTreeModel *model)
575 {
576 	GtkTreeIter t_iter;
577 	LibraryNodeType node_type = 0;
578 	gint location_id;
579 	gchar *data, *uri = NULL;
580 	gboolean valid;
581 
582 	gtk_tree_model_get(model, r_iter, L_NODE_TYPE, &node_type, -1);
583 
584 	switch (node_type) {
585 		case NODE_CATEGORY:
586 		case NODE_FOLDER:
587 		case NODE_GENRE:
588 		case NODE_ARTIST:
589 		case NODE_ALBUM:
590 			valid = gtk_tree_model_iter_children(model, &t_iter, r_iter);
591 			while (valid) {
592 				list = append_pragha_uri_string_list(&t_iter, list, model);
593 
594 				valid = gtk_tree_model_iter_next(model, &t_iter);
595 			}
596 			pragha_process_gtk_events ();
597 	 		break;
598 		case NODE_TRACK:
599 		case NODE_BASENAME:
600 			gtk_tree_model_get(model, r_iter, L_LOCATION_ID, &location_id, -1);
601 			uri = g_strdup_printf("Location:/%d", location_id);
602 			break;
603 		case NODE_PLAYLIST:
604 			gtk_tree_model_get(model, r_iter, L_NODE_DATA, &data, -1);
605 			uri = g_strdup_printf("Playlist:/%s", data);
606 			g_free(data);
607 			break;
608 		case NODE_RADIO:
609 			gtk_tree_model_get(model, r_iter, L_NODE_DATA, &data, -1);
610 			uri = g_strdup_printf("Radio:/%s", data);
611 			g_free(data);
612 			break;
613 		default:
614 			break;
615 	}
616 
617 	if(uri) {
618 		g_string_append (list, uri);
619 		g_string_append (list, "\r\n");
620 		g_free(uri);
621 	}
622 
623 	return list;
624 }
625 
626 static GString *
append_uri_string_list(GtkTreeIter * r_iter,GString * list,GtkTreeModel * model,PraghaLibraryPane * clibrary)627 append_uri_string_list(GtkTreeIter *r_iter,
628                        GString *list,
629                        GtkTreeModel *model,
630                        PraghaLibraryPane *clibrary)
631 {
632 	GtkTreeIter t_iter;
633 	LibraryNodeType node_type = 0;
634 	gint location_id;
635 	gchar *filename = NULL, *uri = NULL;
636 	gboolean valid;
637 
638 	gtk_tree_model_get(model, r_iter, L_NODE_TYPE, &node_type, -1);
639 
640 	switch (node_type) {
641 		case NODE_CATEGORY:
642 		case NODE_FOLDER:
643 		case NODE_GENRE:
644 		case NODE_ARTIST:
645 		case NODE_ALBUM:
646 			valid = gtk_tree_model_iter_children(model, &t_iter, r_iter);
647 			while (valid) {
648 				list = append_uri_string_list(&t_iter, list, model, clibrary);
649 
650 				valid = gtk_tree_model_iter_next(model, &t_iter);
651 			}
652 			pragha_process_gtk_events ();
653 			break;
654 		case NODE_TRACK:
655 		case NODE_BASENAME:
656 			gtk_tree_model_get(model, r_iter, L_LOCATION_ID, &location_id, -1);
657 			filename = pragha_database_get_filename_from_location_id(clibrary->cdbase, location_id);
658 			break;
659 		case NODE_PLAYLIST:
660 		case NODE_RADIO:
661 			g_message("Drag Radios and Playlist not yet implemented");
662 			break;
663 		default:
664 			break;
665 	}
666 
667 	if(filename) {
668 		uri = g_filename_to_uri(filename, NULL, NULL);
669 		if(uri) {
670 			g_string_append (list, uri);
671 			g_string_append (list, "\r\n");
672 
673 			g_free(uri);
674 		}
675 		g_free(filename);
676 	}
677 
678 	return list;
679 }
680 
681 /* Append to the given array the location ids of
682    all the nodes under the given path */
683 
get_location_ids(GtkTreePath * path,GArray * loc_arr,GtkTreeModel * model,PraghaLibraryPane * clibrary)684 static void get_location_ids(GtkTreePath *path,
685 			     GArray *loc_arr,
686 			     GtkTreeModel *model,
687 			     PraghaLibraryPane *clibrary)
688 {
689 	GtkTreeIter t_iter, r_iter;
690 	LibraryNodeType node_type = 0;
691 	gint location_id;
692 	gint j = 0;
693 
694 	clibrary->view_change = TRUE;
695 
696 	gtk_tree_model_get_iter(model, &r_iter, path);
697 
698 	/* If this path is a track, just append it to the array */
699 
700 	gtk_tree_model_get(model, &r_iter, L_NODE_TYPE, &node_type, -1);
701 	if ((node_type == NODE_TRACK) || (node_type == NODE_BASENAME)) {
702 		gtk_tree_model_get(model, &r_iter, L_LOCATION_ID, &location_id, -1);
703 		g_array_append_val(loc_arr, location_id);
704 	}
705 
706 	/* For all other node types do a recursive add */
707 
708 	while (gtk_tree_model_iter_nth_child(model, &t_iter, &r_iter, j++)) {
709 		gtk_tree_model_get(model, &t_iter, L_NODE_TYPE, &node_type, -1);
710 		if ((node_type == NODE_TRACK) || (node_type == NODE_BASENAME)) {
711 			gtk_tree_model_get(model, &t_iter,
712 					   L_LOCATION_ID, &location_id, -1);
713 			g_array_append_val(loc_arr, location_id);
714 		}
715 		else {
716 			path = gtk_tree_model_get_path(model, &t_iter);
717 			get_location_ids(path, loc_arr, model, clibrary);
718 			gtk_tree_path_free(path);
719 		}
720 	}
721 
722 	clibrary->view_change = FALSE;
723 }
724 
725 /* Add all the tracks under the given path to the current playlist */
726 
727 GList *
append_library_row_to_mobj_list(PraghaDatabase * cdbase,GtkTreePath * path,GtkTreeModel * row_model,GList * list)728 append_library_row_to_mobj_list(PraghaDatabase *cdbase,
729                                 GtkTreePath *path,
730                                 GtkTreeModel *row_model,
731                                 GList *list)
732 {
733 	GtkTreeIter t_iter, r_iter;
734 	LibraryNodeType node_type = 0;
735 	gint location_id;
736 	PraghaMusicobject *mobj = NULL;
737 	gchar *data = NULL;
738 	gint j = 0;
739 
740 	/* If this path is a track, just append it to the current playlist */
741 
742 	gtk_tree_model_get_iter(row_model, &r_iter, path);
743 
744 	gtk_tree_model_get(row_model, &r_iter, L_NODE_TYPE, &node_type, -1);
745 	gtk_tree_model_get(row_model, &r_iter, L_LOCATION_ID, &location_id, -1);
746 	gtk_tree_model_get(row_model, &r_iter, L_NODE_DATA, &data, -1);
747 
748 	switch (node_type) {
749 		case NODE_CATEGORY:
750 		case NODE_FOLDER:
751 		case NODE_GENRE:
752 		case NODE_ARTIST:
753 		case NODE_ALBUM:
754 			/* For all other node types do a recursive add */
755 			while (gtk_tree_model_iter_nth_child(row_model, &t_iter, &r_iter, j++)) {
756 				path = gtk_tree_model_get_path(row_model, &t_iter);
757 				list = append_library_row_to_mobj_list(cdbase, path, row_model, list);
758 				gtk_tree_path_free(path);
759 			}
760 			break;
761 		case NODE_TRACK:
762 		case NODE_BASENAME:
763 			mobj = new_musicobject_from_db(cdbase, location_id);
764 			if (G_LIKELY(mobj))
765 				list = g_list_append(list, mobj);
766 			break;
767 		case NODE_PLAYLIST:
768 			list = add_playlist_to_mobj_list(cdbase, data, list);
769 			break;
770 		case NODE_RADIO:
771 			list = add_radio_to_mobj_list(cdbase, data, list);
772 			break;
773 		default:
774 			break;
775 	}
776 
777 	g_free(data);
778 
779 	return list;
780 }
781 
782 static void
delete_row_from_db(PraghaDatabase * cdbase,GtkTreePath * path,GtkTreeModel * model)783 delete_row_from_db(PraghaDatabase *cdbase,
784                    GtkTreePath *path,
785                    GtkTreeModel *model)
786 {
787 	GtkTreeIter t_iter, r_iter;
788 	LibraryNodeType node_type = 0;
789 	gboolean valid;
790 	gint location_id;
791 
792 	/* If this path is a track, delete it immediately */
793 
794 	gtk_tree_model_get_iter(model, &r_iter, path);
795 	gtk_tree_model_get(model, &r_iter, L_NODE_TYPE, &node_type, -1);
796 	if ((node_type == NODE_TRACK) || (node_type == NODE_BASENAME)) {
797 		gtk_tree_model_get(model, &r_iter, L_LOCATION_ID, &location_id, -1);
798 		pragha_database_forget_location(cdbase, location_id);
799 	}
800 
801 	/* For all other node types do a recursive deletion */
802 
803 	valid = gtk_tree_model_iter_children(model, &t_iter, &r_iter);
804 	while (valid) {
805 		gtk_tree_model_get(model, &t_iter, L_NODE_TYPE, &node_type, -1);
806 		if ((node_type == NODE_TRACK) || (node_type == NODE_BASENAME)) {
807 			gtk_tree_model_get(model, &t_iter,
808 					   L_LOCATION_ID, &location_id, -1);
809 			pragha_database_forget_location(cdbase, location_id);
810 		}
811 		else {
812 			path = gtk_tree_model_get_path(model, &t_iter);
813 			delete_row_from_db(cdbase, path, model);
814 			gtk_tree_path_free(path);
815 		}
816 
817 		valid = gtk_tree_model_iter_next(model, &t_iter);
818 	}
819 }
820 
821 static void
trash_or_unlink_row(GArray * loc_arr,gboolean unlink,PraghaLibraryPane * library)822 trash_or_unlink_row (GArray *loc_arr, gboolean unlink, PraghaLibraryPane *library)
823 {
824 	GtkWidget *question_dialog;
825 	gchar *primary, *secondary, *filename = NULL;
826 	gint response, location_id = 0;
827 	guint i;
828 	gboolean deleted = FALSE;
829 	GError *error = NULL;
830 	GFile *file = NULL;
831 
832 	if (!loc_arr)
833 		return;
834 
835 	for(i = 0; i < loc_arr->len; i++) {
836 		location_id = g_array_index(loc_arr, gint, i);
837 		if (location_id) {
838 			filename = pragha_database_get_filename_from_location_id (library->cdbase, location_id);
839 			if (filename && g_file_test(filename, G_FILE_TEST_EXISTS)) {
840 				file = g_file_new_for_path(filename);
841 
842 				if(!unlink && !(deleted = g_file_trash(file, NULL, &error))) {
843 					primary = g_strdup (_("File can't be moved to trash. Delete permanently?"));
844 					secondary = g_strdup_printf (_("The file \"%s\" cannot be moved to the trash. Details: %s"),
845 									g_file_get_basename (file), error->message);
846 
847 					question_dialog = gtk_message_dialog_new (GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(library))),
848 				                                                GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
849 				                                                GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s", primary);
850 					gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (question_dialog), "%s", secondary);
851 
852 					gtk_dialog_add_button (GTK_DIALOG (question_dialog), _("_Cancel"), GTK_RESPONSE_CANCEL);
853 					if (loc_arr->len > 1) {
854 					        gtk_dialog_add_button (GTK_DIALOG (question_dialog), PRAGHA_BUTTON_SKIP, PRAGHA_RESPONSE_SKIP);
855 					        gtk_dialog_add_button (GTK_DIALOG (question_dialog), PRAGHA_BUTTON_SKIP_ALL, PRAGHA_RESPONSE_SKIP_ALL);
856 			        		gtk_dialog_add_button (GTK_DIALOG (question_dialog), PRAGHA_BUTTON_DELETE_ALL, PRAGHA_RESPONSE_DELETE_ALL);
857 					}
858 					gtk_dialog_add_button (GTK_DIALOG (question_dialog), _("_Delete"), GTK_RESPONSE_ACCEPT);
859 
860 					response = gtk_dialog_run (GTK_DIALOG (question_dialog));
861 					gtk_widget_destroy (question_dialog);
862 					g_free (primary);
863 					g_free (secondary);
864 					g_error_free (error);
865 					error = NULL;
866 
867 					switch (response)
868 					{
869 						case PRAGHA_RESPONSE_DELETE_ALL:
870 							unlink = TRUE;
871 							break;
872 						case GTK_RESPONSE_ACCEPT:
873 							g_unlink(filename);
874 							deleted = TRUE;
875 							break;
876 						case PRAGHA_RESPONSE_SKIP:
877 							break;
878 						case PRAGHA_RESPONSE_SKIP_ALL:
879 						case GTK_RESPONSE_CANCEL:
880 						case GTK_RESPONSE_DELETE_EVENT:
881 						default:
882 							return;
883 					}
884 				}
885 				if(unlink) {
886 					g_unlink(filename);
887 					deleted = TRUE;
888 				}
889 				g_object_unref(G_OBJECT(file));
890 			}
891 			if (deleted) {
892 				pragha_database_forget_location (library->cdbase, location_id);
893 			}
894 		}
895 	}
896 }
897 
898 /******************/
899 /* Event handlers */
900 /******************/
901 
902 static void
library_tree_row_activated_cb(GtkTreeView * library_tree,GtkTreePath * path,GtkTreeViewColumn * column,PraghaLibraryPane * library)903 library_tree_row_activated_cb (GtkTreeView *library_tree,
904                                GtkTreePath *path,
905                                GtkTreeViewColumn *column,
906                                PraghaLibraryPane *library)
907 {
908 	GtkTreeIter iter;
909 	GtkTreeModel *filter_model;
910 	LibraryNodeType node_type;
911 
912 	filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW(library->library_tree));
913 	gtk_tree_model_get_iter (filter_model, &iter, path);
914 	gtk_tree_model_get (filter_model, &iter, L_NODE_TYPE, &node_type, -1);
915 
916 	switch(node_type) {
917 	case NODE_CATEGORY:
918 	case NODE_ARTIST:
919 	case NODE_ALBUM:
920 	case NODE_GENRE:
921 	case NODE_FOLDER:
922 		if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW(library->library_tree), path))
923 			gtk_tree_view_expand_row (GTK_TREE_VIEW(library->library_tree), path, TRUE);
924 		else
925 			gtk_tree_view_collapse_row (GTK_TREE_VIEW(library->library_tree), path);
926 		break;
927 	case NODE_TRACK:
928 	case NODE_BASENAME:
929 	case NODE_PLAYLIST:
930 	case NODE_RADIO:
931 		g_signal_emit (library, signals[LIBRARY_APPEND_PLAYLIST], 0);
932 		break;
933 	default:
934 		break;
935 	}
936 }
937 
library_tree_key_press(GtkWidget * win,GdkEventKey * event,PraghaLibraryPane * library)938 static int library_tree_key_press (GtkWidget *win, GdkEventKey *event, PraghaLibraryPane *library)
939 {
940 	if (event->state != 0
941 			&& ((event->state & GDK_CONTROL_MASK)
942 			|| (event->state & GDK_MOD1_MASK)
943 			|| (event->state & GDK_MOD3_MASK)
944 			|| (event->state & GDK_MOD4_MASK)
945 			|| (event->state & GDK_MOD5_MASK)))
946 		return FALSE;
947 	if (event->keyval == GDK_KEY_Delete){
948 		pragha_library_pane_delete_from_db_action (NULL, library);
949 		return TRUE;
950 	}
951 	return FALSE;
952 }
953 
954 /*****************/
955 /* DnD functions */
956 /*****************/
957 /* These two functions are only callbacks that must be passed to
958 gtk_tree_selection_set_select_function() to chose if GTK is allowed
959 to change selection itself or if we handle it ourselves */
960 
961 static gboolean
pragha_library_pane_selection_func_true(GtkTreeSelection * selection,GtkTreeModel * model,GtkTreePath * path,gboolean path_currently_selected,gpointer data)962 pragha_library_pane_selection_func_true(GtkTreeSelection *selection,
963                                         GtkTreeModel *model,
964                                         GtkTreePath *path,
965                                         gboolean path_currently_selected,
966                                         gpointer data)
967 {
968 	return TRUE;
969 }
970 
971 static gboolean
pragha_library_pane_selection_func_false(GtkTreeSelection * selection,GtkTreeModel * model,GtkTreePath * path,gboolean path_currently_selected,gpointer data)972 pragha_library_pane_selection_func_false(GtkTreeSelection *selection,
973                                          GtkTreeModel *model,
974                                          GtkTreePath *path,
975                                          gboolean path_currently_selected,
976                                          gpointer data)
977 {
978 	return FALSE;
979 }
980 
981 
982 static gboolean
library_tree_button_press_cb(GtkWidget * widget,GdkEventButton * event,PraghaLibraryPane * library)983 library_tree_button_press_cb (GtkWidget *widget,
984                               GdkEventButton *event,
985                               PraghaLibraryPane *library)
986 {
987 	GtkWidget *popup_menu, *item_widget;
988 	GtkTreeModel *model;
989 	GtkTreePath *path;
990 	GtkTreeIter iter;
991 	GtkTreeSelection *selection;
992 	gboolean many_selected = FALSE;
993 	LibraryNodeType node_type;
994 	gint n_select = 0;
995 
996 	model = gtk_tree_view_get_model (GTK_TREE_VIEW(library->library_tree));
997 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(library->library_tree));
998 
999 	if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), (gint) event->x,(gint) event->y, &path, NULL, NULL, NULL)){
1000 		switch(event->button) {
1001 		case 1:
1002 			if (gtk_tree_selection_path_is_selected(selection, path)
1003 			    && !(event->state & GDK_CONTROL_MASK)
1004 			    && !(event->state & GDK_SHIFT_MASK)) {
1005 				gtk_tree_selection_set_select_function(selection, &pragha_library_pane_selection_func_false, NULL, NULL);
1006 			}
1007 			else {
1008 				gtk_tree_selection_set_select_function(selection, &pragha_library_pane_selection_func_true, NULL, NULL);
1009 			}
1010 			break;
1011 		case 2:
1012 			if (!gtk_tree_selection_path_is_selected(selection, path)) {
1013 				gtk_tree_selection_unselect_all(selection);
1014 				gtk_tree_selection_select_path(selection, path);
1015 			}
1016 			g_signal_emit (library, signals[LIBRARY_APPEND_PLAYLIST], 0);
1017 			break;
1018 		case 3:
1019 			if (!(gtk_tree_selection_path_is_selected(selection, path))){
1020 				gtk_tree_selection_unselect_all(selection);
1021 				gtk_tree_selection_select_path(selection, path);
1022 			}
1023 
1024 			gtk_tree_model_get_iter(model, &iter, path);
1025 			gtk_tree_model_get(model, &iter, L_NODE_TYPE, &node_type, -1);
1026 
1027 			n_select = gtk_tree_selection_count_selected_rows(selection);
1028 
1029 			if (node_type == NODE_PLAYLIST || node_type == NODE_RADIO) {
1030 				popup_menu = gtk_ui_manager_get_widget(library->library_tree_context_menu,
1031 									"/PlaylistPopup");
1032 
1033 				item_widget = gtk_ui_manager_get_widget(library->library_tree_context_menu,
1034 									"/PlaylistPopup/Rename");
1035 				gtk_widget_set_sensitive (GTK_WIDGET(item_widget),
1036 							  n_select == 1 && gtk_tree_path_get_depth(path) > 1);
1037 
1038 				item_widget = gtk_ui_manager_get_widget(library->library_tree_context_menu,
1039 									"/PlaylistPopup/Delete");
1040 				gtk_widget_set_sensitive (GTK_WIDGET(item_widget),
1041 							  gtk_tree_path_get_depth(path) > 1);
1042 
1043 				item_widget = gtk_ui_manager_get_widget(library->library_tree_context_menu,
1044 									"/PlaylistPopup/Export");
1045 				gtk_widget_set_sensitive (GTK_WIDGET(item_widget),
1046 							  n_select == 1 &&
1047 							  gtk_tree_path_get_depth(path) > 1 &&
1048 							  node_type == NODE_PLAYLIST);
1049 			}
1050 			else {
1051 				if (gtk_tree_path_get_depth(path) > 1)
1052 					popup_menu = gtk_ui_manager_get_widget(library->library_tree_context_menu,
1053 									       "/LibraryPopup");
1054 				else
1055 					popup_menu = gtk_ui_manager_get_widget(library->library_tree_context_menu,
1056 									       "/CategoriesPopup");
1057 			}
1058 
1059 			gtk_menu_popup(GTK_MENU(popup_menu), NULL, NULL, NULL, NULL,
1060 				       event->button, event->time);
1061 
1062 			/* If more than one track is selected, don't propagate event */
1063 
1064 			if (n_select > 1)
1065 				many_selected = TRUE;
1066 			else
1067 				many_selected = FALSE;
1068 			break;
1069 		default:
1070 			many_selected = FALSE;
1071 			break;
1072 		}
1073 	gtk_tree_path_free(path);
1074 	}
1075 	else gtk_tree_selection_unselect_all(selection);
1076 
1077 	return many_selected;
1078 }
1079 
1080 static gboolean
library_tree_button_release_cb(GtkWidget * widget,GdkEventButton * event,PraghaLibraryPane * library)1081 library_tree_button_release_cb (GtkWidget *widget,
1082                                 GdkEventButton *event,
1083                                 PraghaLibraryPane *library)
1084 {
1085 	GtkTreeSelection *selection;
1086 	GtkTreePath *path;
1087 
1088 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(library->library_tree));
1089 
1090 	if((event->state & GDK_CONTROL_MASK) || (event->state & GDK_SHIFT_MASK) || (library->dragging == TRUE) || (event->button!=1)){
1091 		gtk_tree_selection_set_select_function(selection, &pragha_library_pane_selection_func_true, NULL, NULL);
1092 		library->dragging = FALSE;
1093 		return FALSE;
1094 	}
1095 
1096 	gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), (gint) event->x,(gint) event->y, &path, NULL, NULL, NULL);
1097 
1098 	if (path){
1099 		gtk_tree_selection_set_select_function(selection, &pragha_library_pane_selection_func_true, NULL, NULL);
1100 		gtk_tree_selection_unselect_all(selection);
1101 		gtk_tree_selection_select_path(selection, path);
1102 		gtk_tree_path_free(path);
1103 	}
1104 	return FALSE;
1105 }
1106 
1107 /*******/
1108 /* DnD */
1109 /*******/
1110 
1111 static gboolean
dnd_library_tree_begin(GtkWidget * widget,GdkDragContext * context,PraghaLibraryPane * clibrary)1112 dnd_library_tree_begin(GtkWidget *widget,
1113                        GdkDragContext *context,
1114                        PraghaLibraryPane *clibrary)
1115 {
1116 	clibrary->dragging = TRUE;
1117 	return FALSE;
1118 }
1119 
1120 gboolean
gtk_selection_data_set_pragha_uris(GtkSelectionData * selection_data,GString * list)1121 gtk_selection_data_set_pragha_uris (GtkSelectionData  *selection_data,
1122                                     GString *list)
1123 {
1124 	gchar *result;
1125 	gsize length;
1126 
1127 	result = g_convert (list->str, list->len,
1128 	                    "ASCII", "UTF-8",
1129 	                    NULL, &length, NULL);
1130 
1131 	if (result) {
1132 		gtk_selection_data_set (selection_data,
1133 		                        gtk_selection_data_get_target(selection_data),
1134 		                        8, (guchar *) result, length);
1135 		g_free (result);
1136 
1137 		return TRUE;
1138 	}
1139 
1140 	return FALSE;
1141 }
1142 
1143 /* Callback for DnD signal 'drag-data-get' */
1144 
1145 static void
dnd_library_tree_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * data,PraghaDndTarget info,guint time,PraghaLibraryPane * clibrary)1146 dnd_library_tree_get(GtkWidget *widget,
1147                      GdkDragContext *context,
1148                      GtkSelectionData *data,
1149                      PraghaDndTarget info,
1150                      guint time,
1151                      PraghaLibraryPane *clibrary)
1152 {
1153 	GtkTreeSelection *selection;
1154 	GtkTreeModel *model;
1155 	GList *list = NULL, *l;
1156 	GString *rlist;
1157 	GtkTreeIter s_iter;
1158 
1159 	switch(info) {
1160 	case TARGET_REF_LIBRARY:
1161 		rlist = g_string_new (NULL);
1162 
1163 		set_watch_cursor (GTK_WIDGET(clibrary));
1164 		clibrary->view_change = TRUE;
1165 
1166 		selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
1167 							clibrary->library_tree));
1168 		list = gtk_tree_selection_get_selected_rows(selection, &model);
1169 
1170 		l = list;
1171 		while(l) {
1172 			if(gtk_tree_model_get_iter(model, &s_iter, l->data))
1173 				rlist = append_pragha_uri_string_list(&s_iter, rlist, model);
1174 			gtk_tree_path_free(l->data);
1175 			l = l->next;
1176 		}
1177 
1178 		clibrary->view_change = FALSE;
1179 		remove_watch_cursor (GTK_WIDGET(clibrary));
1180 
1181 		gtk_selection_data_set_pragha_uris(data, rlist);
1182 
1183 		g_list_free(list);
1184 		g_string_free (rlist, TRUE);
1185  		break;
1186  	case TARGET_URI_LIST:
1187 		rlist = g_string_new (NULL);
1188 
1189 		set_watch_cursor (GTK_WIDGET(clibrary));
1190 		clibrary->view_change = TRUE;
1191 
1192 		selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
1193 							clibrary->library_tree));
1194 		list = gtk_tree_selection_get_selected_rows(selection, &model);
1195 
1196 		l = list;
1197 		while(l) {
1198 			if(gtk_tree_model_get_iter(model, &s_iter, l->data))
1199 				rlist = append_uri_string_list(&s_iter, rlist, model, clibrary);
1200 			l = l->next;
1201 		}
1202 
1203 		clibrary->view_change = FALSE;
1204 		remove_watch_cursor (GTK_WIDGET(clibrary));
1205 
1206 		gtk_selection_data_set_pragha_uris(data, rlist);
1207 
1208 		g_list_free(list);
1209 		g_string_free (rlist, TRUE);
1210 		break;
1211 	case TARGET_PLAIN_TEXT:
1212 	default:
1213 		g_warning("Unknown DND type");
1214 		break;
1215 	}
1216 }
1217 
1218 static const GtkTargetEntry lentries[] = {
1219 	{"REF_LIBRARY", GTK_TARGET_SAME_APP, TARGET_REF_LIBRARY},
1220 	{"text/uri-list", GTK_TARGET_OTHER_APP, TARGET_URI_LIST},
1221 	{"text/plain", GTK_TARGET_OTHER_APP, TARGET_PLAIN_TEXT}
1222 };
1223 
1224 static void
library_pane_init_dnd(PraghaLibraryPane * clibrary)1225 library_pane_init_dnd(PraghaLibraryPane *clibrary)
1226 {
1227 	/* Source: Library View */
1228 
1229 	gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(clibrary->library_tree),
1230 					       GDK_BUTTON1_MASK,
1231 					       lentries,
1232 					       G_N_ELEMENTS(lentries),
1233 					       GDK_ACTION_COPY);
1234 
1235 	g_signal_connect(G_OBJECT(GTK_WIDGET(clibrary->library_tree)),
1236 	                 "drag-begin",
1237 	                 G_CALLBACK(dnd_library_tree_begin),
1238 	                 clibrary);
1239 	g_signal_connect(G_OBJECT(clibrary->library_tree),
1240 	                 "drag-data-get",
1241 	                 G_CALLBACK(dnd_library_tree_get),
1242 	                 clibrary);
1243 }
1244 
1245 /**********/
1246 /* Search */
1247 /**********/
1248 
set_all_visible(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)1249 static gboolean set_all_visible(GtkTreeModel *model,
1250 				GtkTreePath *path,
1251 				GtkTreeIter *iter,
1252 				gpointer data)
1253 {
1254 	gtk_tree_store_set(GTK_TREE_STORE(model), iter,
1255 			   L_MACH, FALSE,
1256 			   L_VISIBILE, TRUE,
1257 			   -1);
1258 	return FALSE;
1259 }
1260 
1261 static void
library_expand_filtered_tree_func(GtkTreeView * view,GtkTreePath * path,gpointer data)1262 library_expand_filtered_tree_func(GtkTreeView *view,
1263                                   GtkTreePath *path,
1264                                   gpointer data)
1265 {
1266 	GtkTreeIter iter;
1267 	LibraryNodeType node_type;
1268 	gboolean node_mach;
1269 
1270 	GtkTreeModel *filter_model = data;
1271 
1272 	/* Collapse any non-leaf node that matches the seach entry */
1273 
1274 	gtk_tree_model_get_iter(filter_model, &iter, path);
1275 	gtk_tree_model_get(filter_model, &iter, L_MACH, &node_mach, -1);
1276 
1277 	if (node_mach) {
1278 		gtk_tree_model_get(filter_model, &iter, L_NODE_TYPE, &node_type, -1);
1279 		if ((node_type != NODE_TRACK) && (node_type != NODE_BASENAME))
1280 			gtk_tree_view_collapse_row(view, path);
1281 	}
1282 }
1283 
1284 static void
set_visible_parents_nodes(GtkTreeModel * model,GtkTreeIter * c_iter)1285 set_visible_parents_nodes(GtkTreeModel *model, GtkTreeIter *c_iter)
1286 {
1287 	GtkTreeIter t_iter, parent;
1288 
1289 	t_iter = *c_iter;
1290 
1291 	while(gtk_tree_model_iter_parent(model, &parent, &t_iter)) {
1292 		gtk_tree_store_set(GTK_TREE_STORE(model), &parent,
1293 				   L_VISIBILE, TRUE,
1294 				   -1);
1295 		t_iter = parent;
1296 	}
1297 }
1298 
1299 static gboolean
any_parent_node_mach(GtkTreeModel * model,GtkTreeIter * iter)1300 any_parent_node_mach(GtkTreeModel *model, GtkTreeIter *iter)
1301 {
1302 	GtkTreeIter t_iter, parent;
1303 	gboolean p_mach = FALSE;
1304 
1305 	t_iter = *iter;
1306 	while(gtk_tree_model_iter_parent(model, &parent, &t_iter)) {
1307 		gtk_tree_model_get(model, &parent,
1308 				   L_MACH, &p_mach,
1309 				   -1);
1310 		if (p_mach)
1311 			return TRUE;
1312 
1313 		t_iter = parent;
1314 	}
1315 
1316 	return FALSE;
1317 }
1318 
filter_tree_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)1319 static gboolean filter_tree_func(GtkTreeModel *model,
1320 				 GtkTreePath *path,
1321 				 GtkTreeIter *iter,
1322 				 gpointer data)
1323 {
1324 	PraghaLibraryPane *clibrary = data;
1325 	gchar *node_data = NULL, *u_str;
1326 	gboolean p_mach;
1327 
1328 	/* Mark node and its parents visible if search entry matches.
1329 	   If search entry doesn't match, check if _any_ ancestor has
1330 	   been marked as visible and if so, mark current node as visible too. */
1331 
1332 	if (clibrary->filter_entry) {
1333 		gtk_tree_model_get(model, iter, L_NODE_DATA, &node_data, -1);
1334 		u_str = g_utf8_strdown(node_data, -1);
1335 		if (pragha_strstr_lv(u_str, clibrary->filter_entry, clibrary->preferences)) {
1336 			/* Set visible the match row */
1337 			gtk_tree_store_set(GTK_TREE_STORE(model), iter,
1338 					   L_MACH, TRUE,
1339 					   L_VISIBILE, TRUE,
1340 					   -1);
1341 
1342 			/* Also set visible the parents */
1343 			set_visible_parents_nodes(model, iter);
1344 		}
1345 		else {
1346 			/* Check parents. If any node is visible due it mach,
1347 			 * also shows. So, show the children of coincidences. */
1348 			p_mach = any_parent_node_mach(model, iter);
1349 			gtk_tree_store_set(GTK_TREE_STORE(model), iter,
1350 					   L_MACH, FALSE,
1351 					   L_VISIBILE, p_mach,
1352 					   -1);
1353 		}
1354 		g_free(u_str);
1355 		g_free(node_data);
1356 	}
1357 	else
1358 		return TRUE;
1359 
1360 	return FALSE;
1361 }
1362 
do_refilter(PraghaLibraryPane * clibrary)1363 gboolean do_refilter(PraghaLibraryPane *clibrary)
1364 {
1365 	GtkTreeModel *filter_model;
1366 
1367 	/* Remove the model of widget. */
1368 	filter_model = gtk_tree_view_get_model(GTK_TREE_VIEW(clibrary->library_tree));
1369 	g_object_ref(filter_model);
1370 	gtk_tree_view_set_model(GTK_TREE_VIEW(clibrary->library_tree), NULL);
1371 
1372 	/* Set visibility of rows in the library store. */
1373 	gtk_tree_model_foreach(GTK_TREE_MODEL(clibrary->library_store),
1374 				filter_tree_func,
1375 				clibrary);
1376 
1377 	/* Set the model again.*/
1378 	gtk_tree_view_set_model(GTK_TREE_VIEW(clibrary->library_tree), filter_model);
1379 	g_object_unref(filter_model);
1380 
1381 	/* Expand all and then reduce properly. */
1382 	gtk_tree_view_expand_all(GTK_TREE_VIEW(clibrary->library_tree));
1383 	gtk_tree_view_map_expanded_rows(GTK_TREE_VIEW(clibrary->library_tree),
1384 		library_expand_filtered_tree_func,
1385 		filter_model);
1386 
1387 	clibrary->timeout_id = 0;
1388 
1389 	return FALSE;
1390 }
1391 
queue_refilter(PraghaLibraryPane * clibrary)1392 void queue_refilter (PraghaLibraryPane *clibrary)
1393 {
1394 	if (clibrary->timeout_id) {
1395 		g_source_remove (clibrary->timeout_id);
1396 		clibrary->timeout_id = 0;
1397 	}
1398 
1399 	clibrary->timeout_id = g_timeout_add(500, (GSourceFunc)do_refilter, clibrary);
1400 }
1401 
1402 static void
simple_library_search_keyrelease_handler(GtkEntry * entry,PraghaLibraryPane * clibrary)1403 simple_library_search_keyrelease_handler (GtkEntry          *entry,
1404                                           PraghaLibraryPane *clibrary)
1405 {
1406 	const gchar *text = NULL;
1407 	gboolean has_text;
1408 
1409 	if (!pragha_preferences_get_instant_search(clibrary->preferences))
1410 		return;
1411 
1412 	if (clibrary->filter_entry != NULL) {
1413 		g_free (clibrary->filter_entry);
1414 		clibrary->filter_entry = NULL;
1415 	}
1416 
1417 	has_text = gtk_entry_get_text_length (GTK_ENTRY(entry)) > 0;
1418 
1419 	if (has_text) {
1420 		text = gtk_entry_get_text (entry);
1421 		clibrary->filter_entry = g_utf8_strdown (text, -1);
1422 
1423 		queue_refilter(clibrary);
1424 	}
1425 	else {
1426 		clear_library_search (clibrary);
1427 	}
1428 }
1429 
simple_library_search_activate_handler(GtkEntry * entry,PraghaLibraryPane * clibrary)1430 gboolean simple_library_search_activate_handler(GtkEntry *entry,
1431 						PraghaLibraryPane *clibrary)
1432 {
1433 	const gchar *text = NULL;
1434 	gboolean has_text;
1435 
1436 	has_text = gtk_entry_get_text_length (GTK_ENTRY(entry)) > 0;
1437 
1438 	if (clibrary->filter_entry != NULL) {
1439 		g_free (clibrary->filter_entry);
1440 		clibrary->filter_entry = NULL;
1441 	}
1442 
1443 	if (has_text) {
1444 		text = gtk_entry_get_text (entry);
1445 		clibrary->filter_entry = g_utf8_strdown (text, -1);
1446 
1447 		do_refilter (clibrary);
1448 	}
1449 	else {
1450 		clear_library_search (clibrary);
1451 	}
1452 
1453 	return FALSE;
1454 }
1455 
1456 void
pragha_library_expand_categories(PraghaLibraryPane * clibrary)1457 pragha_library_expand_categories(PraghaLibraryPane *clibrary)
1458 {
1459 	GtkTreeModel *filter_model, *model;
1460 	GtkTreePath *path;
1461 	GtkTreeIter iter;
1462 	gboolean valid, visible;
1463 
1464 	filter_model = gtk_tree_view_get_model(GTK_TREE_VIEW(clibrary->library_tree));
1465 	model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(filter_model));
1466 
1467 	valid = gtk_tree_model_get_iter_first (model, &iter);
1468 	while (valid) {
1469 		visible = gtk_tree_model_iter_has_child(model, &iter);
1470 		gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
1471 		                   L_VISIBILE, visible, -1);
1472 
1473 		path = gtk_tree_model_get_path(model, &iter);
1474 		gtk_tree_view_expand_row (GTK_TREE_VIEW(clibrary->library_tree), path, FALSE);
1475 		gtk_tree_path_free(path);
1476 
1477 		valid = gtk_tree_model_iter_next(model, &iter);
1478 	}
1479 }
1480 
clear_library_search(PraghaLibraryPane * clibrary)1481 void clear_library_search(PraghaLibraryPane *clibrary)
1482 {
1483 	GtkTreeModel *filter_model;
1484 
1485 	/* Remove the model of widget. */
1486 	filter_model = gtk_tree_view_get_model(GTK_TREE_VIEW(clibrary->library_tree));
1487 	g_object_ref(filter_model);
1488 	gtk_tree_view_set_model(GTK_TREE_VIEW(clibrary->library_tree), NULL);
1489 
1490 	/* Set all nodes visibles. */
1491 	gtk_tree_model_foreach(GTK_TREE_MODEL(clibrary->library_store),
1492 			       set_all_visible,
1493 			       clibrary);
1494 
1495 	/* Set the model again. */
1496 	gtk_tree_view_set_model(GTK_TREE_VIEW(clibrary->library_tree), filter_model);
1497 	g_object_unref(filter_model);
1498 
1499 	/* Expand the categories. */
1500 
1501 	pragha_library_expand_categories(clibrary);
1502 }
1503 
1504 /*
1505  * Return if you must update the library according to the changes, and the current view.
1506  */
1507 
1508 gboolean
pragha_library_need_update_view(PraghaPreferences * preferences,gint changed)1509 pragha_library_need_update_view(PraghaPreferences *preferences, gint changed)
1510 {
1511 	gboolean need_update = FALSE;
1512 
1513 	switch (pragha_preferences_get_library_style(preferences)) {
1514 		case FOLDERS:
1515 			break;
1516 		case ARTIST:
1517 			need_update = ((changed & TAG_ARTIST_CHANGED) ||
1518 			               (changed & TAG_TITLE_CHANGED));
1519 
1520 			break;
1521 		case ALBUM:
1522 			need_update = ((changed & TAG_ALBUM_CHANGED) ||
1523 			               (pragha_preferences_get_sort_by_year(preferences) && (changed & TAG_YEAR_CHANGED)) ||
1524 			               (changed & TAG_TITLE_CHANGED));
1525 			break;
1526 		case GENRE:
1527 			need_update = ((changed & TAG_GENRE_CHANGED) ||
1528 			               (changed & TAG_TITLE_CHANGED));
1529 			break;
1530 		case ARTIST_ALBUM:
1531 			need_update = ((changed & TAG_ARTIST_CHANGED) ||
1532 			               (changed & TAG_ALBUM_CHANGED) ||
1533 			               (pragha_preferences_get_sort_by_year(preferences) && (changed & TAG_YEAR_CHANGED)) ||
1534 			               (changed & TAG_TITLE_CHANGED));
1535 			break;
1536 		case GENRE_ARTIST:
1537 			need_update = ((changed & TAG_GENRE_CHANGED) ||
1538 			               (changed & TAG_ARTIST_CHANGED) ||
1539 			               (changed & TAG_TITLE_CHANGED));
1540 			break;
1541 		case GENRE_ALBUM:
1542 			need_update = ((changed & TAG_GENRE_CHANGED) ||
1543 			               (changed & TAG_ALBUM_CHANGED) ||
1544 			               (pragha_preferences_get_sort_by_year(preferences) && (changed & TAG_YEAR_CHANGED)) ||
1545 			               (changed & TAG_TITLE_CHANGED));
1546 			break;
1547 		case GENRE_ARTIST_ALBUM:
1548 			need_update = ((changed & TAG_GENRE_CHANGED) ||
1549 			               (changed & TAG_ARTIST_CHANGED) ||
1550 			               (changed & TAG_ALBUM_CHANGED) ||
1551 			               (pragha_preferences_get_sort_by_year(preferences) && (changed & TAG_YEAR_CHANGED)) ||
1552 			               (changed & TAG_TITLE_CHANGED));
1553 			break;
1554 		default:
1555 			break;
1556 	}
1557 
1558 	return need_update;
1559 }
1560 
1561 gboolean
pragha_library_need_update(PraghaLibraryPane * clibrary,gint changed)1562 pragha_library_need_update(PraghaLibraryPane *clibrary, gint changed)
1563 {
1564 	return pragha_library_need_update_view(clibrary->preferences, changed);
1565 }
1566 
1567 /********************************/
1568 /* Library view order selection */
1569 /********************************/
1570 
1571 static void
library_pane_update_style(PraghaLibraryPane * library)1572 library_pane_update_style (PraghaLibraryPane *library)
1573 {
1574 	g_slist_free (library->library_tree_nodes);
1575 	library->library_tree_nodes = NULL;
1576 
1577 	switch (pragha_preferences_get_library_style(library->preferences)) {
1578 		case FOLDERS:
1579 			library->library_tree_nodes =
1580 				g_slist_append(library->library_tree_nodes,
1581 					       GINT_TO_POINTER(NODE_FOLDER));
1582 			library->library_tree_nodes =
1583 				g_slist_append(library->library_tree_nodes,
1584 				              GINT_TO_POINTER(NODE_BASENAME));
1585 			gtk_label_set_text (GTK_LABEL(library->pane_title), _("Folders structure"));
1586 			break;
1587 		case ARTIST:
1588 			library->library_tree_nodes =
1589 				g_slist_append(library->library_tree_nodes,
1590 				               GINT_TO_POINTER(NODE_ARTIST));
1591 			library->library_tree_nodes =
1592 				g_slist_append(library->library_tree_nodes,
1593 				               GINT_TO_POINTER(NODE_TRACK));
1594 			gtk_label_set_text (GTK_LABEL(library->pane_title), _("Artist"));
1595 			break;
1596 		case ALBUM:
1597 			library->library_tree_nodes =
1598 				g_slist_append(library->library_tree_nodes,
1599 				               GINT_TO_POINTER(NODE_ALBUM));
1600 			library->library_tree_nodes =
1601 				g_slist_append(library->library_tree_nodes,
1602 				               GINT_TO_POINTER(NODE_TRACK));
1603 			gtk_label_set_text (GTK_LABEL(library->pane_title), _("Album"));
1604 			break;
1605 		case GENRE:
1606 			library->library_tree_nodes =
1607 				g_slist_append(library->library_tree_nodes,
1608 				               GINT_TO_POINTER(NODE_GENRE));
1609 			library->library_tree_nodes =
1610 				g_slist_append(library->library_tree_nodes,
1611 				               GINT_TO_POINTER(NODE_TRACK));
1612 			gtk_label_set_text (GTK_LABEL(library->pane_title), _("Genre"));
1613 			break;
1614 		case ARTIST_ALBUM:
1615 			library->library_tree_nodes =
1616 				g_slist_append(library->library_tree_nodes,
1617 				               GINT_TO_POINTER(NODE_ARTIST));
1618 			library->library_tree_nodes =
1619 				g_slist_append(library->library_tree_nodes,
1620 				               GINT_TO_POINTER(NODE_ALBUM));
1621 			library->library_tree_nodes =
1622 				g_slist_append(library->library_tree_nodes,
1623 				               GINT_TO_POINTER(NODE_TRACK));
1624 			gtk_label_set_text (GTK_LABEL(library->pane_title), _("Artist / Album"));
1625 			break;
1626 		case GENRE_ARTIST:
1627 			library->library_tree_nodes =
1628 				g_slist_append(library->library_tree_nodes,
1629 				               GINT_TO_POINTER(NODE_GENRE));
1630 			library->library_tree_nodes =
1631 				g_slist_append(library->library_tree_nodes,
1632 				               GINT_TO_POINTER(NODE_ARTIST));
1633 			library->library_tree_nodes =
1634 				g_slist_append(library->library_tree_nodes,
1635 				               GINT_TO_POINTER(NODE_TRACK));
1636 			gtk_label_set_text (GTK_LABEL(library->pane_title), _("Genre / Artist"));
1637 			break;
1638 		case GENRE_ALBUM:
1639 			library->library_tree_nodes =
1640 				g_slist_append(library->library_tree_nodes,
1641 				               GINT_TO_POINTER(NODE_GENRE));
1642 			library->library_tree_nodes =
1643 				g_slist_append(library->library_tree_nodes,
1644 				               GINT_TO_POINTER(NODE_ALBUM));
1645 			library->library_tree_nodes =
1646 				g_slist_append(library->library_tree_nodes,
1647 				               GINT_TO_POINTER(NODE_TRACK));
1648 			gtk_label_set_text (GTK_LABEL(library->pane_title), _("Genre / Album"));
1649 			break;
1650 		case GENRE_ARTIST_ALBUM:
1651 			library->library_tree_nodes =
1652 				g_slist_append(library->library_tree_nodes,
1653 				               GINT_TO_POINTER(NODE_GENRE));
1654 			library->library_tree_nodes =
1655 				g_slist_append(library->library_tree_nodes,
1656 				               GINT_TO_POINTER(NODE_ARTIST));
1657 			library->library_tree_nodes =
1658 				g_slist_append(library->library_tree_nodes,
1659 				               GINT_TO_POINTER(NODE_ALBUM));
1660 			library->library_tree_nodes =
1661 				g_slist_append(library->library_tree_nodes,
1662 				               GINT_TO_POINTER(NODE_TRACK));
1663 			gtk_label_set_text (GTK_LABEL(library->pane_title), _("Genre / Artist / Album"));
1664 			break;
1665 		default:
1666 			break;
1667 	}
1668 }
1669 
1670 static void
library_pane_change_style(GObject * gobject,GParamSpec * pspec,PraghaLibraryPane * library)1671 library_pane_change_style (GObject *gobject, GParamSpec *pspec, PraghaLibraryPane *library)
1672 {
1673 	library_pane_update_style (library);
1674 	library_pane_view_reload (library);
1675 }
1676 
1677 /*********************************/
1678 /* Functions to reload playlist. */
1679 /*********************************/
1680 
1681 static void
library_view_append_playlists(GtkTreeModel * model,GtkTreeIter * p_iter,PraghaLibraryPane * clibrary)1682 library_view_append_playlists(GtkTreeModel *model,
1683                               GtkTreeIter *p_iter,
1684                               PraghaLibraryPane *clibrary)
1685 {
1686 	PraghaPreparedStatement *statement;
1687 	const gchar *sql = NULL, *playlist = NULL;
1688 	GtkTreeIter iter;
1689 
1690 	sql = "SELECT name FROM PLAYLIST WHERE name != ? ORDER BY name COLLATE NOCASE DESC";
1691 	statement = pragha_database_create_statement (clibrary->cdbase, sql);
1692 	pragha_prepared_statement_bind_string (statement, 1, SAVE_PLAYLIST_STATE);
1693 
1694 	while (pragha_prepared_statement_step (statement)) {
1695 		playlist = pragha_prepared_statement_get_string(statement, 0);
1696 
1697 		library_store_prepend_node(model,
1698 		                           &iter,
1699 		                           p_iter,
1700 		                           clibrary->pixbuf_track,
1701 		                           playlist,
1702 		                           NODE_PLAYLIST,
1703 		                           0);
1704 
1705 		pragha_process_gtk_events ();
1706 	}
1707 	pragha_prepared_statement_free (statement);
1708 }
1709 
1710 static void
library_view_append_radios(GtkTreeModel * model,GtkTreeIter * p_iter,PraghaLibraryPane * clibrary)1711 library_view_append_radios(GtkTreeModel *model,
1712                            GtkTreeIter *p_iter,
1713                            PraghaLibraryPane *clibrary)
1714 {
1715 	PraghaPreparedStatement *statement;
1716 	const gchar *sql = NULL, *radio = NULL;
1717 	GtkTreeIter iter;
1718 
1719 	sql = "SELECT name FROM RADIO ORDER BY name COLLATE NOCASE DESC";
1720 	statement = pragha_database_create_statement (clibrary->cdbase, sql);
1721 	while (pragha_prepared_statement_step (statement)) {
1722 		radio = pragha_prepared_statement_get_string(statement, 0);
1723 
1724 		library_store_prepend_node(model,
1725 		                           &iter,
1726 		                           p_iter,
1727 		                           clibrary->pixbuf_track,
1728 		                           radio,
1729 		                           NODE_RADIO,
1730 		                           0);
1731 
1732 		pragha_process_gtk_events ();
1733 	}
1734 	pragha_prepared_statement_free (statement);
1735 }
1736 
1737 void
library_view_complete_folder_view(GtkTreeModel * model,GtkTreeIter * p_iter,PraghaLibraryPane * clibrary)1738 library_view_complete_folder_view(GtkTreeModel *model,
1739                                   GtkTreeIter *p_iter,
1740                                   PraghaLibraryPane *clibrary)
1741 
1742 {
1743 	PraghaPreparedStatement *statement;
1744 	const gchar *sql = NULL, *filepath = NULL;
1745 	gchar *mask = NULL;
1746 	GtkTreeIter iter, *f_iter;
1747 	GSList *list = NULL, *library_dir = NULL;
1748 
1749 	library_dir = pragha_preferences_get_library_list (clibrary->preferences);
1750 	for(list = library_dir ; list != NULL ; list=list->next) {
1751 		/*If no need to fuse folders, add headers and set p_iter */
1752 		if(!pragha_preferences_get_fuse_folders(clibrary->preferences)) {
1753 			gtk_tree_store_append(GTK_TREE_STORE(model),
1754 					      &iter,
1755 					      p_iter);
1756 			gtk_tree_store_set (GTK_TREE_STORE(model), &iter,
1757 			                    L_PIXBUF, clibrary->pixbuf_dir,
1758 			                    L_NODE_DATA, list->data,
1759 			                    L_NODE_BOLD, PANGO_WEIGHT_NORMAL,
1760 			                    L_NODE_TYPE, NODE_FOLDER,
1761 			                    L_LOCATION_ID, 0,
1762 			                    L_MACH, FALSE,
1763 			                    L_VISIBILE, TRUE,
1764 			                    -1);
1765 			f_iter = &iter;
1766 		}
1767 		else {
1768 			f_iter = p_iter;
1769 		}
1770 
1771 		sql = "SELECT name, id FROM LOCATION WHERE name LIKE ? ORDER BY name DESC";
1772 		statement = pragha_database_create_statement (clibrary->cdbase, sql);
1773 		mask = g_strconcat (list->data, "%", NULL);
1774 		pragha_prepared_statement_bind_string (statement, 1, mask);
1775 		while (pragha_prepared_statement_step (statement)) {
1776 			filepath = pragha_prepared_statement_get_string(statement, 0) + strlen(list->data) + 1;
1777 			add_folder_file(model,
1778 			                filepath,
1779 			                pragha_prepared_statement_get_int(statement, 1),
1780 			                f_iter,
1781 			                clibrary);
1782 
1783 			pragha_process_gtk_events ();
1784 		}
1785 		pragha_prepared_statement_free (statement);
1786 		g_free(mask);
1787 	}
1788 	free_str_list(library_dir);
1789 }
1790 
1791 void
library_view_complete_tags_view(GtkTreeModel * model,GtkTreeIter * p_iter,PraghaLibraryPane * clibrary)1792 library_view_complete_tags_view(GtkTreeModel *model,
1793                                 GtkTreeIter *p_iter,
1794                                 PraghaLibraryPane *clibrary)
1795 {
1796 	PraghaPreparedStatement *statement;
1797 	gchar *order_str = NULL, *sql = NULL;
1798 
1799 	/* Get order needed to sqlite query. */
1800 	switch(pragha_preferences_get_library_style(clibrary->preferences)) {
1801 		case FOLDERS:
1802 			break;
1803 		case ARTIST:
1804 			order_str = g_strdup("ARTIST.name COLLATE NOCASE DESC, TRACK.title COLLATE NOCASE DESC");
1805 			break;
1806 		case ALBUM:
1807 			if (pragha_preferences_get_sort_by_year(clibrary->preferences))
1808 				order_str = g_strdup("YEAR.year COLLATE NOCASE DESC, ALBUM.name COLLATE NOCASE DESC, TRACK.title COLLATE NOCASE DESC");
1809 			else
1810 				order_str = g_strdup("ALBUM.name COLLATE NOCASE DESC, TRACK.title COLLATE NOCASE DESC");
1811 			break;
1812 		case GENRE:
1813 			order_str = g_strdup("GENRE.name COLLATE NOCASE DESC, TRACK.title COLLATE NOCASE DESC");
1814 			break;
1815 		case ARTIST_ALBUM:
1816 			if (pragha_preferences_get_sort_by_year(clibrary->preferences))
1817 				order_str = g_strdup("ARTIST.name COLLATE NOCASE DESC, YEAR.year COLLATE NOCASE DESC, ALBUM.name COLLATE NOCASE DESC, TRACK.track_no COLLATE NOCASE DESC");
1818 			else
1819 				order_str = g_strdup("ARTIST.name COLLATE NOCASE DESC, ALBUM.name COLLATE NOCASE DESC, TRACK.track_no COLLATE NOCASE DESC");
1820 			break;
1821 		case GENRE_ARTIST:
1822 			order_str = g_strdup("GENRE.name COLLATE NOCASE DESC, ARTIST.name COLLATE NOCASE DESC, TRACK.title COLLATE NOCASE DESC");
1823 			break;
1824 		case GENRE_ALBUM:
1825 			if (pragha_preferences_get_sort_by_year(clibrary->preferences))
1826 				order_str = g_strdup("GENRE.name COLLATE NOCASE DESC, YEAR.year COLLATE NOCASE DESC, ALBUM.name COLLATE NOCASE DESC, TRACK.track_no COLLATE NOCASE DESC");
1827 			else
1828 				order_str = g_strdup("GENRE.name COLLATE NOCASE DESC, ALBUM.name COLLATE NOCASE DESC, TRACK.track_no COLLATE NOCASE DESC");
1829 			break;
1830 		case GENRE_ARTIST_ALBUM:
1831 			if (pragha_preferences_get_sort_by_year(clibrary->preferences))
1832 				order_str = g_strdup("GENRE.name COLLATE NOCASE DESC, ARTIST.name COLLATE NOCASE DESC, YEAR.year COLLATE NOCASE DESC, ALBUM.name COLLATE NOCASE DESC, TRACK.track_no COLLATE NOCASE DESC");
1833 			else
1834 				order_str = g_strdup("GENRE.name COLLATE NOCASE DESC, ARTIST.name COLLATE NOCASE DESC, ALBUM.name COLLATE NOCASE DESC, TRACK.track_no COLLATE NOCASE DESC");
1835 			break;
1836 		default:
1837 			break;
1838 	}
1839 
1840 	/* Common query for all tag based library views */
1841 	sql = g_strdup_printf("SELECT TRACK.title, ARTIST.name, YEAR.year, ALBUM.name, GENRE.name, LOCATION.name, LOCATION.id "
1842 	                        "FROM TRACK, ARTIST, YEAR, ALBUM, GENRE, LOCATION "
1843 	                        "WHERE ARTIST.id = TRACK.artist AND TRACK.year = YEAR.id AND ALBUM.id = TRACK.album AND GENRE.id = TRACK.genre AND LOCATION.id = TRACK.location "
1844 	                        "ORDER BY %s;", order_str);
1845 
1846 	statement = pragha_database_create_statement (clibrary->cdbase, sql);
1847 	while (pragha_prepared_statement_step (statement)) {
1848 		add_child_node_by_tags(model,
1849 		                       p_iter,
1850 		                       pragha_prepared_statement_get_int(statement, 6),
1851 		                       pragha_prepared_statement_get_string(statement, 5),
1852 		                       pragha_prepared_statement_get_string(statement, 4),
1853 		                       pragha_prepared_statement_get_string(statement, 3),
1854 		                       pragha_prepared_statement_get_string(statement, 2),
1855 		                       pragha_prepared_statement_get_string(statement, 1),
1856 		                       pragha_prepared_statement_get_string(statement, 0),
1857 		                       clibrary);
1858 
1859 		/* Have to give control to GTK periodically ... */
1860 		pragha_process_gtk_events ();
1861 	}
1862 	pragha_prepared_statement_free (statement);
1863 
1864 	g_free(order_str);
1865 	g_free(sql);
1866 }
1867 
1868 void
library_pane_view_reload(PraghaLibraryPane * clibrary)1869 library_pane_view_reload(PraghaLibraryPane *clibrary)
1870 {
1871 	GtkTreeModel *model, *filter_model;
1872 	GtkTreeIter iter;
1873 
1874 	clibrary->view_change = TRUE;
1875 
1876 	set_watch_cursor (GTK_WIDGET(clibrary));
1877 
1878 	filter_model = gtk_tree_view_get_model(GTK_TREE_VIEW(clibrary->library_tree));
1879 	model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(filter_model));
1880 
1881 	g_object_ref(filter_model);
1882 
1883 	gtk_widget_set_sensitive(GTK_WIDGET(GTK_WIDGET(clibrary)), FALSE);
1884 	gtk_tree_view_set_model(GTK_TREE_VIEW(clibrary->library_tree), NULL);
1885 
1886 	gtk_tree_store_clear(GTK_TREE_STORE(model));
1887 
1888 	/* Playlists.*/
1889 
1890 	gtk_tree_store_append(GTK_TREE_STORE(model),
1891 			      &iter,
1892 			      NULL);
1893 	gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
1894 			   L_PIXBUF, clibrary->pixbuf_dir,
1895 			   L_NODE_DATA, _("Playlists"),
1896 			   L_NODE_BOLD, PANGO_WEIGHT_BOLD,
1897 			   L_NODE_TYPE, NODE_CATEGORY,
1898 			   L_MACH, FALSE,
1899 			   L_VISIBILE, TRUE,
1900 			   -1);
1901 
1902 	library_view_append_playlists(model, &iter, clibrary);
1903 
1904 	/* Radios. */
1905 
1906 	gtk_tree_store_append(GTK_TREE_STORE(model),
1907 			      &iter,
1908 			      NULL);
1909 	gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
1910 			   L_PIXBUF, clibrary->pixbuf_dir,
1911 			   L_NODE_DATA, _("Radios"),
1912 			   L_NODE_BOLD, PANGO_WEIGHT_BOLD,
1913 			   L_NODE_TYPE, NODE_CATEGORY,
1914 			   L_MACH, FALSE,
1915 			   L_VISIBILE, TRUE,
1916 			   -1);
1917 
1918 	library_view_append_radios(model, &iter, clibrary);
1919 
1920 	/* Add library header */
1921 
1922 	gtk_tree_store_append(GTK_TREE_STORE(model),
1923 			      &iter,
1924 			      NULL);
1925 	gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
1926 			   L_PIXBUF, clibrary->pixbuf_dir,
1927 			   L_NODE_DATA, _("Library"),
1928 			   L_NODE_BOLD, PANGO_WEIGHT_BOLD,
1929 			   L_NODE_TYPE, NODE_CATEGORY,
1930 			   L_MACH, FALSE,
1931 			   L_VISIBILE, TRUE,
1932 			   -1);
1933 
1934 	if (pragha_preferences_get_library_style(clibrary->preferences) == FOLDERS) {
1935 		library_view_complete_folder_view(model, &iter, clibrary);
1936 	}
1937 	else {
1938 		library_view_complete_tags_view(model, &iter, clibrary);
1939 	}
1940 
1941 	/* Sensitive, set model and filter */
1942 
1943 	gtk_widget_set_sensitive(GTK_WIDGET(GTK_WIDGET(clibrary)), TRUE);
1944 
1945 	gtk_tree_view_set_model(GTK_TREE_VIEW(clibrary->library_tree), filter_model);
1946 	g_object_unref(filter_model);
1947 
1948 	if(gtk_entry_get_text_length (GTK_ENTRY(clibrary->search_entry)))
1949 		g_signal_emit_by_name (G_OBJECT (clibrary->search_entry), "activate");
1950 	else
1951 		pragha_library_expand_categories(clibrary);
1952 
1953 	remove_watch_cursor (GTK_WIDGET(clibrary));
1954 
1955 	clibrary->view_change = FALSE;
1956 }
1957 
1958 static void
update_library_playlist_changes(PraghaDatabase * database,PraghaLibraryPane * clibrary)1959 update_library_playlist_changes (PraghaDatabase *database,
1960                                  PraghaLibraryPane *clibrary)
1961 {
1962 	GtkTreeModel *model, *filter_model;
1963 	GtkTreeIter c_iter, iter;
1964 
1965 	clibrary->view_change = TRUE;
1966 
1967 	set_watch_cursor (GTK_WIDGET(clibrary));
1968 
1969 	filter_model = gtk_tree_view_get_model(GTK_TREE_VIEW(clibrary->library_tree));
1970 	model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(filter_model));
1971 
1972 	g_object_ref(filter_model);
1973 
1974 	gtk_widget_set_sensitive(GTK_WIDGET(GTK_WIDGET(clibrary)), FALSE);
1975 	gtk_tree_view_set_model(GTK_TREE_VIEW(clibrary->library_tree), NULL);
1976 
1977 	if(find_child_node(_("Playlists"), &c_iter, NULL, model)) {
1978 		while (gtk_tree_model_iter_nth_child(model, &iter, &c_iter, 0)) {
1979 			gtk_tree_store_remove(GTK_TREE_STORE(model), &iter);
1980 		}
1981 		library_view_append_playlists(model,
1982 				              &c_iter,
1983 				              clibrary);
1984 	}
1985 
1986 	if(find_child_node(_("Radios"), &c_iter, NULL, model)) {
1987 		while (gtk_tree_model_iter_nth_child(model, &iter, &c_iter, 0)) {
1988 			gtk_tree_store_remove(GTK_TREE_STORE(model), &iter);
1989 		}
1990 		library_view_append_radios(model,
1991 				           &c_iter,
1992 				           clibrary);
1993 	}
1994 
1995 	gtk_widget_set_sensitive(GTK_WIDGET(GTK_WIDGET(clibrary)), TRUE);
1996 
1997 	gtk_tree_view_set_model(GTK_TREE_VIEW(clibrary->library_tree), filter_model);
1998 	g_object_unref(filter_model);
1999 
2000 	if(gtk_entry_get_text_length (GTK_ENTRY(clibrary->search_entry)))
2001 		g_signal_emit_by_name (G_OBJECT (clibrary->search_entry), "activate");
2002 	else
2003 		pragha_library_expand_categories(clibrary);
2004 
2005 	remove_watch_cursor (GTK_WIDGET(clibrary));
2006 
2007 	clibrary->view_change = FALSE;
2008 }
2009 
2010 static void
update_library_tracks_changes(PraghaDatabase * database,PraghaLibraryPane * library)2011 update_library_tracks_changes(PraghaDatabase *database, PraghaLibraryPane *library)
2012 {
2013 	/*
2014 	 * Rework to olny update library tree!!!.
2015 	 **/
2016 	library_pane_view_reload(library);
2017 }
2018 
2019 /*************************************/
2020 /* All menu handlers of library pane */
2021 /*************************************/
2022 
2023 /*
2024  * library_pane_context_menu calbacks
2025  */
2026 
2027 static void
pragha_library_pane_expand_all_action(GtkAction * action,PraghaLibraryPane * library)2028 pragha_library_pane_expand_all_action (GtkAction *action, PraghaLibraryPane *library)
2029 {
2030 	gtk_tree_view_expand_all(GTK_TREE_VIEW(library->library_tree));
2031 }
2032 
2033 static void
pragha_library_pane_collapse_all_action(GtkAction * action,PraghaLibraryPane * library)2034 pragha_library_pane_collapse_all_action (GtkAction *action, PraghaLibraryPane *library)
2035 {
2036 	gtk_tree_view_collapse_all(GTK_TREE_VIEW(library->library_tree));
2037 }
2038 
2039 static void
pragha_library_pane_set_folders_view_action(GtkAction * action,PraghaLibraryPane * library)2040 pragha_library_pane_set_folders_view_action (GtkAction *action, PraghaLibraryPane *library)
2041 {
2042 	pragha_preferences_set_library_style(library->preferences, FOLDERS);
2043 }
2044 
2045 static void
pragha_library_pane_set_artist_view_action(GtkAction * action,PraghaLibraryPane * library)2046 pragha_library_pane_set_artist_view_action (GtkAction *action, PraghaLibraryPane *library)
2047 {
2048 	pragha_preferences_set_library_style(library->preferences, ARTIST);
2049 }
2050 
2051 static void
pragha_library_pane_set_album_view_action(GtkAction * action,PraghaLibraryPane * library)2052 pragha_library_pane_set_album_view_action (GtkAction *action, PraghaLibraryPane *library)
2053 {
2054 	pragha_preferences_set_library_style(library->preferences, ALBUM);
2055 }
2056 
2057 static void
pragha_library_pane_set_genre_view_action(GtkAction * action,PraghaLibraryPane * library)2058 pragha_library_pane_set_genre_view_action (GtkAction *action, PraghaLibraryPane *library)
2059 {
2060 	pragha_preferences_set_library_style(library->preferences, GENRE);
2061 }
2062 
2063 static void
pragha_library_pane_set_artist_album_view_action(GtkAction * action,PraghaLibraryPane * library)2064 pragha_library_pane_set_artist_album_view_action (GtkAction *action, PraghaLibraryPane *library)
2065 {
2066 	pragha_preferences_set_library_style(library->preferences, ARTIST_ALBUM);
2067 }
2068 
2069 static void
pragha_library_pane_set_genre_album_view_action(GtkAction * action,PraghaLibraryPane * library)2070 pragha_library_pane_set_genre_album_view_action (GtkAction *action, PraghaLibraryPane *library)
2071 {
2072 	pragha_preferences_set_library_style(library->preferences, GENRE_ALBUM);
2073 }
2074 
2075 static void
pragha_library_pane_set_genre_artist_action(GtkAction * action,PraghaLibraryPane * library)2076 pragha_library_pane_set_genre_artist_action (GtkAction *action, PraghaLibraryPane *library)
2077 {
2078 	pragha_preferences_set_library_style(library->preferences, GENRE_ARTIST);
2079 }
2080 
2081 static void
pragha_library_pane_set_genre_artist_album_view_action(GtkAction * action,PraghaLibraryPane * library)2082 pragha_library_pane_set_genre_artist_album_view_action (GtkAction *action, PraghaLibraryPane *library)
2083 {
2084 	pragha_preferences_set_library_style(library->preferences, GENRE_ARTIST_ALBUM);
2085 }
2086 
2087 /*
2088  * library_tree_context_menu calbacks
2089  */
2090 GList *
pragha_library_pane_get_mobj_list(PraghaLibraryPane * library)2091 pragha_library_pane_get_mobj_list (PraghaLibraryPane *library)
2092 {
2093 	GtkTreeModel *model;
2094 	GtkTreeSelection *selection;
2095 	GtkTreePath *path;
2096 	GList *mlist = NULL, *list, *i;
2097 
2098 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(library->library_tree));
2099 	list = gtk_tree_selection_get_selected_rows (selection, &model);
2100 
2101 	if (list) {
2102 		/* Add all the rows to the current playlist */
2103 
2104 		for (i = list; i != NULL; i = i->next) {
2105 			path = i->data;
2106 			mlist = append_library_row_to_mobj_list (library->cdbase, path, model, mlist);
2107 			gtk_tree_path_free (path);
2108 
2109 			/* Have to give control to GTK periodically ... */
2110 			pragha_process_gtk_events ();
2111 		}
2112 		g_list_free (list);
2113 	}
2114 
2115 	return mlist;
2116 }
2117 
2118 static void
pragha_library_pane_rename_item_action(GtkAction * action,PraghaLibraryPane * library)2119 pragha_library_pane_rename_item_action (GtkAction *action, PraghaLibraryPane *library)
2120 {
2121 	GtkTreeModel *model;
2122 	GtkTreeSelection *selection;
2123 	GtkTreePath *path;
2124 	GtkTreeIter iter;
2125 	GList *list;
2126 	gchar *playlist = NULL, *n_playlist = NULL;
2127 	gint node_type;
2128 
2129 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(library->library_tree));
2130 	list = gtk_tree_selection_get_selected_rows(selection, &model);
2131 
2132 	if (list) {
2133 		path = list->data;
2134 		if (gtk_tree_path_get_depth(path) > 1) {
2135 			gtk_tree_model_get_iter(model, &iter, path);
2136 			gtk_tree_model_get(model, &iter, L_NODE_DATA, &playlist, -1);
2137 
2138 			n_playlist = rename_playlist_dialog (playlist,
2139 			                                     gtk_widget_get_toplevel(GTK_WIDGET(library)));
2140 			if(n_playlist != NULL) {
2141 				gtk_tree_model_get(model, &iter, L_NODE_TYPE, &node_type, -1);
2142 
2143 				if(node_type == NODE_PLAYLIST)
2144 					pragha_database_update_playlist_name (library->cdbase, playlist, n_playlist);
2145 				else if (node_type == NODE_RADIO)
2146 					pragha_database_update_radio_name (library->cdbase, playlist, n_playlist);
2147 
2148 				pragha_database_change_playlists_done(library->cdbase);
2149 
2150 				g_free(n_playlist);
2151 			}
2152 			g_free(playlist);
2153 		}
2154 		gtk_tree_path_free(path);
2155 	}
2156 	g_list_free(list);
2157 }
2158 
2159 static void
pragha_library_pane_remove_item_action(GtkAction * action,PraghaLibraryPane * library)2160 pragha_library_pane_remove_item_action (GtkAction *action, PraghaLibraryPane *library)
2161 {
2162 	GtkTreeModel *model;
2163 	GtkTreeSelection *selection;
2164 	GtkTreePath *path;
2165 	GtkTreeIter iter;
2166 	GList *list, *i;
2167 	gchar *playlist;
2168 	gint node_type;
2169 	gboolean removed = FALSE;
2170 
2171 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(library->library_tree));
2172 	list = gtk_tree_selection_get_selected_rows (selection, &model);
2173 
2174 	if (list) {
2175 		/* Delete selected playlists */
2176 
2177 		for (i = list; i != NULL; i = i->next) {
2178 			path = i->data;
2179 			if (gtk_tree_path_get_depth(path) > 1) {
2180 				gtk_tree_model_get_iter(model, &iter, path);
2181 				gtk_tree_model_get(model, &iter, L_NODE_TYPE, &node_type, -1);
2182 				gtk_tree_model_get(model, &iter, L_NODE_DATA, &playlist, -1);
2183 
2184 				if (delete_existing_item_dialog(playlist, gtk_widget_get_toplevel(GTK_WIDGET(library)))) {
2185 					if(node_type == NODE_PLAYLIST) {
2186 						pragha_database_delete_playlist (library->cdbase, playlist);
2187 					}
2188 					else if (node_type == NODE_RADIO) {
2189 						pragha_database_delete_radio (library->cdbase, playlist);
2190 					}
2191 					removed = TRUE;
2192 				}
2193 				g_free (playlist);
2194 			}
2195 			gtk_tree_path_free (path);
2196 		}
2197 		g_list_free (list);
2198 	}
2199 
2200 	if (removed)
2201 		pragha_database_change_playlists_done (library->cdbase);
2202 }
2203 
2204 static void
pragha_library_pane_export_playlist_action(GtkAction * action,PraghaLibraryPane * library)2205 pragha_library_pane_export_playlist_action (GtkAction *action, PraghaLibraryPane *library)
2206 {
2207 	GtkWidget *toplevel;
2208 	GIOChannel *chan = NULL;
2209 	GtkTreeModel *model;
2210 	GtkTreeSelection *selection;
2211 	GtkTreePath *path;
2212 	GtkTreeIter iter;
2213 	GList *list, *i;
2214 	GError *err = NULL;
2215 	gint cnt;
2216 	gchar *filename = NULL, *playlist = NULL, *playlistpath = NULL;
2217 	gint node_type;
2218 
2219 	model = gtk_tree_view_get_model (GTK_TREE_VIEW(library->library_tree));
2220 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(library->library_tree));
2221 	cnt = (gtk_tree_selection_count_selected_rows(selection));
2222 
2223 	list = gtk_tree_selection_get_selected_rows(selection, NULL);
2224 	path = list->data;
2225 
2226 	/* If only is 'Playlist' node, just return, else get playlistname. */
2227 	if ((cnt == 1) && (gtk_tree_path_get_depth(path) == 1)) {
2228 		gtk_tree_path_free(path);
2229 		g_list_free(list);
2230 		return;
2231 	}
2232 	else {
2233 		gtk_tree_model_get_iter(model, &iter, path);
2234 		gtk_tree_model_get(model, &iter, L_NODE_DATA, &playlistpath, -1);
2235 
2236 		gtk_tree_model_get(model, &iter, L_NODE_TYPE, &node_type, -1);
2237 		if(node_type != NODE_PLAYLIST) {
2238 			gtk_tree_path_free(path);
2239 			g_list_free(list);
2240 			return;
2241 		}
2242 	}
2243 
2244 	toplevel = gtk_widget_get_toplevel(GTK_WIDGET(library));
2245 
2246 	filename = playlist_export_dialog_get_filename(playlistpath, toplevel);
2247 
2248 	if (!filename)
2249 		goto exit;
2250 
2251 	chan = create_m3u_playlist(filename);
2252 	if (!chan) {
2253 		g_warning("Unable to create M3U playlist file: %s", filename);
2254 		goto exit;
2255 	}
2256 
2257 	set_watch_cursor (toplevel);
2258 
2259 	list = gtk_tree_selection_get_selected_rows(selection, NULL);
2260 
2261 	if (list) {
2262 		/* Export all the playlists to the given file */
2263 
2264 		for (i=list; i != NULL; i = i->next) {
2265 			path = i->data;
2266 			if (gtk_tree_path_get_depth(path) > 1) {
2267 				gtk_tree_model_get_iter(model, &iter, path);
2268 				gtk_tree_model_get(model, &iter, L_NODE_DATA,
2269 						   &playlist, -1);
2270 				if (save_m3u_playlist(chan, playlist,
2271 						      filename, library->cdbase) < 0) {
2272 					g_warning("Unable to save M3U playlist: %s",
2273 						  filename);
2274 					g_free(playlist);
2275 					goto exit_list;
2276 				}
2277 				g_free(playlist);
2278 			}
2279 			gtk_tree_path_free(path);
2280 
2281 			/* Have to give control to GTK periodically ... */
2282 			pragha_process_gtk_events ();
2283 		}
2284 	}
2285 
2286 	if (chan) {
2287 		if (g_io_channel_shutdown(chan, TRUE, &err) != G_IO_STATUS_NORMAL) {
2288 			g_critical("Unable to save M3U playlist: %s", filename);
2289 			g_error_free(err);
2290 			err = NULL;
2291 		} else {
2292 			CDEBUG(DBG_INFO, "Saved M3U playlist: %s", filename);
2293 		}
2294 		g_io_channel_unref(chan);
2295 	}
2296 
2297 exit_list:
2298 	remove_watch_cursor (toplevel);
2299 
2300 	if (list)
2301 		g_list_free(list);
2302 exit:
2303 	g_free(playlistpath);
2304 	g_free(filename);
2305 }
2306 
2307 /*
2308  * library_tree_context_menu_xml calbacks
2309  */
2310 
2311 static void
pragha_library_pane_edit_tags_dialog_response(GtkWidget * dialog,gint response_id,PraghaLibraryPane * library)2312 pragha_library_pane_edit_tags_dialog_response (GtkWidget      *dialog,
2313                                                gint            response_id,
2314                                                PraghaLibraryPane *library)
2315 {
2316 	PraghaMusicobject *nmobj;
2317 	PraghaTagger *tagger;
2318 	GArray *loc_arr = NULL;
2319 	gint changed = 0, elem = 0, ielem;
2320 	GtkWidget  *toplevel;
2321 
2322 	toplevel = gtk_widget_get_toplevel (GTK_WIDGET(library));
2323 
2324 	if (response_id == GTK_RESPONSE_HELP) {
2325 		nmobj = pragha_tags_dialog_get_musicobject(PRAGHA_TAGS_DIALOG(dialog));
2326 		pragha_track_properties_dialog(nmobj, toplevel);
2327 		return;
2328 	}
2329 
2330 	loc_arr = g_object_get_data (G_OBJECT (dialog), "local-array");
2331 
2332 	if (response_id == GTK_RESPONSE_OK) {
2333 		changed = pragha_tags_dialog_get_changed(PRAGHA_TAGS_DIALOG(dialog));
2334 		if(!changed)
2335 			goto no_change;
2336 
2337 		nmobj = pragha_tags_dialog_get_musicobject(PRAGHA_TAGS_DIALOG(dialog));
2338 
2339 		/* Updata the db changes */
2340 		if(loc_arr) {
2341 			if (changed & TAG_TNO_CHANGED) {
2342 				if (loc_arr->len > 1) {
2343 					if (!confirm_tno_multiple_tracks(pragha_musicobject_get_track_no(nmobj), toplevel))
2344 						return;
2345 				}
2346 			}
2347 			if (changed & TAG_TITLE_CHANGED) {
2348 				if (loc_arr->len > 1) {
2349 					if (!confirm_title_multiple_tracks(pragha_musicobject_get_title(nmobj), toplevel))
2350 						return;
2351 				}
2352 			}
2353 
2354 			tagger = pragha_tagger_new();
2355 			/* Get a array of files and update it */
2356 			for(ielem = 0; ielem < loc_arr->len; ielem++) {
2357 				elem = g_array_index(loc_arr, gint, ielem);
2358 				if (G_LIKELY(elem))
2359 					pragha_tagger_add_location_id(tagger, elem);
2360 			}
2361 			pragha_tagger_set_changes(tagger, nmobj, changed);
2362 			pragha_tagger_apply_changes (tagger);
2363 			g_object_unref(tagger);
2364 		}
2365 	}
2366 
2367 no_change:
2368 	g_array_free (loc_arr, TRUE);
2369 	gtk_widget_destroy (dialog);
2370 }
2371 
2372 static void
pragha_library_pane_edit_tags_action(GtkAction * action,PraghaLibraryPane * library)2373 pragha_library_pane_edit_tags_action (GtkAction *action, PraghaLibraryPane *library)
2374 {
2375 	GtkWidget *dialog;
2376 	LibraryNodeType node_type = 0;
2377 	GtkTreeModel *model;
2378 	GtkTreeSelection *selection;
2379 	GtkTreePath *path;
2380 	GtkTreeIter iter;
2381 	GList *list, *i;
2382 	GArray *loc_arr = NULL;
2383 	gint sel, location_id;
2384 	gchar *node_data = NULL, **split_album = NULL;
2385 
2386 	PraghaMusicobject *omobj = NULL;
2387 
2388 	dialog = pragha_tags_dialog_new();
2389 
2390 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(library->library_tree));
2391 	sel = gtk_tree_selection_count_selected_rows(selection);
2392 	list = gtk_tree_selection_get_selected_rows(selection, &model);
2393 
2394 	/* Setup initial entries */
2395 
2396 	if (sel == 1) {
2397 		path = list->data;
2398 
2399 		if (!gtk_tree_model_get_iter(model, &iter, path))
2400 			goto exit;
2401 
2402 		gtk_tree_model_get(model, &iter, L_NODE_TYPE, &node_type, -1);
2403 
2404 		if (node_type == NODE_TRACK || node_type == NODE_BASENAME) {
2405 			gtk_tree_model_get(model, &iter,
2406 					   L_LOCATION_ID, &location_id, -1);
2407 
2408 			omobj = new_musicobject_from_db(library->cdbase, location_id);
2409 		}
2410 		else {
2411 			omobj = pragha_musicobject_new();
2412 			gtk_tree_model_get(model, &iter, L_NODE_DATA, &node_data, -1);
2413 
2414 			switch(node_type) {
2415 			case NODE_ARTIST:
2416 				pragha_musicobject_set_artist(omobj, node_data);
2417 				break;
2418 			case NODE_ALBUM:
2419 				if (pragha_preferences_get_sort_by_year(library->preferences)) {
2420 					split_album = g_strsplit(node_data, " - ", 2);
2421 					pragha_musicobject_set_year(omobj, atoi (split_album[0]));
2422 					pragha_musicobject_set_album(omobj, split_album[1]);
2423 				}
2424 				else {
2425 					pragha_musicobject_set_album(omobj, node_data);
2426 				}
2427 				break;
2428 			case NODE_GENRE:
2429 				pragha_musicobject_set_genre(omobj, node_data);
2430 				break;
2431 			default:
2432 				break;
2433 			}
2434 		}
2435 	}
2436 
2437 	if (omobj)
2438 		pragha_tags_dialog_set_musicobject(PRAGHA_TAGS_DIALOG(dialog), omobj);
2439 
2440 	loc_arr = g_array_new(TRUE, TRUE, sizeof(gint));
2441 	for (i=list; i != NULL; i = i->next) {
2442 		path = i->data;
2443 		/* Form an array of location ids */
2444 		get_location_ids(path, loc_arr, model, library);
2445 	}
2446 	g_object_set_data (G_OBJECT (dialog), "local-array", loc_arr);
2447 
2448 	g_signal_connect (G_OBJECT (dialog), "response",
2449 	                  G_CALLBACK (pragha_library_pane_edit_tags_dialog_response), library);
2450 
2451 	gtk_widget_show (dialog);
2452 
2453 exit:
2454 	g_free(node_data);
2455 	g_strfreev (split_album);
2456 	if (omobj)
2457 		g_object_unref (omobj);
2458 	g_list_free_full(list, (GDestroyNotify) gtk_tree_path_free);
2459 }
2460 
2461 static void
pragha_library_pane_delete_from_hdd_action(GtkAction * action,PraghaLibraryPane * library)2462 pragha_library_pane_delete_from_hdd_action (GtkAction *action, PraghaLibraryPane *library)
2463 {
2464 	GtkWidget *dialog;
2465 	GtkWidget *toggle_unlink;
2466 	GtkTreeModel *model;
2467 	GtkTreeSelection *selection;
2468 	GtkTreePath *path;
2469 	GList *list, *i;
2470 	gint result;
2471 	GArray *loc_arr;
2472 	gboolean unlink = FALSE;
2473 
2474 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(library->library_tree));
2475 	list = gtk_tree_selection_get_selected_rows(selection, &model);
2476 
2477 	if (list) {
2478 		dialog = gtk_message_dialog_new (GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(library))),
2479 		                                 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2480 		                                 GTK_MESSAGE_QUESTION,
2481 		                                 GTK_BUTTONS_YES_NO,
2482 		                                 _("Really want to move the files to trash?"));
2483 
2484 		toggle_unlink = gtk_check_button_new_with_label(_("Delete permanently instead of moving to trash"));
2485 		gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), toggle_unlink, TRUE, TRUE, 0);
2486 
2487 		gtk_widget_show_all(dialog);
2488 		result = gtk_dialog_run(GTK_DIALOG(dialog));
2489 		unlink = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle_unlink));
2490 		gtk_widget_destroy(dialog);
2491 
2492 		if(result == GTK_RESPONSE_YES){
2493 			loc_arr = g_array_new(TRUE, TRUE, sizeof(gint));
2494 
2495 			pragha_database_begin_transaction(library->cdbase);
2496 			for (i=list; i != NULL; i = i->next) {
2497 				path = i->data;
2498 				get_location_ids(path, loc_arr, model, library);
2499 				trash_or_unlink_row(loc_arr, unlink, library);
2500 
2501 				/* Have to give control to GTK periodically ... */
2502 				pragha_process_gtk_events ();
2503 			}
2504 			pragha_database_commit_transaction(library->cdbase);
2505 
2506 			g_array_free(loc_arr, TRUE);
2507 
2508 			pragha_database_flush_stale_entries (library->cdbase);
2509 			pragha_database_change_tracks_done(library->cdbase);
2510 		}
2511 
2512 		g_list_free_full(list, (GDestroyNotify) gtk_tree_path_free);
2513 	}
2514 }
2515 
2516 static void
pragha_library_pane_delete_from_db_action(GtkAction * action,PraghaLibraryPane * library)2517 pragha_library_pane_delete_from_db_action (GtkAction *action, PraghaLibraryPane *library)
2518 {
2519 	GtkWidget *dialog;
2520 	GtkTreeModel *model;
2521 	GtkTreeSelection *selection;
2522 	GtkTreePath *path;
2523 	GList *list, *i;
2524 	gint result;
2525 
2526 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(library->library_tree));
2527 	list = gtk_tree_selection_get_selected_rows(selection, &model);
2528 
2529 	if (list) {
2530 		dialog = gtk_message_dialog_new (GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(library))),
2531 		                                 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2532 		                                 GTK_MESSAGE_QUESTION,
2533 		                                 GTK_BUTTONS_YES_NO,
2534 		                                 _("Are you sure you want to delete current file from library?\n\n"
2535 		                                 "Warning: To recover we must rescan the entire library."));
2536 
2537 		result = gtk_dialog_run(GTK_DIALOG(dialog));
2538 		gtk_widget_destroy(dialog);
2539 
2540 		if( result == GTK_RESPONSE_YES ){
2541 			/* Delete all the rows */
2542 
2543 			pragha_database_begin_transaction (library->cdbase);
2544 
2545 			for (i=list; i != NULL; i = i->next) {
2546 				path = i->data;
2547 				delete_row_from_db(library->cdbase, path, model);
2548 
2549 				/* Have to give control to GTK periodically ... */
2550 				pragha_process_gtk_events ();
2551 			}
2552 
2553 			pragha_database_commit_transaction (library->cdbase);
2554 
2555 			pragha_database_flush_stale_entries (library->cdbase);
2556 			pragha_database_change_tracks_done(library->cdbase);
2557 		}
2558 
2559 		g_list_free_full(list, (GDestroyNotify) gtk_tree_path_free);
2560 	}
2561 }
2562 
2563 /**************************************/
2564 /* Construction menus of library pane */
2565 /**************************************/
2566 
2567 static GtkUIManager *
pragha_library_tree_context_menu_new(PraghaLibraryPane * library)2568 pragha_library_tree_context_menu_new (PraghaLibraryPane *library)
2569 {
2570 	GtkUIManager *context_menu = NULL;
2571 	GtkActionGroup *context_actions;
2572 	GError *error = NULL;
2573 
2574 	context_actions = gtk_action_group_new("Library Tree Context Actions");
2575 	context_menu = gtk_ui_manager_new();
2576 
2577 	gtk_action_group_set_translation_domain (context_actions, GETTEXT_PACKAGE);
2578 
2579 	if (!gtk_ui_manager_add_ui_from_string (context_menu,
2580 	                                       library_tree_context_menu_xml,
2581 	                                       -1, &error)) {
2582 		g_critical ("Unable to create library tree context menu, err : %s",
2583 		            error->message);
2584 	}
2585 
2586 	gtk_action_group_add_actions (context_actions,
2587 	                              library_tree_context_aentries,
2588 	                              G_N_ELEMENTS(library_tree_context_aentries),
2589 	                              (gpointer) library);
2590 
2591 	gtk_ui_manager_insert_action_group (context_menu, context_actions, 0);
2592 
2593 	g_object_unref (context_actions);
2594 
2595 	return context_menu;
2596 }
2597 
2598 static GtkUIManager *
pragha_library_pane_header_context_menu_new(PraghaLibraryPane * library)2599 pragha_library_pane_header_context_menu_new (PraghaLibraryPane *library)
2600 {
2601 	GtkUIManager *context_menu = NULL;
2602 	GtkActionGroup *context_actions;
2603 	GError *error = NULL;
2604 
2605 	context_actions = gtk_action_group_new("Header Library Pane Context Actions");
2606 	context_menu = gtk_ui_manager_new();
2607 
2608 	gtk_action_group_set_translation_domain (context_actions, GETTEXT_PACKAGE);
2609 
2610 	if (!gtk_ui_manager_add_ui_from_string (context_menu,
2611 	                                        library_pane_context_menu_xml,
2612 	                                        -1, &error)) {
2613 		g_critical ("(%s): Unable to create header library tree context menu, err : %s",
2614 		            __func__, error->message);
2615 	}
2616 
2617 	gtk_action_group_add_actions (context_actions,
2618 	                              library_pane_context_aentries,
2619 	                              G_N_ELEMENTS(library_pane_context_aentries),
2620 	                              (gpointer) library);
2621 	gtk_ui_manager_insert_action_group (context_menu, context_actions, 0);
2622 
2623 	g_object_unref (context_actions);
2624 
2625 	return context_menu;
2626 }
2627 
2628 /********************************/
2629 /* Construction of library pane */
2630 /********************************/
2631 
2632 static GtkTreeStore *
pragha_library_pane_store_new()2633 pragha_library_pane_store_new()
2634 {
2635 	GtkTreeStore *store;
2636 	store = gtk_tree_store_new(N_L_COLUMNS,
2637 	                           GDK_TYPE_PIXBUF, /* Pixbuf */
2638 	                           G_TYPE_STRING,   /* Node */
2639 	                           G_TYPE_INT,      /* Bold */
2640 	                           G_TYPE_INT,      /* Node type : Artist / Album / Track */
2641 	                           G_TYPE_INT,      /* Location id (valid only for Track) */
2642 	                           G_TYPE_BOOLEAN,  /* Flag to save mach when filtering */
2643 	                           G_TYPE_BOOLEAN); /* Row visibility */
2644 	return store;
2645 }
2646 
2647 static GtkWidget*
pragha_library_pane_tree_new(PraghaLibraryPane * clibrary)2648 pragha_library_pane_tree_new(PraghaLibraryPane *clibrary)
2649 {
2650 	GtkWidget *library_tree;
2651 	GtkTreeModel *library_filter_tree;
2652 	GtkCellRenderer *renderer;
2653 	GtkTreeViewColumn *column;
2654 	GtkTreeSelection *selection;
2655 
2656 	/* Create the filter model */
2657 
2658 	library_filter_tree = gtk_tree_model_filter_new(GTK_TREE_MODEL(clibrary->library_store), NULL);
2659 	gtk_tree_model_filter_set_visible_column(GTK_TREE_MODEL_FILTER(library_filter_tree),
2660 						 L_VISIBILE);
2661 	/* Create the tree view */
2662 
2663 	library_tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(library_filter_tree));
2664 	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(library_tree), FALSE);
2665 	gtk_tree_view_set_show_expanders(GTK_TREE_VIEW(library_tree), TRUE);
2666 
2667 	/* Selection mode is multiple */
2668 
2669 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(library_tree));
2670 	gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
2671 
2672 	/* Create column and cell renderers */
2673 
2674 	column = gtk_tree_view_column_new();
2675 
2676 	renderer = gtk_cell_renderer_pixbuf_new();
2677 	gtk_tree_view_column_pack_start(column, renderer, FALSE);
2678 	gtk_tree_view_column_set_attributes(column, renderer,
2679 					    "pixbuf", L_PIXBUF,
2680 					    NULL);
2681 	renderer = gtk_cell_renderer_text_new();
2682 	gtk_tree_view_column_pack_start(column, renderer, TRUE);
2683 	gtk_tree_view_column_set_attributes(column, renderer,
2684 					    "text", L_NODE_DATA,
2685 					    "weight", L_NODE_BOLD,
2686 					    NULL);
2687 	g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
2688 
2689 	gtk_tree_view_append_column(GTK_TREE_VIEW(library_tree), column);
2690 
2691 	g_object_unref(library_filter_tree);
2692 
2693 	return library_tree;
2694 }
2695 
2696 static GtkWidget*
pragha_library_pane_search_entry_new(PraghaLibraryPane * clibrary)2697 pragha_library_pane_search_entry_new(PraghaLibraryPane *clibrary)
2698 {
2699 	GtkWidget *search_entry;
2700 
2701 	search_entry = pragha_search_entry_new(clibrary->preferences);
2702 
2703 	g_signal_connect (G_OBJECT(search_entry),
2704 	                  "changed",
2705 	                  G_CALLBACK(simple_library_search_keyrelease_handler),
2706 	                  clibrary);
2707 	g_signal_connect (G_OBJECT(search_entry),
2708 	                  "activate",
2709 	                  G_CALLBACK(simple_library_search_activate_handler),
2710 	                  clibrary);
2711 
2712 	return search_entry;
2713 }
2714 
2715 static void
pragha_library_pane_create_widget(PraghaLibraryPane * library)2716 pragha_library_pane_create_widget (PraghaLibraryPane *library)
2717 {
2718 	GtkWidget *library_tree_scroll;
2719 
2720 	library_tree_scroll = gtk_scrolled_window_new(NULL, NULL);
2721 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(library_tree_scroll),
2722 	                                GTK_POLICY_AUTOMATIC,
2723 	                                GTK_POLICY_AUTOMATIC);
2724 	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(library_tree_scroll),
2725 	                                     GTK_SHADOW_IN);
2726 	gtk_container_set_border_width (GTK_CONTAINER(library_tree_scroll), 2);
2727 
2728 	/* Package all */
2729 
2730 	gtk_box_pack_start (GTK_BOX(library), library->search_entry,
2731 	                    FALSE, FALSE, 0);
2732 	gtk_box_pack_start (GTK_BOX(library), library_tree_scroll,
2733 	                    TRUE, TRUE, 0);
2734 
2735 	gtk_container_add (GTK_CONTAINER(library_tree_scroll),
2736 	                   library->library_tree);
2737 }
2738 
2739 static gint
get_library_icon_size(void)2740 get_library_icon_size (void)
2741 {
2742   gint width, height;
2743 
2744   if (gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, &height))
2745     return MAX (width, height);
2746   else
2747     return 16;
2748 }
2749 
2750 static void
pragha_library_pane_init_pixbufs(PraghaLibraryPane * librarypane)2751 pragha_library_pane_init_pixbufs(PraghaLibraryPane *librarypane)
2752 {
2753 	gchar *pix_uri = NULL;
2754 	GtkIconTheme *icontheme = gtk_icon_theme_get_default();
2755 	gint icon_size = get_library_icon_size();
2756 
2757 	pix_uri = g_build_filename (PIXMAPDIR, "artist.png", NULL);
2758 	librarypane->pixbuf_artist =
2759 		gdk_pixbuf_new_from_file_at_scale(pix_uri,
2760 		                                  icon_size, icon_size,
2761 		                                  TRUE,
2762 		                                  NULL);
2763 	if (!librarypane->pixbuf_artist)
2764 		g_warning("Unable to load artist png");
2765 	g_free (pix_uri);
2766 
2767 	librarypane->pixbuf_album =
2768 		gtk_icon_theme_load_icon(icontheme,
2769 		                         "media-optical",
2770 		                         icon_size, GTK_ICON_LOOKUP_FORCE_SIZE,
2771 		                         NULL);
2772 
2773 	if (!librarypane->pixbuf_album) {
2774 		pix_uri = g_build_filename (PIXMAPDIR, "album.png", NULL);
2775 		librarypane->pixbuf_album =
2776 			gdk_pixbuf_new_from_file_at_scale(pix_uri,
2777 			                                  icon_size, icon_size,
2778 			                                  TRUE, NULL);
2779 		g_free (pix_uri);
2780 	}
2781 	if (!librarypane->pixbuf_album)
2782 		g_warning("Unable to load album png");
2783 
2784 	librarypane->pixbuf_track =
2785 		gtk_icon_theme_load_icon(icontheme,
2786 		                         "audio-x-generic",
2787 		                         icon_size, GTK_ICON_LOOKUP_FORCE_SIZE,
2788 		                         NULL);
2789 	if (!librarypane->pixbuf_track) {
2790 		pix_uri = g_build_filename (PIXMAPDIR, "track.png", NULL);
2791 		librarypane->pixbuf_track =
2792 			gdk_pixbuf_new_from_file_at_scale(pix_uri,
2793 			                                  icon_size, icon_size,
2794 			                                  TRUE, NULL);
2795 		g_free (pix_uri);
2796 	}
2797 	if (!librarypane->pixbuf_track)
2798 		g_warning("Unable to load track png");
2799 
2800 	pix_uri = g_build_filename (PIXMAPDIR, "genre.png", NULL);
2801 	librarypane->pixbuf_genre =
2802 		gdk_pixbuf_new_from_file_at_scale(pix_uri,
2803 		                                  icon_size, icon_size,
2804 		                                  TRUE, NULL);
2805 	if (!librarypane->pixbuf_genre)
2806 		g_warning("Unable to load genre png");
2807 	g_free (pix_uri);
2808 
2809 	librarypane->pixbuf_dir =
2810 		gtk_icon_theme_load_icon(icontheme,
2811 		                         "folder-music",
2812 		                         icon_size, GTK_ICON_LOOKUP_FORCE_SIZE,
2813 		                         NULL);
2814 	if (!librarypane->pixbuf_dir)
2815 		librarypane->pixbuf_dir =
2816 			gtk_icon_theme_load_icon(icontheme,
2817 			                         "folder",
2818 			                         icon_size, GTK_ICON_LOOKUP_FORCE_SIZE,
2819 			                         NULL);
2820 	if (!librarypane->pixbuf_dir)
2821 		g_warning("Unable to load folder png");
2822 }
2823 
2824 void
pragha_library_pane_init_view(PraghaLibraryPane * clibrary)2825 pragha_library_pane_init_view (PraghaLibraryPane *clibrary)
2826 {
2827 	library_pane_update_style(clibrary);
2828 	library_pane_view_reload(clibrary);
2829 }
2830 
2831 GtkWidget *
pragha_library_pane_get_widget(PraghaLibraryPane * librarypane)2832 pragha_library_pane_get_widget(PraghaLibraryPane *librarypane)
2833 {
2834 	return GTK_WIDGET(librarypane);
2835 }
2836 
2837 GtkWidget *
pragha_library_pane_get_pane_title(PraghaLibraryPane * library)2838 pragha_library_pane_get_pane_title (PraghaLibraryPane *library)
2839 {
2840 	return library->pane_title;
2841 }
2842 
2843 GtkMenu *
pragha_library_pane_get_popup_menu(PraghaLibraryPane * library)2844 pragha_library_pane_get_popup_menu (PraghaLibraryPane *library)
2845 {
2846 	return GTK_MENU(gtk_ui_manager_get_widget(library->library_pane_context_menu, "/popup"));
2847 }
2848 
2849 
2850 GtkUIManager *
pragha_library_pane_get_pane_context_menu(PraghaLibraryPane * clibrary)2851 pragha_library_pane_get_pane_context_menu(PraghaLibraryPane *clibrary)
2852 {
2853 	return clibrary->library_pane_context_menu;
2854 }
2855 
2856 static void
pragha_library_pane_init(PraghaLibraryPane * library)2857 pragha_library_pane_init (PraghaLibraryPane *library)
2858 {
2859 	gtk_orientable_set_orientation (GTK_ORIENTABLE (library), GTK_ORIENTATION_VERTICAL);
2860 
2861 	/* Get usefuls instances */
2862 
2863 	library->cdbase = pragha_database_get ();
2864 	library->preferences = pragha_preferences_get ();
2865 
2866 	/* Create the store */
2867 
2868 	library->library_store = pragha_library_pane_store_new();
2869 
2870 	/* Create the widgets */
2871 
2872 	library->search_entry = pragha_library_pane_search_entry_new (library);
2873 	library->library_tree = pragha_library_pane_tree_new (library);
2874 	library->pane_title = gtk_label_new("");
2875 	gtk_misc_set_alignment (GTK_MISC(library->pane_title), 0, 0.5);
2876 
2877 	/* Create main widget */
2878 
2879 	pragha_library_pane_create_widget (library);
2880 
2881 	gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(library)),
2882 	                             GTK_STYLE_CLASS_SIDEBAR);
2883 
2884 	/* Create context menus */
2885 
2886 	library->library_pane_context_menu = pragha_library_pane_header_context_menu_new (library);
2887 	library->library_tree_context_menu = pragha_library_tree_context_menu_new (library);
2888 
2889 	/* Init the rest of flags */
2890 
2891 	library->filter_entry = NULL;
2892 	library->dragging = FALSE;
2893 	library->view_change = FALSE;
2894 	library->timeout_id = 0;
2895 	library->library_tree_nodes = NULL;
2896 
2897 	/* Init drag and drop */
2898 
2899 	library_pane_init_dnd (library);
2900 
2901 	/* Init pixbufs */
2902 
2903 	pragha_library_pane_init_pixbufs (library);
2904 
2905 	/* Conect signals */
2906 
2907 	g_signal_connect (G_OBJECT(library->library_tree), "row-activated",
2908 	                  G_CALLBACK(library_tree_row_activated_cb), library);
2909 	g_signal_connect (G_OBJECT(library->library_tree), "button-press-event",
2910 	                  G_CALLBACK(library_tree_button_press_cb), library);
2911 	g_signal_connect (G_OBJECT(library->library_tree), "button-release-event",
2912 	                  G_CALLBACK(library_tree_button_release_cb), library);
2913 	g_signal_connect (G_OBJECT (library->library_tree), "key-press-event",
2914 	                  G_CALLBACK(library_tree_key_press), library);
2915 
2916 	g_signal_connect (library->cdbase, "PlaylistsChanged",
2917 	                  G_CALLBACK (update_library_playlist_changes), library);
2918 	g_signal_connect (library->cdbase, "TracksChanged",
2919 	                  G_CALLBACK (update_library_tracks_changes), library);
2920 
2921 	g_signal_connect (library->preferences, "notify::library-style",
2922 	                  G_CALLBACK (library_pane_change_style), library);
2923 
2924 	gtk_widget_show_all (GTK_WIDGET(library));
2925 }
2926 
2927 static void
pragha_library_pane_finalize(GObject * object)2928 pragha_library_pane_finalize (GObject *object)
2929 {
2930 	PraghaLibraryPane *library = PRAGHA_LIBRARY_PANE (object);
2931 
2932 	if (library->pixbuf_dir)
2933 		g_object_unref (library->pixbuf_dir);
2934 	if (library->pixbuf_artist)
2935 		g_object_unref (library->pixbuf_artist);
2936 	if (library->pixbuf_album)
2937 		g_object_unref (library->pixbuf_album);
2938 	if (library->pixbuf_track)
2939 		g_object_unref (library->pixbuf_track);
2940 	if (library->pixbuf_genre)
2941 		g_object_unref (library->pixbuf_genre);
2942 
2943 	g_object_unref (library->cdbase);
2944 	g_object_unref (library->preferences);
2945 	g_object_unref (library->library_store);
2946 
2947 	g_slist_free (library->library_tree_nodes);
2948 
2949 	g_object_unref (library->library_pane_context_menu);
2950 	g_object_unref (library->library_tree_context_menu);
2951 
2952 	(*G_OBJECT_CLASS (pragha_library_pane_parent_class)->finalize) (object);
2953 }
2954 
2955 static void
pragha_library_pane_class_init(PraghaLibraryPaneClass * klass)2956 pragha_library_pane_class_init (PraghaLibraryPaneClass *klass)
2957 {
2958 	GObjectClass  *gobject_class;
2959 
2960 	gobject_class = G_OBJECT_CLASS (klass);
2961 	gobject_class->finalize = pragha_library_pane_finalize;
2962 
2963 	/*
2964 	 * Signals:
2965 	 */
2966 	signals[LIBRARY_APPEND_PLAYLIST] = g_signal_new ("library-append-playlist",
2967 	                                                 G_TYPE_FROM_CLASS (gobject_class),
2968 	                                                 G_SIGNAL_RUN_LAST,
2969 	                                                 G_STRUCT_OFFSET (PraghaLibraryPaneClass, library_append_playlist),
2970 	                                                 NULL, NULL,
2971 	                                                 g_cclosure_marshal_VOID__VOID,
2972 	                                                 G_TYPE_NONE, 0);
2973 	signals[LIBRARY_REPLACE_PLAYLIST] = g_signal_new ("library-replace-playlist",
2974 	                                                  G_TYPE_FROM_CLASS (gobject_class),
2975 	                                                  G_SIGNAL_RUN_LAST,
2976 	                                                  G_STRUCT_OFFSET (PraghaLibraryPaneClass, library_replace_playlist),
2977 	                                                  NULL, NULL,
2978 	                                                  g_cclosure_marshal_VOID__VOID,
2979 	                                                  G_TYPE_NONE, 0);
2980 	signals[LIBRARY_REPLACE_PLAYLIST_AND_PLAY] = g_signal_new ("library-replace-playlist-and-play",
2981 	                                                           G_TYPE_FROM_CLASS (gobject_class),
2982 	                                                           G_SIGNAL_RUN_LAST,
2983 	                                                           G_STRUCT_OFFSET (PraghaLibraryPaneClass, library_replace_playlist_and_play),
2984 	                                                           NULL, NULL,
2985 	                                                           g_cclosure_marshal_VOID__VOID,
2986 	                                                           G_TYPE_NONE, 0);
2987 }
2988 
2989 PraghaLibraryPane *
pragha_library_pane_new(void)2990 pragha_library_pane_new (void)
2991 {
2992 	return g_object_new (PRAGHA_TYPE_LIBRARY_PANE, NULL);
2993 }
2994