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/.
30 #include "gtkmarshalers.h"
31 #include "gtkstatusbar.h"
32 #include "gtkwindow.h"
35 typedef struct _GtkStatusbarMsg GtkStatusbarMsg;
37 struct _GtkStatusbarMsg
57 static void gtk_statusbar_class_init (GtkStatusbarClass *class);
58 static void gtk_statusbar_init (GtkStatusbar *statusbar);
59 static void gtk_statusbar_destroy (GtkObject *object);
60 static void gtk_statusbar_update (GtkStatusbar *statusbar,
63 static void gtk_statusbar_size_allocate (GtkWidget *widget,
64 GtkAllocation *allocation);
65 static void gtk_statusbar_realize (GtkWidget *widget);
66 static void gtk_statusbar_unrealize (GtkWidget *widget);
67 static void gtk_statusbar_map (GtkWidget *widget);
68 static void gtk_statusbar_unmap (GtkWidget *widget);
69 static gboolean gtk_statusbar_button_press (GtkWidget *widget,
70 GdkEventButton *event);
71 static gboolean gtk_statusbar_expose_event (GtkWidget *widget,
72 GdkEventExpose *event);
73 static void gtk_statusbar_size_request (GtkWidget *widget,
74 GtkRequisition *requisition);
75 static void gtk_statusbar_size_allocate (GtkWidget *widget,
76 GtkAllocation *allocation);
77 static void gtk_statusbar_create_window (GtkStatusbar *statusbar);
78 static void gtk_statusbar_destroy_window (GtkStatusbar *statusbar);
79 static void gtk_statusbar_get_property (GObject *object,
83 static void gtk_statusbar_set_property (GObject *object,
88 static GtkContainerClass *parent_class;
89 static guint statusbar_signals[SIGNAL_LAST] = { 0 };
92 gtk_statusbar_get_type (void)
94 static GType statusbar_type = 0;
98 static const GTypeInfo statusbar_info =
100 sizeof (GtkStatusbarClass),
101 NULL, /* base_init */
102 NULL, /* base_finalize */
103 (GClassInitFunc) gtk_statusbar_class_init,
104 NULL, /* class_finalize */
105 NULL, /* class_data */
106 sizeof (GtkStatusbar),
108 (GInstanceInitFunc) gtk_statusbar_init,
111 statusbar_type = g_type_register_static (GTK_TYPE_HBOX, "GtkStatusbar",
115 return statusbar_type;
119 gtk_statusbar_class_init (GtkStatusbarClass *class)
121 GObjectClass *gobject_class;
122 GtkObjectClass *object_class;
123 GtkWidgetClass *widget_class;
124 GtkContainerClass *container_class;
126 gobject_class = (GObjectClass *) class;
127 object_class = (GtkObjectClass *) class;
128 widget_class = (GtkWidgetClass *) class;
129 container_class = (GtkContainerClass *) class;
131 parent_class = g_type_class_peek_parent (class);
133 gobject_class->set_property = gtk_statusbar_set_property;
134 gobject_class->get_property = gtk_statusbar_get_property;
136 object_class->destroy = gtk_statusbar_destroy;
138 widget_class->realize = gtk_statusbar_realize;
139 widget_class->unrealize = gtk_statusbar_unrealize;
140 widget_class->map = gtk_statusbar_map;
141 widget_class->unmap = gtk_statusbar_unmap;
143 widget_class->button_press_event = gtk_statusbar_button_press;
144 widget_class->expose_event = gtk_statusbar_expose_event;
146 widget_class->size_request = gtk_statusbar_size_request;
147 widget_class->size_allocate = gtk_statusbar_size_allocate;
149 class->messages_mem_chunk = g_mem_chunk_new ("GtkStatusbar messages mem chunk",
150 sizeof (GtkStatusbarMsg),
151 sizeof (GtkStatusbarMsg) * 64,
154 class->text_pushed = gtk_statusbar_update;
155 class->text_popped = gtk_statusbar_update;
158 * GtkStatusbar:has-resize-grip:
160 * Whether the statusbar has a grip for resizing the toplevel window.
164 g_object_class_install_property (gobject_class,
165 PROP_HAS_RESIZE_GRIP,
166 g_param_spec_boolean ("has_resize_grip",
167 P_("Has Resize Grip"),
168 P_("Whether the statusbar has a grip for resizing the toplevel"),
171 statusbar_signals[SIGNAL_TEXT_PUSHED] =
172 g_signal_new ("text_pushed",
173 G_OBJECT_CLASS_TYPE (class),
175 G_STRUCT_OFFSET (GtkStatusbarClass, text_pushed),
177 _gtk_marshal_VOID__UINT_STRING,
181 statusbar_signals[SIGNAL_TEXT_POPPED] =
182 g_signal_new ("text_popped",
183 G_OBJECT_CLASS_TYPE (class),
185 G_STRUCT_OFFSET (GtkStatusbarClass, text_popped),
187 _gtk_marshal_VOID__UINT_STRING,
192 gtk_widget_class_install_style_property (widget_class,
193 g_param_spec_enum ("shadow_type",
195 P_("Style of bevel around the statusbar text"),
196 GTK_TYPE_SHADOW_TYPE,
202 gtk_statusbar_init (GtkStatusbar *statusbar)
205 GtkShadowType shadow_type;
207 box = GTK_BOX (statusbar);
210 box->homogeneous = FALSE;
212 statusbar->has_resize_grip = TRUE;
214 gtk_widget_style_get (GTK_WIDGET (statusbar), "shadow_type", &shadow_type, NULL);
216 statusbar->frame = gtk_frame_new (NULL);
217 gtk_frame_set_shadow_type (GTK_FRAME (statusbar->frame), shadow_type);
218 gtk_box_pack_start (box, statusbar->frame, TRUE, TRUE, 0);
219 gtk_widget_show (statusbar->frame);
221 statusbar->label = gtk_label_new ("");
222 gtk_misc_set_alignment (GTK_MISC (statusbar->label), 0.0, 0.5);
223 /* don't expand the size request for the label; if we
224 * do that then toplevels weirdly resize
226 gtk_widget_set_size_request (statusbar->label, 1, -1);
227 gtk_container_add (GTK_CONTAINER (statusbar->frame), statusbar->label);
228 gtk_widget_show (statusbar->label);
230 statusbar->seq_context_id = 1;
231 statusbar->seq_message_id = 1;
232 statusbar->messages = NULL;
233 statusbar->keys = NULL;
237 gtk_statusbar_new (void)
239 return g_object_new (GTK_TYPE_STATUSBAR, NULL);
243 gtk_statusbar_update (GtkStatusbar *statusbar,
247 g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
252 gtk_label_set_text (GTK_LABEL (statusbar->label), text);
256 gtk_statusbar_get_context_id (GtkStatusbar *statusbar,
257 const gchar *context_description)
262 g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), 0);
263 g_return_val_if_fail (context_description != NULL, 0);
265 /* we need to preserve namespaces on object datas */
266 string = g_strconcat ("gtk-status-bar-context:", context_description, NULL);
268 id = g_object_get_data (G_OBJECT (statusbar), string);
271 id = g_new (guint, 1);
272 *id = statusbar->seq_context_id++;
273 g_object_set_data_full (G_OBJECT (statusbar), string, id, g_free);
274 statusbar->keys = g_slist_prepend (statusbar->keys, string);
283 gtk_statusbar_push (GtkStatusbar *statusbar,
287 GtkStatusbarMsg *msg;
288 GtkStatusbarClass *class;
290 g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), 0);
291 g_return_val_if_fail (text != NULL, 0);
293 class = GTK_STATUSBAR_GET_CLASS (statusbar);
294 msg = g_chunk_new (GtkStatusbarMsg, class->messages_mem_chunk);
295 msg->text = g_strdup (text);
296 msg->context_id = context_id;
297 msg->message_id = statusbar->seq_message_id++;
299 statusbar->messages = g_slist_prepend (statusbar->messages, msg);
301 g_signal_emit (statusbar,
302 statusbar_signals[SIGNAL_TEXT_PUSHED],
307 return msg->message_id;
311 gtk_statusbar_pop (GtkStatusbar *statusbar,
314 GtkStatusbarMsg *msg;
316 g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
318 if (statusbar->messages)
322 for (list = statusbar->messages; list; list = list->next)
326 if (msg->context_id == context_id)
328 GtkStatusbarClass *class;
330 class = GTK_STATUSBAR_GET_CLASS (statusbar);
332 statusbar->messages = g_slist_remove_link (statusbar->messages,
335 g_mem_chunk_free (class->messages_mem_chunk, msg);
336 g_slist_free_1 (list);
342 msg = statusbar->messages ? statusbar->messages->data : NULL;
344 g_signal_emit (statusbar,
345 statusbar_signals[SIGNAL_TEXT_POPPED],
347 (guint) (msg ? msg->context_id : 0),
348 msg ? msg->text : NULL);
352 gtk_statusbar_remove (GtkStatusbar *statusbar,
356 GtkStatusbarMsg *msg;
358 g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
359 g_return_if_fail (message_id > 0);
361 msg = statusbar->messages ? statusbar->messages->data : NULL;
366 /* care about signal emission if the topmost item is removed */
367 if (msg->context_id == context_id &&
368 msg->message_id == message_id)
370 gtk_statusbar_pop (statusbar, context_id);
374 for (list = statusbar->messages; list; list = list->next)
378 if (msg->context_id == context_id &&
379 msg->message_id == message_id)
381 GtkStatusbarClass *class;
383 class = GTK_STATUSBAR_GET_CLASS (statusbar);
384 statusbar->messages = g_slist_remove_link (statusbar->messages, list);
386 g_mem_chunk_free (class->messages_mem_chunk, msg);
387 g_slist_free_1 (list);
396 gtk_statusbar_set_has_resize_grip (GtkStatusbar *statusbar,
399 g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
401 setting = setting != FALSE;
403 if (setting != statusbar->has_resize_grip)
405 statusbar->has_resize_grip = setting;
406 gtk_widget_queue_draw (GTK_WIDGET (statusbar));
408 if (GTK_WIDGET_REALIZED (statusbar))
410 if (statusbar->has_resize_grip && statusbar->grip_window == NULL)
411 gtk_statusbar_create_window (statusbar);
412 else if (!statusbar->has_resize_grip && statusbar->grip_window != NULL)
413 gtk_statusbar_destroy_window (statusbar);
416 g_object_notify (G_OBJECT (statusbar), "has_resize_grip");
421 gtk_statusbar_get_has_resize_grip (GtkStatusbar *statusbar)
423 g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), FALSE);
425 return statusbar->has_resize_grip;
429 gtk_statusbar_destroy (GtkObject *object)
431 GtkStatusbar *statusbar;
432 GtkStatusbarClass *class;
435 g_return_if_fail (GTK_IS_STATUSBAR (object));
437 statusbar = GTK_STATUSBAR (object);
438 class = GTK_STATUSBAR_GET_CLASS (statusbar);
440 for (list = statusbar->messages; list; list = list->next)
442 GtkStatusbarMsg *msg;
446 g_mem_chunk_free (class->messages_mem_chunk, msg);
448 g_slist_free (statusbar->messages);
449 statusbar->messages = NULL;
451 for (list = statusbar->keys; list; list = list->next)
453 g_slist_free (statusbar->keys);
454 statusbar->keys = NULL;
456 GTK_OBJECT_CLASS (parent_class)->destroy (object);
460 gtk_statusbar_set_property (GObject *object,
465 GtkStatusbar *statusbar = GTK_STATUSBAR (object);
469 case PROP_HAS_RESIZE_GRIP:
470 gtk_statusbar_set_has_resize_grip (statusbar, g_value_get_boolean (value));
473 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
479 gtk_statusbar_get_property (GObject *object,
484 GtkStatusbar *statusbar = GTK_STATUSBAR (object);
488 case PROP_HAS_RESIZE_GRIP:
489 g_value_set_boolean (value, statusbar->has_resize_grip);
492 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
498 get_grip_edge (GtkStatusbar *statusbar)
500 GtkWidget *widget = GTK_WIDGET (statusbar);
502 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
503 return GDK_WINDOW_EDGE_SOUTH_EAST;
505 return GDK_WINDOW_EDGE_SOUTH_WEST;
509 get_grip_rect (GtkStatusbar *statusbar,
515 widget = GTK_WIDGET (statusbar);
517 /* These are in effect the max/default size of the grip. */
521 if (w > (widget->allocation.width))
522 w = widget->allocation.width;
524 if (h > (widget->allocation.height - widget->style->ythickness))
525 h = widget->allocation.height - widget->style->ythickness;
529 rect->y = widget->allocation.y + widget->allocation.height - h;
531 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
532 rect->x = widget->allocation.x + widget->allocation.width - w;
534 rect->x = widget->allocation.x + widget->style->xthickness;
538 gtk_statusbar_create_window (GtkStatusbar *statusbar)
541 GdkWindowAttr attributes;
542 gint attributes_mask;
545 g_return_if_fail (GTK_WIDGET_REALIZED (statusbar));
546 g_return_if_fail (statusbar->has_resize_grip);
548 widget = GTK_WIDGET (statusbar);
550 get_grip_rect (statusbar, &rect);
552 attributes.x = rect.x;
553 attributes.y = rect.y;
554 attributes.width = rect.width;
555 attributes.height = rect.height;
556 attributes.window_type = GDK_WINDOW_CHILD;
557 attributes.wclass = GDK_INPUT_ONLY;
558 attributes.event_mask = gtk_widget_get_events (widget) |
559 GDK_BUTTON_PRESS_MASK;
561 attributes_mask = GDK_WA_X | GDK_WA_Y;
563 statusbar->grip_window = gdk_window_new (widget->window,
564 &attributes, attributes_mask);
565 gdk_window_set_user_data (statusbar->grip_window, widget);
569 gtk_statusbar_destroy_window (GtkStatusbar *statusbar)
571 gdk_window_set_user_data (statusbar->grip_window, NULL);
572 gdk_window_destroy (statusbar->grip_window);
573 statusbar->grip_window = NULL;
577 gtk_statusbar_realize (GtkWidget *widget)
579 GtkStatusbar *statusbar;
581 statusbar = GTK_STATUSBAR (widget);
583 (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
585 if (statusbar->has_resize_grip)
586 gtk_statusbar_create_window (statusbar);
590 gtk_statusbar_unrealize (GtkWidget *widget)
592 GtkStatusbar *statusbar;
594 statusbar = GTK_STATUSBAR (widget);
596 if (statusbar->grip_window)
597 gtk_statusbar_destroy_window (statusbar);
599 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
603 gtk_statusbar_map (GtkWidget *widget)
605 GtkStatusbar *statusbar;
607 statusbar = GTK_STATUSBAR (widget);
609 (* GTK_WIDGET_CLASS (parent_class)->map) (widget);
611 if (statusbar->grip_window)
612 gdk_window_show (statusbar->grip_window);
616 gtk_statusbar_unmap (GtkWidget *widget)
618 GtkStatusbar *statusbar;
620 statusbar = GTK_STATUSBAR (widget);
622 if (statusbar->grip_window)
623 gdk_window_hide (statusbar->grip_window);
625 (* GTK_WIDGET_CLASS (parent_class)->unmap) (widget);
629 gtk_statusbar_button_press (GtkWidget *widget,
630 GdkEventButton *event)
632 GtkStatusbar *statusbar;
636 statusbar = GTK_STATUSBAR (widget);
638 if (!statusbar->has_resize_grip ||
639 event->type != GDK_BUTTON_PRESS)
642 ancestor = gtk_widget_get_toplevel (widget);
644 if (!GTK_IS_WINDOW (ancestor))
647 edge = get_grip_edge (statusbar);
649 if (event->button == 1)
650 gtk_window_begin_resize_drag (GTK_WINDOW (ancestor),
653 event->x_root, event->y_root,
655 else if (event->button == 2)
656 gtk_window_begin_move_drag (GTK_WINDOW (ancestor),
658 event->x_root, event->y_root,
667 gtk_statusbar_expose_event (GtkWidget *widget,
668 GdkEventExpose *event)
670 GtkStatusbar *statusbar;
673 statusbar = GTK_STATUSBAR (widget);
675 GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
677 if (statusbar->has_resize_grip)
681 edge = get_grip_edge (statusbar);
683 get_grip_rect (statusbar, &rect);
685 gtk_paint_resize_grip (widget->style,
687 GTK_WIDGET_STATE (widget),
693 /* don't draw grip over the frame, though you
694 * can click on the frame.
696 rect.width - widget->style->xthickness,
697 rect.height - widget->style->ythickness);
704 gtk_statusbar_size_request (GtkWidget *widget,
705 GtkRequisition *requisition)
707 GtkStatusbar *statusbar;
708 GtkShadowType shadow_type;
710 statusbar = GTK_STATUSBAR (widget);
712 gtk_widget_style_get (GTK_WIDGET (statusbar), "shadow_type", &shadow_type, NULL);
713 gtk_frame_set_shadow_type (GTK_FRAME (statusbar->frame), shadow_type);
715 GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
717 if (statusbar->has_resize_grip)
721 /* x, y in the grip rect depend on size allocation, but
722 * w, h do not so this is OK
724 get_grip_rect (statusbar, &rect);
726 requisition->width += rect.width;
727 requisition->height = MAX (requisition->height, rect.height);
732 gtk_statusbar_size_allocate (GtkWidget *widget,
733 GtkAllocation *allocation)
735 GtkStatusbar *statusbar;
737 statusbar = GTK_STATUSBAR (widget);
739 if (statusbar->has_resize_grip)
742 GtkRequisition saved_req;
744 widget->allocation = *allocation; /* get_grip_rect needs this info */
745 get_grip_rect (statusbar, &rect);
747 if (statusbar->grip_window)
748 gdk_window_move_resize (statusbar->grip_window,
750 rect.width, rect.height);
752 /* enter the bad hack zone */
753 saved_req = widget->requisition;
754 widget->requisition.width -= rect.width; /* HBox::size_allocate needs this */
755 if (widget->requisition.width < 0)
756 widget->requisition.width = 0;
757 GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
758 widget->requisition = saved_req;
762 /* chain up normally */
763 GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);