symbol-db: fixed memory leak on ctags pointed out by Matthew Brush
[anjuta.git] / libanjuta / anjuta-status.c
bloba5ead1e7cac0402f6f42ba39da054a208305cbdd
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 gtk_statusbar_pop (GTK_STATUSBAR (status->priv->status_bar),
251 status->priv->push_message);
253 status->priv->push_values = g_list_remove_link (status->priv->push_values,
254 status->priv->push_values);
257 void
258 anjuta_status_clear_stack (AnjutaStatus *status)
260 GList *l;
261 g_return_if_fail (ANJUTA_IS_STATUS (status));
263 for (l = status->priv->push_values; l != NULL; l = g_list_next (l))
265 guint value = GPOINTER_TO_UINT (l->data);
266 gtk_statusbar_remove (GTK_STATUSBAR (status->priv->status_bar),
267 status->priv->push_message, value);
269 g_list_free (status->priv->push_values);
270 status->priv->push_values = NULL;
273 static void
274 foreach_widget_set_cursor (gpointer widget, gpointer value, gpointer cursor)
276 GdkWindow *window;
278 window = gtk_widget_get_window (widget);
279 if (window)
280 gdk_window_set_cursor (window, (GdkCursor*)cursor);
283 void
284 anjuta_status_busy_push (AnjutaStatus *status)
286 GtkWidget *top;
287 GdkCursor *cursor;
288 GdkWindow *window;
290 g_return_if_fail (ANJUTA_IS_STATUS (status));
292 top = gtk_widget_get_toplevel (GTK_WIDGET (status));
293 if (top == NULL)
294 return;
296 status->priv->busy_count++;
298 DEBUG_PRINT ("Busy status: %d", status->priv->busy_count);
300 if (status->priv->busy_count > 1)
301 return;
302 cursor = gdk_cursor_new (GDK_WATCH);
303 window = gtk_widget_get_window (top);
304 if (window)
305 gdk_window_set_cursor (window, cursor);
306 if (status->priv->widgets)
307 g_hash_table_foreach (status->priv->widgets,
308 foreach_widget_set_cursor, cursor);
309 gdk_cursor_unref (cursor);
310 gdk_flush ();
311 g_signal_emit_by_name (G_OBJECT (status), "busy", TRUE);
314 void
315 anjuta_status_busy_pop (AnjutaStatus *status)
317 GtkWidget *top;
318 GdkWindow *window;
320 g_return_if_fail (ANJUTA_IS_STATUS (status));
322 top = gtk_widget_get_toplevel (GTK_WIDGET (status));
323 if (top == NULL)
324 return;
326 status->priv->busy_count--;
327 DEBUG_PRINT ("Busy status: %d", status->priv->busy_count);
329 if (status->priv->busy_count > 0)
330 return;
332 status->priv->busy_count = 0;
333 window = gtk_widget_get_window (top);
334 if (window)
335 gdk_window_set_cursor (window, NULL);
336 if (status->priv->widgets)
337 g_hash_table_foreach (status->priv->widgets,
338 foreach_widget_set_cursor, NULL);
339 g_signal_emit_by_name (G_OBJECT (status), "busy", FALSE);
342 static void
343 foreach_hash (gpointer key, gpointer value, gpointer userdata)
345 GString *str = (GString*)(userdata);
346 const gchar *divider = ": ";
347 const gchar *separator = " ";
349 g_string_append (str, separator);
350 g_string_append (str, (const gchar*)key);
351 g_string_append (str, divider);
352 g_string_append (str, (const gchar*)value);
355 void
356 anjuta_status_set_default (AnjutaStatus *status, const gchar *label,
357 const gchar *value_format, ...)
359 GString *str;
360 gchar *status_str;
362 g_return_if_fail (ANJUTA_IS_STATUS (status));
363 g_return_if_fail (label != NULL);
365 if (value_format)
367 gchar* value;
368 va_list args;
370 va_start (args, value_format);
371 value = g_strdup_vprintf (value_format, args);
372 va_end (args);
373 g_hash_table_replace (status->priv->default_status_items,
374 g_strdup (label), value);
376 else
378 if (g_hash_table_lookup (status->priv->default_status_items, label))
380 g_hash_table_remove (status->priv->default_status_items, label);
384 /* Update default status */
385 str = g_string_new (NULL);
386 g_hash_table_foreach (status->priv->default_status_items, foreach_hash, str);
387 status_str = g_string_free (str, FALSE);
388 anjuta_status_set (status, status_str, NULL);
389 g_free (status_str);
392 static void
393 on_widget_destroy (AnjutaStatus *status, GObject *widget)
395 if (g_hash_table_lookup (status->priv->widgets, widget))
396 g_hash_table_remove (status->priv->widgets, widget);
399 void
400 anjuta_status_add_widget (AnjutaStatus *status, GtkWidget *widget)
402 g_return_if_fail (ANJUTA_IS_STATUS (status));
403 g_return_if_fail (GTK_IS_WIDGET (widget));
405 if (status->priv->widgets == NULL)
406 status->priv->widgets =
407 g_hash_table_new (g_direct_hash, g_direct_equal);
409 g_hash_table_insert (status->priv->widgets, widget, widget);
410 g_object_weak_ref (G_OBJECT (widget),
411 (GWeakNotify) (on_widget_destroy), status);
414 void
415 anjuta_status_set_splash (AnjutaStatus *status, const gchar *splash_file,
416 gint splash_progress_position)
418 g_return_if_fail (ANJUTA_IS_STATUS (status));
419 g_return_if_fail (splash_file != NULL);
420 g_return_if_fail (splash_progress_position >= 0);
421 if (status->priv->splash_file)
422 g_free (status->priv->splash_file);
423 status->priv->splash_file = g_strdup (splash_file);
424 status->priv->splash_progress_position = splash_progress_position;
427 void
428 anjuta_status_disable_splash (AnjutaStatus *status,
429 gboolean disable_splash)
431 g_return_if_fail (ANJUTA_IS_STATUS (status));
433 status->priv->disable_splash = disable_splash;
434 if (status->priv->splash)
436 gtk_widget_destroy (status->priv->splash);
437 status->priv->splash = NULL;
438 anjuta_status_progress_add_ticks (status, 0);
442 void
443 anjuta_status_progress_add_ticks (AnjutaStatus *status, gint ticks)
445 gfloat percentage;
447 g_return_if_fail (ANJUTA_IS_STATUS (status));
448 g_return_if_fail (ticks >= 0);
450 status->priv->total_ticks += ticks;
451 if (!gtk_widget_get_realized (GTK_WIDGET (status)))
453 if (status->priv->splash == NULL &&
454 status->priv->splash_file &&
455 !status->priv->disable_splash)
457 status->priv->splash = e_splash_new (status->priv->splash_file, 100);
458 if (status->priv->splash)
459 gtk_widget_show (status->priv->splash);
462 percentage = ((gfloat)status->priv->current_ticks)/status->priv->total_ticks;
463 if (status->priv->splash)
465 e_splash_set (E_SPLASH(status->priv->splash), NULL, NULL, NULL,
466 percentage);
467 while (g_main_context_iteration(NULL, FALSE));
469 else
471 if (status->priv->progress_bar && status->priv->status_bar)
473 GdkWindow *progressbar_window, *statusbar_window;
475 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (status->priv->progress_bar),
476 percentage);
477 gtk_widget_queue_draw (GTK_WIDGET (status->priv->status_bar));
478 gtk_widget_queue_draw (GTK_WIDGET (status->priv->progress_bar));
479 progressbar_window = gtk_widget_get_window (GTK_WIDGET(status->priv->progress_bar));
480 statusbar_window = gtk_widget_get_window (GTK_WIDGET(status->priv->status_bar));
481 if (progressbar_window != NULL)
482 gdk_window_process_updates (progressbar_window, TRUE);
483 if (statusbar_window != NULL)
484 gdk_window_process_updates (statusbar_window, TRUE);
489 void
490 anjuta_status_progress_pulse (AnjutaStatus *status, const gchar *text)
492 GtkProgressBar *progressbar;
493 GtkWidget *statusbar;
494 GdkWindow *progressbar_window, *statusbar_window;
496 progressbar = GTK_PROGRESS_BAR (status->priv->progress_bar);
497 statusbar = status->priv->status_bar;
499 if (text)
500 anjuta_status_push (status, "%s", text);
502 gtk_progress_bar_pulse (progressbar);
504 gtk_widget_queue_draw (GTK_WIDGET (statusbar));
505 gtk_widget_queue_draw (GTK_WIDGET (progressbar));
506 progressbar_window = gtk_widget_get_window (GTK_WIDGET (progressbar));
507 if (progressbar_window != NULL)
508 gdk_window_process_updates (progressbar_window, TRUE);
509 statusbar_window = gtk_widget_get_window (GTK_WIDGET (statusbar));
510 if (statusbar_window != NULL)
511 gdk_window_process_updates (statusbar_window, TRUE);
514 void
515 anjuta_status_progress_tick (AnjutaStatus *status,
516 GdkPixbuf *icon, const gchar *text)
518 gfloat percentage;
520 g_return_if_fail (ANJUTA_IS_STATUS (status));
521 g_return_if_fail (status->priv->total_ticks != 0);
523 status->priv->current_ticks++;
524 percentage = ((gfloat)status->priv->current_ticks)/status->priv->total_ticks;
526 if (status->priv->splash)
528 e_splash_set (E_SPLASH(status->priv->splash), icon, text, NULL, percentage);
530 else
532 GtkProgressBar *progressbar;
533 GtkWidget *statusbar;
534 GdkWindow *progressbar_window, *statusbar_window;
536 if (text)
537 anjuta_status_set (status, "%s", text);
538 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (status->priv->progress_bar),
539 percentage);
540 progressbar = GTK_PROGRESS_BAR (status->priv->progress_bar);
541 statusbar = status->priv->status_bar;
542 gtk_widget_queue_draw (GTK_WIDGET (statusbar));
543 gtk_widget_queue_draw (GTK_WIDGET (progressbar));
544 progressbar_window = gtk_widget_get_window (GTK_WIDGET (progressbar));
545 if (progressbar_window != NULL)
546 gdk_window_process_updates (progressbar_window, TRUE);
547 statusbar_window = gtk_widget_get_window (GTK_WIDGET (statusbar));
548 if (statusbar_window != NULL)
549 gdk_window_process_updates (statusbar_window, TRUE);
551 if (status->priv->current_ticks >= status->priv->total_ticks)
552 anjuta_status_progress_reset (status);
555 void
556 anjuta_status_progress_increment_ticks (AnjutaStatus *status, gint ticks,
557 const gchar *text)
559 gfloat percentage;
560 GdkWindow *progressbar_window, *statusbar_window;
562 g_return_if_fail (ANJUTA_IS_STATUS (status));
563 g_return_if_fail (status->priv->total_ticks != 0);
565 status->priv->current_ticks += ticks;
566 percentage = ((gfloat)status->priv->current_ticks)/status->priv->total_ticks;
568 GtkProgressBar *progressbar;
569 GtkWidget *statusbar;
571 if (text)
572 anjuta_status_set (status, "%s", text);
573 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (status->priv->progress_bar),
574 percentage);
575 progressbar = GTK_PROGRESS_BAR (status->priv->progress_bar);
576 statusbar = status->priv->status_bar;
577 gtk_widget_queue_draw (GTK_WIDGET (statusbar));
578 gtk_widget_queue_draw (GTK_WIDGET (progressbar));
579 progressbar_window = gtk_widget_get_window (GTK_WIDGET(progressbar));
580 if (progressbar_window != NULL)
581 gdk_window_process_updates (progressbar_window, TRUE);
582 statusbar_window = gtk_widget_get_window (GTK_WIDGET(statusbar));
583 if (statusbar_window != NULL)
584 gdk_window_process_updates (statusbar_window, TRUE);
586 if (status->priv->current_ticks >= status->priv->total_ticks)
587 anjuta_status_progress_reset (status);
590 void
591 anjuta_status_progress_reset (AnjutaStatus *status)
593 g_return_if_fail (ANJUTA_IS_STATUS (status));
595 if (status->priv->splash)
597 gtk_widget_destroy (status->priv->splash);
598 status->priv->splash = NULL;
600 status->priv->current_ticks = 0;
601 status->priv->total_ticks = 0;
602 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (status->priv->progress_bar), 0);
603 anjuta_status_clear_stack (status);
606 static gboolean
607 anjuta_status_timeout (AnjutaStatus *status)
609 anjuta_status_pop (status);
610 return FALSE;
614 * anjuta_status: (skip)
615 * Display message in status until timeout (seconds)
617 void
618 anjuta_status (AnjutaStatus *status, const gchar *mesg, gint timeout)
620 g_return_if_fail (ANJUTA_IS_STATUS (status));
621 g_return_if_fail (mesg != NULL);
622 anjuta_status_push (status, "%s", mesg);
623 g_timeout_add_seconds (timeout, (void*) anjuta_status_timeout, status);
626 void
627 anjuta_status_set_title_window (AnjutaStatus *status, GtkWidget *window)
629 g_return_if_fail (ANJUTA_IS_STATUS (status));
630 g_return_if_fail (GTK_IS_WINDOW (window));
631 status->priv->window = GTK_WINDOW (window);
632 g_object_add_weak_pointer (G_OBJECT (window),
633 (gpointer*)(gpointer)&status->priv->window);
636 void
637 anjuta_status_set_title (AnjutaStatus *status, const gchar *title)
639 g_return_if_fail (ANJUTA_IS_STATUS (status));
641 if (!status->priv->window)
642 return;
644 const gchar *app_name = g_get_application_name();
645 if (title)
647 gchar* str = g_strconcat (title, " - ", app_name, NULL);
648 gtk_window_set_title (status->priv->window, str);
649 g_free (str);
651 else
653 gtk_window_set_title (status->priv->window, app_name);
657 ANJUTA_TYPE_BEGIN(AnjutaStatus, anjuta_status, GTK_TYPE_HBOX);
658 ANJUTA_TYPE_END;