~ubuntu-branches/ubuntu/utopic/rhythmbox/utopic-proposed

« back to all changes in this revision

Viewing changes to sources/rb-podcast-source.c

Tags: upstream-0.9.2
ImportĀ upstreamĀ versionĀ 0.9.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
 
2
 * 
 
3
 *  arch-tag: Implementation of Internet Podcast source object
 
4
 *
 
5
 *  Copyright (C) 2005 Renato Araujo Oliveira Filho <renato.filho@indt.org.br>
 
6
 *
 
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.
 
11
 *
 
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.
 
16
 *
 
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.
 
20
 *
 
21
 */
 
22
 
 
23
#include <config.h>
 
24
 
 
25
#include <string.h>
 
26
#define __USE_XOPEN
 
27
#include <time.h>
 
28
 
 
29
#include <libxml/tree.h>
 
30
#include <glib.h>
 
31
#include <glib/gi18n.h>
 
32
#include <gtk/gtk.h>
 
33
#include <glade/glade.h>
 
34
 
 
35
#include "rb-podcast-source.h"
 
36
 
 
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"
 
43
#include "rb-util.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"
 
52
#include "rb-debug.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"
 
58
 
 
59
typedef enum
 
60
{
 
61
        RB_PODCAST_QUERY_TYPE_ALL,
 
62
        RB_PODCAST_QUERY_TYPE_ALBUM,
 
63
        RB_PODCAST_QUERY_TYPE_SEARCH,
 
64
} RBPodcastQueryType;
 
65
 
 
66
static void rb_podcast_source_class_init                (RBPodcastSourceClass *klass);
 
67
 
 
68
static void rb_podcast_source_init                      (RBPodcastSource *source);
 
69
 
 
70
static GObject *rb_podcast_source_constructor           (GType type, 
 
71
                                                         guint n_construct_properties,
 
72
                                                         GObjectConstructParam *construct_properties);
 
73
 
 
74
static void rb_podcast_source_dispose                   (GObject *object);
 
75
 
 
76
static void rb_podcast_source_finalize                  (GObject *object);
 
77
 
 
78
static void rb_podcast_source_set_property              (GObject *object,
 
79
                                                         guint prop_id,
 
80
                                                         const GValue *value,
 
81
                                                         GParamSpec *pspec);
 
82
 
 
83
static void rb_podcast_source_get_property              (GObject *object,
 
84
                                                         guint prop_id,
 
85
                                                         GValue *value,
 
86
                                                         GParamSpec *pspec);
 
87
 
 
88
static void rb_podcast_source_songs_show_popup_cb       (RBEntryView *view,
 
89
                                                         RBPodcastSource *source);
 
90
 
 
91
 
 
92
static void rb_podcast_source_feeds_show_popup_cb       (RBSimpleView *view,
 
93
                                                         RBPodcastSource *source);
 
94
 
 
95
static void paned_size_allocate_cb                      (GtkWidget *widget,
 
96
                                                         GtkAllocation *allocation,
 
97
                                                         RBPodcastSource *source);
 
98
                                    
 
99
static void rb_podcast_source_state_pref_changed        (GConfClient *client,
 
100
                                                         guint cnxn_id,
 
101
                                                         GConfEntry *entry,
 
102
                                                         RBPodcastSource *source);
 
103
 
 
104
static void rb_podcast_source_show_browser              (RBPodcastSource *source,
 
105
                                                         gboolean show);
 
106
 
 
107
static void rb_podcast_source_state_prefs_sync          (RBPodcastSource *source);
 
108
 
 
109
 
 
110
static void feed_select_change_cb                       (RBPropertyView *propview, 
 
111
                                                         GList *feeds,
 
112
                                                         RBPodcastSource *podcast_source);
 
113
                                                         
 
114
 
 
115
static void rb_podcast_source_posts_view_sort_order_changed_cb (RBEntryView *view, 
 
116
                                                                RBPodcastSource *source);
 
117
 
 
118
static void rb_podcast_source_download_status_changed_cb(RBPodcastManager *download, 
 
119
                                                         RhythmDBEntry *entry, 
 
120
                                                         gulong status, 
 
121
                                                         RBPodcastSource *source);
 
122
 
 
123
static void rb_podcast_source_btn_file_change_cb        (GtkFileChooserButton *widget, 
 
124
                                                         const char *key);
 
125
 
 
126
 
 
127
static void posts_view_drag_data_received_cb            (GtkWidget *widget,
 
128
                                                         GdkDragContext *dc,
 
129
                                                         gint x, gint y,
 
130
                                                         GtkSelectionData *selection_data, 
 
131
                                                         guint info, guint time,
 
132
                                                         RBPodcastSource *source);
 
133
 
 
134
static void rb_podcast_source_cmd_download_post         (GtkAction *action,
 
135
                                                         RBPodcastSource *source);
 
136
 
 
137
static void rb_podcast_source_cmd_delete_feed           (GtkAction *action,
 
138
                                                         RBPodcastSource *source);
 
139
 
 
140
static void rb_podcast_source_cmd_update_feed           (GtkAction *action,
 
141
                                                         RBPodcastSource *source);
 
142
 
 
143
static void rb_podcast_source_cmd_update_all            (GtkAction *action,
 
144
                                                         RBPodcastSource *source);
 
145
 
 
146
static void rb_podcast_source_cmd_properties_feed       (GtkAction *action,
 
147
                                                         RBPodcastSource *source);
 
148
 
 
149
static void register_action_group                       (RBPodcastSource *source);
 
150
 
 
151
 
 
152
static gint rb_podcast_source_post_date_cell_sort_func  (RhythmDBEntry *a, RhythmDBEntry *b,
 
153
                                                         RBPodcastSource *source);
 
154
 
 
155
static gint rb_podcast_source_post_status_cell_sort_func(RhythmDBEntry *a, RhythmDBEntry *b,
 
156
                                                         RBPodcastSource *source);
 
157
 
 
158
static gint rb_podcast_source_post_feed_cell_sort_func  (RhythmDBEntry *a, RhythmDBEntry *b,
 
159
                                                         RBPodcastSource *source);
 
160
 
 
161
static void rb_podcast_source_post_status_cell_data_func(GtkTreeViewColumn *column, GtkCellRenderer *renderer,
 
162
                                                         GtkTreeModel *tree_model, GtkTreeIter *iter,
 
163
                                                         RBPodcastSource *source);
 
164
 
 
165
static void rb_podcast_source_post_date_cell_data_func  (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
 
166
                                                         GtkTreeModel *tree_model, GtkTreeIter *iter,
 
167
                                                         RBPodcastSource *source);
 
168
 
 
169
static void rb_podcast_source_post_feed_cell_data_func  (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
 
170
                                                         GtkTreeModel *tree_model, GtkTreeIter *iter,
 
171
                                                         RBPodcastSource *source);
 
172
 
 
173
static void rb_podcast_source_feed_status_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
 
174
                                                         GtkTreeModel *tree_model, GtkTreeIter *iter,
 
175
                                                         RBPodcastSource *source);
 
176
 
 
177
static void rb_podcast_source_feed_title_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
 
178
                                                         GtkTreeModel *tree_model, GtkTreeIter *iter,
 
179
                                                         RBPodcastSource *source);
 
180
 
 
181
static void rb_podcast_source_start_download_cb         (RBPodcastManager *pd, 
 
182
                                                         RhythmDBEntry *entry,
 
183
                                                         RBPodcastSource *source);
 
184
        
 
185
static void rb_podcast_source_finish_download_cb        (RBPodcastManager *pd, 
 
186
                                                         RhythmDBEntry *entry,
 
187
                                                         RBPodcastSource *source);
 
188
 
 
189
static void rb_podcast_source_feed_updates_avaliable_cb (RBPodcastManager *pd, 
 
190
                                                         RhythmDBEntry *entry,
 
191
                                                         RBPodcastSource *source);
 
192
 
 
193
static void rb_podcast_source_download_process_error_cb (RBPodcastManager *pd,
 
194
                                                         const char *error,
 
195
                                                         RBPodcastSource *source);
 
196
 
 
197
static void rb_podcast_source_cb_interval_changed_cb    (GtkComboBox *box, gpointer cb_data);
 
