1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * This file is part of the GNOME Devtools Libraries.
4  *
5  * Copyright (C) 2003 Jeroen Zwartepoorte <jeroen@xs4all.nl>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 #include <glib/gi18n-lib.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "gdl-dock.h"
31 #include "gdl-dock-master.h"
32 #include "gdl-dock-bar.h"
33 #include "libgdltypebuiltins.h"
34 
35 /**
36  * SECTION:gdl-dock-bar
37  * @title: GdlDockBar
38  * @short_description: A docking bar
39  * @stability: Unstable
40  * @see_also: #GdlDockMaster
41  *
42  * This docking bar is a widget containing a button for each iconified
43  * #GdlDockItem widget. The widget can be re-opened by clicking on it.
44  *
45  * A dock bar is associated with one #GdlDockMaster and will get all iconified
46  * widgets of this master. This can includes widgets from several #GdlDock
47  * objects.
48  */
49 
50 
51 enum {
52     PROP_0,
53     PROP_MASTER,
54     PROP_DOCKBAR_STYLE
55 };
56 
57 /* ----- Private prototypes ----- */
58 
59 static void  gdl_dock_bar_class_init      (GdlDockBarClass *klass);
60 
61 static void  gdl_dock_bar_get_property    (GObject         *object,
62                                            guint            prop_id,
63                                            GValue          *value,
64                                            GParamSpec      *pspec);
65 static void  gdl_dock_bar_set_property    (GObject         *object,
66                                            guint            prop_id,
67                                            const GValue    *value,
68                                            GParamSpec      *pspec);
69 
70 static void  gdl_dock_bar_dispose         (GObject         *object);
71 
72 static void  gdl_dock_bar_set_master      (GdlDockBar      *dockbar,
73                                            GObject         *master);
74 static void gdl_dock_bar_remove_item      (GdlDockBar      *dockbar,
75                                            GdlDockItem     *item);
76 
77 /* ----- Class variables and definitions ----- */
78 
79 struct _GdlDockBarPrivate {
80     GdlDockMaster   *master;
81     GSList          *items;
82     GdlDockBarStyle  dockbar_style;
83 
84     glong layout_changed_id;
85 };
86 
87 /* ----- Private functions ----- */
88 
89 G_DEFINE_TYPE (GdlDockBar, gdl_dock_bar, GTK_TYPE_BOX)
90 
91 static void gdl_dock_bar_get_preferred_width  (GtkWidget *widget,
92                                                gint      *minimum,
93                                                gint      *natural);
94 static void gdl_dock_bar_get_preferred_height (GtkWidget *widget,
95                                                gint      *minimum,
96                                                gint      *natural);
97 static void gdl_dock_bar_size_allocate (GtkWidget *widget,
98 		                       GtkAllocation *allocation );
99 static void gdl_dock_bar_size_vrequest (GtkWidget *widget,
100 		                       GtkRequisition *requisition );
101 static void gdl_dock_bar_size_vallocate (GtkWidget *widget,
102 		                       GtkAllocation *allocation );
103 static void gdl_dock_bar_size_hrequest (GtkWidget *widget,
104 		                       GtkRequisition *requisition );
105 static void gdl_dock_bar_size_hallocate (GtkWidget *widget,
106 		                       GtkAllocation *allocation );
107 static void update_dock_items (GdlDockBar *dockbar, gboolean full_update);
108 
109 void
gdl_dock_bar_class_init(GdlDockBarClass * klass)110 gdl_dock_bar_class_init (GdlDockBarClass *klass)
111 {
112     GObjectClass       *object_class;
113 
114     object_class = G_OBJECT_CLASS (klass);
115 
116     object_class->get_property = gdl_dock_bar_get_property;
117     object_class->set_property = gdl_dock_bar_set_property;
118     object_class->dispose = gdl_dock_bar_dispose;
119 
120     /**
121      * GdlDockBar:master:
122      *
123      * The #GdlDockMaster object attached to the dockbar.
124      */
125     g_object_class_install_property (
126         object_class, PROP_MASTER,
127         g_param_spec_object ("master", _("Master"),
128                              _("GdlDockMaster object which the dockbar widget "
129                                "is attached to"),
130                              G_TYPE_OBJECT,
131                              G_PARAM_READWRITE));
132 
133     /**
134      * GdlDockBar:style:
135      *
136      * The style of the buttons in the dockbar.
137      */
138     g_object_class_install_property (
139         object_class, PROP_DOCKBAR_STYLE,
140         g_param_spec_enum ("dockbar-style", _("Dockbar style"),
141                            _("Dockbar style to show items on it"),
142                            GDL_TYPE_DOCK_BAR_STYLE,
143                            GDL_DOCK_BAR_BOTH,
144                            G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
145 
146     g_type_class_add_private (object_class, sizeof (GdlDockBarPrivate));
147 }
148 
149 static void
gdl_dock_bar_init(GdlDockBar * dockbar)150 gdl_dock_bar_init (GdlDockBar *dockbar)
151 {
152     dockbar->priv = G_TYPE_INSTANCE_GET_PRIVATE (dockbar,
153                                                  GDL_TYPE_DOCK_BAR,
154                                                  GdlDockBarPrivate);
155 
156     dockbar->priv->master = NULL;
157     dockbar->priv->items = NULL;
158     dockbar->priv->dockbar_style = GDL_DOCK_BAR_BOTH;
159     gtk_orientable_set_orientation (GTK_ORIENTABLE (dockbar), GTK_ORIENTATION_VERTICAL);
160 }
161 
162 static void
gdl_dock_bar_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)163 gdl_dock_bar_get_property (GObject         *object,
164                            guint            prop_id,
165                            GValue          *value,
166                            GParamSpec      *pspec)
167 {
168     GdlDockBar *dockbar = GDL_DOCK_BAR (object);
169 
170     switch (prop_id) {
171         case PROP_MASTER:
172             g_value_set_object (value, dockbar->priv->master);
173             break;
174         case PROP_DOCKBAR_STYLE:
175             g_value_set_enum (value, dockbar->priv->dockbar_style);
176             break;
177         default:
178             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
179     };
180 }
181 
182 static void
gdl_dock_bar_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)183 gdl_dock_bar_set_property (GObject         *object,
184                            guint            prop_id,
185                            const GValue    *value,
186                            GParamSpec      *pspec)
187 {
188     GdlDockBar *dockbar = GDL_DOCK_BAR (object);
189 
190     switch (prop_id) {
191         case PROP_MASTER:
192             gdl_dock_bar_set_master (dockbar, g_value_get_object (value));
193             break;
194         case PROP_DOCKBAR_STYLE:
195             dockbar->priv->dockbar_style = g_value_get_enum (value);
196             update_dock_items (dockbar, TRUE);
197             break;
198         default:
199             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
200     };
201 }
202 
203 static void
on_dock_item_foreach_disconnect(GdlDockItem * item,GdlDockBar * dock_bar)204 on_dock_item_foreach_disconnect (GdlDockItem *item, GdlDockBar *dock_bar)
205 {
206     g_signal_handlers_disconnect_by_func (item, gdl_dock_bar_remove_item,
207                                           dock_bar);
208 }
209 
210 static void
gdl_dock_bar_dispose(GObject * object)211 gdl_dock_bar_dispose (GObject *object)
212 {
213     GdlDockBar *dockbar = GDL_DOCK_BAR (object);
214     GdlDockBarPrivate *priv = dockbar->priv;
215 
216     if (priv->items) {
217         g_slist_foreach (priv->items,
218                          (GFunc) on_dock_item_foreach_disconnect,
219                          object);
220         g_slist_free (priv->items);
221         priv->items = NULL;
222     }
223 
224     if (priv->master) {
225         gdl_dock_bar_set_master (dockbar, NULL);
226     }
227 
228    G_OBJECT_CLASS (gdl_dock_bar_parent_class)->dispose (object);
229 }
230 
231 static void
gdl_dock_bar_remove_item(GdlDockBar * dockbar,GdlDockItem * item)232 gdl_dock_bar_remove_item (GdlDockBar  *dockbar,
233                           GdlDockItem *item)
234 {
235     GdlDockBarPrivate *priv;
236     GtkWidget *button;
237 
238     g_return_if_fail (GDL_IS_DOCK_BAR (dockbar));
239     g_return_if_fail (GDL_IS_DOCK_ITEM (item));
240 
241     priv = dockbar->priv;
242 
243     if (g_slist_index (priv->items, item) == -1) {
244         g_warning ("Item has not been added to the dockbar");
245         return;
246     }
247 
248     priv->items = g_slist_remove (priv->items, item);
249 
250     button = g_object_get_data (G_OBJECT (item), "GdlDockBarButton");
251     g_assert (button != NULL);
252     gtk_container_remove (GTK_CONTAINER (dockbar), button);
253     g_object_set_data (G_OBJECT (item), "GdlDockBarButton", NULL);
254     g_signal_handlers_disconnect_by_func (item,
255                                           G_CALLBACK (gdl_dock_bar_remove_item),
256                                           dockbar);
257 }
258 
259 static void
gdl_dock_bar_item_clicked(GtkWidget * button,GdlDockItem * item)260 gdl_dock_bar_item_clicked (GtkWidget   *button,
261                            GdlDockItem *item)
262 {
263     GdlDockBar *dockbar;
264 
265     g_return_if_fail (item != NULL);
266 
267     dockbar = g_object_get_data (G_OBJECT (item), "GdlDockBar");
268     g_assert (dockbar != NULL);
269     g_object_set_data (G_OBJECT (item), "GdlDockBar", NULL);
270 
271     gdl_dock_item_show_item (item);
272 }
273 
274 static void
gdl_dock_bar_add_item(GdlDockBar * dockbar,GdlDockItem * item)275 gdl_dock_bar_add_item (GdlDockBar  *dockbar,
276                        GdlDockItem *item)
277 {
278     GdlDockBarPrivate *priv;
279     GtkWidget *button;
280     gchar *stock_id;
281     gchar *name;
282     GdkPixbuf *pixbuf_icon;
283     GtkWidget *image, *box, *label;
284 
285     g_return_if_fail (GDL_IS_DOCK_BAR (dockbar));
286     g_return_if_fail (GDL_IS_DOCK_ITEM (item));
287 
288     priv = dockbar->priv;
289 
290     if (g_slist_index (priv->items, item) != -1) {
291         g_warning ("Item has already been added to the dockbar");
292         return;
293     }
294 
295     priv->items = g_slist_append (priv->items, item);
296 
297     /* Create a button for the item. */
298     button = gtk_button_new ();
299     gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
300 
301     box = gtk_box_new (gtk_orientable_get_orientation (GTK_ORIENTABLE (dockbar)), 0);
302 
303     g_object_get (item, "stock-id", &stock_id, "pixbuf-icon", &pixbuf_icon,
304                   "long-name", &name, NULL);
305 
306     if (dockbar->priv->dockbar_style == GDL_DOCK_BAR_TEXT ||
307         dockbar->priv->dockbar_style == GDL_DOCK_BAR_BOTH) {
308         label = gtk_label_new (name);
309         if (gtk_orientable_get_orientation (GTK_ORIENTABLE (dockbar)) == GTK_ORIENTATION_VERTICAL)
310             gtk_label_set_angle (GTK_LABEL (label), 90);
311         gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);
312     }
313 
314     /* FIXME: For now AUTO behaves same as BOTH */
315 
316     if (dockbar->priv->dockbar_style == GDL_DOCK_BAR_ICONS ||
317         dockbar->priv->dockbar_style == GDL_DOCK_BAR_BOTH ||
318         dockbar->priv->dockbar_style == GDL_DOCK_BAR_AUTO) {
319         if (stock_id) {
320             image = gtk_image_new_from_stock (stock_id,
321                                               GTK_ICON_SIZE_SMALL_TOOLBAR);
322             g_free (stock_id);
323         } else if (pixbuf_icon) {
324             image = gtk_image_new_from_pixbuf (pixbuf_icon);
325         } else {
326             image = gtk_image_new_from_stock (GTK_STOCK_NEW,
327                                               GTK_ICON_SIZE_SMALL_TOOLBAR);
328         }
329         gtk_box_pack_start (GTK_BOX (box), image, TRUE, TRUE, 0);
330     }
331 
332     gtk_container_add (GTK_CONTAINER (button), box);
333     gtk_box_pack_start (GTK_BOX (dockbar), button, FALSE, FALSE, 0);
334 
335     gtk_widget_set_tooltip_text (button, name);
336     g_free (name);
337 
338     g_object_set_data (G_OBJECT (item), "GdlDockBar", dockbar);
339     g_object_set_data (G_OBJECT (item), "GdlDockBarButton", button);
340     g_signal_connect (G_OBJECT (button), "clicked",
341                       G_CALLBACK (gdl_dock_bar_item_clicked), item);
342 
343     gtk_widget_show_all (button);
344 
345     /* Set up destroy notify */
346     g_signal_connect_swapped (item, "destroy",
347                               G_CALLBACK (gdl_dock_bar_remove_item),
348                               dockbar);
349 }
350 
351 static void
build_list(GdlDockObject * object,GList ** list)352 build_list (GdlDockObject *object, GList **list)
353 {
354     /* add only items, not toplevels */
355     if (GDL_IS_DOCK_ITEM (object))
356         *list = g_list_prepend (*list, object);
357 }
358 
359 static void
update_dock_items(GdlDockBar * dockbar,gboolean full_update)360 update_dock_items (GdlDockBar *dockbar, gboolean full_update)
361 {
362     GdlDockMaster *master;
363     GList *items, *l;
364 
365     g_return_if_fail (dockbar != NULL);
366 
367     if (!dockbar->priv->master)
368         return;
369 
370     master = dockbar->priv->master;
371 
372     /* build items list */
373     items = NULL;
374     gdl_dock_master_foreach (master, (GFunc) build_list, &items);
375 
376     if (!full_update) {
377         for (l = items; l != NULL; l = l->next) {
378             GdlDockItem *item = GDL_DOCK_ITEM (l->data);
379 
380             if (g_slist_index (dockbar->priv->items, item) != -1 &&
381                 !gdl_dock_item_is_iconified (item))
382                 gdl_dock_bar_remove_item (dockbar, item);
383             else if (g_slist_index (dockbar->priv->items, item) == -1 &&
384                 gdl_dock_item_is_iconified (item) &&
385                 !gdl_dock_item_is_placeholder (item))
386                 gdl_dock_bar_add_item (dockbar, item);
387         }
388     } else {
389         for (l = items; l != NULL; l = l->next) {
390             GdlDockItem *item = GDL_DOCK_ITEM (l->data);
391 
392             if (g_slist_index (dockbar->priv->items, item) != -1)
393                 gdl_dock_bar_remove_item (dockbar, item);
394             if (gdl_dock_item_is_iconified (item) &&
395                 !gdl_dock_item_is_placeholder (item))
396                 gdl_dock_bar_add_item (dockbar, item);
397         }
398     }
399     g_list_free (items);
400 }
401 
402 static void
gdl_dock_bar_layout_changed_cb(GdlDockMaster * master,GdlDockBar * dockbar)403 gdl_dock_bar_layout_changed_cb (GdlDockMaster *master,
404                                 GdlDockBar    *dockbar)
405 {
406     update_dock_items (dockbar, FALSE);
407 }
408 
409 static void
gdl_dock_bar_set_master(GdlDockBar * dockbar,GObject * master)410 gdl_dock_bar_set_master (GdlDockBar    *dockbar,
411                          GObject       *master)
412 {
413     g_return_if_fail (dockbar != NULL);
414     g_return_if_fail (master == NULL || GDL_IS_DOCK_MASTER (master) || GDL_IS_DOCK_OBJECT (master));
415 
416     if (dockbar->priv->master) {
417         g_signal_handler_disconnect (dockbar->priv->master,
418                                      dockbar->priv->layout_changed_id);
419         g_object_unref (dockbar->priv->master);
420     }
421 
422     if (master != NULL)
423     {
424         /* Accept a GdlDockObject instead of a GdlDockMaster */
425         if (GDL_IS_DOCK_OBJECT (master)) {
426             master = gdl_dock_object_get_master (GDL_DOCK_OBJECT (master));
427         }
428         dockbar->priv->master = g_object_ref (master);
429         dockbar->priv->layout_changed_id =
430             g_signal_connect (dockbar->priv->master, "layout-changed",
431                               (GCallback) gdl_dock_bar_layout_changed_cb,
432                               dockbar);
433 
434     } else {
435         dockbar->priv->master = NULL;
436     }
437 
438     update_dock_items (dockbar, FALSE);
439 }
440 
441 
442 /**
443  * gdl_dock_bar_new:
444  * @master: (allow-none): The associated #GdlDockMaster or #GdlDockObject object
445  *
446  * Creates a new GDL dock bar. If a #GdlDockObject is used, the dock bar will
447  * be associated with the master of this object.
448  *
449  * Returns: The newly created dock bar.
450  */
451 GtkWidget *
gdl_dock_bar_new(GObject * master)452 gdl_dock_bar_new (GObject *master)
453 {
454     g_return_val_if_fail (master == NULL || GDL_IS_DOCK_MASTER (master) || GDL_IS_DOCK_OBJECT (master), NULL);
455 
456     return g_object_new (GDL_TYPE_DOCK_BAR,
457                          "master", master, NULL);
458 }
459 
460 /**
461  * gdl_dock_bar_set_style:
462  * @dockbar: a #GdlDockBar
463  * @style: the new style
464  *
465  * Set the style of the @dockbar.
466  */
gdl_dock_bar_set_style(GdlDockBar * dockbar,GdlDockBarStyle style)467 void gdl_dock_bar_set_style(GdlDockBar* dockbar,
468                             GdlDockBarStyle style)
469 {
470     g_return_if_fail (GDL_IS_DOCK_BAR (dockbar));
471 
472     g_object_set(G_OBJECT(dockbar), "dockbar-style", style, NULL);
473 }
474 
475 /**
476  * gdl_dock_bar_get_style:
477  * @dockbar: a #GdlDockBar
478  *
479  * Retrieves the style of the @dockbar.
480  *
481  * Returns: the style of the @docbar
482  */
gdl_dock_bar_get_style(GdlDockBar * dockbar)483 GdlDockBarStyle gdl_dock_bar_get_style(GdlDockBar* dockbar)
484 {
485     GdlDockBarStyle style;
486 
487     g_return_val_if_fail (GDL_IS_DOCK_BAR (dockbar), 0);
488 
489     g_object_get(G_OBJECT(dockbar), "dockbar-style", &style, NULL);
490 
491     return style;
492 }
493