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 "gtksignal.h"
31 #include "gtkstatusbar.h"
32 #include "gtkwindow.h"
35 typedef struct _GtkStatusbarMsg GtkStatusbarMsg;
37 struct _GtkStatusbarMsg
51 static void gtk_statusbar_class_init (GtkStatusbarClass *class);
52 static void gtk_statusbar_init (GtkStatusbar *statusbar);
53 static void gtk_statusbar_destroy (GtkObject *object);
54 static void gtk_statusbar_update (GtkStatusbar *statusbar,
57 static void gtk_statusbar_size_allocate (GtkWidget *widget,
58 GtkAllocation *allocation);
59 static void gtk_statusbar_realize (GtkWidget *widget);
60 static void gtk_statusbar_unrealize (GtkWidget *widget);
61 static void gtk_statusbar_map (GtkWidget *widget);
62 static void gtk_statusbar_unmap (GtkWidget *widget);
63 static gboolean gtk_statusbar_button_press (GtkWidget *widget,
64 GdkEventButton *event);
65 static gboolean gtk_statusbar_expose_event (GtkWidget *widget,
66 GdkEventExpose *event);
67 static void gtk_statusbar_size_request (GtkWidget *widget,
68 GtkRequisition *requisition);
69 static void gtk_statusbar_size_allocate (GtkWidget *widget,
70 GtkAllocation *allocation);
71 static void gtk_statusbar_create_window (GtkStatusbar *statusbar);
72 static void gtk_statusbar_destroy_window (GtkStatusbar *statusbar);
74 static GtkContainerClass *parent_class;
75 static guint statusbar_signals[SIGNAL_LAST] = { 0 };
78 gtk_statusbar_get_type (void)
80 static GtkType statusbar_type = 0;
84 static const GtkTypeInfo statusbar_info =
87 sizeof (GtkStatusbar),
88 sizeof (GtkStatusbarClass),
89 (GtkClassInitFunc) gtk_statusbar_class_init,
90 (GtkObjectInitFunc) gtk_statusbar_init,
91 /* reserved_1 */ NULL,
92 /* reserved_2 */ NULL,
93 (GtkClassInitFunc) NULL,
96 statusbar_type = gtk_type_unique (GTK_TYPE_HBOX, &statusbar_info);
99 return statusbar_type;
103 gtk_statusbar_class_init (GtkStatusbarClass *class)
105 GtkObjectClass *object_class;
106 GtkWidgetClass *widget_class;
107 GtkContainerClass *container_class;
109 object_class = (GtkObjectClass *) class;
110 widget_class = (GtkWidgetClass *) class;
111 container_class = (GtkContainerClass *) class;
113 parent_class = gtk_type_class (GTK_TYPE_HBOX);
115 object_class->destroy = gtk_statusbar_destroy;
117 widget_class->realize = gtk_statusbar_realize;
118 widget_class->unrealize = gtk_statusbar_unrealize;
119 widget_class->map = gtk_statusbar_map;
120 widget_class->unmap = gtk_statusbar_unmap;
122 widget_class->button_press_event = gtk_statusbar_button_press;
123 widget_class->expose_event = gtk_statusbar_expose_event;
125 widget_class->size_request = gtk_statusbar_size_request;
126 widget_class->size_allocate = gtk_statusbar_size_allocate;
128 class->messages_mem_chunk = g_mem_chunk_new ("GtkStatusBar messages mem chunk",
129 sizeof (GtkStatusbarMsg),
130 sizeof (GtkStatusbarMsg) * 64,
133 class->text_pushed = gtk_statusbar_update;
134 class->text_popped = gtk_statusbar_update;
136 statusbar_signals[SIGNAL_TEXT_PUSHED] =
137 gtk_signal_new ("text_pushed",
139 GTK_CLASS_TYPE (object_class),
140 GTK_SIGNAL_OFFSET (GtkStatusbarClass, text_pushed),
141 gtk_marshal_VOID__UINT_STRING,
145 statusbar_signals[SIGNAL_TEXT_POPPED] =
146 gtk_signal_new ("text_popped",
148 GTK_CLASS_TYPE (object_class),
149 GTK_SIGNAL_OFFSET (GtkStatusbarClass, text_popped),
150 gtk_marshal_VOID__UINT_STRING,
155 gtk_widget_class_install_style_property (widget_class,
156 g_param_spec_enum ("shadow_type",
158 _("Style of bevel around the statusbar text"),
159 GTK_TYPE_SHADOW_TYPE,
165 gtk_statusbar_init (GtkStatusbar *statusbar)
168 GtkShadowType shadow_type;
170 box = GTK_BOX (statusbar);
173 box->homogeneous = FALSE;
175 statusbar->has_resize_grip = TRUE;
177 gtk_widget_style_get (GTK_WIDGET (statusbar), "shadow_type", &shadow_type, NULL);
179 statusbar->frame = gtk_frame_new (NULL);
180 gtk_frame_set_shadow_type (GTK_FRAME (statusbar->frame), shadow_type);
181 gtk_box_pack_start (box, statusbar->frame, TRUE, TRUE, 0);
182 gtk_widget_show (statusbar->frame);
184 statusbar->label = gtk_label_new ("");
185 gtk_misc_set_alignment (GTK_MISC (statusbar->label), 0.0, 0.0);
186 /* don't expand the size request for the label; if we
187 * do that then toplevels weirdly resize
189 gtk_widget_set_usize (statusbar->label, 1, -1);
190 gtk_container_add (GTK_CONTAINER (statusbar->frame), statusbar->label);
191 gtk_widget_show (statusbar->label);
193 statusbar->seq_context_id = 1;
194 statusbar->seq_message_id = 1;
195 statusbar->messages = NULL;
196 statusbar->keys = NULL;
200 gtk_statusbar_new (void)
202 return gtk_type_new (GTK_TYPE_STATUSBAR);
206 gtk_statusbar_update (GtkStatusbar *statusbar,
210 g_return_if_fail (statusbar != NULL);
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 (statusbar != NULL, 0);
227 g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), 0);
228 g_return_val_if_fail (context_description != NULL, 0);
230 /* we need to preserve namespaces on object datas */
231 string = g_strconcat ("gtk-status-bar-context:", context_description, NULL);
233 id = gtk_object_get_data (GTK_OBJECT (statusbar), string);
236 id = g_new (guint, 1);
237 *id = statusbar->seq_context_id++;
238 gtk_object_set_data_full (GTK_OBJECT (statusbar), string, id, (GtkDestroyNotify) g_free);
239 statusbar->keys = g_slist_prepend (statusbar->keys, string);
248 gtk_statusbar_push (GtkStatusbar *statusbar,
252 GtkStatusbarMsg *msg;
253 GtkStatusbarClass *class;
255 g_return_val_if_fail (statusbar != NULL, 0);
256 g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), 0);
257 g_return_val_if_fail (text != NULL, 0);
259 class = GTK_STATUSBAR_GET_CLASS (statusbar);
260 msg = g_chunk_new (GtkStatusbarMsg, class->messages_mem_chunk);
261 msg->text = g_strdup (text);
262 msg->context_id = context_id;
263 msg->message_id = statusbar->seq_message_id++;
265 statusbar->messages = g_slist_prepend (statusbar->messages, msg);
267 gtk_signal_emit (GTK_OBJECT (statusbar),
268 statusbar_signals[SIGNAL_TEXT_PUSHED],
272 return msg->message_id;
276 gtk_statusbar_pop (GtkStatusbar *statusbar,
279 GtkStatusbarMsg *msg;
281 g_return_if_fail (statusbar != NULL);
282 g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
284 if (statusbar->messages)
288 for (list = statusbar->messages; list; list = list->next)
292 if (msg->context_id == context_id)
294 GtkStatusbarClass *class;
296 class = GTK_STATUSBAR_GET_CLASS (statusbar);
298 statusbar->messages = g_slist_remove_link (statusbar->messages,
301 g_mem_chunk_free (class->messages_mem_chunk, msg);
302 g_slist_free_1 (list);
308 msg = statusbar->messages ? statusbar->messages->data : NULL;
310 gtk_signal_emit (GTK_OBJECT (statusbar),
311 statusbar_signals[SIGNAL_TEXT_POPPED],
312 (guint) (msg ? msg->context_id : 0),
313 msg ? msg->text : NULL);
317 gtk_statusbar_remove (GtkStatusbar *statusbar,
321 GtkStatusbarMsg *msg;
323 g_return_if_fail (statusbar != NULL);
324 g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
325 g_return_if_fail (message_id > 0);
327 msg = statusbar->messages ? statusbar->messages->data : NULL;
332 /* care about signal emission if the topmost item is removed */
333 if (msg->context_id == context_id &&
334 msg->message_id == message_id)
336 gtk_statusbar_pop (statusbar, context_id);
340 for (list = statusbar->messages; list; list = list->next)
344 if (msg->context_id == context_id &&
345 msg->message_id == message_id)
347 GtkStatusbarClass *class;
349 class = GTK_STATUSBAR_GET_CLASS (statusbar);
350 statusbar->messages = g_slist_remove_link (statusbar->messages, list);
352 g_mem_chunk_free (class->messages_mem_chunk, msg);
353 g_slist_free_1 (list);
362 gtk_statusbar_set_has_resize_grip (GtkStatusbar *statusbar,
365 g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
367 setting = setting != FALSE;
369 if (setting != statusbar->has_resize_grip)
371 statusbar->has_resize_grip = setting;
372 gtk_widget_queue_draw (GTK_WIDGET (statusbar));
374 if (GTK_WIDGET_REALIZED (statusbar))
376 if (statusbar->has_resize_grip && statusbar->grip_window == NULL)
377 gtk_statusbar_create_window (statusbar);
378 else if (!statusbar->has_resize_grip && statusbar->grip_window != NULL)
379 gtk_statusbar_destroy_window (statusbar);
385 gtk_statusbar_get_has_resize_grip (GtkStatusbar *statusbar)
387 g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), FALSE);
389 return statusbar->has_resize_grip;
393 gtk_statusbar_destroy (GtkObject *object)
395 GtkStatusbar *statusbar;
396 GtkStatusbarClass *class;
399 g_return_if_fail (object != NULL);
400 g_return_if_fail (GTK_IS_STATUSBAR (object));
402 statusbar = GTK_STATUSBAR (object);
403 class = GTK_STATUSBAR_GET_CLASS (statusbar);
405 for (list = statusbar->messages; list; list = list->next)
407 GtkStatusbarMsg *msg;
411 g_mem_chunk_free (class->messages_mem_chunk, msg);
413 g_slist_free (statusbar->messages);
414 statusbar->messages = NULL;
416 for (list = statusbar->keys; list; list = list->next)
418 g_slist_free (statusbar->keys);
419 statusbar->keys = NULL;
421 GTK_OBJECT_CLASS (parent_class)->destroy (object);
425 get_grip_rect (GtkStatusbar *statusbar,
431 widget = GTK_WIDGET (statusbar);
433 /* These are in effect the max/default size of the grip. */
437 if (w > (widget->allocation.width))
438 w = widget->allocation.width;
440 if (h > (widget->allocation.height - widget->style->ythickness))
441 h = widget->allocation.height - widget->style->ythickness;
443 rect->x = widget->allocation.x + widget->allocation.width - w;
444 rect->y = widget->allocation.y + widget->allocation.height - h;
450 gtk_statusbar_create_window (GtkStatusbar *statusbar)
453 GdkWindowAttr attributes;
454 gint attributes_mask;
457 g_return_if_fail (GTK_WIDGET_REALIZED (statusbar));
458 g_return_if_fail (statusbar->has_resize_grip);
460 widget = GTK_WIDGET (statusbar);
462 get_grip_rect (statusbar, &rect);
464 attributes.x = rect.x;
465 attributes.y = rect.y;
466 attributes.width = rect.width;
467 attributes.height = rect.height;
468 attributes.window_type = GDK_WINDOW_CHILD;
469 attributes.wclass = GDK_INPUT_ONLY;
470 attributes.event_mask = gtk_widget_get_events (widget) |
471 GDK_BUTTON_PRESS_MASK;
473 attributes_mask = GDK_WA_X | GDK_WA_Y;
475 statusbar->grip_window = gdk_window_new (widget->window,
476 &attributes, attributes_mask);
477 gdk_window_set_user_data (statusbar->grip_window, widget);
481 gtk_statusbar_destroy_window (GtkStatusbar *statusbar)
483 gdk_window_set_user_data (statusbar->grip_window, NULL);
484 gdk_window_destroy (statusbar->grip_window);
485 statusbar->grip_window = NULL;
489 gtk_statusbar_realize (GtkWidget *widget)
491 GtkStatusbar *statusbar;
493 statusbar = GTK_STATUSBAR (widget);
495 (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
497 if (statusbar->has_resize_grip)
498 gtk_statusbar_create_window (statusbar);
502 gtk_statusbar_unrealize (GtkWidget *widget)
504 GtkStatusbar *statusbar;
506 statusbar = GTK_STATUSBAR (widget);
508 if (statusbar->grip_window)
509 gtk_statusbar_destroy_window (statusbar);
511 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
515 gtk_statusbar_map (GtkWidget *widget)
517 GtkStatusbar *statusbar;
519 statusbar = GTK_STATUSBAR (widget);
521 (* GTK_WIDGET_CLASS (parent_class)->map) (widget);
523 if (statusbar->grip_window)
524 gdk_window_show (statusbar->grip_window);
528 gtk_statusbar_unmap (GtkWidget *widget)
530 GtkStatusbar *statusbar;
532 statusbar = GTK_STATUSBAR (widget);
534 if (statusbar->grip_window)
535 gdk_window_hide (statusbar->grip_window);
537 (* GTK_WIDGET_CLASS (parent_class)->unmap) (widget);
541 gtk_statusbar_button_press (GtkWidget *widget,
542 GdkEventButton *event)
544 GtkStatusbar *statusbar;
547 statusbar = GTK_STATUSBAR (widget);
549 if (!statusbar->has_resize_grip)
552 ancestor = gtk_widget_get_toplevel (widget);
554 if (!GTK_IS_WINDOW (ancestor))
557 if (event->button == 1)
558 gtk_window_begin_resize_drag (GTK_WINDOW (ancestor),
559 GDK_WINDOW_EDGE_SOUTH_EAST,
561 event->x_root, event->y_root,
563 else if (event->button == 2)
564 gtk_window_begin_move_drag (GTK_WINDOW (ancestor),
566 event->x_root, event->y_root,
575 gtk_statusbar_expose_event (GtkWidget *widget,
576 GdkEventExpose *event)
578 GtkStatusbar *statusbar;
581 statusbar = GTK_STATUSBAR (widget);
583 GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
585 if (statusbar->has_resize_grip)
587 get_grip_rect (statusbar, &rect);
589 gtk_paint_resize_grip (widget->style,
591 GTK_WIDGET_STATE (widget),
595 GDK_WINDOW_EDGE_SOUTH_EAST,
597 /* don't draw grip over the frame, though you
598 * can click on the frame.
600 rect.width - widget->style->xthickness,
601 rect.height - widget->style->ythickness);
608 gtk_statusbar_size_request (GtkWidget *widget,
609 GtkRequisition *requisition)
611 GtkStatusbar *statusbar;
612 GtkShadowType shadow_type;
614 statusbar = GTK_STATUSBAR (widget);
616 gtk_widget_style_get (GTK_WIDGET (statusbar), "shadow_type", &shadow_type, NULL);
617 gtk_frame_set_shadow_type (GTK_FRAME (statusbar->frame), shadow_type);
619 GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
621 if (statusbar->has_resize_grip)
625 /* x, y in the grip rect depend on size allocation, but
626 * w, h do not so this is OK
628 get_grip_rect (statusbar, &rect);
630 requisition->width += rect.width;
631 requisition->height = MAX (requisition->height, rect.height);
636 gtk_statusbar_size_allocate (GtkWidget *widget,
637 GtkAllocation *allocation)
639 GtkStatusbar *statusbar;
641 statusbar = GTK_STATUSBAR (widget);
643 if (statusbar->has_resize_grip)
646 GtkRequisition saved_req;
648 widget->allocation = *allocation; /* get_grip_rect needs this info */
649 get_grip_rect (statusbar, &rect);
651 if (statusbar->grip_window)
652 gdk_window_move_resize (statusbar->grip_window,
654 rect.width, rect.height);
656 /* enter the bad hack zone */
657 saved_req = widget->requisition;
658 widget->requisition.width -= rect.width; /* HBox::size_allocate needs this */
659 if (widget->requisition.width < 0)
660 widget->requisition.width = 0;
661 GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
662 widget->requisition = saved_req;
666 /* chain up normally */
667 GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);