198
 
 
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);
 
204
 
 
205
 
 
206
 
 
207
/* source methods */
 
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);
 
221
 
 
222
 
 
223
 
 
224
#define CMD_PATH_SHOW_BROWSER "/commands/ShowBrowser"
 
225
#define CMD_PATH_CURRENT_STATION "/commands/CurrentStation"
 
226
#define CMD_PATH_SONG_INFO    "/commands/SongInfo"
 
227
 
 
228
 
 
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"
 
239
 
 
240
struct RBPodcastSourcePrivate
 
241
{
 
242
        gboolean disposed;
 
243
        
 
244
        RhythmDB *db;
 
245
 
 
246
 
 
247
        GtkWidget *vbox;
 
248
        GtkWidget *config_widget;
 
249
        GtkWidget *paned;
 
250
        
 
251
        GdkPixbuf *pixbuf;
 
252
        GdkPixbuf *feed_subscribe_pixbuf;
 
253
        GdkPixbuf *feed_unsubscribe_pixbuf;
 
254
        GdkPixbuf *feed_working;
 
255
 
 
256
        RBSimpleView *feeds;
 
257
        RBEntryView *posts;
 
258
        GtkActionGroup *action_group;
 
259
 
 
260
        char *search_text;
 
261
        GList *selected_feeds;
 
262
        RhythmDBQueryModel *cached_all_query;
 
263
        char *cached_sorting_type;
 
264
 
 
265
        gboolean initialized;
 
266
        
 
267
        RhythmDBEntryType entry_type;
 
268
        RBPodcastManager *podcast_mg;   
 
269
};
 
270
 
 
271
static GtkActionEntry rb_podcast_source_actions [] =
 
272
{
 
273
        { "PodcastSrcDownloadPost", NULL, N_("Download Podcast _Episode"), NULL,
 
274
          N_("Download Post"),
 
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,
 
280
          N_("Update Feed"),
 
281
          G_CALLBACK (rb_podcast_source_cmd_update_feed) },
 
282
        { "PodcastFeedDelete", GTK_STOCK_DELETE, N_("_Delete Podcast Feed"), NULL,
 
283
          N_("Delete Feed"),
 
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) },
 
288
};
 
289
 
 
290
 
 
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 },
 
295
};
 
296
 
 
297
 
 
298
enum
 
299
{
 
300
        PROP_0,
 
301
        PROP_ENTRY_TYPE,
 
302
        PROP_PODCAST_MANAGER
 
303
};
 
304
 
 
305
G_DEFINE_TYPE (RBPodcastSource, rb_podcast_source, RB_TYPE_SOURCE)
 
306
 
 
307
 
 
308
static void
 
309
rb_podcast_source_class_init (RBPodcastSourceClass *klass)
 
310
{
 
311
 
 
312
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
313
        RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
 
314
 
 
315
        object_class->dispose = rb_podcast_source_dispose;
 
316
        object_class->finalize = rb_podcast_source_finalize;
 
317
        object_class->constructor = rb_podcast_source_constructor;
 
318
 
 
319
        object_class->set_property = rb_podcast_source_set_property;
 
320
        object_class->get_property = rb_podcast_source_get_property;
 
321
 
 
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;
 
339
        
 
340
        g_object_class_install_property (object_class,
 
341
                                         PROP_ENTRY_TYPE,
 
342
                                         g_param_spec_uint ("entry-type",
 
343
                                                            "Entry type",
 
344
                                                            "Type of the entries which should be displayed by this source",
 
345
                                                            0,
 
346
                                                            G_MAXINT,
 
347
                                                            RHYTHMDB_ENTRY_TYPE_PODCAST_POST,
 
348
                                                            G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
349
 
 
350
        g_object_class_install_property (object_class,
 
351
                                         PROP_PODCAST_MANAGER,
 
352
                                         g_param_spec_object ("podcast-manager",
 
353
                                                              "RBPodcastManager",
 
354
                                                              "RBPodcastManager object",
 
355
                                                              RB_TYPE_PODCAST_MANAGER,
 
356
                                                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
357
 
 
358
        
 
359
        
 
360
        
 
361
}
 
362
 
 
363
static void
 
364
rb_podcast_source_init (RBPodcastSource *source)
 
365
{
 
366
        GtkWidget *dummy = gtk_tree_view_new ();
 
367
        source->priv = g_new0 (RBPodcastSourcePrivate, 1);
 
368
 
 
369
        source->priv->selected_feeds = NULL;
 
370
        source->priv->vbox = gtk_vbox_new (FALSE, 5);
 
371
 
 
372
        gtk_container_add (GTK_CONTAINER (source), source->priv->vbox);
 
373
        
 
374
        source->priv->pixbuf = gtk_widget_render_icon (dummy,
 
375
                                                       RB_STOCK_PODCAST,
 
376
                                                       GTK_ICON_SIZE_LARGE_TOOLBAR,
 
377
                                                       NULL);
 
378
 
 
379
 
 
380
        source->priv->feed_subscribe_pixbuf = gtk_widget_render_icon (dummy,
 
381
                                                                      GTK_STOCK_YES,
 
382
                                                                      GTK_ICON_SIZE_MENU,
 
383
                                                                      NULL);
 
384
        
 
385
        source->priv->feed_unsubscribe_pixbuf = gtk_widget_render_icon (dummy,
 
386
                                                                        GTK_STOCK_NO,
 
387
                                                                        GTK_ICON_SIZE_MENU,
 
388
                                                                        NULL);
 
389
 
 
390
        gtk_widget_destroy (dummy);
 
391
}
 
392
 
 
393
static void
 
394
rb_podcast_source_dispose (GObject *object)
 
395
{
 
396
        RBPodcastSource *source;
 
397
 
 
398
        rb_debug ("dispose podcast_source");
 
399
        source = RB_PODCAST_SOURCE (object);
 
400
 
 
401
        if (source->priv->disposed)
 
402
                return;
 
403
        
 
404
        source->priv->disposed = TRUE;
 
405
}
 
406
 
 
407
static void
 
408
rb_podcast_source_finalize (GObject *object)
 
409
{
 
410
        RBPodcastSource *source;
 
411
 
 
412
        g_return_if_fail (object != NULL);
 
413
        g_return_if_fail (RB_IS_PODCAST_SOURCE (object));
 
414
 
 
415
        source = RB_PODCAST_SOURCE (object);
 
416
 
 
417
        g_return_if_fail (source->priv != NULL);
 
418
 
 
419
        rb_debug ("finalizing podcast source");
 
420
 
 
421
        g_object_unref (source->priv->podcast_mg);
 
422
 
 
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);
 
426
        }       
 
427
 
 
428
       if (source->priv->cached_all_query)
 
429
                g_object_unref (G_OBJECT (source->priv->cached_all_query));
 
430
 
 
431
        g_free (source->priv);
 
432
 
 
433
 
 
434
        G_OBJECT_CLASS (rb_podcast_source_parent_class)->finalize (object);
 
435
}
 
436
 
 
437
static GObject *
 
438
rb_podcast_source_constructor (GType type, guint n_construct_properties,
 
439
                              GObjectConstructParam *construct_properties)
 
440
{
 
441
        RBPodcastSource *source;
 
442
        RBPodcastSourceClass *klass;
 
443
        GtkTreeViewColumn *column;
 
444
        GtkCellRenderer *renderer;
 
445
        RhythmDBPropertyModel *feed_model;
 
446
 
 
447
 
 
448
        RBShell *shell;
 
449
 
 
450
        RhythmDBQueryModel *query_model;
 
451
        GtkTreeModel *model;
 
452
        GPtrArray *query;
 
453
        gint width;
 
454
 
 
455
        klass = RB_PODCAST_SOURCE_CLASS (g_type_class_peek (RB_TYPE_PODCAST_SOURCE));
 
456
 
 
457
        source = RB_PODCAST_SOURCE (G_OBJECT_CLASS (rb_podcast_source_parent_class)->
 
458
                        constructor (type, n_construct_properties, construct_properties));
 
459
 
 
460
        register_action_group (source);
 
461
        source->priv->paned = gtk_vpaned_new ();
 
462
 
 
463
        g_object_get (G_OBJECT (source), "shell", &shell, NULL);
 
464
        g_object_get (G_OBJECT (shell), "db", &source->priv->db, NULL);
 
465
        
 
466
        gtk_idle_add ((GtkFunction) rb_podcast_source_load_finish_cb, source);
 
467
 
 
468
        source->priv->podcast_mg = rb_podcast_manager_new (source->priv->db);
 
469
 
 
470
        g_object_unref (shell);
 
471
        
 
472
 
 
473
        /* set up posts view */
 
474
        source->priv->posts = rb_entry_view_new (source->priv->db, CONF_STATE_PODCAST_SORTING_POSTS,
 
475
                                                    FALSE, FALSE);
 
476
        g_signal_connect_object (G_OBJECT (source->priv->posts),
 
477
                                 "entry-activated",
 
478
                                 G_CALLBACK (rb_podcast_source_entry_activated_cb),
 
479
                                 source, 0);
 
480
 
 
481
 
 
482
        column = gtk_tree_view_column_new ();
 
483
        renderer = gtk_cell_renderer_text_new();
 
484
        
 
485
        gtk_tree_view_column_pack_start (column, renderer, TRUE);
 
486
 
 
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);
 
490
        
 
491
        gtk_tree_view_column_set_cell_data_func (column, renderer,
 
492
                                                 (GtkTreeCellDataFunc) rb_podcast_source_post_date_cell_data_func,
 
493
                                                 source, NULL);
 
494
        
 
495
        rb_entry_view_append_column_custom (source->priv->posts, column, 
 
496
                                            _("_Date"), "Date", 
 
497
                                            (GCompareDataFunc) rb_podcast_source_post_date_cell_sort_func, NULL);
 
498
                                                    
 
499
 
 
500
        rb_entry_view_append_column (source->priv->posts, RB_ENTRY_VIEW_COL_TITLE);
 
501
        
 
502
        /* COLUMN FEED */
 
503
        column = gtk_tree_view_column_new ();
 
504
        renderer = gtk_cell_renderer_text_new();
 
505
        
 
506
        gtk_tree_view_column_pack_start (column, renderer, TRUE);
 
507
 
 
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);
 
511
        
 
512
        gtk_tree_view_column_set_cell_data_func (column, renderer,
 
513
                                                 (GtkTreeCellDataFunc) rb_podcast_source_post_feed_cell_data_func,
 
514
                                                 source, NULL);
 
515
        
 
516
        rb_entry_view_append_column_custom (source->priv->posts, column, 
 
517
                                            _("_Feed"), "Feed", 
 
518
                                            (GCompareDataFunc) rb_podcast_source_post_feed_cell_sort_func, NULL);
 
519
 
 
520
        
 
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);
 
