hythmbox-2.98/podcast/rb-podcast-properties-dialog.c

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 	"&lt;",
132 	"&gt;",
133 	"&amp;",
134 	"&quo;",
135 	"&#8",
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 }