2006-12-03 James Livingston <doclivingston@gmail.com>
[rhythmbox.git] / shell / rb-statusbar.c
blob5cdd7642956561b6b32c306026379ef4f72286d5
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 GtkWidget *vbox;
154 statusbar->priv->tooltips = gtk_tooltips_new ();
155 gtk_tooltips_enable (statusbar->priv->tooltips);
157 gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (statusbar), TRUE);
159 vbox = gtk_vbox_new (TRUE, 0);
161 statusbar->priv->progress = gtk_progress_bar_new ();
162 statusbar->priv->progress_fraction = 1.0;
164 statusbar->priv->loading_text = g_strdup (_("Loading..."));
166 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (statusbar->priv->progress), 1.0);
167 gtk_widget_set_size_request (statusbar->priv->progress, -1, 10);
168 gtk_widget_hide (statusbar->priv->progress);
170 gtk_box_pack_start (GTK_BOX (vbox),
171 GTK_WIDGET (statusbar->priv->progress),
172 FALSE, FALSE, 0);
174 gtk_box_pack_start (GTK_BOX (statusbar),
175 GTK_WIDGET (vbox), FALSE, TRUE, 10);
179 static void
180 rb_statusbar_dispose (GObject *object)
182 RBStatusbar *statusbar;
184 g_return_if_fail (object != NULL);
185 g_return_if_fail (RB_IS_STATUSBAR (object));
187 statusbar = RB_STATUSBAR (object);
189 g_return_if_fail (statusbar->priv != NULL);
191 if (statusbar->priv->status_poll_id) {
192 g_source_remove (statusbar->priv->status_poll_id);
193 statusbar->priv->status_poll_id = 0;
196 if (statusbar->priv->db != NULL) {
197 g_object_unref (statusbar->priv->db);
198 statusbar->priv->db = NULL;
201 if (statusbar->priv->ui_manager != NULL) {
202 g_object_unref (statusbar->priv->ui_manager);
203 statusbar->priv->ui_manager = NULL;
206 if (statusbar->priv->selected_source != NULL) {
207 g_object_unref (statusbar->priv->selected_source);
208 statusbar->priv->selected_source = NULL;
211 G_OBJECT_CLASS (rb_statusbar_parent_class)->dispose (object);
214 static void
215 rb_statusbar_finalize (GObject *object)
217 RBStatusbar *statusbar;
219 g_return_if_fail (object != NULL);
220 g_return_if_fail (RB_IS_STATUSBAR (object));
222 statusbar = RB_STATUSBAR (object);
224 g_return_if_fail (statusbar->priv != NULL);
226 g_free (statusbar->priv->loading_text);
227 g_free (statusbar->priv->progress_text);
229 G_OBJECT_CLASS (rb_statusbar_parent_class)->finalize (object);
232 typedef struct {
233 GtkWidget *statusbar;
234 char *tooltip;
235 } StatusTip;
237 static void
238 statustip_free (StatusTip *tip)
240 g_object_unref (tip->statusbar);
241 g_free (tip->tooltip);
242 g_free (tip);
245 static void
246 set_statusbar_tooltip (GtkWidget *widget,
247 StatusTip *data)
249 guint context_id;
251 context_id = gtk_statusbar_get_context_id (GTK_STATUSBAR (data->statusbar),
252 "rb_statusbar_tooltip");
253 gtk_statusbar_push (GTK_STATUSBAR (data->statusbar),
254 context_id,
255 data->tooltip ? data->tooltip: "");
258 static void
259 unset_statusbar_tooltip (GtkWidget *widget,
260 GtkWidget *statusbar)
262 guint context_id;
264 context_id = gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar),
265 "rb_statusbar_tooltip");
266 gtk_statusbar_pop (GTK_STATUSBAR (statusbar), context_id);
269 static void
270 rb_statusbar_connect_ui_manager (RBStatusbar *statusbar,
271 GtkAction *action,
272 GtkWidget *proxy,
273 GtkUIManager *ui_manager)
275 char *tooltip;
277 if (! GTK_IS_MENU_ITEM (proxy))
278 return;
280 g_object_get (action, "tooltip", &tooltip, NULL);
282 if (tooltip) {
283 StatusTip *statustip;
285 statustip = g_new (StatusTip, 1);
286 statustip->statusbar = g_object_ref (statusbar);
287 statustip->tooltip = tooltip;
288 g_signal_connect_data (proxy, "select",
289 G_CALLBACK (set_statusbar_tooltip),
290 statustip, (GClosureNotify)statustip_free, 0);
292 g_signal_connect (proxy, "deselect",
293 G_CALLBACK (unset_statusbar_tooltip),
294 statusbar);
298 static void
299 rb_statusbar_set_property (GObject *object,
300 guint prop_id,
301 const GValue *value,
302 GParamSpec *pspec)
304 RBStatusbar *statusbar = RB_STATUSBAR (object);
306 switch (prop_id)
308 case PROP_DB:
309 statusbar->priv->db = g_value_get_object (value);
310 g_object_ref (statusbar->priv->db);
311 statusbar->priv->status_poll_id
312 = g_idle_add ((GSourceFunc) poll_status, statusbar);
313 break;
314 case PROP_SOURCE:
315 if (statusbar->priv->selected_source != NULL) {
316 g_signal_handlers_disconnect_by_func (G_OBJECT (statusbar->priv->selected_source),
317 G_CALLBACK (rb_statusbar_source_status_changed_cb),
318 statusbar);
319 g_object_unref (statusbar->priv->selected_source);
322 statusbar->priv->selected_source = g_value_get_object (value);
323 rb_debug ("selected source %p", statusbar->priv->selected_source);
324 g_object_ref (statusbar->priv->selected_source);
326 if (statusbar->priv->selected_source != NULL) {
327 g_signal_connect_object (G_OBJECT (statusbar->priv->selected_source),
328 "status-changed",
329 G_CALLBACK (rb_statusbar_source_status_changed_cb),
330 statusbar, 0);
332 rb_statusbar_sync_status (statusbar);
334 break;
335 case PROP_UI_MANAGER:
336 if (statusbar->priv->ui_manager) {
337 g_signal_handlers_disconnect_by_func (G_OBJECT (statusbar->priv->ui_manager),
338 G_CALLBACK (rb_statusbar_connect_ui_manager),
339 statusbar);
340 g_object_unref (statusbar->priv->ui_manager);
342 statusbar->priv->ui_manager = g_value_get_object (value);
343 g_object_ref (statusbar->priv->ui_manager);
345 g_signal_connect_object (statusbar->priv->ui_manager,
346 "connect-proxy",
347 G_CALLBACK (rb_statusbar_connect_ui_manager),
348 statusbar,
349 G_CONNECT_SWAPPED);
350 break;
351 default:
352 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
353 break;
357 static void
358 rb_statusbar_get_property (GObject *object,
359 guint prop_id,
360 GValue *value,
361 GParamSpec *pspec)
363 RBStatusbar *statusbar = RB_STATUSBAR (object);
365 switch (prop_id)
367 case PROP_DB:
368 g_value_set_object (value, statusbar->priv->db);
369 break;
370 case PROP_SOURCE:
371 g_value_set_object (value, statusbar->priv->selected_source);
372 break;
373 case PROP_UI_MANAGER:
374 g_value_set_object (value, statusbar->priv->ui_manager);
375 break;
376 default:
377 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
378 break;
382 void
383 rb_statusbar_set_source (RBStatusbar *statusbar,
384 RBSource *source)
386 g_return_if_fail (RB_IS_STATUSBAR (statusbar));
387 g_return_if_fail (RB_IS_SOURCE (source));
389 g_object_set (G_OBJECT (statusbar),
390 "source", source,
391 NULL);
394 static gboolean
395 poll_status (RBStatusbar *status)
397 GDK_THREADS_ENTER ();
399 status->priv->status_poll_id = 0;
400 rb_statusbar_sync_status (status);
402 GDK_THREADS_LEAVE ();
404 return FALSE;
407 static void
408 rb_statusbar_sync_status (RBStatusbar *status)
410 gboolean changed = FALSE;
411 gboolean show_progress = TRUE;
412 char *status_text = NULL;
413 char *progress_text = NULL;
414 float progress = 0.0f;
417 * Behaviour of status bar:
418 * - use source's status text
419 * - use source's progress value and text, unless internal progress value is set,
420 * or the library is busy
423 /* get source details */
424 if (status->priv->selected_source)
425 rb_source_get_status (status->priv->selected_source, &status_text, &progress_text, &progress);
427 /* internal progress bar moving? */
428 if (status->priv->progress_fraction < (1.0 - EPSILON) || status->priv->progress_changed) {
429 progress = status->priv->progress_fraction;
430 progress_text = g_strdup (status->priv->progress_text);
431 status->priv->progress_changed = FALSE;
432 show_progress = TRUE;
433 changed = TRUE;
436 /* library busy? */
437 if (rhythmdb_is_busy (status->priv->db)) {
438 /*g_free (status_text);
439 status_text = g_strdup (status->priv->loading_text); */
440 progress = -1.0f;
441 changed = TRUE;
444 /* set up the status text */
445 if (status_text) {
446 gtk_statusbar_pop (GTK_STATUSBAR (status), 0);
447 gtk_statusbar_push (GTK_STATUSBAR (status), 0, status_text);
448 g_free (status_text);
451 /* set up the progress bar */
452 if (progress > (1.0f - EPSILON) && !show_progress) {
453 gtk_widget_hide (status->priv->progress);
454 } else {
455 gtk_widget_show (status->priv->progress);
457 if (progress < (0.0f - EPSILON)) {
458 gtk_progress_bar_pulse (GTK_PROGRESS_BAR (status->priv->progress));
459 changed = TRUE;
460 } else {
461 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (status->priv->progress),
462 progress);
464 gtk_progress_bar_set_text (GTK_PROGRESS_BAR (status->priv->progress),
465 progress_text);
468 g_free (progress_text);
470 if (status->priv->status_poll_id == 0 && changed)
471 status->priv->status_poll_id = g_timeout_add (250, (GSourceFunc) poll_status, status);
474 RBStatusbar *
475 rb_statusbar_new (RhythmDB *db,
476 GtkUIManager *ui_manager)
478 RBStatusbar *statusbar = g_object_new (RB_TYPE_STATUSBAR,
479 "db", db,
480 "ui-manager", ui_manager,
481 NULL);
483 g_return_val_if_fail (statusbar->priv != NULL, NULL);
485 return statusbar;
488 void
489 rb_statusbar_set_progress (RBStatusbar *statusbar, double progress, const char *text)
491 if (statusbar->priv->progress_text) {
492 g_free (statusbar->priv->progress_text);
493 statusbar->priv->progress_text = NULL;
496 if (progress > (0.0 - EPSILON)) {
497 statusbar->priv->progress_fraction = progress;
498 statusbar->priv->progress_changed = TRUE;
499 if (text)
500 statusbar->priv->progress_text = g_strdup (text);
501 } else {
502 /* trick sync_status into hiding it */
503 statusbar->priv->progress_fraction = 1.0;
504 statusbar->priv->progress_changed = FALSE;
506 rb_statusbar_sync_status (statusbar);
509 static void
510 rb_statusbar_source_status_changed_cb (RBSource *source, RBStatusbar *statusbar)
512 rb_debug ("source status changed");
513 if (statusbar->priv->status_poll_id == 0)
514 statusbar->priv->status_poll_id =
515 g_idle_add ((GSourceFunc) poll_status, statusbar);