525
 
 
526
 
 
527
        column = gtk_tree_view_column_new ();
 
528
        renderer = gtk_cell_renderer_progress_new();
 
529
        
 
530
        gtk_tree_view_column_pack_start (column, renderer, TRUE);
 
531
 
 
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);
 
535
        
 
536
        gtk_tree_view_column_set_cell_data_func (column, renderer,
 
537
                                                 (GtkTreeCellDataFunc) rb_podcast_source_post_status_cell_data_func,
 
538
                                                 source, NULL);
 
539
        
 
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);
 
543
 
 
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),
 
547
                                 source, 0);
 
548
 
 
549
 
 
550
 
 
551
        g_signal_connect (G_OBJECT (source->priv->podcast_mg),
 
552
                          "status_changed",
 
553
                          G_CALLBACK (rb_podcast_source_download_status_changed_cb),
 
554
                          source);
 
555
        
 
556
        g_signal_connect_object (G_OBJECT (source->priv->podcast_mg), 
 
557
                                 "process_error",
 
558
                                 G_CALLBACK (rb_podcast_source_download_process_error_cb),
 
559
                                 source, 0);
 
560
        
 
561
        g_signal_connect_object (G_OBJECT (source->priv->posts),
 
562
                                 "size_allocate",
 
563
                                 G_CALLBACK (paned_size_allocate_cb),
 
564
                                 source, 0);
 
565
        
 
566
        g_signal_connect_object (G_OBJECT (source->priv->posts), "show_popup",
 
567
                                 G_CALLBACK (rb_podcast_source_songs_show_popup_cb), source, 0);
 
568
        
 
569
 
 
570
        /* configure feed view */
 
571
        query = rhythmdb_query_parse (source->priv->db,
 
572
                                      RHYTHMDB_QUERY_PROP_EQUALS,
 
573
                                      RHYTHMDB_PROP_TYPE,
 
574
                                      RHYTHMDB_ENTRY_TYPE_PODCAST_FEED,
 
575
                                      RHYTHMDB_QUERY_END);
 
576
        
 
577
        query_model = rhythmdb_query_model_new_empty (source->priv->db);
 
578
 
 
579
        model = GTK_TREE_MODEL (query_model);
 
580
        
 
581
        rhythmdb_do_full_query_parsed (source->priv->db, model, query);
 
582
 
 
583
 
 
584
        source->priv->feeds = rb_simple_view_new (source->priv->db,  RHYTHMDB_PROP_LOCATION, 
 
585
                                                  _("Feed"));   
 
586
        
 
587
        rb_property_view_set_selection_mode (RB_PROPERTY_VIEW (source->priv->feeds), GTK_SELECTION_MULTIPLE);
 
588
        
 
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));
 
592
        
 
593
        rhythmdb_query_free (query);
 
594
        
 
595
 
 
596
        /* column status */
 
597
        column = gtk_tree_view_column_new ();
 
598
        renderer = rb_cell_renderer_pixbuf_new ();
 
599
        
 
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);
 
604
 
 
605
 
 
606
        gtk_tree_view_column_set_cell_data_func (column, 
 
607
                                                 renderer,
 
608
                                                 (GtkTreeCellDataFunc) rb_podcast_source_feed_status_cell_data_func,
 
609
                                                 source, NULL);
 
610
 
 
611
        rb_simple_view_append_column_custom (source->priv->feeds,
 
612
                                               column, " ", source);
 
613
        
 
614
 
 
615
        /* column title */
 
616
        column = gtk_tree_view_column_new ();
 
617
        renderer = gtk_cell_renderer_text_new ();
 
618
 
 
619
        gtk_tree_view_column_pack_start (column, renderer, TRUE);
 
620
 
 
621
        gtk_tree_view_column_set_cell_data_func (column, 
 
622
                                                 renderer,
 
623
                                                 (GtkTreeCellDataFunc) rb_podcast_source_feed_title_cell_data_func,
 
624
                                                 source, NULL);
 
625
        
 
626
        rb_simple_view_append_column_custom (source->priv->feeds,
 
627
                                             column, _("Feed"), source);
 
628
 
 
629
        
 
630
 
 
631
 
 
632
        g_signal_connect_object (G_OBJECT (source->priv->feeds), "show_popup",
 
633
                                 G_CALLBACK (rb_podcast_source_feeds_show_popup_cb), 
 
634
                                 source, 0);
 
635
 
 
636
        g_signal_connect_object (G_OBJECT (source->priv->feeds),
 
637
                                 "properties-selected",
 
638
                                 G_CALLBACK (feed_select_change_cb),
 
639
                                 source, 0);
 
640
 
 
641
 
 
642
        g_object_ref (G_OBJECT (source->priv->feeds));
 
643
 
 
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),
 
648
                                 source, 0);
 
649
 
 
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);
 
654
        
 
655
        g_signal_connect_object (G_OBJECT (source->priv->posts), 
 
656
                                 "drag_data_received",
 
657
                                 G_CALLBACK (posts_view_drag_data_received_cb),
 
658
                                 source, 0);
 
659
 
 
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);
 
664
 
 
665
        /* set up propiets page */
 
666
        
 
667
        gtk_paned_pack2 (GTK_PANED (source->priv->paned),
 
668
                         GTK_WIDGET (source->priv->posts), TRUE, FALSE);
 
669
 
 
670
        gtk_box_pack_start_defaults (GTK_BOX (source->priv->vbox), source->priv->paned);
 
671
 
 
672
 
 
673
        rb_podcast_source_state_prefs_sync (source);
 
