libanjuta: Avoid a critical warning when an anjuta window is closed quite fast
[anjuta.git] / libanjuta / anjuta-status.c
blobd0a266f62c4797ad005637bf615c74d50815c363
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * anjuta-status.c
4 * Copyright (C) 2004 Naba Kumar <naba@gnome.org>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc., 59
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 /**
22 * SECTION:anjuta-status
23 * @short_description: Program status such as status message, progress etc.
24 * @see_also:
25 * @stability: Unstable
26 * @include: libanjuta/anjuta-status.h
30 #include <config.h>
31 #include <gtk/gtk.h>
32 #include <libanjuta/anjuta-status.h>
33 #include <libanjuta/anjuta-utils.h>
34 #include <libanjuta/anjuta-debug.h>
35 #include <libanjuta/resources.h>
36 #include <libanjuta/e-splash.h>
38 struct _AnjutaStatusPriv
40 GHashTable *default_status_items;
41 gint busy_count;
42 GHashTable *widgets;
44 /* Status bar */
45 GtkWidget *status_bar;
46 guint status_message;
47 guint push_message;
48 GList *push_values;
50 /* Progress bar */
51 GtkWidget *progress_bar;
52 gint total_ticks;
53 gint current_ticks;
55 /* Splash */
56 GtkWidget *splash;
57 gboolean disable_splash;
58 gchar *splash_file;
59 gint splash_progress_position;
61 /* Title window */
62 GtkWindow *window;
65 enum {
66 BUSY,
67 LAST_SIGNAL
70 static gpointer parent_class = NULL;
71 static guint status_signals[LAST_SIGNAL] = { 0 };
73 static void on_widget_destroy (AnjutaStatus *status, GObject *widget);
75 static void
76 anjuta_status_finalize (GObject *widget)
78 g_free(ANJUTA_STATUS(widget)->priv);
79 G_OBJECT_CLASS (parent_class)->finalize (widget);
82 static void
83 foreach_widget_unref (gpointer key, gpointer value, gpointer data)
85 g_object_weak_unref (G_OBJECT (key), (GWeakNotify) on_widget_destroy, data);
88 static void
89 anjuta_status_dispose (GObject *widget)
91 AnjutaStatus *status;
93 status = ANJUTA_STATUS (widget);
95 if (status->priv->default_status_items)
97 g_hash_table_destroy (status->priv->default_status_items);
98 status->priv->default_status_items = NULL;
100 if (status->priv->splash != NULL) {
101 gtk_widget_destroy (status->priv->splash);
102 status->priv->splash = NULL;
104 if (status->priv->splash_file)
106 g_free (status->priv->splash_file);
107 status->priv->splash_file = NULL;
109 if (status->priv->push_values)
111 g_list_free (status->priv->push_values);
112 status->priv->push_values = NULL;
114 if (status->priv->widgets)
116 g_hash_table_foreach (status->priv->widgets,
117 foreach_widget_unref, widget);
118 g_hash_table_destroy (status->priv->widgets);
119 status->priv->widgets = NULL;
121 if (status->priv->window)
123 g_object_remove_weak_pointer (G_OBJECT (status->priv->window),
124 (gpointer*)(gpointer)&status->priv->window);
125 status->priv->window = NULL;
127 if (status->priv->progress_bar)
129 g_object_remove_weak_pointer (G_OBJECT (status->priv->progress_bar),
130 (gpointer)&status->priv->progress_bar);
131 gtk_widget_destroy (status->priv->progress_bar);
132 status->priv->progress_bar = NULL;
134 if (status->priv->status_bar)
136 g_object_remove_weak_pointer (G_OBJECT (status->priv->status_bar),
137 (gpointer)&status->priv->status_bar);
138 gtk_widget_destroy (status->priv->status_bar);
139 status->priv->status_bar = NULL;
142 G_OBJECT_CLASS (parent_class)->dispose (widget);
145 static void
146 anjuta_status_instance_init (AnjutaStatus *status)
148 status->priv = g_new0 (AnjutaStatusPriv, 1);
149 status->priv->progress_bar = gtk_progress_bar_new ();
150 gtk_box_pack_start (GTK_BOX (status), status->priv->progress_bar, FALSE, TRUE, 0);
151 gtk_widget_show (status->priv->progress_bar);
152 g_object_add_weak_pointer (G_OBJECT (status->priv->progress_bar),
153 (gpointer)&status->priv->progress_bar);
154 status->priv->status_bar = gtk_statusbar_new ();
155 gtk_box_pack_start (GTK_BOX (status), status->priv->status_bar, TRUE, TRUE, 0);
156 gtk_widget_show (status->priv->status_bar);
157 g_object_add_weak_pointer (G_OBJECT(status->priv->status_bar),
158 (gpointer)&status->priv->status_bar);
159 status->priv->status_message = gtk_statusbar_get_context_id (GTK_STATUSBAR (status->priv->status_bar),
160 "status-message");
161 status->priv->push_message = gtk_statusbar_get_context_id (GTK_STATUSBAR (status->priv->status_bar),
162 "push-message");
163 status->priv->push_values = NULL;
164 status->priv->splash_file = NULL;
165 status->priv->splash_progress_position = 0;
166 status->priv->disable_splash = FALSE;
167 status->priv->total_ticks = 0;
168 status->priv->current_ticks = 0;
169 status->priv->splash = NULL;
170 status->priv->default_status_items =
171 g_hash_table_new_full (g_str_hash, g_str_equal,
172 g_free, g_free);
175 static void
176 anjuta_status_class_init (AnjutaStatusClass *class)
178 GObjectClass *object_class;
180 parent_class = g_type_class_peek_parent (class);
181 object_class = (GObjectClass*) class;
182 object_class->finalize = anjuta_status_finalize;
183 object_class->dispose = anjuta_status_dispose;
185 status_signals[BUSY] =
186 g_signal_new ("busy",
187 ANJUTA_TYPE_STATUS,
188 G_SIGNAL_RUN_LAST,
189 G_STRUCT_OFFSET (AnjutaStatusClass, busy),
190 NULL, NULL,
191 g_cclosure_marshal_VOID__BOOLEAN,
192 G_TYPE_NONE, 1,
193 G_TYPE_BOOLEAN);
196 GtkWidget *
197 anjuta_status_new (void)
199 GtkWidget *status;
201 status = GTK_WIDGET (g_object_new (ANJUTA_TYPE_STATUS, NULL));
202 return status;
205 void
206 anjuta_status_set (AnjutaStatus *status, const gchar * mesg, ...)
208 gchar* message;
209 va_list args;
211 g_return_if_fail (ANJUTA_IS_STATUS (status));
212 g_return_if_fail (mesg != NULL);
214 va_start (args, mesg);
215 message = g_strdup_vprintf (mesg, args);
216 va_end (args);
217 gtk_statusbar_pop (GTK_STATUSBAR (status->priv->status_bar),
218 status->priv->status_message);
219 gtk_statusbar_push (GTK_STATUSBAR (status->priv->status_bar),
220 status->priv->status_message, message);
221 g_free(message);
224 void
225 anjuta_status_push (AnjutaStatus *status, const gchar * mesg, ...)
227 gchar* message;
228 va_list args;
229 guint value;
231 g_return_if_fail (ANJUTA_IS_STATUS (status));
232 g_return_if_fail (mesg != NULL);
234 va_start (args, mesg);
235 message = g_strdup_vprintf (mesg, args);
236 va_end (args);
238 value = gtk_statusbar_push (GTK_STATUSBAR (status->priv->status_bar),
239 status->priv->push_message, message);
240 status->priv->push_values = g_list_prepend (status->priv->push_values,
241 GUINT_TO_POINTER (value));
242 g_free(message);
245 void
246 anjuta_status_pop (AnjutaStatus *status)
248 g_return_if_fail (ANJUTA_IS_STATUS (status));
250 /* This can be called on a time out when the status object is destroyed */
251 if (status->priv->status_bar != NULL)
253 gtk_statusbar_pop (GTK_STATUSBAR (status->priv->status_bar),
254 status->priv->push_message);
257 status->priv->push_values = g_list_remove_link (status->priv->push_values,
258 status->priv->push_values);
261 void
262 anjuta_status_clear_stack (AnjutaStatus *status)
264 GList *l;
265 g_return_if_fail (ANJUTA_IS_STATUS (status));
267 for (l = status->priv->push_values; l != NULL; l = g_list_next (l))
269 guint value = GPOINTER_TO_UINT (l->data);
270 gtk_statusbar_remove (GTK_STATUSBAR (status->priv->status_bar),
271 status->priv->push_message, value);
273 g_list_free (status->priv->push_values);
274 status->priv->push_values = NULL;
277 static void
278 foreach_widget_set_cursor (gpointer widget, gpointer value, gpointer cursor)
280 GdkWindow *window;
282 window = gtk_widget_get_window (widget);
283 if (window)
284 gdk_window_set_cursor (window, (GdkCursor*)cursor);
287 void
288 anjuta_status_busy_push (AnjutaStatus *status)
290 GtkWidget *top;
291 GdkCursor *cursor;
292 GdkWindow *window;
294 g_return_if_fail (ANJUTA_IS_STATUS (status));
296 top = gtk_widget_get_toplevel (GTK_WIDGET (status));
297 if (top == NULL)
298 return;
300 status->priv->busy_count++;
302 DEBUG_PRINT ("Busy status: %d", status->priv->busy_count);
304 if (status->priv->busy_count > 1)
305 return;
306 cursor = gdk_cursor_new (GDK_WATCH);
307 window = gtk_widget_get_window (top);
308 if (window)
309 gdk_window_set_cursor (window, cursor);
310 if (status->priv->widgets)
311 g_hash_table_foreach (status->priv->widgets,
312 foreach_widget_set_cursor, cursor);
313 gdk_cursor_unref (cursor);
314 gdk_flush ();
315 g_signal_emit_by_name (G_OBJECT (status), "busy", TRUE);
318 void
319 anjuta_status_busy_pop (AnjutaStatus *status)
321 GtkWidget *top;
322 GdkWindow *window;
324 g_return_if_fail (ANJUTA_IS_STATUS (status));
326 top = gtk_widget_get_toplevel (GTK_WIDGET (status));
327 if (top == NULL)
328 return;
330 status->priv->busy_count--;
331 DEBUG_PRINT ("Busy status: %d", status->priv->busy_count);
333 if (status->priv->busy_count > 0)
334 return;
336 status->priv->busy_count = 0;
337 window = gtk_widget_get_window (top);
338 if (window)
339 gdk_window_set_cursor (window, NULL);
340 if (status->priv->widgets)
341 g_hash_table_foreach (status->priv->widgets,
342 foreach_widget_set_cursor, NULL);
343 g_signal_emit_by_name (G_OBJECT (status), "busy", FALSE);
346 static void
347 foreach_hash (gpointer key, gpointer value, gpointer userdata)
349 GString *str = (GString*)(userdata);
350 const gchar *divider = ": ";
351 const gchar *separator = " ";
353 g_string_append (str, separator);
354 g_string_append (str, (const gchar*)key);
355 g_string_append (str, divider);
356 g_string_append (str, (const gchar*)value);
359 void
360 anjuta_status_set_default (AnjutaStatus *status, const gchar *label,
361 const gchar *value_format, ...)
363 GString *str;
364 gchar *status_str;
366 g_return_if_fail (ANJUTA_IS_STATUS (status));
367 g_return_if_fail (label != NULL);
369 if (value_format)
371 gchar* value;
372 va_list args;
374 va_start (args, value_format);
375 value = g_strdup_vprintf (value_format, args);
376 va_end (args);
377 g_hash_table_replace (status->priv->default_status_items,
378 g_strdup (label), value);
380 else
382 if (g_hash_table_lookup (status->priv->default_status_items, label))
384 g_hash_table_remove (status->priv->default_status_items, label);
388 /* Update default status */
389 str = g_string_new (NULL);
390 g_hash_table_foreach (status->priv->default_status_items, foreach_hash, str);
391 status_str = g_string_free (str, FALSE);
392 anjuta_status_set (status, status_str, NULL);
393 g_free (status_str);
396 static void
397 on_widget_destroy (AnjutaStatus *status, GObject *widget)
399 if (g_hash_table_lookup (status->priv->widgets, widget))
400 g_hash_table_remove (status->priv->widgets, widget);
403 void
404 anjuta_status_add_widget (AnjutaStatus *status, GtkWidget *widget)
406 g_return_if_fail (ANJUTA_IS_STATUS (status));
407 g_return_if_fail (GTK_IS_WIDGET (widget));
409 if (status->priv->widgets == NULL)
410 status->priv->widgets =
411 g_hash_table_new (g_direct_hash, g_direct_equal);
413 g_hash_table_insert (status->priv->widgets, widget, widget);
414 g_object_weak_ref (G_OBJECT (widget),
415 (GWeakNotify) (on_widget_destroy), status);
418 void
419 anjuta_status_set_splash (AnjutaStatus *status, const gchar *splash_file,
420 gint splash_progress_position)
422 g_return_if_fail (ANJUTA_IS_STATUS (status));
423 g_return_if_fail (splash_file != NULL);
424 g_return_if_fail (splash_progress_position >= 0);
425 if (status->priv->splash_file)
426 g_free (status->priv->splash_file);
427 status->priv->splash_file = g_strdup (splash_file);
428 status->priv->splash_progress_position = splash_progress_position;
431 void
432 anjuta_status_disable_splash (AnjutaStatus *status,
433 gboolean disable_splash)
435 g_return_if_fail (ANJUTA_IS_STATUS (status));
437 status->priv->disable_splash = disable_splash;
438 if (status->priv->splash)
440 gtk_widget_destroy (status->priv->splash);
441 status->priv->splash = NULL;
442 anjuta_status_progress_add_ticks (status, 0);
446 void
447 anjuta_status_progress_add_ticks (AnjutaStatus *status, gint ticks)
449 gfloat percentage;
451 g_return_if_fail (ANJUTA_IS_STATUS (status));
452 g_return_if_fail (ticks >= 0);
454 status->priv->total_ticks += ticks;
455 if (!gtk_widget_get_realized (GTK_WIDGET (status)))
457 if (status->priv->splash == NULL &&
458 status->priv->splash_file &&
459 !status->priv->disable_splash)
461 status->priv->splash = e_splash_new (status->priv->splash_file, 100);
462 if (status->priv->splash)
463 gtk_widget_show (status->priv->splash);
466 percentage = ((gfloat)status->priv->current_ticks)/status->priv->total_ticks;
467 if (status->priv->splash)
469 e_splash_set (E_SPLASH(status->priv->splash), NULL, NULL, NULL,
470 percentage);
471 while (g_main_context_iteration(NULL, FALSE));
473 else
475 if (status->priv->progress_bar && status->priv->status_bar)
477 GdkWindow *progressbar_window, *statusbar_window;
479 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (status->priv->progress_bar),
480 percentage);
481 gtk_widget_queue_draw (GTK_WIDGET (status->priv->status_bar));
482 gtk_widget_queue_draw (GTK_WIDGET (status->priv->progress_bar));
483 progressbar_window = gtk_widget_get_window (GTK_WIDGET(status->priv->progress_bar));
484 statusbar_window = gtk_widget_get_window (GTK_WIDGET(status->priv->status_bar));
485 if (progressbar_window != NULL)
486 gdk_window_process_updates (progressbar_window, TRUE);
487 if (statusbar_window != NULL)
488 gdk_window_process_updates (statusbar_window, TRUE);
493 void
494 anjuta_status_progress_pulse (AnjutaStatus *status, const gchar *text)
496 GtkProgressBar *progressbar;
497 GtkWidget *statusbar;
498 GdkWindow *progressbar_window, *statusbar_window;
500 progressbar = GTK_PROGRESS_BAR (status->priv->progress_bar);
501 statusbar = status->priv->status_bar;
503 if (text)
504 anjuta_status_push (status, "%s", text);
506 gtk_progress_bar_pulse (progressbar);
508 gtk_widget_queue_draw (GTK_WIDGET (statusbar));
509 gtk_widget_queue_draw (GTK_WIDGET (progressbar));
510 progressbar_window = gtk_widget_get_window (GTK_WIDGET (progressbar));
511 if (progressbar_window != NULL)
512 gdk_window_process_updates (progressbar_window, TRUE);
513 statusbar_window = gtk_widget_get_window (GTK_WIDGET (statusbar));
514 if (statusbar_window != NULL)
515 gdk_window_process_updates (statusbar_window, TRUE);
518 void
519 anjuta_status_progress_tick (AnjutaStatus *status,
520 GdkPixbuf *icon, const gchar *text)
522 gfloat percentage;
524 g_return_if_fail (ANJUTA_IS_STATUS (status));
525 g_return_if_fail (status->priv->total_ticks != 0);
527 status->priv->current_ticks++;
528 percentage = ((gfloat)status->priv->current_ticks)/status->priv->total_ticks;
530 if (status->priv->splash)
532 e_splash_set (E_SPLASH(status->priv->splash), icon, text, NULL, percentage);
534 else
536 GtkProgressBar *progressbar;
537 GtkWidget *statusbar;
538 GdkWindow *progressbar_window, *statusbar_window;
540 if (text)
541 anjuta_status_set (status, "%s", text);
542 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (status->priv->progress_bar),
543 percentage);
544 progressbar = GTK_PROGRESS_BAR (status->priv->progress_bar);
545 statusbar = status->priv->status_bar;
546 gtk_widget_queue_draw (GTK_WIDGET (statusbar));
547 gtk_widget_queue_draw (GTK_WIDGET (progressbar));
548 progressbar_window = gtk_widget_get_window (GTK_WIDGET (progressbar));
549 if (progressbar_window != NULL)
550 gdk_window_process_updates (progressbar_window, TRUE);
551 statusbar_window = gtk_widget_get_window (GTK_WIDGET (statusbar));
552 if (statusbar_window != NULL)
553 gdk_window_process_updates (statusbar_window, TRUE);
555 if (status->priv->current_ticks >= status->priv->total_ticks)
556 anjuta_status_progress_reset (status);
559 void
560 anjuta_status_progress_increment_ticks (AnjutaStatus *status, gint ticks,
561 const gchar *text)
563 gfloat percentage;
564 GdkWindow *progressbar_window, *statusbar_window;
566 g_return_if_fail (ANJUTA_IS_STATUS (status));
567 g_return_if_fail (status->priv->total_ticks != 0);
569 status->priv->current_ticks += ticks;
570 percentage = ((gfloat)status->priv->current_ticks)/status->priv->total_ticks;
572 GtkProgressBar *progressbar;
573 GtkWidget *statusbar;
575 if (text)
576 anjuta_status_set (status, "%s", text);
577 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (status->priv->progress_bar),
578 percentage);
579 progressbar = GTK_PROGRESS_BAR (status->priv->progress_bar);
580 statusbar = status->priv->status_bar;
581 gtk_widget_queue_draw (GTK_WIDGET (statusbar));
582 gtk_widget_queue_draw (GTK_WIDGET (progressbar));
583 progressbar_window = gtk_widget_get_window (GTK_WIDGET(progressbar));
584 if (progressbar_window != NULL)
585 gdk_window_process_updates (progressbar_window, TRUE);
586 statusbar_window = gtk_widget_get_window (GTK_WIDGET(statusbar));
587 if (statusbar_window != NULL)
588 gdk_window_process_updates (statusbar_window, TRUE);
590 if (status->priv->current_ticks >= status->priv->total_ticks)
591 anjuta_status_progress_reset (status);
594 void
595 anjuta_status_progress_reset (AnjutaStatus *status)
597 g_return_if_fail (ANJUTA_IS_STATUS (status));
599 if (status->priv->splash)
601 gtk_widget_destroy (status->priv->splash);
602 status->priv->splash = NULL;
604 status->priv->current_ticks = 0;
605 status->priv->total_ticks = 0;
606 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (status->priv->progress_bar), 0);
607 anjuta_status_clear_stack (status);
610 static gboolean
611 anjuta_status_timeout (AnjutaStatus *status)
613 anjuta_status_pop (status);
614 g_object_unref (status);
616 return FALSE;
620 * anjuta_status: (skip)
621 * Display message in status until timeout (seconds)
623 void
624 anjuta_status (AnjutaStatus *status, const gchar *mesg, gint timeout)
626 g_return_if_fail (ANJUTA_IS_STATUS (status));
627 g_return_if_fail (mesg != NULL);
628 anjuta_status_push (status, "%s", mesg);
629 g_timeout_add_seconds (timeout, (void*) anjuta_status_timeout, g_object_ref (status));
632 void
633 anjuta_status_set_title_window (AnjutaStatus *status, GtkWidget *window)
635 g_return_if_fail (ANJUTA_IS_STATUS (status));
636 g_return_if_fail (GTK_IS_WINDOW (window));
637 status->priv->window = GTK_WINDOW (window);
638 g_object_add_weak_pointer (G_OBJECT (window),
639 (gpointer*)(gpointer)&status->priv->window);
642 void
643 anjuta_status_set_title (AnjutaStatus *status, const gchar *title)
645 g_return_if_fail (ANJUTA_IS_STATUS (status));
647 if (!status->priv->window)
648 return;
650 const gchar *app_name = g_get_application_name();
651 if (title)
653 gchar* str = g_strconcat (title, " - ", app_name, NULL);
654 gtk_window_set_title (status->priv->window, str);
655 g_free (str);
657 else
659 gtk_window_set_title (status->priv->window, app_name);
663 ANJUTA_TYPE_BEGIN(AnjutaStatus, anjuta_status, GTK_TYPE_HBOX);
664 ANJUTA_TYPE_END;