No issues found
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2005 Renato Araujo Oliveira Filho <renato.filho@indt.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 <string.h>
32 #include <time.h>
33
34 #include <glib/gi18n.h>
35 #include <gtk/gtk.h>
36 #include <glib.h>
37
38 #if defined(WITH_WEBKIT)
39 #include <webkit/webkit.h>
40 #endif
41
42 #include "rb-podcast-properties-dialog.h"
43 #include "rb-file-helpers.h"
44 #include "rb-builder-helpers.h"
45 #include "rb-dialog.h"
46 #include "rb-rating.h"
47 #include "rb-util.h"
48 #include "rb-cut-and-paste-code.h"
49 #include "rb-debug.h"
50
51 static void rb_podcast_properties_dialog_class_init (RBPodcastPropertiesDialogClass *klass);
52 static void rb_podcast_properties_dialog_init (RBPodcastPropertiesDialog *dialog);
53 static void rb_podcast_properties_dialog_dispose (GObject *object);
54 static void rb_podcast_properties_dialog_finalize (GObject *object);
55 static void rb_podcast_properties_dialog_set_property (GObject *object,
56 guint prop_id,
57 const GValue *value,
58 GParamSpec *pspec);
59 static void rb_podcast_properties_dialog_get_property (GObject *object,
60 guint prop_id,
61 GValue *value,
62 GParamSpec *pspec);
63 static gboolean rb_podcast_properties_dialog_get_current_entry (RBPodcastPropertiesDialog *dialog);
64 static void rb_podcast_properties_dialog_response_cb (GtkDialog *gtkdialog,
65 int response_id,
66 RBPodcastPropertiesDialog *dialog);
67
68 static void rb_podcast_properties_dialog_update (RBPodcastPropertiesDialog *dialog);
69 static void rb_podcast_properties_dialog_update_title (RBPodcastPropertiesDialog *dialog);
70 static void rb_podcast_properties_dialog_update_title_label (RBPodcastPropertiesDialog *dialog);
71 static void rb_podcast_properties_dialog_update_feed (RBPodcastPropertiesDialog *dialog);
72 static void rb_podcast_properties_dialog_update_location (RBPodcastPropertiesDialog *dialog);
73 static void rb_podcast_properties_dialog_update_download_location (RBPodcastPropertiesDialog *dialog);
74 static void rb_podcast_properties_dialog_update_duration (RBPodcastPropertiesDialog *dialog);
75 static void rb_podcast_properties_dialog_update_play_count (RBPodcastPropertiesDialog *dialog);
76 static void rb_podcast_properties_dialog_update_bitrate (RBPodcastPropertiesDialog *dialog);
77 static void rb_podcast_properties_dialog_update_last_played (RBPodcastPropertiesDialog *dialog);
78 static void rb_podcast_properties_dialog_update_rating (RBPodcastPropertiesDialog *dialog);
79 static void rb_podcast_properties_dialog_update_date (RBPodcastPropertiesDialog *dialog);
80 static void rb_podcast_properties_dialog_update_description (RBPodcastPropertiesDialog *dialog);
81 static gchar* rb_podcast_properties_dialog_parse_time (gulong time);
82 static void rb_podcast_properties_dialog_rated_cb (RBRating *rating,
83 double score,
84 RBPodcastPropertiesDialog *dialog);
85
86 struct RBPodcastPropertiesDialogPrivate
87 {
88 RBEntryView *entry_view;
89 RhythmDB *db;
90 RhythmDBEntry *current_entry;
91
92 GtkWidget *title;
93 GtkWidget *feed;
94 GtkWidget *location;
95 GtkWidget *download_location;
96 GtkWidget *duration;
97 GtkWidget *lastplayed;
98 GtkWidget *playcount;
99 GtkWidget *bitrate;
100 GtkWidget *rating;
101 GtkWidget *date;
102 GtkWidget *description;
103 GtkWidget *description_window;
104
105 GtkWidget *close_button;
106 };
107
108 enum
109 {
110 PROP_0,
111 PROP_ENTRY_VIEW,
112 PROP_BACKEND
113 };
114
115 G_DEFINE_TYPE (RBPodcastPropertiesDialog, rb_podcast_properties_dialog, GTK_TYPE_DIALOG)
116
117 #if defined(WITH_WEBKIT)
118 /* list of HTML-ish strings that we search for to distinguish plain text from HTML podcast
119 * descriptions. we don't really have anything else to go on - regular content type
120 * sniffing only works for proper HTML documents, but these are just tiny fragments, usually
121 * with some simple formatting tags. if we find any of these in a podcast description,
122 * we'll display it as HTML rather than text.
123 */
124 static const char *html_clues[] = {
125 "<a ",
126 "<b>",
127 "<i>",
128 "<ul>",
129 "<br",
130 "<img ",
131 "<",
132 ">",
133 "&",
134 "&quo;",
135 "",
136 "&#x"
137 };
138
139 /* list of URI prefixes for things we ignore when handling navigation requests.
140 * some podcast descriptions include facebook 'like' buttons as iframes, which otherwise
141 * show up as external web browser windows.
142 */
143 static const char *ignore_uris[] = {
144 "http://www.facebook.com/plugins/like.php?"
145 };
146
147 #endif
148
149 static void
150 rb_podcast_properties_dialog_class_init (RBPodcastPropertiesDialogClass *klass)
151 {
152 GObjectClass *object_class = G_OBJECT_CLASS (klass);
153
154 object_class->set_property = rb_podcast_properties_dialog_set_property;
155 object_class->get_property = rb_podcast_properties_dialog_get_property;
156
157 g_object_class_install_property (object_class,
158 PROP_ENTRY_VIEW,
159 g_param_spec_object ("entry-view",
160 "RBEntryView",
161 "RBEntryView object",
162 RB_TYPE_ENTRY_VIEW,
163 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
164
165 object_class->dispose = rb_podcast_properties_dialog_dispose;
166 object_class->finalize = rb_podcast_properties_dialog_finalize;
167
168 g_type_class_add_private (klass, sizeof (RBPodcastPropertiesDialogPrivate));
169 }
170
171 #if defined(WITH_WEBKIT)
172
173 static WebKitNavigationResponse
174 navigation_requested_cb (WebKitWebView *web_view,
175 WebKitWebFrame *frame,
176 WebKitNetworkRequest *request,
177 RBPodcastPropertiesDialog *dialog)
178 {
179 const char *uri;
180 GError *error = NULL;
181 int i;
182
183 uri = webkit_network_request_get_uri (request);
184
185 /* ignore some obnoxious social networking stuff */
186 for (i = 0; i < G_N_ELEMENTS (ignore_uris); i++) {
187 if (g_str_has_prefix (uri, ignore_uris[i])) {
188 rb_debug ("ignoring external URI %s", uri);
189 return WEBKIT_NAVIGATION_RESPONSE_IGNORE;
190 }
191 }
192
193 gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (dialog)), uri, GDK_CURRENT_TIME, &error);
194 if (error != NULL) {
195 rb_error_dialog (NULL, _("Unable to display requested URI"), "%s", error->message);
196 g_error_free (error);
197 }
198
199 return WEBKIT_NAVIGATION_RESPONSE_IGNORE;
200 }
201
202 static void
203 set_webkit_settings (WebKitWebView *view)
204 {
205 WebKitWebSettings *settings;
206
207 settings = webkit_web_settings_new ();
208 g_object_set (settings,
209 "enable-scripts", FALSE,
210 "enable-plugins", FALSE,
211 NULL);
212 webkit_web_view_set_settings (view, settings);
213 }
214
215 static void
216 set_webkit_font_from_gtk_style (WebKitWebView *view)
217 {
218 WebKitWebSettings *settings;
219 const PangoFontDescription *font_desc;
220 GtkStyleContext *style;
221 int font_size;
222 const char *font_family;
223
224 style = gtk_widget_get_style_context (GTK_WIDGET (view));
225 settings = webkit_web_view_get_settings (view);
226
227 font_desc = gtk_style_context_get_font (style,
228 GTK_STATE_FLAG_ACTIVE);
229 font_size = pango_font_description_get_size (font_desc);
230 if (pango_font_description_get_size_is_absolute (font_desc) == FALSE)
231 font_size /= PANGO_SCALE;
232
233 font_family = pango_font_description_get_family (font_desc);
234
235 rb_debug ("setting font settings: %s / %d", font_family, font_size);
236 g_object_set (settings,
237 "default-font-size", font_size,
238 "default-monospace-font-size", font_size,
239 "sans-serif-font-family", font_family,
240 "monospace-font-family", font_family,
241 NULL);
242 }
243 #endif
244
245 static void
246 rb_podcast_properties_dialog_init (RBPodcastPropertiesDialog *dialog)
247 {
248 GtkWidget *content_area;
249 GtkWidget *bin;
250 GtkWidget *widget;
251 GtkBuilder *builder;
252 AtkObject *lobj, *robj;
253
254 dialog->priv = G_TYPE_INSTANCE_GET_PRIVATE (dialog,
255 RB_TYPE_PODCAST_PROPERTIES_DIALOG,
256 RBPodcastPropertiesDialogPrivate);
257
258 g_signal_connect_object (G_OBJECT (dialog),
259 "response",
260 G_CALLBACK (rb_podcast_properties_dialog_response_cb),
261 dialog, 0);
262
263 content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
264
265 gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
266 gtk_box_set_spacing (GTK_BOX (content_area), 2);
267
268 gtk_dialog_set_default_response (GTK_DIALOG (dialog),
269 GTK_RESPONSE_OK);
270
271 builder = rb_builder_load ("podcast-properties.ui", dialog);
272
273 gtk_container_add (GTK_CONTAINER (content_area),
274 GTK_WIDGET (gtk_builder_get_object (builder, "podcastproperties")));
275 dialog->priv->close_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
276 GTK_STOCK_CLOSE,
277 GTK_RESPONSE_CLOSE);
278 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);
279
280 /* get the widgets from the builder */
281 dialog->priv->title = GTK_WIDGET (gtk_builder_get_object (builder, "titleLabel"));
282 dialog->priv->feed = GTK_WIDGET (gtk_builder_get_object (builder, "feedLabel"));
283 dialog->priv->duration = GTK_WIDGET (gtk_builder_get_object (builder, "durationLabel"));
284 dialog->priv->location = GTK_WIDGET (gtk_builder_get_object (builder, "locationLabel"));
285 dialog->priv->download_location = GTK_WIDGET (gtk_builder_get_object (builder, "downloadLocationLabel"));
286 dialog->priv->lastplayed = GTK_WIDGET (gtk_builder_get_object (builder, "lastplayedLabel"));
287 dialog->priv->playcount = GTK_WIDGET (gtk_builder_get_object (builder, "playcountLabel"));
288 dialog->priv->bitrate = GTK_WIDGET (gtk_builder_get_object (builder, "bitrateLabel"));
289 dialog->priv->date = GTK_WIDGET (gtk_builder_get_object (builder, "dateLabel"));
290 #if defined(WITH_WEBKIT)
291 dialog->priv->description = webkit_web_view_new ();
292 set_webkit_settings (WEBKIT_WEB_VIEW (dialog->priv->description));
293 set_webkit_font_from_gtk_style (WEBKIT_WEB_VIEW (dialog->priv->description));
294
295 g_signal_connect_object (dialog->priv->description,
296 "navigation-requested",
297 G_CALLBACK (navigation_requested_cb),
298 dialog,
299 0);
300 #else
301 dialog->priv->description = gtk_label_new (NULL);
302 gtk_label_set_line_wrap (GTK_LABEL (dialog->priv->description), TRUE);
303 #endif
304 /* add relationship between the description label and the description widget */
305 widget = GTK_WIDGET (gtk_builder_get_object (builder, "descriptionDescLabel"));
306 gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->description);
307 lobj = gtk_widget_get_accessible (widget);
308 robj = gtk_widget_get_accessible (dialog->priv->description);
309 atk_object_add_relationship (lobj, ATK_RELATION_LABEL_FOR, robj);
310 atk_object_add_relationship (robj, ATK_RELATION_LABELLED_BY, lobj);
311
312 bin = GTK_WIDGET (gtk_builder_get_object (builder, "descriptionViewport"));
313 gtk_container_add (GTK_CONTAINER (bin), dialog->priv->description);
314
315 dialog->priv->description_window = GTK_WIDGET (gtk_builder_get_object (builder, "descriptionWindow"));
316
317 rb_builder_boldify_label (builder, "titleDescLabel");
318 rb_builder_boldify_label (builder, "feedDescLabel");
319 rb_builder_boldify_label (builder, "locationDescLabel");
320 rb_builder_boldify_label (builder, "downloadLocationDescLabel");
321 rb_builder_boldify_label (builder, "durationDescLabel");
322 rb_builder_boldify_label (builder, "ratingDescLabel");
323 rb_builder_boldify_label (builder, "lastplayedDescLabel");
324 rb_builder_boldify_label (builder, "playcountDescLabel");
325 rb_builder_boldify_label (builder, "bitrateDescLabel");
326 rb_builder_boldify_label (builder, "dateDescLabel");
327 rb_builder_boldify_label (builder, "descriptionDescLabel");
328
329 dialog->priv->rating = GTK_WIDGET (rb_rating_new ());
330 g_signal_connect_object (dialog->priv->rating,
331 "rated",
332 G_CALLBACK (rb_podcast_properties_dialog_rated_cb),
333 G_OBJECT (dialog), 0);
334 gtk_container_add (GTK_CONTAINER (gtk_builder_get_object (builder, "ratingVBox")),
335 dialog->priv->rating);
336
337 /* add relationship between the rating label and the rating widget */
338 lobj = gtk_widget_get_accessible (GTK_WIDGET (gtk_builder_get_object (builder, "ratingDescLabel")));
339 robj = gtk_widget_get_accessible (dialog->priv->rating);
340
341 atk_object_add_relationship (lobj, ATK_RELATION_LABEL_FOR, robj);
342 atk_object_add_relationship (robj, ATK_RELATION_LABELLED_BY, lobj);
343
344 g_object_unref (builder);
345 }
346
347 static void
348 rb_podcast_properties_dialog_dispose (GObject *object)
349 {
350 RBPodcastPropertiesDialog *dialog;
351
352 g_return_if_fail (object != NULL);
353 g_return_if_fail (RB_IS_PODCAST_PROPERTIES_DIALOG (object));
354
355 dialog = RB_PODCAST_PROPERTIES_DIALOG (object);
356
357 g_return_if_fail (dialog->priv != NULL);
358
359 if (dialog->priv->db != NULL) {
360 g_object_unref (dialog->priv->db);
361 dialog->priv->db = NULL;
362 }
363
364 G_OBJECT_CLASS (rb_podcast_properties_dialog_parent_class)->dispose (object);
365 }
366
367 static void
368 rb_podcast_properties_dialog_finalize (GObject *object)
369 {
370 RBPodcastPropertiesDialog *dialog;
371
372 g_return_if_fail (object != NULL);
373 g_return_if_fail (RB_IS_PODCAST_PROPERTIES_DIALOG (object));
374
375 dialog = RB_PODCAST_PROPERTIES_DIALOG (object);
376
377 g_return_if_fail (dialog->priv != NULL);
378
379 G_OBJECT_CLASS (rb_podcast_properties_dialog_parent_class)->finalize (object);
380 }
381
382 static void
383 rb_podcast_properties_dialog_set_entry_view (RBPodcastPropertiesDialog *dialog,
384 RBEntryView *view)
385 {
386 if (dialog->priv->db != NULL) {
387 g_object_unref (dialog->priv->db);
388 }
389
390 dialog->priv->entry_view = view;
391
392 if (dialog->priv->entry_view != NULL) {
393 g_object_get (dialog->priv->entry_view,
394 "db", &dialog->priv->db, NULL);
395 }
396 }
397
398 static void
399 rb_podcast_properties_dialog_set_property (GObject *object,
400 guint prop_id,
401 const GValue *value,
402 GParamSpec *pspec)
403 {
404 RBPodcastPropertiesDialog *dialog = RB_PODCAST_PROPERTIES_DIALOG (object);
405
406 switch (prop_id) {
407 case PROP_ENTRY_VIEW:
408 rb_podcast_properties_dialog_set_entry_view (dialog, g_value_get_object (value));
409 break;
410 default:
411 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
412 break;
413 }
414 }
415
416 static void
417 rb_podcast_properties_dialog_get_property (GObject *object,
418 guint prop_id,
419 GValue *value,
420 GParamSpec *pspec)
421 {
422 RBPodcastPropertiesDialog *dialog = RB_PODCAST_PROPERTIES_DIALOG (object);
423
424 switch (prop_id) {
425 case PROP_ENTRY_VIEW:
426 g_value_set_object (value, dialog->priv->entry_view);
427 break;
428 default:
429 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
430 break;
431 }
432 }
433
434 GtkWidget *
435 rb_podcast_properties_dialog_new (RBEntryView *entry_view)
436 {
437 RBPodcastPropertiesDialog *dialog;
438
439 g_return_val_if_fail (RB_IS_ENTRY_VIEW (entry_view), NULL);
440
441 dialog = g_object_new (RB_TYPE_PODCAST_PROPERTIES_DIALOG,
442 "entry-view", entry_view, NULL);
443
444 if (!rb_podcast_properties_dialog_get_current_entry (dialog)) {
445 g_object_unref (G_OBJECT (dialog));
446 return NULL;
447 }
448 rb_podcast_properties_dialog_update (dialog);
449
450 return GTK_WIDGET (dialog);
451 }
452
453 static void
454 rb_podcast_properties_dialog_response_cb (GtkDialog *gtkdialog,
455 int response_id,
456 RBPodcastPropertiesDialog *dialog)
457 {
458 if (response_id != GTK_RESPONSE_OK)
459 goto cleanup;
460 cleanup:
461 gtk_widget_destroy (GTK_WIDGET (dialog));
462 }
463
464 static gboolean
465 rb_podcast_properties_dialog_get_current_entry (RBPodcastPropertiesDialog *dialog)
466 {
467 GList *selected_entries;
468
469 /* get the entry */
470 selected_entries = rb_entry_view_get_selected_entries (dialog->priv->entry_view);
471
472 if ((selected_entries == NULL) ||
473 (selected_entries->data == NULL)) {
474 dialog->priv->current_entry = NULL;
475 return FALSE;
476 }
477
478 dialog->priv->current_entry = selected_entries->data;
479 return TRUE;
480 }
481
482 static void
483 rb_podcast_properties_dialog_update (RBPodcastPropertiesDialog *dialog)
484 {
485 g_return_if_fail (dialog->priv->current_entry != NULL);
486 rb_podcast_properties_dialog_update_location (dialog);
487 rb_podcast_properties_dialog_update_download_location (dialog);
488 rb_podcast_properties_dialog_update_title (dialog);
489 rb_podcast_properties_dialog_update_title_label (dialog);
490 rb_podcast_properties_dialog_update_feed (dialog);
491 rb_podcast_properties_dialog_update_duration (dialog);
492 rb_podcast_properties_dialog_update_play_count (dialog);
493 rb_podcast_properties_dialog_update_bitrate (dialog);
494 rb_podcast_properties_dialog_update_last_played (dialog);
495 rb_podcast_properties_dialog_update_rating (dialog);
496 rb_podcast_properties_dialog_update_date (dialog);
497 rb_podcast_properties_dialog_update_description (dialog);
498 }
499
500 static void
501 rb_podcast_properties_dialog_update_title (RBPodcastPropertiesDialog *dialog)
502 {
503 const char *name;
504 char *tmp;
505
506 name = rhythmdb_entry_get_string (dialog->priv->current_entry, RHYTHMDB_PROP_TITLE);
507 tmp = g_strdup_printf (_("%s Properties"), name);
508 gtk_window_set_title (GTK_WINDOW (dialog), tmp);
509 g_free (tmp);
510 }
511
512 static void
513 rb_podcast_properties_dialog_update_title_label (RBPodcastPropertiesDialog *dialog)
514 {
515 const char *title;
516
517 title = rhythmdb_entry_get_string (dialog->priv->current_entry, RHYTHMDB_PROP_TITLE);
518 gtk_label_set_text (GTK_LABEL (dialog->priv->title), title);
519 }
520
521 static void
522 rb_podcast_properties_dialog_update_feed (RBPodcastPropertiesDialog *dialog)
523 {
524 const char *feed;
525
526 feed = rhythmdb_entry_get_string (dialog->priv->current_entry, RHYTHMDB_PROP_ALBUM);
527 gtk_label_set_text (GTK_LABEL (dialog->priv->feed), feed);
528 }
529
530 static void
531 rb_podcast_properties_dialog_update_duration (RBPodcastPropertiesDialog *dialog)
532 {
533 char *text;
534 gulong duration = 0;
535
536 duration = rhythmdb_entry_get_ulong (dialog->priv->current_entry, RHYTHMDB_PROP_DURATION);
537
538 text = rb_make_duration_string (duration);
539 gtk_label_set_text (GTK_LABEL (dialog->priv->duration), text);
540 g_free (text);
541 }
542
543 static void
544 rb_podcast_properties_dialog_update_location (RBPodcastPropertiesDialog *dialog)
545 {
546 const char *location;
547 char *display;
548
549 location = rhythmdb_entry_get_string (dialog->priv->current_entry, RHYTHMDB_PROP_MOUNTPOINT);
550 if (location == NULL)
551 location = rhythmdb_entry_get_string (dialog->priv->current_entry, RHYTHMDB_PROP_LOCATION);
552 display = g_uri_unescape_string (location, NULL);
553 gtk_label_set_text (GTK_LABEL (dialog->priv->location), display);
554 g_free (display);
555 }
556
557 static void
558 rb_podcast_properties_dialog_update_download_location (RBPodcastPropertiesDialog *dialog)
559 {
560 const char *location;
561
562 location = rhythmdb_entry_get_string (dialog->priv->current_entry, RHYTHMDB_PROP_MOUNTPOINT);
563 if (location != NULL && location[0] != '\0') {
564 char *display;
565 location = rhythmdb_entry_get_string (dialog->priv->current_entry, RHYTHMDB_PROP_LOCATION);
566 display = g_uri_unescape_string (location, NULL);
567 gtk_label_set_text (GTK_LABEL (dialog->priv->download_location), display);
568 g_free (display);
569 } else {
570 gtk_label_set_text (GTK_LABEL (dialog->priv->download_location), _("Not Downloaded"));
571 }
572 }
573
574 static void
575 rb_podcast_properties_dialog_rated_cb (RBRating *rating,
576 double score,
577 RBPodcastPropertiesDialog *dialog)
578 {
579 GValue value = { 0, };
580
581 g_return_if_fail (RB_IS_RATING (rating));
582 g_return_if_fail (RB_IS_PODCAST_PROPERTIES_DIALOG (dialog));
583 g_return_if_fail (score >= 0 && score <= 5 );
584
585 /* set the new value for the song */
586 g_value_init (&value, G_TYPE_DOUBLE);
587 g_value_set_double (&value, score);
588 rhythmdb_entry_set (dialog->priv->db,
589 dialog->priv->current_entry,
590 RHYTHMDB_PROP_RATING,
591 &value);
592 rhythmdb_commit (dialog->priv->db);
593 g_value_unset (&value);
594
595 g_object_set (G_OBJECT (dialog->priv->rating),
596 "rating", score,
597 NULL);
598 }
599
600 static void
601 rb_podcast_properties_dialog_update_play_count (RBPodcastPropertiesDialog *dialog)
602 {
603 gulong count;
604 char *text;
605
606 count = rhythmdb_entry_get_ulong (dialog->priv->current_entry, RHYTHMDB_PROP_PLAY_COUNT);
607 text = g_strdup_printf ("%ld", count);
608 gtk_label_set_text (GTK_LABEL (dialog->priv->playcount), text);
609 g_free (text);
610 }
611
612 static void
613 rb_podcast_properties_dialog_update_bitrate (RBPodcastPropertiesDialog *dialog)
614 {
615 char *tmp = NULL;
616 gulong bitrate = 0;
617
618 bitrate = rhythmdb_entry_get_ulong (dialog->priv->current_entry, RHYTHMDB_PROP_BITRATE);
619 if (bitrate > 0)
620 tmp = g_strdup_printf (_("%lu kbps"), bitrate);
621 else
622 tmp = g_strdup (_("Unknown"));
623
624 gtk_label_set_text (GTK_LABEL (dialog->priv->bitrate), tmp);
625 g_free (tmp);
626 }
627
628 static void
629 rb_podcast_properties_dialog_update_last_played (RBPodcastPropertiesDialog *dialog)
630 {
631 const char *str;
632
633 str = rhythmdb_entry_get_string (dialog->priv->current_entry, RHYTHMDB_PROP_LAST_PLAYED_STR);
634 gtk_label_set_text (GTK_LABEL (dialog->priv->lastplayed), str);
635 }
636
637 static void
638 rb_podcast_properties_dialog_update_rating (RBPodcastPropertiesDialog *dialog)
639 {
640 double rating;
641
642 rating = rhythmdb_entry_get_double (dialog->priv->current_entry, RHYTHMDB_PROP_RATING);
643 g_object_set (G_OBJECT (dialog->priv->rating), "rating", rating, NULL);
644 }
645
646 static void
647 rb_podcast_properties_dialog_update_date (RBPodcastPropertiesDialog *dialog)
648 {
649 gulong post_time;
650 char *time;
651
652 post_time = rhythmdb_entry_get_ulong (dialog->priv->current_entry, RHYTHMDB_PROP_POST_TIME);
653 time = rb_podcast_properties_dialog_parse_time (post_time);
654
655 gtk_label_set_text (GTK_LABEL (dialog->priv->date), time);
656 g_free (time);
657 }
658
659 #if defined(WITH_WEBKIT)
660 static gboolean
661 update_scrollbar_policy_cb (WebKitWebFrame *frame, RBPodcastPropertiesDialog *dialog)
662 {
663 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (dialog->priv->description_window),
664 webkit_web_frame_get_horizontal_scrollbar_policy (frame),
665 webkit_web_frame_get_vertical_scrollbar_policy (frame));
666 return TRUE;
667 }
668
669 #endif
670
671 static void
672 rb_podcast_properties_dialog_update_description (RBPodcastPropertiesDialog *dialog)
673 {
674 #if defined(WITH_WEBKIT)
675 WebKitWebFrame *frame;
676 const char *str;
677 int i;
678 gboolean loaded = FALSE;
679 str = rhythmdb_entry_get_string (dialog->priv->current_entry, RHYTHMDB_PROP_DESCRIPTION);
680 for (i = 0; i < G_N_ELEMENTS (html_clues); i++) {
681 if (g_strstr_len (str, -1, html_clues[i]) != NULL) {
682 webkit_web_view_load_html_string (WEBKIT_WEB_VIEW (dialog->priv->description),
683 str,
684 "");
685 loaded = TRUE;
686 }
687 }
688
689 if (loaded == FALSE) {
690 webkit_web_view_load_string (WEBKIT_WEB_VIEW (dialog->priv->description),
691 str,
692 "text/plain",
693 "utf-8",
694 "");
695 }
696
697 /* ensure scrollbar policy for the frame matches the viewport */
698 frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (dialog->priv->description));
699 g_signal_connect_object (frame,
700 "scrollbars-policy-changed",
701 G_CALLBACK (update_scrollbar_policy_cb),
702 dialog, 0);
703 update_scrollbar_policy_cb (frame, dialog);
704 #else
705 const char *str;
706 str = rhythmdb_entry_get_string (dialog->priv->current_entry, RHYTHMDB_PROP_DESCRIPTION);
707 gtk_label_set_text (GTK_LABEL (dialog->priv->description), str);
708 #endif
709 }
710
711 static char *
712 rb_podcast_properties_dialog_parse_time (gulong value)
713 {
714 char *str;
715
716 if (0 == value) {
717 str = g_strdup (_("Unknown"));
718 } else {
719 str = rb_utf_friendly_time ((time_t)value);
720 }
721
722 return str;
723 }