674
        
 
675
        
 
676
        eel_gconf_notification_add (CONF_STATE_PODCAST_PREFIX,
 
677
                                    (GConfClientNotifyFunc) rb_podcast_source_state_pref_changed,
 
678
                                    source);
 
679
        
 
680
        gtk_widget_show_all (GTK_WIDGET (source));
 
681
 
 
682
        rb_podcast_source_do_query (source, RB_PODCAST_QUERY_TYPE_ALL);
 
683
        
 
684
 
 
685
        return G_OBJECT (source);
 
686
}
 
687
 
 
688
static void
 
689
rb_podcast_source_set_property (GObject *object,
 
690
                              guint prop_id,
 
691
                              const GValue *value,
 
692
                              GParamSpec *pspec)
 
693
{
 
694
        RBPodcastSource *source = RB_PODCAST_SOURCE (object);
 
695
 
 
696
        switch (prop_id)
 
697
        {
 
698
        case PROP_ENTRY_TYPE:
 
699
                source->priv->entry_type = g_value_get_uint (value);
 
700
                break;
 
701
        case PROP_PODCAST_MANAGER:
 
702
                source->priv->podcast_mg = g_value_get_object (value);
 
703
                break;          
 
704
        default:
 
705
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
706
                break;
 
707
        }
 
708
}
 
709
 
 
710
static void
 
711
rb_podcast_source_get_property (GObject *object,
 
712
                              guint prop_id,
 
713
                              GValue *value,
 
714
                              GParamSpec *pspec)
 
715
{
 
716
        RBPodcastSource *source = RB_PODCAST_SOURCE (object);
 
717
 
 
718
        switch (prop_id)
 
719
        {
 
720
        case PROP_ENTRY_TYPE:
 
721
                g_value_set_uint (value, source->priv->entry_type);
 
722
                break;
 
723
        case PROP_PODCAST_MANAGER:
 
724
                g_value_set_object (value, source->priv->podcast_mg);
 
725
                break;  
 
726
        default:
 
727
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
728
                break;
 
729
        }
 
730
}
 
731
 
 
732
RBSource *
 
733
rb_podcast_source_new (RBShell *shell)
 
734
{
 
735
        RBSource *source;
 
736
        source = RB_SOURCE (g_object_new (RB_TYPE_PODCAST_SOURCE,
 
737
                                          "name", _("Podcasts"),
 
738
                                          "shell", shell,
 
739
                                          NULL));
 
740
 
 
741
        rb_shell_register_entry_type_for_source (shell, source, 
 
742
                                                 RHYTHMDB_ENTRY_TYPE_PODCAST_POST);
 
743
        
 
744
        return source;
 
745
}
 
746
 
 
747
static GdkPixbuf *
 
748
impl_get_pixbuf (RBSource *asource)
 
749
{
 
750
        RBPodcastSource *source = RB_PODCAST_SOURCE (asource);
 
751
 
 
752
        return source->priv->pixbuf;
 
753
}
 
754
 
 
755
static void
 
756
impl_search (RBSource *asource, const char *search_text)
 
757
{
 
758
        RBPodcastSource *source = RB_PODCAST_SOURCE (asource);
 
759
 
 
760
        if (source->priv->initialized) {
 
761
                if (search_text == NULL && source->priv->search_text == NULL)
 
762
                        return;
 
763
                if (search_text != NULL &&
 
764
                    source->priv->search_text != NULL
 
765
                    && !strcmp (search_text, source->priv->search_text))
 
766
                        return;
 
767
        }
 
768
 
 
769
        source->priv->initialized = TRUE;
 
770
        if (search_text != NULL && search_text[0] == '\0')
 
771
                search_text = NULL;
 
772
 
 
773
        g_free (source->priv->search_text);
 
774
        if (search_text)
 
775
                source->priv->search_text = g_utf8_casefold (search_text, -1);
 
776
        else
 
777
                source->priv->search_text = NULL;
 
778
        rb_podcast_source_do_query (source, RB_PODCAST_QUERY_TYPE_SEARCH);
 
779
 
 
780
        rb_source_notify_filter_changed (RB_SOURCE (source));
 
781
}
 
782
 
 
783
static RBEntryView *
 
784
impl_get_entry_view (RBSource *asource)
 
785
{
 
786
        RBPodcastSource *source = RB_PODCAST_SOURCE (asource);
 
787
 
 
788
        return source->priv->posts;
 
789
}
 
790
 
 
791
static RBSourceEOFType
 
792
impl_handle_eos (RBSource *asource)
 
793
{
 
794
        return RB_SOURCE_EOF_NEXT;
 
795
}
 
796
 
 
797
static char *
 
798
impl_get_status (RBSource *asource)
 
799
{
 
800
        RBPodcastSource *source = RB_PODCAST_SOURCE (asource);
 
801
        gchar *status;
 
802
 
 
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));
 
806
        return status;
 
807
}
 
808
 
 
809
static const char *
 
810
impl_get_browser_key (RBSource *asource)
 
811
{
 
812
        return CONF_STATE_SHOW_BROWSER;
 
813
}
 
814
 
 
815
static void
 
816
impl_delete (RBSource *asource)
 
817
{
 
818
        RBPodcastSource *source = RB_PODCAST_SOURCE (asource);
 
819
        GList *l;
 
820
        gint ret;
 
821
        GtkWidget *dialog;
 
822
        GtkWidget *button;
 
823
        GtkWindow *window;
 
824
        RBShell *shell;
 
825
 
 
826
        rb_debug ("Delete episode action");
 
827
        
 
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));
 
831
        
 
832
        dialog = gtk_message_dialog_new (window,
 
833
                                         GTK_DIALOG_DESTROY_WITH_PARENT,
 
834
                                         GTK_MESSAGE_WARNING,
 
835
                                         GTK_BUTTONS_NONE,
 
836
                                         _("Delete the podcast episode and downloaded file?"));
 
837
 
 
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."));
 
843
 
 
844
        gtk_window_set_title (GTK_WINDOW (dialog), "");
 
845
 
 
846
        gtk_dialog_add_buttons (GTK_DIALOG (dialog), 
 
847
                                _("Delete _Episode Only"), 
 
848
                                GTK_RESPONSE_NO,
 
849
                                GTK_STOCK_CANCEL, 
 
850
                                GTK_RESPONSE_CANCEL, 
 
851
                                NULL);
 
852
        button = gtk_dialog_add_button (GTK_DIALOG (dialog),
 
853
                                        _("_Delete Episode And File"),
 
854
                                        GTK_RESPONSE_YES);
 
855
        
 
856
        gtk_window_set_focus (GTK_WINDOW (dialog), button);
 
857
        gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
 
858
 
 
859
        ret = gtk_dialog_run (GTK_DIALOG (dialog));
 
860
        gtk_widget_destroy (dialog);
 
861
 
 
862
        if (ret == GTK_RESPONSE_CANCEL || ret == GTK_RESPONSE_DELETE_EVENT)
 
863
                return;
 
864
 
 
865
        rb_podcast_manager_set_remove_files (source->priv->podcast_mg, 
 
866
                                             (ret == GTK_RESPONSE_YES));
 
867
        
 
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);
 
872
        }
 
873
}
 
874
 
 
875
static void
 
876
impl_song_properties (RBSource *asource)
 
877
{
 
878
        RBPodcastSource *source = RB_PODCAST_SOURCE (asource);
 
879
        GtkWidget *dialog = rb_podcast_properties_dialog_new (source->priv->posts);
 
880
        rb_debug ("in song properties");
 
881
        if (dialog)
 
882
                gtk_widget_show_all (dialog);
 
883
        else
 
884
                rb_debug ("no selection!");
 
885
}
 
886
 
 
887
static void
 
888
paned_size_allocate_cb (GtkWidget *widget,
 
889
                        GtkAllocation *allocation,
 
890
                        RBPodcastSource *source)
 
891
{
 
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)));
 
895
}
 
896
 
 
897
static void
 
898
rb_podcast_source_state_prefs_sync (RBPodcastSource *source)
 
899
{
 
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));
 
905
}
 
906
 
 
907
static void
 
