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 "gtksignal.h"
32 #include "gtkstatusbar.h"
33 #include "gtkwindow.h"
36 typedef struct _GtkStatusbarMsg GtkStatusbarMsg;
38 struct _GtkStatusbarMsg
52 static void gtk_statusbar_class_init (GtkStatusbarClass *class);
53 static void gtk_statusbar_init (GtkStatusbar *statusbar);
54 static void gtk_statusbar_destroy (GtkObject *object);
55 static void gtk_statusbar_update (GtkStatusbar *statusbar,
58 static void gtk_statusbar_size_allocate (GtkWidget *widget,
59 GtkAllocation *allocation);
60 static void gtk_statusbar_realize (GtkWidget *widget);
61 static void gtk_statusbar_unrealize (GtkWidget *widget);
62 static void gtk_statusbar_map (GtkWidget *widget);
63 static void gtk_statusbar_unmap (GtkWidget *widget);
64 static gboolean gtk_statusbar_button_press (GtkWidget *widget,
65 GdkEventButton *event);
66 static gboolean gtk_statusbar_expose_event (GtkWidget *widget,
67 GdkEventExpose *event);
68 static void gtk_statusbar_size_request (GtkWidget *widget,
69 GtkRequisition *requisition);
70 static void gtk_statusbar_size_allocate (GtkWidget *widget,
71 GtkAllocation *allocation);
72 static void gtk_statusbar_create_window (GtkStatusbar *statusbar);
73 static void gtk_statusbar_destroy_window (GtkStatusbar *statusbar);
75 static GtkContainerClass *parent_class;
76 static guint statusbar_signals[SIGNAL_LAST] = { 0 };
79 gtk_statusbar_get_type (void)
81 static GtkType statusbar_type = 0;
85 static const GtkTypeInfo statusbar_info =
88 sizeof (GtkStatusbar),
89 sizeof (GtkStatusbarClass),
90 (GtkClassInitFunc) gtk_statusbar_class_init,
91 (GtkObjectInitFunc) gtk_statusbar_init,
92 /* reserved_1 */ NULL,
93 /* reserved_2 */ NULL,
94 (GtkClassInitFunc) NULL,
97 statusbar_type = gtk_type_unique (GTK_TYPE_HBOX, &statusbar_info);
100 return statusbar_type;
104 gtk_statusbar_class_init (GtkStatusbarClass *class)
106 GtkObjectClass *object_class;
107 GtkWidgetClass *widget_class;
108 GtkContainerClass *container_class;
110 object_class = (GtkObjectClass *) class;
111 widget_class = (GtkWidgetClass *) class;
112 container_class = (GtkContainerClass *) class;
114 parent_class = gtk_type_class (GTK_TYPE_HBOX);
116 object_class->destroy = gtk_statusbar_destroy;
118 widget_class->realize = gtk_statusbar_realize;
119 widget_class->unrealize = gtk_statusbar_unrealize;
120 widget_class->map = gtk_statusbar_map;
121 widget_class->unmap = gtk_statusbar_unmap;
123 widget_class->button_press_event = gtk_statusbar_button_press;
124 widget_class->expose_event = gtk_statusbar_expose_event;
126 widget_class->size_request = gtk_statusbar_size_request;
127 widget_class->size_allocate = gtk_statusbar_size_allocate;
129 class->messages_mem_chunk = g_mem_chunk_new ("GtkStatusBar messages mem chunk",
130 sizeof (GtkStatusbarMsg),
131 sizeof (GtkStatusbarMsg) * 64,
134 class->text_pushed = gtk_statusbar_update;
135 class->text_popped = gtk_statusbar_update;
137 statusbar_signals[SIGNAL_TEXT_PUSHED] =
138 gtk_signal_new ("text_pushed",
140 GTK_CLASS_TYPE (object_class),
141 GTK_SIGNAL_OFFSET (GtkStatusbarClass, text_pushed),
142 _gtk_marshal_VOID__UINT_STRING,
146 statusbar_signals[SIGNAL_TEXT_POPPED] =
147 gtk_signal_new ("text_popped",
149 GTK_CLASS_TYPE (object_class),
150 GTK_SIGNAL_OFFSET (GtkStatusbarClass, text_popped),
151 _gtk_marshal_VOID__UINT_STRING,
156 gtk_widget_class_install_style_property (widget_class,
157 g_param_spec_enum ("shadow_type",
159 _("Style of bevel around the statusbar text"),
160 GTK_TYPE_SHADOW_TYPE,
166 gtk_statusbar_init (GtkStatusbar *statusbar)
169 GtkShadowType shadow_type;
171 box = GTK_BOX (statusbar);
174 box->homogeneous = FALSE;
176 statusbar->has_resize_grip = TRUE;
178 gtk_widget_style_get (GTK_WIDGET (statusbar), "shadow_type", &shadow_type, NULL);
180 statusbar->frame = gtk_frame_new (NULL);
181 gtk_frame_set_shadow_type (GTK_FRAME (statusbar->frame), shadow_type);
182 gtk_box_pack_start (box, statusbar->frame, TRUE, TRUE, 0);
183 gtk_widget_show (statusbar->frame);
185 statusbar->label = gtk_label_new ("");
186 gtk_misc_set_alignment (GTK_MISC (statusbar->label), 0.0, 0.0);
187 /* don't expand the size request for the label; if we
188 * do that then toplevels weirdly resize
190 gtk_widget_set_usize (statusbar->label, 1, -1);
191 gtk_container_add (GTK_CONTAINER (statusbar->frame), statusbar->label);
192 gtk_widget_show (statusbar->label);
194 statusbar->seq_context_id = 1;
195 statusbar->seq_message_id = 1;
196 statusbar->messages = NULL;
197 statusbar->keys = NULL;
201 gtk_statusbar_new (void)
203 return gtk_type_new (GTK_TYPE_STATUSBAR);
207 gtk_statusbar_update (GtkStatusbar *statusbar,
211 g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
216 gtk_label_set_text (GTK_LABEL (statusbar->label), text);
220 gtk_statusbar_get_context_id (GtkStatusbar *statusbar,
221 const gchar *context_description)
226 g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), 0);
227 g_return_val_if_fail (context_description != NULL, 0);
229 /* we need to preserve namespaces on object datas */
230 string = g_strconcat ("gtk-status-bar-context:", context_description, NULL);
232 id = gtk_object_get_data (GTK_OBJECT (statusbar), string);
235 id = g_new (guint, 1);
236 *id = statusbar->seq_context_id++;
237 gtk_object_set_data_full (GTK_OBJECT (statusbar), string, id, (GtkDestroyNotify) g_free);
238 statusbar->keys = g_slist_prepend (statusbar->keys, string);
247 gtk_statusbar_push (GtkStatusbar *statusbar,
251 GtkStatusbarMsg *msg;
252 GtkStatusbarClass *class;
254 g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), 0);
255 g_return_val_if_fail (text != NULL, 0);
257 class = GTK_STATUSBAR_GET_CLASS (statusbar);
258 msg = g_chunk_new (GtkStatusbarMsg, class->messages_mem_chunk);
259 msg->text = g_strdup (text);
260 msg->context_id = context_id;
261 msg->message_id = statusbar->seq_message_id++;
263 statusbar->messages = g_slist_prepend (statusbar->messages, msg);
265 gtk_signal_emit (GTK_OBJECT (statusbar),
266 statusbar_signals[SIGNAL_TEXT_PUSHED],
270 return msg->message_id;
274 gtk_statusbar_pop (GtkStatusbar *statusbar,
277 GtkStatusbarMsg *msg;
279 g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
281 if (statusbar->messages)
285 for (list = statusbar->messages; list; list = list->next)
289 if (msg->context_id == context_id)
291 GtkStatusbarClass *class;
293 class = GTK_STATUSBAR_GET_CLASS (statusbar);
295 statusbar->messages = g_slist_remove_link (statusbar->messages,
298 g_mem_chunk_free (class->messages_mem_chunk, msg);
299 g_slist_free_1 (list);
305 msg = statusbar->messages ? statusbar->messages->data : NULL;
307 gtk_signal_emit (GTK_OBJECT (statusbar),
308 statusbar_signals[SIGNAL_TEXT_POPPED],
309 (guint) (msg ? msg->context_id : 0),
310 msg ? msg->text : NULL);
314 gtk_statusbar_remove (GtkStatusbar *statusbar,
318 GtkStatusbarMsg *msg;
320 g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
321 g_return_if_fail (message_id > 0);
323 msg = statusbar->messages ? statusbar->messages->data : NULL;
328 /* care about signal emission if the topmost item is removed */
329 if (msg->context_id == context_id &&
330 msg->message_id == message_id)
332 gtk_statusbar_pop (statusbar, context_id);
336 for (list = statusbar->messages; list; list = list->next)
340 if (msg->context_id == context_id &&
341 msg->message_id == message_id)
343 GtkStatusbarClass *class;
345 class = GTK_STATUSBAR_GET_CLASS (statusbar);
346 statusbar->messages = g_slist_remove_link (statusbar->messages, list);
348 g_mem_chunk_free (class->messages_mem_chunk, msg);
349 g_slist_free_1 (list);
358 gtk_statusbar_set_has_resize_grip (GtkStatusbar *statusbar,
361 g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
363 setting = setting != FALSE;
365 if (setting != statusbar->has_resize_grip)
367 statusbar->has_resize_grip = setting;
368 gtk_widget_queue_draw (GTK_WIDGET (statusbar));
370 if (GTK_WIDGET_REALIZED (statusbar))
372 if (statusbar->has_resize_grip && statusbar->grip_window == NULL)
373 gtk_statusbar_create_window (statusbar);
374 else if (!statusbar->has_resize_grip && statusbar->grip_window != NULL)
375 gtk_statusbar_destroy_window (statusbar);
381 gtk_statusbar_get_has_resize_grip (GtkStatusbar *statusbar)
383 g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), FALSE);
385 return statusbar->has_resize_grip;
389 gtk_statusbar_destroy (GtkObject *object)
391 GtkStatusbar *statusbar;
392 GtkStatusbarClass *class;
395 g_return_if_fail (GTK_IS_STATUSBAR (object));
397 statusbar = GTK_STATUSBAR (object);
398 class = GTK_STATUSBAR_GET_CLASS (statusbar);
400 for (list = statusbar->messages; list; list = list->next)
402 GtkStatusbarMsg *msg;
406 g_mem_chunk_free (class->messages_mem_chunk, msg);
408 g_slist_free (statusbar->messages);
409 statusbar->messages = NULL;
411 for (list = statusbar->keys; list; list = list->next)
413 g_slist_free (statusbar->keys);
414 statusbar->keys = NULL;
416 GTK_OBJECT_CLASS (parent_class)->destroy (object);
420 get_grip_rect (GtkStatusbar *statusbar,
426 widget = GTK_WIDGET (statusbar);
428 /* These are in effect the max/default size of the grip. */
432 if (w > (widget->allocation.width))
433 w = widget->allocation.width;
435 if (h > (widget->allocation.height - widget->style->ythickness))
436 h = widget->allocation.height - widget->style->ythickness;
438 rect->x = widget->allocation.x + widget->allocation.width - w;
439 rect->y = widget->allocation.y + widget->allocation.height - h;
445 gtk_statusbar_create_window (GtkStatusbar *statusbar)
448 GdkWindowAttr attributes;
449 gint attributes_mask;
452 g_return_if_fail (GTK_WIDGET_REALIZED (statusbar));
453 g_return_if_fail (statusbar->has_resize_grip);
455 widget = GTK_WIDGET (statusbar);
457 get_grip_rect (statusbar, &rect);
459 attributes.x = rect.x;
460 attributes.y = rect.y;
461 attributes.width = rect.width;
462 attributes.height = rect.height;
463 attributes.window_type = GDK_WINDOW_CHILD;
464 attributes.wclass = GDK_INPUT_ONLY;
465 attributes.event_mask = gtk_widget_get_events (widget) |
466 GDK_BUTTON_PRESS_MASK;
468 attributes_mask = GDK_WA_X | GDK_WA_Y;
470 statusbar->grip_window = gdk_window_new (widget->window,
471 &attributes, attributes_mask);
472 gdk_window_set_user_data (statusbar->grip_window, widget);
476 gtk_statusbar_destroy_window (GtkStatusbar *statusbar)
478 gdk_window_set_user_data (statusbar->grip_window, NULL);
479 gdk_window_destroy (statusbar->grip_window);
480 statusbar->grip_window = NULL;
484 gtk_statusbar_realize (GtkWidget *widget)
486 GtkStatusbar *statusbar;
488 statusbar = GTK_STATUSBAR (widget);
490 (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
492 if (statusbar->has_resize_grip)
493 gtk_statusbar_create_window (statusbar);
497 gtk_statusbar_unrealize (GtkWidget *widget)
499 GtkStatusbar *statusbar;
501 statusbar = GTK_STATUSBAR (widget);
503 if (statusbar->grip_window)
504 gtk_statusbar_destroy_window (statusbar);
506 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
510 gtk_statusbar_map (GtkWidget *widget)
512 GtkStatusbar *statusbar;
514 statusbar = GTK_STATUSBAR (widget);
516 (* GTK_WIDGET_CLASS (parent_class)->map) (widget);
518 if (statusbar->grip_window)
519 gdk_window_show (statusbar->grip_window);
523 gtk_statusbar_unmap (GtkWidget *widget)
525 GtkStatusbar *statusbar;
527 statusbar = GTK_STATUSBAR (widget);
529 if (statusbar->grip_window)
530 gdk_window_hide (statusbar->grip_window);
532 (* GTK_WIDGET_CLASS (parent_class)->unmap) (widget);
536 gtk_statusbar_button_press (GtkWidget *widget,
537 GdkEventButton *event)
539 GtkStatusbar *statusbar;
542 statusbar = GTK_STATUSBAR (widget);
544 if (!statusbar->has_resize_grip)
547 ancestor = gtk_widget_get_toplevel (widget);
549 if (!GTK_IS_WINDOW (ancestor))
552 if (event->button == 1)
553 gtk_window_begin_resize_drag (GTK_WINDOW (ancestor),
554 GDK_WINDOW_EDGE_SOUTH_EAST,
556 event->x_root, event->y_root,
558 else if (event->button == 2)
559 gtk_window_begin_move_drag (GTK_WINDOW (ancestor),
561 event->x_root, event->y_root,
570 gtk_statusbar_expose_event (GtkWidget *widget,
571 GdkEventExpose *event)
573 GtkStatusbar *statusbar;
576 statusbar = GTK_STATUSBAR (widget);
578 GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
580 if (statusbar->has_resize_grip)
582 get_grip_rect (statusbar, &rect);
584 gtk_paint_resize_grip (widget->style,
586 GTK_WIDGET_STATE (widget),
590 GDK_WINDOW_EDGE_SOUTH_EAST,
592 /* don't draw grip over the frame, though you
593 * can click on the frame.
595 rect.width - widget->style->xthickness,
596 rect.height - widget->style->ythickness);
603 gtk_statusbar_size_request (GtkWidget *widget,
604 GtkRequisition *requisition)
606 GtkStatusbar *statusbar;
607 GtkShadowType shadow_type;
609 statusbar = GTK_STATUSBAR (widget);
611 gtk_widget_style_get (GTK_WIDGET (statusbar), "shadow_type", &shadow_type, NULL);
612 gtk_frame_set_shadow_type (GTK_FRAME (statusbar->frame), shadow_type);
614 GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
616 if (statusbar->has_resize_grip)
620 /* x, y in the grip rect depend on size allocation, but
621 * w, h do not so this is OK
623 get_grip_rect (statusbar, &rect);
625 requisition->width += rect.width;
626 requisition->height = MAX (requisition->height, rect.height);
631 gtk_statusbar_size_allocate (GtkWidget *widget,
632 GtkAllocation *allocation)
634 GtkStatusbar *statusbar;
636 statusbar = GTK_STATUSBAR (widget);
638 if (statusbar->has_resize_grip)
641 GtkRequisition saved_req;
643 widget->allocation = *allocation; /* get_grip_rect needs this info */
644 get_grip_rect (statusbar, &rect);
646 if (statusbar->grip_window)
647 gdk_window_move_resize (statusbar->grip_window,
649 rect.width, rect.height);
651 /* enter the bad hack zone */
652 saved_req = widget->requisition;
653 widget->requisition.width -= rect.width; /* HBox::size_allocate needs this */
654 if (widget->requisition.width < 0)
655 widget->requisition.width = 0;
656 GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
657 widget->requisition = saved_req;
661 /* chain up normally */
662 GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);