1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3
* arch-tag: Implementation of Internet Podcast source object
5
* Copyright (C) 2005 Renato Araujo Oliveira Filho <renato.filho@indt.org.br>
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29
#include <libxml/tree.h>
31
#include <glib/gi18n.h>
33
#include <glade/glade.h>
35
#include "rb-podcast-source.h"
37
#include "rhythmdb-query-model.h"
38
#include "rb-statusbar.h"
39
#include "rb-glade-helpers.h"
40
#include "rb-stock-icons.h"
41
#include "rb-entry-view.h"
42
#include "rb-property-view.h"
44
#include "rb-file-helpers.h"
45
#include "totem-pl-parser.h"
46
#include "rb-preferences.h"
47
#include "rb-dialog.h"
48
#include "rb-new-podcast-dialog.h"
49
#include "rb-podcast-properties-dialog.h"
50
#include "rb-feed-podcast-properties-dialog.h"
51
#include "rb-playlist-manager.h"
53
#include "rb-statusbar.h"
54
#include "eel-gconf-extensions.h"
55
#include "rb-podcast-manager.h"
56
#include "rb-cell-renderer-pixbuf.h"
57
#include "rb-cut-and-paste-code.h"
61
RB_PODCAST_QUERY_TYPE_ALL,
62
RB_PODCAST_QUERY_TYPE_ALBUM,
63
RB_PODCAST_QUERY_TYPE_SEARCH,
66
static void rb_podcast_source_class_init (RBPodcastSourceClass *klass);
68
static void rb_podcast_source_init (RBPodcastSource *source);
70
static GObject *rb_podcast_source_constructor (GType type,
71
guint n_construct_properties,
72
GObjectConstructParam *construct_properties);
74
static void rb_podcast_source_dispose (GObject *object);
76
static void rb_podcast_source_finalize (GObject *object);
78
static void rb_podcast_source_set_property (GObject *object,
83
static void rb_podcast_source_get_property (GObject *object,
88
static void rb_podcast_source_songs_show_popup_cb (RBEntryView *view,
89
RBPodcastSource *source);
92
static void rb_podcast_source_feeds_show_popup_cb (RBSimpleView *view,
93
RBPodcastSource *source);
95
static void paned_size_allocate_cb (GtkWidget *widget,
96
GtkAllocation *allocation,
97
RBPodcastSource *source);
99
static void rb_podcast_source_state_pref_changed (GConfClient *client,
102
RBPodcastSource *source);
104
static void rb_podcast_source_show_browser (RBPodcastSource *source,
107
static void rb_podcast_source_state_prefs_sync (RBPodcastSource *source);
110
static void feed_select_change_cb (RBPropertyView *propview,
112
RBPodcastSource *podcast_source);
115
static void rb_podcast_source_posts_view_sort_order_changed_cb (RBEntryView *view,
116
RBPodcastSource *source);
118
static void rb_podcast_source_download_status_changed_cb(RBPodcastManager *download,
119
RhythmDBEntry *entry,
121
RBPodcastSource *source);
123
static void rb_podcast_source_btn_file_change_cb (GtkFileChooserButton *widget,
127
static void posts_view_drag_data_received_cb (GtkWidget *widget,
130
GtkSelectionData *selection_data,
131
guint info, guint time,
132
RBPodcastSource *source);
134
static void rb_podcast_source_cmd_download_post (GtkAction *action,
135
RBPodcastSource *source);
137
static void rb_podcast_source_cmd_delete_feed (GtkAction *action,
138
RBPodcastSource *source);
140
static void rb_podcast_source_cmd_update_feed (GtkAction *action,
141
RBPodcastSource *source);
143
static void rb_podcast_source_cmd_update_all (GtkAction *action,
144
RBPodcastSource *source);
146
static void rb_podcast_source_cmd_properties_feed (GtkAction *action,
147
RBPodcastSource *source);
149
static void register_action_group (RBPodcastSource *source);
152
static gint rb_podcast_source_post_date_cell_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
153
RBPodcastSource *source);
155
static gint rb_podcast_source_post_status_cell_sort_func(RhythmDBEntry *a, RhythmDBEntry *b,
156
RBPodcastSource *source);
158
static gint rb_podcast_source_post_feed_cell_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
159
RBPodcastSource *source);
161
static void rb_podcast_source_post_status_cell_data_func(GtkTreeViewColumn *column, GtkCellRenderer *renderer,
162
GtkTreeModel *tree_model, GtkTreeIter *iter,
163
RBPodcastSource *source);
165
static void rb_podcast_source_post_date_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
166
GtkTreeModel *tree_model, GtkTreeIter *iter,
167
RBPodcastSource *source);
169
static void rb_podcast_source_post_feed_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
170
GtkTreeModel *tree_model, GtkTreeIter *iter,
171
RBPodcastSource *source);
173
static void rb_podcast_source_feed_status_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
174
GtkTreeModel *tree_model, GtkTreeIter *iter,
175
RBPodcastSource *source);
177
static void rb_podcast_source_feed_title_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
178
GtkTreeModel *tree_model, GtkTreeIter *iter,
179
RBPodcastSource *source);
181
static void rb_podcast_source_start_download_cb (RBPodcastManager *pd,
182
RhythmDBEntry *entry,
183
RBPodcastSource *source);
185
static void rb_podcast_source_finish_download_cb (RBPodcastManager *pd,
186
RhythmDBEntry *entry,
187
RBPodcastSource *source);
189
static void rb_podcast_source_feed_updates_avaliable_cb (RBPodcastManager *pd,
190
RhythmDBEntry *entry,
191
RBPodcastSource *source);
193
static void rb_podcast_source_download_process_error_cb (RBPodcastManager *pd,
195
RBPodcastSource *source);
197
static void rb_podcast_source_cb_interval_changed_cb (GtkComboBox *box, gpointer cb_data);
199
static gboolean rb_podcast_source_load_finish_cb (gpointer cb_data);
200
static RBShell *rb_podcast_source_get_shell (RBPodcastSource *source);
201
static void rb_podcast_source_entry_activated_cb (RBEntryView *view,
202
RhythmDBEntry *entry,
203
RBPodcastSource *source);
208
static char *impl_get_status (RBSource *source);
209
static const char *impl_get_browser_key (RBSource *source);
210
static GdkPixbuf *impl_get_pixbuf (RBSource *source);
211
static RBEntryView *impl_get_entry_view (RBSource *source);
212
static void impl_search (RBSource *source, const char *text);
213
static void impl_delete (RBSource *source);
214
static void impl_song_properties (RBSource *source);
215
static RBSourceEOFType impl_handle_eos (RBSource *asource);
216
static gboolean impl_show_popup (RBSource *source);
217
static void rb_podcast_source_do_query (RBPodcastSource *source, RBPodcastQueryType type);
218
static GtkWidget *impl_get_config_widget (RBSource *source);
219
static gboolean impl_receive_drag (RBSource *source,
220
GtkSelectionData *data);
224
#define CMD_PATH_SHOW_BROWSER "/commands/ShowBrowser"
225
#define CMD_PATH_CURRENT_STATION "/commands/CurrentStation"
226
#define CMD_PATH_SONG_INFO "/commands/SongInfo"
229
#define CONF_UI_PODCAST_DIR CONF_PREFIX "/ui/podcast"
230
#define CONF_UI_PODCAST_COLUMNS_SETUP CONF_PREFIX "/ui/podcast/columns_setup"
231
#define CONF_STATE_PODCAST_PREFIX CONF_PREFIX "/state/podcast"
232
#define CONF_STATE_PANED_POSITION CONF_STATE_PODCAST_PREFIX "/paned_position"
233
#define CONF_STATE_PODCAST_SORTING_POSTS CONF_STATE_PODCAST_PREFIX "/sorting_posts"
234
#define CONF_STATE_PODCAST_SORTING_FEEDS CONF_STATE_PODCAST_PREFIX "/sorting_feeds"
235
#define CONF_STATE_SHOW_BROWSER CONF_STATE_PODCAST_PREFIX "/show_browser"
236
#define CONF_STATE_PODCAST_DOWNLOAD_DIR CONF_STATE_PODCAST_PREFIX "/download_prefix"
237
#define CONF_STATE_PODCAST_DOWNLOAD_INTERVAL CONF_STATE_PODCAST_PREFIX "/download_interval"
238
#define CONF_STATE_PODCAST_DOWNLOAD_NEXT_TIME CONF_STATE_PODCAST_PREFIX "/download_next_time"
240
struct RBPodcastSourcePrivate
248
GtkWidget *config_widget;
252
GdkPixbuf *feed_subscribe_pixbuf;
253
GdkPixbuf *feed_unsubscribe_pixbuf;
254
GdkPixbuf *feed_working;
258
GtkActionGroup *action_group;
261
GList *selected_feeds;
262
RhythmDBQueryModel *cached_all_query;
263
char *cached_sorting_type;
265
gboolean initialized;
267
RhythmDBEntryType entry_type;
268
RBPodcastManager *podcast_mg;
271
static GtkActionEntry rb_podcast_source_actions [] =
273
{ "PodcastSrcDownloadPost", NULL, N_("Download Podcast _Episode"), NULL,
275
G_CALLBACK (rb_podcast_source_cmd_download_post) },
276
{ "PodcastFeedProperties", GTK_STOCK_PROPERTIES, N_("_Properties"), NULL,
277
N_("Properties Feed"),
278
G_CALLBACK (rb_podcast_source_cmd_properties_feed) },
279
{ "PodcastFeedUpdate", GTK_STOCK_REFRESH, N_("_Update Podcast Feed"), NULL,
281
G_CALLBACK (rb_podcast_source_cmd_update_feed) },
282
{ "PodcastFeedDelete", GTK_STOCK_DELETE, N_("_Delete Podcast Feed"), NULL,
284
G_CALLBACK (rb_podcast_source_cmd_delete_feed) },
285
{ "PodcastUpdateAllFeeds", GTK_STOCK_REFRESH, N_("_Update All Feeds"), NULL,
286
N_("Update all feeds"),
287
G_CALLBACK (rb_podcast_source_cmd_update_all) },
291
static const GtkTargetEntry posts_view_drag_types[] = {
292
{ "text/uri-list", 0, 0 },
293
{ "_NETSCAPE_URL", 0, 1 },
294
{ "application/rss+xml", 0, 2 },
305
G_DEFINE_TYPE (RBPodcastSource, rb_podcast_source, RB_TYPE_SOURCE)
309
rb_podcast_source_class_init (RBPodcastSourceClass *klass)
312
GObjectClass *object_class = G_OBJECT_CLASS (klass);
313
RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
315
object_class->dispose = rb_podcast_source_dispose;
316
object_class->finalize = rb_podcast_source_finalize;
317
object_class->constructor = rb_podcast_source_constructor;
319
object_class->set_property = rb_podcast_source_set_property;
320
object_class->get_property = rb_podcast_source_get_property;
322
source_class->impl_get_status = impl_get_status;
323
source_class->impl_get_browser_key = impl_get_browser_key;
324
source_class->impl_get_pixbuf = impl_get_pixbuf;
325
source_class->impl_can_search = (RBSourceFeatureFunc) rb_true_function;
326
source_class->impl_search = impl_search;
327
source_class->impl_get_config_widget = impl_get_config_widget;
328
source_class->impl_search = impl_search;
329
source_class->impl_get_entry_view = impl_get_entry_view;
330
source_class->impl_can_pause = (RBSourceFeatureFunc) rb_true_function;
331
source_class->impl_can_delete = (RBSourceFeatureFunc) rb_true_function;
332
source_class->impl_delete = impl_delete;
333
source_class->impl_song_properties = impl_song_properties;
334
source_class->impl_can_pause = (RBSourceFeatureFunc) rb_true_function;
335
source_class->impl_handle_eos = impl_handle_eos;
336
source_class->impl_have_url = (RBSourceFeatureFunc) rb_true_function;
337
source_class->impl_show_popup = impl_show_popup;
338
source_class->impl_receive_drag = impl_receive_drag;
340
g_object_class_install_property (object_class,
342
g_param_spec_uint ("entry-type",
344
"Type of the entries which should be displayed by this source",
347
RHYTHMDB_ENTRY_TYPE_PODCAST_POST,
348
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
350
g_object_class_install_property (object_class,
351
PROP_PODCAST_MANAGER,
352
g_param_spec_object ("podcast-manager",
354
"RBPodcastManager object",
355
RB_TYPE_PODCAST_MANAGER,
356
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
364
rb_podcast_source_init (RBPodcastSource *source)
366
GtkWidget *dummy = gtk_tree_view_new ();
367
source->priv = g_new0 (RBPodcastSourcePrivate, 1);
369
source->priv->selected_feeds = NULL;
370
source->priv->vbox = gtk_vbox_new (FALSE, 5);
372
gtk_container_add (GTK_CONTAINER (source), source->priv->vbox);
374
source->priv->pixbuf = gtk_widget_render_icon (dummy,
376
GTK_ICON_SIZE_LARGE_TOOLBAR,
380
source->priv->feed_subscribe_pixbuf = gtk_widget_render_icon (dummy,
385
source->priv->feed_unsubscribe_pixbuf = gtk_widget_render_icon (dummy,
390
gtk_widget_destroy (dummy);
394
rb_podcast_source_dispose (GObject *object)
396
RBPodcastSource *source;
398
rb_debug ("dispose podcast_source");
399
source = RB_PODCAST_SOURCE (object);
401
if (source->priv->disposed)
404
source->priv->disposed = TRUE;
408
rb_podcast_source_finalize (GObject *object)
410
RBPodcastSource *source;
412
g_return_if_fail (object != NULL);
413
g_return_if_fail (RB_IS_PODCAST_SOURCE (object));
415
source = RB_PODCAST_SOURCE (object);
417
g_return_if_fail (source->priv != NULL);
419
rb_debug ("finalizing podcast source");
421
g_object_unref (source->priv->podcast_mg);
423
if (source->priv->selected_feeds) {
424
g_list_foreach (source->priv->selected_feeds, (GFunc) g_free, NULL);
425
g_list_free (source->priv->selected_feeds);
428
if (source->priv->cached_all_query)
429
g_object_unref (G_OBJECT (source->priv->cached_all_query));
431
g_free (source->priv);
434
G_OBJECT_CLASS (rb_podcast_source_parent_class)->finalize (object);
438
rb_podcast_source_constructor (GType type, guint n_construct_properties,
439
GObjectConstructParam *construct_properties)
441
RBPodcastSource *source;
442
RBPodcastSourceClass *klass;
443
GtkTreeViewColumn *column;
444
GtkCellRenderer *renderer;
445
RhythmDBPropertyModel *feed_model;
450
RhythmDBQueryModel *query_model;
455
klass = RB_PODCAST_SOURCE_CLASS (g_type_class_peek (RB_TYPE_PODCAST_SOURCE));
457
source = RB_PODCAST_SOURCE (G_OBJECT_CLASS (rb_podcast_source_parent_class)->
458
constructor (type, n_construct_properties, construct_properties));
460
register_action_group (source);
461
source->priv->paned = gtk_vpaned_new ();
463
g_object_get (G_OBJECT (source), "shell", &shell, NULL);
464
g_object_get (G_OBJECT (shell), "db", &source->priv->db, NULL);
466
gtk_idle_add ((GtkFunction) rb_podcast_source_load_finish_cb, source);
468
source->priv->podcast_mg = rb_podcast_manager_new (source->priv->db);
470
g_object_unref (shell);
473
/* set up posts view */
474
source->priv->posts = rb_entry_view_new (source->priv->db, CONF_STATE_PODCAST_SORTING_POSTS,
476
g_signal_connect_object (G_OBJECT (source->priv->posts),
478
G_CALLBACK (rb_podcast_source_entry_activated_cb),
482
column = gtk_tree_view_column_new ();
483
renderer = gtk_cell_renderer_text_new();
485
gtk_tree_view_column_pack_start (column, renderer, TRUE);
487
gtk_tree_view_column_set_clickable (column, TRUE);
488
gtk_tree_view_column_set_resizable (column, TRUE);
489
gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
491
gtk_tree_view_column_set_cell_data_func (column, renderer,
492
(GtkTreeCellDataFunc) rb_podcast_source_post_date_cell_data_func,
495
rb_entry_view_append_column_custom (source->priv->posts, column,
497
(GCompareDataFunc) rb_podcast_source_post_date_cell_sort_func, NULL);
500
rb_entry_view_append_column (source->priv->posts, RB_ENTRY_VIEW_COL_TITLE);
503
column = gtk_tree_view_column_new ();
504
renderer = gtk_cell_renderer_text_new();
506
gtk_tree_view_column_pack_start (column, renderer, TRUE);
508
gtk_tree_view_column_set_clickable (column, TRUE);
509
gtk_tree_view_column_set_resizable (column, TRUE);
510
gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
512
gtk_tree_view_column_set_cell_data_func (column, renderer,
513
(GtkTreeCellDataFunc) rb_podcast_source_post_feed_cell_data_func,
516
rb_entry_view_append_column_custom (source->priv->posts, column,
518
(GCompareDataFunc) rb_podcast_source_post_feed_cell_sort_func, NULL);
521
rb_entry_view_append_column (source->priv->posts, RB_ENTRY_VIEW_COL_DURATION);
522
rb_entry_view_append_column (source->priv->posts, RB_ENTRY_VIEW_COL_RATING);
523
rb_entry_view_append_column (source->priv->posts, RB_ENTRY_VIEW_COL_PLAY_COUNT);
524
rb_entry_view_append_column (source->priv->posts, RB_ENTRY_VIEW_COL_LAST_PLAYED);
527
column = gtk_tree_view_column_new ();
528
renderer = gtk_cell_renderer_progress_new();
530
gtk_tree_view_column_pack_start (column, renderer, TRUE);
532
gtk_tree_view_column_set_clickable (column, TRUE);
533
gtk_tree_view_column_set_fixed_width (column, 80);
534
gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
536
gtk_tree_view_column_set_cell_data_func (column, renderer,
537
(GtkTreeCellDataFunc) rb_podcast_source_post_status_cell_data_func,
540
rb_entry_view_append_column_custom (source->priv->posts, column,
541
_("Status"), "Status",
542
(GCompareDataFunc) rb_podcast_source_post_status_cell_sort_func, NULL);
544
g_signal_connect_object (G_OBJECT (source->priv->posts),
545
"sort-order-changed",
546
G_CALLBACK (rb_podcast_source_posts_view_sort_order_changed_cb),
551
g_signal_connect (G_OBJECT (source->priv->podcast_mg),
553
G_CALLBACK (rb_podcast_source_download_status_changed_cb),
556
g_signal_connect_object (G_OBJECT (source->priv->podcast_mg),
558
G_CALLBACK (rb_podcast_source_download_process_error_cb),
561
g_signal_connect_object (G_OBJECT (source->priv->posts),
563
G_CALLBACK (paned_size_allocate_cb),
566
g_signal_connect_object (G_OBJECT (source->priv->posts), "show_popup",
567
G_CALLBACK (rb_podcast_source_songs_show_popup_cb), source, 0);
570
/* configure feed view */
571
query = rhythmdb_query_parse (source->priv->db,
572
RHYTHMDB_QUERY_PROP_EQUALS,
574
RHYTHMDB_ENTRY_TYPE_PODCAST_FEED,
577
query_model = rhythmdb_query_model_new_empty (source->priv->db);
579
model = GTK_TREE_MODEL (query_model);
581
rhythmdb_do_full_query_parsed (source->priv->db, model, query);
584
source->priv->feeds = rb_simple_view_new (source->priv->db, RHYTHMDB_PROP_LOCATION,
587
rb_property_view_set_selection_mode (RB_PROPERTY_VIEW (source->priv->feeds), GTK_SELECTION_MULTIPLE);
589
feed_model = rb_property_view_get_model (RB_PROPERTY_VIEW (source->priv->feeds));
590
g_object_set (G_OBJECT (feed_model), "query-model", query_model, NULL);
591
g_object_unref (G_OBJECT (feed_model));
593
rhythmdb_query_free (query);
597
column = gtk_tree_view_column_new ();
598
renderer = rb_cell_renderer_pixbuf_new ();
600
gtk_tree_view_column_pack_start (column, renderer, TRUE);
601
gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
602
gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, NULL);
603
gtk_tree_view_column_set_fixed_width (column, width + 5);
606
gtk_tree_view_column_set_cell_data_func (column,
608
(GtkTreeCellDataFunc) rb_podcast_source_feed_status_cell_data_func,
611
rb_simple_view_append_column_custom (source->priv->feeds,
612
column, " ", source);
616
column = gtk_tree_view_column_new ();
617
renderer = gtk_cell_renderer_text_new ();
619
gtk_tree_view_column_pack_start (column, renderer, TRUE);
621
gtk_tree_view_column_set_cell_data_func (column,
623
(GtkTreeCellDataFunc) rb_podcast_source_feed_title_cell_data_func,
626
rb_simple_view_append_column_custom (source->priv->feeds,
627
column, _("Feed"), source);
632
g_signal_connect_object (G_OBJECT (source->priv->feeds), "show_popup",
633
G_CALLBACK (rb_podcast_source_feeds_show_popup_cb),
636
g_signal_connect_object (G_OBJECT (source->priv->feeds),
637
"properties-selected",
638
G_CALLBACK (feed_select_change_cb),
642
g_object_ref (G_OBJECT (source->priv->feeds));
644
/* set up drag and drop */
645
g_signal_connect_object (G_OBJECT (source->priv->feeds),
646
"drag_data_received",
647
G_CALLBACK (posts_view_drag_data_received_cb),
650
gtk_drag_dest_set (GTK_WIDGET (source->priv->feeds),
651
GTK_DEST_DEFAULT_ALL,
652
posts_view_drag_types, 2,
653
GDK_ACTION_COPY | GDK_ACTION_MOVE);
655
g_signal_connect_object (G_OBJECT (source->priv->posts),
656
"drag_data_received",
657
G_CALLBACK (posts_view_drag_data_received_cb),
660
gtk_drag_dest_set (GTK_WIDGET (source->priv->posts),
661
GTK_DEST_DEFAULT_ALL,
662
posts_view_drag_types, 2,
663
GDK_ACTION_COPY | GDK_ACTION_MOVE);
665
/* set up propiets page */
667
gtk_paned_pack2 (GTK_PANED (source->priv->paned),
668
GTK_WIDGET (source->priv->posts), TRUE, FALSE);
670
gtk_box_pack_start_defaults (GTK_BOX (source->priv->vbox), source->priv->paned);
673
rb_podcast_source_state_prefs_sync (source);
676
eel_gconf_notification_add (CONF_STATE_PODCAST_PREFIX,
677
(GConfClientNotifyFunc) rb_podcast_source_state_pref_changed,
680
gtk_widget_show_all (GTK_WIDGET (source));
682
rb_podcast_source_do_query (source, RB_PODCAST_QUERY_TYPE_ALL);
685
return G_OBJECT (source);
689
rb_podcast_source_set_property (GObject *object,
694
RBPodcastSource *source = RB_PODCAST_SOURCE (object);
698
case PROP_ENTRY_TYPE:
699
source->priv->entry_type = g_value_get_uint (value);
701
case PROP_PODCAST_MANAGER:
702
source->priv->podcast_mg = g_value_get_object (value);
705
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
711
rb_podcast_source_get_property (GObject *object,
716
RBPodcastSource *source = RB_PODCAST_SOURCE (object);
720
case PROP_ENTRY_TYPE:
721
g_value_set_uint (value, source->priv->entry_type);
723
case PROP_PODCAST_MANAGER:
724
g_value_set_object (value, source->priv->podcast_mg);
727
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
733
rb_podcast_source_new (RBShell *shell)
736
source = RB_SOURCE (g_object_new (RB_TYPE_PODCAST_SOURCE,
737
"name", _("Podcasts"),
741
rb_shell_register_entry_type_for_source (shell, source,
742
RHYTHMDB_ENTRY_TYPE_PODCAST_POST);
748
impl_get_pixbuf (RBSource *asource)
750
RBPodcastSource *source = RB_PODCAST_SOURCE (asource);
752
return source->priv->pixbuf;
756
impl_search (RBSource *asource, const char *search_text)
758
RBPodcastSource *source = RB_PODCAST_SOURCE (asource);
760
if (source->priv->initialized) {
761
if (search_text == NULL && source->priv->search_text == NULL)
763
if (search_text != NULL &&
764
source->priv->search_text != NULL
765
&& !strcmp (search_text, source->priv->search_text))
769
source->priv->initialized = TRUE;
770
if (search_text != NULL && search_text[0] == '\0')
773
g_free (source->priv->search_text);
775
source->priv->search_text = g_utf8_casefold (search_text, -1);
777
source->priv->search_text = NULL;
778
rb_podcast_source_do_query (source, RB_PODCAST_QUERY_TYPE_SEARCH);
780
rb_source_notify_filter_changed (RB_SOURCE (source));
784
impl_get_entry_view (RBSource *asource)
786
RBPodcastSource *source = RB_PODCAST_SOURCE (asource);
788
return source->priv->posts;
791
static RBSourceEOFType
792
impl_handle_eos (RBSource *asource)
794
return RB_SOURCE_EOF_NEXT;
798
impl_get_status (RBSource *asource)
800
RBPodcastSource *source = RB_PODCAST_SOURCE (asource);
803
status = rhythmdb_compute_status_normal (rb_entry_view_get_num_entries (source->priv->posts),
804
rb_entry_view_get_duration (source->priv->posts),
805
rb_entry_view_get_total_size (source->priv->posts));
810
impl_get_browser_key (RBSource *asource)
812
return CONF_STATE_SHOW_BROWSER;
816
impl_delete (RBSource *asource)
818
RBPodcastSource *source = RB_PODCAST_SOURCE (asource);
826
rb_debug ("Delete episode action");
828
g_object_get (G_OBJECT (source), "shell", &shell, NULL);
829
g_object_get (G_OBJECT (shell), "window", &window, NULL);
830
g_object_unref (G_OBJECT (shell));
832
dialog = gtk_message_dialog_new (window,
833
GTK_DIALOG_DESTROY_WITH_PARENT,
836
_("Delete the podcast episode and downloaded file?"));
838
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
839
_("If you choose to delete the episode and file, "
840
"they will be permanently lost. Please note that "
841
"you can delete the episode but keep the downloaded "
842
"file by choosing to delete the episode only."));
844
gtk_window_set_title (GTK_WINDOW (dialog), "");
846
gtk_dialog_add_buttons (GTK_DIALOG (dialog),
847
_("Delete _Episode Only"),
852
button = gtk_dialog_add_button (GTK_DIALOG (dialog),
853
_("_Delete Episode And File"),
856
gtk_window_set_focus (GTK_WINDOW (dialog), button);
857
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
859
ret = gtk_dialog_run (GTK_DIALOG (dialog));
860
gtk_widget_destroy (dialog);
862
if (ret == GTK_RESPONSE_CANCEL || ret == GTK_RESPONSE_DELETE_EVENT)
865
rb_podcast_manager_set_remove_files (source->priv->podcast_mg,
866
(ret == GTK_RESPONSE_YES));
868
for (l = rb_entry_view_get_selected_entries (source->priv->posts); l != NULL;
869
l = g_list_next (l)) {
870
rhythmdb_entry_delete (source->priv->db, l->data);
871
rhythmdb_commit (source->priv->db);
876
impl_song_properties (RBSource *asource)
878
RBPodcastSource *source = RB_PODCAST_SOURCE (asource);
879
GtkWidget *dialog = rb_podcast_properties_dialog_new (source->priv->posts);
880
rb_debug ("in song properties");
882
gtk_widget_show_all (dialog);
884
rb_debug ("no selection!");
888
paned_size_allocate_cb (GtkWidget *widget,
889
GtkAllocation *allocation,
890
RBPodcastSource *source)
892
rb_debug ("paned size allocate");
893
eel_gconf_set_integer (CONF_STATE_PANED_POSITION,
894
gtk_paned_get_position (GTK_PANED (source->priv->paned)));
898
rb_podcast_source_state_prefs_sync (RBPodcastSource *source)
900
rb_debug ("syncing state");
901
gtk_paned_set_position (GTK_PANED (source->priv->paned),
902
eel_gconf_get_integer (CONF_STATE_PANED_POSITION));
903
rb_podcast_source_show_browser (source,
904
eel_gconf_get_boolean (CONF_STATE_SHOW_BROWSER));
908
rb_podcast_source_state_pref_changed (GConfClient *client,
911
RBPodcastSource *source)
913
rb_debug ("state prefs changed");
914
rb_podcast_source_state_prefs_sync (source);
918
rb_podcast_source_posts_view_sort_order_changed_cb (RBEntryView *view,
919
RBPodcastSource *source)
921
rb_debug ("sort order changed");
922
rb_entry_view_set_resorting (view);
923
rb_podcast_source_do_query (source, RB_PODCAST_QUERY_TYPE_SEARCH);
927
rb_podcast_source_download_status_changed_cb (RBPodcastManager *download,
928
RhythmDBEntry *entry,
930
RBPodcastSource *source)
932
gtk_widget_queue_draw(GTK_WIDGET(source->priv->posts));
939
rb_podcast_source_songs_show_popup_cb (RBEntryView *view,
940
RBPodcastSource *source)
942
if (G_OBJECT (source) == NULL) {
948
gulong all_status = 999;
949
RhythmDBEntry *entry = NULL;
950
GList *lst = rb_entry_view_get_selected_entries(view);
953
entry = (RhythmDBEntry *) lst->data;
954
status = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_STATUS);
955
if ((status != all_status) && (all_status != 999)) {
966
act_post = gtk_action_group_get_action (source->priv->action_group, "PodcastSrcDownloadPost");
968
if ((all_status < RHYTHMDB_PODCAST_STATUS_COMPLETE) ||
969
(all_status == RHYTHMDB_PODCAST_STATUS_WAITING)) {
970
g_object_set (G_OBJECT (act_post), "label", _("_Cancel Download") , NULL);
971
gtk_action_set_visible (act_post, TRUE);
973
else if ((all_status > RHYTHMDB_PODCAST_STATUS_COMPLETE) && (all_status != 999)) {
974
g_object_set (G_OBJECT (act_post), "label", _("_Download Post") , NULL);
975
gtk_action_set_visible (act_post, TRUE);
978
gtk_action_set_visible (act_post, FALSE);
980
_rb_source_show_popup (RB_SOURCE (source), "/PodcastViewPopup");
986
rb_podcast_source_feeds_show_popup_cb (RBSimpleView *view,
987
RBPodcastSource *source)
989
if (G_OBJECT (source) == NULL) {
993
GtkAction* act_update;
994
GtkAction* act_properties;
995
GtkAction* act_delete;
998
gulong all_status = 999;
999
RhythmDBEntry *entry = NULL;
1000
GList *lst = source->priv->selected_feeds;
1002
act_update = gtk_action_group_get_action (source->priv->action_group, "PodcastFeedUpdate");
1003
act_properties = gtk_action_group_get_action (source->priv->action_group, "PodcastFeedProperties");
1004
act_delete = gtk_action_group_get_action (source->priv->action_group, "PodcastFeedDelete");
1008
entry = rhythmdb_entry_lookup_by_location (source->priv->db,
1009
(gchar *) lst->data);
1010
status = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_STATUS);
1011
if ((status != all_status) && (all_status != 999)) {
1016
all_status = status;
1022
gtk_action_set_visible (act_properties, TRUE);
1023
gtk_action_set_visible (act_delete, TRUE);
1025
gtk_action_set_visible (act_update, FALSE);
1026
gtk_action_set_visible (act_properties, FALSE);
1027
gtk_action_set_visible (act_delete, FALSE);
1030
_rb_source_show_popup (RB_SOURCE (source), "/PodcastFeedViewPopup");
1037
feed_select_change_cb (RBPropertyView *propview, GList *feeds,
1038
RBPodcastSource *source)
1040
if (source->priv->selected_feeds) {
1041
g_list_foreach (source->priv->selected_feeds, (GFunc) g_free, NULL);
1042
g_list_free (source->priv->selected_feeds);
1045
source->priv->selected_feeds = feeds;
1047
rb_podcast_source_do_query (source, RB_PODCAST_QUERY_TYPE_ALBUM);
1049
rb_source_notify_filter_changed (RB_SOURCE (source));
1055
rb_podcast_source_show_browser (RBPodcastSource *source,
1059
GtkWidget *feedswidget = GTK_WIDGET (source->priv->feeds);
1062
gtk_paned_pack1 (GTK_PANED (source->priv->paned), feedswidget, FALSE, FALSE);
1063
gtk_widget_show_all (feedswidget);
1064
} else if (show == FALSE) {
1065
GList *children = gtk_container_get_children (GTK_CONTAINER (source->priv->paned));
1066
gtk_widget_hide (feedswidget);
1067
if (g_list_find (children, feedswidget))
1068
gtk_container_remove (GTK_CONTAINER (source->priv->paned), feedswidget);
1070
g_list_free (children);
1076
construct_query_from_selection (RBPodcastSource *source)
1079
RhythmDBEntryType entry_type;
1081
g_object_get (G_OBJECT (source), "entry-type", &entry_type, NULL);
1082
query = rhythmdb_query_parse (source->priv->db,
1083
RHYTHMDB_QUERY_PROP_EQUALS,
1086
RHYTHMDB_QUERY_END);
1088
if (source->priv->search_text) {
1089
GPtrArray *subquery = rhythmdb_query_parse (source->priv->db,
1090
RHYTHMDB_QUERY_PROP_LIKE,
1091
RHYTHMDB_PROP_ALBUM_FOLDED,
1092
source->priv->search_text,
1093
RHYTHMDB_QUERY_DISJUNCTION,
1094
RHYTHMDB_QUERY_PROP_LIKE,
1095
RHYTHMDB_PROP_TITLE_FOLDED,
1096
source->priv->search_text,
1097
RHYTHMDB_QUERY_END);
1098
rhythmdb_query_append (source->priv->db,
1100
RHYTHMDB_QUERY_SUBQUERY,
1102
RHYTHMDB_QUERY_END);
1105
if (source->priv->selected_feeds) {
1106
GPtrArray *subquery = g_ptr_array_new ();
1109
for (l = source->priv->selected_feeds; l != NULL; l = g_list_next (l)) {
1110
rb_debug ("equals loop");
1111
rhythmdb_query_append (source->priv->db,
1113
RHYTHMDB_QUERY_PROP_EQUALS,
1114
RHYTHMDB_PROP_SUBTITLE,
1116
RHYTHMDB_QUERY_END);
1118
rhythmdb_query_append (source->priv->db, subquery,
1119
RHYTHMDB_QUERY_DISJUNCTION);
1122
rhythmdb_query_append (source->priv->db, query,
1123
RHYTHMDB_QUERY_SUBQUERY, subquery,
1124
RHYTHMDB_QUERY_END);
1131
rb_podcast_source_do_query (RBPodcastSource *source, RBPodcastQueryType qtype)
1133
RhythmDBQueryModel *query_model;
1135
gboolean is_all_query, sorting_matches;
1136
const char *current_sorting_type;
1138
rb_debug ("select entry filter");
1140
is_all_query = ((qtype == RB_PODCAST_QUERY_TYPE_ALL) ||
1141
((source->priv->selected_feeds == NULL) &&
1142
(source->priv->search_text == NULL)));
1143
current_sorting_type = rb_entry_view_get_sorting_type (source->priv->posts);
1144
sorting_matches = source->priv->cached_sorting_type
1145
&& !strcmp (source->priv->cached_sorting_type, current_sorting_type);
1146
rb_debug ("current sorting: %s, match: %s", current_sorting_type, sorting_matches ? "TRUE" : "FALSE" );
1149
if (sorting_matches) {
1150
rb_debug ("using cached query");
1151
rb_entry_view_set_model (source->priv->posts, RHYTHMDB_QUERY_MODEL (source->priv->cached_all_query));
1153
} else if (source->priv->cached_all_query) {
1154
g_object_unref (source->priv->cached_all_query);
1155
source->priv->cached_all_query = NULL;
1156
g_free (source->priv->cached_sorting_type);
1160
query_model = rhythmdb_query_model_new_empty (source->priv->db);
1161
if (source->priv->cached_all_query == NULL) {
1162
rb_debug ("caching new query");
1163
source->priv->cached_all_query = query_model;
1164
source->priv->cached_sorting_type = g_strdup (current_sorting_type);
1167
rb_debug ("setting empty model");
1168
rb_entry_view_set_model (source->priv->posts, query_model);
1170
rb_debug ("doing query");
1171
query = construct_query_from_selection (source);
1172
rhythmdb_do_full_query_async_parsed (source->priv->db,
1173
GTK_TREE_MODEL (query_model),
1176
rhythmdb_query_free (query);
1179
g_object_unref (G_OBJECT (query_model));
1183
impl_show_popup (RBSource *source)
1185
_rb_source_show_popup (RB_SOURCE (source), "/PodcastSourcePopup");
1191
impl_get_config_widget (RBSource *asource)
1193
RBPodcastSource *source = RB_PODCAST_SOURCE (asource);
1194
GtkWidget *cb_update_interval;
1195
GtkWidget *btn_file;
1199
if (source->priv->config_widget)
1200
return source->priv->config_widget;
1203
xml = rb_glade_xml_new ("podcast-prefs.glade", "podcast_vbox", source);
1204
source->priv->config_widget = glade_xml_get_widget (xml, "podcast_vbox");
1206
btn_file = glade_xml_get_widget (xml, "location_chooser");
1207
download_dir = rb_podcast_manager_get_podcast_dir (source->priv->podcast_mg);
1208
gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (btn_file),
1210
g_free (download_dir);
1212
g_signal_connect (btn_file,
1213
"selection-changed",
1214
G_CALLBACK (rb_podcast_source_btn_file_change_cb),
1215
CONF_STATE_PODCAST_DOWNLOAD_DIR);
1217
cb_update_interval = glade_xml_get_widget (xml, "cb_update_interval");
1218
gtk_combo_box_set_active (GTK_COMBO_BOX (cb_update_interval),
1219
eel_gconf_get_integer (CONF_STATE_PODCAST_DOWNLOAD_INTERVAL));
1220
g_signal_connect (cb_update_interval,
1222
G_CALLBACK (rb_podcast_source_cb_interval_changed_cb),
1225
return source->priv->config_widget;
1229
rb_podcast_source_btn_file_change_cb (GtkFileChooserButton *widget, const char *key)
1231
char *uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (widget));
1233
eel_gconf_set_string (key, gnome_vfs_get_local_path_from_uri (uri));
1239
posts_view_drag_data_received_cb (GtkWidget *widget,
1242
GtkSelectionData *selection_data,
1243
guint info, guint time,
1244
RBPodcastSource *source)
1246
impl_receive_drag (RB_SOURCE (source), selection_data);
1250
rb_podcast_source_add_feed (RBPodcastSource *source, const char *uri)
1252
rb_podcast_manager_subscribe_feed (source->priv->podcast_mg, uri);
1257
register_action_group (RBPodcastSource *source)
1259
GtkUIManager *uimanager;
1260
GList *actiongroups;
1263
g_object_get (G_OBJECT (source), "ui-manager", &uimanager, NULL);
1264
actiongroups = gtk_ui_manager_get_action_groups (uimanager);
1266
/* Don't create the action group if it's already registered */
1267
for (group = actiongroups; group != NULL; group = group->next) {
1269
name = gtk_action_group_get_name (GTK_ACTION_GROUP (group->data));
1270
if (strcmp (name, "PodcastActions") == 0) {
1271
g_object_unref (G_OBJECT (uimanager));
1272
source->priv->action_group = GTK_ACTION_GROUP (group->data);
1277
source->priv->action_group = gtk_action_group_new ("PodcastActions");
1278
gtk_action_group_set_translation_domain (source->priv->action_group,
1280
gtk_action_group_add_actions (source->priv->action_group,
1281
rb_podcast_source_actions,
1282
G_N_ELEMENTS (rb_podcast_source_actions),
1284
gtk_ui_manager_insert_action_group (uimanager,
1285
source->priv->action_group, 0);
1286
g_object_unref (G_OBJECT (uimanager));
1291
rb_podcast_source_cmd_download_post (GtkAction *action,
1292
RBPodcastSource *source)
1299
rb_debug ("Add to download action");
1300
posts = source->priv->posts;
1302
lst = rb_entry_view_get_selected_entries (posts);
1303
g_value_init (&val, G_TYPE_ULONG);
1305
while (lst != NULL) {
1306
RhythmDBEntry *entry = (RhythmDBEntry *) lst->data;
1307
status = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_STATUS);
1308
if ( (status < RHYTHMDB_PODCAST_STATUS_COMPLETE) || (status == RHYTHMDB_PODCAST_STATUS_WAITING)) {
1309
/* cancel download */
1310
g_value_set_ulong (&val, RHYTHMDB_PODCAST_STATUS_PAUSED);
1311
rhythmdb_entry_set (source->priv->db, entry, RHYTHMDB_PROP_STATUS, &val);
1312
rb_podcast_manager_cancel_download (source->priv->podcast_mg, entry);
1315
/* add to download */
1316
g_value_set_ulong (&val, RHYTHMDB_PODCAST_STATUS_WAITING);
1317
rhythmdb_entry_set (source->priv->db, entry, RHYTHMDB_PROP_STATUS, &val);
1318
rb_podcast_manager_download_entry (source->priv->podcast_mg, entry);
1323
g_value_unset (&val);
1324
rhythmdb_commit (source->priv->db);
1328
rb_podcast_source_cmd_delete_feed (GtkAction *action,
1329
RBPodcastSource *source)
1338
rb_debug ("Delete feed action");
1340
g_object_get (G_OBJECT (source), "shell", &shell, NULL);
1341
g_object_get (G_OBJECT (shell), "window", &window, NULL);
1342
g_object_unref (G_OBJECT (shell));
1344
dialog = gtk_message_dialog_new (window,
1345
GTK_DIALOG_DESTROY_WITH_PARENT,
1346
GTK_MESSAGE_WARNING,
1348
_("Delete the podcast feed and downloaded files?"));
1350
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
1351
_("If you choose to delete the feed and files, "
1352
"they will be permanently lost. Please note that "
1353
"you can delete the feed but keep the downloaded "
1354
"files by choosing to delete the feed only."));
1356
gtk_window_set_title (GTK_WINDOW (dialog), "");
1358
gtk_dialog_add_buttons (GTK_DIALOG (dialog),
1359
_("Delete _Feed Only"),
1362
GTK_RESPONSE_CANCEL,
1365
button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1366
_("_Delete Feed And Files"),
1369
gtk_window_set_focus (GTK_WINDOW (dialog), button);
1370
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
1372
ret = gtk_dialog_run (GTK_DIALOG (dialog));
1373
gtk_widget_destroy (dialog);
1375
if (ret == GTK_RESPONSE_CANCEL || ret == GTK_RESPONSE_DELETE_EVENT)
1378
lst = source->priv->selected_feeds;
1380
while (lst != NULL) {
1381
g_return_if_fail (lst->data != NULL);
1382
rb_podcast_manager_remove_feed (source->priv->podcast_mg,
1383
(gchar *) lst->data,
1384
(ret == GTK_RESPONSE_YES) );
1391
rb_podcast_source_cmd_properties_feed (GtkAction *action,
1392
RBPodcastSource *source)
1394
RhythmDBEntry *entry;
1396
entry = rhythmdb_entry_lookup_by_location (source->priv->db,
1397
(gchar *) source->priv->selected_feeds->data );
1399
GtkWidget *dialog = rb_feed_podcast_properties_dialog_new (entry);
1400
rb_debug ("in feed properties");
1403
gtk_widget_show_all (dialog);
1405
rb_debug ("no selection!");
1410
rb_podcast_source_cmd_update_feed (GtkAction *action,
1411
RBPodcastSource *source)
1415
rb_debug ("Update action");
1416
lst = source->priv->selected_feeds;
1418
while (lst != NULL) {
1419
rb_podcast_manager_subscribe_feed (source->priv->podcast_mg,
1420
(gchar *) lst->data);
1427
rb_podcast_source_update_feed_func (GtkTreeModel *model,
1430
RBPodcastSource *source)
1432
RhythmDBEntry *entry;
1435
gtk_tree_model_get (model, iter, 0, &entry, -1);
1436
uri = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
1438
rb_podcast_manager_subscribe_feed (source->priv->podcast_mg, uri);
1444
rb_podcast_source_cmd_update_all (GtkAction *action, RBPodcastSource *source)
1446
RhythmDBPropertyModel *feed_model;
1447
RhythmDBQueryModel *query_model;
1449
feed_model = rb_property_view_get_model (RB_PROPERTY_VIEW (source->priv->feeds));
1450
g_object_get (G_OBJECT (feed_model), "query-model", &query_model, NULL);
1451
g_object_unref (G_OBJECT (feed_model));
1453
gtk_tree_model_foreach (GTK_TREE_MODEL (query_model),
1454
(GtkTreeModelForeachFunc) rb_podcast_source_update_feed_func,
1459
rb_podcast_source_post_status_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
1460
GtkTreeModel *tree_model, GtkTreeIter *iter,
1461
RBPodcastSource *source)
1464
RhythmDBEntry *entry;
1467
gtk_tree_model_get (tree_model, iter, 0, &entry, -1);
1469
switch (rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_STATUS))
1471
case RHYTHMDB_PODCAST_STATUS_COMPLETE:
1472
g_object_set (G_OBJECT (renderer), "text", _("Completed"), NULL);
1475
case RHYTHMDB_PODCAST_STATUS_ERROR:
1476
g_object_set (G_OBJECT (renderer), "text", _("Failed"), NULL);
1479
case RHYTHMDB_PODCAST_STATUS_WAITING:
1480
g_object_set (G_OBJECT (renderer), "text", _("Waiting"), NULL);
1483
case RHYTHMDB_PODCAST_STATUS_PAUSED:
1484
g_object_set (G_OBJECT (renderer), "text", _("Paused"), NULL);
1489
char *s = g_strdup_printf ("%u %%", (guint)entry->podcast->status);
1491
g_object_set (G_OBJECT (renderer), "text", s, NULL);
1492
value = entry->podcast->status;
1497
g_object_set (G_OBJECT (renderer), "value", value, NULL);
1504
rb_podcast_source_post_feed_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
1505
GtkTreeModel *tree_model, GtkTreeIter *iter,
1506
RBPodcastSource *source)
1509
RhythmDBEntry *entry;
1512
gtk_tree_model_get (tree_model, iter, 0, &entry, -1);
1513
album = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM);
1515
g_object_set (G_OBJECT (renderer), "text", album, NULL);
1520
rb_podcast_source_feed_status_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
1521
GtkTreeModel *tree_model, GtkTreeIter *iter,
1522
RBPodcastSource *source)
1526
RhythmDBEntry *entry = NULL;
1528
gtk_tree_model_get (tree_model, iter, 0, &location, -1);
1530
entry = rhythmdb_entry_lookup_by_location (source->priv->db, location);
1533
if (entry == NULL) {
1534
g_object_set (G_OBJECT (renderer), "pixbuf", NULL, NULL);
1539
switch (rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_STATUS))
1542
pixbuf = source->priv->feed_unsubscribe_pixbuf;
1545
pixbuf = source->priv->feed_subscribe_pixbuf;
1548
pixbuf = source->priv->feed_unsubscribe_pixbuf;
1551
g_object_set (G_OBJECT (renderer), "pixbuf", pixbuf, NULL);
1558
rb_podcast_source_feed_title_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
1559
GtkTreeModel *tree_model, GtkTreeIter *iter,
1560
RBPodcastSource *source)
1562
RhythmDBEntry *entry = NULL;
1566
gtk_tree_model_get (tree_model, iter, 0, &str, 1, &bold, -1);
1568
entry = rhythmdb_entry_lookup_by_location (source->priv->db, str);
1570
if (entry != NULL) {
1571
str = g_strdup (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE));
1574
g_object_set (G_OBJECT (renderer), "text", str,
1575
"weight", G_UNLIKELY (bold) ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
1584
rb_podcast_source_post_date_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
1585
GtkTreeModel *tree_model, GtkTreeIter *iter,
1586
RBPodcastSource *source)
1588
RhythmDBEntry *entry;
1592
gtk_tree_model_get (tree_model, iter, 0, &entry, -1);
1594
value = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_POST_TIME);
1596
str = g_strdup (_("Unknown"));
1599
time_t time = (time_t) value;
1601
localtime_r (&time, &time_tm);
1602
str = eel_strdup_strftime (_("%Y-%m-%d %H:%M"), &time_tm);
1605
g_object_set (G_OBJECT (renderer), "text", str, NULL);
1613
rb_podcast_source_cb_interval_changed_cb (GtkComboBox *box, gpointer cb_data)
1615
guint index = gtk_combo_box_get_active (box);
1616
eel_gconf_set_integer (CONF_STATE_PODCAST_DOWNLOAD_INTERVAL,
1619
rb_podcast_manager_start_sync (RB_PODCAST_SOURCE (cb_data)->priv->podcast_mg);
1623
rb_podcast_source_load_finish_cb (gpointer cb_data)
1625
RBPodcastSource *source = RB_PODCAST_SOURCE (cb_data);
1627
rb_podcast_manager_start_sync (source->priv->podcast_mg);
1629
g_signal_connect_after (G_OBJECT (source->priv->podcast_mg),
1631
G_CALLBACK (rb_podcast_source_start_download_cb),
1634
g_signal_connect_after (G_OBJECT (source->priv->podcast_mg),
1636
G_CALLBACK (rb_podcast_source_finish_download_cb),
1639
g_signal_connect_after (G_OBJECT (source->priv->podcast_mg),
1640
"feed_updates_avaliable",
1641
G_CALLBACK (rb_podcast_source_feed_updates_avaliable_cb),
1648
impl_receive_drag (RBSource *asource, GtkSelectionData *selection_data)
1650
GList *list, *uri_list, *i;
1651
RBPodcastSource *source = RB_PODCAST_SOURCE (asource);
1653
rb_debug ("parsing uri list");
1654
list = gnome_vfs_uri_list_parse ((char *)selection_data->data);
1661
for (i = list; i != NULL; i = g_list_next (i))
1662
uri_list = g_list_prepend (uri_list, gnome_vfs_uri_to_string ((const GnomeVFSURI *) i->data, 0));
1664
gnome_vfs_uri_list_free (list);
1666
if (uri_list == NULL)
1669
rb_debug ("adding uris");
1677
/* as totem source says, "Super _NETSCAPE_URL trick" */
1678
if (selection_data->type == gdk_atom_intern ("_NETSCAPE_URL", FALSE)) {
1684
if ((uri != NULL) &&
1685
(!rhythmdb_entry_lookup_by_location (source->priv->db, uri))) {
1686
rb_podcast_source_add_feed (source, uri);
1695
g_list_free (uri_list);
1701
rb_podcast_source_start_download_cb (RBPodcastManager *pd, RhythmDBEntry *entry, RBPodcastSource *source)
1703
RBShell *shell = rb_podcast_source_get_shell (source);
1704
const gchar *podcast_name = rhythmdb_entry_get_string(entry, RHYTHMDB_PROP_TITLE);
1705
rb_debug ("Start download");
1706
rb_shell_hidden_notify (shell, 4000, _("Downloading podcast"), NULL, podcast_name);
1707
g_object_unref (G_OBJECT (shell));
1711
rb_podcast_source_finish_download_cb (RBPodcastManager *pd, RhythmDBEntry *entry, RBPodcastSource *source)
1713
RBShell *shell = rb_podcast_source_get_shell (source);
1714
const gchar *podcast_name = rhythmdb_entry_get_string(entry, RHYTHMDB_PROP_TITLE);
1715
rb_debug ("Finish download");
1716
rb_shell_hidden_notify (shell, 4000, _("Finished downloading podcast"), NULL, podcast_name);
1717
g_object_unref (G_OBJECT (shell));
1721
rb_podcast_source_feed_updates_avaliable_cb (RBPodcastManager *pd, RhythmDBEntry *entry, RBPodcastSource *source)
1723
RBShell *shell = rb_podcast_source_get_shell (source);
1724
const gchar *podcast_name = rhythmdb_entry_get_string(entry, RHYTHMDB_PROP_TITLE);
1725
rb_debug ("Updates avaliable");
1726
rb_shell_hidden_notify (shell, 4000, _("New updates avaliable from"), NULL, podcast_name);
1727
g_object_unref (G_OBJECT (shell));
1732
rb_podcast_source_get_shell (RBPodcastSource *source)
1735
g_object_get (G_OBJECT (source), "shell", &shell, NULL);
1740
rb_podcast_source_shutdown (RBPodcastSource *source)
1742
rb_debug ("podcast source shutdown");
1743
rb_podcast_manager_cancel_all (source->priv->podcast_mg);
1747
rb_podcast_source_post_date_cell_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
1748
RBPodcastSource *source)
1750
gulong a_val, b_val;
1753
a_val = rhythmdb_entry_get_ulong (a, RHYTHMDB_PROP_POST_TIME);
1754
b_val = rhythmdb_entry_get_ulong (b, RHYTHMDB_PROP_POST_TIME);
1757
ret = (a_val > b_val) ? 1 : -1;
1759
ret = rb_podcast_source_post_feed_cell_sort_func (a, b, source);
1765
rb_podcast_source_post_status_cell_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
1766
RBPodcastSource *source)
1768
gulong a_val, b_val;
1771
a_val = rhythmdb_entry_get_ulong (a, RHYTHMDB_PROP_STATUS);
1772
b_val = rhythmdb_entry_get_ulong (b, RHYTHMDB_PROP_STATUS);
1775
ret = (a_val > b_val) ? 1 : -1;
1777
ret = rb_podcast_source_post_feed_cell_sort_func (a, b, source);
1783
rb_podcast_source_post_feed_cell_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
1784
RBPodcastSource *source)
1786
const char *a_str, *b_str;
1790
a_str = rhythmdb_entry_get_string (a, RHYTHMDB_PROP_ALBUM);
1791
b_str = rhythmdb_entry_get_string (b, RHYTHMDB_PROP_ALBUM);
1793
ret = strcmp (a_str, b_str);
1798
a_str = rhythmdb_entry_get_string (a, RHYTHMDB_PROP_TITLE);
1799
b_str = rhythmdb_entry_get_string (b, RHYTHMDB_PROP_TITLE);
1801
ret = strcmp (a_str, b_str);
1806
a_str = rhythmdb_entry_get_string (a, RHYTHMDB_PROP_LOCATION);
1807
b_str = rhythmdb_entry_get_string (b, RHYTHMDB_PROP_LOCATION);
1809
ret = strcmp (a_str, b_str);
1814
rb_podcast_source_download_process_error_cb (RBPodcastManager *pd,
1816
RBPodcastSource *source)
1818
rb_error_dialog (NULL, _("Error in podcast"), "%s", error);
1821
static void rb_podcast_source_entry_activated_cb (RBEntryView *view,
1822
RhythmDBEntry *entry,
1823
RBPodcastSource *source)
1827
/* check to see if it has already been downloaded */
1828
if (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MOUNTPOINT) != NULL)
1831
g_value_init (&val, G_TYPE_ULONG);
1832
g_value_set_ulong (&val, RHYTHMDB_PODCAST_STATUS_WAITING);
1833
rhythmdb_entry_set (source->priv->db, entry, RHYTHMDB_PROP_STATUS, &val);
1834
rhythmdb_commit (source->priv->db);
1835
g_value_unset (&val);
1837
rb_podcast_manager_download_entry (source->priv->podcast_mg, entry);