]> Pileus Git - ~andy/gtk/blob - gtk/gtkstatusbar.c
*** empty log message ***
[~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 "gtksignal.h"
31 #include "gtkstatusbar.h"
32 #include "gtkwindow.h"
33
34 typedef struct _GtkStatusbarMsg GtkStatusbarMsg;
35
36 struct _GtkStatusbarMsg
37 {
38   gchar *text;
39   guint context_id;
40   guint message_id;
41 };
42
43 enum
44 {
45   SIGNAL_TEXT_PUSHED,
46   SIGNAL_TEXT_POPPED,
47   SIGNAL_LAST
48 };
49
50 static void     gtk_statusbar_class_init     (GtkStatusbarClass *class);
51 static void     gtk_statusbar_init           (GtkStatusbar      *statusbar);
52 static void     gtk_statusbar_destroy        (GtkObject         *object);
53 static void     gtk_statusbar_update         (GtkStatusbar      *statusbar,
54                                               guint              context_id,
55                                               const gchar       *text);
56 static void     gtk_statusbar_size_allocate  (GtkWidget         *widget,
57                                               GtkAllocation     *allocation);
58 static void     gtk_statusbar_realize        (GtkWidget         *widget);
59 static void     gtk_statusbar_unrealize      (GtkWidget         *widget);
60 static void     gtk_statusbar_map            (GtkWidget         *widget);
61 static void     gtk_statusbar_unmap          (GtkWidget         *widget);
62 static gboolean gtk_statusbar_button_press   (GtkWidget         *widget,
63                                               GdkEventButton    *event);
64 static gboolean gtk_statusbar_expose_event   (GtkWidget         *widget,
65                                               GdkEventExpose    *event);
66 static void     gtk_statusbar_create_window  (GtkStatusbar      *statusbar);
67 static void     gtk_statusbar_destroy_window (GtkStatusbar      *statusbar);
68
69 static GtkContainerClass *parent_class;
70 static guint              statusbar_signals[SIGNAL_LAST] = { 0 };
71
72 GtkType      
73 gtk_statusbar_get_type (void)
74 {
75   static GtkType statusbar_type = 0;
76
77   if (!statusbar_type)
78     {
79       static const GtkTypeInfo statusbar_info =
80       {
81         "GtkStatusbar",
82         sizeof (GtkStatusbar),
83         sizeof (GtkStatusbarClass),
84         (GtkClassInitFunc) gtk_statusbar_class_init,
85         (GtkObjectInitFunc) gtk_statusbar_init,
86         /* reserved_1 */ NULL,
87         /* reserved_2 */ NULL,
88         (GtkClassInitFunc) NULL,
89       };
90
91       statusbar_type = gtk_type_unique (GTK_TYPE_HBOX, &statusbar_info);
92     }
93
94   return statusbar_type;
95 }
96
97 static void
98 gtk_statusbar_class_init (GtkStatusbarClass *class)
99 {
100   GtkObjectClass *object_class;
101   GtkWidgetClass *widget_class;
102   GtkContainerClass *container_class;
103
104   object_class = (GtkObjectClass *) class;
105   widget_class = (GtkWidgetClass *) class;
106   container_class = (GtkContainerClass *) class;
107
108   parent_class = gtk_type_class (GTK_TYPE_HBOX);
109   
110   object_class->destroy = gtk_statusbar_destroy;
111
112   widget_class->size_allocate = gtk_statusbar_size_allocate;
113   
114   widget_class->realize = gtk_statusbar_realize;
115   widget_class->unrealize = gtk_statusbar_unrealize;
116   widget_class->map = gtk_statusbar_map;
117   widget_class->unmap = gtk_statusbar_unmap;
118   
119   widget_class->button_press_event = gtk_statusbar_button_press;
120   widget_class->expose_event = gtk_statusbar_expose_event;
121   
122   class->messages_mem_chunk = g_mem_chunk_new ("GtkStatusBar messages mem chunk",
123                                                sizeof (GtkStatusbarMsg),
124                                                sizeof (GtkStatusbarMsg) * 64,
125                                                G_ALLOC_AND_FREE);
126
127   class->text_pushed = gtk_statusbar_update;
128   class->text_popped = gtk_statusbar_update;
129   
130   statusbar_signals[SIGNAL_TEXT_PUSHED] =
131     gtk_signal_new ("text_pushed",
132                     GTK_RUN_LAST,
133                     GTK_CLASS_TYPE (object_class),
134                     GTK_SIGNAL_OFFSET (GtkStatusbarClass, text_pushed),
135                     gtk_marshal_VOID__UINT_STRING,
136                     GTK_TYPE_NONE, 2,
137                     GTK_TYPE_UINT,
138                     GTK_TYPE_STRING);
139   statusbar_signals[SIGNAL_TEXT_POPPED] =
140     gtk_signal_new ("text_popped",
141                     GTK_RUN_LAST,
142                     GTK_CLASS_TYPE (object_class),
143                     GTK_SIGNAL_OFFSET (GtkStatusbarClass, text_popped),
144                     gtk_marshal_VOID__UINT_STRING,
145                     GTK_TYPE_NONE, 2,
146                     GTK_TYPE_UINT,
147                     GTK_TYPE_STRING);
148 }
149
150 static void
151 gtk_statusbar_init (GtkStatusbar *statusbar)
152 {
153   GtkBox *box;
154
155   box = GTK_BOX (statusbar);
156
157   box->spacing = 2;
158   box->homogeneous = FALSE;
159
160   statusbar->has_resize_grip = TRUE;
161   
162   statusbar->frame = gtk_frame_new (NULL);
163   gtk_frame_set_shadow_type (GTK_FRAME (statusbar->frame), GTK_SHADOW_IN);
164   gtk_box_pack_start (box, statusbar->frame, TRUE, TRUE, 0);
165   gtk_widget_show (statusbar->frame);
166
167   statusbar->label = gtk_label_new ("");
168   gtk_misc_set_alignment (GTK_MISC (statusbar->label), 0.0, 0.0);
169   gtk_container_add (GTK_CONTAINER (statusbar->frame), statusbar->label);
170   gtk_widget_show (statusbar->label);
171
172   statusbar->seq_context_id = 1;
173   statusbar->seq_message_id = 1;
174   statusbar->messages = NULL;
175   statusbar->keys = NULL;
176 }
177
178 GtkWidget* 
179 gtk_statusbar_new (void)
180 {
181   return gtk_type_new (GTK_TYPE_STATUSBAR);
182 }
183
184 static void
185 gtk_statusbar_update (GtkStatusbar *statusbar,
186                       guint         context_id,
187                       const gchar  *text)
188 {
189   g_return_if_fail (statusbar != NULL);
190   g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
191
192   if (!text)
193     text = "";
194
195   gtk_label_set_text (GTK_LABEL (statusbar->label), text);
196 }
197
198 guint
199 gtk_statusbar_get_context_id (GtkStatusbar *statusbar,
200                               const gchar  *context_description)
201 {
202   gchar *string;
203   guint *id;
204   
205   g_return_val_if_fail (statusbar != NULL, 0);
206   g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), 0);
207   g_return_val_if_fail (context_description != NULL, 0);
208
209   /* we need to preserve namespaces on object datas */
210   string = g_strconcat ("gtk-status-bar-context:", context_description, NULL);
211
212   id = gtk_object_get_data (GTK_OBJECT (statusbar), string);
213   if (!id)
214     {
215       id = g_new (guint, 1);
216       *id = statusbar->seq_context_id++;
217       gtk_object_set_data_full (GTK_OBJECT (statusbar), string, id, (GtkDestroyNotify) g_free);
218       statusbar->keys = g_slist_prepend (statusbar->keys, string);
219     }
220   else
221     g_free (string);
222
223   return *id;
224 }
225
226 guint
227 gtk_statusbar_push (GtkStatusbar *statusbar,
228                     guint         context_id,
229                     const gchar  *text)
230 {
231   GtkStatusbarMsg *msg;
232   GtkStatusbarClass *class;
233
234   g_return_val_if_fail (statusbar != NULL, 0);
235   g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), 0);
236   g_return_val_if_fail (text != NULL, 0);
237
238   class = GTK_STATUSBAR_GET_CLASS (statusbar);
239   msg = g_chunk_new (GtkStatusbarMsg, class->messages_mem_chunk);
240   msg->text = g_strdup (text);
241   msg->context_id = context_id;
242   msg->message_id = statusbar->seq_message_id++;
243
244   statusbar->messages = g_slist_prepend (statusbar->messages, msg);
245
246   gtk_signal_emit (GTK_OBJECT (statusbar),
247                    statusbar_signals[SIGNAL_TEXT_PUSHED],
248                    msg->context_id,
249                    msg->text);
250
251   return msg->message_id;
252 }
253
254 void
255 gtk_statusbar_pop (GtkStatusbar *statusbar,
256                    guint         context_id)
257 {
258   GtkStatusbarMsg *msg;
259
260   g_return_if_fail (statusbar != NULL);
261   g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
262
263   if (statusbar->messages)
264     {
265       GSList *list;
266
267       for (list = statusbar->messages; list; list = list->next)
268         {
269           msg = list->data;
270
271           if (msg->context_id == context_id)
272             {
273               GtkStatusbarClass *class;
274
275               class = GTK_STATUSBAR_GET_CLASS (statusbar);
276
277               statusbar->messages = g_slist_remove_link (statusbar->messages,
278                                                          list);
279               g_free (msg->text);
280               g_mem_chunk_free (class->messages_mem_chunk, msg);
281               g_slist_free_1 (list);
282               break;
283             }
284         }
285     }
286
287   msg = statusbar->messages ? statusbar->messages->data : NULL;
288
289   gtk_signal_emit (GTK_OBJECT (statusbar),
290                    statusbar_signals[SIGNAL_TEXT_POPPED],
291                    (guint) (msg ? msg->context_id : 0),
292                    msg ? msg->text : NULL);
293 }
294
295 void
296 gtk_statusbar_remove (GtkStatusbar *statusbar,
297                       guint        context_id,
298                       guint        message_id)
299 {
300   GtkStatusbarMsg *msg;
301
302   g_return_if_fail (statusbar != NULL);
303   g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
304   g_return_if_fail (message_id > 0);
305
306   msg = statusbar->messages ? statusbar->messages->data : NULL;
307   if (msg)
308     {
309       GSList *list;
310
311       /* care about signal emission if the topmost item is removed */
312       if (msg->context_id == context_id &&
313           msg->message_id == message_id)
314         {
315           gtk_statusbar_pop (statusbar, context_id);
316           return;
317         }
318       
319       for (list = statusbar->messages; list; list = list->next)
320         {
321           msg = list->data;
322           
323           if (msg->context_id == context_id &&
324               msg->message_id == message_id)
325             {
326               GtkStatusbarClass *class;
327               
328               class = GTK_STATUSBAR_GET_CLASS (statusbar);
329               statusbar->messages = g_slist_remove_link (statusbar->messages, list);
330               g_free (msg->text);
331               g_mem_chunk_free (class->messages_mem_chunk, msg);
332               g_slist_free_1 (list);
333               
334               break;
335             }
336         }
337     }
338 }
339
340 void
341 gtk_statusbar_set_has_resize_grip (GtkStatusbar *statusbar,
342                                    gboolean      setting)
343 {
344   g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
345
346   setting = setting != FALSE;
347
348   if (setting != statusbar->has_resize_grip)
349     {
350       statusbar->has_resize_grip = setting;
351       gtk_widget_queue_draw (GTK_WIDGET (statusbar));
352
353       if (GTK_WIDGET_REALIZED (statusbar))
354         {
355           if (statusbar->has_resize_grip && statusbar->grip_window == NULL)
356             gtk_statusbar_create_window (statusbar);
357           else if (!statusbar->has_resize_grip && statusbar->grip_window != NULL)
358             gtk_statusbar_destroy_window (statusbar);
359         }
360     }
361 }
362
363 gboolean
364 gtk_statusbar_get_has_resize_grip (GtkStatusbar *statusbar)
365 {
366   g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), FALSE);
367
368   return statusbar->has_resize_grip;
369 }
370
371 static void
372 gtk_statusbar_destroy (GtkObject *object)
373 {
374   GtkStatusbar *statusbar;
375   GtkStatusbarClass *class;
376   GSList *list;
377
378   g_return_if_fail (object != NULL);
379   g_return_if_fail (GTK_IS_STATUSBAR (object));
380
381   statusbar = GTK_STATUSBAR (object);
382   class = GTK_STATUSBAR_GET_CLASS (statusbar);
383
384   for (list = statusbar->messages; list; list = list->next)
385     {
386       GtkStatusbarMsg *msg;
387
388       msg = list->data;
389       g_free (msg->text);
390       g_mem_chunk_free (class->messages_mem_chunk, msg);
391     }
392   g_slist_free (statusbar->messages);
393   statusbar->messages = NULL;
394
395   for (list = statusbar->keys; list; list = list->next)
396     g_free (list->data);
397   g_slist_free (statusbar->keys);
398   statusbar->keys = NULL;
399
400   GTK_OBJECT_CLASS (parent_class)->destroy (object);
401 }
402
403 static void
404 get_grip_rect (GtkStatusbar *statusbar,
405                GdkRectangle *rect)
406 {
407   GtkWidget *widget;
408   gint w, h;
409   
410   widget = GTK_WIDGET (statusbar);
411
412   /* These are in effect the max/default size of the grip. */
413   w = 18;
414   h = 18;
415
416   if (w > (widget->allocation.width))
417     w = widget->allocation.width;
418
419   if (h > (widget->allocation.height - widget->style->ythickness))
420     h = widget->allocation.height - widget->style->ythickness;
421   
422   rect->x = widget->allocation.x + widget->allocation.width - w;
423   rect->y = widget->allocation.y + widget->allocation.height - h;
424   rect->width = w;
425   rect->height = h;
426 }
427
428 static void
429 gtk_statusbar_size_allocate (GtkWidget     *widget,
430                              GtkAllocation *allocation)
431 {
432   GtkStatusbar *statusbar;
433   GdkRectangle rect;
434   
435   statusbar = GTK_STATUSBAR (widget);
436   
437   GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
438
439   get_grip_rect (statusbar, &rect);
440   
441   if (statusbar->grip_window)
442     gdk_window_move_resize (statusbar->grip_window,
443                             rect.x, rect.y,
444                             rect.width, rect.height);
445 }
446
447 static void
448 gtk_statusbar_create_window (GtkStatusbar *statusbar)
449 {
450   GtkWidget *widget;
451   GdkWindowAttr attributes;
452   gint attributes_mask;
453   GdkRectangle rect;
454   
455   g_return_if_fail (GTK_WIDGET_REALIZED (statusbar));
456   g_return_if_fail (statusbar->has_resize_grip);
457   
458   widget = GTK_WIDGET (statusbar);
459
460   get_grip_rect (statusbar, &rect);
461
462   attributes.x = rect.x;
463   attributes.y = rect.y;
464   attributes.width = rect.width;
465   attributes.height = rect.height;
466   attributes.window_type = GDK_WINDOW_CHILD;
467   attributes.wclass = GDK_INPUT_ONLY;
468   attributes.event_mask = gtk_widget_get_events (widget) |
469     GDK_BUTTON_PRESS_MASK;
470
471   attributes_mask = GDK_WA_X | GDK_WA_Y;
472
473   statusbar->grip_window = gdk_window_new (widget->window,
474                                            &attributes, attributes_mask);
475   gdk_window_set_user_data (statusbar->grip_window, widget);
476 }
477
478 static void
479 gtk_statusbar_destroy_window (GtkStatusbar *statusbar)
480 {
481   gdk_window_set_user_data (statusbar->grip_window, NULL);
482   gdk_window_destroy (statusbar->grip_window);
483   statusbar->grip_window = NULL;
484 }
485
486 static void
487 gtk_statusbar_realize (GtkWidget *widget)
488 {
489   GtkStatusbar *statusbar;
490
491   statusbar = GTK_STATUSBAR (widget);
492   
493   (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
494
495   if (statusbar->has_resize_grip)
496     gtk_statusbar_create_window (statusbar);
497 }
498
499 static void
500 gtk_statusbar_unrealize (GtkWidget *widget)
501 {
502   GtkStatusbar *statusbar;
503
504   statusbar = GTK_STATUSBAR (widget);
505
506   if (statusbar->grip_window)
507     gtk_statusbar_destroy_window (statusbar);
508   
509   (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
510 }
511
512 static void
513 gtk_statusbar_map (GtkWidget *widget)
514 {
515   GtkStatusbar *statusbar;
516
517   statusbar = GTK_STATUSBAR (widget);
518   
519   (* GTK_WIDGET_CLASS (parent_class)->map) (widget);
520   
521   if (statusbar->grip_window)
522     gdk_window_show (statusbar->grip_window);
523 }
524
525 static void
526 gtk_statusbar_unmap (GtkWidget *widget)
527 {
528   GtkStatusbar *statusbar;
529
530   statusbar = GTK_STATUSBAR (widget);
531
532   if (statusbar->grip_window)
533     gdk_window_hide (statusbar->grip_window);
534   
535   (* GTK_WIDGET_CLASS (parent_class)->unmap) (widget);
536 }
537
538 static gboolean
539 gtk_statusbar_button_press (GtkWidget      *widget,
540                             GdkEventButton *event)
541 {
542   GtkStatusbar *statusbar;
543   GtkWidget *ancestor;
544   
545   statusbar = GTK_STATUSBAR (widget);
546   
547   if (!statusbar->has_resize_grip)
548     return FALSE;
549   
550   ancestor = gtk_widget_get_toplevel (widget);
551
552   if (!GTK_IS_WINDOW (ancestor))
553     return FALSE;
554
555   if (event->button == 1)
556     gtk_window_begin_resize_drag (GTK_WINDOW (ancestor),
557                                   GDK_WINDOW_EDGE_SOUTH_EAST,
558                                   event->button,
559                                   event->x_root, event->y_root,
560                                   event->time);
561   else if (event->button == 2)
562     gtk_window_begin_move_drag (GTK_WINDOW (ancestor),
563                                 event->button,
564                                 event->x_root, event->y_root,
565                                 event->time);
566   else
567     return FALSE;
568   
569   return TRUE;
570 }
571
572 static gboolean
573 gtk_statusbar_expose_event (GtkWidget      *widget,
574                             GdkEventExpose *event)
575 {
576   GtkStatusbar *statusbar;
577   GdkRectangle rect;
578   
579   statusbar = GTK_STATUSBAR (widget);
580
581   GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
582
583   if (statusbar->has_resize_grip)
584     {
585       get_grip_rect (statusbar, &rect);
586       
587       gtk_paint_resize_grip (widget->style,
588                              widget->window,
589                              GTK_WIDGET_STATE (widget),
590                              NULL,
591                              widget,
592                              "statusbar",
593                              GDK_WINDOW_EDGE_SOUTH_EAST,
594                              rect.x, rect.y,
595                              /* don't draw grip over the frame, though you
596                               * can click on the frame.
597                               */
598                              rect.width - widget->style->xthickness,
599                              rect.height - widget->style->ythickness);
600     }
601
602   return FALSE;
603 }