No issues found
Tool | Failure ID | Location | Function | Message | Data |
---|---|---|---|---|---|
clang-analyzer | no-output-found | rb-podcast-main-source.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None | |
clang-analyzer | no-output-found | rb-podcast-main-source.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None |
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2010 Jonathan Matthew <jonathan@d14n.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * The Rhythmbox authors hereby grant permission for non-GPL compatible
11 * GStreamer plugins to be used and distributed together with GStreamer
12 * and Rhythmbox. This permission is above and beyond the permissions granted
13 * by the GPL license by which Rhythmbox is covered. If you modify this code
14 * you may extend this exception to your version of the code, but you are not
15 * obligated to do so. If you do not wish to do so, delete this exception
16 * statement from your version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
26 *
27 */
28
29 #include "config.h"
30
31 #include <glib.h>
32 #include <glib/gi18n.h>
33
34 #include "rb-podcast-settings.h"
35 #include "rb-podcast-main-source.h"
36 #include "rb-podcast-entry-types.h"
37 #include "rb-shell.h"
38 #include "rb-builder-helpers.h"
39 #include "rb-file-helpers.h"
40 #include "rb-util.h"
41 #include "rb-stock-icons.h"
42
43 struct _RBPodcastMainSourcePrivate
44 {
45 GtkWidget *config_widget;
46 };
47
48 G_DEFINE_TYPE (RBPodcastMainSource, rb_podcast_main_source, RB_TYPE_PODCAST_SOURCE)
49
50
51 RBSource *
52 rb_podcast_main_source_new (RBShell *shell, RBPodcastManager *podcast_manager)
53 {
54 RBSource *source;
55 RhythmDBQuery *base_query;
56 RhythmDB *db;
57 GSettings *settings;
58
59 g_object_get (shell, "db", &db, NULL);
60 base_query = rhythmdb_query_parse (db,
61 RHYTHMDB_QUERY_PROP_EQUALS,
62 RHYTHMDB_PROP_TYPE,
63 RHYTHMDB_ENTRY_TYPE_PODCAST_POST,
64 RHYTHMDB_QUERY_END);
65 g_object_unref (db);
66
67 settings = g_settings_new (PODCAST_SETTINGS_SCHEMA);
68
69 source = RB_SOURCE (g_object_new (RB_TYPE_PODCAST_MAIN_SOURCE,
70 "name", _("Podcasts"),
71 "shell", shell,
72 "entry-type", RHYTHMDB_ENTRY_TYPE_PODCAST_POST,
73 "podcast-manager", podcast_manager,
74 "base-query", base_query,
75 "settings", g_settings_get_child (settings, "source"),
76 "toolbar-path", "/PodcastSourceToolBar",
77 NULL));
78 g_object_unref (settings);
79
80 rhythmdb_query_free (base_query);
81
82 rb_shell_register_entry_type_for_source (shell, source,
83 RHYTHMDB_ENTRY_TYPE_PODCAST_FEED);
84 rb_shell_register_entry_type_for_source (shell, source,
85 RHYTHMDB_ENTRY_TYPE_PODCAST_POST);
86
87 return source;
88 }
89
90 void
91 rb_podcast_main_source_add_subsources (RBPodcastMainSource *source)
92 {
93 RhythmDBQuery *query;
94 RBSource *podcast_subsource;
95 RBPodcastManager *podcast_mgr;
96 RhythmDB *db;
97 RBShell *shell;
98
99 g_object_get (source,
100 "shell", &shell,
101 "podcast-manager", &podcast_mgr,
102 NULL);
103 g_object_get (shell, "db", &db, NULL);
104
105 query = rhythmdb_query_parse (db,
106 RHYTHMDB_QUERY_PROP_EQUALS,
107 RHYTHMDB_PROP_TYPE,
108 RHYTHMDB_ENTRY_TYPE_PODCAST_POST,
109 RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN,
110 RHYTHMDB_PROP_FIRST_SEEN,
111 3600 * 24 * 7,
112 RHYTHMDB_QUERY_END);
113
114 podcast_subsource = rb_podcast_source_new (shell,
115 podcast_mgr,
116 query,
117 _("New Episodes"),
118 RB_STOCK_AUTO_PLAYLIST);
119 rhythmdb_query_free (query);
120 rb_source_set_hidden_when_empty (podcast_subsource, TRUE);
121 rb_shell_append_display_page (shell, RB_DISPLAY_PAGE (podcast_subsource), RB_DISPLAY_PAGE (source));
122
123 query = rhythmdb_query_parse (db,
124 RHYTHMDB_QUERY_PROP_EQUALS,
125 RHYTHMDB_PROP_TYPE,
126 RHYTHMDB_ENTRY_TYPE_PODCAST_POST,
127 RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN,
128 RHYTHMDB_PROP_LAST_SEEN,
129 3600 * 24 * 7,
130 RHYTHMDB_QUERY_END);
131
132 podcast_subsource = rb_podcast_source_new (shell,
133 podcast_mgr,
134 query,
135 _("New Downloads"), /* better name? */
136 RB_STOCK_AUTO_PLAYLIST);
137 rhythmdb_query_free (query);
138 rb_source_set_hidden_when_empty (podcast_subsource, TRUE);
139 rb_shell_append_display_page (shell, RB_DISPLAY_PAGE (podcast_subsource), RB_DISPLAY_PAGE (source));
140
141 g_object_unref (db);
142 g_object_unref (shell);
143 }
144
145 static void
146 start_download_cb (RBPodcastManager *pd,
147 RhythmDBEntry *entry,
148 RBPodcastMainSource *source)
149 {
150 RBShell *shell;
151 char *podcast_name;
152
153 podcast_name = g_markup_escape_text (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE), -1);
154
155 g_object_get (source, "shell", &shell, NULL);
156 rb_shell_notify_custom (shell, 4000, _("Downloading podcast"), podcast_name, NULL, FALSE);
157 g_object_unref (shell);
158
159 g_free (podcast_name);
160 }
161
162 static void
163 finish_download_cb (RBPodcastManager *pd,
164 RhythmDBEntry *entry,
165 RBPodcastMainSource *source)
166 {
167 RBShell *shell;
168 char *podcast_name;
169
170 podcast_name = g_markup_escape_text (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE), -1);
171
172 g_object_get (source, "shell", &shell, NULL);
173 rb_shell_notify_custom (shell, 4000, _("Finished downloading podcast"), podcast_name, NULL, FALSE);
174 g_object_unref (shell);
175
176 g_free (podcast_name);
177 }
178
179 static void
180 feed_updates_available_cb (RBPodcastManager *pd,
181 RhythmDBEntry *entry,
182 RBPodcastMainSource *source)
183 {
184 RBShell *shell;
185 char *podcast_name;
186
187 podcast_name = g_markup_escape_text (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE), -1);
188
189 g_object_get (source, "shell", &shell, NULL);
190 rb_shell_notify_custom (shell, 4000, _("New updates available from"), podcast_name, NULL, FALSE);
191 g_object_unref (shell);
192
193 g_free (podcast_name);
194
195 }
196
197 static void
198 error_dialog_response_cb (GtkDialog *dialog, int response, RBPodcastMainSource *source)
199 {
200 const char *url = g_object_get_data (G_OBJECT (dialog), "feed-url");
201
202 if (response == GTK_RESPONSE_YES) {
203 RBPodcastManager *pd;
204 g_object_get (source, "podcast-manager", &pd, NULL);
205 rb_podcast_manager_insert_feed_url (pd, url);
206 g_object_unref (pd);
207 }
208
209 gtk_widget_destroy (GTK_WIDGET (dialog));
210 }
211
212 static void
213 feed_error_cb (RBPodcastManager *pd,
214 const char *url,
215 const char *error,
216 gboolean existing,
217 RBPodcastMainSource *source)
218 {
219 GtkWidget *dialog;
220
221 /* if the podcast feed doesn't already exist in the db,
222 * ask if the user wants to add it anyway; if it already
223 * exists, there's nothing to do besides reporting the error.
224 */
225 dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (source))),
226 GTK_DIALOG_DESTROY_WITH_PARENT,
227 GTK_MESSAGE_ERROR,
228 existing ? GTK_BUTTONS_OK : GTK_BUTTONS_YES_NO,
229 _("Error in podcast"));
230
231 if (existing) {
232 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
233 "%s", error);
234 } else {
235 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
236 _("%s. Would you like to add the podcast feed anyway?"), error);
237 }
238
239 gtk_window_set_title (GTK_WINDOW (dialog), "");
240 gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
241
242 g_object_set_data_full (G_OBJECT (dialog), "feed-url", g_strdup (url), g_free);
243 g_signal_connect (dialog, "response", G_CALLBACK (error_dialog_response_cb), source);
244
245 gtk_widget_show_all (dialog);
246 }
247
248 static void
249 rb_podcast_main_source_btn_file_change_cb (GtkFileChooserButton *widget, RBPodcastSource *source)
250 {
251 GSettings *settings;
252 char *uri;
253
254 settings = g_settings_new (PODCAST_SETTINGS_SCHEMA);
255
256 uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (widget));
257 g_settings_set_string (settings, PODCAST_DOWNLOAD_DIR_KEY, uri);
258 g_free (uri);
259
260 g_object_unref (settings);
261 }
262
263 static GtkWidget *
264 impl_get_config_widget (RBDisplayPage *page, RBShellPreferences *prefs)
265 {
266 RBPodcastMainSource *source = RB_PODCAST_MAIN_SOURCE (page);
267 RBPodcastManager *podcast_mgr;
268 GtkBuilder *builder;
269 GtkWidget *update_interval;
270 GtkWidget *btn_file;
271 GSettings *settings;
272 char *download_dir;
273
274 if (source->priv->config_widget)
275 return source->priv->config_widget;
276
277 builder = rb_builder_load ("podcast-prefs.ui", source);
278 source->priv->config_widget = GTK_WIDGET (gtk_builder_get_object (builder, "podcast_vbox"));
279
280 btn_file = GTK_WIDGET (gtk_builder_get_object (builder, "location_chooser"));
281 gtk_file_chooser_add_shortcut_folder (GTK_FILE_CHOOSER (btn_file),
282 rb_music_dir (),
283 NULL);
284
285 g_object_get (source,
286 "podcast-manager", &podcast_mgr,
287 NULL);
288 download_dir = rb_podcast_manager_get_podcast_dir (podcast_mgr);
289
290 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (btn_file),
291 download_dir);
292 g_object_unref (podcast_mgr);
293 g_free (download_dir);
294
295 g_signal_connect_object (btn_file,
296 "selection-changed",
297 G_CALLBACK (rb_podcast_main_source_btn_file_change_cb),
298 source, 0);
299
300 update_interval = GTK_WIDGET (gtk_builder_get_object (builder, "update_interval"));
301 g_object_set (update_interval, "id-column", 1, NULL);
302
303 settings = g_settings_new (PODCAST_SETTINGS_SCHEMA);
304 g_settings_bind (settings, PODCAST_DOWNLOAD_INTERVAL,
305 update_interval, "active-id",
306 G_SETTINGS_BIND_DEFAULT);
307 g_object_unref (settings);
308
309 return source->priv->config_widget;
310 }
311
312 static guint
313 impl_want_uri (RBSource *source, const char *uri)
314 {
315 if (g_str_has_prefix (uri, "http://") == FALSE)
316 return 0;
317
318 if (g_str_has_suffix (uri, ".xml") ||
319 g_str_has_suffix (uri, ".rss"))
320 return 100;
321
322 return 0;
323 }
324
325 static void
326 impl_add_uri (RBSource *source,
327 const char *uri,
328 const char *title,
329 const char *genre,
330 RBSourceAddCallback callback,
331 gpointer data,
332 GDestroyNotify destroy_data)
333 {
334 RBPodcastManager *podcast_mgr;
335
336 g_object_get (source, "podcast-manager", &podcast_mgr, NULL);
337 rb_podcast_manager_subscribe_feed (podcast_mgr, uri, FALSE);
338 g_object_unref (podcast_mgr);
339
340 if (callback != NULL) {
341 callback (source, uri, data);
342 if (destroy_data != NULL) {
343 destroy_data (data);
344 }
345 }
346 }
347
348 static void
349 impl_constructed (GObject *object)
350 {
351 RBPodcastMainSource *source;
352 RBPodcastManager *podcast_mgr;
353 GdkPixbuf *pixbuf;
354 gint size;
355
356 RB_CHAIN_GOBJECT_METHOD (rb_podcast_main_source_parent_class, constructed, object);
357 source = RB_PODCAST_MAIN_SOURCE (object);
358
359 g_object_get (source, "podcast-manager", &podcast_mgr, NULL);
360
361 g_signal_connect_object (podcast_mgr,
362 "start_download",
363 G_CALLBACK (start_download_cb),
364 source, 0);
365
366 g_signal_connect_object (podcast_mgr,
367 "finish_download",
368 G_CALLBACK (finish_download_cb),
369 source, 0);
370
371 g_signal_connect_object (podcast_mgr,
372 "feed_updates_available",
373 G_CALLBACK (feed_updates_available_cb),
374 source, 0);
375
376 g_signal_connect_object (podcast_mgr,
377 "process_error",
378 G_CALLBACK (feed_error_cb),
379 source, 0);
380
381 gtk_icon_size_lookup (RB_SOURCE_ICON_SIZE, &size, NULL);
382 pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
383 RB_STOCK_PODCAST,
384 size,
385 0, NULL);
386
387 if (pixbuf != NULL) {
388 g_object_set (source, "pixbuf", pixbuf, NULL);
389 g_object_unref (pixbuf);
390 }
391 }
392
393 static void
394 impl_dispose (GObject *object)
395 {
396 RBPodcastMainSource *source;
397
398 source = RB_PODCAST_MAIN_SOURCE (object);
399 if (source->priv->config_widget != NULL) {
400 g_object_unref (source->priv->config_widget);
401 source->priv->config_widget = NULL;
402 }
403
404 G_OBJECT_CLASS (rb_podcast_main_source_parent_class)->dispose (object);
405 }
406
407 static void
408 rb_podcast_main_source_init (RBPodcastMainSource *source)
409 {
410 source->priv = G_TYPE_INSTANCE_GET_PRIVATE (source,
411 RB_TYPE_PODCAST_MAIN_SOURCE,
412 RBPodcastMainSourcePrivate);
413 }
414
415 static void
416 rb_podcast_main_source_class_init (RBPodcastMainSourceClass *klass)
417 {
418 GObjectClass *object_class = G_OBJECT_CLASS (klass);
419 RBDisplayPageClass *page_class = RB_DISPLAY_PAGE_CLASS (klass);
420 RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
421
422 object_class->dispose = impl_dispose;
423 object_class->constructed = impl_constructed;
424
425 page_class->get_config_widget = impl_get_config_widget;
426
427 source_class->impl_want_uri = impl_want_uri;
428 source_class->impl_add_uri = impl_add_uri;
429
430 g_type_class_add_private (klass, sizeof (RBPodcastMainSourcePrivate));
431 }