1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /***************************************************************************
3 * play-list.c
4 *
5 * mer mai 25 22:22:53 2005
6 * Copyright 2005 Philippe Rouquier
7 * brasero-app@wanadoo.fr
8 ****************************************************************************/
9
10 /*
11 * Brasero is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * Brasero is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Library General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to:
23 * The Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor
25 * Boston, MA 02110-1301, USA.
26 */
27
28
29 #ifdef HAVE_CONFIG_H
30 # include <config.h>
31 #endif
32
33 #include <string.h>
34
35 #include <glib.h>
36 #include <glib/gi18n-lib.h>
37 #include <glib-object.h>
38
39 #include <gio/gio.h>
40
41 #include <gtk/gtk.h>
42
43 #include <totem-pl-parser.h>
44
45 #include "brasero-misc.h"
46
47 #include "brasero-units.h"
48
49 #include "brasero-app.h"
50 #include "brasero-playlist.h"
51 #include "brasero-search-engine.h"
52 #include "brasero-utils.h"
53 #include "brasero-metadata.h"
54 #include "brasero-io.h"
55 #include "eggtreemultidnd.h"
56
57 #include "brasero-uri-container.h"
58 #include "brasero-layout-object.h"
59
60
61 struct BraseroPlaylistPrivate {
62 BraseroSearchEngine *engine;
63 int id;
64
65 GtkWidget *tree;
66 GtkWidget *button_add;
67 GtkWidget *button_remove;
68 guint activity_counter;
69
70 BraseroIOJobBase *parse_type;
71
72 gint searched:1;
73 };
74
75 enum {
76 BRASERO_PLAYLIST_DISPLAY_COL,
77 BRASERO_PLAYLIST_NB_SONGS_COL,
78 BRASERO_PLAYLIST_LEN_COL,
79 BRASERO_PLAYLIST_GENRE_COL,
80 BRASERO_PLAYLIST_URI_COL,
81 BRASERO_PLAYLIST_DSIZE_COL,
82 BRASERO_PLAYLIST_NB_COL,
83 };
84
85 enum {
86 TARGET_URIS_LIST,
87 };
88
89 static GtkTargetEntry ntables[] = {
90 {"text/uri-list", 0, TARGET_URIS_LIST}
91 };
92 static guint nb_ntables = sizeof (ntables) / sizeof (ntables[0]);
93
94 static void brasero_playlist_iface_uri_container_init (BraseroURIContainerIFace *iface);
95 static void brasero_playlist_iface_layout_object_init (BraseroLayoutObjectIFace *iface);
96
97 G_DEFINE_TYPE_WITH_CODE (BraseroPlaylist,
98 brasero_playlist,
99 GTK_TYPE_BOX,
100 G_IMPLEMENT_INTERFACE (BRASERO_TYPE_URI_CONTAINER,
101 brasero_playlist_iface_uri_container_init)
102 G_IMPLEMENT_INTERFACE (BRASERO_TYPE_LAYOUT_OBJECT,
103 brasero_playlist_iface_layout_object_init));
104
105 #define BRASERO_PLAYLIST_SPACING 6
106
107 static void
brasero_playlist_get_proportion(BraseroLayoutObject * object,gint * header,gint * center,gint * footer)108 brasero_playlist_get_proportion (BraseroLayoutObject *object,
109 gint *header,
110 gint *center,
111 gint *footer)
112 {
113 GtkRequisition requisition;
114
115 gtk_widget_get_preferred_size (gtk_widget_get_parent (BRASERO_PLAYLIST (object)->priv->button_add),
116 &requisition, NULL);
117 (*footer) = requisition.height + BRASERO_PLAYLIST_SPACING;
118 }
119
120 static void
brasero_playlist_increase_activity_counter(BraseroPlaylist * playlist)121 brasero_playlist_increase_activity_counter (BraseroPlaylist *playlist)
122 {
123 GdkWindow *window;
124
125 window = gtk_widget_get_window (GTK_WIDGET (playlist->priv->tree));
126 if (!window)
127 return;
128
129 if (playlist->priv->activity_counter == 0) {
130 GdkCursor *cursor;
131
132 cursor = gdk_cursor_new (GDK_WATCH);
133 gdk_window_set_cursor (window,
134 cursor);
135 g_object_unref (cursor);
136 }
137 playlist->priv->activity_counter++;
138 }
139
140 static void
brasero_playlist_decrease_activity_counter(BraseroPlaylist * playlist)141 brasero_playlist_decrease_activity_counter (BraseroPlaylist *playlist)
142 {
143 GdkWindow *window;
144
145 if (playlist->priv->activity_counter > 0)
146 playlist->priv->activity_counter--;
147
148 window = gtk_widget_get_window (GTK_WIDGET (playlist->priv->tree));
149 if (!window)
150 return;
151
152 if (playlist->priv->activity_counter == 0)
153 gdk_window_set_cursor (window, NULL);
154 }
155
156 static void
brasero_playlist_search_playlists_rhythmbox(BraseroPlaylist * playlist)157 brasero_playlist_search_playlists_rhythmbox (BraseroPlaylist *playlist)
158 {
159 /* RBSource *source;
160
161 manager = rb_playlist_manager_new ();
162 lists = rb_playlist_manager_get_playlists (manager);
163 */
164 }
165
166 static void
brasero_playlist_search(BraseroPlaylist * playlist)167 brasero_playlist_search (BraseroPlaylist *playlist)
168 {
169 const gchar* mimes [] = {"audio/x-ms-asx",
170 "audio/x-mpegurl",
171 "audio/x-scpls",
172 "audio/x-mp3-playlist",
173 NULL};
174
175 brasero_search_engine_new_query (playlist->priv->engine, NULL);
176 brasero_search_engine_set_query_mime (playlist->priv->engine, mimes);
177 brasero_search_engine_start_query (playlist->priv->engine);
178 brasero_playlist_increase_activity_counter (playlist);
179 }
180
181 static gboolean
brasero_playlist_try_again(BraseroPlaylist * playlist)182 brasero_playlist_try_again (BraseroPlaylist *playlist)
183 {
184 if (!brasero_search_engine_is_available (playlist->priv->engine))
185 return TRUE;
186
187 brasero_playlist_search (playlist);
188
189 playlist->priv->id = 0;
190 return FALSE;
191 }
192
193 static void
brasero_playlist_start_search(BraseroPlaylist * playlist)194 brasero_playlist_start_search (BraseroPlaylist *playlist)
195 {
196 if (!playlist->priv->engine)
197 return;
198
199 if (!brasero_search_engine_is_available (playlist->priv->engine)) {
200 /* we will retry in 10 seconds */
201 playlist->priv->id = g_timeout_add_seconds (10,
202 (GSourceFunc) brasero_playlist_try_again,
203 playlist);
204 return;
205 }
206
207 brasero_playlist_search (playlist);
208 }
209
210 static gboolean
brasero_playlist_draw_cb(GtkWidget * widget,cairo_t * cr,gpointer null_data)211 brasero_playlist_draw_cb (GtkWidget *widget,
212 cairo_t *cr,
213 gpointer null_data)
214 {
215 BraseroPlaylist *playlist = BRASERO_PLAYLIST (widget);
216
217 /* we only want to load playlists if the user is going to use them that
218 * is if they become apparent. That will avoid overhead */
219 if (!playlist->priv->searched) {
220 playlist->priv->searched = 1;
221 brasero_playlist_start_search (playlist);
222 brasero_playlist_search_playlists_rhythmbox (playlist);
223 }
224
225 return FALSE;
226 }
227
228 static gchar **
brasero_playlist_get_selected_uris_real(BraseroPlaylist * playlist)229 brasero_playlist_get_selected_uris_real (BraseroPlaylist *playlist)
230 {
231 GtkTreeSelection *selection;
232 GtkTreeModel *model;
233 GtkTreePath *path;
234 GList *rows, *iter;
235 GtkTreeIter row;
236 gchar **uris = NULL, *uri;
237 GPtrArray *array;
238 gboolean valid;
239
240 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (playlist->priv->tree));
241 rows = gtk_tree_selection_get_selected_rows (selection, &model);
242
243 if (rows == NULL)
244 return NULL;
245
246 array = g_ptr_array_sized_new (g_list_length (rows) + 1);
247 for (iter = rows; iter; iter = iter->next) {
248 path = iter->data;
249 valid = gtk_tree_model_get_iter (model, &row, path);
250 gtk_tree_path_free (path);
251
252 if (valid == FALSE)
253 continue;
254
255 /* FIXME : we must find a way to reverse the list */
256 /* check if it is a list or not */
257 if (gtk_tree_model_iter_has_child (model, &row)) {
258 GtkTreeIter child;
259
260 if (gtk_tree_model_iter_children (model, &child, &row) == FALSE)
261 continue;
262
263 do {
264 /* first check if the row is selected to prevent to put it in the list twice */
265 if (gtk_tree_selection_iter_is_selected (selection, &child) == TRUE)
266 continue;
267
268 gtk_tree_model_get (model, &child,
269 BRASERO_PLAYLIST_URI_COL, &uri,
270 -1);
271 g_ptr_array_add (array, uri);
272 } while (gtk_tree_model_iter_next (model, &child));
273
274 continue;
275 }
276
277 gtk_tree_model_get (model, &row,
278 BRASERO_PLAYLIST_URI_COL, &uri,
279 -1);
280 g_ptr_array_add (array, uri);
281 }
282
283 g_list_free (rows);
284
285 g_ptr_array_set_size (array, array->len + 1);
286 uris = (gchar **) array->pdata;
287 g_ptr_array_free (array, FALSE);
288 return uris;
289 }
290
291 static gchar **
brasero_playlist_get_selected_uris(BraseroURIContainer * container)292 brasero_playlist_get_selected_uris (BraseroURIContainer *container)
293 {
294 BraseroPlaylist *playlist;
295
296 playlist = BRASERO_PLAYLIST (container);
297 return brasero_playlist_get_selected_uris_real (playlist);
298 }
299
300 static gchar *
brasero_playlist_get_selected_uri(BraseroURIContainer * container)301 brasero_playlist_get_selected_uri (BraseroURIContainer *container)
302 {
303 BraseroPlaylist *playlist;
304 gchar **uris = NULL;
305 gchar *uri;
306
307 playlist = BRASERO_PLAYLIST (container);
308 uris = brasero_playlist_get_selected_uris_real (playlist);
309
310 if (uris) {
311 uri = g_strdup (uris [0]);
312 g_strfreev (uris);
313 return uri;
314 }
315
316 return NULL;
317 }
318
319 static void
brasero_playlist_remove_cb(GtkButton * button,BraseroPlaylist * playlist)320 brasero_playlist_remove_cb (GtkButton *button, BraseroPlaylist *playlist)
321 {
322 GtkTreeSelection *selection;
323 GtkTreeModel *model;
324 GtkTreePath *path;
325 GtkTreeIter row;
326 GList *rows, *iter;
327 gboolean valid;
328
329 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (playlist->priv->tree));
330 rows = gtk_tree_selection_get_selected_rows (selection, &model);
331
332 if (rows == NULL)
333 return;
334
335 /* we just remove the lists removing particular songs would be a nonsense */
336 /* we must reverse the list otherwise the last paths wouldn't be valid */
337 for (iter = g_list_last (rows); iter; iter = iter->prev) {
338 path = iter->data;
339 valid = gtk_tree_model_get_iter (model, &row, path);
340 gtk_tree_path_free (path);
341
342 if (valid == FALSE) /* if we remove the whole list it could happen that we try to remove twice a song */
343 continue;
344
345 if (gtk_tree_model_iter_has_child (model, &row)) {
346 GtkTreeIter child;
347
348 /* we remove the songs if it's a list */
349 gtk_tree_model_iter_children (model, &child, &row);
350 while (gtk_tree_store_remove (GTK_TREE_STORE (model), &child) == TRUE);
351 gtk_tree_store_remove (GTK_TREE_STORE (model),
352 &row);
353 }
354 }
355
356 g_list_free (rows);
357 }
358
359 static void
brasero_playlist_drag_data_get_cb(GtkTreeView * tree,GdkDragContext * drag_context,GtkSelectionData * selection_data,guint info,guint time,BraseroPlaylist * playlist)360 brasero_playlist_drag_data_get_cb (GtkTreeView *tree,
361 GdkDragContext *drag_context,
362 GtkSelectionData *selection_data,
363 guint info,
364 guint time,
365 BraseroPlaylist *playlist)
366 {
367 gchar **uris;
368
369 uris = brasero_playlist_get_selected_uris_real (playlist);
370 gtk_selection_data_set_uris (selection_data, uris);
371 g_strfreev (uris);
372 }
373
374 struct _BraseroPlaylistParseData {
375 GtkTreeRowReference *reference;
376
377 guint title:1;
378 guint quiet:1;
379 };
380 typedef struct _BraseroPlaylistParseData BraseroPlaylistParseData;
381
382 static void
brasero_playlist_dialog_error(BraseroPlaylist * playlist,const gchar * uri)383 brasero_playlist_dialog_error (BraseroPlaylist *playlist, const gchar *uri)
384 {
385 gchar *name;
386 gchar *primary;
387
388 BRASERO_GET_BASENAME_FOR_DISPLAY (uri, name);
389
390 primary = g_strdup_printf (_("Error parsing playlist \"%s\"."), name);
391 brasero_app_alert (brasero_app_get_default (),
392 primary,
393 _("An unknown error occurred"),
394 GTK_MESSAGE_ERROR);
395 g_free (primary);
396 g_free (name);
397 }
398
399 static void
brasero_playlist_parse_end(GObject * object,gboolean cancelled,gpointer callback_data)400 brasero_playlist_parse_end (GObject *object,
401 gboolean cancelled,
402 gpointer callback_data)
403 {
404 BraseroPlaylistParseData *data = callback_data;
405 BraseroPlaylist *playlist = BRASERO_PLAYLIST (object);
406
407 brasero_playlist_decrease_activity_counter (playlist);
408
409 gtk_tree_row_reference_free (data->reference);
410 g_free (data);
411 }
412
413 static void
brasero_playlist_parse_result(GObject * object,GError * error,const gchar * uri,GFileInfo * info,gpointer callback_data)414 brasero_playlist_parse_result (GObject *object,
415 GError *error,
416 const gchar *uri,
417 GFileInfo *info,
418 gpointer callback_data)
419 {
420 gint num;
421 gint64 total_length;
422 GtkTreeModel *model;
423 GtkTreePath *treepath;
424 GtkTreeIter parent, row;
425 gchar *len_string, *num_string;
426 BraseroPlaylistParseData *data = callback_data;
427 BraseroPlaylist *playlist = BRASERO_PLAYLIST (object);
428
429 model = gtk_tree_view_get_model (GTK_TREE_VIEW (playlist->priv->tree));
430 treepath = gtk_tree_row_reference_get_path (data->reference);
431 gtk_tree_model_get_iter (model, &parent, treepath);
432 gtk_tree_path_free (treepath);
433
434 if (info && g_file_info_get_attribute_boolean (info, BRASERO_IO_IS_PLAYLIST)) {
435 const gchar *playlist_title = NULL;
436
437 /* The first entry returned is always the playlist as a whole:
438 * if it was successfully parsed uri is the title if any. If not
439 * it's simply the URI */
440
441 /* this is for the playlist as a whole */
442 if (error) {
443 if (!data->quiet)
444 brasero_playlist_dialog_error (playlist, uri);
445
446 gtk_list_store_remove (GTK_LIST_STORE (model), &parent);
447 data->title = 1;
448 return;
449 }
450
451 playlist_title = g_file_info_get_attribute_string (info, BRASERO_IO_PLAYLIST_TITLE);
452 if (playlist_title)
453 gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
454 BRASERO_PLAYLIST_DISPLAY_COL, playlist_title,
455 -1);
456
457 data->title = 1;
458 return;
459 }
460
461 /* See if the song can be added */
462 if (!error && info && g_file_info_get_attribute_boolean (info, BRASERO_IO_HAS_AUDIO)) {
463 gchar *name;
464 guint64 len;
465 const gchar *title;
466 const gchar *genre;
467
468 gtk_tree_store_append (GTK_TREE_STORE (model), &row, &parent);
469
470 len = g_file_info_get_attribute_uint64 (info, BRASERO_IO_LEN);
471 title = g_file_info_get_attribute_string (info, BRASERO_IO_TITLE);
472 genre = g_file_info_get_attribute_string (info, BRASERO_IO_GENRE);
473
474 if (len > 0)
475 len_string = brasero_units_get_time_string (len, TRUE, FALSE);
476 else
477 len_string = NULL;
478
479 BRASERO_GET_BASENAME_FOR_DISPLAY (uri, name);
480 gtk_tree_store_set (GTK_TREE_STORE (model), &row,
481 BRASERO_PLAYLIST_DISPLAY_COL, title ? title : name,
482 BRASERO_PLAYLIST_LEN_COL, len_string,
483 BRASERO_PLAYLIST_GENRE_COL, genre,
484 BRASERO_PLAYLIST_URI_COL, uri,
485 BRASERO_PLAYLIST_DSIZE_COL, len,
486 -1);
487 g_free (name);
488 g_free (len_string);
489
490 if (len)
491 total_length += len;
492 }
493
494 /* update the playlist information */
495 num = gtk_tree_model_iter_n_children (model, &parent);
496 if (!num)
497 num_string = g_strdup (_("Empty"));
498 else /* Translators: %d is the number of songs */
499 num_string = g_strdup_printf (ngettext ("%d song", "%d songs", num), num);
500
501 /* get total length in time of the playlist */
502 gtk_tree_model_get (model, &parent,
503 BRASERO_PLAYLIST_DSIZE_COL, &total_length,
504 -1);
505
506 if (total_length > 0)
507 len_string = brasero_units_get_time_string (total_length, TRUE, FALSE);
508 else
509 len_string = NULL;
510
511 gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
512 BRASERO_PLAYLIST_NB_SONGS_COL, num_string,
513 BRASERO_PLAYLIST_LEN_COL, len_string,
514 BRASERO_PLAYLIST_DSIZE_COL, total_length,
515 -1);
516 g_free (len_string);
517 g_free (num_string);
518 }
519
520 static GtkTreeRowReference *
brasero_playlist_insert(BraseroPlaylist * playlist,const gchar * uri)521 brasero_playlist_insert (BraseroPlaylist *playlist, const gchar *uri)
522 {
523 gchar *name;
524 GtkTreeIter parent;
525 GtkTreeModel *model;
526 GtkTreePath *treepath;
527 GtkTreeRowReference *reference;
528
529 /* actually add it */
530 BRASERO_GET_BASENAME_FOR_DISPLAY (uri, name);
531 model = gtk_tree_view_get_model (GTK_TREE_VIEW (playlist->priv->tree));
532 gtk_tree_store_append (GTK_TREE_STORE (model), &parent, NULL);
533 gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
534 BRASERO_PLAYLIST_DISPLAY_COL, name,
535 BRASERO_PLAYLIST_URI_COL, uri,
536 BRASERO_PLAYLIST_NB_SONGS_COL, _("(loading…)"),
537 -1);
538 g_free (name);
539
540 treepath = gtk_tree_model_get_path (model, &parent);
541 reference = gtk_tree_row_reference_new (model, treepath);
542 gtk_tree_path_free (treepath);
543
544 return reference;
545 }
546
547 static void
brasero_playlist_add_uri_playlist(BraseroPlaylist * playlist,const gchar * uri,gboolean quiet)548 brasero_playlist_add_uri_playlist (BraseroPlaylist *playlist,
549 const gchar *uri,
550 gboolean quiet)
551 {
552 BraseroPlaylistParseData *data;
553
554 data = g_new0 (BraseroPlaylistParseData, 1);
555 data->reference = brasero_playlist_insert (playlist, uri);
556
557 if (!playlist->priv->parse_type)
558 playlist->priv->parse_type = brasero_io_register (G_OBJECT (playlist),
559 brasero_playlist_parse_result,
560 brasero_playlist_parse_end,
561 NULL);
562
563 brasero_io_parse_playlist (uri,
564 playlist->priv->parse_type,
565 BRASERO_IO_INFO_PERM|
566 BRASERO_IO_INFO_MIME|
567 BRASERO_IO_INFO_METADATA,
568 data);
569 brasero_playlist_increase_activity_counter (playlist);
570 }
571
572 static void
brasero_playlist_add_cb(GtkButton * button,BraseroPlaylist * playlist)573 brasero_playlist_add_cb (GtkButton *button, BraseroPlaylist *playlist)
574 {
575 GtkWidget *dialog, *toplevel;
576 gchar *uri;
577 GSList *uris, *iter;
578 gint result;
579
580 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (playlist));
581 if (!gtk_widget_is_toplevel (toplevel))
582 return;
583
584 dialog = gtk_file_chooser_dialog_new (_("Select Playlist"),
585 GTK_WINDOW (toplevel),
586 GTK_FILE_CHOOSER_ACTION_OPEN,
587 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
588 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
589 NULL);
590
591 gtk_dialog_set_default_response (GTK_DIALOG (dialog),
592 GTK_RESPONSE_ACCEPT);
593 gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), FALSE);
594 gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
595 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dialog),
596 g_get_home_dir ());
597
598 gtk_widget_show_all (dialog);
599 result = gtk_dialog_run (GTK_DIALOG (dialog));
600 if (result == GTK_RESPONSE_CANCEL) {
601 gtk_widget_destroy (dialog);
602 return;
603 }
604
605 uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
606 gtk_widget_destroy (dialog);
607
608 for (iter = uris; iter; iter = iter->next) {
609 uri = iter->data;
610 brasero_playlist_add_uri_playlist (playlist, uri, FALSE);
611 g_free (uri);
612 }
613 g_slist_free (uris);
614 }
615
616 static void
brasero_playlist_search_hit_added_cb(BraseroSearchEngine * engine,gpointer hit,BraseroPlaylist * playlist)617 brasero_playlist_search_hit_added_cb (BraseroSearchEngine *engine,
618 gpointer hit,
619 BraseroPlaylist *playlist)
620 {
621 const char *uri;
622
623 uri = brasero_search_engine_uri_from_hit (engine, hit);
624 brasero_playlist_add_uri_playlist (playlist, uri, TRUE);
625 brasero_playlist_decrease_activity_counter (playlist);
626 }
627
628 static void
brasero_playlist_search_hit_substracted_cb(BraseroSearchEngine * engine,gpointer hit,BraseroPlaylist * playlist)629 brasero_playlist_search_hit_substracted_cb (BraseroSearchEngine *engine,
630 gpointer hit,
631 BraseroPlaylist *playlist)
632 {
633 GtkTreeModel *model;
634 GtkTreeIter row;
635 const gchar *uri;
636
637 model = gtk_tree_view_get_model (GTK_TREE_VIEW (playlist->priv->tree));
638 uri = brasero_search_engine_uri_from_hit (engine, hit);
639
640 if (!gtk_tree_model_get_iter_first (model, &row))
641 return;
642
643 do {
644 char *row_uri;
645
646 gtk_tree_model_get (model, &row,
647 BRASERO_PLAYLIST_URI_COL, &row_uri,
648 -1);
649
650 if (!strcmp (row_uri, uri)) {
651 gtk_tree_store_remove (GTK_TREE_STORE (model), &row);
652 g_free (row_uri);
653 return;
654 }
655
656 g_free (row_uri);
657 } while (gtk_tree_model_iter_next (model, &row));
658 }
659
660 static void
brasero_playlist_search_finished_cb(BraseroSearchEngine * engine,BraseroPlaylist * playlist)661 brasero_playlist_search_finished_cb (BraseroSearchEngine *engine,
662 BraseroPlaylist *playlist)
663 {
664 brasero_playlist_decrease_activity_counter (playlist);
665 }
666
667 static void
brasero_playlist_search_error_cb(BraseroSearchEngine * engine,BraseroPlaylist * playlist)668 brasero_playlist_search_error_cb (BraseroSearchEngine *engine,
669 BraseroPlaylist *playlist)
670 {
671
672 }
673
674 static void
brasero_playlist_row_activated_cb(GtkTreeView * tree,GtkTreeIter * row,GtkTreeViewColumn * column,BraseroPlaylist * playlist)675 brasero_playlist_row_activated_cb (GtkTreeView *tree,
676 GtkTreeIter *row,
677 GtkTreeViewColumn *column,
678 BraseroPlaylist *playlist)
679 {
680 brasero_uri_container_uri_activated (BRASERO_URI_CONTAINER (playlist));
681 }
682
683 static void
brasero_playlist_selection_changed_cb(GtkTreeSelection * selection,BraseroPlaylist * playlist)684 brasero_playlist_selection_changed_cb (GtkTreeSelection *selection,
685 BraseroPlaylist *playlist)
686 {
687 brasero_uri_container_uri_selected (BRASERO_URI_CONTAINER (playlist));
688 }
689
690 static void
brasero_playlist_init(BraseroPlaylist * obj)691 brasero_playlist_init (BraseroPlaylist *obj)
692 {
693 GtkWidget *hbox, *scroll;
694 GtkTreeStore *store = NULL;
695 GtkCellRenderer *renderer;
696 GtkTreeViewColumn *column;
697
698 obj->priv = g_new0 (BraseroPlaylistPrivate, 1);
699 gtk_box_set_spacing (GTK_BOX (obj), BRASERO_PLAYLIST_SPACING);
700 gtk_orientable_set_orientation (GTK_ORIENTABLE (obj), GTK_ORIENTATION_VERTICAL);
701
702 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8);
703 gtk_widget_show (hbox);
704
705 obj->priv->button_add = gtk_button_new_from_stock (GTK_STOCK_ADD);
706 gtk_widget_show (obj->priv->button_add);
707 gtk_box_pack_end (GTK_BOX (hbox),
708 obj->priv->button_add,
709 FALSE,
710 FALSE,
711 0);
712 g_signal_connect (G_OBJECT (obj->priv->button_add),
713 "clicked",
714 G_CALLBACK (brasero_playlist_add_cb),
715 obj);
716
717 obj->priv->button_remove = gtk_button_new_from_stock (GTK_STOCK_REMOVE);
718 gtk_widget_show (obj->priv->button_remove);
719 gtk_box_pack_end (GTK_BOX (hbox),
720 obj->priv->button_remove,
721 FALSE,
722 FALSE,
723 0);
724 g_signal_connect (G_OBJECT (obj->priv->button_remove),
725 "clicked",
726 G_CALLBACK (brasero_playlist_remove_cb),
727 obj);
728
729 gtk_box_pack_end (GTK_BOX (obj), hbox, FALSE, FALSE, 0);
730
731 store = gtk_tree_store_new (BRASERO_PLAYLIST_NB_COL,
732 G_TYPE_STRING,
733 G_TYPE_STRING,
734 G_TYPE_STRING,
735 G_TYPE_STRING,
736 G_TYPE_STRING,
737 G_TYPE_INT64);
738
739 obj->priv->tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
740 egg_tree_multi_drag_add_drag_support (GTK_TREE_VIEW (obj->priv->tree));
741
742 gtk_tree_view_set_enable_tree_lines (GTK_TREE_VIEW (obj->priv->tree), TRUE);
743 gtk_tree_view_set_rubber_banding (GTK_TREE_VIEW (obj->priv->tree), TRUE);
744 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW (obj->priv->tree), TRUE);
745 gtk_tree_view_set_enable_search (GTK_TREE_VIEW (obj->priv->tree), TRUE);
746 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (obj->priv->tree), TRUE);
747 gtk_tree_view_set_expander_column (GTK_TREE_VIEW (obj->priv->tree),
748 BRASERO_PLAYLIST_DISPLAY_COL);
749
750 renderer = gtk_cell_renderer_text_new ();
751 column = gtk_tree_view_column_new_with_attributes (_("Playlists"),
752 renderer, "text",
753 BRASERO_PLAYLIST_DISPLAY_COL,
754 NULL);
755 gtk_tree_view_column_set_sort_column_id (column,
756 BRASERO_PLAYLIST_DISPLAY_COL);
757 gtk_tree_view_append_column (GTK_TREE_VIEW (obj->priv->tree),
758 column);
759 gtk_tree_view_column_set_expand (column, TRUE);
760
761 renderer = gtk_cell_renderer_text_new ();
762 column = gtk_tree_view_column_new_with_attributes (_("Number of Songs"),
763 renderer, "text",
764 BRASERO_PLAYLIST_NB_SONGS_COL,
765 NULL);
766 gtk_tree_view_column_set_sort_column_id (column,
767 BRASERO_PLAYLIST_NB_SONGS_COL);
768 gtk_tree_view_append_column (GTK_TREE_VIEW (obj->priv->tree),
769 column);
770
771 renderer = gtk_cell_renderer_text_new ();
772 column = gtk_tree_view_column_new_with_attributes (_("Length"),
773 renderer, "text",
774 BRASERO_PLAYLIST_LEN_COL,
775 NULL);
776 gtk_tree_view_column_set_sort_column_id (column,
777 BRASERO_PLAYLIST_LEN_COL);
778 gtk_tree_view_append_column (GTK_TREE_VIEW (obj->priv->tree),
779 column);
780
781 renderer = gtk_cell_renderer_text_new ();
782 column = gtk_tree_view_column_new_with_attributes (_("Genre"), renderer,
783 "text",
784 BRASERO_PLAYLIST_GENRE_COL,
785 NULL);
786 gtk_tree_view_column_set_sort_column_id (column,
787 BRASERO_PLAYLIST_GENRE_COL);
788 gtk_tree_view_append_column (GTK_TREE_VIEW (obj->priv->tree),
789 column);
790
791 gtk_tree_view_set_search_column (GTK_TREE_VIEW (obj->priv->tree),
792 BRASERO_PLAYLIST_DISPLAY_COL);
793 gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (obj->priv->tree),
794 GDK_BUTTON1_MASK, ntables,
795 nb_ntables,
796 GDK_ACTION_COPY |
797 GDK_ACTION_MOVE);
798
799 g_signal_connect (G_OBJECT (obj->priv->tree), "drag_data_get",
800 G_CALLBACK (brasero_playlist_drag_data_get_cb),
801 obj);
802
803 g_signal_connect (G_OBJECT (obj->priv->tree), "row_activated",
804 G_CALLBACK (brasero_playlist_row_activated_cb),
805 obj);
806
807 g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (obj->priv->tree))),
808 "changed",
809 G_CALLBACK (brasero_playlist_selection_changed_cb),
810 obj);
811
812 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (obj->priv->tree)),
813 GTK_SELECTION_MULTIPLE);
814
815 scroll = gtk_scrolled_window_new (NULL, NULL);
816 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
817 GTK_POLICY_AUTOMATIC,
818 GTK_POLICY_AUTOMATIC);
819 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll),
820 GTK_SHADOW_IN);
821
822 gtk_container_add (GTK_CONTAINER (scroll), obj->priv->tree);
823 gtk_box_pack_start (GTK_BOX (obj), scroll, TRUE, TRUE, 0);
824
825 obj->priv->engine = brasero_search_engine_new_default ();
826 if (!obj->priv->engine)
827 return;
828
829 g_signal_connect (G_OBJECT (obj->priv->engine), "hit-added",
830 G_CALLBACK (brasero_playlist_search_hit_added_cb),
831 obj);
832 g_signal_connect (G_OBJECT (obj->priv->engine), "hit-removed",
833 G_CALLBACK (brasero_playlist_search_hit_substracted_cb),
834 obj);
835 g_signal_connect (G_OBJECT (obj->priv->engine), "search-finished",
836 G_CALLBACK (brasero_playlist_search_finished_cb),
837 obj);
838 g_signal_connect (G_OBJECT (obj->priv->engine), "search-error",
839 G_CALLBACK (brasero_playlist_search_error_cb),
840 obj);
841 }
842
843 static void
brasero_playlist_destroy(GtkWidget * object)844 brasero_playlist_destroy (GtkWidget *object)
845 {
846 BraseroPlaylist *playlist = BRASERO_PLAYLIST (object);
847
848 if (playlist->priv->id) {
849 g_source_remove (playlist->priv->id);
850 playlist->priv->id = 0;
851 }
852
853 if (playlist->priv->engine) {
854 g_object_unref (playlist->priv->engine);
855 playlist->priv->engine = NULL;
856 }
857
858 /* NOTE: we must do it here since cancel could call brasero_playlist_end
859 * itself calling decrease_activity_counter. In finalize the latter will
860 * raise problems since the GdkWindow has been destroyed */
861 if (playlist->priv->parse_type) {
862 brasero_io_cancel_by_base (playlist->priv->parse_type);
863 brasero_io_job_base_free (playlist->priv->parse_type);
864 playlist->priv->parse_type = NULL;
865 }
866
867 if (GTK_WIDGET_CLASS (brasero_playlist_parent_class)->destroy)
868 GTK_WIDGET_CLASS (brasero_playlist_parent_class)->destroy (object);
869 }
870
871 static void
brasero_playlist_finalize(GObject * object)872 brasero_playlist_finalize (GObject *object)
873 {
874 BraseroPlaylist *cobj;
875
876 cobj = BRASERO_PLAYLIST (object);
877
878 g_free (cobj->priv);
879
880 G_OBJECT_CLASS (brasero_playlist_parent_class)->finalize (object);
881 }
882
883 static void
brasero_playlist_class_init(BraseroPlaylistClass * klass)884 brasero_playlist_class_init (BraseroPlaylistClass *klass)
885 {
886 GObjectClass *object_class = G_OBJECT_CLASS (klass);
887 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
888
889 object_class->finalize = brasero_playlist_finalize;
890 widget_class->destroy = brasero_playlist_destroy;
891 }
892
893 static void
brasero_playlist_iface_uri_container_init(BraseroURIContainerIFace * iface)894 brasero_playlist_iface_uri_container_init (BraseroURIContainerIFace *iface)
895 {
896 iface->get_selected_uri = brasero_playlist_get_selected_uri;
897 iface->get_selected_uris = brasero_playlist_get_selected_uris;
898 }
899
900 static void
brasero_playlist_iface_layout_object_init(BraseroLayoutObjectIFace * iface)901 brasero_playlist_iface_layout_object_init (BraseroLayoutObjectIFace *iface)
902 {
903 iface->get_proportion = brasero_playlist_get_proportion;
904 }
905
906 GtkWidget *
brasero_playlist_new()907 brasero_playlist_new ()
908 {
909 BraseroPlaylist *obj;
910
911 obj = BRASERO_PLAYLIST (g_object_new (BRASERO_TYPE_PLAYLIST, NULL));
912
913 g_signal_connect (obj,
914 "draw",
915 G_CALLBACK (brasero_playlist_draw_cb),
916 NULL);
917
918 return GTK_WIDGET (obj);
919 }
920