]> Pileus Git - ~andy/gtk/blob - gtk/gtkstatusbar.c
Add gtkcolorbutton.c and gtkfontbutton.c.
[~andy/gtk] / gtk / gtkstatusbar.c
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
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 /*
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/. 
26  */
27
28 #include "gtkframe.h"
29 #include "gtklabel.h"
30 #include "gtkmarshalers.h"
31 #include "gtkstatusbar.h"
32 #include "gtkwindow.h"
33 #include "gtkintl.h"
34
35 typedef struct _GtkStatusbarMsg GtkStatusbarMsg;
36
37 struct _GtkStatusbarMsg
38 {
39   gchar *text;
40   guint context_id;
41   guint message_id;
42 };
43
44 enum
45 {
46   SIGNAL_TEXT_PUSHED,
47   SIGNAL_TEXT_POPPED,
48   SIGNAL_LAST
49 };
50
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,
55                                               guint              context_id,
56                                               const gchar       *text);
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);
73
74 static GtkContainerClass *parent_class;
75 static guint              statusbar_signals[SIGNAL_LAST] = { 0 };
76
77 GType      
78 gtk_statusbar_get_type (void)
79 {
80   static GType statusbar_type = 0;
81
82   if (!statusbar_type)
83     {
84       static const GTypeInfo statusbar_info =
85       {
86         sizeof (GtkStatusbarClass),
87         NULL,           /* base_init */
88         NULL,           /* base_finalize */
89         (GClassInitFunc) gtk_statusbar_class_init,
90         NULL,           /* class_finalize */
91         NULL,           /* class_data */
92         sizeof (GtkStatusbar),
93         0,              /* n_preallocs */
94         (GInstanceInitFunc) gtk_statusbar_init,
95       };
96
97       statusbar_type = g_type_register_static (GTK_TYPE_HBOX, "GtkStatusbar",
98                                                &statusbar_info, 0);
99     }
100
101   return statusbar_type;
102 }
103
104 static void
105 gtk_statusbar_class_init (GtkStatusbarClass *class)
106 {
107   GtkObjectClass *object_class;
108   GtkWidgetClass *widget_class;
109   GtkContainerClass *container_class;
110
111   object_class = (GtkObjectClass *) class;
112   widget_class = (GtkWidgetClass *) class;
113   container_class = (GtkContainerClass *) class;
114
115   parent_class = g_type_class_peek_parent (class);
116   
117   object_class->destroy = gtk_statusbar_destroy;
118
119   widget_class->realize = gtk_statusbar_realize;
120   widget_class->unrealize = gtk_statusbar_unrealize;
121   widget_class->map = gtk_statusbar_map;
122   widget_class->unmap = gtk_statusbar_unmap;
123   
124   widget_class->button_press_event = gtk_statusbar_button_press;
125   widget_class->expose_event = gtk_statusbar_expose_event;
126
127   widget_class->size_request = gtk_statusbar_size_request;
128   widget_class->size_allocate = gtk_statusbar_size_allocate;
129   
130   class->messages_mem_chunk = g_mem_chunk_new ("GtkStatusbar messages mem chunk",
131                                                sizeof (GtkStatusbarMsg),
132                                                sizeof (GtkStatusbarMsg) * 64,
133                                                G_ALLOC_AND_FREE);
134
135   class->text_pushed = gtk_statusbar_update;
136   class->text_popped = gtk_statusbar_update;
137   
138   statusbar_signals[SIGNAL_TEXT_PUSHED] =
139     g_signal_new ("text_pushed",
140                   G_OBJECT_CLASS_TYPE (class),
141                   G_SIGNAL_RUN_LAST,
142                   G_STRUCT_OFFSET (GtkStatusbarClass, text_pushed),
143                   NULL, NULL,
144                   _gtk_marshal_VOID__UINT_STRING,
145                   G_TYPE_NONE, 2,
146                   G_TYPE_UINT,
147                   G_TYPE_STRING);
148   statusbar_signals[SIGNAL_TEXT_POPPED] =
149     g_signal_new ("text_popped",
150                   G_OBJECT_CLASS_TYPE (class),
151                   G_SIGNAL_RUN_LAST,
152                   G_STRUCT_OFFSET (GtkStatusbarClass, text_popped),
153                   NULL, NULL,
154                   _gtk_marshal_VOID__UINT_STRING,
155                   G_TYPE_NONE, 2,
156                   G_TYPE_UINT,
157                   G_TYPE_STRING);
158
159   gtk_widget_class_install_style_property (widget_class,
160                                            g_param_spec_enum ("shadow_type",
161                                                               _("Shadow type"),
162                                                               _("Style of bevel around the statusbar text"),
163                                                               GTK_TYPE_SHADOW_TYPE,
164                                                               GTK_SHADOW_IN,
165                                                               G_PARAM_READABLE));
166 }
167
168 static void
169 gtk_statusbar_init (GtkStatusbar *statusbar)
170 {
171   GtkBox *box;
172   GtkShadowType shadow_type;
173   
174   box = GTK_BOX (statusbar);
175
176   box->spacing = 2;
177   box->homogeneous = FALSE;
178
179   statusbar->has_resize_grip = TRUE;
180
181   gtk_widget_style_get (GTK_WIDGET (statusbar), "shadow_type", &shadow_type, NULL);
182   
183   statusbar->frame = gtk_frame_new (NULL);
184   gtk_frame_set_shadow_type (GTK_FRAME (statusbar->frame), shadow_type);
185   gtk_box_pack_start (box, statusbar->frame, TRUE, TRUE, 0);
186   gtk_widget_show (statusbar->frame);
187
188   statusbar->label = gtk_label_new ("");
189   gtk_misc_set_alignment (GTK_MISC (statusbar->label), 0.0, 0.5);
190   /* don't expand the size request for the label; if we
191    * do that then toplevels weirdly resize
192    */
193   gtk_widget_set_size_request (statusbar->label, 1, -1);
194   gtk_container_add (GTK_CONTAINER (statusbar->frame), statusbar->label);
195   gtk_widget_show (statusbar->label);
196
197   statusbar->seq_context_id = 1;
198   statusbar->seq_message_id = 1;
199   statusbar->messages = NULL;
200   statusbar->keys = NULL;
201 }
202
203 GtkWidget* 
204 gtk_statusbar_new (void)
205 {
206   return g_object_new (GTK_TYPE_STATUSBAR, NULL);
207 }
208
209 static void
210 gtk_statusbar_update (GtkStatusbar *statusbar,
211                       guint         context_id,
212                       const gchar  *text)
213 {
214   g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
215
216   if (!text)
217     text = "";
218
219   gtk_label_set_text (GTK_LABEL (statusbar->label), text);
220 }
221
222 guint
223 gtk_statusbar_get_context_id (GtkStatusbar *statusbar,
224                               const gchar  *context_description)
225 {
226   gchar *string;
227   guint *id;
228   
229   g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), 0);
230   g_return_val_if_fail (context_description != NULL, 0);
231
232   /* we need to preserve namespaces on object datas */
233   string = g_strconcat ("gtk-status-bar-context:", context_description, NULL);
234
235   id = g_object_get_data (G_OBJECT (statusbar), string);
236   if (!id)
237     {
238       id = g_new (guint, 1);
239       *id = statusbar->seq_context_id++;
240       g_object_set_data_full (G_OBJECT (statusbar), string, id, g_free);
241       statusbar->keys = g_slist_prepend (statusbar->keys, string);
242     }
243   else
244     g_free (string);
245
246   return *id;
247 }
248
249 guint
250 gtk_statusbar_push (GtkStatusbar *statusbar,
251                     guint         context_id,
252                     const gchar  *text)
253 {
254   GtkStatusbarMsg *msg;
255   GtkStatusbarClass *class;
256
257   g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), 0);
258   g_return_val_if_fail (text != NULL, 0);
259
260   class = GTK_STATUSBAR_GET_CLASS (statusbar);
261   msg = g_chunk_new (GtkStatusbarMsg, class->messages_mem_chunk);
262   msg->text = g_strdup (text);
263   msg->context_id = context_id;
264   msg->message_id = statusbar->seq_message_id++;
265
266   statusbar->messages = g_slist_prepend (statusbar->messages, msg);
267
268   g_signal_emit (statusbar,
269                  statusbar_signals[SIGNAL_TEXT_PUSHED],
270                  0,
271                  msg->context_id,
272                  msg->text);
273
274   return msg->message_id;
275 }
276
277 void
278 gtk_statusbar_pop (GtkStatusbar *statusbar,
279                    guint         context_id)
280 {
281   GtkStatusbarMsg *msg;
282
283   g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
284
285   if (statusbar->messages)
286     {
287       GSList *list;
288
289       for (list = statusbar->messages; list; list = list->next)
290         {
291           msg = list->data;
292
293           if (msg->context_id == context_id)
294             {
295               GtkStatusbarClass *class;
296
297               class = GTK_STATUSBAR_GET_CLASS (statusbar);
298
299               statusbar->messages = g_slist_remove_link (statusbar->messages,
300                                                          list);
301               g_free (msg->text);
302               g_mem_chunk_free (class->messages_mem_chunk, msg);
303               g_slist_free_1 (list);
304               break;
305             }
306         }
307     }
308
309   msg = statusbar->messages ? statusbar->messages->data : NULL;
310
311   g_signal_emit (statusbar,
312                  statusbar_signals[SIGNAL_TEXT_POPPED],
313                  0,
314                  (guint) (msg ? msg->context_id : 0),
315                  msg ? msg->text : NULL);
316 }
317
318 void
319 gtk_statusbar_remove (GtkStatusbar *statusbar,
320                       guint        context_id,
321                       guint        message_id)
322 {
323   GtkStatusbarMsg *msg;
324
325   g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
326   g_return_if_fail (message_id > 0);
327
328   msg = statusbar->messages ? statusbar->messages->data : NULL;
329   if (msg)
330     {
331       GSList *list;
332
333       /* care about signal emission if the topmost item is removed */
334       if (msg->context_id == context_id &&
335           msg->message_id == message_id)
336         {
337           gtk_statusbar_pop (statusbar, context_id);
338           return;
339         }
340       
341       for (list = statusbar->messages; list; list = list->next)
342         {
343           msg = list->data;
344           
345           if (msg->context_id == context_id &&
346               msg->message_id == message_id)
347             {
348               GtkStatusbarClass *class;
349               
350               class = GTK_STATUSBAR_GET_CLASS (statusbar);
351               statusbar->messages = g_slist_remove_link (statusbar->messages, list);
352               g_free (msg->text);
353               g_mem_chunk_free (class->messages_mem_chunk, msg);
354               g_slist_free_1 (list);
355               
356               break;
357             }
358         }
359     }
360 }
361
362 void
363 gtk_statusbar_set_has_resize_grip (GtkStatusbar *statusbar,
364                                    gboolean      setting)
365 {
366   g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
367
368   setting = setting != FALSE;
369
370   if (setting != statusbar->has_resize_grip)
371     {
372       statusbar->has_resize_grip = setting;
373       gtk_widget_queue_draw (GTK_WIDGET (statusbar));
374
375       if (GTK_WIDGET_REALIZED (statusbar))
376         {
377           if (statusbar->has_resize_grip && statusbar->grip_window == NULL)
378             gtk_statusbar_create_window (statusbar);
379           else if (!statusbar->has_resize_grip && statusbar->grip_window != NULL)
380             gtk_statusbar_destroy_window (statusbar);
381         }
382     }
383 }
384
385 gboolean
386 gtk_statusbar_get_has_resize_grip (GtkStatusbar *statusbar)
387 {
388   g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), FALSE);
389
390   return statusbar->has_resize_grip;
391 }
392
393 static void
394 gtk_statusbar_destroy (GtkObject *object)
395 {
396   GtkStatusbar *statusbar;
397   GtkStatusbarClass *class;
398   GSList *list;
399
400   g_return_if_fail (GTK_IS_STATUSBAR (object));
401
402   statusbar = GTK_STATUSBAR (object);
403   class = GTK_STATUSBAR_GET_CLASS (statusbar);
404
405   for (list = statusbar->messages; list; list = list->next)
406     {
407       GtkStatusbarMsg *msg;
408
409       msg = list->data;
410       g_free (msg->text);
411       g_mem_chunk_free (class->messages_mem_chunk, msg);
412     }
413   g_slist_free (statusbar->messages);
414   statusbar->messages = NULL;
415
416   for (list = statusbar->keys; list; list = list->next)
417     g_free (list->data);
418   g_slist_free (statusbar->keys);
419   statusbar->keys = NULL;
420
421   GTK_OBJECT_CLASS (parent_class)->destroy (object);
422 }
423
424 static GdkWindowEdge
425 get_grip_edge (GtkStatusbar *statusbar)
426 {
427   GtkWidget *widget = GTK_WIDGET (statusbar);
428
429   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
430     return GDK_WINDOW_EDGE_SOUTH_EAST; 
431   else
432     return GDK_WINDOW_EDGE_SOUTH_WEST; 
433 }
434
435 static void
436 get_grip_rect (GtkStatusbar *statusbar,
437                GdkRectangle *rect)
438 {
439   GtkWidget *widget;
440   gint w, h;
441   
442   widget = GTK_WIDGET (statusbar);
443
444   /* These are in effect the max/default size of the grip. */
445   w = 18;
446   h = 18;
447
448   if (w > (widget->allocation.width))
449     w = widget->allocation.width;
450
451   if (h > (widget->allocation.height - widget->style->ythickness))
452     h = widget->allocation.height - widget->style->ythickness;
453   
454   rect->width = w;
455   rect->height = h;
456   rect->y = widget->allocation.y + widget->allocation.height - h;
457
458   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
459       rect->x = widget->allocation.x + widget->allocation.width - w;
460   else 
461       rect->x = widget->allocation.x + widget->style->xthickness;
462 }
463
464 static void
465 gtk_statusbar_create_window (GtkStatusbar *statusbar)
466 {
467   GtkWidget *widget;
468   GdkWindowAttr attributes;
469   gint attributes_mask;
470   GdkRectangle rect;
471   
472   g_return_if_fail (GTK_WIDGET_REALIZED (statusbar));
473   g_return_if_fail (statusbar->has_resize_grip);
474   
475   widget = GTK_WIDGET (statusbar);
476
477   get_grip_rect (statusbar, &rect);
478
479   attributes.x = rect.x;
480   attributes.y = rect.y;
481   attributes.width = rect.width;
482   attributes.height = rect.height;
483   attributes.window_type = GDK_WINDOW_CHILD;
484   attributes.wclass = GDK_INPUT_ONLY;
485   attributes.event_mask = gtk_widget_get_events (widget) |
486     GDK_BUTTON_PRESS_MASK;
487
488   attributes_mask = GDK_WA_X | GDK_WA_Y;
489
490   statusbar->grip_window = gdk_window_new (widget->window,
491                                            &attributes, attributes_mask);
492   gdk_window_set_user_data (statusbar->grip_window, widget);
493 }
494
495 static void
496 gtk_statusbar_destroy_window (GtkStatusbar *statusbar)
497 {
498   gdk_window_set_user_data (statusbar->grip_window, NULL);
499   gdk_window_destroy (statusbar->grip_window);
500   statusbar->grip_window = NULL;
501 }
502
503 static void
504 gtk_statusbar_realize (GtkWidget *widget)
505 {
506   GtkStatusbar *statusbar;
507
508   statusbar = GTK_STATUSBAR (widget);
509   
510   (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
511
512   if (statusbar->has_resize_grip)
513     gtk_statusbar_create_window (statusbar);
514 }
515
516 static void
517 gtk_statusbar_unrealize (GtkWidget *widget)
518 {
519   GtkStatusbar *statusbar;
520
521   statusbar = GTK_STATUSBAR (widget);
522
523   if (statusbar->grip_window)
524     gtk_statusbar_destroy_window (statusbar);
525   
526   (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
527 }
528
529 static void
530 gtk_statusbar_map (GtkWidget *widget)
531 {
532   GtkStatusbar *statusbar;
533
534   statusbar = GTK_STATUSBAR (widget);
535   
536   (* GTK_WIDGET_CLASS (parent_class)->map) (widget);
537   
538   if (statusbar->grip_window)
539     gdk_window_show (statusbar->grip_window);
540 }
541
542 static void
543 gtk_statusbar_unmap (GtkWidget *widget)
544 {
545   GtkStatusbar *statusbar;
546
547   statusbar = GTK_STATUSBAR (widget);
548
549   if (statusbar->grip_window)
550     gdk_window_hide (statusbar->grip_window);
551   
552   (* GTK_WIDGET_CLASS (parent_class)->unmap) (widget);
553 }
554
555 static gboolean
556 gtk_statusbar_button_press (GtkWidget      *widget,
557                             GdkEventButton *event)
558 {
559   GtkStatusbar *statusbar;
560   GtkWidget *ancestor;
561   GdkWindowEdge edge;
562   
563   statusbar = GTK_STATUSBAR (widget);
564   
565   if (!statusbar->has_resize_grip ||
566     event->type != GDK_BUTTON_PRESS)
567     return FALSE;
568   
569   ancestor = gtk_widget_get_toplevel (widget);
570
571   if (!GTK_IS_WINDOW (ancestor))
572     return FALSE;
573
574   edge = get_grip_edge (statusbar);
575
576   if (event->button == 1)
577     gtk_window_begin_resize_drag (GTK_WINDOW (ancestor),
578                                   edge,
579                                   event->button,
580                                   event->x_root, event->y_root,
581                                   event->time);
582   else if (event->button == 2)
583     gtk_window_begin_move_drag (GTK_WINDOW (ancestor),
584                                 event->button,
585                                 event->x_root, event->y_root,
586                                 event->time);
587   else
588     return FALSE;
589   
590   return TRUE;
591 }
592
593 static gboolean
594 gtk_statusbar_expose_event (GtkWidget      *widget,
595                             GdkEventExpose *event)
596 {
597   GtkStatusbar *statusbar;
598   GdkRectangle rect;
599   
600   statusbar = GTK_STATUSBAR (widget);
601
602   GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
603
604   if (statusbar->has_resize_grip)
605     {
606       GdkWindowEdge edge;
607       
608       edge = get_grip_edge (statusbar);
609
610       get_grip_rect (statusbar, &rect);
611
612       gtk_paint_resize_grip (widget->style,
613                              widget->window,
614                              GTK_WIDGET_STATE (widget),
615                              NULL,
616                              widget,
617                              "statusbar",
618                              edge,
619                              rect.x, rect.y,
620                              /* don't draw grip over the frame, though you
621                               * can click on the frame.
622                               */
623                              rect.width - widget->style->xthickness,
624                              rect.height - widget->style->ythickness);
625     }
626
627   return FALSE;
628 }
629
630 static void
631 gtk_statusbar_size_request   (GtkWidget      *widget,
632                               GtkRequisition *requisition)
633 {
634   GtkStatusbar *statusbar;
635   GtkShadowType shadow_type;
636   
637   statusbar = GTK_STATUSBAR (widget);
638
639   gtk_widget_style_get (GTK_WIDGET (statusbar), "shadow_type", &shadow_type, NULL);  
640   gtk_frame_set_shadow_type (GTK_FRAME (statusbar->frame), shadow_type);
641   
642   GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
643
644   if (statusbar->has_resize_grip)
645     {
646       GdkRectangle rect;
647
648       /* x, y in the grip rect depend on size allocation, but
649        * w, h do not so this is OK
650        */
651       get_grip_rect (statusbar, &rect);
652       
653       requisition->width += rect.width;
654       requisition->height = MAX (requisition->height, rect.height);
655     }
656 }
657
658 static void
659 gtk_statusbar_size_allocate  (GtkWidget     *widget,
660                               GtkAllocation *allocation)
661 {
662   GtkStatusbar *statusbar;
663   
664   statusbar = GTK_STATUSBAR (widget);
665
666   if (statusbar->has_resize_grip)
667     {
668       GdkRectangle rect;
669       GtkRequisition saved_req;
670       
671       widget->allocation = *allocation; /* get_grip_rect needs this info */
672       get_grip_rect (statusbar, &rect);
673   
674       if (statusbar->grip_window)
675         gdk_window_move_resize (statusbar->grip_window,
676                                 rect.x, rect.y,
677                                 rect.width, rect.height);
678       
679       /* enter the bad hack zone */      
680       saved_req = widget->requisition;
681       widget->requisition.width -= rect.width; /* HBox::size_allocate needs this */
682       if (widget->requisition.width < 0)
683         widget->requisition.width = 0;
684       GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
685       widget->requisition = saved_req;
686     }
687   else
688     {
689       /* chain up normally */
690       GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
691     }
692 }