908
rb_podcast_source_state_pref_changed (GConfClient *client,
 
909
                                     guint cnxn_id,
 
910
                                     GConfEntry *entry,
 
911
                                     RBPodcastSource *source)
 
912
{
 
913
        rb_debug ("state prefs changed");
 
914
        rb_podcast_source_state_prefs_sync (source);
 
915
}
 
916
 
 
917
static void
 
918
rb_podcast_source_posts_view_sort_order_changed_cb (RBEntryView *view,
 
919
                                                   RBPodcastSource *source)
 
920
{
 
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);
 
924
}
 
925
 
 
926
static void
 
927
rb_podcast_source_download_status_changed_cb (RBPodcastManager *download,
 
928
                                                RhythmDBEntry *entry,
 
929
                                                gulong status,
 
930
                                                RBPodcastSource *source)
 
931
{
 
932
        gtk_widget_queue_draw(GTK_WIDGET(source->priv->posts));
 
933
        return;
 
934
 
 
935
}
 
936
 
 
937
 
 
938
static void
 
939
rb_podcast_source_songs_show_popup_cb (RBEntryView *view,
 
940
                                       RBPodcastSource *source)
 
941
{
 
942
        if (G_OBJECT (source) == NULL) {
 
943
                return;
 
944
        }
 
945
        else {
 
946
                GtkAction* act_post;
 
947
                gulong status;
 
948
                gulong all_status = 999;
 
949
                RhythmDBEntry *entry = NULL;
 
950
                GList *lst = rb_entry_view_get_selected_entries(view);
 
951
                
 
952
                while (lst) {
 
953
                        entry = (RhythmDBEntry *) lst->data;
 
954
                        status = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_STATUS);
 
955
                        if ((status != all_status) && (all_status != 999)) {
 
956
                                all_status = 999;
 
957
                                break;
 
958
                        } 
 
959
                        else {
 
960
                                all_status = status;
 
961
                        }
 
962
                                        
 
963
                        lst = lst->next;
 
964
                }
 
965
 
 
966
                act_post = gtk_action_group_get_action (source->priv->action_group, "PodcastSrcDownloadPost");
 
967
 
 
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);
 
972
                }
 
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);
 
976
                }
 
977
                else 
 
978
                        gtk_action_set_visible (act_post, FALSE);
 
979
 
 
980
                _rb_source_show_popup (RB_SOURCE (source), "/PodcastViewPopup");
 
981
        }
 
982
}
 
983
 
 
984
 
 
985
static void
 
986
rb_podcast_source_feeds_show_popup_cb (RBSimpleView *view,
 
987
                                       RBPodcastSource *source)
 
988
{
 
989
        if (G_OBJECT (source) == NULL) {
 
990
                return;
 
991
        }
 
992
        else {
 
993
                GtkAction* act_update;
 
994
                GtkAction* act_properties;
 
995
                GtkAction* act_delete;
 
996
 
 
997
                gulong status;
 
998
                gulong all_status = 999;
 
999
                RhythmDBEntry *entry = NULL;
 
1000
                GList *lst = source->priv->selected_feeds;
 
1001
 
 
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");
 
1005
                
 
1006
                if (lst) {
 
1007
                        while (lst) {
 
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)) {
 
1012
                                        all_status = 999;
 
1013
                                        break;
 
1014
                                } 
 
1015
                                else {
 
1016
                                        all_status = status;
 
1017
                                }
 
1018
                                                
 
1019
                                lst = lst->next;
 
1020
                        }
 
1021
 
 
1022
                        gtk_action_set_visible (act_properties, TRUE);
 
1023
                        gtk_action_set_visible (act_delete, TRUE);
 
1024
                } else {
 
1025
                        gtk_action_set_visible (act_update, FALSE);
 
1026
                        gtk_action_set_visible (act_properties, FALSE);
 
1027
                        gtk_action_set_visible (act_delete, FALSE);
 
1028
                }
 
1029
 
 
1030
                _rb_source_show_popup (RB_SOURCE (source), "/PodcastFeedViewPopup");
 
1031
        }
 
1032
}
 
1033
 
 
1034
 
 
1035
 
 
1036
static void
 
1037
feed_select_change_cb (RBPropertyView *propview, GList *feeds,
 
1038
                   RBPodcastSource *source)
 
1039
{
 
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);
 
1043
        }       
 
1044
 
 
1045
        source->priv->selected_feeds = feeds;
 
1046
        
 
1047
        rb_podcast_source_do_query (source, RB_PODCAST_QUERY_TYPE_ALBUM);
 
1048
 
 
1049
        rb_source_notify_filter_changed (RB_SOURCE (source));
 
1050
}
 
1051
 
 
1052
 
 
1053
 
 
1054
static void
 
1055
rb_podcast_source_show_browser (RBPodcastSource *source,
 
1056
                               gboolean show)
 
1057
{
 
1058
        
 
1059
        GtkWidget *feedswidget = GTK_WIDGET (source->priv->feeds);
 
1060
 
 
1061
        if (show == TRUE) {
 
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);
 
1069
                
 
1070
                g_list_free (children);
 
1071
        }
 
1072
}
 
1073
 
 
1074
 
 
1075
static GPtrArray *
 
1076
construct_query_from_selection (RBPodcastSource *source)
 
1077
{
 
1078
        GPtrArray *query;
 
1079
        RhythmDBEntryType entry_type;
 
1080
 
 
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,
 
1084
                                      RHYTHMDB_PROP_TYPE,
 
1085
                                      entry_type,
 
1086
                                      RHYTHMDB_QUERY_END);
 
1087
 
 
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,
 
1099
                                       query,
 
1100
                                       RHYTHMDB_QUERY_SUBQUERY,
 
1101
                                       subquery,
 
1102
                                       RHYTHMDB_QUERY_END);
 
1103
        }
 
1104
 
 
1105
        if (source->priv->selected_feeds) {
 
1106
                GPtrArray *subquery = g_ptr_array_new ();       
 
1107
                GList *l;              
 
1108
 
 
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,
 
1112
                                               subquery,
 
1113
                                               RHYTHMDB_QUERY_PROP_EQUALS,
 
1114
                                               RHYTHMDB_PROP_SUBTITLE,
 
1115
                                               (gchar *) l->data,
 
1116
                                               RHYTHMDB_QUERY_END);
 
1117
                        if (g_list_next(l))
 
1118
                                rhythmdb_query_append (source->priv->db, subquery,
 
1119
                                                RHYTHMDB_QUERY_DISJUNCTION);
 
1120
                }
 
1121
                
 
1122
                rhythmdb_query_append (source->priv->db, query,
 
1123
                                       RHYTHMDB_QUERY_SUBQUERY, subquery,
 
1124
                                       RHYTHMDB_QUERY_END);
 
1125
        }
 
1126
 
 
1127
        return query;
 
1128
}
 
1129
 
 
1130
static void
 
1131
rb_podcast_source_do_query (RBPodcastSource *source, RBPodcastQueryType qtype)
 
1132
{
 
1133
        RhythmDBQueryModel *query_model;
 
1134
        GPtrArray *query;
 
1135
        gboolean is_all_query, sorting_matches;
 
1136
        const char *current_sorting_type;
 
1137
 
 
1138
        rb_debug ("select entry filter");
 
1139
        
 
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" );
 
1147
 
 
1148
        if (is_all_query) {
 
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));
 
1152
                        return;
 
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);
 
1157
                }
 
1158
        }
 
1159
 
 
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);
 
1165
        }
 
1166
 
 
1167
        rb_debug ("setting empty model");
 
1168
        rb_entry_view_set_model (source->priv->posts, query_model);
 
1169
 
 
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),
 
1174
                                             query);
 
1175
                
 
1176
        rhythmdb_query_free (query);
 
1177
        
 
1178
        if (!is_all_query)
 
1179
                g_object_unref (G_OBJECT (query_model));
 
1180
}
 
1181
 
 
1182
static gboolean
 
1183
impl_show_popup (RBSource *source)
 
1184
{
 
1185
        _rb_source_show_popup (RB_SOURCE (source), "/PodcastSourcePopup");
 
1186
        return TRUE;
 
1187
}
 
1188
 
 
1189
 
 
1190
static GtkWidget *
 
