2006-12-14 Francisco Javier F. Serrador <serrador@openshine.com>
[rhythmbox.git] / shell / rb-statusbar.c
blobe83ae0d7edbbd9bf19f1535c49378dfb1462a4e7
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
3 * arch-tag: Implementation of status display widget
5 * Copyright (C) 2003 Jorn Baayen <jorn@nl.linux.org>
6 * Copyright (C) 2003,2004 Colin Walters <walters@redhat.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "config.h"
26 #include <unistd.h>
27 #include <stdlib.h>
28 #include <string.h>
30 #include <glib/gi18n.h>
31 #include <gtk/gtk.h>
33 #include "rb-statusbar.h"
34 #include "rb-debug.h"
36 #define EPSILON (0.00001)
38 static GObject* rb_statusbar_construct (GType type,
39 guint n_construct_properties,
40 GObjectConstructParam *construct_properties);
41 static void rb_statusbar_class_init (RBStatusbarClass *klass);
42 static void rb_statusbar_init (RBStatusbar *statusbar);
43 static void rb_statusbar_dispose (GObject *object);
44 static void rb_statusbar_finalize (GObject *object);
45 static void rb_statusbar_set_property (GObject *object,
46 guint prop_id,
47 const GValue *value,
48 GParamSpec *pspec);
49 static void rb_statusbar_get_property (GObject *object,
50 guint prop_id,
51 GValue *value,
52 GParamSpec *pspec);
54 static gboolean poll_status (RBStatusbar *status);
55 static void rb_statusbar_sync_status (RBStatusbar *status);
56 static void rb_statusbar_source_status_changed_cb (RBSource *source,
57 RBStatusbar *statusbar);
59 struct RBStatusbarPrivate
61 RBSource *selected_source;
63 RhythmDB *db;
65 GtkUIManager *ui_manager;
66 GtkTooltips *tooltips;
68 GtkWidget *progress;
69 double progress_fraction;
70 gboolean progress_changed;
71 gchar *progress_text;
73 gchar *loading_text;
75 guint status_poll_id;
78 enum
80 PROP_0,
81 PROP_DB,
82 PROP_UI_MANAGER,
83 PROP_SOURCE
86 G_DEFINE_TYPE (RBStatusbar, rb_statusbar, GTK_TYPE_STATUSBAR)
88 static void
89 rb_statusbar_class_init (RBStatusbarClass *klass)
91 GObjectClass *object_class = G_OBJECT_CLASS (klass);
93 object_class->constructor = rb_statusbar_construct;
94 object_class->dispose = rb_statusbar_dispose;
95 object_class->finalize = rb_statusbar_finalize;
97 object_class->set_property = rb_statusbar_set_property;
98 object_class->get_property = rb_statusbar_get_property;
100 g_object_class_install_property (object_class,
101 PROP_DB,
102 g_param_spec_object ("db",
103 "RhythmDB",
104 "RhythmDB object",
105 RHYTHMDB_TYPE,
106 G_PARAM_READWRITE));
107 g_object_class_install_property (object_class,
108 PROP_SOURCE,
109 g_param_spec_object ("source",
110 "RBSource",
111 "RBSource object",
112 RB_TYPE_SOURCE,
113 G_PARAM_READWRITE));
114 g_object_class_install_property (object_class,
115 PROP_UI_MANAGER,
116 g_param_spec_object ("ui-manager",
117 "GtkUIManager",
118 "GtkUIManager object",
119 GTK_TYPE_UI_MANAGER,
120 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
122 g_type_class_add_private (klass, sizeof (RBStatusbarPrivate));
125 static GObject*
126 rb_statusbar_construct (GType type,
127 guint n_construct_properties,
128 GObjectConstructParam *construct_properties)
130 RBStatusbarClass *klass;
131 GObject *object;
132 RBStatusbar *statusbar;
134 klass = RB_STATUSBAR_CLASS (g_type_class_peek (RB_TYPE_STATUSBAR));
135 object = G_OBJECT_CLASS (rb_statusbar_parent_class)->constructor
136 (type,
137 n_construct_properties,
138 construct_properties);
140 statusbar = RB_STATUSBAR (object);
142 return object;
145 static void
146 rb_statusbar_init (RBStatusbar *statusbar)
148 statusbar->priv = G_TYPE_INSTANCE_GET_PRIVATE (statusbar,
149 RB_TYPE_STATUSBAR,
150 RBStatusbarPrivate);
152 statusbar->priv->tooltips = gtk_tooltips_new ();
153 gtk_tooltips_enable (statusbar->priv->tooltips);
155 gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (statusbar), TRUE);
157 statusbar->priv->progress = gtk_progress_bar_new ();
158 statusbar->priv->progress_fraction = 1.0;
160 statusbar->priv->loading_text = g_strdup (_("Loading..."));
162 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (statusbar->priv->progress), 1.0);
163 gtk_widget_hide (statusbar->priv->progress);
165 gtk_box_pack_start (GTK_BOX (statusbar),
166 GTK_WIDGET (statusbar->priv->progress), FALSE, TRUE, 0);
169 static void
170 rb_statusbar_dispose (GObject *object)
172 RBStatusbar *statusbar;
174 g_return_if_fail (object != NULL);
175 g_return_if_fail (RB_IS_STATUSBAR (object));
177 statusbar = RB_STATUSBAR (object);
179 g_return_if_fail (statusbar->priv != NULL);
181 if (statusbar->priv->status_poll_id) {
182 g_source_remove (statusbar->priv->status_poll_id);
183 statusbar->priv->status_poll_id = 0;
186 if (statusbar->priv->db != NULL) {
187 g_object_unref (statusbar->priv->db);
188 statusbar->priv->db = NULL;
191 if (statusbar->priv->ui_manager != NULL) {
192 g_object_unref (statusbar->priv->ui_manager);
193 statusbar->priv->ui_manager = NULL;
196 if (statusbar->priv->selected_source != NULL) {
197 g_object_unref (statusbar->priv->selected_source);
198 statusbar->priv->selected_source = NULL;
201 G_OBJECT_CLASS (rb_statusbar_parent_class)->dispose (object);
204 static void
205 rb_statusbar_finalize (GObject *object)
207 RBStatusbar *statusbar;
209 g_return_if_fail (object != NULL);
210 g_return_if_fail (RB_IS_STATUSBAR (object));
212 statusbar = RB_STATUSBAR (object);
214 g_return_if_fail (statusbar->priv != NULL);
216 g_free (statusbar->priv->loading_text);
217 g_free (statusbar->priv->progress_text);
219 G_OBJECT_CLASS (rb_statusbar_parent_class)->finalize (object);
222 typedef struct {
223 GtkWidget *statusbar;
224 char *tooltip;
225 } StatusTip;
227 static void
228 statustip_free (StatusTip *tip)
230 g_object_unref (tip->statusbar);
231 g_free (tip->tooltip);
232 g_free (tip);
235 static void
236 set_statusbar_tooltip (GtkWidget *widget,
237 StatusTip *data)
239 guint context_id;
241 context_id = gtk_statusbar_get_context_id (GTK_STATUSBAR (data->statusbar),
242 "rb_statusbar_tooltip");
243 gtk_statusbar_push (GTK_STATUSBAR (data->statusbar),
244 context_id,
245 data->tooltip ? data->tooltip: "");
248 static void
249 unset_statusbar_tooltip (GtkWidget *widget,
250 GtkWidget *statusbar)
252 guint context_id;
254 context_id = gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar),
255 "rb_statusbar_tooltip");
256 gtk_statusbar_pop (GTK_STATUSBAR (statusbar), context_id);
259 static void
260 rb_statusbar_connect_ui_manager (RBStatusbar *statusbar,
261 GtkAction *action,
262 GtkWidget *proxy,
263 GtkUIManager *ui_manager)
265 char *tooltip;
267 if (! GTK_IS_MENU_ITEM (proxy))
268 return;
270 g_object_get (action, "tooltip", &tooltip, NULL);
272 if (tooltip) {
273 StatusTip *statustip;
275 statustip = g_new (StatusTip, 1);
276 statustip->statusbar = g_object_ref (statusbar);
277 statustip->tooltip = tooltip;
278 g_signal_connect_data (proxy, "select",
279 G_CALLBACK (set_statusbar_tooltip),
280 statustip, (GClosureNotify)statustip_free, 0);
282 g_signal_connect (proxy, "deselect",
283 G_CALLBACK (unset_statusbar_tooltip),
284 statusbar);
288 static void
289 rb_statusbar_set_property (GObject *object,
290 guint prop_id,
291 const GValue *value,
292 GParamSpec *pspec)
294 RBStatusbar *statusbar = RB_STATUSBAR (object);
296 switch (prop_id)
298 case PROP_DB:
299 statusbar->priv->db = g_value_get_object (value);
300 g_object_ref (statusbar->priv->db);
301 statusbar->priv->status_poll_id
302 = g_idle_add ((GSourceFunc) poll_status, statusbar);
303 break;
304 case PROP_SOURCE:
305 if (statusbar->priv->selected_source != NULL) {
306 g_signal_handlers_disconnect_by_func (G_OBJECT (statusbar->priv->selected_source),
307 G_CALLBACK (rb_statusbar_source_status_changed_cb),
308 statusbar);
309 g_object_unref (statusbar->priv->selected_source);
312 statusbar->priv->selected_source = g_value_get_object (value);
313 rb_debug ("selected source %p", statusbar->priv->selected_source);
314 g_object_ref (statusbar->priv->selected_source);
316 if (statusbar->priv->selected_source != NULL) {
317 g_signal_connect_object (G_OBJECT (statusbar->priv->selected_source),
318 "status-changed",
319 G_CALLBACK (rb_statusbar_source_status_changed_cb),
320 statusbar, 0);
322 rb_statusbar_sync_status (statusbar);
324 break;
325 case PROP_UI_MANAGER:
326 if (statusbar->priv->ui_manager) {
327 g_signal_handlers_disconnect_by_func (G_OBJECT (statusbar->priv->ui_manager),
328 G_CALLBACK (rb_statusbar_connect_ui_manager),
329 statusbar);
330 g_object_unref (statusbar->priv->ui_manager);
332 statusbar->priv->ui_manager = g_value_get_object (value);
333 g_object_ref (statusbar->priv->ui_manager);
335 g_signal_connect_object (statusbar->priv->ui_manager,
336 "connect-proxy",
337 G_CALLBACK (rb_statusbar_connect_ui_manager),
338 statusbar,
339 G_CONNECT_SWAPPED);
340 break;
341 default:
342 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
343 break;
347 static void
348 rb_statusbar_get_property (GObject *object,
349 guint prop_id,
350 GValue *value,
351 GParamSpec *pspec)
353 RBStatusbar *statusbar = RB_STATUSBAR (object);
355 switch (prop_id)
357 case PROP_DB:
358 g_value_set_object (value, statusbar->priv->db);
359 break;
360 case PROP_SOURCE:
361 g_value_set_object (value, statusbar->priv->selected_source);
362 break;
363 case PROP_UI_MANAGER:
364 g_value_set_object (value, statusbar->priv->ui_manager);
365 break;
366 default:
367 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
368 break;
372 void
373 rb_statusbar_set_source (RBStatusbar *statusbar,
374 RBSource *source)
376 g_return_if_fail (RB_IS_STATUSBAR (statusbar));
377 g_return_if_fail (RB_IS_SOURCE (source));
379 g_object_set (G_OBJECT (statusbar),
380 "source", source,
381 NULL);
384 static gboolean
385 poll_status (RBStatusbar *status)
387 GDK_THREADS_ENTER ();
389 status->priv->status_poll_id = 0;
390 rb_statusbar_sync_status (status);
392 GDK_THREADS_LEAVE ();
394 return FALSE;
397 static void
398 rb_statusbar_sync_status (RBStatusbar *status)
400 gboolean changed = FALSE;
401 gboolean show_progress = TRUE;
402 char *status_text = NULL;
403 char *progress_text = NULL;
404 float progress = 0.0f;
407 * Behaviour of status bar:
408 * - use source's status text
409 * - use source's progress value and text, unless internal progress value is set,
410 * or the library is busy
413 /* get source details */
414 if (status->priv->selected_source)
415 rb_source_get_status (status->priv->selected_source, &status_text, &progress_text, &progress);
417 /* internal progress bar moving? */
418 if (status->priv->progress_fraction < (1.0 - EPSILON) || status->priv->progress_changed) {
419 progress = status->priv->progress_fraction;
420 progress_text = g_strdup (status->priv->progress_text);
421 status->priv->progress_changed = FALSE;
422 show_progress = TRUE;
423 changed = TRUE;
426 /* library busy? */
427 if (rhythmdb_is_busy (status->priv->db)) {
428 /*g_free (status_text);
429 status_text = g_strdup (status->priv->loading_text); */
430 progress = -1.0f;
431 changed = TRUE;
434 /* set up the status text */
435 if (status_text) {
436 gtk_statusbar_pop (GTK_STATUSBAR (status), 0);
437 gtk_statusbar_push (GTK_STATUSBAR (status), 0, status_text);
438 g_free (status_text);
441 /* set up the progress bar */
442 if (progress > (1.0f - EPSILON) && !show_progress) {
443 gtk_widget_hide (status->priv->progress);
444 } else {
445 gtk_widget_show (status->priv->progress);
447 if (progress < (0.0f - EPSILON)) {
448 gtk_progress_bar_pulse (GTK_PROGRESS_BAR (status->priv->progress));
449 changed = TRUE;
450 } else {
451 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (status->priv->progress),
452 progress);
454 gtk_progress_bar_set_text (GTK_PROGRESS_BAR (status->priv->progress),
455 progress_text);
458 g_free (progress_text);
460 if (status->priv->status_poll_id == 0 && changed)
461 status->priv->status_poll_id = g_timeout_add (250, (GSourceFunc) poll_status, status);
464 RBStatusbar *
465 rb_statusbar_new (RhythmDB *db,
466 GtkUIManager *ui_manager)
468 RBStatusbar *statusbar = g_object_new (RB_TYPE_STATUSBAR,
469 "db", db,
470 "ui-manager", ui_manager,
471 NULL);
473 g_return_val_if_fail (statusbar->priv != NULL, NULL);
475 return statusbar;
478 void
479 rb_statusbar_set_progress (RBStatusbar *statusbar, double progress, const char *text)
481 if (statusbar->priv->progress_text) {
482 g_free (statusbar->priv->progress_text);
483 statusbar->priv->progress_text = NULL;
486 if (progress > (0.0 - EPSILON)) {
487 statusbar->priv->progress_fraction = progress;
488 statusbar->priv->progress_changed = TRUE;
489 if (text)
490 statusbar->priv->progress_text = g_strdup (text);
491 } else {
492 /* trick sync_status into hiding it */
493 statusbar->priv->progress_fraction = 1.0;
494 statusbar->priv->progress_changed = FALSE;
496 rb_statusbar_sync_status (statusbar);
499 static void
500 rb_statusbar_source_status_changed_cb (RBSource *source, RBStatusbar *statusbar)
502 rb_debug ("source status changed");
503 if (statusbar->priv->status_poll_id == 0)
504 statusbar->priv->status_poll_id =
505 g_idle_add ((GSourceFunc) poll_status, statusbar);