1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3 * GtkStatusbar Copyright (C) 1998 Shawn T. Amundson
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
23 * file for a list of people on the GTK+ Team. See the ChangeLog
24 * files for a list of changes. These files are distributed with
25 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
31 #include "gtkmarshalers.h"
32 #include "gtkstatusbar.h"
33 #include "gtkwindow.h"
36 typedef struct _GtkStatusbarMsg GtkStatusbarMsg;
38 struct _GtkStatusbarMsg
58 static void gtk_statusbar_class_init (GtkStatusbarClass *class);
59 static void gtk_statusbar_init (GtkStatusbar *statusbar);
60 static void gtk_statusbar_destroy (GtkObject *object);
61 static void gtk_statusbar_update (GtkStatusbar *statusbar,
64 static void gtk_statusbar_size_allocate (GtkWidget *widget,
65 GtkAllocation *allocation);
66 static void gtk_statusbar_realize (GtkWidget *widget);
67 static void gtk_statusbar_unrealize (GtkWidget *widget);
68 static void gtk_statusbar_map (GtkWidget *widget);
69 static void gtk_statusbar_unmap (GtkWidget *widget);
70 static gboolean gtk_statusbar_button_press (GtkWidget *widget,
71 GdkEventButton *event);
72 static gboolean gtk_statusbar_expose_event (GtkWidget *widget,
73 GdkEventExpose *event);
74 static void gtk_statusbar_size_request (GtkWidget *widget,
75 GtkRequisition *requisition);
76 static void gtk_statusbar_size_allocate (GtkWidget *widget,
77 GtkAllocation *allocation);
78 static void gtk_statusbar_direction_changed (GtkWidget *widget,
79 GtkTextDirection prev_dir);
80 static void gtk_statusbar_create_window (GtkStatusbar *statusbar);
81 static void gtk_statusbar_destroy_window (GtkStatusbar *statusbar);
82 static void gtk_statusbar_get_property (GObject *object,
86 static void gtk_statusbar_set_property (GObject *object,
92 static GtkContainerClass *parent_class;
93 static guint statusbar_signals[SIGNAL_LAST] = { 0 };
96 gtk_statusbar_get_type (void)
98 static GType statusbar_type = 0;
102 static const GTypeInfo statusbar_info =
104 sizeof (GtkStatusbarClass),
105 NULL, /* base_init */
106 NULL, /* base_finalize */
107 (GClassInitFunc) gtk_statusbar_class_init,
108 NULL, /* class_finalize */
109 NULL, /* class_data */
110 sizeof (GtkStatusbar),
112 (GInstanceInitFunc) gtk_statusbar_init,
115 statusbar_type = g_type_register_static (GTK_TYPE_HBOX, "GtkStatusbar",
119 return statusbar_type;
123 gtk_statusbar_class_init (GtkStatusbarClass *class)
125 GObjectClass *gobject_class;
126 GtkObjectClass *object_class;
127 GtkWidgetClass *widget_class;
128 GtkContainerClass *container_class;
130 gobject_class = (GObjectClass *) class;
131 object_class = (GtkObjectClass *) class;
132 widget_class = (GtkWidgetClass *) class;
133 container_class = (GtkContainerClass *) class;
135 parent_class = g_type_class_peek_parent (class);
137 gobject_class->set_property = gtk_statusbar_set_property;
138 gobject_class->get_property = gtk_statusbar_get_property;
140 object_class->destroy = gtk_statusbar_destroy;
142 widget_class->realize = gtk_statusbar_realize;
143 widget_class->unrealize = gtk_statusbar_unrealize;
144 widget_class->map = gtk_statusbar_map;
145 widget_class->unmap = gtk_statusbar_unmap;
147 widget_class->button_press_event = gtk_statusbar_button_press;
148 widget_class->expose_event = gtk_statusbar_expose_event;
150 widget_class->size_request = gtk_statusbar_size_request;
151 widget_class->size_allocate = gtk_statusbar_size_allocate;
153 widget_class->direction_changed = gtk_statusbar_direction_changed;
155 class->messages_mem_chunk = g_mem_chunk_new ("GtkStatusbar messages mem chunk",
156 sizeof (GtkStatusbarMsg),
157 sizeof (GtkStatusbarMsg) * 64,
160 class->text_pushed = gtk_statusbar_update;
161 class->text_popped = gtk_statusbar_update;
164 * GtkStatusbar:has-resize-grip:
166 * Whether the statusbar has a grip for resizing the toplevel window.
170 g_object_class_install_property (gobject_class,
171 PROP_HAS_RESIZE_GRIP,
172 g_param_spec_boolean ("has_resize_grip",
173 P_("Has Resize Grip"),
174 P_("Whether the statusbar has a grip for resizing the toplevel"),
177 statusbar_signals[SIGNAL_TEXT_PUSHED] =
178 g_signal_new ("text_pushed",
179 G_OBJECT_CLASS_TYPE (class),
181 G_STRUCT_OFFSET (GtkStatusbarClass, text_pushed),
183 _gtk_marshal_VOID__UINT_STRING,
187 statusbar_signals[SIGNAL_TEXT_POPPED] =
188 g_signal_new ("text_popped",
189 G_OBJECT_CLASS_TYPE (class),
191 G_STRUCT_OFFSET (GtkStatusbarClass, text_popped),
193 _gtk_marshal_VOID__UINT_STRING,
198 gtk_widget_class_install_style_property (widget_class,
199 g_param_spec_enum ("shadow_type",
201 P_("Style of bevel around the statusbar text"),
202 GTK_TYPE_SHADOW_TYPE,
208 gtk_statusbar_init (GtkStatusbar *statusbar)
211 GtkShadowType shadow_type;
213 box = GTK_BOX (statusbar);
216 box->homogeneous = FALSE;
218 statusbar->has_resize_grip = TRUE;
220 gtk_widget_style_get (GTK_WIDGET (statusbar), "shadow_type", &shadow_type, NULL);
222 statusbar->frame = gtk_frame_new (NULL);
223 gtk_frame_set_shadow_type (GTK_FRAME (statusbar->frame), shadow_type);
224 gtk_box_pack_start (box, statusbar->frame, TRUE, TRUE, 0);
225 gtk_widget_show (statusbar->frame);
227 statusbar->label = gtk_label_new ("");
228 gtk_misc_set_alignment (GTK_MISC (statusbar->label), 0.0, 0.5);
229 /* don't expand the size request for the label; if we
230 * do that then toplevels weirdly resize
232 gtk_widget_set_size_request (statusbar->label, 1, -1);
233 gtk_container_add (GTK_CONTAINER (statusbar->frame), statusbar->label);
234 gtk_widget_show (statusbar->label);
236 statusbar->seq_context_id = 1;
237 statusbar->seq_message_id = 1;
238 statusbar->messages = NULL;
239 statusbar->keys = NULL;
243 gtk_statusbar_new (void)
245 return g_object_new (GTK_TYPE_STATUSBAR, NULL);
249 gtk_statusbar_update (GtkStatusbar *statusbar,
253 g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
258 gtk_label_set_text (GTK_LABEL (statusbar->label), text);
262 gtk_statusbar_get_context_id (GtkStatusbar *statusbar,
263 const gchar *context_description)
268 g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), 0);
269 g_return_val_if_fail (context_description != NULL, 0);
271 /* we need to preserve namespaces on object datas */
272 string = g_strconcat ("gtk-status-bar-context:", context_description, NULL);
274 id = g_object_get_data (G_OBJECT (statusbar), string);
277 id = g_new (guint, 1);
278 *id = statusbar->seq_context_id++;
279 g_object_set_data_full (G_OBJECT (statusbar), string, id, g_free);
280 statusbar->keys = g_slist_prepend (statusbar->keys, string);
289 gtk_statusbar_push (GtkStatusbar *statusbar,
293 GtkStatusbarMsg *msg;
294 GtkStatusbarClass *class;
296 g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), 0);
297 g_return_val_if_fail (text != NULL, 0);
299 class = GTK_STATUSBAR_GET_CLASS (statusbar);
300 msg = g_chunk_new (GtkStatusbarMsg, class->messages_mem_chunk);
301 msg->text = g_strdup (text);
302 msg->context_id = context_id;
303 msg->message_id = statusbar->seq_message_id++;
305 statusbar->messages = g_slist_prepend (statusbar->messages, msg);
307 g_signal_emit (statusbar,
308 statusbar_signals[SIGNAL_TEXT_PUSHED],
313 return msg->message_id;
317 gtk_statusbar_pop (GtkStatusbar *statusbar,
320 GtkStatusbarMsg *msg;
322 g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
324 if (statusbar->messages)
328 for (list = statusbar->messages; list; list = list->next)
332 if (msg->context_id == context_id)
334 GtkStatusbarClass *class;
336 class = GTK_STATUSBAR_GET_CLASS (statusbar);
338 statusbar->messages = g_slist_remove_link (statusbar->messages,
341 g_mem_chunk_free (class->messages_mem_chunk, msg);
342 g_slist_free_1 (list);
348 msg = statusbar->messages ? statusbar->messages->data : NULL;
350 g_signal_emit (statusbar,
351 statusbar_signals[SIGNAL_TEXT_POPPED],
353 (guint) (msg ? msg->context_id : 0),
354 msg ? msg->text : NULL);
358 gtk_statusbar_remove (GtkStatusbar *statusbar,
362 GtkStatusbarMsg *msg;
364 g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
365 g_return_if_fail (message_id > 0);
367 msg = statusbar->messages ? statusbar->messages->data : NULL;
372 /* care about signal emission if the topmost item is removed */
373 if (msg->context_id == context_id &&
374 msg->message_id == message_id)
376 gtk_statusbar_pop (statusbar, context_id);
380 for (list = statusbar->messages; list; list = list->next)
384 if (msg->context_id == context_id &&
385 msg->message_id == message_id)
387 GtkStatusbarClass *class;
389 class = GTK_STATUSBAR_GET_CLASS (statusbar);
390 statusbar->messages = g_slist_remove_link (statusbar->messages, list);
392 g_mem_chunk_free (class->messages_mem_chunk, msg);
393 g_slist_free_1 (list);
402 gtk_statusbar_set_has_resize_grip (GtkStatusbar *statusbar,
405 g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
407 setting = setting != FALSE;
409 if (setting != statusbar->has_resize_grip)
411 statusbar->has_resize_grip = setting;
412 gtk_widget_queue_draw (GTK_WIDGET (statusbar));
414 if (GTK_WIDGET_REALIZED (statusbar))
416 if (statusbar->has_resize_grip && statusbar->grip_window == NULL)
417 gtk_statusbar_create_window (statusbar);
418 else if (!statusbar->has_resize_grip && statusbar->grip_window != NULL)
419 gtk_statusbar_destroy_window (statusbar);
422 g_object_notify (G_OBJECT (statusbar), "has_resize_grip");
427 gtk_statusbar_get_has_resize_grip (GtkStatusbar *statusbar)
429 g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), FALSE);
431 return statusbar->has_resize_grip;
435 gtk_statusbar_destroy (GtkObject *object)
437 GtkStatusbar *statusbar;
438 GtkStatusbarClass *class;
441 g_return_if_fail (GTK_IS_STATUSBAR (object));
443 statusbar = GTK_STATUSBAR (object);
444 class = GTK_STATUSBAR_GET_CLASS (statusbar);
446 for (list = statusbar->messages; list; list = list->next)
448 GtkStatusbarMsg *msg;
452 g_mem_chunk_free (class->messages_mem_chunk, msg);
454 g_slist_free (statusbar->messages);
455 statusbar->messages = NULL;
457 for (list = statusbar->keys; list; list = list->next)
459 g_slist_free (statusbar->keys);
460 statusbar->keys = NULL;
462 GTK_OBJECT_CLASS (parent_class)->destroy (object);
466 gtk_statusbar_set_property (GObject *object,
471 GtkStatusbar *statusbar = GTK_STATUSBAR (object);
475 case PROP_HAS_RESIZE_GRIP:
476 gtk_statusbar_set_has_resize_grip (statusbar, g_value_get_boolean (value));
479 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
485 gtk_statusbar_get_property (GObject *object,
490 GtkStatusbar *statusbar = GTK_STATUSBAR (object);
494 case PROP_HAS_RESIZE_GRIP:
495 g_value_set_boolean (value, statusbar->has_resize_grip);
498 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
504 get_grip_edge (GtkStatusbar *statusbar)
506 GtkWidget *widget = GTK_WIDGET (statusbar);
508 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
509 return GDK_WINDOW_EDGE_SOUTH_EAST;
511 return GDK_WINDOW_EDGE_SOUTH_WEST;
515 get_grip_rect (GtkStatusbar *statusbar,
521 widget = GTK_WIDGET (statusbar);
523 /* These are in effect the max/default size of the grip. */
527 if (w > (widget->allocation.width))
528 w = widget->allocation.width;
530 if (h > (widget->allocation.height - widget->style->ythickness))
531 h = widget->allocation.height - widget->style->ythickness;
535 rect->y = widget->allocation.y + widget->allocation.height - h;
537 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
538 rect->x = widget->allocation.x + widget->allocation.width - w;
540 rect->x = widget->allocation.x + widget->style->xthickness;
544 set_grip_cursor (GtkStatusbar *statusbar)
546 if (statusbar->has_resize_grip)
548 GtkWidget *widget = GTK_WIDGET (statusbar);
549 GdkDisplay *display = gtk_widget_get_display (widget);
550 GdkCursorType cursor_type;
553 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
554 cursor_type = GDK_BOTTOM_RIGHT_CORNER;
556 cursor_type = GDK_BOTTOM_LEFT_CORNER;
558 cursor = gdk_cursor_new_for_display (display, cursor_type);
559 gdk_window_set_cursor (statusbar->grip_window, cursor);
560 gdk_cursor_unref (cursor);
565 gtk_statusbar_create_window (GtkStatusbar *statusbar)
568 GdkWindowAttr attributes;
569 gint attributes_mask;
572 g_return_if_fail (GTK_WIDGET_REALIZED (statusbar));
573 g_return_if_fail (statusbar->has_resize_grip);
575 widget = GTK_WIDGET (statusbar);
577 get_grip_rect (statusbar, &rect);
579 attributes.x = rect.x;
580 attributes.y = rect.y;
581 attributes.width = rect.width;
582 attributes.height = rect.height;
583 attributes.window_type = GDK_WINDOW_CHILD;
584 attributes.wclass = GDK_INPUT_ONLY;
585 attributes.event_mask = gtk_widget_get_events (widget) |
586 GDK_BUTTON_PRESS_MASK;
588 attributes_mask = GDK_WA_X | GDK_WA_Y;
590 statusbar->grip_window = gdk_window_new (widget->window,
591 &attributes, attributes_mask);
592 gdk_window_set_user_data (statusbar->grip_window, widget);
594 set_grip_cursor (statusbar);
598 gtk_statusbar_direction_changed (GtkWidget *widget,
599 GtkTextDirection prev_dir)
601 GtkStatusbar *statusbar = GTK_STATUSBAR (widget);
603 set_grip_cursor (statusbar);
607 gtk_statusbar_destroy_window (GtkStatusbar *statusbar)
609 gdk_window_set_user_data (statusbar->grip_window, NULL);
610 gdk_window_destroy (statusbar->grip_window);
611 statusbar->grip_window = NULL;
615 gtk_statusbar_realize (GtkWidget *widget)
617 GtkStatusbar *statusbar;
619 statusbar = GTK_STATUSBAR (widget);
621 (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
623 if (statusbar->has_resize_grip)
624 gtk_statusbar_create_window (statusbar);
628 gtk_statusbar_unrealize (GtkWidget *widget)
630 GtkStatusbar *statusbar;
632 statusbar = GTK_STATUSBAR (widget);
634 if (statusbar->grip_window)
635 gtk_statusbar_destroy_window (statusbar);
637 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
641 gtk_statusbar_map (GtkWidget *widget)
643 GtkStatusbar *statusbar;
645 statusbar = GTK_STATUSBAR (widget);
647 (* GTK_WIDGET_CLASS (parent_class)->map) (widget);
649 if (statusbar->grip_window)
650 gdk_window_show (statusbar->grip_window);
654 gtk_statusbar_unmap (GtkWidget *widget)
656 GtkStatusbar *statusbar;
658 statusbar = GTK_STATUSBAR (widget);
660 if (statusbar->grip_window)
661 gdk_window_hide (statusbar->grip_window);
663 (* GTK_WIDGET_CLASS (parent_class)->unmap) (widget);
667 gtk_statusbar_button_press (GtkWidget *widget,
668 GdkEventButton *event)
670 GtkStatusbar *statusbar;
674 statusbar = GTK_STATUSBAR (widget);
676 if (!statusbar->has_resize_grip ||
677 event->type != GDK_BUTTON_PRESS)
680 ancestor = gtk_widget_get_toplevel (widget);
682 if (!GTK_IS_WINDOW (ancestor))
685 edge = get_grip_edge (statusbar);
687 if (event->button == 1)
688 gtk_window_begin_resize_drag (GTK_WINDOW (ancestor),
691 event->x_root, event->y_root,
693 else if (event->button == 2)
694 gtk_window_begin_move_drag (GTK_WINDOW (ancestor),
696 event->x_root, event->y_root,
705 gtk_statusbar_expose_event (GtkWidget *widget,
706 GdkEventExpose *event)
708 GtkStatusbar *statusbar;
711 statusbar = GTK_STATUSBAR (widget);
713 GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
715 if (statusbar->has_resize_grip)
719 edge = get_grip_edge (statusbar);
721 get_grip_rect (statusbar, &rect);
723 gtk_paint_resize_grip (widget->style,
725 GTK_WIDGET_STATE (widget),
731 /* don't draw grip over the frame, though you
732 * can click on the frame.
734 rect.width - widget->style->xthickness,
735 rect.height - widget->style->ythickness);
742 gtk_statusbar_size_request (GtkWidget *widget,
743 GtkRequisition *requisition)
745 GtkStatusbar *statusbar;
746 GtkShadowType shadow_type;
748 statusbar = GTK_STATUSBAR (widget);
750 gtk_widget_style_get (GTK_WIDGET (statusbar), "shadow_type", &shadow_type, NULL);
751 gtk_frame_set_shadow_type (GTK_FRAME (statusbar->frame), shadow_type);
753 GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
755 if (statusbar->has_resize_grip)
759 /* x, y in the grip rect depend on size allocation, but
760 * w, h do not so this is OK
762 get_grip_rect (statusbar, &rect);
764 requisition->width += rect.width;
765 requisition->height = MAX (requisition->height, rect.height);
770 gtk_statusbar_size_allocate (GtkWidget *widget,
771 GtkAllocation *allocation)
773 GtkStatusbar *statusbar;
775 statusbar = GTK_STATUSBAR (widget);
777 if (statusbar->has_resize_grip)
780 GtkRequisition saved_req;
782 widget->allocation = *allocation; /* get_grip_rect needs this info */
783 get_grip_rect (statusbar, &rect);
785 if (statusbar->grip_window)
786 gdk_window_move_resize (statusbar->grip_window,
788 rect.width, rect.height);
790 /* enter the bad hack zone */
791 saved_req = widget->requisition;
792 widget->requisition.width -= rect.width; /* HBox::size_allocate needs this */
793 if (widget->requisition.width < 0)
794 widget->requisition.width = 0;
795 GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
796 widget->requisition = saved_req;
800 /* chain up normally */
801 GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);