1191
impl_get_config_widget (RBSource *asource)
 
1192
{
 
1193
        RBPodcastSource *source = RB_PODCAST_SOURCE (asource);
 
1194
        GtkWidget *cb_update_interval;
 
1195
        GtkWidget *btn_file;
 
1196
        char *download_dir;
 
1197
        GladeXML *xml;
 
1198
 
 
1199
        if (source->priv->config_widget)
 
1200
                return source->priv->config_widget;
 
1201
 
 
1202
 
 
1203
        xml = rb_glade_xml_new ("podcast-prefs.glade", "podcast_vbox", source);
 
1204
        source->priv->config_widget = glade_xml_get_widget (xml, "podcast_vbox");
 
1205
        
 
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), 
 
1209
                                                 download_dir);
 
1210
        g_free (download_dir);
 
1211
 
 
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);
 
1216
 
 
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,
 
1221
                          "changed",
 
1222
                          G_CALLBACK (rb_podcast_source_cb_interval_changed_cb),
 
1223
                          source);
 
1224
                                
 
1225
        return source->priv->config_widget;
 
1226
}
 
1227
 
 
1228
static void 
 
1229
rb_podcast_source_btn_file_change_cb (GtkFileChooserButton *widget, const char *key)
 
1230
{
 
1231
        char *uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (widget));
 
1232
        
 
1233
        eel_gconf_set_string (key, gnome_vfs_get_local_path_from_uri (uri));
 
1234
        g_free (uri);
 
1235
}
 
1236
 
 
1237
 
 
1238
static void
 
1239
posts_view_drag_data_received_cb (GtkWidget *widget,
 
1240
                                  GdkDragContext *dc,
 
1241
                                  gint x, gint y,
 
1242
                                  GtkSelectionData *selection_data, 
 
1243
                                  guint info, guint time,
 
1244
                                  RBPodcastSource *source)
 
1245
{
 
1246
        impl_receive_drag (RB_SOURCE (source), selection_data);
 
1247
}
 
1248
 
 
1249
void 
 
1250
rb_podcast_source_add_feed (RBPodcastSource *source, const char *uri)
 
1251
{
 
1252
        rb_podcast_manager_subscribe_feed (source->priv->podcast_mg, uri);
 
1253
}
 
1254
 
 
1255
 
 
1256
static void
 
1257
register_action_group (RBPodcastSource *source)
 
1258
{
 
1259
        GtkUIManager *uimanager;
 
1260
        GList *actiongroups;
 
1261
        GList *group;
 
1262
 
 
1263
        g_object_get (G_OBJECT (source), "ui-manager", &uimanager, NULL);
 
1264
        actiongroups = gtk_ui_manager_get_action_groups (uimanager);
 
1265
 
 
1266
        /* Don't create the action group if it's already registered */
 
1267
        for (group = actiongroups; group != NULL; group = group->next) {
 
1268
                const gchar *name;
 
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);
 
1273
                        return;
 
1274
                }
 
1275
        }
 
1276
 
 
1277
        source->priv->action_group = gtk_action_group_new ("PodcastActions");
 
1278
        gtk_action_group_set_translation_domain (source->priv->action_group,
 
1279
                                                 GETTEXT_PACKAGE);
 
1280
        gtk_action_group_add_actions (source->priv->action_group, 
 
1281
                                      rb_podcast_source_actions,
 
1282
                                      G_N_ELEMENTS (rb_podcast_source_actions),
 
1283
                                      source);
 
1284
        gtk_ui_manager_insert_action_group (uimanager, 
 
1285
                                            source->priv->action_group, 0);
 
1286
        g_object_unref (G_OBJECT (uimanager));
 
1287
 
 
1288
}
 
1289
 
 
1290
static void
 
1291
rb_podcast_source_cmd_download_post (GtkAction *action,
 
1292
                                     RBPodcastSource *source)
 
1293
{
 
1294
        GList *lst;
 
1295
        gulong status;
 
1296
        GValue val = {0, };
 
1297
        RBEntryView *posts;
 
1298
        
 
1299
        rb_debug ("Add to download action");
 
1300
        posts = source->priv->posts;
 
1301
        
 
1302
        lst = rb_entry_view_get_selected_entries (posts);
 
1303
        g_value_init (&val, G_TYPE_ULONG);
 
1304
 
 
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);
 
1313
                }
 
1314
                else {
 
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);
 
1319
                }
 
1320
                        
 
1321
                lst = lst->next;
 
1322
        }
 
1323
        g_value_unset (&val);
 
1324
        rhythmdb_commit (source->priv->db);
 
1325
}
 
1326
 
 
1327
static void
 
1328
rb_podcast_source_cmd_delete_feed (GtkAction *action,
 
1329
                                   RBPodcastSource *source)
 
1330
{
 
1331
        GList *lst;
 
1332
        gint ret;
 
1333
        GtkWidget *dialog;
 
1334
        GtkWidget *button;
 
1335
        GtkWindow *window;
 
1336
        RBShell *shell;
 
1337
 
 
1338
        rb_debug ("Delete feed action");
 
1339
 
 
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));
 
1343
 
 
1344
        dialog = gtk_message_dialog_new (window,
 
1345
                                         GTK_DIALOG_DESTROY_WITH_PARENT,
 
1346
                                         GTK_MESSAGE_WARNING,
 
1347
                                         GTK_BUTTONS_NONE,
 
1348
                                         _("Delete the podcast feed and downloaded files?"));
 
1349
 
 
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."));
 
1355
        
 
1356
        gtk_window_set_title (GTK_WINDOW (dialog), "");
 
1357
 
 
1358
        gtk_dialog_add_buttons (GTK_DIALOG (dialog), 
 
1359
                                _("Delete _Feed Only"), 
 
1360
                                GTK_RESPONSE_NO,
 
1361
                                GTK_STOCK_CANCEL, 
 
1362
                                GTK_RESPONSE_CANCEL, 
 
1363
                                NULL);
 
1364
 
 
1365
        button = gtk_dialog_add_button (GTK_DIALOG (dialog),
 
1366
                                        _("_Delete Feed And Files"),
 
1367
                                        GTK_RESPONSE_YES);
 
1368
        
 
1369
        gtk_window_set_focus (GTK_WINDOW (dialog), button);
 
1370
        gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
 
1371
        
 
1372
        ret = gtk_dialog_run (GTK_DIALOG (dialog));
 
1373
        gtk_widget_destroy (dialog);
 
1374
 
 
1375
        if (ret == GTK_RESPONSE_CANCEL || ret == GTK_RESPONSE_DELETE_EVENT)
 
1376
                return;
 
1377
        
 
1378
        lst = source->priv->selected_feeds;
 
1379
 
 
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) );
 
1385
 
 
1386
                lst = lst->next;
 
1387
        }
 
1388
}
 
1389
 
 
1390
static void
 
1391
rb_podcast_source_cmd_properties_feed (GtkAction *action,
 
1392
                                       RBPodcastSource *source)
 
1393
{
 
1394
        RhythmDBEntry *entry;
 
1395
        
 
1396
        entry = rhythmdb_entry_lookup_by_location (source->priv->db, 
 
1397
                                                   (gchar *) source->priv->selected_feeds->data );
 
1398
        
 
1399
        GtkWidget *dialog = rb_feed_podcast_properties_dialog_new (entry);
 
1400
        rb_debug ("in feed properties");
 
1401
        
 
1402
        if (dialog)
 
1403
                gtk_widget_show_all (dialog);
 
1404
        else
 
1405
                rb_debug ("no selection!");
 
1406
 
 
1407
}
 
1408
        
 
1409
static void
 
1410
rb_podcast_source_cmd_update_feed (GtkAction *action,
 
1411
                                   RBPodcastSource *source)
 
1412
{
 
1413
        GList *lst;
 
1414
        
 
1415
        rb_debug ("Update action");
 
1416
        lst = source->priv->selected_feeds;
 
1417
 
 
1418
        while (lst != NULL) {
 
1419
                rb_podcast_manager_subscribe_feed (source->priv->podcast_mg,
 
1420
                                                   (gchar *) lst->data);
 
1421
 
 
1422
                lst = lst->next;
 
1423
        }
 
1424
}
 
