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);
358 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (item), GTK_CAN_FOCUS);
360 item->child = NULL;
362 item->orientation = GTK_ORIENTATION_VERTICAL;
363 item->behavior = GDL_DOCK_ITEM_BEH_NORMAL;
365 item->resize = TRUE;
367 item->dragoff_x = item->dragoff_y = 0;
369 item->_priv = g_new0 (GdlDockItemPrivate, 1);
370 item->_priv->menu = NULL;
372 item->_priv->preferred_width = item->_priv->preferred_height = -1;
373 item->_priv->tab_label = NULL;
375 item->_priv->ph = NULL;
376 }
378 static GObject *
379 gdl_dock_item_constructor (GType type,
380 guint n_construct_properties,
381 GObjectConstructParam *construct_param)
382 {
383 GObject *g_object;
385 g_object = GDL_CALL_PARENT_WITH_DEFAULT (G_OBJECT_CLASS,
386 constructor,
387 (type,
388 n_construct_properties,
389 construct_param),
390 NULL);
391 if (g_object) {
392 GdlDockItem *item = GDL_DOCK_ITEM (g_object);
394 if (GDL_DOCK_ITEM_HAS_GRIP (item)) {
395 item->_priv->grip_shown = TRUE;
396 item->_priv->grip = gdl_dock_item_grip_new (item);
397 gtk_widget_set_parent (item->_priv->grip, GTK_WIDGET (item));
398 gtk_widget_show (item->_priv->grip);
399 }
400 else {
401 item->_priv->grip_shown = FALSE;
402 }
403 }
405 return g_object;
406 }
408 static void
409 gdl_dock_item_set_property (GObject *g_object,
410 guint prop_id,
411 const GValue *value,
412 GParamSpec *pspec)
413 {
414 GdlDockItem *item = GDL_DOCK_ITEM (g_object);
416 switch (prop_id) {
417 case PROP_ORIENTATION:
418 gdl_dock_item_set_orientation (item, g_value_get_enum (value));
419 break;
420 case PROP_RESIZE:
421 item->resize = g_value_get_boolean (value);
422 gtk_widget_queue_resize (GTK_WIDGET (item));
423 break;
424 case PROP_BEHAVIOR:
425 {
426 GdlDockItemBehavior old_beh = item->behavior;
427 item->behavior = g_value_get_flags (value);
429 if ((old_beh ^ item->behavior) & GDL_DOCK_ITEM_BEH_LOCKED) {
430 if (GDL_DOCK_OBJECT_GET_MASTER (item))
431 g_signal_emit_by_name (GDL_DOCK_OBJECT_GET_MASTER (item),
432 "layout-changed");
433 g_object_notify (g_object, "locked");
434 gdl_dock_item_showhide_grip (item);
435 }
437 break;
438 }
439 case PROP_LOCKED:
440 {
441 GdlDockItemBehavior old_beh = item->behavior;
443 if (g_value_get_boolean (value))
444 item->behavior |= GDL_DOCK_ITEM_BEH_LOCKED;
445 else
446 item->behavior &= ~GDL_DOCK_ITEM_BEH_LOCKED;
448 if (old_beh ^ item->behavior) {
449 gdl_dock_item_showhide_grip (item);
450 g_object_notify (g_object, "behavior");
452 if (GDL_DOCK_OBJECT_GET_MASTER (item))
453 g_signal_emit_by_name (GDL_DOCK_OBJECT_GET_MASTER (item),
454 "layout-changed");
455 }
456 break;
457 }
458 case PROP_PREFERRED_WIDTH:
459 item->_priv->preferred_width = g_value_get_int (value);
460 break;
461 case PROP_PREFERRED_HEIGHT:
462 item->_priv->preferred_height = g_value_get_int (value);
463 break;
464 default:
465 G_OBJECT_WARN_INVALID_PROPERTY_ID (g_object, prop_id, pspec);
466 break;
467 }
468 }
470 static void
471 gdl_dock_item_get_property (GObject *g_object,
472 guint prop_id,
473 GValue *value,
474 GParamSpec *pspec)
475 {
476 GdlDockItem *item = GDL_DOCK_ITEM (g_object);
478 switch (prop_id) {
479 case PROP_ORIENTATION:
480 g_value_set_enum (value, item->orientation);
481 break;
482 case PROP_RESIZE:
483 g_value_set_boolean (value, item->resize);
484 break;
485 case PROP_BEHAVIOR:
486 g_value_set_flags (value, item->behavior);
487 break;
488 case PROP_LOCKED:
489 g_value_set_boolean (value, !GDL_DOCK_ITEM_NOT_LOCKED (item));
490 break;
491 case PROP_PREFERRED_WIDTH:
492 g_value_set_int (value, item->_priv->preferred_width);
493 break;
494 case PROP_PREFERRED_HEIGHT:
495 g_value_set_int (value, item->_priv->preferred_height);
496 break;
497 default:
498 G_OBJECT_WARN_INVALID_PROPERTY_ID (g_object, prop_id, pspec);
499 break;
500 }
501 }
503 static void
504 gdl_dock_item_destroy (GtkObject *object)
505 {
506 GdlDockItem *item = GDL_DOCK_ITEM (object);
508 if (item->_priv) {
509 GdlDockItemPrivate *priv = item->_priv;
511 if (priv->tab_label) {
512 gdl_dock_item_set_tablabel (item, NULL);
513 };
514 if (priv->menu) {
515 gtk_menu_detach (GTK_MENU (priv->menu));
516 priv->menu = NULL;
517 };
518 if (priv->grip) {
519 gtk_container_remove (GTK_CONTAINER (item), priv->grip);
520 priv->grip = NULL;
521 }
522 if (priv->ph) {
523 g_object_unref (priv->ph);
524 priv->ph = NULL;
525 }
527 item->_priv = NULL;
528 g_free (priv);
529 }
531 GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
532 }
534 static void
535 gdl_dock_item_add (GtkContainer *container,
536 GtkWidget *widget)
537 {
538 GdlDockItem *item;
540 g_return_if_fail (GDL_IS_DOCK_ITEM (container));
542 item = GDL_DOCK_ITEM (container);
543 if (GDL_IS_DOCK_OBJECT (widget)) {
544 g_warning (_("You can't add a dock object (%p of type %s) inside a %s. "
545 "Use a GdlDock or some other compound dock object."),
546 widget, G_OBJECT_TYPE_NAME (widget), G_OBJECT_TYPE_NAME (item));
547 return;
548 }
550 if (item->child != NULL) {
551 g_warning (_("Attempting to add a widget with type %s to a %s, "
552 "but it can only contain one widget at a time; "
553 "it already contains a widget of type %s"),
554 G_OBJECT_TYPE_NAME (widget),
555 G_OBJECT_TYPE_NAME (item),
556 G_OBJECT_TYPE_NAME (item->child));
557 return;
558 }
560 gtk_widget_set_parent (widget, GTK_WIDGET (item));
561 item->child = widget;
562 }
564 static void
565 gdl_dock_item_remove (GtkContainer *container,
566 GtkWidget *widget)
567 {
568 GdlDockItem *item;
569 gboolean was_visible;
571 g_return_if_fail (GDL_IS_DOCK_ITEM (container));
573 item = GDL_DOCK_ITEM (container);
574 if (item->_priv && widget == item->_priv->grip) {
575 gboolean grip_was_visible = GTK_WIDGET_VISIBLE (widget);
576 gtk_widget_unparent (widget);
577 item->_priv->grip = NULL;
578 if (grip_was_visible)
579 gtk_widget_queue_resize (GTK_WIDGET (item));
580 return;
581 }
583 if (GDL_DOCK_ITEM_IN_DRAG (item)) {
584 gdl_dock_item_drag_end (item, TRUE);
585 }
587 g_return_if_fail (item->child == widget);
589 was_visible = GTK_WIDGET_VISIBLE (widget);
591 gtk_widget_unparent (widget);
592 item->child = NULL;
594 if (was_visible)
595 gtk_widget_queue_resize (GTK_WIDGET (container));
596 }
598 static void
599 gdl_dock_item_forall (GtkContainer *container,
600 gboolean include_internals,
601 GtkCallback callback,
602 gpointer callback_data)
603 {
604 GdlDockItem *item = (GdlDockItem *) container;
606 g_return_if_fail (callback != NULL);
608 if (include_internals && item->_priv->grip)
609 (* callback) (item->_priv->grip, callback_data);
611 if (item->child)
612 (* callback) (item->child, callback_data);
613 }
615 static GtkType
616 gdl_dock_item_child_type (GtkContainer *container)
617 {
618 g_return_val_if_fail (GDL_IS_DOCK_ITEM (container), G_TYPE_NONE);
620 if (!GDL_DOCK_ITEM (container)->child)
621 return GTK_TYPE_WIDGET;
622 else
623 return G_TYPE_NONE;
624 }
626 static void
627 gdl_dock_item_size_request (GtkWidget *widget,
628 GtkRequisition *requisition)
629 {
630 GtkRequisition child_requisition;
631 GtkRequisition grip_requisition;
632 GdlDockItem *item;
634 g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
635 g_return_if_fail (requisition != NULL);
637 item = GDL_DOCK_ITEM (widget);
639 /* If our child is not visible, we still request its size, since
640 we won't have any useful hint for our size otherwise. */
641 if (item->child)
642 gtk_widget_size_request (item->child, &child_requisition);
643 else {
644 child_requisition.width = 0;
645 child_requisition.height = 0;
646 }
648 if (item->orientation == GTK_ORIENTATION_HORIZONTAL) {
649 if (GDL_DOCK_ITEM_GRIP_SHOWN (item)) {
650 gtk_widget_size_request (item->_priv->grip, &grip_requisition);
651 requisition->width = grip_requisition.width;
652 } else {
653 requisition->width = 0;
654 }
656 if (item->child) {
657 requisition->width += child_requisition.width;
658 requisition->height = child_requisition.height;
659 } else
660 requisition->height = 0;
661 } else {
662 if (GDL_DOCK_ITEM_GRIP_SHOWN (item)) {
663 gtk_widget_size_request (item->_priv->grip, &grip_requisition);
664 requisition->height = grip_requisition.height;
665 } else {
666 requisition->height = 0;
667 }
669 if (item->child) {
670 requisition->width = child_requisition.width;
671 requisition->height += child_requisition.height;
672 } else
673 requisition->width = 0;
674 }
676 requisition->width += (GTK_CONTAINER (widget)->border_width + widget->style->xthickness) * 2;
677 requisition->height += (GTK_CONTAINER (widget)->border_width + widget->style->ythickness) * 2;
679 widget->requisition = *requisition;
680 }
682 static void
683 gdl_dock_item_size_allocate (GtkWidget *widget,
684 GtkAllocation *allocation)
685 {
686 GdlDockItem *item;
688 g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
689 g_return_if_fail (allocation != NULL);
691 item = GDL_DOCK_ITEM (widget);
693 widget->allocation = *allocation;
695 /* Once size is allocated, preferred size is no longer necessary */
696 item->_priv->preferred_height = -1;
697 item->_priv->preferred_width = -1;
699 if (GTK_WIDGET_REALIZED (widget))
700 gdk_window_move_resize (widget->window,
701 widget->allocation.x,
702 widget->allocation.y,
703 widget->allocation.width,
704 widget->allocation.height);
706 if (item->child && GTK_WIDGET_VISIBLE (item->child)) {
707 GtkAllocation child_allocation;
708 int border_width;
710 border_width = GTK_CONTAINER (widget)->border_width;
712 child_allocation.x = border_width + widget->style->xthickness;
713 child_allocation.y = border_width + widget->style->ythickness;
714 child_allocation.width = allocation->width
715 - 2 * (border_width + widget->style->xthickness);
716 child_allocation.height = allocation->height
717 - 2 * (border_width + widget->style->ythickness);
719 if (GDL_DOCK_ITEM_GRIP_SHOWN (item)) {
720 GtkAllocation grip_alloc = child_allocation;
721 GtkRequisition grip_req;
723 gtk_widget_size_request (item->_priv->grip, &grip_req);
725 if (item->orientation == GTK_ORIENTATION_HORIZONTAL) {
726 child_allocation.x += grip_req.width;
727 child_allocation.width -= grip_req.width;
728 grip_alloc.width = grip_req.width;
729 } else {
730 child_allocation.y += grip_req.height;
731 child_allocation.height -= grip_req.height;
732 grip_alloc.height = grip_req.height;
733 }
734 if (item->_priv->grip)
735 gtk_widget_size_allocate (item->_priv->grip, &grip_alloc);
736 }
737 /* Allocation can't be negative */
738 if (child_allocation.width < 0)
739 child_allocation.width = 0;
740 if (child_allocation.height < 0)
741 child_allocation.height = 0;
742 gtk_widget_size_allocate (item->child, &child_allocation);
743 }
744 }
746 static void
747 gdl_dock_item_map (GtkWidget *widget)
748 {
749 GdlDockItem *item;
751 g_return_if_fail (widget != NULL);
752 g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
754 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
756 item = GDL_DOCK_ITEM (widget);
758 gdk_window_show (widget->window);
760 if (item->child
761 && GTK_WIDGET_VISIBLE (item->child)
762 && !GTK_WIDGET_MAPPED (item->child))
763 gtk_widget_map (item->child);
765 if (item->_priv->grip
766 && GTK_WIDGET_VISIBLE (item->_priv->grip)
767 && !GTK_WIDGET_MAPPED (item->_priv->grip))
768 gtk_widget_map (item->_priv->grip);
769 }
771 static void
772 gdl_dock_item_unmap (GtkWidget *widget)
773 {
774 GdlDockItem *item;
776 g_return_if_fail (widget != NULL);
777 g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
779 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
781 item = GDL_DOCK_ITEM (widget);
783 gdk_window_hide (widget->window);
785 if (item->_priv->grip)
786 gtk_widget_unmap (item->_priv->grip);
787 }
789 static void
790 gdl_dock_item_realize (GtkWidget *widget)
791 {
792 GdkWindowAttr attributes;
793 gint attributes_mask;
794 GdlDockItem *item;
796 g_return_if_fail (widget != NULL);
797 g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
799 item = GDL_DOCK_ITEM (widget);
801 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
803 /* widget window */
804 attributes.x = widget->allocation.x;
805 attributes.y = widget->allocation.y;
806 attributes.width = widget->allocation.width;
807 attributes.height = widget->allocation.height;
808 attributes.window_type = GDK_WINDOW_CHILD;
809 attributes.wclass = GDK_INPUT_OUTPUT;
810 attributes.visual = gtk_widget_get_visual (widget);
811 attributes.colormap = gtk_widget_get_colormap (widget);
812 attributes.event_mask = (gtk_widget_get_events (widget) |
813 GDK_EXPOSURE_MASK |
814 GDK_BUTTON1_MOTION_MASK |
815 GDK_BUTTON_PRESS_MASK |
816 GDK_BUTTON_RELEASE_MASK);
817 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
818 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
819 &attributes, attributes_mask);
820 gdk_window_set_user_data (widget->window, widget);
822 widget->style = gtk_style_attach (widget->style, widget->window);
823 gtk_style_set_background (widget->style, widget->window,
824 GTK_WIDGET_STATE (item));
825 gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
827 if (item->child)
828 gtk_widget_set_parent_window (item->child, widget->window);
830 if (item->_priv->grip)
831 gtk_widget_set_parent_window (item->_priv->grip, widget->window);
832 }
834 static void
835 gdl_dock_item_style_set (GtkWidget *widget,
836 GtkStyle *previous_style)
837 {
838 g_return_if_fail (widget != NULL);
839 g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
841 if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_NO_WINDOW (widget)) {
842 gtk_style_set_background (widget->style, widget->window,
843 widget->state);
844 if (GTK_WIDGET_DRAWABLE (widget))
845 gdk_window_clear (widget->window);
846 }
847 }
849 static void
850 gdl_dock_item_paint (GtkWidget *widget,
851 GdkEventExpose *event)
852 {
853 GdlDockItem *item;
855 item = GDL_DOCK_ITEM (widget);
857 gtk_paint_box (widget->style,
858 widget->window,
859 GTK_WIDGET_STATE (widget),
860 GTK_SHADOW_NONE,
861 &event->area, widget,
862 "dockitem",
863 0, 0, -1, -1);
864 }
866 static gint
867 gdl_dock_item_expose (GtkWidget *widget,
868 GdkEventExpose *event)
869 {
870 g_return_val_if_fail (widget != NULL, FALSE);
871 g_return_val_if_fail (GDL_IS_DOCK_ITEM (widget), FALSE);
872 g_return_val_if_fail (event != NULL, FALSE);
874 if (GTK_WIDGET_DRAWABLE (widget) && event->window == widget->window) {
875 gdl_dock_item_paint (widget, event);
876 GDL_CALL_PARENT_GBOOLEAN(GTK_WIDGET_CLASS, expose_event, (widget,event));
877 }
879 return FALSE;
880 }
882 #define EVENT_IN_GRIP_EVENT_WINDOW(ev,gr) \
883 ((gr) != NULL && (ev)->window == GDL_DOCK_ITEM_GRIP (gr)->title_window)
885 #define EVENT_IN_TABLABEL_EVENT_WINDOW(ev,tl) \
886 ((tl) != NULL && (ev)->window == GDL_DOCK_TABLABEL (tl)->event_window)
888 static gint
889 gdl_dock_item_button_changed (GtkWidget *widget,
890 GdkEventButton *event)
891 {
892 GdlDockItem *item;
893 gboolean locked;
894 gboolean event_handled;
895 gboolean in_handle;
896 GdkCursor *cursor;
898 g_return_val_if_fail (widget != NULL, FALSE);
899 g_return_val_if_fail (GDL_IS_DOCK_ITEM (widget), FALSE);
900 g_return_val_if_fail (event != NULL, FALSE);
902 item = GDL_DOCK_ITEM (widget);
904 if (!EVENT_IN_GRIP_EVENT_WINDOW (event, item->_priv->grip))
905 return FALSE;
907 locked = !GDL_DOCK_ITEM_NOT_LOCKED (item);
909 event_handled = FALSE;
911 /* Check if user clicked on the drag handle. */
912 switch (item->orientation) {
913 case GTK_ORIENTATION_HORIZONTAL:
914 in_handle = event->x < item->_priv->grip->allocation.width;
915 break;
916 case GTK_ORIENTATION_VERTICAL:
917 in_handle = event->y < item->_priv->grip->allocation.height;
918 break;
919 default:
920 in_handle = FALSE;
921 break;
922 }
924 /* Left mousebutton click on dockitem. */
925 if (!locked && event->button == 1 && event->type == GDK_BUTTON_PRESS) {
926 /* Set in_drag flag, grab pointer and call begin drag operation. */
927 if (in_handle) {
928 item->_priv->start_x = event->x;
929 item->_priv->start_y = event->y;
931 GDL_DOCK_ITEM_SET_FLAGS (item, GDL_DOCK_IN_PREDRAG);
933 cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
934 GDK_FLEUR);
935 gdk_window_set_cursor (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window,
936 cursor);
937 gdk_cursor_unref (cursor);
939 event_handled = TRUE;
940 };
942 } else if (!locked &&event->type == GDK_BUTTON_RELEASE && event->button == 1) {
943 if (GDL_DOCK_ITEM_IN_DRAG (item)) {
944 /* User dropped widget somewhere. */
945 gdl_dock_item_drag_end (item, FALSE);
946 event_handled = TRUE;
947 }
948 else if (GDL_DOCK_ITEM_IN_PREDRAG (item)) {
949 GDL_DOCK_ITEM_UNSET_FLAGS (item, GDL_DOCK_IN_PREDRAG);
950 event_handled = TRUE;
951 }
953 /* we check the window since if the item was redocked it's
954 been unrealized and maybe it's not realized again yet */
955 if (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window) {
956 cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
957 GDK_HAND2);
958 gdk_window_set_cursor (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window,
959 cursor);
960 gdk_cursor_unref (cursor);
961 }
963 } else if (event->button == 3 && event->type == GDK_BUTTON_PRESS && in_handle) {
964 gdl_dock_item_popup_menu (item, event->button, event->time);
965 event_handled = TRUE;
966 }
968 return event_handled;
969 }
971 static gint
972 gdl_dock_item_motion (GtkWidget *widget,
973 GdkEventMotion *event)
974 {
975 GdlDockItem *item;
976 gint new_x, new_y;
978 g_return_val_if_fail (widget != NULL, FALSE);
979 g_return_val_if_fail (GDL_IS_DOCK_ITEM (widget), FALSE);
980 g_return_val_if_fail (event != NULL, FALSE);
982 item = GDL_DOCK_ITEM (widget);
984 if (!EVENT_IN_GRIP_EVENT_WINDOW (event, item->_priv->grip))
985 return FALSE;
987 if (GDL_DOCK_ITEM_IN_PREDRAG (item)) {
988 if (gtk_drag_check_threshold (widget,
989 item->_priv->start_x,
990 item->_priv->start_y,
991 event->x,
992 event->y)) {
993 GDL_DOCK_ITEM_UNSET_FLAGS (item, GDL_DOCK_IN_PREDRAG);
994 item->dragoff_x = item->_priv->start_x;
995 item->dragoff_y = item->_priv->start_y;
997 gdl_dock_item_drag_start (item);
998 }
999 }
1001 if (!GDL_DOCK_ITEM_IN_DRAG (item))
1002 return FALSE;
1004 new_x = event->x_root;
1005 new_y = event->y_root;
1007 g_signal_emit (item, gdl_dock_item_signals [DOCK_DRAG_MOTION],
1008 0, new_x, new_y);
1010 return TRUE;
1011 }
1013 static gboolean
1014 gdl_dock_item_key_press (GtkWidget *widget,
1015 GdkEventKey *event)
1016 {
1017 gboolean event_handled = FALSE;
1018 if (GDL_DOCK_ITEM_IN_DRAG (widget)) {
1019 if (event->keyval == GDK_Escape) {
1020 gdl_dock_item_drag_end (GDL_DOCK_ITEM (widget), TRUE);
1021 event_handled = TRUE;
1022 }
1023 }
1025 if (event_handled)
1026 return TRUE;
1027 else
1028 return GDL_CALL_PARENT_WITH_DEFAULT (GTK_WIDGET_CLASS,
1029 key_press_event,
1030 (widget, event),
1031 FALSE);
1032 }
1034 static gboolean
1035 gdl_dock_item_dock_request (GdlDockObject *object,
1036 gint x,
1037 gint y,
1038 GdlDockRequest *request)
1039 {
1040 GtkAllocation *alloc;
1041 gint rel_x, rel_y;
1043 /* we get (x,y) in our allocation coordinates system */
1045 /* Get item's allocation. */
1046 alloc = &(GTK_WIDGET (object)->allocation);
1048 /* Get coordinates relative to our window. */
1049 rel_x = x - alloc->x;
1050 rel_y = y - alloc->y;
1052 /* Location is inside. */
1053 if (rel_x > 0 && rel_x < alloc->width &&
1054 rel_y > 0 && rel_y < alloc->height) {
1055 float rx, ry;
1056 GtkRequisition my, other;
1057 gint divider = -1;
1059 /* this are for calculating the extra docking parameter */
1060 gdl_dock_item_preferred_size (GDL_DOCK_ITEM (request->applicant), &other);
1061 gdl_dock_item_preferred_size (GDL_DOCK_ITEM (object), &my);
1063 /* Calculate location in terms of the available space (0-100%). */
1064 rx = (float) rel_x / alloc->width;
1065 ry = (float) rel_y / alloc->height;
1067 /* Determine dock location. */
1068 if (rx < SPLIT_RATIO) {
1069 request->position = GDL_DOCK_LEFT;
1070 divider = other.width;
1071 }
1072 else if (rx > (1 - SPLIT_RATIO)) {
1073 request->position = GDL_DOCK_RIGHT;
1074 rx = 1 - rx;
1075 divider = MAX (0, my.width - other.width);
1076 }
1077 else if (ry < SPLIT_RATIO && ry < rx) {
1078 request->position = GDL_DOCK_TOP;
1079 divider = other.height;
1080 }
1081 else if (ry > (1 - SPLIT_RATIO) && (1 - ry) < rx) {
1082 request->position = GDL_DOCK_BOTTOM;
1083 divider = MAX (0, my.height - other.height);
1084 }
1085 else
1086 request->position = GDL_DOCK_CENTER;
1088 /* Reset rectangle coordinates to entire item. */
1089 request->rect.x = 0;
1090 request->rect.y = 0;
1091 request->rect.width = alloc->width;
1092 request->rect.height = alloc->height;
1094 GdlDockItemBehavior behavior = GDL_DOCK_ITEM(object)->behavior;
1096 /* Calculate docking indicator rectangle size for new locations. Only
1097 do this when we're not over the item's current location. */
1098 if (request->applicant != object) {
1099 switch (request->position) {
1100 case GDL_DOCK_TOP:
1101 if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_TOP)
1102 return FALSE;
1103 request->rect.height *= SPLIT_RATIO;
1104 break;
1105 case GDL_DOCK_BOTTOM:
1106 if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_BOTTOM)
1107 return FALSE;
1108 request->rect.y += request->rect.height * (1 - SPLIT_RATIO);
1109 request->rect.height *= SPLIT_RATIO;
1110 break;
1111 case GDL_DOCK_LEFT:
1112 if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_LEFT)
1113 return FALSE;
1114 request->rect.width *= SPLIT_RATIO;
1115 break;
1116 case GDL_DOCK_RIGHT:
1117 if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_RIGHT)
1118 return FALSE;
1119 request->rect.x += request->rect.width * (1 - SPLIT_RATIO);
1120 request->rect.width *= SPLIT_RATIO;
1121 break;
1122 case GDL_DOCK_CENTER:
1123 if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_CENTER)
1124 return FALSE;
1125 request->rect.x = request->rect.width * SPLIT_RATIO/2;
1126 request->rect.y = request->rect.height * SPLIT_RATIO/2;
1127 request->rect.width = (request->rect.width *
1128 (1 - SPLIT_RATIO/2)) - request->rect.x;
1129 request->rect.height = (request->rect.height *
1130 (1 - SPLIT_RATIO/2)) - request->rect.y;
1131 break;
1132 default:
1133 break;
1134 }
1135 }
1137 /* adjust returned coordinates so they are have the same
1138 origin as our window */
1139 request->rect.x += alloc->x;
1140 request->rect.y += alloc->y;
1142 /* Set possible target location and return TRUE. */
1143 request->target = object;
1145 /* fill-in other dock information */
1146 if (request->position != GDL_DOCK_CENTER && divider >= 0) {
1147 if (G_IS_VALUE (&request->extra))
1148 g_value_unset (&request->extra);
1149 g_value_init (&request->extra, G_TYPE_UINT);
1150 g_value_set_uint (&request->extra, (guint) divider);
1151 }
1153 return TRUE;
1154 }
1155 else /* No docking possible at this location. */
1156 return FALSE;
1157 }
1159 static void
1160 gdl_dock_item_dock (GdlDockObject *object,
1161 GdlDockObject *requestor,
1162 GdlDockPlacement position,
1163 GValue *other_data)
1164 {
1165 GdlDockObject *new_parent, *parent;
1166 gboolean add_ourselves_first;
1168 guint available_space=0;
1169 gint pref_size=-1;
1170 guint splitpos=0;
1171 GtkRequisition req, object_req, parent_req;
1173 parent = gdl_dock_object_get_parent_object (object);
1174 gdl_dock_item_preferred_size (GDL_DOCK_ITEM (requestor), &req);
1175 gdl_dock_item_preferred_size (GDL_DOCK_ITEM (object), &object_req);
1176 if (GDL_IS_DOCK_ITEM (parent))
1177 gdl_dock_item_preferred_size (GDL_DOCK_ITEM (parent), &parent_req);
1178 else
1179 {
1180 parent_req.height = GTK_WIDGET (parent)->allocation.height;
1181 parent_req.width = GTK_WIDGET (parent)->allocation.width;
1182 }
1184 /* If preferred size is not set on the requestor (perhaps a new item),
1185 * then estimate and set it. The default value (either 0 or 1 pixels) is
1186 * not any good.
1187 */
1188 switch (position) {
1189 case GDL_DOCK_TOP:
1190 case GDL_DOCK_BOTTOM:
1191 if (req.width < 2)
1192 {
1193 req.width = object_req.width;
1194 g_object_set (requestor, "preferred-width", req.width, NULL);
1195 }
1196 if (req.height < 2)
1197 {
1198 req.height = NEW_DOCK_ITEM_RATIO * object_req.height;
1199 g_object_set (requestor, "preferred-height", req.height, NULL);
1200 }
1201 if (req.width > 1)
1202 g_object_set (object, "preferred-width", req.width, NULL);
1203 if (req.height > 1)
1204 g_object_set (object, "preferred-height",
1205 object_req.height - req.height, NULL);
1206 break;
1207 case GDL_DOCK_LEFT:
1208 case GDL_DOCK_RIGHT:
1209 if (req.height < 2)
1210 {
1211 req.height = object_req.height;
1212 g_object_set (requestor, "preferred-height", req.height, NULL);
1213 }
1214 if (req.width < 2)
1215 {
1216 req.width = NEW_DOCK_ITEM_RATIO * object_req.width;
1217 g_object_set (requestor, "preferred-width", req.width, NULL);
1218 }
1219 if (req.height > 1)
1220 g_object_set (object, "preferred-height", req.height, NULL);
1221 if (req.width > 1)
1222 g_object_set (object, "preferred-width",
1223 object_req.width - req.width, NULL);
1224 break;
1225 case GDL_DOCK_CENTER:
1226 if (req.height < 2)
1227 {
1228 req.height = object_req.height;
1229 g_object_set (requestor, "preferred-height", req.height, NULL);
1230 }
1231 if (req.width < 2)
1232 {
1233 req.width = object_req.width;
1234 g_object_set (requestor, "preferred-width", req.width, NULL);
1235 }
1236 if (req.height > 1)
1237 g_object_set (object, "preferred-height", req.height, NULL);
1238 if (req.width > 1)
1239 g_object_set (object, "preferred-width", req.width, NULL);
1240 break;
1241 default:
1242 {
1243 GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_ref (GDL_TYPE_DOCK_PLACEMENT));
1244 GEnumValue *enum_value = g_enum_get_value (enum_class, position);
1245 const gchar *name = enum_value ? enum_value->value_name : NULL;
1247 g_warning (_("Unsupported docking strategy %s in dock object of type %s"),
1248 name, G_OBJECT_TYPE_NAME (object));
1249 g_type_class_unref (enum_class);
1250 return;
1251 }
1252 }
1253 switch (position) {
1254 case GDL_DOCK_TOP:
1255 case GDL_DOCK_BOTTOM:
1256 /* get a paned style dock object */
1257 new_parent = g_object_new (gdl_dock_object_type_from_nick ("paned"),
1258 "orientation", GTK_ORIENTATION_VERTICAL,
1259 "preferred-width", object_req.width,
1260 "preferred-height", object_req.height,
1261 NULL);
1262 add_ourselves_first = (position == GDL_DOCK_BOTTOM);
1263 if (parent)
1264 available_space = parent_req.height;
1265 pref_size = req.height;
1266 break;
1267 case GDL_DOCK_LEFT:
1268 case GDL_DOCK_RIGHT:
1269 new_parent = g_object_new (gdl_dock_object_type_from_nick ("paned"),
1270 "orientation", GTK_ORIENTATION_HORIZONTAL,
1271 "preferred-width", object_req.width,
1272 "preferred-height", object_req.height,
1273 NULL);
1274 add_ourselves_first = (position == GDL_DOCK_RIGHT);
1275 if(parent)
1276 available_space = parent_req.width;
1277 pref_size = req.width;
1278 break;
1279 case GDL_DOCK_CENTER:
1280 new_parent = g_object_new (gdl_dock_object_type_from_nick ("notebook"),
1281 "preferred-width", object_req.width,
1282 "preferred-height", object_req.height,
1283 NULL);
1284 add_ourselves_first = TRUE;
1285 break;
1286 default:
1287 {
1288 GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_ref (GDL_TYPE_DOCK_PLACEMENT));
1289 GEnumValue *enum_value = g_enum_get_value (enum_class, position);
1290 const gchar *name = enum_value ? enum_value->value_name : NULL;
1292 g_warning (_("Unsupported docking strategy %s in dock object of type %s"),
1293 name, G_OBJECT_TYPE_NAME (object));
1294 g_type_class_unref (enum_class);
1295 return;
1296 }
1297 }
1299 /* freeze the parent so it doesn't reduce automatically */
1300 if (parent)
1301 gdl_dock_object_freeze (parent);
1303 /* ref ourselves since we could be destroyed when detached */
1304 g_object_ref (object);
1305 GDL_DOCK_OBJECT_SET_FLAGS (object, GDL_DOCK_IN_REFLOW);
1306 gdl_dock_object_detach (object, FALSE);
1308 /* freeze the new parent, so reduce won't get called before it's
1309 actually added to our parent */
1310 gdl_dock_object_freeze (new_parent);
1312 /* bind the new parent to our master, so the following adds work */
1313 gdl_dock_object_bind (new_parent, G_OBJECT (GDL_DOCK_OBJECT_GET_MASTER (object)));
1315 /* add the objects */
1316 if (add_ourselves_first) {
1317 gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (object));
1318 gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (requestor));
1319 splitpos = available_space - pref_size;
1320 } else {
1321 gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (requestor));
1322 gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (object));
1323 splitpos = pref_size;
1324 }
1326 /* add the new parent to the parent */
1327 if (parent)
1328 gtk_container_add (GTK_CONTAINER (parent), GTK_WIDGET (new_parent));
1330 /* show automatic object */
1331 if (GTK_WIDGET_VISIBLE (object))
1332 gtk_widget_show (GTK_WIDGET (new_parent));
1334 /* use extra docking parameter */
1335 if (position != GDL_DOCK_CENTER && other_data &&
1336 G_VALUE_HOLDS (other_data, G_TYPE_UINT)) {
1338 g_object_set (G_OBJECT (new_parent),
1339 "position", g_value_get_uint (other_data),
1340 NULL);
1341 } else if (splitpos > 0 && splitpos < available_space) {
1342 g_object_set (G_OBJECT (new_parent), "position", splitpos, NULL);
1343 }
1345 GDL_DOCK_OBJECT_UNSET_FLAGS (object, GDL_DOCK_IN_REFLOW);
1346 g_object_unref (object);
1348 gdl_dock_object_thaw (new_parent);
1349 if (parent)
1350 gdl_dock_object_thaw (parent);
1351 }
1353 static void
1354 gdl_dock_item_detach_menu (GtkWidget *widget,
1355 GtkMenu *menu)
1356 {
1357 GdlDockItem *item;
1359 item = GDL_DOCK_ITEM (widget);
1360 item->_priv->menu = NULL;
1361 }
1363 static void
1364 gdl_dock_item_popup_menu (GdlDockItem *item,
1365 guint button,
1366 guint32 time)
1367 {
1368 GtkWidget *mitem;
1370 if (!item->_priv->menu) {
1371 /* Create popup menu and attach it to the dock item */
1372 item->_priv->menu = gtk_menu_new ();
1373 gtk_menu_attach_to_widget (GTK_MENU (item->_priv->menu),
1374 GTK_WIDGET (item),
1375 gdl_dock_item_detach_menu);
1377 if (item->behavior & GDL_DOCK_ITEM_BEH_LOCKED) {
1378 /* UnLock menuitem */
1379 mitem = gtk_menu_item_new_with_label (_("UnLock"));
1380 gtk_menu_shell_append (GTK_MENU_SHELL (item->_priv->menu),
1381 mitem);
1382 g_signal_connect (mitem, "activate",
1383 G_CALLBACK (gdl_dock_item_unlock_cb), item);
1384 } else {
1385 /* Hide menuitem. */
1386 mitem = gtk_menu_item_new_with_label (_("Hide"));
1387 gtk_menu_shell_append (GTK_MENU_SHELL (item->_priv->menu), mitem);
1388 g_signal_connect (mitem, "activate",
1389 G_CALLBACK (gdl_dock_item_hide_cb), item);
1390 /* Lock menuitem */
1391 mitem = gtk_menu_item_new_with_label (_("Lock"));
1392 gtk_menu_shell_append (GTK_MENU_SHELL (item->_priv->menu), mitem);
1393 g_signal_connect (mitem, "activate",
1394 G_CALLBACK (gdl_dock_item_lock_cb), item);
1395 }
1396 }
1398 /* Show popup menu. */
1399 gtk_widget_show_all (item->_priv->menu);
1400 gtk_menu_popup (GTK_MENU (item->_priv->menu), NULL, NULL, NULL, NULL,
1401 button, time);
1402 }
1404 static void
1405 gdl_dock_item_drag_start (GdlDockItem *item)
1406 {
1407 GdkCursor *fleur;
1409 if (!GTK_WIDGET_REALIZED (item))
1410 gtk_widget_realize (GTK_WIDGET (item));
1412 GDL_DOCK_ITEM_SET_FLAGS (item, GDL_DOCK_IN_DRAG);
1414 /* grab the pointer so we receive all mouse events */
1415 fleur = gdk_cursor_new (GDK_FLEUR);
1417 /* grab the keyboard & pointer */
1418 gtk_grab_add (GTK_WIDGET (item));
1420 gdk_cursor_unref (fleur);
1422 g_signal_emit (item, gdl_dock_item_signals [DOCK_DRAG_BEGIN], 0);
1423 }
1425 static void
1426 gdl_dock_item_drag_end (GdlDockItem *item,
1427 gboolean cancel)
1428 {
1429 /* Release pointer & keyboard. */
1430 gtk_grab_remove (gtk_grab_get_current ());
1432 g_signal_emit (item, gdl_dock_item_signals [DOCK_DRAG_END], 0, cancel);
1434 GDL_DOCK_ITEM_UNSET_FLAGS (item, GDL_DOCK_IN_DRAG);
1435 }
1437 static void
1438 gdl_dock_item_tab_button (GtkWidget *widget,
1439 GdkEventButton *event,
1440 gpointer data)
1441 {
1442 GdlDockItem *item;
1444 item = GDL_DOCK_ITEM (data);
1446 if (!GDL_DOCK_ITEM_NOT_LOCKED (item))
1447 return;
1449 switch (event->button) {
1450 case 1:
1451 /* set dragoff_{x,y} as we the user clicked on the middle of the
1452 drag handle */
1453 switch (item->orientation) {
1454 case GTK_ORIENTATION_HORIZONTAL:
1455 /*item->dragoff_x = item->_priv->grip_size / 2;*/
1456 item->dragoff_y = GTK_WIDGET (data)->allocation.height / 2;
1457 break;
1458 case GTK_ORIENTATION_VERTICAL:
1459 /*item->dragoff_x = GTK_WIDGET (data)->allocation.width / 2;*/
1460 item->dragoff_y = item->_priv->grip_size / 2;
1461 break;
1462 };
1463 gdl_dock_item_drag_start (item);
1464 break;
1466 case 3:
1467 gdl_dock_item_popup_menu (item, event->button, event->time);
1468 break;
1470 default:
1471 break;
1472 };
1473 }
1475 static void
1476 gdl_dock_item_hide_cb (GtkWidget *widget,
1477 GdlDockItem *item)
1478 {
1479 GdlDockMaster *master;
1481 g_return_if_fail (item != NULL);
1483 master = GDL_DOCK_OBJECT_GET_MASTER (item);
1484 gdl_dock_item_hide_item (item);
1485 }
1487 static void
1488 gdl_dock_item_lock_cb (GtkWidget *widget,
1489 GdlDockItem *item)
1490 {
1491 g_return_if_fail (item != NULL);
1493 gdl_dock_item_lock (item);
1494 }
1496 static void
1497 gdl_dock_item_unlock_cb (GtkWidget *widget,
1498 GdlDockItem *item)
1499 {
1500 g_return_if_fail (item != NULL);
1502 gdl_dock_item_unlock (item);
1503 }
1505 static void
1506 gdl_dock_item_showhide_grip (GdlDockItem *item)
1507 {
1508 GdkDisplay *display;
1509 GdkCursor *cursor;
1511 gdl_dock_item_detach_menu (GTK_WIDGET (item), NULL);
1512 display = gtk_widget_get_display (GTK_WIDGET (item));
1513 cursor = NULL;
1515 if (item->_priv->grip) {
1516 if (GDL_DOCK_ITEM_GRIP_SHOWN (item) &&
1517 GDL_DOCK_ITEM_NOT_LOCKED(item))
1518 cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
1519 }
1520 if (item->_priv->grip && GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window)
1521 gdk_window_set_cursor (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window, cursor);
1523 if (cursor)
1524 gdk_cursor_unref (cursor);
1526 gtk_widget_queue_resize (GTK_WIDGET (item));
1527 }
1529 static void
1530 gdl_dock_item_real_set_orientation (GdlDockItem *item,
1531 GtkOrientation orientation)
1532 {
1533 item->orientation = orientation;
1535 if (GTK_WIDGET_DRAWABLE (item))
1536 gtk_widget_queue_draw (GTK_WIDGET (item));
1537 gtk_widget_queue_resize (GTK_WIDGET (item));
1538 }
1541 /* ----- Public interface ----- */
1543 GtkWidget *
1544 gdl_dock_item_new (const gchar *name,
1545 const gchar *long_name,
1546 GdlDockItemBehavior behavior)
1547 {
1548 GdlDockItem *item;
1550 item = GDL_DOCK_ITEM (g_object_new (GDL_TYPE_DOCK_ITEM,
1551 "name", name,
1552 "long-name", long_name,
1553 "behavior", behavior,
1554 NULL));
1555 GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_AUTOMATIC);
1556 gdl_dock_item_set_tablabel (item, gtk_label_new (long_name));
1557 return GTK_WIDGET (item);
1558 }
1560 GtkWidget *
1561 gdl_dock_item_new_with_stock (const gchar *name,
1562 const gchar *long_name,
1563 const gchar *stock_id,
1564 GdlDockItemBehavior behavior)
1565 {
1566 GdlDockItem *item;
1568 item = GDL_DOCK_ITEM (g_object_new (GDL_TYPE_DOCK_ITEM,
1569 "name", name,
1570 "long-name", long_name,
1571 "stock-id", stock_id,
1572 "behavior", behavior,
1573 NULL));
1574 GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_AUTOMATIC);
1575 gdl_dock_item_set_tablabel (item, gtk_label_new (long_name));
1577 return GTK_WIDGET (item);
1578 }
1580 GtkWidget *
1581 gdl_dock_item_new_with_pixbuf_icon (const gchar *name,
1582 const gchar *long_name,
1583 const GdkPixbuf *pixbuf_icon,
1584 GdlDockItemBehavior behavior)
1585 {
1586 GdlDockItem *item;
1588 item = GDL_DOCK_ITEM (g_object_new (GDL_TYPE_DOCK_ITEM,
1589 "name", name,
1590 "long-name", long_name,
1591 "pixbuf-icon", pixbuf_icon,
1592 "behavior", behavior,
1593 NULL));
1595 GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_AUTOMATIC);
1596 gdl_dock_item_set_tablabel (item, gtk_label_new (long_name));
1598 return GTK_WIDGET (item);
1599 }
1601 /* convenient function (and to preserve source compat) */
1602 void
1603 gdl_dock_item_dock_to (GdlDockItem *item,
1604 GdlDockItem *target,
1605 GdlDockPlacement position,
1606 gint docking_param)
1607 {
1608 g_return_if_fail (item != NULL);
1609 g_return_if_fail (item != target);
1610 g_return_if_fail (target != NULL || position == GDL_DOCK_FLOATING);
1611 g_return_if_fail ((item->behavior & GDL_DOCK_ITEM_BEH_NEVER_FLOATING) == 0 || position != GDL_DOCK_FLOATING);
1613 if (position == GDL_DOCK_FLOATING || !target) {
1614 GdlDockObject *controller;
1616 if (!gdl_dock_object_is_bound (GDL_DOCK_OBJECT (item))) {
1617 g_warning (_("Attempt to bind an unbound item %p"), item);
1618 return;
1619 }
1621 controller = gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (item));
1623 /* FIXME: save previous docking position for later
1624 re-docking... does this make sense now? */
1626 /* Create new floating dock for widget. */
1627 item->dragoff_x = item->dragoff_y = 0;
1628 gdl_dock_add_floating_item (GDL_DOCK (controller),
1629 item, 0, 0, -1, -1);
1631 } else
1632 gdl_dock_object_dock (GDL_DOCK_OBJECT (target),
1633 GDL_DOCK_OBJECT (item),
1634 position, NULL);
1635 }
1637 void
1638 gdl_dock_item_set_orientation (GdlDockItem *item,
1639 GtkOrientation orientation)
1640 {
1641 GParamSpec *pspec;
1643 g_return_if_fail (item != NULL);
1645 if (item->orientation != orientation) {
1646 /* push the property down the hierarchy if our child supports it */
1647 if (item->child != NULL) {
1648 pspec = g_object_class_find_property (
1649 G_OBJECT_GET_CLASS (item->child), "orientation");
1650 if (pspec && pspec->value_type == GTK_TYPE_ORIENTATION)
1651 g_object_set (G_OBJECT (item->child),
1652 "orientation", orientation,
1653 NULL);
1654 };
1656 GDL_CALL_VIRTUAL (item, GDL_DOCK_ITEM_GET_CLASS, set_orientation, (item, orientation));
1657 g_object_notify (G_OBJECT (item), "orientation");
1658 }
1659 }
1661 GtkWidget *
1662 gdl_dock_item_get_tablabel (GdlDockItem *item)
1663 {
1664 g_return_val_if_fail (item != NULL, NULL);
1665 g_return_val_if_fail (GDL_IS_DOCK_ITEM (item), NULL);
1667 return item->_priv->tab_label;
1668 }
1670 void
1671 gdl_dock_item_set_tablabel (GdlDockItem *item,
1672 GtkWidget *tablabel)
1673 {
1674 g_return_if_fail (item != NULL);
1676 if (item->_priv->tab_label) {
1677 /* disconnect and unref the previous tablabel */
1678 if (GDL_IS_DOCK_TABLABEL (item->_priv->tab_label)) {
1679 g_signal_handlers_disconnect_matched (item->_priv->tab_label,
1680 G_SIGNAL_MATCH_DATA,
1681 0, 0, NULL,
1682 NULL, item);
1683 g_object_set (item->_priv->tab_label, "item", NULL, NULL);
1684 }
1685 gtk_widget_unref (item->_priv->tab_label);
1686 item->_priv->tab_label = NULL;
1687 }
1689 if (tablabel) {
1690 gtk_widget_ref (tablabel);
1691 gtk_object_sink (GTK_OBJECT (tablabel));
1692 item->_priv->tab_label = tablabel;
1693 if (GDL_IS_DOCK_TABLABEL (tablabel)) {
1694 g_object_set (tablabel, "item", item, NULL);
1695 /* connect to tablabel signal */
1696 g_signal_connect (tablabel, "button_pressed_handle",
1697 G_CALLBACK (gdl_dock_item_tab_button), item);
1698 }
1699 }
1700 }
1702 void
1703 gdl_dock_item_hide_grip (GdlDockItem *item)
1704 {
1705 g_return_if_fail (item != NULL);
1706 if (item->_priv->grip_shown) {
1707 item->_priv->grip_shown = FALSE;
1708 gdl_dock_item_showhide_grip (item);
1709 };
1710 g_warning ("Grips always show unless GDL_DOCK_ITEM_BEH_NO_GRIP is set\n" );
1711 }
1713 void
1714 gdl_dock_item_show_grip (GdlDockItem *item)
1715 {
1716 g_return_if_fail (item != NULL);
1717 if (!item->_priv->grip_shown) {
1718 item->_priv->grip_shown = TRUE;
1719 gdl_dock_item_showhide_grip (item);
1720 };
1721 }
1723 /* convenient function (and to preserve source compat) */
1724 void
1725 gdl_dock_item_bind (GdlDockItem *item,
1726 GtkWidget *dock)
1727 {
1728 g_return_if_fail (item != NULL);
1729 g_return_if_fail (dock == NULL || GDL_IS_DOCK (dock));
1731 gdl_dock_object_bind (GDL_DOCK_OBJECT (item),
1732 G_OBJECT (GDL_DOCK_OBJECT_GET_MASTER (dock)));
1733 }
1735 /* convenient function (and to preserve source compat) */
1736 void
1737 gdl_dock_item_unbind (GdlDockItem *item)
1738 {
1739 g_return_if_fail (item != NULL);
1741 gdl_dock_object_unbind (GDL_DOCK_OBJECT (item));
1742 }
1744 void
1745 gdl_dock_item_hide_item (GdlDockItem *item)
1746 {
1747 g_return_if_fail (item != NULL);
1749 if (!GDL_DOCK_OBJECT_ATTACHED (item))
1750 /* already hidden/detached */
1751 return;
1753 /* if the object is manual, create a new placeholder to be able to
1754 restore the position later */
1755 if (!GDL_DOCK_OBJECT_AUTOMATIC (item)) {
1756 if (item->_priv->ph)
1757 g_object_unref (item->_priv->ph);
1759 gboolean isFloating = FALSE;
1760 gint width=0, height=0, x=0, y = 0;
1762 if (GDL_IS_DOCK (gdl_dock_object_get_parent_object (GDL_DOCK_OBJECT (item))))
1763 {
1764 GdlDock* dock = GDL_DOCK (gdl_dock_object_get_parent_object (GDL_DOCK_OBJECT (item)));
1765 g_object_get (dock,
1766 "floating", &isFloating,
1767 "width", &width,
1768 "height",&height,
1769 "floatx",&x,
1770 "floaty",&y,
1771 NULL);
1772 } else {
1773 item->_priv->preferred_width=GTK_WIDGET (item)->allocation.width;
1774 item->_priv->preferred_height=GTK_WIDGET (item)->allocation.height;
1775 }
1776 item->_priv->ph = GDL_DOCK_PLACEHOLDER (
1777 g_object_new (GDL_TYPE_DOCK_PLACEHOLDER,
1778 "sticky", FALSE,
1779 "host", item,
1780 "width", width,
1781 "height", height,
1782 "floating", isFloating,
1783 "floatx", x,
1784 "floaty", y,
1785 NULL));
1786 g_object_ref (item->_priv->ph);
1787 gtk_object_sink (GTK_OBJECT (item->_priv->ph));
1788 }
1790 gdl_dock_object_freeze (GDL_DOCK_OBJECT (item));
1792 /* hide our children first, so they can also set placeholders */
1793 if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT (item)))
1794 gtk_container_foreach (GTK_CONTAINER (item),
1795 (GtkCallback) gdl_dock_item_hide_item,
1796 NULL);
1798 /* detach the item recursively */
1799 gdl_dock_object_detach (GDL_DOCK_OBJECT (item), TRUE);
1801 gtk_widget_hide (GTK_WIDGET (item));
1803 gdl_dock_object_thaw (GDL_DOCK_OBJECT (item));
1804 }
1806 void
1807 gdl_dock_item_iconify_item (GdlDockItem *item)
1808 {
1809 g_return_if_fail (item != NULL);
1811 GDL_DOCK_OBJECT_SET_FLAGS (item, GDL_DOCK_ICONIFIED);
1812 gdl_dock_item_hide_item (item);
1813 }
1815 void
1816 gdl_dock_item_show_item (GdlDockItem *item)
1817 {
1818 g_return_if_fail (item != NULL);
1820 GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_ICONIFIED);
1822 if (item->_priv->ph) {
1823 gboolean isFloating=FALSE;
1824 gint width = 0, height = 0, x= 0, y = 0;
1825 g_object_get (G_OBJECT(item->_priv->ph),
1826 "width", &width,
1827 "height", &height,
1828 "floating",&isFloating,
1829 "floatx", &x,
1830 "floaty", &y,
1831 NULL);
1832 if (isFloating) {
1833 GdlDockObject *controller =
1834 gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (item));
1835 gdl_dock_add_floating_item (GDL_DOCK (controller),
1836 item, x, y, width, height);
1837 } else {
1838 gtk_container_add (GTK_CONTAINER (item->_priv->ph),
1839 GTK_WIDGET (item));
1840 }
1841 g_object_unref (item->_priv->ph);
1842 item->_priv->ph = NULL;
1844 } else if (gdl_dock_object_is_bound (GDL_DOCK_OBJECT (item))) {
1845 GdlDockObject *toplevel;
1847 toplevel = gdl_dock_master_get_controller
1848 (GDL_DOCK_OBJECT_GET_MASTER (item));
1850 if (item->behavior & GDL_DOCK_ITEM_BEH_NEVER_FLOATING) {
1851 g_warning("Object %s has no default position and flag GDL_DOCK_ITEM_BEH_NEVER_FLOATING is set.\n",
1852 GDL_DOCK_OBJECT(item)->name);
1853 } else if (toplevel) {
1854 gdl_dock_object_dock (toplevel, GDL_DOCK_OBJECT (item),
1855 GDL_DOCK_FLOATING, NULL);
1856 } else
1857 g_warning("There is no toplevel window. GdlDockItem %s cannot be shown.\n", GDL_DOCK_OBJECT(item)->name);
1859 } else
1860 g_warning("GdlDockItem %s is not bound. It cannot be shown.\n",
1861 GDL_DOCK_OBJECT(item)->name);
1863 gtk_widget_show (GTK_WIDGET (item));
1864 }
1866 void
1867 gdl_dock_item_lock (GdlDockItem *item)
1868 {
1869 g_object_set (item, "locked", TRUE, NULL);
1870 }
1872 void
1873 gdl_dock_item_unlock (GdlDockItem *item)
1874 {
1875 g_object_set (item, "locked", FALSE, NULL);
1876 }
1878 void
1879 gdl_dock_item_set_default_position (GdlDockItem *item,
1880 GdlDockObject *reference)
1881 {
1882 g_return_if_fail (item != NULL);
1884 if (item->_priv->ph) {
1885 g_object_unref (item->_priv->ph);
1886 item->_priv->ph = NULL;
1887 }
1889 if (reference && GDL_DOCK_OBJECT_ATTACHED (reference)) {
1890 if (GDL_IS_DOCK_PLACEHOLDER (reference)) {
1891 g_object_ref (reference);
1892 gtk_object_sink (GTK_OBJECT (reference));
1893 item->_priv->ph = GDL_DOCK_PLACEHOLDER (reference);
1894 } else {
1895 item->_priv->ph = GDL_DOCK_PLACEHOLDER (
1896 g_object_new (GDL_TYPE_DOCK_PLACEHOLDER,
1897 "sticky", TRUE,
1898 "host", reference,
1899 NULL));
1900 g_object_ref (item->_priv->ph);
1901 gtk_object_sink (GTK_OBJECT (item->_priv->ph));
1902 }
1903 }
1904 }
1906 void
1907 gdl_dock_item_preferred_size (GdlDockItem *item,
1908 GtkRequisition *req)
1909 {
1910 if (!req)
1911 return;
1913 req->width = MAX (item->_priv->preferred_width,
1914 GTK_WIDGET (item)->allocation.width);
1915 req->height = MAX (item->_priv->preferred_height,
1916 GTK_WIDGET (item)->allocation.height);
1917 }
1920 /* ----- gtk orientation type exporter/importer ----- */
1922 static void
1923 gdl_dock_param_export_gtk_orientation (const GValue *src,
1924 GValue *dst)
1925 {
1926 dst->data [0].v_pointer =
1927 g_strdup_printf ("%s", (src->data [0].v_int == GTK_ORIENTATION_HORIZONTAL) ?
1928 "horizontal" : "vertical");
1929 }
1931 static void
1932 gdl_dock_param_import_gtk_orientation (const GValue *src,
1933 GValue *dst)
1934 {
1935 if (!strcmp (src->data [0].v_pointer, "horizontal"))
1936 dst->data [0].v_int = GTK_ORIENTATION_HORIZONTAL;
1937 else
1938 dst->data [0].v_int = GTK_ORIENTATION_VERTICAL;
1939 }