1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * This file is part of the GNOME Devtools Libraries.
4 *
5 * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
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 */
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
26 #include "gdl-i18n.h"
27 #include "gdl-switcher.h"
29 #include "gdl-tools.h"
30 #include "gdl-dock-notebook.h"
31 #include "gdl-dock-tablabel.h"
34 /* Private prototypes */
36 static void gdl_dock_notebook_class_init (GdlDockNotebookClass *klass);
37 static void gdl_dock_notebook_instance_init (GdlDockNotebook *notebook);
38 static void gdl_dock_notebook_set_property (GObject *object,
39 guint prop_id,
40 const GValue *value,
41 GParamSpec *pspec);
42 static void gdl_dock_notebook_get_property (GObject *object,
43 guint prop_id,
44 GValue *value,
45 GParamSpec *pspec);
47 static void gdl_dock_notebook_destroy (GtkObject *object);
49 static void gdl_dock_notebook_add (GtkContainer *container,
50 GtkWidget *widget);
51 static void gdl_dock_notebook_forall (GtkContainer *container,
52 gboolean include_internals,
53 GtkCallback callback,
54 gpointer callback_data);
55 static GType gdl_dock_notebook_child_type (GtkContainer *container);
57 static void gdl_dock_notebook_dock (GdlDockObject *object,
58 GdlDockObject *requestor,
59 GdlDockPlacement position,
60 GValue *other_data);
62 static void gdl_dock_notebook_switch_page_cb (GtkNotebook *nb,
63 GtkNotebookPage *page,
64 gint page_num,
65 gpointer data);
67 static void gdl_dock_notebook_set_orientation (GdlDockItem *item,
68 GtkOrientation orientation);
70 static gboolean gdl_dock_notebook_child_placement (GdlDockObject *object,
71 GdlDockObject *child,
72 GdlDockPlacement *placement);
74 static void gdl_dock_notebook_present (GdlDockObject *object,
75 GdlDockObject *child);
77 static gboolean gdl_dock_notebook_reorder (GdlDockObject *object,
78 GdlDockObject *requestor,
79 GdlDockPlacement new_position,
80 GValue *other_data);
83 /* Class variables and definitions */
85 enum {
86 PROP_0,
87 PROP_PAGE
88 };
91 /* ----- Private functions ----- */
93 GDL_CLASS_BOILERPLATE (GdlDockNotebook, gdl_dock_notebook, GdlDockItem, GDL_TYPE_DOCK_ITEM) ;
95 static void
96 gdl_dock_notebook_class_init (GdlDockNotebookClass *klass)
97 {
98 static gboolean style_initialized = FALSE;
100 GObjectClass *g_object_class;
101 GtkObjectClass *gtk_object_class;
102 GtkWidgetClass *widget_class;
103 GtkContainerClass *container_class;
104 GdlDockObjectClass *object_class;
105 GdlDockItemClass *item_class;
107 g_object_class = G_OBJECT_CLASS (klass);
108 gtk_object_class = GTK_OBJECT_CLASS (klass);
109 widget_class = GTK_WIDGET_CLASS (klass);
110 container_class = GTK_CONTAINER_CLASS (klass);
111 object_class = GDL_DOCK_OBJECT_CLASS (klass);
112 item_class = GDL_DOCK_ITEM_CLASS (klass);
114 g_object_class->set_property = gdl_dock_notebook_set_property;
115 g_object_class->get_property = gdl_dock_notebook_get_property;
117 gtk_object_class->destroy = gdl_dock_notebook_destroy;
119 container_class->add = gdl_dock_notebook_add;
120 container_class->forall = gdl_dock_notebook_forall;
121 container_class->child_type = gdl_dock_notebook_child_type;
123 object_class->is_compound = TRUE;
124 object_class->dock = gdl_dock_notebook_dock;
125 object_class->child_placement = gdl_dock_notebook_child_placement;
126 object_class->present = gdl_dock_notebook_present;
127 object_class->reorder = gdl_dock_notebook_reorder;
129 item_class->has_grip = FALSE;
130 item_class->set_orientation = gdl_dock_notebook_set_orientation;
132 g_object_class_install_property (
133 g_object_class, PROP_PAGE,
134 g_param_spec_int ("page", _("Page"),
135 _("The index of the current page"),
136 0, G_MAXINT,
137 0,
138 G_PARAM_READWRITE |
139 GDL_DOCK_PARAM_EXPORT | GDL_DOCK_PARAM_AFTER));
141 if (!style_initialized) {
142 style_initialized = TRUE;
144 gtk_rc_parse_string (
145 "style \"gdl-dock-notebook-default\" {\n"
146 "xthickness = 2\n"
147 "ythickness = 2\n"
148 "}\n"
149 "widget_class \"*.GtkNotebook.GdlDockItem\" "
150 "style : gtk \"gdl-dock-notebook-default\"\n");
151 }
152 }
154 static void
155 gdl_dock_notebook_notify_cb (GObject *g_object,
156 GParamSpec *pspec,
157 gpointer user_data)
158 {
159 g_return_if_fail (user_data != NULL && GDL_IS_DOCK_NOTEBOOK (user_data));
161 /* chain the notify signal */
162 g_object_notify (G_OBJECT (user_data), pspec->name);
163 }
165 static gboolean
166 gdl_dock_notebook_button_cb (GtkWidget *widget,
167 GdkEventButton *event,
168 gpointer user_data)
169 {
170 if (event->type == GDK_BUTTON_PRESS)
171 GDL_DOCK_ITEM_SET_FLAGS (user_data, GDL_DOCK_USER_ACTION);
172 else
173 GDL_DOCK_ITEM_UNSET_FLAGS (user_data, GDL_DOCK_USER_ACTION);
175 return FALSE;
176 }
178 static void
179 gdl_dock_notebook_instance_init (GdlDockNotebook *notebook)
180 {
181 GdlDockItem *item;
183 item = GDL_DOCK_ITEM (notebook);
185 /* create the container notebook */
186 item->child = gdl_switcher_new ();
187 gtk_widget_set_parent (item->child, GTK_WIDGET (notebook));
188 gtk_notebook_set_tab_pos (GTK_NOTEBOOK (item->child), GTK_POS_BOTTOM);
189 g_signal_connect (item->child, "switch-page",
190 (GCallback) gdl_dock_notebook_switch_page_cb, (gpointer) item);
191 g_signal_connect (item->child, "notify::page",
192 (GCallback) gdl_dock_notebook_notify_cb, (gpointer) item);
193 g_signal_connect (item->child, "button-press-event",
194 (GCallback) gdl_dock_notebook_button_cb, (gpointer) item);
195 g_signal_connect (item->child, "button-release-event",
196 (GCallback) gdl_dock_notebook_button_cb, (gpointer) item);
197 gtk_notebook_set_scrollable (GTK_NOTEBOOK (item->child), TRUE);
198 gtk_widget_show (item->child);
199 }
201 static void
202 gdl_dock_notebook_set_property (GObject *object,
203 guint prop_id,
204 const GValue *value,
205 GParamSpec *pspec)
206 {
207 GdlDockItem *item = GDL_DOCK_ITEM (object);
209 switch (prop_id) {
210 case PROP_PAGE:
211 if (item->child && GTK_IS_NOTEBOOK (item->child)) {
212 gtk_notebook_set_current_page (GTK_NOTEBOOK (item->child),
213 g_value_get_int (value));
214 }
216 break;
217 default:
218 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
219 break;
220 }
221 }
223 static void
224 gdl_dock_notebook_get_property (GObject *object,
225 guint prop_id,
226 GValue *value,
227 GParamSpec *pspec)
228 {
229 GdlDockItem *item = GDL_DOCK_ITEM (object);
231 switch (prop_id) {
232 case PROP_PAGE:
233 if (item->child && GTK_IS_NOTEBOOK (item->child)) {
234 g_value_set_int (value, gtk_notebook_get_current_page
235 (GTK_NOTEBOOK (item->child)));
236 }
238 break;
239 default:
240 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
241 break;
242 }
243 }
246 static void
247 gdl_dock_notebook_destroy (GtkObject *object)
248 {
249 GdlDockItem *item = GDL_DOCK_ITEM (object);
251 /* we need to call the virtual first, since in GdlDockDestroy our
252 children dock objects are detached */
253 GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
255 /* after that we can remove the GtkNotebook */
256 if (item->child) {
257 gtk_widget_unparent (item->child);
258 item->child = NULL;
259 };
260 }
262 static void
263 gdl_dock_notebook_switch_page_cb (GtkNotebook *nb,
264 GtkNotebookPage *page,
265 gint page_num,
266 gpointer data)
267 {
268 GdlDockNotebook *notebook;
269 GtkWidget *tablabel;
271 notebook = GDL_DOCK_NOTEBOOK (data);
273 /* deactivate old tablabel */
274 if (nb->cur_page) {
275 tablabel = gtk_notebook_get_tab_label (
276 nb, gtk_notebook_get_nth_page (
277 nb, gtk_notebook_get_current_page (nb)));
278 if (tablabel && GDL_IS_DOCK_TABLABEL (tablabel))
279 gdl_dock_tablabel_deactivate (GDL_DOCK_TABLABEL (tablabel));
280 };
282 /* activate new label */
283 tablabel = gtk_notebook_get_tab_label (
284 nb, gtk_notebook_get_nth_page (nb, page_num));
285 if (tablabel && GDL_IS_DOCK_TABLABEL (tablabel))
286 gdl_dock_tablabel_activate (GDL_DOCK_TABLABEL (tablabel));
288 if (GDL_DOCK_ITEM_USER_ACTION (notebook) &&
289 GDL_DOCK_OBJECT (notebook)->master)
290 g_signal_emit_by_name (GDL_DOCK_OBJECT (notebook)->master,
291 "layout-changed");
292 }
294 static void
295 gdl_dock_notebook_add (GtkContainer *container,
296 GtkWidget *widget)
297 {
298 g_return_if_fail (container != NULL && widget != NULL);
299 g_return_if_fail (GDL_IS_DOCK_NOTEBOOK (container));
300 g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
302 gdl_dock_object_dock (GDL_DOCK_OBJECT (container),
303 GDL_DOCK_OBJECT (widget),
304 GDL_DOCK_CENTER,
305 NULL);
306 }
308 static void
309 gdl_dock_notebook_forall (GtkContainer *container,
310 gboolean include_internals,
311 GtkCallback callback,
312 gpointer callback_data)
313 {
314 GdlDockItem *item;
316 g_return_if_fail (container != NULL);
317 g_return_if_fail (GDL_IS_DOCK_NOTEBOOK (container));
318 g_return_if_fail (callback != NULL);
320 if (include_internals) {
321 /* use GdlDockItem's forall */
322 GDL_CALL_PARENT (GTK_CONTAINER_CLASS, forall,
323 (container, include_internals, callback, callback_data));
324 }
325 else {
326 item = GDL_DOCK_ITEM (container);
327 if (item->child)
328 gtk_container_foreach (GTK_CONTAINER (item->child), callback, callback_data);
329 }
330 }
332 static GType
333 gdl_dock_notebook_child_type (GtkContainer *container)
334 {
335 return GDL_TYPE_DOCK_ITEM;
336 }
338 static void
339 gdl_dock_notebook_dock_child (GdlDockObject *requestor,
340 gpointer user_data)
341 {
342 struct {
343 GdlDockObject *object;
344 GdlDockPlacement position;
345 GValue *other_data;
346 } *data = user_data;
348 gdl_dock_object_dock (data->object, requestor, data->position, data->other_data);
349 }
351 static void
352 gdl_dock_notebook_dock (GdlDockObject *object,
353 GdlDockObject *requestor,
354 GdlDockPlacement position,
355 GValue *other_data)
356 {
357 g_return_if_fail (GDL_IS_DOCK_NOTEBOOK (object));
358 g_return_if_fail (GDL_IS_DOCK_ITEM (requestor));
360 /* we only add support for GDL_DOCK_CENTER docking strategy here... for the rest
361 use our parent class' method */
362 if (position == GDL_DOCK_CENTER) {
363 /* we can only dock simple (not compound) items */
364 if (gdl_dock_object_is_compound (requestor)) {
365 struct {
366 GdlDockObject *object;
367 GdlDockPlacement position;
368 GValue *other_data;
369 } data;
371 gdl_dock_object_freeze (requestor);
373 data.object = object;
374 data.position = position;
375 data.other_data = other_data;
377 gtk_container_foreach (GTK_CONTAINER (requestor),
378 (GtkCallback) gdl_dock_notebook_dock_child, &data);
380 gdl_dock_object_thaw (requestor);
381 }
382 else {
383 GdlDockItem *item = GDL_DOCK_ITEM (object);
384 GdlDockItem *requestor_item = GDL_DOCK_ITEM (requestor);
385 gchar *long_name, *stock_id;
386 GdkPixbuf *pixbuf_icon;
387 GtkWidget *label;
388 gint position = -1;
390 g_object_get (requestor_item, "long-name", &long_name,
391 "stock-id", &stock_id, "pixbuf-icon", &pixbuf_icon, NULL);
392 label = gdl_dock_item_get_tablabel (requestor_item);
393 if (!label) {
394 label = gtk_label_new (long_name);
395 gdl_dock_item_set_tablabel (requestor_item, label);
396 }
397 #if 0
398 if (GDL_IS_DOCK_TABLABEL (label)) {
399 gdl_dock_tablabel_deactivate (GDL_DOCK_TABLABEL (label));
400 /* hide the item grip, as we will use the tablabel's */
401 gdl_dock_item_hide_grip (requestor_item);
402 }
403 #endif
405 if (other_data && G_VALUE_HOLDS (other_data, G_TYPE_INT))
406 position = g_value_get_int (other_data);
408 position = gdl_switcher_insert_page (GDL_SWITCHER (item->child),
409 GTK_WIDGET (requestor), label,
410 long_name, long_name,
411 stock_id, pixbuf_icon, position);
413 GDL_DOCK_OBJECT_SET_FLAGS (requestor, GDL_DOCK_ATTACHED);
415 /* Set current page to the newly docked widget. set current page
416 * really doesn't work if the page widget is not shown
417 */
418 gtk_widget_show (GTK_WIDGET (requestor));
419 gtk_notebook_set_current_page (GTK_NOTEBOOK (item->child),
420 position);
421 g_free (long_name);
422 g_free (stock_id);
423 }
424 }
425 else
426 GDL_CALL_PARENT (GDL_DOCK_OBJECT_CLASS, dock,
427 (object, requestor, position, other_data));
428 }
430 static void
431 gdl_dock_notebook_set_orientation (GdlDockItem *item,
432 GtkOrientation orientation)
433 {
434 if (item->child && GTK_IS_NOTEBOOK (item->child)) {
435 if (orientation == GTK_ORIENTATION_HORIZONTAL)
436 gtk_notebook_set_tab_pos (GTK_NOTEBOOK (item->child), GTK_POS_TOP);
437 else
438 gtk_notebook_set_tab_pos (GTK_NOTEBOOK (item->child), GTK_POS_LEFT);
439 }
441 GDL_CALL_PARENT (GDL_DOCK_ITEM_CLASS, set_orientation, (item, orientation));
442 }
444 static gboolean
445 gdl_dock_notebook_child_placement (GdlDockObject *object,
446 GdlDockObject *child,
447 GdlDockPlacement *placement)
448 {
449 GdlDockItem *item = GDL_DOCK_ITEM (object);
450 GdlDockPlacement pos = GDL_DOCK_NONE;
452 if (item->child) {
453 GList *children, *l;
455 children = gtk_container_get_children (GTK_CONTAINER (item->child));
456 for (l = children; l; l = l->next) {
457 if (l->data == (gpointer) child) {
458 pos = GDL_DOCK_CENTER;
459 break;
460 }
461 }
462 g_list_free (children);
463 }
465 if (pos != GDL_DOCK_NONE) {
466 if (placement)
467 *placement = pos;
468 return TRUE;
469 }
470 else
471 return FALSE;
472 }
474 static void
475 gdl_dock_notebook_present (GdlDockObject *object,
476 GdlDockObject *child)
477 {
478 GdlDockItem *item = GDL_DOCK_ITEM (object);
479 int i;
481 i = gtk_notebook_page_num (GTK_NOTEBOOK (item->child),
482 GTK_WIDGET (child));
483 if (i >= 0)
484 gtk_notebook_set_current_page (GTK_NOTEBOOK (item->child), i);
486 GDL_CALL_PARENT (GDL_DOCK_OBJECT_CLASS, present, (object, child));
487 }
489 static gboolean
490 gdl_dock_notebook_reorder (GdlDockObject *object,
491 GdlDockObject *requestor,
492 GdlDockPlacement new_position,
493 GValue *other_data)
494 {
495 GdlDockItem *item = GDL_DOCK_ITEM (object);
496 gint current_position, new_pos = -1;
497 gboolean handled = FALSE;
499 if (item->child && new_position == GDL_DOCK_CENTER) {
500 current_position = gtk_notebook_page_num (GTK_NOTEBOOK (item->child),
501 GTK_WIDGET (requestor));
502 if (current_position >= 0) {
503 handled = TRUE;
505 if (other_data && G_VALUE_HOLDS (other_data, G_TYPE_INT))
506 new_pos = g_value_get_int (other_data);
508 gtk_notebook_reorder_child (GTK_NOTEBOOK (item->child),
509 GTK_WIDGET (requestor),
510 new_pos);
511 }
512 }
513 return handled;
514 }
516 /* ----- Public interface ----- */
518 GtkWidget *
519 gdl_dock_notebook_new (void)
520 {
521 GdlDockNotebook *notebook;
523 notebook = GDL_DOCK_NOTEBOOK (g_object_new (GDL_TYPE_DOCK_NOTEBOOK, NULL));
524 GDL_DOCK_OBJECT_UNSET_FLAGS (notebook, GDL_DOCK_AUTOMATIC);
526 return GTK_WIDGET (notebook);
527 }