1425
 
 
1426
static gboolean
 
1427
rb_podcast_source_update_feed_func (GtkTreeModel *model,
 
1428
                                    GtkTreePath *path,
 
1429
                                    GtkTreeIter *iter,
 
1430
                                    RBPodcastSource *source)
 
1431
{
 
1432
        RhythmDBEntry *entry;
 
1433
        const char *uri;
 
1434
 
 
1435
        gtk_tree_model_get (model, iter, 0, &entry, -1);
 
1436
        uri = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
 
1437
        
 
1438
        rb_podcast_manager_subscribe_feed (source->priv->podcast_mg, uri);
 
1439
 
 
1440
        return FALSE;
 
1441
}
 
1442
 
 
1443
static void
 
1444
rb_podcast_source_cmd_update_all (GtkAction *action, RBPodcastSource *source)
 
1445
{
 
1446
        RhythmDBPropertyModel *feed_model;
 
1447
        RhythmDBQueryModel *query_model;
 
1448
 
 
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));
 
1452
 
 
1453
        gtk_tree_model_foreach (GTK_TREE_MODEL (query_model),
 
1454
                                (GtkTreeModelForeachFunc) rb_podcast_source_update_feed_func,
 
1455
                                source);
 
1456
}
 
1457
 
 
1458
static void
 
1459
rb_podcast_source_post_status_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
 
1460
                                              GtkTreeModel *tree_model, GtkTreeIter *iter,
 
1461
                                              RBPodcastSource *source)
 
1462
 
 
1463
{
 
1464
        RhythmDBEntry *entry;
 
1465
        guint value;
 
1466
 
 
1467
        gtk_tree_model_get (tree_model, iter, 0, &entry, -1);
 
1468
 
 
1469
        switch (rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_STATUS))
 
1470
        {
 
1471
        case RHYTHMDB_PODCAST_STATUS_COMPLETE:
 
1472
                g_object_set (G_OBJECT (renderer), "text", _("Completed"), NULL);
 
1473
                value = 100;  
 
1474
                break;
 
1475
        case RHYTHMDB_PODCAST_STATUS_ERROR:
 
1476
                g_object_set (G_OBJECT (renderer), "text", _("Failed"), NULL);
 
1477
                value = 0;
 
1478
                break;
 
1479
        case RHYTHMDB_PODCAST_STATUS_WAITING:
 
1480
                g_object_set (G_OBJECT (renderer), "text", _("Waiting"), NULL);
 
1481
                value = 0;
 
1482
                break;
 
1483
        case RHYTHMDB_PODCAST_STATUS_PAUSED:
 
1484
                g_object_set (G_OBJECT (renderer), "text", _("Paused"), NULL);
 
1485
                value = 0;
 
1486
                break;
 
1487
        default:
 
1488
                {
 
1489
                        char *s = g_strdup_printf ("%u %%", (guint)entry->podcast->status);
 
1490
                        
 
1491
                        g_object_set (G_OBJECT (renderer), "text", s, NULL);
 
1492
                        value = entry->podcast->status;
 
1493
                        g_free (s);
 
1494
                }
 
1495
        }
 
1496
                
 
1497
        g_object_set (G_OBJECT (renderer), "value", value, NULL);
 
1498
        
 
1499
 
 
1500
 
 
1501
}
 
1502
 
 
1503
static void
 
1504
rb_podcast_source_post_feed_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
 
1505
                                            GtkTreeModel *tree_model, GtkTreeIter *iter,
 
1506
                                            RBPodcastSource *source)
 
1507
 
 
1508
{
 
1509
        RhythmDBEntry *entry;
 
1510
        const gchar *album;
 
1511
 
 
1512
        gtk_tree_model_get (tree_model, iter, 0, &entry, -1);
 
1513
        album = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM);
 
1514
                
 
1515
        g_object_set (G_OBJECT (renderer), "text", album, NULL);
 
1516
}
 
1517
 
 
1518
 
 
1519
static void
 
1520
rb_podcast_source_feed_status_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
 
1521
                                              GtkTreeModel *tree_model, GtkTreeIter *iter,
 
1522
                                              RBPodcastSource *source)
 
1523
{
 
1524
        GdkPixbuf *pixbuf;
 
1525
        gchar *location;
 
1526
        RhythmDBEntry *entry = NULL;
 
1527
 
 
1528
        gtk_tree_model_get (tree_model, iter, 0, &location, -1);
 
1529
        
 
1530
        entry = rhythmdb_entry_lookup_by_location (source->priv->db, location);
 
1531
        g_free (location);      
 
1532
 
 
1533
        if (entry == NULL) { 
 
1534
                g_object_set (G_OBJECT (renderer), "pixbuf", NULL, NULL);
 
1535
                return;
 
1536
        }
 
1537
        
 
1538
        
 
1539
        switch (rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_STATUS))
 
1540
        {
 
1541
        case 0:
 
1542
                pixbuf = source->priv->feed_unsubscribe_pixbuf;
 
1543
                break;
 
1544
        case 1:
 
1545
                pixbuf = source->priv->feed_subscribe_pixbuf;
 
1546
                break;
 
1547
        default:                
 
1548
                pixbuf = source->priv->feed_unsubscribe_pixbuf;
 
1549
        }
 
1550
        
 
1551
        g_object_set (G_OBJECT (renderer), "pixbuf", pixbuf, NULL);
 
1552
        
 
1553
}
 
1554
 
 
1555
 
 
1556
 
 
1557
static void
 
1558
rb_podcast_source_feed_title_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
 
1559
                                              GtkTreeModel *tree_model, GtkTreeIter *iter,
 
1560
                                              RBPodcastSource *source)
 
1561
{
 
1562
        RhythmDBEntry *entry = NULL;
 
1563
        gchar *str;
 
1564
        gboolean bold;
 
1565
 
 
1566
        gtk_tree_model_get (tree_model, iter, 0, &str, 1, &bold, -1);
 
1567
        
 
1568
        entry = rhythmdb_entry_lookup_by_location (source->priv->db, str);
 
1569
 
 
1570
        if (entry != NULL) {
 
1571
                str = g_strdup (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE));
 
1572
        }
 
1573
 
 
1574
        g_object_set (G_OBJECT (renderer), "text", str,
 
1575
                      "weight", G_UNLIKELY (bold) ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
 
1576
                      NULL);
 
1577
 
 
1578
        g_free (str);   
 
1579
 
 
1580
}
 
1581
 
 
1582
 
 
1583
static void
 
1584
rb_podcast_source_post_date_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
 
1585
                                            GtkTreeModel *tree_model, GtkTreeIter *iter,
 
1586
                                            RBPodcastSource *source)
 
1587
{
 
1588
        RhythmDBEntry *entry;
 
1589
        gulong value;
 
1590
        char *str;
 
1591
 
 
1592
        gtk_tree_model_get (tree_model, iter, 0, &entry, -1);
 
1593
 
 
1594
        value = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_POST_TIME);
 
1595
        if (value == 0) {
 
1596
                str = g_strdup (_("Unknown"));
 
1597
        } else {
 
1598
                struct tm time_tm;
 
1599
                time_t time = (time_t) value;
 
1600
 
 
1601
                localtime_r (&time, &time_tm);
 
1602
                str = eel_strdup_strftime (_("%Y-%m-%d %H:%M"), &time_tm);
 
1603
        }
 
1604
        
 
1605
        g_object_set (G_OBJECT (renderer), "text", str, NULL);
 
1606
        g_free (str);
 
1607
}
 
1608
 
 
1609
 
 
1610
 
 
1611
 
 
1612
static void
 
1613
rb_podcast_source_cb_interval_changed_cb (GtkComboBox *box, gpointer cb_data)
 
1614
{
 
1615
        guint index = gtk_combo_box_get_active (box);
 
1616
        eel_gconf_set_integer (CONF_STATE_PODCAST_DOWNLOAD_INTERVAL,
 
1617
                               index);
 
1618
 
 
1619
        rb_podcast_manager_start_sync (RB_PODCAST_SOURCE (cb_data)->priv->podcast_mg);
 
1620
}
 
1621
 
 
1622
static gboolean 
 
