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