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