1623
rb_podcast_source_load_finish_cb  (gpointer cb_data)
 
1624
{
 
1625
        RBPodcastSource *source  = RB_PODCAST_SOURCE (cb_data);
 
1626
        
 
1627
        rb_podcast_manager_start_sync (source->priv->podcast_mg);
 
1628
        
 
1629
        g_signal_connect_after (G_OBJECT (source->priv->podcast_mg),
 
1630
                                "start_download",
 
1631
                                G_CALLBACK (rb_podcast_source_start_download_cb),
 
1632
                                source);
 
1633
 
 
1634
        g_signal_connect_after (G_OBJECT (source->priv->podcast_mg),
 
1635
                                "finish_download",
 
1636
                                G_CALLBACK (rb_podcast_source_finish_download_cb),
 
1637
                                source);
 
1638
 
 
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),
 
1642
                                source);
 
1643
 
 
1644
        return FALSE;
 
1645
}
 
1646
 
 
1647
static gboolean
 
1648
impl_receive_drag (RBSource *asource, GtkSelectionData *selection_data)
 
1649
{
 
1650
        GList *list, *uri_list, *i;
 
1651
        RBPodcastSource *source = RB_PODCAST_SOURCE (asource);
 
1652
 
 
1653
        rb_debug ("parsing uri list");
 
1654
        list = gnome_vfs_uri_list_parse ((char *)selection_data->data);
 
1655
 
 
1656
        if (list == NULL)
 
1657
                return FALSE;
 
1658
 
 
1659
        uri_list = NULL;
 
1660
 
 
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));
 
1663
 
 
1664
        gnome_vfs_uri_list_free (list);
 
1665
 
 
1666
        if (uri_list == NULL)
 
1667
                return FALSE;
 
1668
 
 
1669
        rb_debug ("adding uris");
 
1670
 
 
1671
        i = uri_list;
 
1672
        while (i != NULL) {
 
1673
                char *uri = NULL;
 
1674
 
 
1675
                uri = i->data;
 
1676
 
 
1677
                /* as totem source says, "Super _NETSCAPE_URL trick" */
 
1678
                if (selection_data->type == gdk_atom_intern ("_NETSCAPE_URL", FALSE)) {
 
1679
                        i = i->next;
 
1680
                        if (i != NULL) {
 
1681
                                g_free (i->data);
 
1682
                        }
 
1683
                }
 
1684
                if ((uri != NULL) && 
 
1685
                    (!rhythmdb_entry_lookup_by_location (source->priv->db, uri))) {
 
1686
                        rb_podcast_source_add_feed (source, uri);
 
1687
                }
 
1688
 
 
1689
                g_free (uri);
 
1690
 
 
1691
                if (i != NULL)
 
1692
                        i = i->next;
 
1693
        }
 
1694
 
 
1695
        g_list_free (uri_list);
 
1696
        return TRUE;
 
1697
}
 
1698
 
 
1699
 
 
1700
static void
 
1701
rb_podcast_source_start_download_cb (RBPodcastManager *pd, RhythmDBEntry *entry, RBPodcastSource *source)
 
1702
{
 
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));
 
1708
}
 
1709
 
 
1710
static void
 
1711
rb_podcast_source_finish_download_cb (RBPodcastManager *pd, RhythmDBEntry *entry, RBPodcastSource *source)
 
1712
{
 
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));
 
1718
}
 
1719
 
 
1720
static void
 
1721
rb_podcast_source_feed_updates_avaliable_cb (RBPodcastManager *pd, RhythmDBEntry *entry, RBPodcastSource *source)
 
1722
{
 
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));
 
1728
 
 
1729
}
 
1730
 
 
1731
static RBShell*
 
1732
rb_podcast_source_get_shell (RBPodcastSource *source)
 
1733
{
 
1734
        RBShell *shell;
 
1735
        g_object_get (G_OBJECT (source), "shell", &shell, NULL);
 
1736
        return shell;
 
1737
}
 
1738
 
 
1739
void 
 
1740
rb_podcast_source_shutdown      (RBPodcastSource *source)
 
1741
{
 
1742
        rb_debug ("podcast source shutdown");
 
1743
        rb_podcast_manager_cancel_all (source->priv->podcast_mg);
 
1744
}
 
1745
 
 
1746
static gint
 
1747
rb_podcast_source_post_date_cell_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
 
1748
                                            RBPodcastSource *source)
 
1749
{
 
1750
        gulong a_val, b_val;
 
1751
        gint ret;
 
1752
 
 
1753
        a_val = rhythmdb_entry_get_ulong (a, RHYTHMDB_PROP_POST_TIME);
 
1754
        b_val = rhythmdb_entry_get_ulong (b, RHYTHMDB_PROP_POST_TIME);
 
1755
 
 
1756
        if (a_val != b_val)
 
1757
                ret = (a_val > b_val) ? 1 : -1;
 
1758
        else
 
1759
                ret = rb_podcast_source_post_feed_cell_sort_func (a, b, source);
 
1760
 
 
1761
        return ret;
 
1762
}
 
1763
 
 
1764
static gint
 
1765
rb_podcast_source_post_status_cell_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
 
1766
                                              RBPodcastSource *source)
 
1767
{
 
1768
        gulong a_val, b_val;
 
1769
        gint ret;
 
1770
 
 
1771
        a_val = rhythmdb_entry_get_ulong (a, RHYTHMDB_PROP_STATUS);
 
1772
        b_val = rhythmdb_entry_get_ulong (b, RHYTHMDB_PROP_STATUS);
 
1773
 
 
1774
        if (a_val != b_val)
 
1775
                ret = (a_val > b_val) ? 1 : -1;
 
1776
        else
 
1777
                ret = rb_podcast_source_post_feed_cell_sort_func (a, b, source);
 
1778
 
 
1779
        return ret;
 
1780
}
 
1781
 
 
1782
static gint
 
1783
rb_podcast_source_post_feed_cell_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
 
1784
                                            RBPodcastSource *source)
 
1785
{
 
1786
        const char *a_str, *b_str;
 
1787
        gint ret;
 
1788
 
 
1789
        /* feeds */
 
1790
        a_str = rhythmdb_entry_get_string (a, RHYTHMDB_PROP_ALBUM);
 
1791
        b_str = rhythmdb_entry_get_string (b, RHYTHMDB_PROP_ALBUM);
 
1792
 
 
1793
        ret = strcmp (a_str, b_str);
 
1794
        if (ret != 0)
 
1795
                return ret;
 
1796
 
 
1797
        /* titles */
 
1798
        a_str = rhythmdb_entry_get_string (a, RHYTHMDB_PROP_TITLE);
 
1799
        b_str = rhythmdb_entry_get_string (b, RHYTHMDB_PROP_TITLE);
 
1800
 
 
1801
        ret = strcmp (a_str, b_str);
 
1802
        if (ret != 0)
 
1803
                return ret;
 
1804
 
 
1805
        /* location */
 
1806
        a_str = rhythmdb_entry_get_string (a, RHYTHMDB_PROP_LOCATION);
 
1807
        b_str = rhythmdb_entry_get_string (b, RHYTHMDB_PROP_LOCATION);
 
1808
 
 
1809
        ret = strcmp (a_str, b_str);
 
1810
        return ret;
 
1811
}
 
1812
 
 
1813
static void
 
1814
rb_podcast_source_download_process_error_cb (RBPodcastManager *pd,
 
1815
                                             const char *error,
 
1816
                                             RBPodcastSource *source)
 
1817
{
 
1818
        rb_error_dialog (NULL, _("Error in podcast"), "%s", error);
 
1819
}
 
1820
 
 
1821
static void rb_podcast_source_entry_activated_cb (RBEntryView *view,
 
1822
                                                  RhythmDBEntry *entry,
 
1823
                                                  RBPodcastSource *source)
 
1824
{
 
1825
        GValue val = {0,};
 
1826
        
 
1827
        /* check to see if it has already been downloaded */
 
1828
        if (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MOUNTPOINT) != NULL)
 
1829
                return;
 
1830
        
 
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);
 
1836
 
 
1837
        rb_podcast_manager_download_entry (source->priv->podcast_mg, entry);
 
1838
}
 
1839