d9f805ffe1b3eaafb0099488629dfc763a1fdaab
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * gdl-dock-item.c
4 *
5 * Author: Gustavo Giráldez <gustavo.giraldez@gmx.net>
6 * Naba Kumar <naba@gnome.org>
7 *
8 * Based on GnomeDockItem/BonoboDockItem. Original copyright notice follows.
9 *
10 * Copyright (C) 1998 Ettore Perazzoli
11 * Copyright (C) 1998 Elliot Lee
12 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
13 * All rights reserved.
14 *
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Library General Public
17 * License as published by the Free Software Foundation; either
18 * version 2 of the License, or (at your option) any later version.
19 *
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Library General Public License for more details.
24 *
25 * You should have received a copy of the GNU Library General Public
26 * License along with this library; if not, write to the
27 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
28 * Boston, MA 02111-1307, USA.
29 */
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
36 #include "gdl-i18n.h"
37 #include <string.h>
38 #include <gdk/gdkkeysyms.h>
40 #include "gdl-tools.h"
41 #include "gdl-dock.h"
42 #include "gdl-dock-item.h"
43 #include "gdl-dock-item-grip.h"
44 #include "gdl-dock-notebook.h"
45 #include "gdl-dock-paned.h"
46 #include "gdl-dock-tablabel.h"
47 #include "gdl-dock-placeholder.h"
48 #include "gdl-dock-master.h"
49 #include "libgdltypebuiltins.h"
50 #include "libgdlmarshal.h"
52 #define NEW_DOCK_ITEM_RATIO 0.3
54 /* ----- Private prototypes ----- */
56 static void gdl_dock_item_class_init (GdlDockItemClass *class);
57 static void gdl_dock_item_instance_init (GdlDockItem *item);
59 static GObject *gdl_dock_item_constructor (GType type,
60 guint n_construct_properties,
61 GObjectConstructParam *construct_param);
63 static void gdl_dock_item_set_property (GObject *object,
64 guint prop_id,
65 const GValue *value,
66 GParamSpec *pspec);
67 static void gdl_dock_item_get_property (GObject *object,
68 guint prop_id,
69 GValue *value,
70 GParamSpec *pspec);
72 static void gdl_dock_item_destroy (GtkObject *object);
74 static void gdl_dock_item_add (GtkContainer *container,
75 GtkWidget *widget);
76 static void gdl_dock_item_remove (GtkContainer *container,
77 GtkWidget *widget);
78 static void gdl_dock_item_forall (GtkContainer *container,
79 gboolean include_internals,
80 GtkCallback callback,
81 gpointer callback_data);
82 static GtkType gdl_dock_item_child_type (GtkContainer *container);
84 static void gdl_dock_item_size_request (GtkWidget *widget,
85 GtkRequisition *requisition);
86 static void gdl_dock_item_size_allocate (GtkWidget *widget,
87 GtkAllocation *allocation);
88 static void gdl_dock_item_map (GtkWidget *widget);
89 static void gdl_dock_item_unmap (GtkWidget *widget);
90 static void gdl_dock_item_realize (GtkWidget *widget);
91 static void gdl_dock_item_style_set (GtkWidget *widget,
92 GtkStyle *previous_style);
93 static gint gdl_dock_item_expose (GtkWidget *widget,
94 GdkEventExpose *event);
96 static gint gdl_dock_item_button_changed (GtkWidget *widget,
97 GdkEventButton *event);
98 static gint gdl_dock_item_motion (GtkWidget *widget,
99 GdkEventMotion *event);
100 static gboolean gdl_dock_item_key_press (GtkWidget *widget,
101 GdkEventKey *event);
103 static gboolean gdl_dock_item_dock_request (GdlDockObject *object,
104 gint x,
105 gint y,
106 GdlDockRequest *request);
107 static void gdl_dock_item_dock (GdlDockObject *object,
108 GdlDockObject *requestor,
109 GdlDockPlacement position,
110 GValue *other_data);
112 static void gdl_dock_item_popup_menu (GdlDockItem *item,
113 guint button,
114 guint32 time);
115 static void gdl_dock_item_drag_start (GdlDockItem *item);
116 static void gdl_dock_item_drag_end (GdlDockItem *item,
117 gboolean cancel);
119 static void gdl_dock_item_tab_button (GtkWidget *widget,
120 GdkEventButton *event,
121 gpointer data);
123 static void gdl_dock_item_hide_cb (GtkWidget *widget,
124 GdlDockItem *item);
126 static void gdl_dock_item_lock_cb (GtkWidget *widget,
127 GdlDockItem *item);
129 static void gdl_dock_item_unlock_cb (GtkWidget *widget,
130 GdlDockItem *item);
132 static void gdl_dock_item_showhide_grip (GdlDockItem *item);
134 static void gdl_dock_item_real_set_orientation (GdlDockItem *item,
135 GtkOrientation orientation);
137 static void gdl_dock_param_export_gtk_orientation (const GValue *src,
138 GValue *dst);
139 static void gdl_dock_param_import_gtk_orientation (const GValue *src,
140 GValue *dst);
144 /* ----- Class variables and definitions ----- */
146 enum {
147 PROP_0,
148 PROP_ORIENTATION,
149 PROP_RESIZE,
150 PROP_BEHAVIOR,
151 PROP_LOCKED,
152 PROP_PREFERRED_WIDTH,
153 PROP_PREFERRED_HEIGHT
154 };
156 enum {
157 DOCK_DRAG_BEGIN,
158 DOCK_DRAG_MOTION,
159 DOCK_DRAG_END,
160 LAST_SIGNAL
161 };
163 static guint gdl_dock_item_signals [LAST_SIGNAL] = { 0 };
165 #define GDL_DOCK_ITEM_GRIP_SHOWN(item) \
166 (GDL_DOCK_ITEM_HAS_GRIP (item))
168 struct _GdlDockItemPrivate {
169 GtkWidget *menu;
171 gboolean grip_shown;
172 GtkWidget *grip;
173 guint grip_size;
175 GtkWidget *tab_label;
177 gint preferred_width;
178 gint preferred_height;
180 GdlDockPlaceholder *ph;
182 gint start_x, start_y;
183 };
185 /* FIXME: implement the rest of the behaviors */
187 #define SPLIT_RATIO 0.4
190 /* ----- Private functions ----- */
192 GDL_CLASS_BOILERPLATE (GdlDockItem, gdl_dock_item, GdlDockObject, GDL_TYPE_DOCK_OBJECT);
194 static void
195 gdl_dock_item_class_init (GdlDockItemClass *klass)
196 {
197 static gboolean style_initialized = FALSE;
199 GObjectClass *g_object_class;
200 GtkObjectClass *gtk_object_class;
201 GtkWidgetClass *widget_class;
202 GtkContainerClass *container_class;
203 GdlDockObjectClass *object_class;
205 g_object_class = G_OBJECT_CLASS (klass);
206 gtk_object_class = GTK_OBJECT_CLASS (klass);
207 widget_class = GTK_WIDGET_CLASS (klass);
208 container_class = GTK_CONTAINER_CLASS (klass);
209 object_class = GDL_DOCK_OBJECT_CLASS (klass);
211 g_object_class->constructor = gdl_dock_item_constructor;
212 g_object_class->set_property = gdl_dock_item_set_property;
213 g_object_class->get_property = gdl_dock_item_get_property;
215 gtk_object_class->destroy = gdl_dock_item_destroy;
217 widget_class->realize = gdl_dock_item_realize;
218 widget_class->map = gdl_dock_item_map;
219 widget_class->unmap = gdl_dock_item_unmap;
220 widget_class->size_request = gdl_dock_item_size_request;
221 widget_class->size_allocate = gdl_dock_item_size_allocate;
222 widget_class->style_set = gdl_dock_item_style_set;
223 widget_class->expose_event = gdl_dock_item_expose;
224 widget_class->button_press_event = gdl_dock_item_button_changed;
225 widget_class->button_release_event = gdl_dock_item_button_changed;
226 widget_class->motion_notify_event = gdl_dock_item_motion;
227 widget_class->key_press_event = gdl_dock_item_key_press;
229 container_class->add = gdl_dock_item_add;
230 container_class->remove = gdl_dock_item_remove;
231 container_class->forall = gdl_dock_item_forall;
232 container_class->child_type = gdl_dock_item_child_type;
234 object_class->is_compound = FALSE;
236 object_class->dock_request = gdl_dock_item_dock_request;
237 object_class->dock = gdl_dock_item_dock;
239 /* properties */
241 g_object_class_install_property (
242 g_object_class, PROP_ORIENTATION,
243 g_param_spec_enum ("orientation", _("Orientation"),
244 _("Orientation of the docking item"),
245 GTK_TYPE_ORIENTATION,
246 GTK_ORIENTATION_VERTICAL,
247 G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
248 GDL_DOCK_PARAM_EXPORT));
250 /* --- register exporter/importer for GTK_ORIENTATION */
251 g_value_register_transform_func (GTK_TYPE_ORIENTATION, GDL_TYPE_DOCK_PARAM,
252 gdl_dock_param_export_gtk_orientation);
253 g_value_register_transform_func (GDL_TYPE_DOCK_PARAM, GTK_TYPE_ORIENTATION,
254 gdl_dock_param_import_gtk_orientation);
255 /* --- end of registration */
257 g_object_class_install_property (
258 g_object_class, PROP_RESIZE,
259 g_param_spec_boolean ("resize", _("Resizable"),
260 _("If set, the dock item can be resized when "
261 "docked in a paned"),
262 TRUE,
263 G_PARAM_READWRITE));
265 g_object_class_install_property (
266 g_object_class, PROP_BEHAVIOR,
267 g_param_spec_flags ("behavior", _("Item behavior"),
268 _("General behavior for the dock item (i.e. "
269 "whether it can float, if it's locked, etc.)"),
270 GDL_TYPE_DOCK_ITEM_BEHAVIOR,
271 GDL_DOCK_ITEM_BEH_NORMAL,
272 G_PARAM_READWRITE));
274 g_object_class_install_property (
275 g_object_class, PROP_LOCKED,
276 g_param_spec_boolean ("locked", _("Locked"),
277 _("If set, the dock item cannot be dragged around "
278 "and it doesn't show a grip"),
279 FALSE,
280 G_PARAM_READWRITE |
281 GDL_DOCK_PARAM_EXPORT));
283 g_object_class_install_property (
284 g_object_class, PROP_PREFERRED_WIDTH,
285 g_param_spec_int ("preferred-width", _("Preferred width"),
286 _("Preferred width for the dock item"),
287 -1, G_MAXINT, -1,
288 G_PARAM_READWRITE));
290 g_object_class_install_property (
291 g_object_class, PROP_PREFERRED_HEIGHT,
292 g_param_spec_int ("preferred-height", _("Preferred height"),
293 _("Preferred height for the dock item"),
294 -1, G_MAXINT, -1,
295 G_PARAM_READWRITE));
297 /* signals */
299 gdl_dock_item_signals [DOCK_DRAG_BEGIN] =
300 g_signal_new ("dock-drag-begin",
301 G_TYPE_FROM_CLASS (klass),
302 G_SIGNAL_RUN_FIRST,
303 G_STRUCT_OFFSET (GdlDockItemClass, dock_drag_begin),
304 NULL, /* accumulator */
305 NULL, /* accu_data */
306 gdl_marshal_VOID__VOID,
307 G_TYPE_NONE,
308 0);
310 gdl_dock_item_signals [DOCK_DRAG_MOTION] =
311 g_signal_new ("dock-drag-motion",
312 G_TYPE_FROM_CLASS (klass),
313 G_SIGNAL_RUN_FIRST,
314 G_STRUCT_OFFSET (GdlDockItemClass, dock_drag_motion),
315 NULL, /* accumulator */
316 NULL, /* accu_data */
317 gdl_marshal_VOID__INT_INT,
318 G_TYPE_NONE,
319 2,
320 G_TYPE_INT,
321 G_TYPE_INT);
323 gdl_dock_item_signals [DOCK_DRAG_END] =
324 g_signal_new ("dock_drag_end",
325 G_TYPE_FROM_CLASS (klass),
326 G_SIGNAL_RUN_FIRST,
327 G_STRUCT_OFFSET (GdlDockItemClass, dock_drag_end),
328 NULL, /* accumulator */
329 NULL, /* accu_data */
330 gdl_marshal_VOID__BOOLEAN,
331 G_TYPE_NONE,
332 1,
333 G_TYPE_BOOLEAN);
335 klass->has_grip = TRUE;
336 klass->dock_drag_begin = NULL;
337 klass->dock_drag_motion = NULL;
338 klass->dock_drag_end = NULL;
339 klass->set_orientation = gdl_dock_item_real_set_orientation;
341 if (!style_initialized)
342 {
343 style_initialized = TRUE;
344 gtk_rc_parse_string (
345 "style \"gdl-dock-item-default\" {\n"
346 "xthickness = 0\n"
347 "ythickness = 0\n"
348 "}\n"
349 "class \"GdlDockItem\" "
350 "style : gtk \"gdl-dock-item-default\"\n");
351 }
352 }
354 static void
355 gdl_dock_item_instance_init (GdlDockItem *item)
356 {
357 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (item), GTK_NO_WINDOW);
359 item->child = NULL;
361 item->orientation = GTK_ORIENTATION_VERTICAL;
362 item->behavior = GDL_DOCK_ITEM_BEH_NORMAL;
364 item->resize = TRUE;
366 item->dragoff_x = item->dragoff_y = 0;
368 item->_priv = g_new0 (GdlDockItemPrivate, 1);
369 item->_priv->menu = NULL;
371 item->_priv->preferred_width = item->_priv->preferred_height = -1;
372 item->_priv->tab_label = NULL;
374 item->_priv->ph = NULL;
375 }
377 static GObject *
378 gdl_dock_item_constructor (GType type,
379 guint n_construct_properties,
380 GObjectConstructParam *construct_param)
381 {
382 GObject *g_object;
384 g_object = GDL_CALL_PARENT_WITH_DEFAULT (G_OBJECT_CLASS,
385 constructor,
386 (type,
387 n_construct_properties,
388 construct_param),
389 NULL);
390 if (g_object) {
391 GdlDockItem *item = GDL_DOCK_ITEM (g_object);
393 if (GDL_DOCK_ITEM_HAS_GRIP (item)) {
394 item->_priv->grip_shown = TRUE;
395 item->_priv->grip = gdl_dock_item_grip_new (item);
396 gtk_widget_set_parent (item->_priv->grip, GTK_WIDGET (item));
397 gtk_widget_show (item->_priv->grip);
398 }
399 else {
400 item->_priv->grip_shown = FALSE;
401 }
402 }
404 return g_object;
405 }
407 static void
408 gdl_dock_item_set_property (GObject *g_object,
409 guint prop_id,
410 const GValue *value,
411 GParamSpec *pspec)
412 {
413 GdlDockItem *item = GDL_DOCK_ITEM (g_object);
415 switch (prop_id) {
416 case PROP_ORIENTATION:
417 gdl_dock_item_set_orientation (item, g_value_get_enum (value));
418 break;
419 case PROP_RESIZE:
420 item->resize = g_value_get_boolean (value);
421 gtk_widget_queue_resize (GTK_WIDGET (item));
422 break;
423 case PROP_BEHAVIOR:
424 {
425 GdlDockItemBehavior old_beh = item->behavior;
426 item->behavior = g_value_get_flags (value);
428 if ((old_beh ^ item->behavior) & GDL_DOCK_ITEM_BEH_LOCKED) {
429 if (GDL_DOCK_OBJECT_GET_MASTER (item))
430 g_signal_emit_by_name (GDL_DOCK_OBJECT_GET_MASTER (item),
431 "layout-changed");
432 g_object_notify (g_object, "locked");
433 gdl_dock_item_showhide_grip (item);
434 }
436 break;
437 }
438 case PROP_LOCKED:
439 {
440 GdlDockItemBehavior old_beh = item->behavior;
442 if (g_value_get_boolean (value))
443 item->behavior |= GDL_DOCK_ITEM_BEH_LOCKED;
444 else
445 item->behavior &= ~GDL_DOCK_ITEM_BEH_LOCKED;
447 if (old_beh ^ item->behavior) {
448 gdl_dock_item_showhide_grip (item);
449 g_object_notify (g_object, "behavior");
451 if (GDL_DOCK_OBJECT_GET_MASTER (item))
452 g_signal_emit_by_name (GDL_DOCK_OBJECT_GET_MASTER (item),
453 "layout-changed");
454 }
455 break;
456 }
457 case PROP_PREFERRED_WIDTH:
458 item->_priv->preferred_width = g_value_get_int (value);
459 break;
460 case PROP_PREFERRED_HEIGHT:
461 item->_priv->preferred_height = g_value_get_int (value);
462 break;
463 default:
464 G_OBJECT_WARN_INVALID_PROPERTY_ID (g_object, prop_id, pspec);
465 break;
466 }
467 }
469 static void
470 gdl_dock_item_get_property (GObject *g_object,
471 guint prop_id,
472 GValue *value,
473 GParamSpec *pspec)
474 {
475 GdlDockItem *item = GDL_DOCK_ITEM (g_object);
477 switch (prop_id) {
478 case PROP_ORIENTATION:
479 g_value_set_enum (value, item->orientation);
480 break;
481 case PROP_RESIZE:
482 g_value_set_boolean (value, item->resize);
483 break;
484 case PROP_BEHAVIOR:
485 g_value_set_flags (value, item->behavior);
486 break;
487 case PROP_LOCKED:
488 g_value_set_boolean (value, !GDL_DOCK_ITEM_NOT_LOCKED (item));
489 break;
490 case PROP_PREFERRED_WIDTH:
491 g_value_set_int (value, item->_priv->preferred_width);
492 break;
493 case PROP_PREFERRED_HEIGHT:
494 g_value_set_int (value, item->_priv->preferred_height);
495 break;
496 default:
497 G_OBJECT_WARN_INVALID_PROPERTY_ID (g_object, prop_id, pspec);
498 break;
499 }
500 }
502 static void
503 gdl_dock_item_destroy (GtkObject *object)
504 {
505 GdlDockItem *item = GDL_DOCK_ITEM (object);
507 if (item->_priv) {
508 GdlDockItemPrivate *priv = item->_priv;
510 if (priv->tab_label) {
511 gdl_dock_item_set_tablabel (item, NULL);
512 };
513 if (priv->menu) {
514 gtk_menu_detach (GTK_MENU (priv->menu));
515 priv->menu = NULL;
516 };
517 if (priv->grip) {
518 gtk_container_remove (GTK_CONTAINER (item), priv->grip);
519 priv->grip = NULL;
520 }
521 if (priv->ph) {
522 g_object_unref (priv->ph);
523 priv->ph = NULL;
524 }
526 item->_priv = NULL;
527 g_free (priv);
528 }
530 GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
531 }
533 static void
534 gdl_dock_item_add (GtkContainer *container,
535 GtkWidget *widget)
536 {
537 GdlDockItem *item;
539 g_return_if_fail (GDL_IS_DOCK_ITEM (container));
541 item = GDL_DOCK_ITEM (container);
542 if (GDL_IS_DOCK_OBJECT (widget)) {
543 g_warning (_("You can't add a dock object (%p of type %s) inside a %s. "
544 "Use a GdlDock or some other compound dock object."),
545 widget, G_OBJECT_TYPE_NAME (widget), G_OBJECT_TYPE_NAME (item));
546 return;
547 }
549 if (item->child != NULL) {
550 g_warning (_("Attempting to add a widget with type %s to a %s, "
551 "but it can only contain one widget at a time; "
552 "it already contains a widget of type %s"),
553 G_OBJECT_TYPE_NAME (widget),
554 G_OBJECT_TYPE_NAME (item),
555 G_OBJECT_TYPE_NAME (item->child));
556 return;
557 }
559 gtk_widget_set_parent (widget, GTK_WIDGET (item));
560 item->child = widget;
561 }
563 static void
564 gdl_dock_item_remove (GtkContainer *container,
565 GtkWidget *widget)
566 {
567 GdlDockItem *item;
568 gboolean was_visible;
570 g_return_if_fail (GDL_IS_DOCK_ITEM (container));
572 item = GDL_DOCK_ITEM (container);
573 if (item->_priv && widget == item->_priv->grip) {
574 gboolean grip_was_visible = GTK_WIDGET_VISIBLE (widget);
575 gtk_widget_unparent (widget);
576 item->_priv->grip = NULL;
577 if (grip_was_visible)
578 gtk_widget_queue_resize (GTK_WIDGET (item));
579 return;
580 }
582 if (GDL_DOCK_ITEM_IN_DRAG (item)) {
583 gdl_dock_item_drag_end (item, TRUE);
584 }
586 g_return_if_fail (item->child == widget);
588 was_visible = GTK_WIDGET_VISIBLE (widget);
590 gtk_widget_unparent (widget);
591 item->child = NULL;
593 if (was_visible)
594 gtk_widget_queue_resize (GTK_WIDGET (container));
595 }
597 static void
598 gdl_dock_item_forall (GtkContainer *container,
599 gboolean include_internals,
600 GtkCallback callback,
601 gpointer callback_data)
602 {
603 GdlDockItem *item = (GdlDockItem *) container;
605 g_return_if_fail (callback != NULL);
607 if (include_internals && item->_priv->grip)
608 (* callback) (item->_priv->grip, callback_data);
610 if (item->child)
611 (* callback) (item->child, callback_data);
612 }
614 static GtkType
615 gdl_dock_item_child_type (GtkContainer *container)
616 {
617 g_return_val_if_fail (GDL_IS_DOCK_ITEM (container), G_TYPE_NONE);
619 if (!GDL_DOCK_ITEM (container)->child)
620 return GTK_TYPE_WIDGET;
621 else
622 return G_TYPE_NONE;
623 }
625 static void
626 gdl_dock_item_size_request (GtkWidget *widget,
627 GtkRequisition *requisition)
628 {
629 GtkRequisition child_requisition;
630 GtkRequisition grip_requisition;
631 GdlDockItem *item;
633 g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
634 g_return_if_fail (requisition != NULL);
636 item = GDL_DOCK_ITEM (widget);
638 /* If our child is not visible, we still request its size, since
639 we won't have any useful hint for our size otherwise. */
640 if (item->child)
641 gtk_widget_size_request (item->child, &child_requisition);
642 else {
643 child_requisition.width = 0;
644 child_requisition.height = 0;
645 }
647 if (item->orientation == GTK_ORIENTATION_HORIZONTAL) {
648 if (GDL_DOCK_ITEM_GRIP_SHOWN (item)) {
649 gtk_widget_size_request (item->_priv->grip, &grip_requisition);
650 requisition->width = grip_requisition.width;
651 } else {
652 requisition->width = 0;
653 }
655 if (item->child) {
656 requisition->width += child_requisition.width;
657 requisition->height = child_requisition.height;
658 } else
659 requisition->height = 0;
660 } else {
661 if (GDL_DOCK_ITEM_GRIP_SHOWN (item)) {
662 gtk_widget_size_request (item->_priv->grip, &grip_requisition);
663 requisition->height = grip_requisition.height;
664 } else {
665 requisition->height = 0;
666 }
668 if (item->child) {
669 requisition->width = child_requisition.width;
670 requisition->height += child_requisition.height;
671 } else
672 requisition->width = 0;
673 }
675 requisition->width += (GTK_CONTAINER (widget)->border_width + widget->style->xthickness) * 2;
676 requisition->height += (GTK_CONTAINER (widget)->border_width + widget->style->ythickness) * 2;
678 widget->requisition = *requisition;
679 }
681 static void
682 gdl_dock_item_size_allocate (GtkWidget *widget,
683 GtkAllocation *allocation)
684 {
685 GdlDockItem *item;
687 g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
688 g_return_if_fail (allocation != NULL);
690 item = GDL_DOCK_ITEM (widget);
692 widget->allocation = *allocation;
694 /* Once size is allocated, preferred size is no longer necessary */
695 item->_priv->preferred_height = -1;
696 item->_priv->preferred_width = -1;
698 if (GTK_WIDGET_REALIZED (widget))
699 gdk_window_move_resize (widget->window,
700 widget->allocation.x,
701 widget->allocation.y,
702 widget->allocation.width,
703 widget->allocation.height);
705 if (item->child && GTK_WIDGET_VISIBLE (item->child)) {
706 GtkAllocation child_allocation;
707 int border_width;
709 border_width = GTK_CONTAINER (widget)->border_width;
711 child_allocation.x = border_width + widget->style->xthickness;
712 child_allocation.y = border_width + widget->style->ythickness;
713 child_allocation.width = allocation->width
714 - 2 * (border_width + widget->style->xthickness);
715 child_allocation.height = allocation->height
716 - 2 * (border_width + widget->style->ythickness);
718 if (GDL_DOCK_ITEM_GRIP_SHOWN (item)) {
719 GtkAllocation grip_alloc = child_allocation;
720 GtkRequisition grip_req;
722 gtk_widget_size_request (item->_priv->grip, &grip_req);
724 if (item->orientation == GTK_ORIENTATION_HORIZONTAL) {
725 child_allocation.x += grip_req.width;
726 child_allocation.width -= grip_req.width;
727 grip_alloc.width = grip_req.width;
728 } else {
729 child_allocation.y += grip_req.height;
730 child_allocation.height -= grip_req.height;
731 grip_alloc.height = grip_req.height;
732 }
733 if (item->_priv->grip)
734 gtk_widget_size_allocate (item->_priv->grip, &grip_alloc);
735 }
736 /* Allocation can't be negative */
737 if (child_allocation.width < 0)
738 child_allocation.width = 0;
739 if (child_allocation.height < 0)
740 child_allocation.height = 0;
741 gtk_widget_size_allocate (item->child, &child_allocation);
742 }
743 }
745 static void
746 gdl_dock_item_map (GtkWidget *widget)
747 {
748 GdlDockItem *item;
750 g_return_if_fail (widget != NULL);
751 g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
753 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
755 item = GDL_DOCK_ITEM (widget);
757 gdk_window_show (widget->window);
759 if (item->child
760 && GTK_WIDGET_VISIBLE (item->child)
761 && !GTK_WIDGET_MAPPED (item->child))
762 gtk_widget_map (item->child);
764 if (item->_priv->grip
765 && GTK_WIDGET_VISIBLE (item->_priv->grip)
766 && !GTK_WIDGET_MAPPED (item->_priv->grip))
767 gtk_widget_map (item->_priv->grip);
768 }
770 static void
771 gdl_dock_item_unmap (GtkWidget *widget)
772 {
773 GdlDockItem *item;
775 g_return_if_fail (widget != NULL);
776 g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
778 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
780 item = GDL_DOCK_ITEM (widget);
782 gdk_window_hide (widget->window);
784 if (item->_priv->grip)
785 gtk_widget_unmap (item->_priv->grip);
786 }
788 static void
789 gdl_dock_item_realize (GtkWidget *widget)
790 {
791 GdkWindowAttr attributes;
792 gint attributes_mask;
793 GdlDockItem *item;
795 g_return_if_fail (widget != NULL);
796 g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
798 item = GDL_DOCK_ITEM (widget);
800 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
802 /* widget window */
803 attributes.x = widget->allocation.x;
804 attributes.y = widget->allocation.y;
805 attributes.width = widget->allocation.width;
806 attributes.height = widget->allocation.height;
807 attributes.window_type = GDK_WINDOW_CHILD;
808 attributes.wclass = GDK_INPUT_OUTPUT;
809 attributes.visual = gtk_widget_get_visual (widget);
810 attributes.colormap = gtk_widget_get_colormap (widget);
811 attributes.event_mask = (gtk_widget_get_events (widget) |
812 GDK_EXPOSURE_MASK |
813 GDK_BUTTON1_MOTION_MASK |
814 GDK_BUTTON_PRESS_MASK |
815 GDK_BUTTON_RELEASE_MASK);
816 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
817 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
818 &attributes, attributes_mask);
819 gdk_window_set_user_data (widget->window, widget);
821 widget->style = gtk_style_attach (widget->style, widget->window);
822 gtk_style_set_background (widget->style, widget->window,
823 GTK_WIDGET_STATE (item));
824 gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
826 if (item->child)
827 gtk_widget_set_parent_window (item->child, widget->window);
829 if (item->_priv->grip)
830 gtk_widget_set_parent_window (item->_priv->grip, widget->window);
831 }
833 static void
834 gdl_dock_item_style_set (GtkWidget *widget,
835 GtkStyle *previous_style)
836 {
837 g_return_if_fail (widget != NULL);
838 g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
840 if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_NO_WINDOW (widget)) {
841 gtk_style_set_background (widget->style, widget->window,
842 widget->state);
843 if (GTK_WIDGET_DRAWABLE (widget))
844 gdk_window_clear (widget->window);
845 }
846 }
848 static void
849 gdl_dock_item_paint (GtkWidget *widget,
850 GdkEventExpose *event)
851 {
852 GdlDockItem *item;
854 item = GDL_DOCK_ITEM (widget);
856 gtk_paint_box (widget->style,
857 widget->window,
858 GTK_WIDGET_STATE (widget),
859 GTK_SHADOW_NONE,
860 &event->area, widget,
861 "dockitem",
862 0, 0, -1, -1);
863 }
865 static gint
866 gdl_dock_item_expose (GtkWidget *widget,
867 GdkEventExpose *event)
868 {
869 g_return_val_if_fail (widget != NULL, FALSE);
870 g_return_val_if_fail (GDL_IS_DOCK_ITEM (widget), FALSE);
871 g_return_val_if_fail (event != NULL, FALSE);
873 if (GTK_WIDGET_DRAWABLE (widget) && event->window == widget->window) {
874 gdl_dock_item_paint (widget, event);
875 GDL_CALL_PARENT_GBOOLEAN(GTK_WIDGET_CLASS, expose_event, (widget,event));
876 }
878 return FALSE;
879 }
881 #define EVENT_IN_GRIP_EVENT_WINDOW(ev,gr) \
882 ((gr) != NULL && (ev)->window == GDL_DOCK_ITEM_GRIP (gr)->title_window)
884 #define EVENT_IN_TABLABEL_EVENT_WINDOW(ev,tl) \
885 ((tl) != NULL && (ev)->window == GDL_DOCK_TABLABEL (tl)->event_window)
887 static gint
888 gdl_dock_item_button_changed (GtkWidget *widget,
889 GdkEventButton *event)
890 {
891 GdlDockItem *item;
892 gboolean locked;
893 gboolean event_handled;
894 gboolean in_handle;
895 GdkCursor *cursor;
897 g_return_val_if_fail (widget != NULL, FALSE);
898 g_return_val_if_fail (GDL_IS_DOCK_ITEM (widget), FALSE);
899 g_return_val_if_fail (event != NULL, FALSE);
901 item = GDL_DOCK_ITEM (widget);
903 if (!EVENT_IN_GRIP_EVENT_WINDOW (event, item->_priv->grip))
904 return FALSE;
906 locked = !GDL_DOCK_ITEM_NOT_LOCKED (item);
908 event_handled = FALSE;
910 /* Check if user clicked on the drag handle. */
911 switch (item->orientation) {
912 case GTK_ORIENTATION_HORIZONTAL:
913 in_handle = event->x < item->_priv->grip->allocation.width;
914 break;
915 case GTK_ORIENTATION_VERTICAL:
916 in_handle = event->y < item->_priv->grip->allocation.height;
917 break;
918 default:
919 in_handle = FALSE;
920 break;
921 }
923 /* Left mousebutton click on dockitem. */
924 if (!locked && event->button == 1 && event->type == GDK_BUTTON_PRESS) {
925 /* Set in_drag flag, grab pointer and call begin drag operation. */
926 if (in_handle) {
927 item->_priv->start_x = event->x;
928 item->_priv->start_y = event->y;
930 GDL_DOCK_ITEM_SET_FLAGS (item, GDL_DOCK_IN_PREDRAG);
932 cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
933 GDK_FLEUR);
934 gdk_window_set_cursor (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window,
935 cursor);
936 gdk_cursor_unref (cursor);
938 event_handled = TRUE;
939 };
941 } else if (!locked &&event->type == GDK_BUTTON_RELEASE && event->button == 1) {
942 if (GDL_DOCK_ITEM_IN_DRAG (item)) {
943 /* User dropped widget somewhere. */
944 gdl_dock_item_drag_end (item, FALSE);
945 event_handled = TRUE;
946 }
947 else if (GDL_DOCK_ITEM_IN_PREDRAG (item)) {
948 GDL_DOCK_ITEM_UNSET_FLAGS (item, GDL_DOCK_IN_PREDRAG);
949 event_handled = TRUE;
950 }
952 /* we check the window since if the item was redocked it's
953 been unrealized and maybe it's not realized again yet */
954 if (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window) {
955 cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
956 GDK_HAND2);
957 gdk_window_set_cursor (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window,
958 cursor);
959 gdk_cursor_unref (cursor);
960 }
962 } else if (event->button == 3 && event->type == GDK_BUTTON_PRESS && in_handle) {
963 gdl_dock_item_popup_menu (item, event->button, event->time);
964 event_handled = TRUE;
965 }
967 return event_handled;
968 }
970 static gint
971 gdl_dock_item_motion (GtkWidget *widget,
972 GdkEventMotion *event)
973 {
974 GdlDockItem *item;
975 gint new_x, new_y;
977 g_return_val_if_fail (widget != NULL, FALSE);
978 g_return_val_if_fail (GDL_IS_DOCK_ITEM (widget), FALSE);
979 g_return_val_if_fail (event != NULL, FALSE);
981 item = GDL_DOCK_ITEM (widget);
983 if (!EVENT_IN_GRIP_EVENT_WINDOW (event, item->_priv->grip))
984 return FALSE;
986 if (GDL_DOCK_ITEM_IN_PREDRAG (item)) {
987 if (gtk_drag_check_threshold (widget,
988 item->_priv->start_x,
989 item->_priv->start_y,
990 event->x,
991 event->y)) {
992 GDL_DOCK_ITEM_UNSET_FLAGS (item, GDL_DOCK_IN_PREDRAG);
993 item->dragoff_x = item->_priv->start_x;
994 item->dragoff_y = item->_priv->start_y;
996 gdl_dock_item_drag_start (item);
997 }
998 }
1000 if (!GDL_DOCK_ITEM_IN_DRAG (item))
1001 return FALSE;
1003 new_x = event->x_root;
1004 new_y = event->y_root;
1006 g_signal_emit (item, gdl_dock_item_signals [DOCK_DRAG_MOTION],
1007 0, new_x, new_y);
1009 return TRUE;
1010 }
1012 static gboolean
1013 gdl_dock_item_key_press (GtkWidget *widget,
1014 GdkEventKey *event)
1015 {
1016 gboolean event_handled = FALSE;
1017 if (GDL_DOCK_ITEM_IN_DRAG (widget)) {
1018 if (event->keyval == GDK_Escape) {
1019 gdl_dock_item_drag_end (GDL_DOCK_ITEM (widget), TRUE);
1020 event_handled = TRUE;
1021 }
1022 }
1024 if (event_handled)
1025 return TRUE;
1026 else
1027 return GDL_CALL_PARENT_WITH_DEFAULT (GTK_WIDGET_CLASS,
1028 key_press_event,
1029 (widget, event),
1030 FALSE);
1031 }
1033 static gboolean
1034 gdl_dock_item_dock_request (GdlDockObject *object,
1035 gint x,
1036 gint y,
1037 GdlDockRequest *request)
1038 {
1039 GtkAllocation *alloc;
1040 gint rel_x, rel_y;
1042 /* we get (x,y) in our allocation coordinates system */
1044 /* Get item's allocation. */
1045 alloc = &(GTK_WIDGET (object)->allocation);
1047 /* Get coordinates relative to our window. */
1048 rel_x = x - alloc->x;
1049 rel_y = y - alloc->y;
1051 /* Location is inside. */
1052 if (rel_x > 0 && rel_x < alloc->width &&
1053 rel_y > 0 && rel_y < alloc->height) {
1054 float rx, ry;
1055 GtkRequisition my, other;
1056 gint divider = -1;
1058 /* this are for calculating the extra docking parameter */
1059 gdl_dock_item_preferred_size (GDL_DOCK_ITEM (request->applicant), &other);
1060 gdl_dock_item_preferred_size (GDL_DOCK_ITEM (object), &my);
1062 /* Calculate location in terms of the available space (0-100%). */
1063 rx = (float) rel_x / alloc->width;
1064 ry = (float) rel_y / alloc->height;
1066 /* Determine dock location. */
1067 if (rx < SPLIT_RATIO) {
1068 request->position = GDL_DOCK_LEFT;
1069 divider = other.width;
1070 }
1071 else if (rx > (1 - SPLIT_RATIO)) {
1072 request->position = GDL_DOCK_RIGHT;
1073 rx = 1 - rx;
1074 divider = MAX (0, my.width - other.width);
1075 }
1076 else if (ry < SPLIT_RATIO && ry < rx) {
1077 request->position = GDL_DOCK_TOP;
1078 divider = other.height;
1079 }
1080 else if (ry > (1 - SPLIT_RATIO) && (1 - ry) < rx) {
1081 request->position = GDL_DOCK_BOTTOM;
1082 divider = MAX (0, my.height - other.height);
1083 }
1084 else
1085 request->position = GDL_DOCK_CENTER;
1087 /* Reset rectangle coordinates to entire item. */
1088 request->rect.x = 0;
1089 request->rect.y = 0;
1090 request->rect.width = alloc->width;
1091 request->rect.height = alloc->height;
1093 GdlDockItemBehavior behavior = GDL_DOCK_ITEM(object)->behavior;
1095 /* Calculate docking indicator rectangle size for new locations. Only
1096 do this when we're not over the item's current location. */
1097 if (request->applicant != object) {
1098 switch (request->position) {
1099 case GDL_DOCK_TOP:
1100 if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_TOP)
1101 return FALSE;
1102 request->rect.height *= SPLIT_RATIO;
1103 break;
1104 case GDL_DOCK_BOTTOM:
1105 if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_BOTTOM)
1106 return FALSE;
1107 request->rect.y += request->rect.height * (1 - SPLIT_RATIO);
1108 request->rect.height *= SPLIT_RATIO;
1109 break;
1110 case GDL_DOCK_LEFT:
1111 if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_LEFT)
1112 return FALSE;
1113 request->rect.width *= SPLIT_RATIO;
1114 break;
1115 case GDL_DOCK_RIGHT:
1116 if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_RIGHT)
1117 return FALSE;
1118 request->rect.x += request->rect.width * (1 - SPLIT_RATIO);
1119 request->rect.width *= SPLIT_RATIO;
1120 break;
1121 case GDL_DOCK_CENTER:
1122 if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_CENTER)
1123 return FALSE;
1124 request->rect.x = request->rect.width * SPLIT_RATIO/2;
1125 request->rect.y = request->rect.height * SPLIT_RATIO/2;
1126 request->rect.width = (request->rect.width *
1127 (1 - SPLIT_RATIO/2)) - request->rect.x;
1128 request->rect.height = (request->rect.height *
1129 (1 - SPLIT_RATIO/2)) - request->rect.y;
1130 break;
1131 default:
1132 break;
1133 }
1134 }
1136 /* adjust returned coordinates so they are have the same
1137 origin as our window */
1138 request->rect.x += alloc->x;
1139 request->rect.y += alloc->y;
1141 /* Set possible target location and return TRUE. */
1142 request->target = object;
1144 /* fill-in other dock information */
1145 if (request->position != GDL_DOCK_CENTER && divider >= 0) {
1146 if (G_IS_VALUE (&request->extra))
1147 g_value_unset (&request->extra);
1148 g_value_init (&request->extra, G_TYPE_UINT);
1149 g_value_set_uint (&request->extra, (guint) divider);
1150 }
1152 return TRUE;
1153 }
1154 else /* No docking possible at this location. */
1155 return FALSE;
1156 }
1158 static void
1159 gdl_dock_item_dock (GdlDockObject *object,
1160 GdlDockObject *requestor,
1161 GdlDockPlacement position,
1162 GValue *other_data)
1163 {
1164 GdlDockObject *new_parent, *parent;
1165 gboolean add_ourselves_first;
1167 guint available_space=0;
1168 gint pref_size=-1;
1169 guint splitpos=0;
1170 GtkRequisition req, object_req, parent_req;
1172 parent = gdl_dock_object_get_parent_object (object);
1173 gdl_dock_item_preferred_size (GDL_DOCK_ITEM (requestor), &req);
1174 gdl_dock_item_preferred_size (GDL_DOCK_ITEM (object), &object_req);
1175 if (GDL_IS_DOCK_ITEM (parent))
1176 gdl_dock_item_preferred_size (GDL_DOCK_ITEM (parent), &parent_req);
1177 else
1178 {
1179 parent_req.height = GTK_WIDGET (parent)->allocation.height;
1180 parent_req.width = GTK_WIDGET (parent)->allocation.width;
1181 }
1183 /* If preferred size is not set on the requestor (perhaps a new item),
1184 * then estimate and set it. The default value (either 0 or 1 pixels) is
1185 * not any good.
1186 */
1187 switch (position) {
1188 case GDL_DOCK_TOP:
1189 case GDL_DOCK_BOTTOM:
1190 if (req.width < 2)
1191 {
1192 req.width = object_req.width;
1193 g_object_set (requestor, "preferred-width", req.width, NULL);
1194 }
1195 if (req.height < 2)
1196 {
1197 req.height = NEW_DOCK_ITEM_RATIO * object_req.height;
1198 g_object_set (requestor, "preferred-height", req.height, NULL);
1199 }
1200 if (req.width > 1)
1201 g_object_set (object, "preferred-width", req.width, NULL);
1202 if (req.height > 1)
1203 g_object_set (object, "preferred-height",
1204 object_req.height - req.height, NULL);
1205 break;
1206 case GDL_DOCK_LEFT:
1207 case GDL_DOCK_RIGHT:
1208 if (req.height < 2)
1209 {
1210 req.height = object_req.height;
1211 g_object_set (requestor, "preferred-height", req.height, NULL);
1212 }
1213 if (req.width < 2)
1214 {
1215 req.width = NEW_DOCK_ITEM_RATIO * object_req.width;
1216 g_object_set (requestor, "preferred-width", req.width, NULL);
1217 }
1218 if (req.height > 1)
1219 g_object_set (object, "preferred-height", req.height, NULL);
1220 if (req.width > 1)
1221 g_object_set (object, "preferred-width",
1222 object_req.width - req.width, NULL);
1223 break;
1224 case GDL_DOCK_CENTER:
1225 if (req.height < 2)
1226 {
1227 req.height = object_req.height;
1228 g_object_set (requestor, "preferred-height", req.height, NULL);
1229 }
1230 if (req.width < 2)
1231 {
1232 req.width = object_req.width;
1233 g_object_set (requestor, "preferred-width", req.width, NULL);
1234 }
1235 if (req.height > 1)
1236 g_object_set (object, "preferred-height", req.height, NULL);
1237 if (req.width > 1)
1238 g_object_set (object, "preferred-width", req.width, NULL);
1239 break;
1240 default:
1241 {
1242 GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_ref (GDL_TYPE_DOCK_PLACEMENT));
1243 GEnumValue *enum_value = g_enum_get_value (enum_class, position);
1244 const gchar *name = enum_value ? enum_value->value_name : NULL;
1246 g_warning (_("Unsupported docking strategy %s in dock object of type %s"),
1247 name, G_OBJECT_TYPE_NAME (object));
1248 g_type_class_unref (enum_class);
1249 return;
1250 }
1251 }
1252 switch (position) {
1253 case GDL_DOCK_TOP:
1254 case GDL_DOCK_BOTTOM:
1255 /* get a paned style dock object */
1256 new_parent = g_object_new (gdl_dock_object_type_from_nick ("paned"),
1257 "orientation", GTK_ORIENTATION_VERTICAL,
1258 "preferred-width", object_req.width,
1259 "preferred-height", object_req.height,
1260 NULL);
1261 add_ourselves_first = (position == GDL_DOCK_BOTTOM);
1262 if (parent)
1263 available_space = parent_req.height;
1264 pref_size = req.height;
1265 break;
1266 case GDL_DOCK_LEFT:
1267 case GDL_DOCK_RIGHT:
1268 new_parent = g_object_new (gdl_dock_object_type_from_nick ("paned"),
1269 "orientation", GTK_ORIENTATION_HORIZONTAL,
1270 "preferred-width", object_req.width,
1271 "preferred-height", object_req.height,
1272 NULL);
1273 add_ourselves_first = (position == GDL_DOCK_RIGHT);
1274 if(parent)
1275 available_space = parent_req.width;
1276 pref_size = req.width;
1277 break;
1278 case GDL_DOCK_CENTER:
1279 new_parent = g_object_new (gdl_dock_object_type_from_nick ("notebook"),
1280 "preferred-width", object_req.width,
1281 "preferred-height", object_req.height,
1282 NULL);
1283 add_ourselves_first = TRUE;
1284 break;
1285 default:
1286 {
1287 GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_ref (GDL_TYPE_DOCK_PLACEMENT));
1288 GEnumValue *enum_value = g_enum_get_value (enum_class, position);
1289 const gchar *name = enum_value ? enum_value->value_name : NULL;
1291 g_warning (_("Unsupported docking strategy %s in dock object of type %s"),
1292 name, G_OBJECT_TYPE_NAME (object));
1293 g_type_class_unref (enum_class);
1294 return;
1295 }
1296 }
1298 /* freeze the parent so it doesn't reduce automatically */
1299 if (parent)
1300 gdl_dock_object_freeze (parent);
1302 /* ref ourselves since we could be destroyed when detached */
1303 g_object_ref (object);
1304 GDL_DOCK_OBJECT_SET_FLAGS (object, GDL_DOCK_IN_REFLOW);
1305 gdl_dock_object_detach (object, FALSE);
1307 /* freeze the new parent, so reduce won't get called before it's
1308 actually added to our parent */
1309 gdl_dock_object_freeze (new_parent);
1311 /* bind the new parent to our master, so the following adds work */
1312 gdl_dock_object_bind (new_parent, G_OBJECT (GDL_DOCK_OBJECT_GET_MASTER (object)));
1314 /* add the objects */
1315 if (add_ourselves_first) {
1316 gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (object));
1317 gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (requestor));
1318 splitpos = available_space - pref_size;
1319 } else {
1320 gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (requestor));
1321 gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (object));
1322 splitpos = pref_size;
1323 }
1325 /* add the new parent to the parent */
1326 if (parent)
1327 gtk_container_add (GTK_CONTAINER (parent), GTK_WIDGET (new_parent));
1329 /* show automatic object */
1330 if (GTK_WIDGET_VISIBLE (object))
1331 gtk_widget_show (GTK_WIDGET (new_parent));
1333 /* use extra docking parameter */
1334 if (position != GDL_DOCK_CENTER && other_data &&
1335 G_VALUE_HOLDS (other_data, G_TYPE_UINT)) {
1337 g_object_set (G_OBJECT (new_parent),
1338 "position", g_value_get_uint (other_data),
1339 NULL);
1340 } else if (splitpos > 0 && splitpos < available_space) {
1341 g_object_set (G_OBJECT (new_parent), "position", splitpos, NULL);
1342 }
1344 GDL_DOCK_OBJECT_UNSET_FLAGS (object, GDL_DOCK_IN_REFLOW);
1345 g_object_unref (object);
1347 gdl_dock_object_thaw (new_parent);
1348 if (parent)
1349 gdl_dock_object_thaw (parent);
1350 }
1352 static void
1353 gdl_dock_item_detach_menu (GtkWidget *widget,
1354 GtkMenu *menu)
1355 {
1356 GdlDockItem *item;
1358 item = GDL_DOCK_ITEM (widget);
1359 item->_priv->menu = NULL;
1360 }
1362 static void
1363 gdl_dock_item_popup_menu (GdlDockItem *item,
1364 guint button,
1365 guint32 time)
1366 {
1367 GtkWidget *mitem;
1369 if (!item->_priv->menu) {
1370 /* Create popup menu and attach it to the dock item */
1371 item->_priv->menu = gtk_menu_new ();
1372 gtk_menu_attach_to_widget (GTK_MENU (item->_priv->menu),
1373 GTK_WIDGET (item),
1374 gdl_dock_item_detach_menu);
1376 if (item->behavior & GDL_DOCK_ITEM_BEH_LOCKED) {
1377 /* UnLock menuitem */
1378 mitem = gtk_menu_item_new_with_label (_("UnLock"));
1379 gtk_menu_shell_append (GTK_MENU_SHELL (item->_priv->menu),
1380 mitem);
1381 g_signal_connect (mitem, "activate",
1382 G_CALLBACK (gdl_dock_item_unlock_cb), item);
1383 } else {
1384 /* Hide menuitem. */
1385 mitem = gtk_menu_item_new_with_label (_("Hide"));
1386 gtk_menu_shell_append (GTK_MENU_SHELL (item->_priv->menu), mitem);
1387 g_signal_connect (mitem, "activate",
1388 G_CALLBACK (gdl_dock_item_hide_cb), item);
1389 /* Lock menuitem */
1390 mitem = gtk_menu_item_new_with_label (_("Lock"));
1391 gtk_menu_shell_append (GTK_MENU_SHELL (item->_priv->menu), mitem);
1392 g_signal_connect (mitem, "activate",
1393 G_CALLBACK (gdl_dock_item_lock_cb), item);
1394 }
1395 }
1397 /* Show popup menu. */
1398 gtk_widget_show_all (item->_priv->menu);
1399 gtk_menu_popup (GTK_MENU (item->_priv->menu), NULL, NULL, NULL, NULL,
1400 button, time);
1401 }
1403 static void
1404 gdl_dock_item_drag_start (GdlDockItem *item)
1405 {
1406 GdkCursor *fleur;
1408 if (!GTK_WIDGET_REALIZED (item))
1409 gtk_widget_realize (GTK_WIDGET (item));
1411 GDL_DOCK_ITEM_SET_FLAGS (item, GDL_DOCK_IN_DRAG);
1413 /* grab the pointer so we receive all mouse events */
1414 fleur = gdk_cursor_new (GDK_FLEUR);
1416 /* grab the keyboard & pointer */
1417 gtk_grab_add (GTK_WIDGET (item));
1419 gdk_cursor_unref (fleur);
1421 g_signal_emit (item, gdl_dock_item_signals [DOCK_DRAG_BEGIN], 0);
1422 }
1424 static void
1425 gdl_dock_item_drag_end (GdlDockItem *item,
1426 gboolean cancel)
1427 {
1428 /* Release pointer & keyboard. */
1429 gtk_grab_remove (gtk_grab_get_current ());
1431 g_signal_emit (item, gdl_dock_item_signals [DOCK_DRAG_END], 0, cancel);
1433 GDL_DOCK_ITEM_UNSET_FLAGS (item, GDL_DOCK_IN_DRAG);
1434 }
1436 static void
1437 gdl_dock_item_tab_button (GtkWidget *widget,
1438 GdkEventButton *event,
1439 gpointer data)
1440 {
1441 GdlDockItem *item;
1443 item = GDL_DOCK_ITEM (data);
1445 if (!GDL_DOCK_ITEM_NOT_LOCKED (item))
1446 return;
1448 switch (event->button) {
1449 case 1:
1450 /* set dragoff_{x,y} as we the user clicked on the middle of the
1451 drag handle */
1452 switch (item->orientation) {
1453 case GTK_ORIENTATION_HORIZONTAL:
1454 /*item->dragoff_x = item->_priv->grip_size / 2;*/
1455 item->dragoff_y = GTK_WIDGET (data)->allocation.height / 2;
1456 break;
1457 case GTK_ORIENTATION_VERTICAL:
1458 /*item->dragoff_x = GTK_WIDGET (data)->allocation.width / 2;*/
1459 item->dragoff_y = item->_priv->grip_size / 2;
1460 break;
1461 };
1462 gdl_dock_item_drag_start (item);
1463 break;
1465 case 3:
1466 gdl_dock_item_popup_menu (item, event->button, event->time);
1467 break;
1469 default:
1470 break;
1471 };
1472 }
1474 static void
1475 gdl_dock_item_hide_cb (GtkWidget *widget,
1476 GdlDockItem *item)
1477 {
1478 GdlDockMaster *master;
1480 g_return_if_fail (item != NULL);
1482 master = GDL_DOCK_OBJECT_GET_MASTER (item);
1483 gdl_dock_item_hide_item (item);
1484 }
1486 static void
1487 gdl_dock_item_lock_cb (GtkWidget *widget,
1488 GdlDockItem *item)
1489 {
1490 g_return_if_fail (item != NULL);
1492 gdl_dock_item_lock (item);
1493 }
1495 static void
1496 gdl_dock_item_unlock_cb (GtkWidget *widget,
1497 GdlDockItem *item)
1498 {
1499 g_return_if_fail (item != NULL);
1501 gdl_dock_item_unlock (item);
1502 }
1504 static void
1505 gdl_dock_item_showhide_grip (GdlDockItem *item)
1506 {
1507 GdkDisplay *display;
1508 GdkCursor *cursor;
1510 gdl_dock_item_detach_menu (GTK_WIDGET (item), NULL);
1511 display = gtk_widget_get_display (GTK_WIDGET (item));
1512 cursor = NULL;
1514 if (item->_priv->grip) {
1515 if (GDL_DOCK_ITEM_GRIP_SHOWN (item) &&
1516 GDL_DOCK_ITEM_NOT_LOCKED(item))
1517 cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
1518 }
1519 if (item->_priv->grip && GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window)
1520 gdk_window_set_cursor (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window, cursor);
1522 if (cursor)
1523 gdk_cursor_unref (cursor);
1525 gtk_widget_queue_resize (GTK_WIDGET (item));
1526 }
1528 static void
1529 gdl_dock_item_real_set_orientation (GdlDockItem *item,
1530 GtkOrientation orientation)
1531 {
1532 item->orientation = orientation;
1534 if (GTK_WIDGET_DRAWABLE (item))
1535 gtk_widget_queue_draw (GTK_WIDGET (item));
1536 gtk_widget_queue_resize (GTK_WIDGET (item));
1537 }
1540 /* ----- Public interface ----- */
1542 GtkWidget *
1543 gdl_dock_item_new (const gchar *name,
1544 const gchar *long_name,
1545 GdlDockItemBehavior behavior)
1546 {
1547 GdlDockItem *item;
1549 item = GDL_DOCK_ITEM (g_object_new (GDL_TYPE_DOCK_ITEM,
1550 "name", name,
1551 "long-name", long_name,
1552 "behavior", behavior,
1553 NULL));
1554 GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_AUTOMATIC);
1555 gdl_dock_item_set_tablabel (item, gtk_label_new (long_name));
1556 return GTK_WIDGET (item);
1557 }
1559 GtkWidget *
1560 gdl_dock_item_new_with_stock (const gchar *name,
1561 const gchar *long_name,
1562 const gchar *stock_id,
1563 GdlDockItemBehavior behavior)
1564 {
1565 GdlDockItem *item;
1567 item = GDL_DOCK_ITEM (g_object_new (GDL_TYPE_DOCK_ITEM,
1568 "name", name,
1569 "long-name", long_name,
1570 "stock-id", stock_id,
1571 "behavior", behavior,
1572 NULL));
1573 GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_AUTOMATIC);
1574 gdl_dock_item_set_tablabel (item, gtk_label_new (long_name));
1576 return GTK_WIDGET (item);
1577 }
1579 GtkWidget *
1580 gdl_dock_item_new_with_pixbuf_icon (const gchar *name,
1581 const gchar *long_name,
1582 const GdkPixbuf *pixbuf_icon,
1583 GdlDockItemBehavior behavior)
1584 {
1585 GdlDockItem *item;
1587 item = GDL_DOCK_ITEM (g_object_new (GDL_TYPE_DOCK_ITEM,
1588 "name", name,
1589 "long-name", long_name,
1590 "pixbuf-icon", pixbuf_icon,
1591 "behavior", behavior,
1592 NULL));
1594 GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_AUTOMATIC);
1595 gdl_dock_item_set_tablabel (item, gtk_label_new (long_name));
1597 return GTK_WIDGET (item);
1598 }
1600 /* convenient function (and to preserve source compat) */
1601 void
1602 gdl_dock_item_dock_to (GdlDockItem *item,
1603 GdlDockItem *target,
1604 GdlDockPlacement position,
1605 gint docking_param)
1606 {
1607 g_return_if_fail (item != NULL);
1608 g_return_if_fail (item != target);
1609 g_return_if_fail (target != NULL || position == GDL_DOCK_FLOATING);
1610 g_return_if_fail ((item->behavior & GDL_DOCK_ITEM_BEH_NEVER_FLOATING) == 0 || position != GDL_DOCK_FLOATING);
1612 if (position == GDL_DOCK_FLOATING || !target) {
1613 GdlDockObject *controller;
1615 if (!gdl_dock_object_is_bound (GDL_DOCK_OBJECT (item))) {
1616 g_warning (_("Attempt to bind an unbound item %p"), item);
1617 return;
1618 }
1620 controller = gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (item));
1622 /* FIXME: save previous docking position for later
1623 re-docking... does this make sense now? */
1625 /* Create new floating dock for widget. */
1626 item->dragoff_x = item->dragoff_y = 0;
1627 gdl_dock_add_floating_item (GDL_DOCK (controller),
1628 item, 0, 0, -1, -1);
1630 } else
1631 gdl_dock_object_dock (GDL_DOCK_OBJECT (target),
1632 GDL_DOCK_OBJECT (item),
1633 position, NULL);
1634 }
1636 void
1637 gdl_dock_item_set_orientation (GdlDockItem *item,
1638 GtkOrientation orientation)
1639 {
1640 GParamSpec *pspec;
1642 g_return_if_fail (item != NULL);
1644 if (item->orientation != orientation) {
1645 /* push the property down the hierarchy if our child supports it */
1646 if (item->child != NULL) {
1647 pspec = g_object_class_find_property (
1648 G_OBJECT_GET_CLASS (item->child), "orientation");
1649 if (pspec && pspec->value_type == GTK_TYPE_ORIENTATION)
1650 g_object_set (G_OBJECT (item->child),
1651 "orientation", orientation,
1652 NULL);
1653 };
1655 GDL_CALL_VIRTUAL (item, GDL_DOCK_ITEM_GET_CLASS, set_orientation, (item, orientation));
1656 g_object_notify (G_OBJECT (item), "orientation");
1657 }
1658 }
1660 GtkWidget *
1661 gdl_dock_item_get_tablabel (GdlDockItem *item)
1662 {
1663 g_return_val_if_fail (item != NULL, NULL);
1664 g_return_val_if_fail (GDL_IS_DOCK_ITEM (item), NULL);
1666 return item->_priv->tab_label;
1667 }
1669 void
1670 gdl_dock_item_set_tablabel (GdlDockItem *item,
1671 GtkWidget *tablabel)
1672 {
1673 g_return_if_fail (item != NULL);
1675 if (item->_priv->tab_label) {
1676 /* disconnect and unref the previous tablabel */
1677 if (GDL_IS_DOCK_TABLABEL (item->_priv->tab_label)) {
1678 g_signal_handlers_disconnect_matched (item->_priv->tab_label,
1679 G_SIGNAL_MATCH_DATA,
1680 0, 0, NULL,
1681 NULL, item);
1682 g_object_set (item->_priv->tab_label, "item", NULL, NULL);
1683 }
1684 gtk_widget_unref (item->_priv->tab_label);
1685 item->_priv->tab_label = NULL;
1686 }
1688 if (tablabel) {
1689 gtk_widget_ref (tablabel);
1690 gtk_object_sink (GTK_OBJECT (tablabel));
1691 item->_priv->tab_label = tablabel;
1692 if (GDL_IS_DOCK_TABLABEL (tablabel)) {
1693 g_object_set (tablabel, "item", item, NULL);
1694 /* connect to tablabel signal */
1695 g_signal_connect (tablabel, "button_pressed_handle",
1696 G_CALLBACK (gdl_dock_item_tab_button), item);
1697 }
1698 }
1699 }
1701 void
1702 gdl_dock_item_hide_grip (GdlDockItem *item)
1703 {
1704 g_return_if_fail (item != NULL);
1705 if (item->_priv->grip_shown) {
1706 item->_priv->grip_shown = FALSE;
1707 gdl_dock_item_showhide_grip (item);
1708 };
1709 g_warning ("Grips always show unless GDL_DOCK_ITEM_BEH_NO_GRIP is set\n" );
1710 }
1712 void
1713 gdl_dock_item_show_grip (GdlDockItem *item)
1714 {
1715 g_return_if_fail (item != NULL);
1716 if (!item->_priv->grip_shown) {
1717 item->_priv->grip_shown = TRUE;
1718 gdl_dock_item_showhide_grip (item);
1719 };
1720 }
1722 /* convenient function (and to preserve source compat) */
1723 void
1724 gdl_dock_item_bind (GdlDockItem *item,
1725 GtkWidget *dock)
1726 {
1727 g_return_if_fail (item != NULL);
1728 g_return_if_fail (dock == NULL || GDL_IS_DOCK (dock));
1730 gdl_dock_object_bind (GDL_DOCK_OBJECT (item),
1731 G_OBJECT (GDL_DOCK_OBJECT_GET_MASTER (dock)));
1732 }
1734 /* convenient function (and to preserve source compat) */
1735 void
1736 gdl_dock_item_unbind (GdlDockItem *item)
1737 {
1738 g_return_if_fail (item != NULL);
1740 gdl_dock_object_unbind (GDL_DOCK_OBJECT (item));
1741 }
1743 void
1744 gdl_dock_item_hide_item (GdlDockItem *item)
1745 {
1746 g_return_if_fail (item != NULL);
1748 if (!GDL_DOCK_OBJECT_ATTACHED (item))
1749 /* already hidden/detached */
1750 return;
1752 /* if the object is manual, create a new placeholder to be able to
1753 restore the position later */
1754 if (!GDL_DOCK_OBJECT_AUTOMATIC (item)) {
1755 if (item->_priv->ph)
1756 g_object_unref (item->_priv->ph);
1758 gboolean isFloating = FALSE;
1759 gint width=0, height=0, x=0, y = 0;
1761 if (GDL_IS_DOCK (gdl_dock_object_get_parent_object (GDL_DOCK_OBJECT (item))))
1762 {
1763 GdlDock* dock = GDL_DOCK (gdl_dock_object_get_parent_object (GDL_DOCK_OBJECT (item)));
1764 g_object_get (dock,
1765 "floating", &isFloating,
1766 "width", &width,
1767 "height",&height,
1768 "floatx",&x,
1769 "floaty",&y,
1770 NULL);
1771 } else {
1772 item->_priv->preferred_width=GTK_WIDGET (item)->allocation.width;
1773 item->_priv->preferred_height=GTK_WIDGET (item)->allocation.height;
1774 }
1775 item->_priv->ph = GDL_DOCK_PLACEHOLDER (
1776 g_object_new (GDL_TYPE_DOCK_PLACEHOLDER,
1777 "sticky", FALSE,
1778 "host", item,
1779 "width", width,
1780 "height", height,
1781 "floating", isFloating,
1782 "floatx", x,
1783 "floaty", y,
1784 NULL));
1785 g_object_ref (item->_priv->ph);
1786 gtk_object_sink (GTK_OBJECT (item->_priv->ph));
1787 }
1789 gdl_dock_object_freeze (GDL_DOCK_OBJECT (item));
1791 /* hide our children first, so they can also set placeholders */
1792 if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT (item)))
1793 gtk_container_foreach (GTK_CONTAINER (item),
1794 (GtkCallback) gdl_dock_item_hide_item,
1795 NULL);
1797 /* detach the item recursively */
1798 gdl_dock_object_detach (GDL_DOCK_OBJECT (item), TRUE);
1800 gtk_widget_hide (GTK_WIDGET (item));
1802 gdl_dock_object_thaw (GDL_DOCK_OBJECT (item));
1803 }
1805 void
1806 gdl_dock_item_iconify_item (GdlDockItem *item)
1807 {
1808 g_return_if_fail (item != NULL);
1810 GDL_DOCK_OBJECT_SET_FLAGS (item, GDL_DOCK_ICONIFIED);
1811 gdl_dock_item_hide_item (item);
1812 }
1814 void
1815 gdl_dock_item_show_item (GdlDockItem *item)
1816 {
1817 g_return_if_fail (item != NULL);
1819 GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_ICONIFIED);
1821 if (item->_priv->ph) {
1822 gboolean isFloating=FALSE;
1823 gint width = 0, height = 0, x= 0, y = 0;
1824 g_object_get (G_OBJECT(item->_priv->ph),
1825 "width", &width,
1826 "height", &height,
1827 "floating",&isFloating,
1828 "floatx", &x,
1829 "floaty", &y,
1830 NULL);
1831 if (isFloating) {
1832 GdlDockObject *controller =
1833 gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (item));
1834 gdl_dock_add_floating_item (GDL_DOCK (controller),
1835 item, x, y, width, height);
1836 } else {
1837 gtk_container_add (GTK_CONTAINER (item->_priv->ph),
1838 GTK_WIDGET (item));
1839 }
1840 g_object_unref (item->_priv->ph);
1841 item->_priv->ph = NULL;
1843 } else if (gdl_dock_object_is_bound (GDL_DOCK_OBJECT (item))) {
1844 GdlDockObject *toplevel;
1846 toplevel = gdl_dock_master_get_controller
1847 (GDL_DOCK_OBJECT_GET_MASTER (item));
1849 if (item->behavior & GDL_DOCK_ITEM_BEH_NEVER_FLOATING) {
1850 g_warning("Object %s has no default position and flag GDL_DOCK_ITEM_BEH_NEVER_FLOATING is set.\n",
1851 GDL_DOCK_OBJECT(item)->name);
1852 } else if (toplevel) {
1853 gdl_dock_object_dock (toplevel, GDL_DOCK_OBJECT (item),
1854 GDL_DOCK_FLOATING, NULL);
1855 } else
1856 g_warning("There is no toplevel window. GdlDockItem %s cannot be shown.\n", GDL_DOCK_OBJECT(item)->name);
1858 } else
1859 g_warning("GdlDockItem %s is not bound. It cannot be shown.\n",
1860 GDL_DOCK_OBJECT(item)->name);
1862 gtk_widget_show (GTK_WIDGET (item));
1863 }
1865 void
1866 gdl_dock_item_lock (GdlDockItem *item)
1867 {
1868 g_object_set (item, "locked", TRUE, NULL);
1869 }
1871 void
1872 gdl_dock_item_unlock (GdlDockItem *item)
1873 {
1874 g_object_set (item, "locked", FALSE, NULL);
1875 }
1877 void
1878 gdl_dock_item_set_default_position (GdlDockItem *item,
1879 GdlDockObject *reference)
1880 {
1881 g_return_if_fail (item != NULL);
1883 if (item->_priv->ph) {
1884 g_object_unref (item->_priv->ph);
1885 item->_priv->ph = NULL;
1886 }
1888 if (reference && GDL_DOCK_OBJECT_ATTACHED (reference)) {
1889 if (GDL_IS_DOCK_PLACEHOLDER (reference)) {
1890 g_object_ref (reference);
1891 gtk_object_sink (GTK_OBJECT (reference));
1892 item->_priv->ph = GDL_DOCK_PLACEHOLDER (reference);
1893 } else {
1894 item->_priv->ph = GDL_DOCK_PLACEHOLDER (
1895 g_object_new (GDL_TYPE_DOCK_PLACEHOLDER,
1896 "sticky", TRUE,
1897 "host", reference,
1898 NULL));
1899 g_object_ref (item->_priv->ph);
1900 gtk_object_sink (GTK_OBJECT (item->_priv->ph));
1901 }
1902 }
1903 }
1905 void
1906 gdl_dock_item_preferred_size (GdlDockItem *item,
1907 GtkRequisition *req)
1908 {
1909 if (!req)
1910 return;
1912 req->width = MAX (item->_priv->preferred_width,
1913 GTK_WIDGET (item)->allocation.width);
1914 req->height = MAX (item->_priv->preferred_height,
1915 GTK_WIDGET (item)->allocation.height);
1916 }
1919 /* ----- gtk orientation type exporter/importer ----- */
1921 static void
1922 gdl_dock_param_export_gtk_orientation (const GValue *src,
1923 GValue *dst)
1924 {
1925 dst->data [0].v_pointer =
1926 g_strdup_printf ("%s", (src->data [0].v_int == GTK_ORIENTATION_HORIZONTAL) ?
1927 "horizontal" : "vertical");
1928 }
1930 static void
1931 gdl_dock_param_import_gtk_orientation (const GValue *src,
1932 GValue *dst)
1933 {
1934 if (!strcmp (src->data [0].v_pointer, "horizontal"))
1935 dst->data [0].v_int = GTK_ORIENTATION_HORIZONTAL;
1936 else
1937 dst->data [0].v_int = GTK_ORIENTATION_VERTICAL;
1938 }