Code

Dockable dialogs patch applied
authorgustav_b <gustav_b@users.sourceforge.net>
Wed, 29 Aug 2007 21:27:07 +0000 (21:27 +0000)
committergustav_b <gustav_b@users.sourceforge.net>
Wed, 29 Aug 2007 21:27:07 +0000 (21:27 +0000)
(https://sourceforge.net/tracker/?func=detail&atid=604308&aid=1688508&group_id=93438)

110 files changed:
configure.ac
src/Makefile.am
src/Makefile_insert
src/desktop.h
src/dialogs/iconpreview.cpp
src/dialogs/iconpreview.h
src/dialogs/layers-panel.cpp
src/dialogs/layers-panel.h
src/dialogs/tiledialog.cpp
src/dialogs/tiledialog.h
src/libgdl/Makefile_insert [new file with mode: 0644]
src/libgdl/README.gdl-dock [new file with mode: 0644]
src/libgdl/gdl-combo-button.c [new file with mode: 0644]
src/libgdl/gdl-combo-button.h [new file with mode: 0644]
src/libgdl/gdl-dock-bar.c [new file with mode: 0644]
src/libgdl/gdl-dock-bar.h [new file with mode: 0644]
src/libgdl/gdl-dock-item-grip.c [new file with mode: 0644]
src/libgdl/gdl-dock-item-grip.h [new file with mode: 0644]
src/libgdl/gdl-dock-item.c [new file with mode: 0644]
src/libgdl/gdl-dock-item.h [new file with mode: 0644]
src/libgdl/gdl-dock-master.c [new file with mode: 0644]
src/libgdl/gdl-dock-master.h [new file with mode: 0644]
src/libgdl/gdl-dock-notebook.c [new file with mode: 0644]
src/libgdl/gdl-dock-notebook.h [new file with mode: 0644]
src/libgdl/gdl-dock-object.c [new file with mode: 0644]
src/libgdl/gdl-dock-object.h [new file with mode: 0644]
src/libgdl/gdl-dock-paned.c [new file with mode: 0644]
src/libgdl/gdl-dock-paned.h [new file with mode: 0644]
src/libgdl/gdl-dock-placeholder.c [new file with mode: 0644]
src/libgdl/gdl-dock-placeholder.h [new file with mode: 0644]
src/libgdl/gdl-dock-tablabel.c [new file with mode: 0644]
src/libgdl/gdl-dock-tablabel.h [new file with mode: 0644]
src/libgdl/gdl-dock.c [new file with mode: 0644]
src/libgdl/gdl-dock.h [new file with mode: 0644]
src/libgdl/gdl-i18n.c [new file with mode: 0644]
src/libgdl/gdl-i18n.h [new file with mode: 0644]
src/libgdl/gdl-stock-icons.h [new file with mode: 0644]
src/libgdl/gdl-stock.c [new file with mode: 0644]
src/libgdl/gdl-stock.h [new file with mode: 0644]
src/libgdl/gdl-switcher.c [new file with mode: 0644]
src/libgdl/gdl-switcher.h [new file with mode: 0644]
src/libgdl/gdl-tools.h [new file with mode: 0644]
src/libgdl/layout.glade [new file with mode: 0644]
src/libgdl/libgdl.h [new file with mode: 0644]
src/libgdl/libgdlmarshal.c [new file with mode: 0644]
src/libgdl/libgdlmarshal.h [new file with mode: 0644]
src/libgdl/libgdlmarshal.list [new file with mode: 0644]
src/libgdl/libgdltypebuiltins.c [new file with mode: 0644]
src/libgdl/libgdltypebuiltins.h [new file with mode: 0644]
src/libgdl/makefile [new file with mode: 0644]
src/libgdl/makefile.in [new file with mode: 0644]
src/preferences-skeleton.h
src/ui/dialog/Makefile_insert
src/ui/dialog/align-and-distribute.cpp
src/ui/dialog/align-and-distribute.h
src/ui/dialog/behavior.h [new file with mode: 0644]
src/ui/dialog/dialog-manager.cpp
src/ui/dialog/dialog.cpp
src/ui/dialog/dialog.h
src/ui/dialog/dock-behavior.cpp [new file with mode: 0644]
src/ui/dialog/dock-behavior.h [new file with mode: 0644]
src/ui/dialog/document-metadata.cpp
src/ui/dialog/document-metadata.h
src/ui/dialog/document-properties.cpp
src/ui/dialog/document-properties.h
src/ui/dialog/export.cpp
src/ui/dialog/export.h
src/ui/dialog/extension-editor.cpp
src/ui/dialog/extension-editor.h
src/ui/dialog/fill-and-stroke.cpp
src/ui/dialog/fill-and-stroke.h
src/ui/dialog/filter-effects-dialog.cpp
src/ui/dialog/filter-effects-dialog.h
src/ui/dialog/find.cpp
src/ui/dialog/find.h
src/ui/dialog/floating-behavior.cpp [new file with mode: 0644]
src/ui/dialog/floating-behavior.h [new file with mode: 0644]
src/ui/dialog/inkscape-preferences.cpp
src/ui/dialog/inkscape-preferences.h
src/ui/dialog/layer-editor.cpp
src/ui/dialog/layer-editor.h
src/ui/dialog/livepatheffect-editor.cpp
src/ui/dialog/livepatheffect-editor.h
src/ui/dialog/memory.cpp
src/ui/dialog/memory.h
src/ui/dialog/messages.cpp
src/ui/dialog/messages.h
src/ui/dialog/scriptdialog.cpp
src/ui/dialog/scriptdialog.h
src/ui/dialog/text-properties.cpp
src/ui/dialog/text-properties.h
src/ui/dialog/tracedialog.cpp
src/ui/dialog/tracedialog.h
src/ui/dialog/transformation.cpp
src/ui/dialog/transformation.h
src/ui/dialog/undo-history.cpp
src/ui/dialog/undo-history.h
src/ui/dialog/xml-editor.cpp
src/ui/dialog/xml-editor.h
src/ui/view/edit-widget-interface.h
src/ui/view/edit-widget.cpp
src/ui/view/edit-widget.h
src/ui/widget/Makefile_insert
src/ui/widget/dock-item.cpp [new file with mode: 0644]
src/ui/widget/dock-item.h [new file with mode: 0644]
src/ui/widget/dock.cpp [new file with mode: 0644]
src/ui/widget/dock.h [new file with mode: 0644]
src/verbs.cpp
src/widgets/desktop-widget.cpp
src/widgets/desktop-widget.h

index 0210bd507d0d81cc370a434e220b8d4f1c495d5f..360fd875e24c3b0c19fcf6948e6df65bc150c463 100644 (file)
@@ -886,6 +886,7 @@ src/helper/makefile
 src/inkjar/makefile
 src/io/makefile
 src/libcroco/makefile
+src/libgdl/makefile
 src/libnr/makefile
 src/libnrtype/makefile
 src/libavoid/makefile
index 260280a409a55e1d08e2857f0a04307b95c11418..7e6cd1f263ded0a0f3dabdf03cc23cc46b8bb987 100644 (file)
@@ -43,6 +43,7 @@ include io/Makefile_insert
 include pedro/Makefile_insert
 include jabber_whiteboard/Makefile_insert
 include libcroco/Makefile_insert
+include libgdl/Makefile_insert
 include libnr/Makefile_insert
 include libnrtype/Makefile_insert
 include libavoid/Makefile_insert
@@ -87,6 +88,7 @@ noinst_LIBRARIES =    \
        helper/libspchelp.a     \
        io/libio.a      \
        libcroco/libcroco.a     \
+       libgdl/libgdl.a         \
        live_effects/liblive_effects.a \
        live_effects/parameter/liblpeparam.a \
        ui/libui.a      \
@@ -144,6 +146,7 @@ EXTRA_DIST =        \
        io/doc2html.xsl         \
        pedro/makefile.in       \
        jabber_whiteboard/makefile.in   \
+       libgdl/makefile.in      \
        libcroco/makefile.in    \
        libnr/makefile.in       \
        libnrtype/makefile.in   \
index ebba357ef4bfa0baf800a20df76a867c2858f0db..f0b6166263ed0e4899916425e5d81cf018525eb9 100644 (file)
@@ -339,6 +339,7 @@ inkscape_private_libs =     \
        ui/view/libuiview.a     \
        ui/libui.a              \
        ui/widget/libuiwidget.a \
+       libgdl/libgdl.a         \
        graphlayout/libgraphlayout.a    \
        removeoverlap/libremoveoverlap.a                \
        libcola/libcola.a       \
index 10509c2749402aee91cdb08400db10c79d305ce0..bc80336de4eb0b9d8de05e972dd4e7cbd6f64286 100644 (file)
@@ -181,6 +181,8 @@ struct SPDesktop : public Inkscape::UI::View::View
     int displayMode;
     int getMode() const { return displayMode; }
 
+    Inkscape::UI::Widget::Dock* getDock() { return _widget->getDock(); }
+
     void set_active (bool new_active);
     SPObject *currentRoot() const;
     SPObject *currentLayer() const;
index 90f48a4a6f320963350484fb1bd0122d4d44db26..d0b8b4a8fff87510715f38e8c9b195346085dd9d 100644 (file)
@@ -49,16 +49,16 @@ namespace Dialogs {
 
 IconPreviewPanel* IconPreviewPanel::instance = 0;
 
-
-IconPreviewPanel& IconPreviewPanel::getInstance()
+IconPreviewPanel*
+IconPreviewPanel::create(Inkscape::UI::Dialog::Behavior::BehaviorFactory behavior_factory)
 {
     if ( !instance ) {
-        instance = new IconPreviewPanel();
+        instance = new IconPreviewPanel(behavior_factory);
     }
 
     instance->refreshPreview();
 
-    return *instance;
+    return instance;
 }
 
 //#########################################################################
@@ -72,7 +72,7 @@ void IconPreviewPanel::on_button_clicked(int which)
 
         hot = which;
         updateMagnify();
-        queue_draw();
+        get_vbox()->queue_draw();
     }
 }
 
@@ -85,8 +85,8 @@ void IconPreviewPanel::on_button_clicked(int which)
 /**
  * Constructor
  */
-IconPreviewPanel::IconPreviewPanel() :
-    Panel(),
+IconPreviewPanel::IconPreviewPanel(Inkscape::UI::Dialog::Behavior::BehaviorFactory behavior_factory) :
+    Inkscape::UI::Dialog::Dialog(behavior_factory, "dialogs.iconpreview", SP_VERB_VIEW_ICON_PREVIEW),
     hot(1),
     refreshButton(0),
     selectionButton(0)
@@ -186,7 +186,7 @@ IconPreviewPanel::IconPreviewPanel() :
 
 
     Gtk::HButtonBox* holder = new Gtk::HButtonBox( Gtk::BUTTONBOX_END );
-    _getContents()->pack_end( *holder, false, false );
+    get_vbox()->pack_end( *holder, false, false );
 
     selectionButton = new Gtk::ToggleButton(_("Selection")); // , GTK_RESPONSE_APPLY
     holder->pack_start( *selectionButton, false, false );
@@ -202,7 +202,7 @@ IconPreviewPanel::IconPreviewPanel() :
     refreshButton->signal_clicked().connect( sigc::mem_fun(*this, &IconPreviewPanel::refreshPreview) );
 
 
-    _getContents()->pack_start(iconBox, Gtk::PACK_EXPAND_WIDGET);
+    get_vbox()->pack_start(iconBox, Gtk::PACK_EXPAND_WIDGET);
 
     show_all_children();
 }
index e4be831d3380d92c5211a186e77a714e4e2d8568..9cfe81b117bcf90dc8124c0e2e4ab927b49342e6 100644 (file)
 #include <gtkmm/label.h>
 #include <gtkmm/paned.h>
 #include <gtkmm/image.h>
+#include <gtkmm/togglebutton.h>
 #include <gtkmm/toggletoolbutton.h>
 
-#include "ui/widget/panel.h"
+#include "ui/dialog/dialog.h"
 
 struct SPObject;
 
@@ -32,13 +33,13 @@ namespace Dialogs {
 /**
  * A panel that displays an icon preview
  */
-class IconPreviewPanel : public Inkscape::UI::Widget::Panel
+class IconPreviewPanel : public Inkscape::UI::Dialog::Dialog
 {
 public:
-    IconPreviewPanel();
+    IconPreviewPanel(Inkscape::UI::Dialog::Behavior::BehaviorFactory behavior_factory);
     //IconPreviewPanel(Glib::ustring const &label);
 
-    static IconPreviewPanel& getInstance();
+    static IconPreviewPanel *create(Inkscape::UI::Dialog::Behavior::BehaviorFactory behavior_factory);
 
     void refreshPreview();
     void modeToggled();
index 32a52a3737ecbeee89810d1e8662438d6f0f090b..383d11f57b0e54a0559bd418c21ce9e09bf2fa6b 100644 (file)
@@ -46,13 +46,14 @@ namespace Dialogs {
 
 LayersPanel* LayersPanel::instance = 0;
 
-LayersPanel& LayersPanel::getInstance()
+LayersPanel*
+LayersPanel::create(Inkscape::UI::Dialog::Behavior::BehaviorFactory behavior_factory)
 {
     if ( !instance ) {
-        instance = new LayersPanel();
+        instance = new LayersPanel(behavior_factory);
     }
 
-    return *instance;
+    return instance;
 }
 
 enum {
@@ -707,8 +708,8 @@ void LayersPanel::_opacityChanged()
 /**
  * Constructor
  */
-LayersPanel::LayersPanel() :
-    Inkscape::UI::Widget::Panel( Glib::ustring(), "dialogs.layers" ),
+LayersPanel::LayersPanel(Inkscape::UI::Dialog::Behavior::BehaviorFactory behavior_factory) :
+    Inkscape::UI::Dialog::Dialog(behavior_factory, "dialogs.layers", SP_VERB_DIALOG_LAYERS),
     _maxNestDepth(20),
     _mgr(0),
     _desktop(0),
@@ -775,10 +776,10 @@ LayersPanel::LayersPanel() :
     _opacityBox.pack_end( _spinBtn, Gtk::PACK_SHRINK );
     _watching.push_back( &_opacityBox );
 
-    _getContents()->pack_start( _scroller, Gtk::PACK_EXPAND_WIDGET );
+    get_vbox()->pack_start( _scroller, Gtk::PACK_EXPAND_WIDGET );
 
-    _getContents()->pack_end(_opacityBox, Gtk::PACK_SHRINK);
-    _getContents()->pack_end(_buttonsRow, Gtk::PACK_SHRINK);
+    get_vbox()->pack_end(_opacityBox, Gtk::PACK_SHRINK);
+    get_vbox()->pack_end(_buttonsRow, Gtk::PACK_SHRINK);
 
     _opacityConnection = _opacity.get_adjustment()->signal_value_changed().connect( sigc::mem_fun(*this, &LayersPanel::_opacityChanged) );
 
@@ -859,7 +860,7 @@ LayersPanel::LayersPanel() :
 
     show_all_children();
 
-    restorePanelPrefs();
+    // restorePanelPrefs();
 }
 
 LayersPanel::~LayersPanel()
@@ -892,7 +893,7 @@ void LayersPanel::setDesktop( SPDesktop* desktop )
 
         _desktop = SP_ACTIVE_DESKTOP;
         if ( _desktop ) {
-            setLabel( _desktop->doc()->name );
+            //setLabel( _desktop->doc()->name );
 
             _mgr = _desktop->layer_manager;
             if ( _mgr ) {
index b253aae27ed659cf4a6639f6e062dbb685e895a2..83c5089fc8a434726caa00a02c9dae7de4718957 100644 (file)
@@ -21,8 +21,8 @@
 #include <gtkmm/buttonbox.h>
 #include <gtkmm/spinbutton.h>
 
-#include "ui/widget/panel.h"
 //#include "ui/previewholder.h"
+#include "ui/dialog/dialog.h"
 
 class SPObject;
 
@@ -37,14 +37,14 @@ namespace Dialogs {
 /**
  * A panel that displays layers.
  */
-class LayersPanel : public Inkscape::UI::Widget::Panel
+class LayersPanel : public Inkscape::UI::Dialog::Dialog
 {
 public:
-    LayersPanel();
+    LayersPanel(Inkscape::UI::Dialog::Behavior::BehaviorFactory behavior_factory);
     virtual ~LayersPanel();
 
-    static LayersPanel& getInstance();
     //virtual void setOrientation( Gtk::AnchorType how );
+    static LayersPanel *create(Inkscape::UI::Dialog::Behavior::BehaviorFactory behavior_factory);
 
     void setDesktop( SPDesktop* desktop );
 
index 75311ddd03dc3fa3b837a9ea02ffe0fce9817240..e46b6e127bd1a297d4ec5382b01ab93a99c90312 100644 (file)
@@ -612,8 +612,8 @@ static void updateSelectionCallback(Inkscape::Application *inkscape, Inkscape::S
 /**
  * Constructor
  */
-TileDialog::TileDialog()
-    : Dialog ("dialogs.gridtiler", SP_VERB_SELECTION_GRIDTILE)
+TileDialog::TileDialog(Behavior::BehaviorFactory behavior_factory)
+    : Dialog (behavior_factory, "dialogs.gridtiler", SP_VERB_SELECTION_GRIDTILE)
 {
      // bool used by spin button callbacks to stop loops where they change each other.
     updating = false;
index 69a5ea3e2dc95177fc4cd60691385abd983195de..5fe114d8351edf6ac3a55648cdcce652728dbbbb 100644 (file)
@@ -40,13 +40,13 @@ public:
     /**
      * Constructor
      */
-    TileDialog() ;
-
+    TileDialog(Behavior::BehaviorFactory behavior_factory) ;
 
     /**
      * Factory method
      */
-    static TileDialog *create() { return new TileDialog(); }
+    static TileDialog *create(Behavior::BehaviorFactory behavior_factory)
+    { return new TileDialog(behavior_factory); }
 
     /**
      * Destructor
diff --git a/src/libgdl/Makefile_insert b/src/libgdl/Makefile_insert
new file mode 100644 (file)
index 0000000..b4a7209
--- /dev/null
@@ -0,0 +1,43 @@
+## Makefile.am fragment sourced by src/Makefile.am.
+
+libgdl/all: libgdl/libgdl.a
+
+libgdl/clean:
+       rm -f libgdl/libgdl.a $(libgdl_gdl_a_OBJECTS)
+
+libgdl_libgdl_a_SOURCES =      \
+       libgdl/gdl-tools.h              \
+       libgdl/gdl-dock-object.h        \
+       libgdl/gdl-dock-master.h        \
+       libgdl/gdl-dock.h               \
+       libgdl/gdl-dock-item.h          \
+       libgdl/gdl-dock-notebook.h      \
+       libgdl/gdl-dock-paned.h         \
+       libgdl/gdl-dock-tablabel.h      \
+       libgdl/gdl-dock-placeholder.h   \
+       libgdl/gdl-dock-bar.h           \
+       libgdl/gdl-combo-button.h       \
+       libgdl/gdl-stock.h              \
+       libgdl/gdl-stock-icons.h        \
+       libgdl/gdl-i18n.h               \
+       libgdl/gdl-i18n.c               \
+       libgdl/gdl-dock-object.c        \
+       libgdl/gdl-dock-master.c        \
+       libgdl/gdl-dock.c               \
+       libgdl/gdl-dock-item.c          \
+       libgdl/gdl-dock-item-grip.h     \
+       libgdl/gdl-dock-item-grip.c     \
+       libgdl/gdl-dock-notebook.c      \
+       libgdl/gdl-dock-paned.c         \
+       libgdl/gdl-dock-tablabel.c      \
+       libgdl/gdl-dock-placeholder.c   \
+       libgdl/gdl-dock-bar.c           \
+       libgdl/gdl-combo-button.c       \
+       libgdl/gdl-stock.c              \
+       libgdl/gdl-switcher.h           \
+       libgdl/gdl-switcher.c           \
+       libgdl/libgdltypebuiltins.h     \
+       libgdl/libgdltypebuiltins.c     \
+       libgdl/libgdlmarshal.h          \
+       libgdl/libgdlmarshal.c          \
+       libgdl/libgdl.h         
diff --git a/src/libgdl/README.gdl-dock b/src/libgdl/README.gdl-dock
new file mode 100644 (file)
index 0000000..113926d
--- /dev/null
@@ -0,0 +1,184 @@
+This file is meant to contain a little documentation about the GdlDock
+and related widgets.  It's incomplete and probably a bit out of date.
+And it probably belongs to a doc/ subdirectory.
+
+Please send comments to the devtools list (gnome-devtools@gnome.org)
+and report bugs to bugzilla (bugzilla.gnome.org).  Also check the todo
+list at the end of this document.
+
+Have fun,
+Gustavo
+
+
+Overview
+--------
+
+The GdlDock is a hierarchical based docking widget.  It is composed of
+widgets derived from the abstract class GdlDockObject which defines
+the basic interface for docking widgets on top of others.  
+
+The toplevel entries for docks are GdlDock widgets, which in turn hold
+a tree of GdlDockItem widgets.  For the toplevel docks to be able to
+interact with each other (when the user drags items from one place to
+another) they're all kept in a user-invisible and automatic object
+called the master.  To participate in docking operations every
+GdlDockObject must have the same master (the binding to the master is
+done automatically).  The master also keeps track of the manual items
+(mostly those created with gdl_dock_*_new functions) which are in the
+dock.
+
+Layout loading/saving service is provided by a separate object called
+GdlDockLayout.  Currently it stores information in XML format, but
+another backend could be easily written.  To keep the external XML
+file in sync with the dock, monitor the "dirty" property of the layout
+object and call gdl_dock_layout_save_to_file when this changes to
+TRUE.  No other action is required (the layout_changed is monitored by
+the layout object for you now).
+
+
+GdlDockObject
+=============
+
+A DockObject has:
+
+- a master, which manages all the objects in a dock ring ("master"
+  property, construct only).
+
+- a name.  If the object is manual the name can be used to recall the
+  object from any other object in the ring ("name" property).
+
+- a long, descriptive name ("long-name" property).
+
+A DockObject can be:
+
+- automatic or manual.  If it's automatic it means the lifecycle of
+  the object is entirely managed by the dock master.  If it's manual
+  in general means the user specified such object, and so it's not to
+  be destroyed automatically at any time.
+
+- frozen.  In this case any call to reduce on the object has no
+  immediate effect.  When the object is later thawn, any pending
+  reduce calls are made (maybe leading to the destruction of the
+  object).
+
+- attached or detached.  In general for dock items, being attached
+  will mean the item has its widget->parent set.  For docks it will
+  mean they belong to some dock master's ring.  In general, automatic
+  objects which are detached will be destroyed (unless frozen).
+
+- bound to a master or free.  In order to participate in dock
+  operations, each dock object must be bound to a dock master (which
+  is a separate, non-gui object).  In general, bindings are treated
+  automatically by the object, and this is entirely true for automatic
+  objects.  For manual objects, the master holds an additional
+  reference and has structures to store and recall them by nick names.
+  Normally, a manual object will only be destroyed when it's unbound
+  from the master.
+
+- simple or compound.  This actually depends on the subclass of the
+  dock object.  The difference is made so we can put restrictions in
+  how the objects are docked on top of another (e.g. you can't dock a
+  compound object inside a notebook).  If you look at the whole
+  docking layout as a tree, simple objects are the leaves, while all
+  the interior nodes are compound.
+
+- reduced.  This is only meaningful for compound objects.  If the
+  number of contained items has decreased to one the compound type
+  object is no longer necessary to hold the child.  In this case the
+  child is reattached to the object's parent.  If the number of
+  contained items has reached zero, the object is detached and reduce
+  is called on its parent.  For toplevel docks, the object is only
+  detached if it's automatic.  In any case, the future of the object
+  itself depends on whether it's automatic or manual.
+
+- requested to possibly dock another object.  Depending on the
+  type's behavior, the object can accept or reject this request.  If
+  it accepts it, it should fill in some additional information
+  regarding how it will host the peer object.
+
+- asked to dock another object.  Depending on the object's internal
+  structure and behavior two options can be taken: to dock the object
+  directly (e.g. a notebook docking another object); or to create an
+  automatic compound object which will be attached in place of the
+  actual object, and will host both the original object and the
+  requestor (e.g. a simple item docking another simple item, which
+  should create a paned/notebook).  The type of the new object will be
+  decided by the original objet based on the docking position.
+
+
+DETACHING: the action by which an object is unparented.  The object is
+then ready to be docked in some other place.  Newly created objects
+are always detached, except for toplevels (which are created attached
+by default).  An automatic object which is detached gets destroyed
+afterwards, since its ref count drops to zero.  Floating automatic
+toplevels never reach a zero ref count when detached, since the
+GtkWindow always keeps a reference to it (and the GtkWindow has a user
+reference).  That's why floating automatic toplevels are destroyed
+when reduced.
+
+REDUCING: for compound objects, when the number of contained children
+drops to one or zero, the container is no longer necessary.  In this
+case, the object is detached, and any remaining child is reattached to
+the object's former parent.  The limit for toplevels is one for
+automatic objects and zero for manual (i.e. they can even be empty).
+For simple (not compound) objects reducing doesn't make sense.
+
+UNBINDING: to participate in a dock ring, every object must be bound
+to a master.  The master connects to dock item signals and keeps a
+list of bound toplevels.  Additionally, a reference is kept for manual
+objects (this is so the user doesn't need to keep track of them, but
+can perform operations like hiding and such).
+
+
+
+GdlDock
+=======
+
+- Properties:
+
+  "floating" (gboolean, construct-only): whether the dock is floating in
+  its own window or not.
+
+  "default-title" (gchar, read-write): title for new floating docks.
+  This property is proxied to the master, which truly holds it.  
+
+The title for the floating docks is: the user supplied title
+(GdlDockObject's long_name property) if it's set, the default title
+(from the master) or an automatically generated title.
+
+
+- Signals:
+
+  "layout-changed": emitted when the user changed the layout of the
+  dock somehow.
+
+
+TODO LIST
+=========
+
+- Functionality for the item grip: provide a11y
+
+- Implement notebook tab reordering
+
+- Implement dock bands for toolbars and menus.
+
+- A dock-related thing is to build resizable toolbars (something like
+  the ones Windows have, where the buttons are reflowed according to
+  the space available).
+
+- Redesign paneds so they can hold more than two items and resize all
+  of them at once by using the handle (see if gimpdock does that).
+
+- Find a way to allow the merging of menu items to the item's popup
+  menu.  Also, there should be a way for the master to insert some
+  menu items.
+
+- Bonobo UI synchronizer.
+
+- Item behavoirs: implement the ones missing and maybe think more of
+  them (e.g. positions where it's possible to dock the item, etc.)
+
+- Make a nicer dragbar for the items, with buttons for undocking,
+  closing, hidding, etc. (See sodipodi, kdevelop)
+
+
diff --git a/src/libgdl/gdl-combo-button.c b/src/libgdl/gdl-combo-button.c
new file mode 100644 (file)
index 0000000..6414a81
--- /dev/null
@@ -0,0 +1,383 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- 
+ * gdl-combo-button.c
+ * 
+ * Copyright (C) 2003 Jeroen Zwartepoorte
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include "gdl-tools.h"
+#include "gdl-combo-button.h"
+
+struct _GdlComboButtonPrivate {
+       GtkWidget *default_button;
+       GtkWidget *image;
+       GtkWidget *label;
+       GtkWidget *menu_button;
+       GtkWidget *menu;
+       gboolean   menu_popped_up;
+};
+
+GDL_CLASS_BOILERPLATE (GdlComboButton, gdl_combo_button, GtkHBox, GTK_TYPE_HBOX);
+
+static void
+default_button_clicked_cb (GtkButton *button,
+                          gpointer user_data)
+{
+       GdlComboButton *combo;
+       GdlComboButtonPrivate *priv;
+
+       combo = GDL_COMBO_BUTTON (user_data);
+       priv = combo->priv;
+
+       if (!priv->menu_popped_up)
+               g_signal_emit_by_name (G_OBJECT (combo),
+                                      "activate-default", NULL);
+}
+
+static gboolean
+default_button_press_event_cb (GtkWidget *widget,
+                              GdkEventButton *event,
+                              gpointer user_data)
+{
+       GdlComboButton *combo_button;
+       GdlComboButtonPrivate *priv;
+
+       combo_button = GDL_COMBO_BUTTON (user_data);
+       priv = combo_button->priv;
+
+       if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
+               GTK_BUTTON (priv->menu_button)->button_down = TRUE;
+               gtk_button_pressed (GTK_BUTTON (priv->menu_button));
+       }
+
+       return FALSE;
+}
+
+static gboolean
+default_button_release_event_cb (GtkWidget *widget,
+                                GdkEventButton *event,
+                                gpointer user_data)
+{
+       GdlComboButton *combo_button;
+       GdlComboButtonPrivate *priv;
+
+       combo_button = GDL_COMBO_BUTTON (user_data);
+       priv = combo_button->priv;
+
+       if (event->button == 1) {
+               gtk_button_released (GTK_BUTTON (priv->menu_button));
+       }
+
+       return FALSE;
+}
+
+static gboolean
+button_enter_notify_cb (GtkWidget *widget,
+                       GdkEventCrossing *event,
+                       gpointer user_data)
+{
+       GdlComboButton *combo_button;
+       GdlComboButtonPrivate *priv;
+
+       combo_button = GDL_COMBO_BUTTON (user_data);
+       priv = combo_button->priv;
+
+       if (event->detail != GDK_NOTIFY_INFERIOR) {
+               GTK_BUTTON (priv->default_button)->in_button = TRUE;
+               GTK_BUTTON (priv->menu_button)->in_button = TRUE;
+               gtk_button_enter (GTK_BUTTON (priv->default_button));
+               gtk_button_enter (GTK_BUTTON (priv->menu_button));
+       }
+
+       return TRUE;
+}
+
+static gboolean
+button_leave_notify_cb (GtkWidget *widget,
+                       GdkEventCrossing *event,
+                       gpointer user_data)
+{
+       GdlComboButton *combo_button;
+       GdlComboButtonPrivate *priv;
+
+       combo_button = GDL_COMBO_BUTTON (user_data);
+       priv = combo_button->priv;
+
+       if (priv->menu_popped_up)
+               return TRUE;
+
+       if (event->detail != GDK_NOTIFY_INFERIOR) {
+               GTK_BUTTON (priv->default_button)->in_button = FALSE;
+               GTK_BUTTON (priv->menu_button)->in_button = FALSE;
+               gtk_button_leave (GTK_BUTTON (priv->default_button));
+               gtk_button_leave (GTK_BUTTON (priv->menu_button));
+       }
+
+       return TRUE;
+}
+
+static void
+menu_position_func (GtkMenu *menu,
+                   gint *x_return,
+                   gint *y_return,
+                   gboolean *push_in,
+                   gpointer user_data)
+{
+       GdlComboButton *combo_button;
+       GdlComboButtonPrivate *priv;
+       GtkAllocation *allocation;
+
+       combo_button = GDL_COMBO_BUTTON (user_data);
+       priv = combo_button->priv;
+       allocation = &(priv->default_button->allocation);
+
+       gdk_window_get_origin (priv->default_button->window, x_return, y_return);
+
+       *x_return += allocation->x; 
+       *y_return += allocation->height;
+}
+
+static gboolean
+menu_button_press_event_cb (GtkWidget *widget,
+                           GdkEventButton *event,
+                           gpointer user_data)
+{
+       GdlComboButton *combo_button;
+       GdlComboButtonPrivate *priv;
+
+       combo_button = GDL_COMBO_BUTTON (user_data);
+       priv = combo_button->priv;
+
+       if (event->type == GDK_BUTTON_PRESS && 
+           (event->button == 1 || event->button == 3)) {
+               GTK_BUTTON (priv->menu_button)->button_down = TRUE;
+
+               gtk_button_pressed (GTK_BUTTON (priv->menu_button));
+
+               priv->menu_popped_up = TRUE;
+               gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL,
+                               menu_position_func, combo_button,
+                               event->button, event->time);
+       }
+
+       return TRUE;
+}
+
+static void
+menu_deactivate_cb (GtkMenuShell *menu_shell,
+                   gpointer user_data)
+{
+       GdlComboButton *combo_button;
+       GdlComboButtonPrivate *priv;
+
+       combo_button = GDL_COMBO_BUTTON (user_data);
+       priv = combo_button->priv;
+
+       priv->menu_popped_up = FALSE;
+
+       GTK_BUTTON (priv->menu_button)->button_down = FALSE;
+       GTK_BUTTON (priv->menu_button)->in_button = FALSE;
+       GTK_BUTTON (priv->default_button)->in_button = FALSE;
+       gtk_button_leave (GTK_BUTTON (priv->menu_button));
+       gtk_button_leave (GTK_BUTTON (priv->default_button));
+       gtk_button_clicked (GTK_BUTTON (priv->menu_button));
+}
+
+static void
+menu_detacher (GtkWidget *widget,
+              GtkMenu *menu)
+{
+       GdlComboButton *combo_button;
+
+       combo_button = GDL_COMBO_BUTTON (widget);
+
+       g_signal_handlers_disconnect_by_func (G_OBJECT (menu),
+                                             menu_deactivate_cb,
+                                             combo_button);
+       combo_button->priv->menu = NULL;
+}
+
+static void
+gdl_combo_button_destroy (GtkObject *object)
+{
+       GdlComboButton *combo_button;
+       GdlComboButtonPrivate *priv;
+
+       combo_button = GDL_COMBO_BUTTON (object);
+       priv = combo_button->priv;
+
+       if (priv) {
+               g_free (priv);
+               combo_button->priv = NULL;
+       }
+       
+       (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void
+gdl_combo_button_class_init (GdlComboButtonClass *klass)
+{
+       GtkObjectClass *object_class;
+       GtkWidgetClass *widget_class;
+
+       parent_class = g_type_class_peek_parent (klass);
+       object_class = GTK_OBJECT_CLASS (klass);
+       widget_class = GTK_WIDGET_CLASS (klass);
+
+       object_class->destroy = gdl_combo_button_destroy;
+
+       g_signal_new ("activate-default",
+                     G_TYPE_FROM_CLASS (klass),
+                     G_SIGNAL_RUN_FIRST,
+                     G_STRUCT_OFFSET (GdlComboButtonClass, activate_default),
+                     NULL, NULL,
+                     g_cclosure_marshal_VOID__VOID,
+                     G_TYPE_NONE, 0);
+}
+
+static void
+gdl_combo_button_instance_init (GdlComboButton *combo_button)
+{
+       GdlComboButtonPrivate *priv;
+       GtkWidget *hbox, *align, *arrow;
+
+       priv = g_new (GdlComboButtonPrivate, 1);
+       combo_button->priv = priv;
+
+       priv->menu = NULL;
+       priv->menu_popped_up = FALSE;
+
+       priv->default_button = gtk_button_new ();
+       gtk_button_set_relief (GTK_BUTTON (priv->default_button), GTK_RELIEF_NONE);
+
+       /* Following code copied from gtk_button_construct_child. */
+       priv->label = gtk_label_new ("");
+       gtk_label_set_use_underline (GTK_LABEL (priv->label), TRUE);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (priv->label),
+                                      priv->default_button);
+      
+       priv->image = gtk_image_new ();
+       hbox = gtk_hbox_new (FALSE, 2);
+
+       align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+      
+       gtk_box_pack_start (GTK_BOX (hbox), priv->image, FALSE, FALSE, 0);
+       gtk_box_pack_end (GTK_BOX (hbox), priv->label, FALSE, FALSE, 0);
+      
+       gtk_container_add (GTK_CONTAINER (priv->default_button), align);
+       gtk_container_add (GTK_CONTAINER (align), hbox);
+       /* End copied block. */
+
+       gtk_box_pack_start (GTK_BOX (combo_button), priv->default_button,
+                           FALSE, FALSE, 0);
+       gtk_widget_show_all (priv->default_button);
+
+       priv->menu_button = gtk_button_new ();
+       gtk_button_set_relief (GTK_BUTTON (priv->menu_button), GTK_RELIEF_NONE);
+       arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+       gtk_container_add (GTK_CONTAINER (priv->menu_button), arrow);
+       gtk_box_pack_start (GTK_BOX (combo_button), priv->menu_button, FALSE,
+                           FALSE, 0);
+       gtk_widget_show_all (priv->menu_button);
+
+       /* Default button. */
+       g_signal_connect (G_OBJECT (priv->default_button), "clicked",
+                         G_CALLBACK (default_button_clicked_cb), combo_button);
+       g_signal_connect (G_OBJECT (priv->default_button), "button_press_event",
+                         G_CALLBACK (default_button_press_event_cb), combo_button);
+       g_signal_connect (G_OBJECT (priv->default_button), "button_release_event",
+                         G_CALLBACK (default_button_release_event_cb), combo_button);
+       g_signal_connect (G_OBJECT (priv->default_button), "enter_notify_event",
+                         G_CALLBACK (button_enter_notify_cb), combo_button);
+       g_signal_connect (G_OBJECT (priv->default_button), "leave_notify_event",
+                         G_CALLBACK (button_leave_notify_cb), combo_button);
+
+       /* Menu button. */
+       g_signal_connect (G_OBJECT (priv->menu_button), "button_press_event",
+                         G_CALLBACK (menu_button_press_event_cb), combo_button);
+       g_signal_connect (G_OBJECT (priv->menu_button), "enter_notify_event",
+                         G_CALLBACK (button_enter_notify_cb), combo_button);
+       g_signal_connect (G_OBJECT (priv->menu_button), "leave_notify_event",
+                         G_CALLBACK (button_leave_notify_cb), combo_button);
+}
+
+GtkWidget *
+gdl_combo_button_new (void)
+{
+       GtkWidget *combo_button;
+       
+       combo_button = GTK_WIDGET (g_object_new (GDL_TYPE_COMBO_BUTTON, NULL));
+
+       return combo_button;
+}
+
+void
+gdl_combo_button_set_icon (GdlComboButton *combo_button,
+                          GdkPixbuf *pixbuf)
+{
+       GdlComboButtonPrivate *priv;
+
+       g_return_if_fail (GDL_IS_COMBO_BUTTON (combo_button));
+       g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
+
+       priv = combo_button->priv;
+
+       gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
+}
+
+void
+gdl_combo_button_set_label (GdlComboButton *combo_button,
+                           const gchar *label)
+{
+       GdlComboButtonPrivate *priv;
+
+       g_return_if_fail (GDL_IS_COMBO_BUTTON (combo_button));
+       g_return_if_fail (label != NULL);
+
+       priv = combo_button->priv;
+
+       gtk_label_set_text (GTK_LABEL (priv->label), label);
+}
+
+void
+gdl_combo_button_set_menu (GdlComboButton *combo_button,
+                          GtkMenu *menu)
+{
+       GdlComboButtonPrivate *priv;
+
+       g_return_if_fail (GDL_IS_COMBO_BUTTON (combo_button));
+       g_return_if_fail (GTK_IS_MENU (menu));
+
+       priv = combo_button->priv;
+
+       if (priv->menu != NULL)
+               gtk_menu_detach (GTK_MENU (priv->menu));
+
+       priv->menu = GTK_WIDGET (menu);
+       if (menu == NULL)
+               return;
+
+       gtk_menu_attach_to_widget (menu, GTK_WIDGET (combo_button), menu_detacher);
+
+       g_signal_connect (G_OBJECT (menu), "deactivate",
+                         G_CALLBACK (menu_deactivate_cb), combo_button);
+}
diff --git a/src/libgdl/gdl-combo-button.h b/src/libgdl/gdl-combo-button.h
new file mode 100644 (file)
index 0000000..6e80af0
--- /dev/null
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- 
+ * gdl-combo-button.h
+ * 
+ * Copyright (C) 2003 Jeroen Zwartepoorte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _GDL_COMBO_BUTTON_H_
+#define _GDL_COMBO_BUTTON_H_
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gtk/gtkhbox.h>
+#include <gtk/gtkmenu.h>
+
+G_BEGIN_DECLS
+
+#define GDL_TYPE_COMBO_BUTTON           (gdl_combo_button_get_type ())
+#define GDL_COMBO_BUTTON(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDL_TYPE_COMBO_BUTTON, GdlComboButton))
+#define GDL_COMBO_BUTTON_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST ((klass), GDL_TYPE_COMBO_BUTTON, GdlComboButtonClass))
+#define GDL_IS_COMBO_BUTTON(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDL_TYPE_COMBO_BUTTON))
+#define GDL_IS_COMBO_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), GDL_TYPE_COMBO_BUTTON))
+
+typedef struct _GdlComboButton        GdlComboButton;
+typedef struct _GdlComboButtonPrivate GdlComboButtonPrivate;
+typedef struct _GdlComboButtonClass   GdlComboButtonClass;
+
+struct _GdlComboButton {
+       GtkHBox parent;
+
+       GdlComboButtonPrivate *priv;
+};
+
+struct _GdlComboButtonClass {
+       GtkHBoxClass parent_class;
+
+       /* Signals. */
+       void (* activate_default) (GdlComboButton *combo_button);
+};
+
+GType      gdl_combo_button_get_type       (void);
+GtkWidget *gdl_combo_button_new            (void);
+
+void       gdl_combo_button_set_icon   (GdlComboButton *combo_button,
+                                       GdkPixbuf      *pixbuf);
+void       gdl_combo_button_set_label  (GdlComboButton *combo_button,
+                                       const gchar    *label);
+void       gdl_combo_button_set_menu   (GdlComboButton *combo_button,
+                                       GtkMenu        *menu);
+
+G_END_DECLS
+
+#endif /* _GDL_COMBO_BUTTON_H_ */
diff --git a/src/libgdl/gdl-dock-bar.c b/src/libgdl/gdl-dock-bar.c
new file mode 100644 (file)
index 0000000..c4882e7
--- /dev/null
@@ -0,0 +1,976 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 
+ *
+ * This file is part of the GNOME Devtools Libraries.
+ *
+ * Copyright (C) 2003 Jeroen Zwartepoorte <jeroen@xs4all.nl>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gdl-i18n.h"
+#include <stdlib.h>
+#include <string.h>
+
+#include "gdl-tools.h"
+#include "gdl-dock.h"
+#include "gdl-dock-master.h"
+#include "gdl-dock-bar.h"
+#include "libgdltypebuiltins.h"
+
+enum {
+    PROP_0,
+    PROP_MASTER,
+    PROP_DOCKBAR_STYLE
+};
+
+/* ----- Private prototypes ----- */
+
+static void  gdl_dock_bar_class_init      (GdlDockBarClass *klass);
+static void  gdl_dock_bar_instance_init   (GdlDockBar      *dockbar);
+
+static void  gdl_dock_bar_get_property    (GObject         *object,
+                                           guint            prop_id,
+                                           GValue          *value,
+                                           GParamSpec      *pspec);
+static void  gdl_dock_bar_set_property    (GObject         *object,
+                                           guint            prop_id,
+                                           const GValue    *value,
+                                           GParamSpec      *pspec);
+
+static void  gdl_dock_bar_destroy         (GtkObject       *object);
+
+static void  gdl_dock_bar_attach          (GdlDockBar      *dockbar,
+                                           GdlDockMaster   *master);
+static void gdl_dock_bar_remove_item      (GdlDockBar      *dockbar,
+                                           GdlDockItem     *item);
+
+/* ----- Class variables and definitions ----- */
+
+struct _GdlDockBarPrivate {
+    GdlDockMaster   *master;
+    GSList          *items;
+    GtkTooltips     *tooltips;
+    GtkOrientation   orientation;
+    GdlDockBarStyle  dockbar_style;
+};
+
+/* ----- Private functions ----- */
+
+GDL_CLASS_BOILERPLATE (GdlDockBar, gdl_dock_bar, GtkBox, GTK_TYPE_BOX)
+
+static void gdl_dock_bar_size_request (GtkWidget *widget,
+                                      GtkRequisition *requisition );
+static void gdl_dock_bar_size_allocate (GtkWidget *widget,
+                                      GtkAllocation *allocation );
+static void gdl_dock_bar_size_vrequest (GtkWidget *widget,
+                                      GtkRequisition *requisition );
+static void gdl_dock_bar_size_vallocate (GtkWidget *widget,
+                                      GtkAllocation *allocation );
+static void gdl_dock_bar_size_hrequest (GtkWidget *widget,
+                                      GtkRequisition *requisition );
+static void gdl_dock_bar_size_hallocate (GtkWidget *widget,
+                                      GtkAllocation *allocation );
+static void update_dock_items (GdlDockBar *dockbar, gboolean full_update);
+
+void
+gdl_dock_bar_class_init (GdlDockBarClass *klass)
+{
+    GObjectClass       *g_object_class;
+    GtkObjectClass     *gtk_object_class;
+    GtkWidgetClass     *widget_class;
+    
+    g_object_class = G_OBJECT_CLASS (klass);
+    gtk_object_class = GTK_OBJECT_CLASS (klass);
+
+    g_object_class->get_property = gdl_dock_bar_get_property;
+    g_object_class->set_property = gdl_dock_bar_set_property;
+
+    gtk_object_class->destroy = gdl_dock_bar_destroy;
+
+    g_object_class_install_property (
+        g_object_class, PROP_MASTER,
+        g_param_spec_object ("master", _("Master"),
+                             _("GdlDockMaster object which the dockbar widget "
+                               "is attached to"),
+                             GDL_TYPE_DOCK_MASTER, 
+                             G_PARAM_READWRITE));
+
+    g_object_class_install_property (
+        g_object_class, PROP_DOCKBAR_STYLE,
+        g_param_spec_enum ("dockbar-style", _("Dockbar style"),
+                           _("Dockbar style to show items on it"),
+                           GDL_TYPE_DOCK_BAR_STYLE,
+                           GDL_DOCK_BAR_BOTH,
+                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+    widget_class = GTK_WIDGET_CLASS (klass);
+    widget_class->size_request = gdl_dock_bar_size_request;
+    widget_class->size_allocate = gdl_dock_bar_size_allocate;
+}
+
+static void
+gdl_dock_bar_instance_init (GdlDockBar *dockbar)
+{
+    dockbar->_priv = g_new0 (GdlDockBarPrivate, 1);
+    dockbar->_priv->master = NULL;
+    dockbar->_priv->items = NULL;
+    dockbar->_priv->tooltips = gtk_tooltips_new ();
+    dockbar->_priv->orientation = GTK_ORIENTATION_VERTICAL;
+    dockbar->_priv->dockbar_style = GDL_DOCK_BAR_BOTH;
+    g_object_ref (dockbar->_priv->tooltips);
+    gtk_object_sink (GTK_OBJECT (dockbar->_priv->tooltips));
+}
+
+static void
+gdl_dock_bar_get_property (GObject         *object,
+                           guint            prop_id,
+                           GValue          *value,
+                           GParamSpec      *pspec)
+{
+    GdlDockBar *dockbar = GDL_DOCK_BAR (object);
+
+    switch (prop_id) {
+        case PROP_MASTER:
+            g_value_set_object (value, dockbar->_priv->master);
+            break;
+        case PROP_DOCKBAR_STYLE:
+            g_value_set_enum (value, dockbar->_priv->dockbar_style);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    };
+}
+
+static void
+gdl_dock_bar_set_property (GObject         *object,
+                           guint            prop_id,
+                           const GValue    *value,
+                           GParamSpec      *pspec)
+{
+    GdlDockBar *dockbar = GDL_DOCK_BAR (object);
+
+    switch (prop_id) {
+        case PROP_MASTER:
+            gdl_dock_bar_attach (dockbar, g_value_get_object (value));
+            break;
+        case PROP_DOCKBAR_STYLE:
+            dockbar->_priv->dockbar_style = g_value_get_enum (value);
+            update_dock_items (dockbar, TRUE);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    };
+}
+
+static void
+on_dock_item_foreach_disconnect (GdlDockItem *item, GdlDockBar *dock_bar)
+{
+    g_signal_handlers_disconnect_by_func (item, gdl_dock_bar_remove_item,
+                                          dock_bar);
+}
+
+static void
+gdl_dock_bar_destroy (GtkObject *object)
+{
+    GdlDockBar *dockbar = GDL_DOCK_BAR (object);
+
+    if (dockbar->_priv) {
+        GdlDockBarPrivate *priv = dockbar->_priv;
+
+        if (priv->items) {
+            g_slist_foreach (priv->items,
+                             (GFunc) on_dock_item_foreach_disconnect,
+                             object);
+            g_slist_free (priv->items);
+        }
+        
+        if (priv->master) {
+            g_signal_handlers_disconnect_matched (priv->master,
+                                                  G_SIGNAL_MATCH_DATA,
+                                                  0, 0, NULL, NULL, dockbar);
+            g_object_unref (priv->master);
+            priv->master = NULL;
+        }
+
+        if (priv->tooltips) {
+            g_object_unref (priv->tooltips);
+            priv->tooltips = NULL;
+        }
+        
+        dockbar->_priv = NULL;
+
+        g_free (priv);
+    }
+    
+    GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
+}
+
+static void
+gdl_dock_bar_remove_item (GdlDockBar  *dockbar,
+                          GdlDockItem *item)
+{
+    GdlDockBarPrivate *priv;
+    GtkWidget *button;
+
+    g_return_if_fail (GDL_IS_DOCK_BAR (dockbar));
+    g_return_if_fail (GDL_IS_DOCK_ITEM (item));
+
+    priv = dockbar->_priv;
+
+    if (g_slist_index (priv->items, item) == -1) {
+        g_warning ("Item has not been added to the dockbar");
+        return;
+    }
+    
+    priv->items = g_slist_remove (priv->items, item);
+    
+    button = g_object_get_data (G_OBJECT (item), "GdlDockBarButton");
+    g_assert (button != NULL);
+    gtk_container_remove (GTK_CONTAINER (dockbar), button);
+    g_object_set_data (G_OBJECT (item), "GdlDockBarButton", NULL);
+    g_signal_handlers_disconnect_by_func (item,
+                                          G_CALLBACK (gdl_dock_bar_remove_item),
+                                          dockbar);
+}
+
+static void
+gdl_dock_bar_item_clicked (GtkWidget   *button,
+                           GdlDockItem *item)
+{
+    GdlDockBar *dockbar;
+    GdlDockObject *controller;
+
+    g_return_if_fail (item != NULL);
+    
+    dockbar = g_object_get_data (G_OBJECT (item), "GdlDockBar");
+    g_assert (dockbar != NULL);
+    g_object_set_data (G_OBJECT (item), "GdlDockBar", NULL);
+
+    controller = gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (item));
+
+    GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_ICONIFIED);
+    gdl_dock_item_show_item (item);
+    gdl_dock_bar_remove_item (dockbar, item);
+    gtk_widget_queue_resize (GTK_WIDGET (controller));
+}
+
+static void
+gdl_dock_bar_add_item (GdlDockBar  *dockbar,
+                       GdlDockItem *item)
+{
+    GdlDockBarPrivate *priv;
+    GtkWidget *button;
+    gchar *stock_id;
+    gchar *name;
+    GdkPixbuf *pixbuf_icon;
+    GtkWidget *image, *box, *label;
+
+    g_return_if_fail (GDL_IS_DOCK_BAR (dockbar));
+    g_return_if_fail (GDL_IS_DOCK_ITEM (item));
+
+    priv = dockbar->_priv;
+
+    if (g_slist_index (priv->items, item) != -1) {
+        g_warning ("Item has already been added to the dockbar");
+        return;
+    }
+
+    priv->items = g_slist_append (priv->items, item);
+    
+    /* Create a button for the item. */
+    button = gtk_button_new ();
+    gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+    
+    if (dockbar->_priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+        box = gtk_hbox_new (FALSE, 0);
+    else
+        box = gtk_vbox_new (FALSE, 0);
+    
+    g_object_get (item, "stock-id", &stock_id, "pixbuf-icon", &pixbuf_icon,
+                  "long-name", &name, NULL);
+
+    if (dockbar->_priv->dockbar_style == GDL_DOCK_BAR_TEXT ||
+        dockbar->_priv->dockbar_style == GDL_DOCK_BAR_BOTH) {
+        label = gtk_label_new (name);
+        if (dockbar->_priv->orientation == GTK_ORIENTATION_VERTICAL)
+            gtk_label_set_angle (GTK_LABEL (label), 90);
+        gtk_box_pack_start_defaults (GTK_BOX (box), label);
+    }
+    
+    /* FIXME: For now AUTO behaves same as BOTH */
+    
+    if (dockbar->_priv->dockbar_style == GDL_DOCK_BAR_ICONS ||
+        dockbar->_priv->dockbar_style == GDL_DOCK_BAR_BOTH ||
+        dockbar->_priv->dockbar_style == GDL_DOCK_BAR_AUTO) {
+        if (stock_id) {
+            image = gtk_image_new_from_stock (stock_id,
+                                              GTK_ICON_SIZE_SMALL_TOOLBAR);
+            g_free (stock_id);
+        } else if (pixbuf_icon) {
+            image = gtk_image_new_from_pixbuf (pixbuf_icon);
+        } else {
+            image = gtk_image_new_from_stock (GTK_STOCK_NEW,
+                                              GTK_ICON_SIZE_SMALL_TOOLBAR);
+        }
+        gtk_box_pack_start_defaults (GTK_BOX (box), image);
+    }
+    
+    gtk_container_add (GTK_CONTAINER (button), box);
+    gtk_box_pack_start (GTK_BOX (dockbar), button, FALSE, FALSE, 0);
+
+    gtk_tooltips_set_tip (priv->tooltips, button, name, name);
+    g_free (name);
+
+    g_object_set_data (G_OBJECT (item), "GdlDockBar", dockbar);
+    g_object_set_data (G_OBJECT (item), "GdlDockBarButton", button);
+    g_signal_connect (G_OBJECT (button), "clicked",
+                      G_CALLBACK (gdl_dock_bar_item_clicked), item);
+
+    gtk_widget_show_all (button);
+    
+    /* Set up destroy notify */
+    g_signal_connect_swapped (item, "destroy",
+                              G_CALLBACK (gdl_dock_bar_remove_item),
+                              dockbar);
+}
+
+static void
+build_list (GdlDockObject *object, GList **list)
+{
+    /* add only items, not toplevels */
+    if (GDL_IS_DOCK_ITEM (object))
+        *list = g_list_prepend (*list, object);
+}
+
+static void
+update_dock_items (GdlDockBar *dockbar, gboolean full_update)
+{
+    GdlDockMaster *master;
+    GList *items, *l;
+
+    g_return_if_fail (dockbar != NULL);
+    
+    if (!dockbar->_priv->master)
+        return;
+
+    master = dockbar->_priv->master;
+    
+    /* build items list */
+    items = NULL;
+    gdl_dock_master_foreach (master, (GFunc) build_list, &items);
+    
+    if (!full_update) {
+        for (l = items; l != NULL; l = l->next) {
+            GdlDockItem *item = GDL_DOCK_ITEM (l->data);
+            
+            if (g_slist_index (dockbar->_priv->items, item) != -1 &&
+                !GDL_DOCK_ITEM_ICONIFIED (item))
+                gdl_dock_bar_remove_item (dockbar, item);
+            else if (g_slist_index (dockbar->_priv->items, item) == -1 &&
+                GDL_DOCK_ITEM_ICONIFIED (item))
+                gdl_dock_bar_add_item (dockbar, item);
+        }
+    } else {
+        for (l = items; l != NULL; l = l->next) {
+            GdlDockItem *item = GDL_DOCK_ITEM (l->data);
+            
+            if (g_slist_index (dockbar->_priv->items, item) != -1)
+                gdl_dock_bar_remove_item (dockbar, item);
+            if (GDL_DOCK_ITEM_ICONIFIED (item))
+                gdl_dock_bar_add_item (dockbar, item);
+        }
+    }
+    g_list_free (items);
+}
+
+static void
+gdl_dock_bar_layout_changed_cb (GdlDockMaster *master,
+                                GdlDockBar    *dockbar)
+{
+    update_dock_items (dockbar, FALSE);
+}
+
+static void
+gdl_dock_bar_attach (GdlDockBar    *dockbar,
+                     GdlDockMaster *master)
+{
+    g_return_if_fail (dockbar != NULL);
+    g_return_if_fail (master == NULL || GDL_IS_DOCK_MASTER (master));
+    
+    if (dockbar->_priv->master) {
+        g_signal_handlers_disconnect_matched (dockbar->_priv->master,
+                                              G_SIGNAL_MATCH_DATA,
+                                              0, 0, NULL, NULL, dockbar);
+        g_object_unref (dockbar->_priv->master);
+    }
+    
+    dockbar->_priv->master = master;
+    if (dockbar->_priv->master) {
+        g_object_ref (dockbar->_priv->master);
+        g_signal_connect (dockbar->_priv->master, "layout-changed",
+                          G_CALLBACK (gdl_dock_bar_layout_changed_cb),
+                          dockbar);
+    }
+
+    update_dock_items (dockbar, FALSE);
+}
+
+static void gdl_dock_bar_size_request (GtkWidget *widget,
+                                      GtkRequisition *requisition )
+{
+    GdlDockBar *dockbar;
+
+    dockbar = GDL_DOCK_BAR (widget);
+    
+    /* default to vertical for unknown values */
+    switch (dockbar->_priv->orientation) {
+       case GTK_ORIENTATION_HORIZONTAL:
+               gdl_dock_bar_size_hrequest (widget, requisition);
+               break;
+       case GTK_ORIENTATION_VERTICAL:
+       default:
+               gdl_dock_bar_size_vrequest (widget, requisition);
+               break;
+    }
+}
+
+static void gdl_dock_bar_size_allocate (GtkWidget *widget,
+                                      GtkAllocation *allocation )
+{
+    GdlDockBar *dockbar;
+
+    dockbar = GDL_DOCK_BAR (widget);
+    
+    /* default to vertical for unknown values */
+    switch (dockbar->_priv->orientation) {
+       case GTK_ORIENTATION_HORIZONTAL:
+               gdl_dock_bar_size_hallocate (widget, allocation);
+               break;
+       case GTK_ORIENTATION_VERTICAL:
+       default:
+               gdl_dock_bar_size_vallocate (widget, allocation);
+               break;
+    }
+}
+
+static void gdl_dock_bar_size_vrequest (GtkWidget *widget,
+                                      GtkRequisition *requisition )
+{
+  GtkBox *box;
+  GtkBoxChild *child;
+  GtkRequisition child_requisition;
+  GList *children;
+  gint nvis_children;
+  gint height;
+
+  box = GTK_BOX (widget);
+  requisition->width = 0;
+  requisition->height = 0;
+  nvis_children = 0;
+
+  children = box->children;
+  while (children)
+    {
+      child = children->data;
+      children = children->next;
+
+      if (GTK_WIDGET_VISIBLE (child->widget))
+       {
+         gtk_widget_size_request (child->widget, &child_requisition);
+
+         if (box->homogeneous)
+           {
+             height = child_requisition.height + child->padding * 2;
+             requisition->height = MAX (requisition->height, height);
+           }
+         else
+           {
+             requisition->height += child_requisition.height + child->padding * 2;
+           }
+
+         requisition->width = MAX (requisition->width, child_requisition.width);
+
+         nvis_children += 1;
+       }
+    }
+
+  if (nvis_children > 0)
+    {
+      if (box->homogeneous)
+       requisition->height *= nvis_children;
+      requisition->height += (nvis_children - 1) * box->spacing;
+    }
+
+  requisition->width += GTK_CONTAINER (box)->border_width * 2;
+  requisition->height += GTK_CONTAINER (box)->border_width * 2;
+
+}
+
+static void gdl_dock_bar_size_vallocate (GtkWidget     *widget,
+                       GtkAllocation *allocation)
+{
+  GtkBox *box;
+  GtkBoxChild *child;
+  GList *children;
+  GtkAllocation child_allocation;
+  gint nvis_children;
+  gint nexpand_children;
+  gint child_height;
+  gint height;
+  gint extra;
+  gint y;
+
+  box = GTK_BOX (widget);
+  widget->allocation = *allocation;
+
+  nvis_children = 0;
+  nexpand_children = 0;
+  children = box->children;
+
+  while (children)
+    {
+      child = children->data;
+      children = children->next;
+
+      if (GTK_WIDGET_VISIBLE (child->widget))
+       {
+         nvis_children += 1;
+         if (child->expand)
+           nexpand_children += 1;
+       }
+    }
+
+  if (nvis_children > 0)
+    {
+      if (box->homogeneous)
+       {
+         height = (allocation->height -
+                  GTK_CONTAINER (box)->border_width * 2 -
+                  (nvis_children - 1) * box->spacing);
+         extra = height / nvis_children;
+       }
+      else if (nexpand_children > 0)
+       {
+         height = (gint) allocation->height - (gint) widget->requisition.height;
+         extra = height / nexpand_children;
+       }
+      else
+       {
+         height = 0;
+         extra = 0;
+       }
+
+      y = allocation->y + GTK_CONTAINER (box)->border_width;
+      child_allocation.x = allocation->x + GTK_CONTAINER (box)->border_width;
+      child_allocation.width = MAX (1, (gint) allocation->width - (gint) GTK_CONTAINER (box)->border_width * 2);
+
+      children = box->children;
+      while (children)
+       {
+         child = children->data;
+         children = children->next;
+
+         if ((child->pack == GTK_PACK_START) && GTK_WIDGET_VISIBLE (child->widget))
+           {
+             if (box->homogeneous)
+               {
+                 if (nvis_children == 1)
+                   child_height = height;
+                 else
+                   child_height = extra;
+
+                 nvis_children -= 1;
+                 height -= extra;
+               }
+             else
+               {
+                 GtkRequisition child_requisition;
+
+                 gtk_widget_get_child_requisition (child->widget, &child_requisition);
+                 child_height = child_requisition.height + child->padding * 2;
+
+                 if (child->expand)
+                   {
+                     if (nexpand_children == 1)
+                       child_height += height;
+                     else
+                       child_height += extra;
+
+                     nexpand_children -= 1;
+                     height -= extra;
+                   }
+               }
+
+             if (child->fill)
+               {
+                 child_allocation.height = MAX (1, child_height - (gint)child->padding * 2);
+                 child_allocation.y = y + child->padding;
+               }
+             else
+               {
+                 GtkRequisition child_requisition;
+
+                 gtk_widget_get_child_requisition (child->widget, &child_requisition);
+                 child_allocation.height = child_requisition.height;
+                 child_allocation.y = y + (child_height - child_allocation.height) / 2;
+               }
+
+             gtk_widget_size_allocate (child->widget, &child_allocation);
+
+             y += child_height + box->spacing;
+           }
+       }
+
+      y = allocation->y + allocation->height - GTK_CONTAINER (box)->border_width;
+
+      children = box->children;
+      while (children)
+       {
+         child = children->data;
+         children = children->next;
+
+         if ((child->pack == GTK_PACK_END) && GTK_WIDGET_VISIBLE (child->widget))
+           {
+             GtkRequisition child_requisition;
+             gtk_widget_get_child_requisition (child->widget, &child_requisition);
+
+              if (box->homogeneous)
+                {
+                  if (nvis_children == 1)
+                    child_height = height;
+                  else
+                    child_height = extra;
+
+                  nvis_children -= 1;
+                  height -= extra;
+                }
+              else
+                {
+                 child_height = child_requisition.height + child->padding * 2;
+
+                  if (child->expand)
+                    {
+                      if (nexpand_children == 1)
+                        child_height += height;
+                      else
+                        child_height += extra;
+
+                      nexpand_children -= 1;
+                      height -= extra;
+                    }
+                }
+
+              if (child->fill)
+                {
+                  child_allocation.height = MAX (1, child_height - (gint)child->padding * 2);
+                  child_allocation.y = y + child->padding - child_height;
+                }
+              else
+                {
+                 child_allocation.height = child_requisition.height;
+                  child_allocation.y = y + (child_height - child_allocation.height) / 2 - child_height;
+                }
+
+              gtk_widget_size_allocate (child->widget, &child_allocation);
+
+              y -= (child_height + box->spacing);
+           }
+       }
+    }
+}
+
+static void gdl_dock_bar_size_hrequest (GtkWidget *widget,
+                                      GtkRequisition *requisition )
+{
+  GtkBox *box;
+  GtkBoxChild *child;
+  GList *children;
+  gint nvis_children;
+  gint width;
+
+  box = GTK_BOX (widget);
+  requisition->width = 0;
+  requisition->height = 0;
+  nvis_children = 0;
+
+  children = box->children;
+  while (children)
+    {
+      child = children->data;
+      children = children->next;
+
+      if (GTK_WIDGET_VISIBLE (child->widget))
+       {
+         GtkRequisition child_requisition;
+
+         gtk_widget_size_request (child->widget, &child_requisition);
+
+         if (box->homogeneous)
+           {
+             width = child_requisition.width + child->padding * 2;
+             requisition->width = MAX (requisition->width, width);
+           }
+         else
+           {
+             requisition->width += child_requisition.width + child->padding * 2;
+           }
+
+         requisition->height = MAX (requisition->height, child_requisition.height);
+
+         nvis_children += 1;
+       }
+    }
+
+  if (nvis_children > 0)
+    {
+      if (box->homogeneous)
+       requisition->width *= nvis_children;
+      requisition->width += (nvis_children - 1) * box->spacing;
+    }
+
+  requisition->width += GTK_CONTAINER (box)->border_width * 2;
+  requisition->height += GTK_CONTAINER (box)->border_width * 2;
+}
+
+static void gdl_dock_bar_size_hallocate (GtkWidget     *widget,
+                       GtkAllocation *allocation)
+{
+ GtkBox *box;
+  GtkBoxChild *child;
+  GList *children;
+  GtkAllocation child_allocation;
+  gint nvis_children;
+  gint nexpand_children;
+  gint child_width;
+  gint width;
+  gint extra;
+  gint x;
+  GtkTextDirection direction;
+
+  box = GTK_BOX (widget);
+  widget->allocation = *allocation;
+
+  direction = gtk_widget_get_direction (widget);
+  
+  nvis_children = 0;
+  nexpand_children = 0;
+  children = box->children;
+
+  while (children)
+    {
+      child = children->data;
+      children = children->next;
+
+      if (GTK_WIDGET_VISIBLE (child->widget))
+       {
+         nvis_children += 1;
+         if (child->expand)
+           nexpand_children += 1;
+       }
+    }
+
+  if (nvis_children > 0)
+    {
+      if (box->homogeneous)
+       {
+         width = (allocation->width -
+                  GTK_CONTAINER (box)->border_width * 2 -
+                  (nvis_children - 1) * box->spacing);
+         extra = width / nvis_children;
+       }
+      else if (nexpand_children > 0)
+       {
+         width = (gint) allocation->width - (gint) widget->requisition.width;
+         extra = width / nexpand_children;
+       }
+      else
+       {
+         width = 0;
+         extra = 0;
+       }
+
+      x = allocation->x + GTK_CONTAINER (box)->border_width;
+      child_allocation.y = allocation->y + GTK_CONTAINER (box)->border_width;
+      child_allocation.height = MAX (1, (gint) allocation->height - (gint) GTK_CONTAINER (box)->border_width * 2);
+
+      children = box->children;
+      while (children)
+       {
+         child = children->data;
+         children = children->next;
+
+         if ((child->pack == GTK_PACK_START) && GTK_WIDGET_VISIBLE (child->widget))
+           {
+             if (box->homogeneous)
+               {
+                 if (nvis_children == 1)
+                   child_width = width;
+                 else
+                   child_width = extra;
+
+                 nvis_children -= 1;
+                 width -= extra;
+               }
+             else
+               {
+                 GtkRequisition child_requisition;
+
+                 gtk_widget_get_child_requisition (child->widget, &child_requisition);
+
+                 child_width = child_requisition.width + child->padding * 2;
+
+                 if (child->expand)
+                   {
+                     if (nexpand_children == 1)
+                       child_width += width;
+                     else
+                       child_width += extra;
+
+                     nexpand_children -= 1;
+                     width -= extra;
+                   }
+               }
+
+             if (child->fill)
+               {
+                 child_allocation.width = MAX (1, (gint) child_width - (gint) child->padding * 2);
+                 child_allocation.x = x + child->padding;
+               }
+             else
+               {
+                 GtkRequisition child_requisition;
+
+                 gtk_widget_get_child_requisition (child->widget, &child_requisition);
+                 child_allocation.width = child_requisition.width;
+                 child_allocation.x = x + (child_width - child_allocation.width) / 2;
+               }
+
+             if (direction == GTK_TEXT_DIR_RTL)
+               child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
+
+             gtk_widget_size_allocate (child->widget, &child_allocation);
+
+             x += child_width + box->spacing;
+           }
+       }
+
+      x = allocation->x + allocation->width - GTK_CONTAINER (box)->border_width;
+
+      children = box->children;
+      while (children)
+       {
+         child = children->data;
+         children = children->next;
+
+         if ((child->pack == GTK_PACK_END) && GTK_WIDGET_VISIBLE (child->widget))
+           {
+             GtkRequisition child_requisition;
+             gtk_widget_get_child_requisition (child->widget, &child_requisition);
+
+              if (box->homogeneous)
+                {
+                  if (nvis_children == 1)
+                    child_width = width;
+                  else
+                    child_width = extra;
+
+                  nvis_children -= 1;
+                  width -= extra;
+                }
+              else
+                {
+                 child_width = child_requisition.width + child->padding * 2;
+
+                  if (child->expand)
+                    {
+                      if (nexpand_children == 1)
+                        child_width += width;
+                      else
+                        child_width += extra;
+
+                      nexpand_children -= 1;
+                      width -= extra;
+                    }
+                }
+
+              if (child->fill)
+                {
+                  child_allocation.width = MAX (1, (gint)child_width - (gint)child->padding * 2);
+                  child_allocation.x = x + child->padding - child_width;
+                }
+              else
+                {
+                 child_allocation.width = child_requisition.width;
+                  child_allocation.x = x + (child_width - child_allocation.width) / 2 - child_width;
+                }
+
+             if (direction == GTK_TEXT_DIR_RTL)
+               child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
+
+              gtk_widget_size_allocate (child->widget, &child_allocation);
+
+              x -= (child_width + box->spacing);
+           }
+       }
+    }
+}
+
+GtkWidget *
+gdl_dock_bar_new (GdlDock *dock)
+{
+    GdlDockMaster *master = NULL;
+    
+    /* get the master of the given dock */
+    if (dock)
+        master = GDL_DOCK_OBJECT_GET_MASTER (dock);
+
+    return g_object_new (GDL_TYPE_DOCK_BAR,
+                         "master", master, NULL);
+}
+
+GtkOrientation gdl_dock_bar_get_orientation (GdlDockBar *dockbar)
+{
+    g_return_val_if_fail (GDL_IS_DOCK_BAR (dockbar),
+                          GTK_ORIENTATION_VERTICAL);
+
+    return dockbar->_priv->orientation;
+}
+
+void gdl_dock_bar_set_orientation (GdlDockBar *dockbar,
+                                    GtkOrientation orientation)
+{
+    g_return_if_fail (GDL_IS_DOCK_BAR (dockbar));
+
+    dockbar->_priv->orientation = orientation;
+
+    gtk_widget_queue_resize (GTK_WIDGET (dockbar));
+}
+
+void gdl_dock_bar_set_style(GdlDockBar* dockbar,
+                           GdlDockBarStyle style)
+{
+    g_object_set(G_OBJECT(dockbar), "dockbar-style", style, NULL);
+}
+
+GdlDockBarStyle gdl_dock_bar_get_style(GdlDockBar* dockbar)
+{
+    GdlDockBarStyle style;
+    g_object_get(G_OBJECT(dockbar), "dockbar-style", &style, NULL);
+    return style;
+}
diff --git a/src/libgdl/gdl-dock-bar.h b/src/libgdl/gdl-dock-bar.h
new file mode 100644 (file)
index 0000000..22ba94d
--- /dev/null
@@ -0,0 +1,74 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This file is part of the GNOME Devtools Libraries.
+ *
+ * Copyright (C) 2003 Jeroen Zwartepoorte <jeroen@xs4all.nl>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GDL_DOCK_BAR_H__
+#define __GDL_DOCK_BAR_H__
+
+#include <gtk/gtkvbox.h>
+
+G_BEGIN_DECLS
+
+/* standard macros */
+#define GDL_TYPE_DOCK_BAR            (gdl_dock_bar_get_type ())
+#define GDL_DOCK_BAR(obj)            (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_BAR, GdlDockBar))
+#define GDL_DOCK_BAR_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_BAR, GdlDockBarClass))
+#define GDL_IS_DOCK_BAR(obj)         (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_BAR))
+#define GDL_IS_DOCK_BAR_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_BAR))
+#define GDL_DOCK_BAR_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK_BAR, GdlDockBarClass))
+
+/* data types & structures */
+typedef struct _GdlDockBar        GdlDockBar;
+typedef struct _GdlDockBarClass   GdlDockBarClass;
+typedef struct _GdlDockBarPrivate GdlDockBarPrivate;
+
+typedef enum {
+    GDL_DOCK_BAR_ICONS,
+    GDL_DOCK_BAR_TEXT,
+    GDL_DOCK_BAR_BOTH,
+    GDL_DOCK_BAR_AUTO
+} GdlDockBarStyle;
+
+struct _GdlDockBar {
+    GtkBox parent;
+
+    GdlDock *dock;
+
+    GdlDockBarPrivate *_priv;
+};
+
+struct _GdlDockBarClass {
+    GtkVBoxClass parent_class;
+};
+
+GType      gdl_dock_bar_get_type            (void); 
+
+GtkWidget *gdl_dock_bar_new                 (GdlDock     *dock);
+
+GtkOrientation gdl_dock_bar_get_orientation (GdlDockBar *dockbar);
+void           gdl_dock_bar_set_orientation (GdlDockBar *dockbar,
+                                             GtkOrientation orientation);
+void           gdl_dock_bar_set_style       (GdlDockBar *dockbar,
+                                             GdlDockBarStyle style);
+GdlDockBarStyle gdl_dock_bar_get_style      (GdlDockBar *dockbar);
+
+G_END_DECLS
+
+#endif /* __GDL_DOCK_BAR_H__ */
diff --git a/src/libgdl/gdl-dock-item-grip.c b/src/libgdl/gdl-dock-item-grip.c
new file mode 100644 (file)
index 0000000..0a8007d
--- /dev/null
@@ -0,0 +1,711 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 8 -*- */
+/**
+ * gdl-dock-item-grip.c
+ *
+ * Based on bonobo-dock-item-grip.  Original copyright notice follows.
+ *
+ * Author:
+ *    Michael Meeks
+ *
+ * Copyright (C) 2002 Sun Microsystems, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gdl-i18n.h"
+#include <string.h>
+#include <glib-object.h>
+#include <gtk/gtkbutton.h>
+#include <gtk/gtktooltips.h>
+#include <gtk/gtkimage.h>
+#include "gdl-dock-item.h"
+#include "gdl-dock-item-grip.h"
+#include "gdl-stock.h"
+#include "gdl-tools.h"
+
+#define ALIGN_BORDER 5
+
+enum {
+    PROP_0,
+    PROP_ITEM
+};
+struct _GdlDockItemGripPrivate {
+    GtkWidget   *close_button;
+    GtkWidget   *iconify_button;
+    GtkTooltips *tooltips;
+
+    gboolean     icon_pixbuf_valid;
+    GdkPixbuf   *icon_pixbuf;
+
+    gchar       *title;
+    PangoLayout *title_layout;
+};
+GDL_CLASS_BOILERPLATE (GdlDockItemGrip, gdl_dock_item_grip,
+                      GtkContainer, GTK_TYPE_CONTAINER);
+
+/* must be called after size_allocate */
+static void
+gdl_dock_item_grip_get_title_area (GdlDockItemGrip *grip,
+                                   GdkRectangle    *area)
+{
+    GtkWidget *widget = GTK_WIDGET (grip);
+    gint       border = GTK_CONTAINER (grip)->border_width;
+    gint       alloc_height;
+
+    area->width = (widget->allocation.width - 2 * border - ALIGN_BORDER);
+    
+    pango_layout_get_pixel_size (grip->_priv->title_layout, NULL, &alloc_height);
+    
+    alloc_height = MAX (grip->_priv->close_button->allocation.height, alloc_height);
+    alloc_height = MAX (grip->_priv->iconify_button->allocation.height, alloc_height);
+    if (GTK_WIDGET_VISIBLE (grip->_priv->close_button)) {
+        area->width -= grip->_priv->close_button->allocation.width;
+    }
+    if (GTK_WIDGET_VISIBLE (grip->_priv->iconify_button)) {
+        area->width -= grip->_priv->iconify_button->allocation.width;
+    }
+
+    area->x      = widget->allocation.x + border + ALIGN_BORDER;
+    area->y      = widget->allocation.y + border;
+    area->height = alloc_height;
+
+    if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+        area->x += (widget->allocation.width - 2 * border) - area->width;
+}
+
+static void
+ensure_title_and_icon_pixbuf (GdlDockItemGrip *grip)
+{
+    gchar *stock_id;
+    GdkPixbuf *pixbuf;
+    
+    g_return_if_fail (GDL_IS_DOCK_ITEM_GRIP (grip));
+    
+    /* get long name property from the dock object */
+    if (!grip->_priv->title) {
+        g_object_get (G_OBJECT (grip->item), "long-name", &grip->_priv->title, NULL);
+        if (!grip->_priv->title)
+            grip->_priv->title = g_strdup ("");
+    }
+
+    /* retrieve stock pixbuf, if any */
+    if (!grip->_priv->icon_pixbuf_valid) {
+        g_object_get (G_OBJECT (grip->item), "stock-id", &stock_id, NULL);
+        
+        if (stock_id) {
+            grip->_priv->icon_pixbuf = gtk_widget_render_icon (GTK_WIDGET (grip),
+                                                               stock_id,
+                                                               GTK_ICON_SIZE_MENU, "");
+            g_free (stock_id);
+            grip->_priv->icon_pixbuf_valid = TRUE;
+        }
+    }
+
+    /* retrieve pixbuf icon, if any */
+    if (!grip->_priv->icon_pixbuf_valid) {
+        g_object_get (G_OBJECT (grip->item), "pixbuf-icon", &pixbuf, NULL);
+        
+        if (pixbuf) {
+            grip->_priv->icon_pixbuf = pixbuf;
+            grip->_priv->icon_pixbuf_valid = TRUE;
+        }
+    }
+
+    /* create layout: the actual text is reset at size_allocate */
+    if (!grip->_priv->title_layout) {
+        grip->_priv->title_layout = gtk_widget_create_pango_layout (GTK_WIDGET (grip),
+                                                                    grip->_priv->title);
+        pango_layout_set_single_paragraph_mode (grip->_priv->title_layout, TRUE);
+    }
+}
+
+static gint
+gdl_dock_item_grip_expose (GtkWidget      *widget,
+                          GdkEventExpose *event)
+{
+    GdlDockItemGrip *grip;
+    GdkRectangle     title_area;
+    GdkRectangle     expose_area;
+    gint             layout_width;
+    gint             layout_height;
+    gint             text_x;
+    gint             text_y;
+
+    grip = GDL_DOCK_ITEM_GRIP (widget);
+    gdl_dock_item_grip_get_title_area (grip, &title_area);
+
+    if (grip->_priv->icon_pixbuf) {
+        GdkRectangle pixbuf_rect;
+        
+        pixbuf_rect.width = gdk_pixbuf_get_width (grip->_priv->icon_pixbuf);
+        pixbuf_rect.height = gdk_pixbuf_get_height (grip->_priv->icon_pixbuf);
+        if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) {
+            pixbuf_rect.x = title_area.x + title_area.width - pixbuf_rect.width;
+        } else {
+            pixbuf_rect.x = title_area.x;
+            title_area.x += pixbuf_rect.width + 1;
+        }
+        /* shrink title area by the pixbuf width plus a 1px spacing */
+        title_area.width -= pixbuf_rect.width + 1;
+        pixbuf_rect.y = title_area.y + (title_area.height - pixbuf_rect.height) / 2;
+
+        if (gdk_rectangle_intersect (&event->area, &pixbuf_rect, &expose_area)) {
+            GdkGC *gc;
+            GtkStyle *style;
+
+            style = gtk_widget_get_style (widget);
+            gc = style->bg_gc[widget->state];
+            gdk_draw_pixbuf (GDK_DRAWABLE (widget->window), gc,
+                             grip->_priv->icon_pixbuf,
+                             0, 0, pixbuf_rect.x, pixbuf_rect.y,
+                             pixbuf_rect.width, pixbuf_rect.height,
+                             GDK_RGB_DITHER_NONE, 0, 0);
+       }
+    }
+
+    if (gdk_rectangle_intersect (&title_area, &event->area, &expose_area)) {
+        pango_layout_get_pixel_size (grip->_priv->title_layout, &layout_width,
+                                     &layout_height);
+
+        if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+            text_x = title_area.x + title_area.width - layout_width;
+        else
+            text_x = title_area.x;
+
+        text_y = title_area.y + (title_area.height - layout_height) / 2;
+
+        gtk_paint_layout (widget->style, widget->window, widget->state, TRUE,
+                          &expose_area, widget, NULL, text_x, text_y,
+                          grip->_priv->title_layout);
+    }
+
+    return GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
+}  
+
+static void
+gdl_dock_item_grip_item_notify (GObject    *master,
+                                GParamSpec *pspec,
+                                gpointer    data)
+{
+    GdlDockItemGrip *grip;
+    gboolean cursor;
+    
+    grip = GDL_DOCK_ITEM_GRIP (data);
+
+    if (strcmp (pspec->name, "stock-id") == 0) {
+        if (grip->_priv->icon_pixbuf) {
+            g_object_unref (grip->_priv->icon_pixbuf);
+            grip->_priv->icon_pixbuf = NULL;
+        }
+        grip->_priv->icon_pixbuf_valid = FALSE;
+        ensure_title_and_icon_pixbuf (grip);
+
+    } else if (strcmp (pspec->name, "long-name") == 0) {
+        g_free (grip->_priv->title);
+        g_object_unref (grip->_priv->title_layout);
+        grip->_priv->title_layout = NULL;
+        grip->_priv->title = NULL;
+        ensure_title_and_icon_pixbuf (grip);
+       gtk_widget_queue_draw (GTK_WIDGET (grip));
+    } else if (strcmp (pspec->name, "behavior") == 0) {
+       cursor = FALSE;
+        if (grip->_priv->close_button) {
+            if (GDL_DOCK_ITEM_CANT_CLOSE (grip->item)) {
+                gtk_widget_hide (GTK_WIDGET (grip->_priv->close_button));
+            } else {
+                gtk_widget_show (GTK_WIDGET (grip->_priv->close_button));
+               cursor = TRUE;
+            }
+        }
+        if (grip->_priv->iconify_button) {
+            if (GDL_DOCK_ITEM_CANT_ICONIFY (grip->item)) {
+                gtk_widget_hide (GTK_WIDGET (grip->_priv->iconify_button));
+            } else {
+                gtk_widget_show (GTK_WIDGET (grip->_priv->iconify_button));
+               cursor = TRUE;
+            }
+        }
+       if (grip->title_window && !cursor)
+            gdk_window_set_cursor (grip->title_window, NULL);
+
+    }
+}
+
+static void
+gdl_dock_item_grip_destroy (GtkObject *object)
+{
+    GdlDockItemGrip *grip = GDL_DOCK_ITEM_GRIP (object);
+    
+    if (grip->_priv) {
+        GdlDockItemGripPrivate *priv = grip->_priv;
+
+        if (priv->title_layout) {
+            g_object_unref (priv->title_layout);
+            priv->title_layout = NULL;
+        }
+        g_free (priv->title);
+        priv->title = NULL;
+
+        if (priv->icon_pixbuf) {
+            g_object_unref (priv->icon_pixbuf);
+            priv->icon_pixbuf = NULL;
+        }
+
+        if (priv->tooltips) {
+            g_object_unref (priv->tooltips);
+            priv->tooltips = NULL;
+        }
+
+        if (grip->item)
+            g_signal_handlers_disconnect_by_func (grip->item,
+                                                  gdl_dock_item_grip_item_notify,
+                                                  grip);
+        grip->item = NULL;
+
+        grip->_priv = NULL;
+        g_free (priv);
+    }
+
+    GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
+}
+
+static void
+gdl_dock_item_grip_set_property (GObject      *object,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+    GdlDockItemGrip *grip;
+
+    g_return_if_fail (GDL_IS_DOCK_ITEM_GRIP (object));
+
+    grip = GDL_DOCK_ITEM_GRIP (object);
+    
+    switch (prop_id) {
+        case PROP_ITEM:
+            grip->item = g_value_get_object (value);
+            if (grip->item) {
+                g_signal_connect (grip->item, "notify::long_name",
+                                  G_CALLBACK (gdl_dock_item_grip_item_notify),
+                                  grip);
+                g_signal_connect (grip->item, "notify::stock_id",
+                                  G_CALLBACK (gdl_dock_item_grip_item_notify),
+                                  grip);
+               g_signal_connect (grip->item, "notify::behavior",
+                                 G_CALLBACK (gdl_dock_item_grip_item_notify),
+                                 grip);
+
+                if (!GDL_DOCK_ITEM_CANT_CLOSE (grip->item) && grip->_priv->close_button)
+                    gtk_widget_show (grip->_priv->close_button);
+                if (!GDL_DOCK_ITEM_CANT_ICONIFY (grip->item) && grip->_priv->iconify_button)
+                    gtk_widget_show (grip->_priv->iconify_button);
+            }
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+gdl_dock_item_grip_close_clicked (GtkWidget       *widget,
+                                  GdlDockItemGrip *grip)
+{
+    g_return_if_fail (grip->item != NULL);
+
+    gdl_dock_item_hide_item (grip->item);
+}
+
+static void
+gdl_dock_item_grip_iconify_clicked (GtkWidget       *widget,
+                                    GdlDockItemGrip *grip)
+{
+    g_return_if_fail (grip->item != NULL);
+
+    gdl_dock_item_iconify_item (grip->item);
+    
+    /* Workaround to unhighlight the iconify button. */
+    GTK_BUTTON (grip->_priv->iconify_button)->in_button = FALSE;
+    gtk_button_leave (GTK_BUTTON (grip->_priv->iconify_button));
+}
+  
+static void
+gdl_dock_item_grip_instance_init (GdlDockItemGrip *grip)
+{
+    GtkWidget *image;
+
+    GTK_WIDGET_SET_FLAGS (grip, GTK_NO_WINDOW);
+    
+    grip->_priv = g_new0 (GdlDockItemGripPrivate, 1);
+    grip->_priv->icon_pixbuf_valid = FALSE;
+    grip->_priv->icon_pixbuf = NULL;
+    grip->_priv->title_layout = NULL;
+
+    gtk_widget_push_composite_child ();
+    grip->_priv->close_button = gtk_button_new ();
+    gtk_widget_pop_composite_child ();
+    
+    GTK_WIDGET_UNSET_FLAGS (grip->_priv->close_button, GTK_CAN_FOCUS);
+    gtk_widget_set_parent (grip->_priv->close_button, GTK_WIDGET (grip));
+    gtk_button_set_relief (GTK_BUTTON (grip->_priv->close_button), GTK_RELIEF_NONE);
+    gtk_widget_show (grip->_priv->close_button);
+
+    image = gtk_image_new_from_stock (GDL_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
+    gtk_container_add (GTK_CONTAINER (grip->_priv->close_button), image);
+    gtk_widget_show (image);
+
+    g_signal_connect (G_OBJECT (grip->_priv->close_button), "clicked",
+                      G_CALLBACK (gdl_dock_item_grip_close_clicked), grip);
+
+    gtk_widget_push_composite_child ();
+    grip->_priv->iconify_button = gtk_button_new ();
+    gtk_widget_pop_composite_child ();
+    
+    GTK_WIDGET_UNSET_FLAGS (grip->_priv->iconify_button, GTK_CAN_FOCUS);
+    gtk_widget_set_parent (grip->_priv->iconify_button, GTK_WIDGET (grip));
+    gtk_button_set_relief (GTK_BUTTON (grip->_priv->iconify_button), GTK_RELIEF_NONE);
+    gtk_widget_show (grip->_priv->iconify_button);
+
+    image = gtk_image_new_from_stock (GDL_STOCK_MENU_LEFT, GTK_ICON_SIZE_MENU);
+    gtk_container_add (GTK_CONTAINER (grip->_priv->iconify_button), image);
+    gtk_widget_show (image);
+
+    g_signal_connect (G_OBJECT (grip->_priv->iconify_button), "clicked",
+                      G_CALLBACK (gdl_dock_item_grip_iconify_clicked), grip);
+
+    grip->_priv->tooltips = gtk_tooltips_new ();
+    g_object_ref (grip->_priv->tooltips);
+    gtk_object_sink (GTK_OBJECT (grip->_priv->tooltips));
+    gtk_tooltips_set_tip (grip->_priv->tooltips, grip->_priv->iconify_button,
+                          _("Iconify"), _("Iconify this dock"));
+    gtk_tooltips_set_tip (grip->_priv->tooltips, grip->_priv->close_button,
+                          _("Close"), _("Close this dock"));
+}
+
+static void
+gdl_dock_item_grip_realize (GtkWidget *widget)
+{
+    GdlDockItemGrip *grip = GDL_DOCK_ITEM_GRIP (widget);
+
+    GTK_WIDGET_CLASS (parent_class)->realize (widget);
+
+    if (!grip->title_window) {
+        GdkWindowAttr  attributes;
+        GdkRectangle   area;
+        GdkCursor     *cursor;
+
+        ensure_title_and_icon_pixbuf (grip);
+        gdl_dock_item_grip_get_title_area (grip, &area);
+
+        attributes.x                 = area.x;
+        attributes.y                 = area.y;
+        attributes.width             = area.width;
+        attributes.height            = area.height;
+        attributes.window_type       = GDK_WINDOW_TEMP;
+        attributes.wclass            = GDK_INPUT_ONLY;
+        attributes.override_redirect = TRUE;
+        attributes.event_mask        = (GDK_BUTTON_PRESS_MASK   |
+                                        GDK_BUTTON_RELEASE_MASK |
+                                        GDK_BUTTON_MOTION_MASK  |
+                                        gtk_widget_get_events (widget));
+
+        grip->title_window = gdk_window_new (gtk_widget_get_parent_window (widget),
+                                             &attributes,
+                                             (GDK_WA_X |
+                                              GDK_WA_Y |
+                                              GDK_WA_NOREDIR));
+
+        gdk_window_set_user_data (grip->title_window, widget);
+        if (GDL_DOCK_ITEM_CANT_CLOSE (grip->item))
+           cursor = NULL;
+       else if (GDL_DOCK_ITEM_CANT_ICONIFY (grip->item))
+           cursor = NULL;
+       else 
+           cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
+                                             GDK_HAND2);
+        gdk_window_set_cursor (grip->title_window, cursor);
+       if (cursor)
+            gdk_cursor_unref (cursor);
+    }
+}
+
+static void
+gdl_dock_item_grip_unrealize (GtkWidget *widget)
+{
+    GdlDockItemGrip *grip = GDL_DOCK_ITEM_GRIP (widget);
+
+    if (grip->title_window) {
+        gdk_window_set_user_data (grip->title_window, NULL);
+        gdk_window_destroy (grip->title_window);
+        grip->title_window = NULL;
+    }
+
+    GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
+}
+
+static void
+gdl_dock_item_grip_map (GtkWidget *widget)
+{
+    GdlDockItemGrip *grip = GDL_DOCK_ITEM_GRIP (widget);
+
+    GTK_WIDGET_CLASS (parent_class)->map (widget);
+
+    if (grip->title_window)
+        gdk_window_show (grip->title_window);
+}
+
+static void
+gdl_dock_item_grip_unmap (GtkWidget *widget)
+{
+    GdlDockItemGrip *grip = GDL_DOCK_ITEM_GRIP (widget);
+
+    if (grip->title_window)
+        gdk_window_hide (grip->title_window);
+
+    GTK_WIDGET_CLASS (parent_class)->unmap (widget);
+}
+
+static void
+gdl_dock_item_grip_size_request (GtkWidget      *widget,
+                                 GtkRequisition *requisition)
+{
+    GtkRequisition   child_requisition;
+    GtkContainer    *container;
+    GdlDockItemGrip *grip;
+    gint             layout_height;
+
+    g_return_if_fail (GDL_IS_DOCK_ITEM_GRIP (widget));
+    g_return_if_fail (requisition != NULL);
+
+    container = GTK_CONTAINER (widget);
+    grip = GDL_DOCK_ITEM_GRIP (widget);
+    
+    requisition->width = container->border_width * 2 + ALIGN_BORDER;
+    requisition->height = container->border_width * 2;
+
+    ensure_title_and_icon_pixbuf (grip);
+    pango_layout_get_pixel_size (grip->_priv->title_layout, NULL, &layout_height);
+
+    gtk_widget_size_request (grip->_priv->close_button, &child_requisition);
+
+    requisition->width += child_requisition.width;
+    layout_height = MAX (layout_height, child_requisition.height);
+    
+    gtk_widget_size_request (grip->_priv->iconify_button, &child_requisition);
+
+    requisition->width += child_requisition.width;
+    layout_height = MAX (layout_height, child_requisition.height);
+    
+    requisition->height += layout_height;
+
+    if (grip->_priv->icon_pixbuf) {
+        requisition->width += gdk_pixbuf_get_width (grip->_priv->icon_pixbuf) + 1;
+    }
+}
+
+#define ELLIPSIS "..."
+
+static void
+ellipsize_layout (PangoLayout *layout, gint width)
+{
+    PangoLayoutLine *line;
+    PangoLayout *ell;
+    gint h, w, ell_w, x;
+    GString *text;
+    
+    if (width <= 0) {
+        pango_layout_set_text (layout, "", -1);
+        return;
+    }
+    
+    pango_layout_get_pixel_size (layout, &w, &h);
+    if (w <= width) return;
+    
+    /* calculate ellipsis width */
+    ell = pango_layout_copy (layout);
+    pango_layout_set_text (ell, ELLIPSIS, -1);
+    pango_layout_get_pixel_size (ell, &ell_w, NULL);
+    g_object_unref (ell);
+
+    if (width < ell_w) {
+        /* not even ellipsis fits, so hide the text */
+        pango_layout_set_text (layout, "", -1);
+        return;
+    }
+
+    /* shrink total available width by the width of the ellipsis */
+    width -= ell_w;
+    line = pango_layout_get_line (layout, 0);
+    text = g_string_new (pango_layout_get_text (layout));
+    if (pango_layout_line_x_to_index (line, width * PANGO_SCALE, &x, NULL)) {
+        g_string_set_size (text, x);
+        g_string_append (text, ELLIPSIS);
+        pango_layout_set_text (layout, text->str, -1);
+    }
+    g_string_free (text, TRUE);
+}
+
+static void
+gdl_dock_item_grip_size_allocate (GtkWidget     *widget,
+                                  GtkAllocation *allocation)
+{
+    GdlDockItemGrip *grip;
+    GtkContainer    *container;
+    GtkRequisition   button_requisition = { 0, };
+    GtkAllocation    child_allocation;
+
+    g_return_if_fail (GDL_IS_DOCK_ITEM_GRIP (widget));
+    g_return_if_fail (allocation != NULL);
+  
+    grip = GDL_DOCK_ITEM_GRIP (widget);
+    container = GTK_CONTAINER (widget);
+
+    GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
+
+    if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+        child_allocation.x = allocation->x + container->border_width + ALIGN_BORDER;
+    else
+        child_allocation.x = allocation->x + allocation->width - container->border_width;
+    child_allocation.y = allocation->y + container->border_width;
+
+    gtk_widget_size_request (grip->_priv->close_button, &button_requisition);
+
+    if (gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL)
+        child_allocation.x -= button_requisition.width;
+
+    child_allocation.width = button_requisition.width;
+    child_allocation.height = button_requisition.height;
+
+    gtk_widget_size_allocate (grip->_priv->close_button, &child_allocation);
+
+    if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+        child_allocation.x += button_requisition.width;
+    
+
+    gtk_widget_size_request (grip->_priv->iconify_button, &button_requisition);
+
+    if (gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL)
+        child_allocation.x -= button_requisition.width;
+
+    child_allocation.width = button_requisition.width;
+    child_allocation.height = button_requisition.height;
+
+    gtk_widget_size_allocate (grip->_priv->iconify_button, &child_allocation);
+
+    if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+        child_allocation.x += button_requisition.width;
+
+    
+    if (grip->title_window) {
+        GdkRectangle area;
+        
+        /* set layout text */
+        ensure_title_and_icon_pixbuf (grip);
+        pango_layout_set_text (grip->_priv->title_layout, grip->_priv->title, -1);
+
+        gdl_dock_item_grip_get_title_area (grip, &area);
+
+        gdk_window_move_resize (grip->title_window,
+                                area.x, area.y, area.width, area.height);
+
+        if (grip->_priv->icon_pixbuf)
+            area.width -= gdk_pixbuf_get_width (grip->_priv->icon_pixbuf) + 1;
+            
+        /* ellipsize title if it doesn't fit the title area */
+        ellipsize_layout (grip->_priv->title_layout, area.width);
+    }
+}
+
+static void 
+gdl_dock_item_grip_add (GtkContainer *container,
+                        GtkWidget    *widget)
+{
+    g_warning ("gtk_container_add not implemented for GdlDockItemGrip");
+}
+
+static void  
+gdl_dock_item_grip_remove (GtkContainer *container,
+                           GtkWidget    *widget)
+{
+    g_warning ("gtk_container_remove not implemented for GdlDockItemGrip");
+}
+
+static void
+gdl_dock_item_grip_forall (GtkContainer *container,
+                           gboolean      include_internals,
+                           GtkCallback   callback,
+                           gpointer      callback_data)
+{
+    GdlDockItemGrip *grip;
+    
+    g_return_if_fail (GDL_IS_DOCK_ITEM_GRIP (container));
+
+    grip = GDL_DOCK_ITEM_GRIP (container);
+
+    if (include_internals) {
+        (* callback) (grip->_priv->close_button, callback_data);
+        (* callback) (grip->_priv->iconify_button, callback_data);
+    }
+}
+
+static GtkType
+gdl_dock_item_grip_child_type (GtkContainer *container)
+{
+    return G_TYPE_NONE;
+}
+
+static void
+gdl_dock_item_grip_class_init (GdlDockItemGripClass *klass)
+{
+    GObjectClass *gobject_class;
+    GtkObjectClass *gtk_object_class;
+    GtkWidgetClass *widget_class;
+    GtkContainerClass *container_class;
+
+    parent_class = g_type_class_peek_parent (klass);
+    gobject_class = G_OBJECT_CLASS (klass);
+    gtk_object_class = GTK_OBJECT_CLASS (klass);
+    widget_class = GTK_WIDGET_CLASS (klass);
+    container_class = GTK_CONTAINER_CLASS (klass);
+
+    gobject_class->set_property = gdl_dock_item_grip_set_property;
+
+    gtk_object_class->destroy = gdl_dock_item_grip_destroy;
+
+    widget_class->expose_event = gdl_dock_item_grip_expose;
+    widget_class->realize = gdl_dock_item_grip_realize;
+    widget_class->unrealize = gdl_dock_item_grip_unrealize;
+    widget_class->map = gdl_dock_item_grip_map;
+    widget_class->unmap = gdl_dock_item_grip_unmap;
+    widget_class->size_request = gdl_dock_item_grip_size_request;
+    widget_class->size_allocate = gdl_dock_item_grip_size_allocate;
+
+    container_class->add = gdl_dock_item_grip_add;
+    container_class->remove = gdl_dock_item_grip_remove;
+    container_class->forall = gdl_dock_item_grip_forall;
+    container_class->child_type = gdl_dock_item_grip_child_type;
+
+    g_object_class_install_property (
+        gobject_class, PROP_ITEM,
+        g_param_spec_object ("item", _("Controlling dock item"),
+                             _("Dockitem which 'owns' this grip"),
+                             GDL_TYPE_DOCK_ITEM,
+                             G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+
+    /* initialize stock images */
+    gdl_stock_init ();
+}
+
+GtkWidget *
+gdl_dock_item_grip_new (GdlDockItem *item)
+{
+    GdlDockItemGrip *grip = g_object_new (GDL_TYPE_DOCK_ITEM_GRIP, "item", item,
+                                          NULL);
+
+    return GTK_WIDGET (grip);
+}
diff --git a/src/libgdl/gdl-dock-item-grip.h b/src/libgdl/gdl-dock-item-grip.h
new file mode 100644 (file)
index 0000000..377ea14
--- /dev/null
@@ -0,0 +1,56 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 8 -*- */
+/**
+ * gdl-dock-item-grip.h
+ * 
+ * Based on bonobo-dock-item-grip.  Original copyright notice follows.
+ *
+ * Author:
+ *    Michael Meeks
+ *
+ * Copyright (C) 2002 Sun Microsystems, Inc.
+ */
+
+#ifndef _GDL_DOCK_ITEM_GRIP_H_
+#define _GDL_DOCK_ITEM_GRIP_H_
+
+#include <gtk/gtkwidget.h>
+#include "libgdl/gdl-dock-item.h"
+
+G_BEGIN_DECLS
+
+#define GDL_TYPE_DOCK_ITEM_GRIP            (gdl_dock_item_grip_get_type())
+#define GDL_DOCK_ITEM_GRIP(obj)            \
+    (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_ITEM_GRIP, GdlDockItemGrip))
+#define GDL_DOCK_ITEM_GRIP_CLASS(klass)    \
+    (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_ITEM_GRIP, GdlDockItemGripClass))
+#define GDL_IS_DOCK_ITEM_GRIP(obj)         \
+    (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_ITEM_GRIP))
+#define GDL_IS_DOCK_ITEM_GRIP_CLASS(klass) \
+    (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_ITEM_GRIP))
+#define GDL_DOCK_ITEM_GRIP_GET_CLASS(obj)  \
+    (GTK_CHECK_GET_CLASS ((obj), GDL_TYPE_DOCK_ITEM_GRIP, GdlDockItemGripClass))
+
+typedef struct _GdlDockItemGrip        GdlDockItemGrip;
+typedef struct _GdlDockItemGripClass   GdlDockItemGripClass;
+typedef struct _GdlDockItemGripPrivate GdlDockItemGripPrivate;
+
+struct _GdlDockItemGrip {
+    GtkContainer parent;
+       
+    GdlDockItem *item;
+    
+    GdkWindow *title_window;
+    
+    GdlDockItemGripPrivate *_priv;
+};
+
+struct _GdlDockItemGripClass {
+    GtkContainerClass parent_class;
+};
+
+GType      gdl_dock_item_grip_get_type (void);
+GtkWidget *gdl_dock_item_grip_new      (GdlDockItem *item);
+
+G_END_DECLS
+
+#endif /* _GDL_DOCK_ITEM_GRIP_H_ */
diff --git a/src/libgdl/gdl-dock-item.c b/src/libgdl/gdl-dock-item.c
new file mode 100644 (file)
index 0000000..d9f805f
--- /dev/null
@@ -0,0 +1,1939 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 
+ *
+ * gdl-dock-item.c
+ *
+ * Author: Gustavo Giráldez <gustavo.giraldez@gmx.net>
+ *         Naba Kumar  <naba@gnome.org>
+ *
+ * Based on GnomeDockItem/BonoboDockItem.  Original copyright notice follows.
+ *
+ * Copyright (C) 1998 Ettore Perazzoli
+ * Copyright (C) 1998 Elliot Lee
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald 
+ * All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gdl-i18n.h"
+#include <string.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "gdl-tools.h"
+#include "gdl-dock.h"
+#include "gdl-dock-item.h"
+#include "gdl-dock-item-grip.h"
+#include "gdl-dock-notebook.h"
+#include "gdl-dock-paned.h"
+#include "gdl-dock-tablabel.h"
+#include "gdl-dock-placeholder.h"
+#include "gdl-dock-master.h"
+#include "libgdltypebuiltins.h"
+#include "libgdlmarshal.h"
+
+#define NEW_DOCK_ITEM_RATIO 0.3
+
+/* ----- Private prototypes ----- */
+
+static void  gdl_dock_item_class_init    (GdlDockItemClass *class);
+static void  gdl_dock_item_instance_init (GdlDockItem *item);
+
+static GObject *gdl_dock_item_constructor (GType                  type,
+                                           guint                  n_construct_properties,
+                                           GObjectConstructParam *construct_param);
+
+static void  gdl_dock_item_set_property  (GObject      *object,
+                                          guint         prop_id,
+                                          const GValue *value,
+                                          GParamSpec   *pspec);
+static void  gdl_dock_item_get_property  (GObject      *object,
+                                          guint         prop_id,
+                                          GValue       *value,
+                                          GParamSpec   *pspec);
+
+static void  gdl_dock_item_destroy       (GtkObject *object);
+
+static void  gdl_dock_item_add           (GtkContainer *container,
+                                          GtkWidget    *widget);
+static void  gdl_dock_item_remove        (GtkContainer *container,
+                                          GtkWidget    *widget);
+static void  gdl_dock_item_forall        (GtkContainer *container,
+                                          gboolean      include_internals,
+                                          GtkCallback   callback,
+                                          gpointer      callback_data);
+static GtkType gdl_dock_item_child_type  (GtkContainer *container);
+
+static void  gdl_dock_item_size_request  (GtkWidget *widget,
+                                          GtkRequisition *requisition);
+static void  gdl_dock_item_size_allocate (GtkWidget *widget,
+                                          GtkAllocation *allocation);
+static void  gdl_dock_item_map           (GtkWidget *widget);
+static void  gdl_dock_item_unmap         (GtkWidget *widget);
+static void  gdl_dock_item_realize       (GtkWidget *widget);
+static void  gdl_dock_item_style_set     (GtkWidget *widget,
+                                          GtkStyle  *previous_style);
+static gint  gdl_dock_item_expose        (GtkWidget *widget,
+                                          GdkEventExpose *event);
+
+static gint  gdl_dock_item_button_changed (GtkWidget *widget,
+                                           GdkEventButton *event);
+static gint  gdl_dock_item_motion         (GtkWidget *widget,
+                                           GdkEventMotion *event);
+static gboolean  gdl_dock_item_key_press  (GtkWidget *widget,
+                                           GdkEventKey *event);
+
+static gboolean gdl_dock_item_dock_request (GdlDockObject    *object,
+                                            gint              x,
+                                            gint              y,
+                                            GdlDockRequest   *request);
+static void     gdl_dock_item_dock         (GdlDockObject    *object,
+                                            GdlDockObject    *requestor,
+                                            GdlDockPlacement  position,
+                                            GValue           *other_data);
+
+static void  gdl_dock_item_popup_menu    (GdlDockItem *item, 
+                                          guint        button,
+                                          guint32      time);
+static void  gdl_dock_item_drag_start    (GdlDockItem *item);
+static void  gdl_dock_item_drag_end      (GdlDockItem *item,
+                                          gboolean     cancel);
+
+static void  gdl_dock_item_tab_button    (GtkWidget      *widget,
+                                          GdkEventButton *event,
+                                          gpointer        data);
+                                          
+static void  gdl_dock_item_hide_cb       (GtkWidget   *widget,
+                                          GdlDockItem *item);
+
+static void  gdl_dock_item_lock_cb       (GtkWidget   *widget,
+                                          GdlDockItem *item);
+
+static void  gdl_dock_item_unlock_cb     (GtkWidget   *widget,
+                                          GdlDockItem *item);
+
+static void  gdl_dock_item_showhide_grip (GdlDockItem *item);
+
+static void  gdl_dock_item_real_set_orientation (GdlDockItem    *item,
+                                                 GtkOrientation  orientation);
+
+static void gdl_dock_param_export_gtk_orientation (const GValue *src,
+                                                   GValue       *dst);
+static void gdl_dock_param_import_gtk_orientation (const GValue *src,
+                                                   GValue       *dst);
+
+
+
+/* ----- Class variables and definitions ----- */
+
+enum {
+    PROP_0,
+    PROP_ORIENTATION,
+    PROP_RESIZE,
+    PROP_BEHAVIOR,
+    PROP_LOCKED,
+    PROP_PREFERRED_WIDTH,
+    PROP_PREFERRED_HEIGHT
+};
+
+enum {
+    DOCK_DRAG_BEGIN,
+    DOCK_DRAG_MOTION,
+    DOCK_DRAG_END,
+    LAST_SIGNAL
+};
+
+static guint gdl_dock_item_signals [LAST_SIGNAL] = { 0 };
+
+#define GDL_DOCK_ITEM_GRIP_SHOWN(item) \
+    (GDL_DOCK_ITEM_HAS_GRIP (item)) 
+
+struct _GdlDockItemPrivate {
+    GtkWidget *menu;
+
+    gboolean   grip_shown;
+    GtkWidget *grip;
+    guint      grip_size;
+    
+    GtkWidget *tab_label;
+
+    gint       preferred_width;
+    gint       preferred_height;
+
+    GdlDockPlaceholder *ph;
+
+    gint       start_x, start_y;
+};
+
+/* FIXME: implement the rest of the behaviors */
+
+#define SPLIT_RATIO  0.4
+
+
+/* ----- Private functions ----- */
+
+GDL_CLASS_BOILERPLATE (GdlDockItem, gdl_dock_item, GdlDockObject, GDL_TYPE_DOCK_OBJECT);
+
+static void
+gdl_dock_item_class_init (GdlDockItemClass *klass)
+{
+    static gboolean style_initialized = FALSE;
+    
+    GObjectClass       *g_object_class;
+    GtkObjectClass     *gtk_object_class;
+    GtkWidgetClass     *widget_class;
+    GtkContainerClass  *container_class;
+    GdlDockObjectClass *object_class;
+    
+    g_object_class = G_OBJECT_CLASS (klass);
+    gtk_object_class = GTK_OBJECT_CLASS (klass);
+    widget_class = GTK_WIDGET_CLASS (klass);
+    container_class = GTK_CONTAINER_CLASS (klass);
+    object_class = GDL_DOCK_OBJECT_CLASS (klass);
+
+    g_object_class->constructor = gdl_dock_item_constructor;
+    g_object_class->set_property = gdl_dock_item_set_property;
+    g_object_class->get_property = gdl_dock_item_get_property;
+
+    gtk_object_class->destroy = gdl_dock_item_destroy;
+
+    widget_class->realize = gdl_dock_item_realize;
+    widget_class->map = gdl_dock_item_map;
+    widget_class->unmap = gdl_dock_item_unmap;
+    widget_class->size_request = gdl_dock_item_size_request;
+    widget_class->size_allocate = gdl_dock_item_size_allocate;
+    widget_class->style_set = gdl_dock_item_style_set;
+    widget_class->expose_event = gdl_dock_item_expose;
+    widget_class->button_press_event = gdl_dock_item_button_changed;
+    widget_class->button_release_event = gdl_dock_item_button_changed;
+    widget_class->motion_notify_event = gdl_dock_item_motion;
+    widget_class->key_press_event = gdl_dock_item_key_press;
+    
+    container_class->add = gdl_dock_item_add;
+    container_class->remove = gdl_dock_item_remove;
+    container_class->forall = gdl_dock_item_forall;
+    container_class->child_type = gdl_dock_item_child_type;
+    
+    object_class->is_compound = FALSE;
+
+    object_class->dock_request = gdl_dock_item_dock_request;
+    object_class->dock = gdl_dock_item_dock;
+
+    /* properties */
+
+    g_object_class_install_property (
+        g_object_class, PROP_ORIENTATION,
+        g_param_spec_enum ("orientation", _("Orientation"),
+                           _("Orientation of the docking item"),
+                           GTK_TYPE_ORIENTATION,
+                           GTK_ORIENTATION_VERTICAL,
+                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+                           GDL_DOCK_PARAM_EXPORT));
+
+    /* --- register exporter/importer for GTK_ORIENTATION */
+    g_value_register_transform_func (GTK_TYPE_ORIENTATION, GDL_TYPE_DOCK_PARAM,
+                                     gdl_dock_param_export_gtk_orientation);
+    g_value_register_transform_func (GDL_TYPE_DOCK_PARAM, GTK_TYPE_ORIENTATION,
+                                     gdl_dock_param_import_gtk_orientation);
+    /* --- end of registration */
+    
+    g_object_class_install_property (
+        g_object_class, PROP_RESIZE,
+        g_param_spec_boolean ("resize", _("Resizable"),
+                              _("If set, the dock item can be resized when "
+                                "docked in a paned"),
+                              TRUE,
+                              G_PARAM_READWRITE));
+                                     
+    g_object_class_install_property (
+        g_object_class, PROP_BEHAVIOR,
+        g_param_spec_flags ("behavior", _("Item behavior"),
+                            _("General behavior for the dock item (i.e. "
+                              "whether it can float, if it's locked, etc.)"),
+                            GDL_TYPE_DOCK_ITEM_BEHAVIOR,
+                            GDL_DOCK_ITEM_BEH_NORMAL,
+                            G_PARAM_READWRITE));
+                                     
+    g_object_class_install_property (
+        g_object_class, PROP_LOCKED,
+        g_param_spec_boolean ("locked", _("Locked"),
+                              _("If set, the dock item cannot be dragged around "
+                                "and it doesn't show a grip"),
+                              FALSE,
+                              G_PARAM_READWRITE |
+                              GDL_DOCK_PARAM_EXPORT));
+
+    g_object_class_install_property (
+        g_object_class, PROP_PREFERRED_WIDTH,
+        g_param_spec_int ("preferred-width", _("Preferred width"),
+                          _("Preferred width for the dock item"),
+                          -1, G_MAXINT, -1,
+                          G_PARAM_READWRITE));
+
+    g_object_class_install_property (
+        g_object_class, PROP_PREFERRED_HEIGHT,
+        g_param_spec_int ("preferred-height", _("Preferred height"),
+                          _("Preferred height for the dock item"),
+                          -1, G_MAXINT, -1,
+                          G_PARAM_READWRITE));
+
+    /* signals */
+    
+    gdl_dock_item_signals [DOCK_DRAG_BEGIN] = 
+        g_signal_new ("dock-drag-begin",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_FIRST,
+                      G_STRUCT_OFFSET (GdlDockItemClass, dock_drag_begin),
+                      NULL, /* accumulator */
+                      NULL, /* accu_data */
+                      gdl_marshal_VOID__VOID,
+                      G_TYPE_NONE, 
+                      0);
+
+    gdl_dock_item_signals [DOCK_DRAG_MOTION] = 
+        g_signal_new ("dock-drag-motion",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_FIRST,
+                      G_STRUCT_OFFSET (GdlDockItemClass, dock_drag_motion),
+                      NULL, /* accumulator */
+                      NULL, /* accu_data */
+                      gdl_marshal_VOID__INT_INT,
+                      G_TYPE_NONE, 
+                      2,
+                      G_TYPE_INT,
+                      G_TYPE_INT);
+
+    gdl_dock_item_signals [DOCK_DRAG_END] = 
+        g_signal_new ("dock_drag_end",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_FIRST,
+                      G_STRUCT_OFFSET (GdlDockItemClass, dock_drag_end),
+                      NULL, /* accumulator */
+                      NULL, /* accu_data */
+                      gdl_marshal_VOID__BOOLEAN,
+                      G_TYPE_NONE, 
+                      1,
+                      G_TYPE_BOOLEAN);
+
+    klass->has_grip = TRUE;
+    klass->dock_drag_begin = NULL;
+    klass->dock_drag_motion = NULL;
+    klass->dock_drag_end = NULL;
+    klass->set_orientation = gdl_dock_item_real_set_orientation;
+
+    if (!style_initialized)
+    {
+        style_initialized = TRUE;
+        gtk_rc_parse_string (
+            "style \"gdl-dock-item-default\" {\n"
+            "xthickness = 0\n"
+            "ythickness = 0\n"
+            "}\n"
+            "class \"GdlDockItem\" "
+            "style : gtk \"gdl-dock-item-default\"\n");
+    }
+}
+
+static void
+gdl_dock_item_instance_init (GdlDockItem *item)
+{
+    GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (item), GTK_NO_WINDOW);
+
+    item->child = NULL;
+    
+    item->orientation = GTK_ORIENTATION_VERTICAL;
+    item->behavior = GDL_DOCK_ITEM_BEH_NORMAL;
+
+    item->resize = TRUE;
+
+    item->dragoff_x = item->dragoff_y = 0;
+
+    item->_priv = g_new0 (GdlDockItemPrivate, 1);
+    item->_priv->menu = NULL;
+
+    item->_priv->preferred_width = item->_priv->preferred_height = -1;
+    item->_priv->tab_label = NULL;
+
+    item->_priv->ph = NULL;
+}
+
+static GObject *
+gdl_dock_item_constructor (GType                  type,
+                           guint                  n_construct_properties,
+                           GObjectConstructParam *construct_param)
+{
+    GObject *g_object;
+    
+    g_object = GDL_CALL_PARENT_WITH_DEFAULT (G_OBJECT_CLASS, 
+                                               constructor, 
+                                               (type,
+                                                n_construct_properties,
+                                                construct_param),
+                                               NULL);
+    if (g_object) {
+        GdlDockItem *item = GDL_DOCK_ITEM (g_object);
+
+        if (GDL_DOCK_ITEM_HAS_GRIP (item)) {
+            item->_priv->grip_shown = TRUE;
+            item->_priv->grip = gdl_dock_item_grip_new (item);
+            gtk_widget_set_parent (item->_priv->grip, GTK_WIDGET (item));
+            gtk_widget_show (item->_priv->grip);
+        }
+        else {
+            item->_priv->grip_shown = FALSE;
+        }
+    }
+
+    return g_object;
+}
+
+static void
+gdl_dock_item_set_property  (GObject      *g_object,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+    GdlDockItem *item = GDL_DOCK_ITEM (g_object);
+
+    switch (prop_id) {
+        case PROP_ORIENTATION:
+            gdl_dock_item_set_orientation (item, g_value_get_enum (value));
+            break;
+        case PROP_RESIZE:
+            item->resize = g_value_get_boolean (value);
+            gtk_widget_queue_resize (GTK_WIDGET (item));
+            break;
+        case PROP_BEHAVIOR:
+        {
+            GdlDockItemBehavior old_beh = item->behavior;
+            item->behavior = g_value_get_flags (value);
+
+            if ((old_beh ^ item->behavior) & GDL_DOCK_ITEM_BEH_LOCKED) {
+                if (GDL_DOCK_OBJECT_GET_MASTER (item))
+                    g_signal_emit_by_name (GDL_DOCK_OBJECT_GET_MASTER (item),
+                                           "layout-changed");
+                g_object_notify (g_object, "locked");
+                gdl_dock_item_showhide_grip (item);
+            }
+            
+            break;
+        }
+        case PROP_LOCKED:
+        {
+            GdlDockItemBehavior old_beh = item->behavior;
+
+            if (g_value_get_boolean (value))
+                item->behavior |= GDL_DOCK_ITEM_BEH_LOCKED;
+            else
+                item->behavior &= ~GDL_DOCK_ITEM_BEH_LOCKED;
+
+            if (old_beh ^ item->behavior) {
+                gdl_dock_item_showhide_grip (item);
+                g_object_notify (g_object, "behavior");
+
+                if (GDL_DOCK_OBJECT_GET_MASTER (item))
+                    g_signal_emit_by_name (GDL_DOCK_OBJECT_GET_MASTER (item),
+                                           "layout-changed");
+            }
+            break;
+        }
+        case PROP_PREFERRED_WIDTH:
+            item->_priv->preferred_width = g_value_get_int (value);
+            break;
+        case PROP_PREFERRED_HEIGHT:
+            item->_priv->preferred_height = g_value_get_int (value);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (g_object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+gdl_dock_item_get_property  (GObject      *g_object,
+                             guint         prop_id,
+                             GValue       *value,
+                             GParamSpec   *pspec)
+{
+    GdlDockItem *item = GDL_DOCK_ITEM (g_object);
+    
+    switch (prop_id) {
+        case PROP_ORIENTATION:
+            g_value_set_enum (value, item->orientation);
+            break;
+        case PROP_RESIZE:
+            g_value_set_boolean (value, item->resize);
+            break;
+        case PROP_BEHAVIOR:
+            g_value_set_flags (value, item->behavior);
+            break;
+        case PROP_LOCKED:
+            g_value_set_boolean (value, !GDL_DOCK_ITEM_NOT_LOCKED (item));
+            break;
+        case PROP_PREFERRED_WIDTH:
+            g_value_set_int (value, item->_priv->preferred_width);
+            break;
+        case PROP_PREFERRED_HEIGHT:
+            g_value_set_int (value, item->_priv->preferred_height);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (g_object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+gdl_dock_item_destroy (GtkObject *object)
+{
+    GdlDockItem *item = GDL_DOCK_ITEM (object);
+
+    if (item->_priv) {
+        GdlDockItemPrivate *priv = item->_priv;
+        
+        if (priv->tab_label) {
+            gdl_dock_item_set_tablabel (item, NULL);
+        };
+        if (priv->menu) {
+            gtk_menu_detach (GTK_MENU (priv->menu));
+            priv->menu = NULL;
+        };
+        if (priv->grip) {
+            gtk_container_remove (GTK_CONTAINER (item), priv->grip);
+            priv->grip = NULL;
+        }
+        if (priv->ph) {
+            g_object_unref (priv->ph);
+            priv->ph = NULL;
+        }
+        
+        item->_priv = NULL;
+        g_free (priv);
+    }
+
+    GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
+}
+
+static void 
+gdl_dock_item_add (GtkContainer *container,
+                   GtkWidget    *widget)
+{
+    GdlDockItem *item;
+    
+    g_return_if_fail (GDL_IS_DOCK_ITEM (container));
+
+    item = GDL_DOCK_ITEM (container);
+    if (GDL_IS_DOCK_OBJECT (widget)) {
+        g_warning (_("You can't add a dock object (%p of type %s) inside a %s. "
+                     "Use a GdlDock or some other compound dock object."),
+                   widget, G_OBJECT_TYPE_NAME (widget), G_OBJECT_TYPE_NAME (item));
+        return;
+    }
+
+    if (item->child != NULL) {
+        g_warning (_("Attempting to add a widget with type %s to a %s, "
+                     "but it can only contain one widget at a time; "
+                     "it already contains a widget of type %s"),
+                     G_OBJECT_TYPE_NAME (widget),
+                     G_OBJECT_TYPE_NAME (item),
+                     G_OBJECT_TYPE_NAME (item->child));
+        return;
+    }
+
+    gtk_widget_set_parent (widget, GTK_WIDGET (item));
+    item->child = widget;
+}
+
+static void  
+gdl_dock_item_remove (GtkContainer *container,
+                      GtkWidget    *widget)
+{
+    GdlDockItem *item;
+    gboolean     was_visible;
+    
+    g_return_if_fail (GDL_IS_DOCK_ITEM (container));
+    
+    item = GDL_DOCK_ITEM (container);
+    if (item->_priv && widget == item->_priv->grip) {
+        gboolean grip_was_visible = GTK_WIDGET_VISIBLE (widget);
+        gtk_widget_unparent (widget);
+        item->_priv->grip = NULL;
+        if (grip_was_visible)
+            gtk_widget_queue_resize (GTK_WIDGET (item));
+        return;
+    }
+    
+    if (GDL_DOCK_ITEM_IN_DRAG (item)) {
+        gdl_dock_item_drag_end (item, TRUE);
+    }
+    
+    g_return_if_fail (item->child == widget);
+    
+    was_visible = GTK_WIDGET_VISIBLE (widget);
+
+    gtk_widget_unparent (widget);
+    item->child = NULL;
+    
+    if (was_visible)
+        gtk_widget_queue_resize (GTK_WIDGET (container));
+}
+
+static void
+gdl_dock_item_forall (GtkContainer *container,
+                      gboolean      include_internals,
+                      GtkCallback   callback,
+                      gpointer      callback_data)
+{
+    GdlDockItem *item = (GdlDockItem *) container;
+    
+    g_return_if_fail (callback != NULL);
+    
+    if (include_internals && item->_priv->grip)
+        (* callback) (item->_priv->grip, callback_data);
+    
+    if (item->child)
+        (* callback) (item->child, callback_data);
+}
+
+static GtkType
+gdl_dock_item_child_type (GtkContainer *container)
+{
+    g_return_val_if_fail (GDL_IS_DOCK_ITEM (container), G_TYPE_NONE);
+    
+    if (!GDL_DOCK_ITEM (container)->child)
+        return GTK_TYPE_WIDGET;
+    else
+        return G_TYPE_NONE;
+}
+
+static void
+gdl_dock_item_size_request (GtkWidget      *widget,
+                            GtkRequisition *requisition)
+{
+    GtkRequisition  child_requisition;
+    GtkRequisition  grip_requisition;
+    GdlDockItem    *item;
+
+    g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
+    g_return_if_fail (requisition != NULL);
+
+    item = GDL_DOCK_ITEM (widget);
+
+    /* If our child is not visible, we still request its size, since
+       we won't have any useful hint for our size otherwise.  */
+    if (item->child)
+        gtk_widget_size_request (item->child, &child_requisition);
+    else {
+        child_requisition.width = 0;
+        child_requisition.height = 0;
+    }
+
+    if (item->orientation == GTK_ORIENTATION_HORIZONTAL) {
+        if (GDL_DOCK_ITEM_GRIP_SHOWN (item)) {
+            gtk_widget_size_request (item->_priv->grip, &grip_requisition);
+            requisition->width = grip_requisition.width;
+        } else {
+            requisition->width = 0;
+        }
+
+        if (item->child) {
+            requisition->width += child_requisition.width;
+            requisition->height = child_requisition.height;
+        } else
+            requisition->height = 0;
+    } else {
+        if (GDL_DOCK_ITEM_GRIP_SHOWN (item)) {
+            gtk_widget_size_request (item->_priv->grip, &grip_requisition);
+            requisition->height = grip_requisition.height;
+        } else {
+            requisition->height = 0;
+        }
+
+        if (item->child) {
+            requisition->width = child_requisition.width;
+            requisition->height += child_requisition.height;
+        } else
+            requisition->width = 0;
+    }
+
+    requisition->width += (GTK_CONTAINER (widget)->border_width + widget->style->xthickness) * 2;
+    requisition->height += (GTK_CONTAINER (widget)->border_width + widget->style->ythickness) * 2;
+
+    widget->requisition = *requisition;
+}
+
+static void
+gdl_dock_item_size_allocate (GtkWidget     *widget,
+                             GtkAllocation *allocation)
+{
+    GdlDockItem *item;
+  
+    g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
+    g_return_if_fail (allocation != NULL);
+  
+    item = GDL_DOCK_ITEM (widget);
+
+    widget->allocation = *allocation;
+
+    /* Once size is allocated, preferred size is no longer necessary */
+    item->_priv->preferred_height = -1;
+    item->_priv->preferred_width = -1;
+    
+    if (GTK_WIDGET_REALIZED (widget))
+        gdk_window_move_resize (widget->window,
+                                widget->allocation.x,
+                                widget->allocation.y,
+                                widget->allocation.width,
+                                widget->allocation.height);
+
+    if (item->child && GTK_WIDGET_VISIBLE (item->child)) {
+        GtkAllocation  child_allocation;
+        int            border_width;
+
+        border_width = GTK_CONTAINER (widget)->border_width;
+
+        child_allocation.x = border_width + widget->style->xthickness;
+        child_allocation.y = border_width + widget->style->ythickness;
+        child_allocation.width = allocation->width
+            - 2 * (border_width + widget->style->xthickness);
+        child_allocation.height = allocation->height
+            - 2 * (border_width + widget->style->ythickness);
+        
+        if (GDL_DOCK_ITEM_GRIP_SHOWN (item)) {
+            GtkAllocation grip_alloc = child_allocation;
+            GtkRequisition grip_req;
+            
+            gtk_widget_size_request (item->_priv->grip, &grip_req);
+            
+            if (item->orientation == GTK_ORIENTATION_HORIZONTAL) {
+                child_allocation.x += grip_req.width;
+                child_allocation.width -= grip_req.width;
+                grip_alloc.width = grip_req.width;
+            } else {
+                child_allocation.y += grip_req.height;
+                child_allocation.height -= grip_req.height;
+                grip_alloc.height = grip_req.height;
+            }
+            if (item->_priv->grip)
+                gtk_widget_size_allocate (item->_priv->grip, &grip_alloc);
+        }
+        /* Allocation can't be negative */
+        if (child_allocation.width < 0)
+            child_allocation.width = 0;
+        if (child_allocation.height < 0)
+            child_allocation.height = 0;
+        gtk_widget_size_allocate (item->child, &child_allocation);
+    }
+}
+
+static void
+gdl_dock_item_map (GtkWidget *widget)
+{
+    GdlDockItem *item;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
+
+    GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
+
+    item = GDL_DOCK_ITEM (widget);
+
+    gdk_window_show (widget->window);
+
+    if (item->child
+        && GTK_WIDGET_VISIBLE (item->child)
+        && !GTK_WIDGET_MAPPED (item->child))
+        gtk_widget_map (item->child);
+
+    if (item->_priv->grip
+        && GTK_WIDGET_VISIBLE (item->_priv->grip)
+        && !GTK_WIDGET_MAPPED (item->_priv->grip))
+        gtk_widget_map (item->_priv->grip);
+}
+
+static void
+gdl_dock_item_unmap (GtkWidget *widget)
+{
+    GdlDockItem *item;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
+
+    GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
+    
+    item = GDL_DOCK_ITEM (widget);
+
+    gdk_window_hide (widget->window);
+
+    if (item->_priv->grip)
+        gtk_widget_unmap (item->_priv->grip);
+}
+
+static void
+gdl_dock_item_realize (GtkWidget *widget)
+{
+    GdkWindowAttr  attributes;
+    gint           attributes_mask;
+    GdlDockItem   *item;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
+
+    item = GDL_DOCK_ITEM (widget);
+
+    GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+    /* widget window */
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.wclass = GDK_INPUT_OUTPUT;
+    attributes.visual = gtk_widget_get_visual (widget);
+    attributes.colormap = gtk_widget_get_colormap (widget);
+    attributes.event_mask = (gtk_widget_get_events (widget) |
+                             GDK_EXPOSURE_MASK |
+                             GDK_BUTTON1_MOTION_MASK |
+                             GDK_BUTTON_PRESS_MASK |
+                             GDK_BUTTON_RELEASE_MASK);
+    attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+    widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), 
+                                     &attributes, attributes_mask);
+    gdk_window_set_user_data (widget->window, widget);
+  
+    widget->style = gtk_style_attach (widget->style, widget->window);
+    gtk_style_set_background (widget->style, widget->window, 
+                              GTK_WIDGET_STATE (item));
+    gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
+
+    if (item->child)
+        gtk_widget_set_parent_window (item->child, widget->window);
+    
+    if (item->_priv->grip)
+        gtk_widget_set_parent_window (item->_priv->grip, widget->window);
+}
+
+static void
+gdl_dock_item_style_set (GtkWidget *widget,
+                         GtkStyle  *previous_style)
+{
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
+
+    if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_NO_WINDOW (widget)) {
+        gtk_style_set_background (widget->style, widget->window,
+                                  widget->state);
+        if (GTK_WIDGET_DRAWABLE (widget))
+            gdk_window_clear (widget->window);
+    }
+}
+
+static void
+gdl_dock_item_paint (GtkWidget      *widget,
+                     GdkEventExpose *event)
+{
+    GdlDockItem  *item;
+
+    item = GDL_DOCK_ITEM (widget);
+
+    gtk_paint_box (widget->style,
+                   widget->window,
+                   GTK_WIDGET_STATE (widget),
+                   GTK_SHADOW_NONE,
+                   &event->area, widget,
+                   "dockitem",
+                   0, 0, -1, -1);
+}
+
+static gint
+gdl_dock_item_expose (GtkWidget      *widget,
+                      GdkEventExpose *event)
+{
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (GDL_IS_DOCK_ITEM (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    if (GTK_WIDGET_DRAWABLE (widget) && event->window == widget->window) {
+        gdl_dock_item_paint (widget, event);
+        GDL_CALL_PARENT_GBOOLEAN(GTK_WIDGET_CLASS, expose_event, (widget,event));
+    }
+  
+    return FALSE;
+}
+
+#define EVENT_IN_GRIP_EVENT_WINDOW(ev,gr) \
+    ((gr) != NULL && (ev)->window == GDL_DOCK_ITEM_GRIP (gr)->title_window)
+
+#define EVENT_IN_TABLABEL_EVENT_WINDOW(ev,tl) \
+    ((tl) != NULL && (ev)->window == GDL_DOCK_TABLABEL (tl)->event_window)
+
+static gint
+gdl_dock_item_button_changed (GtkWidget      *widget,
+                              GdkEventButton *event)
+{
+    GdlDockItem *item;
+    gboolean     locked;
+    gboolean     event_handled;
+    gboolean     in_handle;
+    GdkCursor   *cursor;
+  
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (GDL_IS_DOCK_ITEM (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+    
+    item = GDL_DOCK_ITEM (widget);
+
+    if (!EVENT_IN_GRIP_EVENT_WINDOW (event, item->_priv->grip))
+        return FALSE;
+    
+    locked = !GDL_DOCK_ITEM_NOT_LOCKED (item);
+
+    event_handled = FALSE;
+
+    /* Check if user clicked on the drag handle. */      
+    switch (item->orientation) {
+    case GTK_ORIENTATION_HORIZONTAL:
+        in_handle = event->x < item->_priv->grip->allocation.width;
+        break;
+    case GTK_ORIENTATION_VERTICAL:
+        in_handle = event->y < item->_priv->grip->allocation.height;
+        break;
+    default:
+        in_handle = FALSE;
+        break;
+    }
+
+    /* Left mousebutton click on dockitem. */
+    if (!locked && event->button == 1 && event->type == GDK_BUTTON_PRESS) {
+        /* Set in_drag flag, grab pointer and call begin drag operation. */      
+        if (in_handle) {
+            item->_priv->start_x = event->x;
+            item->_priv->start_y = event->y;
+
+            GDL_DOCK_ITEM_SET_FLAGS (item, GDL_DOCK_IN_PREDRAG);
+            
+            cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
+                                                 GDK_FLEUR);
+            gdk_window_set_cursor (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window,
+                                   cursor);
+            gdk_cursor_unref (cursor);
+        
+            event_handled = TRUE;
+        };
+        
+    } else if (!locked &&event->type == GDK_BUTTON_RELEASE && event->button == 1) {
+        if (GDL_DOCK_ITEM_IN_DRAG (item)) {
+            /* User dropped widget somewhere. */
+            gdl_dock_item_drag_end (item, FALSE);
+            event_handled = TRUE;
+        }
+        else if (GDL_DOCK_ITEM_IN_PREDRAG (item)) {
+            GDL_DOCK_ITEM_UNSET_FLAGS (item, GDL_DOCK_IN_PREDRAG);
+            event_handled = TRUE;
+        }
+
+        /* we check the window since if the item was redocked it's
+           been unrealized and maybe it's not realized again yet */
+        if (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window) {
+            cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
+                                                 GDK_HAND2);
+            gdk_window_set_cursor (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window,
+                                   cursor);
+            gdk_cursor_unref (cursor);
+        }
+
+    } else if (event->button == 3 && event->type == GDK_BUTTON_PRESS && in_handle) {
+        gdl_dock_item_popup_menu (item, event->button, event->time);
+        event_handled = TRUE;          
+    }
+
+    return event_handled;
+}
+
+static gint
+gdl_dock_item_motion (GtkWidget      *widget,
+                      GdkEventMotion *event)
+{
+    GdlDockItem *item;
+    gint         new_x, new_y;
+
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (GDL_IS_DOCK_ITEM (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    item = GDL_DOCK_ITEM (widget);
+
+    if (!EVENT_IN_GRIP_EVENT_WINDOW (event, item->_priv->grip))
+        return FALSE;
+
+    if (GDL_DOCK_ITEM_IN_PREDRAG (item)) {
+        if (gtk_drag_check_threshold (widget,
+                                      item->_priv->start_x,
+                                      item->_priv->start_y,
+                                      event->x,
+                                      event->y)) {
+            GDL_DOCK_ITEM_UNSET_FLAGS (item, GDL_DOCK_IN_PREDRAG);
+            item->dragoff_x = item->_priv->start_x;
+            item->dragoff_y = item->_priv->start_y;
+
+            gdl_dock_item_drag_start (item);
+        }
+    }
+    
+    if (!GDL_DOCK_ITEM_IN_DRAG (item))
+        return FALSE;
+
+    new_x = event->x_root;
+    new_y = event->y_root;
+    
+    g_signal_emit (item, gdl_dock_item_signals [DOCK_DRAG_MOTION], 
+                   0, new_x, new_y);
+
+    return TRUE;
+}
+
+static gboolean
+gdl_dock_item_key_press (GtkWidget   *widget,
+                         GdkEventKey *event)
+{
+    gboolean event_handled = FALSE;
+    if (GDL_DOCK_ITEM_IN_DRAG (widget)) {
+        if (event->keyval == GDK_Escape) {
+            gdl_dock_item_drag_end (GDL_DOCK_ITEM (widget), TRUE);
+            event_handled = TRUE;
+        }
+    }
+
+    if (event_handled)
+        return TRUE;
+    else
+        return GDL_CALL_PARENT_WITH_DEFAULT (GTK_WIDGET_CLASS,
+                                               key_press_event,
+                                               (widget, event),
+                                               FALSE);
+}
+
+static gboolean
+gdl_dock_item_dock_request (GdlDockObject  *object,
+                            gint            x,
+                            gint            y,
+                            GdlDockRequest *request)
+{
+    GtkAllocation *alloc;
+    gint           rel_x, rel_y;
+
+    /* we get (x,y) in our allocation coordinates system */
+    
+    /* Get item's allocation. */
+    alloc = &(GTK_WIDGET (object)->allocation);
+    
+    /* Get coordinates relative to our window. */
+    rel_x = x - alloc->x;
+    rel_y = y - alloc->y;
+
+    /* Location is inside. */
+    if (rel_x > 0 && rel_x < alloc->width &&
+        rel_y > 0 && rel_y < alloc->height) {
+        float rx, ry;
+        GtkRequisition my, other;
+        gint divider = -1;
+        
+        /* this are for calculating the extra docking parameter */
+        gdl_dock_item_preferred_size (GDL_DOCK_ITEM (request->applicant), &other);
+        gdl_dock_item_preferred_size (GDL_DOCK_ITEM (object), &my);
+        
+        /* Calculate location in terms of the available space (0-100%). */
+        rx = (float) rel_x / alloc->width;
+        ry = (float) rel_y / alloc->height;
+
+        /* Determine dock location. */
+        if (rx < SPLIT_RATIO) {
+            request->position = GDL_DOCK_LEFT;
+            divider = other.width;
+        }
+        else if (rx > (1 - SPLIT_RATIO)) {
+            request->position = GDL_DOCK_RIGHT;
+            rx = 1 - rx;
+            divider = MAX (0, my.width - other.width);
+        }
+        else if (ry < SPLIT_RATIO && ry < rx) {
+            request->position = GDL_DOCK_TOP;
+            divider = other.height;
+        }
+        else if (ry > (1 - SPLIT_RATIO) && (1 - ry) < rx) {
+            request->position = GDL_DOCK_BOTTOM;
+            divider = MAX (0, my.height - other.height);
+        }
+        else
+            request->position = GDL_DOCK_CENTER;
+
+        /* Reset rectangle coordinates to entire item. */
+        request->rect.x = 0;
+        request->rect.y = 0;
+        request->rect.width = alloc->width;
+        request->rect.height = alloc->height;
+
+        GdlDockItemBehavior behavior = GDL_DOCK_ITEM(object)->behavior;
+
+        /* Calculate docking indicator rectangle size for new locations. Only
+           do this when we're not over the item's current location. */
+        if (request->applicant != object) {
+            switch (request->position) {
+                case GDL_DOCK_TOP:
+                    if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_TOP)
+                        return FALSE;
+                    request->rect.height *= SPLIT_RATIO;
+                    break;
+                case GDL_DOCK_BOTTOM:
+                    if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_BOTTOM)
+                        return FALSE;
+                    request->rect.y += request->rect.height * (1 - SPLIT_RATIO);
+                    request->rect.height *= SPLIT_RATIO;
+                    break;
+                case GDL_DOCK_LEFT:
+                    if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_LEFT)
+                        return FALSE;
+                    request->rect.width *= SPLIT_RATIO;
+                    break;
+                case GDL_DOCK_RIGHT:
+                    if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_RIGHT)
+                        return FALSE;
+                    request->rect.x += request->rect.width * (1 - SPLIT_RATIO);
+                    request->rect.width *= SPLIT_RATIO;
+                    break;
+                case GDL_DOCK_CENTER:
+                    if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_CENTER)
+                        return FALSE;
+                    request->rect.x = request->rect.width * SPLIT_RATIO/2;
+                    request->rect.y = request->rect.height * SPLIT_RATIO/2;
+                    request->rect.width = (request->rect.width *
+                                           (1 - SPLIT_RATIO/2)) - request->rect.x;
+                    request->rect.height = (request->rect.height *
+                                            (1 - SPLIT_RATIO/2)) - request->rect.y;
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        /* adjust returned coordinates so they are have the same
+           origin as our window */
+        request->rect.x += alloc->x;
+        request->rect.y += alloc->y;
+        
+        /* Set possible target location and return TRUE. */            
+        request->target = object;
+
+        /* fill-in other dock information */
+        if (request->position != GDL_DOCK_CENTER && divider >= 0) {
+            if (G_IS_VALUE (&request->extra))
+                g_value_unset (&request->extra);
+            g_value_init (&request->extra, G_TYPE_UINT);
+            g_value_set_uint (&request->extra, (guint) divider);
+        }
+        
+        return TRUE;         
+    }
+    else /* No docking possible at this location. */            
+        return FALSE;
+}
+
+static void
+gdl_dock_item_dock (GdlDockObject    *object,
+                    GdlDockObject    *requestor,
+                    GdlDockPlacement  position,
+                    GValue           *other_data)
+{
+    GdlDockObject *new_parent, *parent;
+    gboolean       add_ourselves_first;
+
+    guint         available_space=0;
+    gint          pref_size=-1;
+    guint         splitpos=0;
+    GtkRequisition req, object_req, parent_req;
+    
+    parent = gdl_dock_object_get_parent_object (object);
+    gdl_dock_item_preferred_size (GDL_DOCK_ITEM (requestor), &req);
+    gdl_dock_item_preferred_size (GDL_DOCK_ITEM (object), &object_req);
+    if (GDL_IS_DOCK_ITEM (parent))
+        gdl_dock_item_preferred_size (GDL_DOCK_ITEM (parent), &parent_req);
+    else
+    {
+        parent_req.height = GTK_WIDGET (parent)->allocation.height;
+        parent_req.width = GTK_WIDGET (parent)->allocation.width;
+    }
+    
+    /* If preferred size is not set on the requestor (perhaps a new item),
+     * then estimate and set it. The default value (either 0 or 1 pixels) is
+     * not any good.
+     */
+    switch (position) {
+        case GDL_DOCK_TOP:
+        case GDL_DOCK_BOTTOM:
+            if (req.width < 2)
+            {
+                req.width = object_req.width;
+                g_object_set (requestor, "preferred-width", req.width, NULL);
+            }
+            if (req.height < 2)
+            {
+                req.height = NEW_DOCK_ITEM_RATIO * object_req.height;
+                g_object_set (requestor, "preferred-height", req.height, NULL);
+            }
+            if (req.width > 1)
+                g_object_set (object, "preferred-width", req.width, NULL);
+            if (req.height > 1)
+                g_object_set (object, "preferred-height",
+                              object_req.height - req.height, NULL);
+            break;
+        case GDL_DOCK_LEFT:
+        case GDL_DOCK_RIGHT:
+            if (req.height < 2)
+            {
+                req.height = object_req.height;
+                g_object_set (requestor, "preferred-height", req.height, NULL);
+            }
+            if (req.width < 2)
+            {
+                req.width = NEW_DOCK_ITEM_RATIO * object_req.width;
+                g_object_set (requestor, "preferred-width", req.width, NULL);
+            }
+            if (req.height > 1)
+                g_object_set (object, "preferred-height", req.height, NULL);
+            if (req.width > 1)
+                g_object_set (object, "preferred-width",
+                          object_req.width - req.width, NULL);
+            break;
+        case GDL_DOCK_CENTER:
+            if (req.height < 2)
+            {
+                req.height = object_req.height;
+                g_object_set (requestor, "preferred-height", req.height, NULL);
+            }
+            if (req.width < 2)
+            {
+                req.width = object_req.width;
+                g_object_set (requestor, "preferred-width", req.width, NULL);
+            }
+            if (req.height > 1)
+                g_object_set (object, "preferred-height", req.height, NULL);
+            if (req.width > 1)
+                g_object_set (object, "preferred-width", req.width, NULL);
+            break;
+        default: 
+        {
+            GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_ref (GDL_TYPE_DOCK_PLACEMENT));
+            GEnumValue *enum_value = g_enum_get_value (enum_class, position);
+            const gchar *name = enum_value ? enum_value->value_name : NULL;
+            
+            g_warning (_("Unsupported docking strategy %s in dock object of type %s"),
+                       name,  G_OBJECT_TYPE_NAME (object));
+            g_type_class_unref (enum_class);
+            return;
+        }
+    }
+    switch (position) {
+        case GDL_DOCK_TOP:
+        case GDL_DOCK_BOTTOM:
+            /* get a paned style dock object */
+            new_parent = g_object_new (gdl_dock_object_type_from_nick ("paned"),
+                                       "orientation", GTK_ORIENTATION_VERTICAL,
+                                       "preferred-width", object_req.width,
+                                       "preferred-height", object_req.height,
+                                       NULL);
+            add_ourselves_first = (position == GDL_DOCK_BOTTOM);
+            if (parent)
+                available_space = parent_req.height;
+            pref_size = req.height;
+            break;
+        case GDL_DOCK_LEFT:
+        case GDL_DOCK_RIGHT:
+            new_parent = g_object_new (gdl_dock_object_type_from_nick ("paned"),
+                                       "orientation", GTK_ORIENTATION_HORIZONTAL,
+                                       "preferred-width", object_req.width,
+                                       "preferred-height", object_req.height,
+                                       NULL);
+            add_ourselves_first = (position == GDL_DOCK_RIGHT);
+            if(parent)
+                available_space = parent_req.width;
+            pref_size = req.width;
+            break;
+        case GDL_DOCK_CENTER:
+            new_parent = g_object_new (gdl_dock_object_type_from_nick ("notebook"),
+                                       "preferred-width", object_req.width,
+                                       "preferred-height", object_req.height,
+                                       NULL);
+            add_ourselves_first = TRUE;
+            break;
+        default: 
+        {
+            GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_ref (GDL_TYPE_DOCK_PLACEMENT));
+            GEnumValue *enum_value = g_enum_get_value (enum_class, position);
+            const gchar *name = enum_value ? enum_value->value_name : NULL;
+            
+            g_warning (_("Unsupported docking strategy %s in dock object of type %s"),
+                       name,  G_OBJECT_TYPE_NAME (object));
+            g_type_class_unref (enum_class);
+            return;
+        }
+    }
+
+    /* freeze the parent so it doesn't reduce automatically */
+    if (parent)
+        gdl_dock_object_freeze (parent);
+
+    /* ref ourselves since we could be destroyed when detached */
+    g_object_ref (object);
+    GDL_DOCK_OBJECT_SET_FLAGS (object, GDL_DOCK_IN_REFLOW);
+    gdl_dock_object_detach (object, FALSE);
+
+    /* freeze the new parent, so reduce won't get called before it's
+       actually added to our parent */
+    gdl_dock_object_freeze (new_parent);
+    
+    /* bind the new parent to our master, so the following adds work */
+    gdl_dock_object_bind (new_parent, G_OBJECT (GDL_DOCK_OBJECT_GET_MASTER (object)));
+    
+    /* add the objects */
+    if (add_ourselves_first) {
+        gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (object));
+        gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (requestor));
+        splitpos = available_space - pref_size;
+    } else {
+        gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (requestor));
+        gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (object));
+        splitpos = pref_size;
+    }
+
+    /* add the new parent to the parent */
+    if (parent)
+        gtk_container_add (GTK_CONTAINER (parent), GTK_WIDGET (new_parent));
+
+    /* show automatic object */
+    if (GTK_WIDGET_VISIBLE (object))
+        gtk_widget_show (GTK_WIDGET (new_parent));
+    
+    /* use extra docking parameter */
+    if (position != GDL_DOCK_CENTER && other_data &&
+        G_VALUE_HOLDS (other_data, G_TYPE_UINT)) {
+        
+        g_object_set (G_OBJECT (new_parent),
+                      "position", g_value_get_uint (other_data),
+                      NULL);
+    } else if (splitpos > 0 && splitpos < available_space) {
+        g_object_set (G_OBJECT (new_parent), "position", splitpos, NULL);
+    }
+    
+    GDL_DOCK_OBJECT_UNSET_FLAGS (object, GDL_DOCK_IN_REFLOW);
+    g_object_unref (object);
+
+    gdl_dock_object_thaw (new_parent);
+    if (parent)
+        gdl_dock_object_thaw (parent);
+}
+
+static void
+gdl_dock_item_detach_menu (GtkWidget *widget,
+                           GtkMenu   *menu)
+{
+    GdlDockItem *item;
+   
+    item = GDL_DOCK_ITEM (widget);
+    item->_priv->menu = NULL;
+}
+
+static void
+gdl_dock_item_popup_menu (GdlDockItem  *item, 
+                          guint         button,
+                          guint32       time)
+{
+    GtkWidget *mitem;
+
+    if (!item->_priv->menu) {
+        /* Create popup menu and attach it to the dock item */
+        item->_priv->menu = gtk_menu_new ();
+        gtk_menu_attach_to_widget (GTK_MENU (item->_priv->menu),
+                                   GTK_WIDGET (item),
+                                   gdl_dock_item_detach_menu);
+        
+        if (item->behavior & GDL_DOCK_ITEM_BEH_LOCKED) {
+            /* UnLock menuitem */
+            mitem = gtk_menu_item_new_with_label (_("UnLock"));
+            gtk_menu_shell_append (GTK_MENU_SHELL (item->_priv->menu), 
+                                   mitem);
+            g_signal_connect (mitem, "activate",
+                              G_CALLBACK (gdl_dock_item_unlock_cb), item);
+        } else {
+            /* Hide menuitem. */
+            mitem = gtk_menu_item_new_with_label (_("Hide"));
+            gtk_menu_shell_append (GTK_MENU_SHELL (item->_priv->menu), mitem);
+            g_signal_connect (mitem, "activate", 
+                              G_CALLBACK (gdl_dock_item_hide_cb), item);
+            /* Lock menuitem */
+            mitem = gtk_menu_item_new_with_label (_("Lock"));
+            gtk_menu_shell_append (GTK_MENU_SHELL (item->_priv->menu), mitem);
+            g_signal_connect (mitem, "activate",
+                              G_CALLBACK (gdl_dock_item_lock_cb), item);
+        }
+    }
+
+    /* Show popup menu. */
+    gtk_widget_show_all (item->_priv->menu);
+    gtk_menu_popup (GTK_MENU (item->_priv->menu), NULL, NULL, NULL, NULL, 
+                    button, time);
+}
+
+static void
+gdl_dock_item_drag_start (GdlDockItem *item)
+{
+    GdkCursor *fleur;
+
+    if (!GTK_WIDGET_REALIZED (item))
+        gtk_widget_realize (GTK_WIDGET (item));
+    
+    GDL_DOCK_ITEM_SET_FLAGS (item, GDL_DOCK_IN_DRAG);
+            
+    /* grab the pointer so we receive all mouse events */
+    fleur = gdk_cursor_new (GDK_FLEUR);
+
+    /* grab the keyboard & pointer */
+    gtk_grab_add (GTK_WIDGET (item));
+    
+    gdk_cursor_unref (fleur);
+            
+    g_signal_emit (item, gdl_dock_item_signals [DOCK_DRAG_BEGIN], 0);
+}
+
+static void
+gdl_dock_item_drag_end (GdlDockItem *item,
+                        gboolean     cancel)
+{
+    /* Release pointer & keyboard. */
+    gtk_grab_remove (gtk_grab_get_current ());
+    
+    g_signal_emit (item, gdl_dock_item_signals [DOCK_DRAG_END], 0, cancel);
+    
+    GDL_DOCK_ITEM_UNSET_FLAGS (item, GDL_DOCK_IN_DRAG);
+}
+
+static void 
+gdl_dock_item_tab_button (GtkWidget      *widget,
+                          GdkEventButton *event,
+                          gpointer        data)
+{
+    GdlDockItem *item;
+
+    item = GDL_DOCK_ITEM (data);
+
+    if (!GDL_DOCK_ITEM_NOT_LOCKED (item))
+        return;
+
+    switch (event->button) {
+    case 1:
+        /* set dragoff_{x,y} as we the user clicked on the middle of the 
+           drag handle */
+        switch (item->orientation) {
+        case GTK_ORIENTATION_HORIZONTAL:
+            /*item->dragoff_x = item->_priv->grip_size / 2;*/
+            item->dragoff_y = GTK_WIDGET (data)->allocation.height / 2;
+            break;
+        case GTK_ORIENTATION_VERTICAL:
+            /*item->dragoff_x = GTK_WIDGET (data)->allocation.width / 2;*/
+            item->dragoff_y = item->_priv->grip_size / 2;
+            break;
+        };
+        gdl_dock_item_drag_start (item);
+        break;
+
+    case 3:
+        gdl_dock_item_popup_menu (item, event->button, event->time);
+        break;
+
+    default:
+        break;
+    };
+}
+
+static void
+gdl_dock_item_hide_cb (GtkWidget   *widget, 
+                       GdlDockItem *item)
+{
+    GdlDockMaster *master;
+    
+    g_return_if_fail (item != NULL);
+
+    master = GDL_DOCK_OBJECT_GET_MASTER (item);
+    gdl_dock_item_hide_item (item);
+}
+
+static void
+gdl_dock_item_lock_cb (GtkWidget   *widget,
+                       GdlDockItem *item)
+{
+    g_return_if_fail (item != NULL);
+
+    gdl_dock_item_lock (item);
+}
+
+static void
+gdl_dock_item_unlock_cb (GtkWidget   *widget,
+                       GdlDockItem *item)
+{
+    g_return_if_fail (item != NULL);
+
+    gdl_dock_item_unlock (item);
+}
+
+static void
+gdl_dock_item_showhide_grip (GdlDockItem *item)
+{
+    GdkDisplay *display;
+    GdkCursor *cursor;
+    
+    gdl_dock_item_detach_menu (GTK_WIDGET (item), NULL); 
+    display = gtk_widget_get_display (GTK_WIDGET (item));
+    cursor = NULL;
+    
+    if (item->_priv->grip) {
+        if (GDL_DOCK_ITEM_GRIP_SHOWN (item) && 
+            GDL_DOCK_ITEM_NOT_LOCKED(item))
+             cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
+    }
+    if (item->_priv->grip && GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window)
+        gdk_window_set_cursor (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window, cursor);
+
+    if (cursor)
+        gdk_cursor_unref (cursor);
+    
+    gtk_widget_queue_resize (GTK_WIDGET (item));
+}
+
+static void
+gdl_dock_item_real_set_orientation (GdlDockItem    *item,
+                                    GtkOrientation  orientation)
+{
+    item->orientation = orientation;
+    
+    if (GTK_WIDGET_DRAWABLE (item))
+        gtk_widget_queue_draw (GTK_WIDGET (item));
+    gtk_widget_queue_resize (GTK_WIDGET (item));
+}
+
+
+/* ----- Public interface ----- */
+
+GtkWidget *
+gdl_dock_item_new (const gchar         *name,
+                   const gchar         *long_name,
+                   GdlDockItemBehavior  behavior)
+{
+    GdlDockItem *item;
+
+    item = GDL_DOCK_ITEM (g_object_new (GDL_TYPE_DOCK_ITEM, 
+                                        "name", name, 
+                                        "long-name", long_name,
+                                        "behavior", behavior,
+                                        NULL));
+    GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_AUTOMATIC);
+    gdl_dock_item_set_tablabel (item, gtk_label_new (long_name));
+    return GTK_WIDGET (item);
+}
+
+GtkWidget *
+gdl_dock_item_new_with_stock (const gchar         *name,
+                              const gchar         *long_name,
+                              const gchar         *stock_id,
+                              GdlDockItemBehavior  behavior)
+{
+    GdlDockItem *item;
+
+    item = GDL_DOCK_ITEM (g_object_new (GDL_TYPE_DOCK_ITEM, 
+                                        "name", name, 
+                                        "long-name", long_name,
+                                        "stock-id", stock_id,
+                                        "behavior", behavior,
+                                        NULL));
+    GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_AUTOMATIC);
+    gdl_dock_item_set_tablabel (item, gtk_label_new (long_name));
+
+    return GTK_WIDGET (item);
+}
+
+GtkWidget *
+gdl_dock_item_new_with_pixbuf_icon (const gchar         *name,
+                                    const gchar         *long_name,
+                                    const GdkPixbuf     *pixbuf_icon,
+                                    GdlDockItemBehavior  behavior)
+{
+    GdlDockItem *item;
+
+    item = GDL_DOCK_ITEM (g_object_new (GDL_TYPE_DOCK_ITEM, 
+                                        "name", name, 
+                                        "long-name", long_name,
+                                        "pixbuf-icon", pixbuf_icon,
+                                        "behavior", behavior,
+                                        NULL));
+
+    GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_AUTOMATIC);
+    gdl_dock_item_set_tablabel (item, gtk_label_new (long_name));
+
+    return GTK_WIDGET (item);
+}
+
+/* convenient function (and to preserve source compat) */
+void
+gdl_dock_item_dock_to (GdlDockItem      *item,
+                       GdlDockItem      *target,
+                       GdlDockPlacement  position,
+                       gint              docking_param)
+{
+    g_return_if_fail (item != NULL);
+    g_return_if_fail (item != target);
+    g_return_if_fail (target != NULL || position == GDL_DOCK_FLOATING);
+    g_return_if_fail ((item->behavior & GDL_DOCK_ITEM_BEH_NEVER_FLOATING) == 0 || position != GDL_DOCK_FLOATING);
+
+    if (position == GDL_DOCK_FLOATING || !target) {
+        GdlDockObject *controller;
+
+        if (!gdl_dock_object_is_bound (GDL_DOCK_OBJECT (item))) {
+            g_warning (_("Attempt to bind an unbound item %p"), item);
+            return;
+        }
+
+        controller = gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (item));
+        
+        /* FIXME: save previous docking position for later
+           re-docking... does this make sense now? */
+
+        /* Create new floating dock for widget. */
+        item->dragoff_x = item->dragoff_y = 0;
+        gdl_dock_add_floating_item (GDL_DOCK (controller),
+                                    item, 0, 0, -1, -1);
+
+    } else
+        gdl_dock_object_dock (GDL_DOCK_OBJECT (target),
+                              GDL_DOCK_OBJECT (item),
+                              position, NULL);
+}
+
+void
+gdl_dock_item_set_orientation (GdlDockItem    *item,
+                               GtkOrientation  orientation)
+{
+    GParamSpec *pspec;
+
+    g_return_if_fail (item != NULL);
+
+    if (item->orientation != orientation) {
+        /* push the property down the hierarchy if our child supports it */
+        if (item->child != NULL) {
+            pspec = g_object_class_find_property (
+                G_OBJECT_GET_CLASS (item->child), "orientation");
+            if (pspec && pspec->value_type == GTK_TYPE_ORIENTATION)
+                g_object_set (G_OBJECT (item->child),
+                              "orientation", orientation,
+                              NULL);
+        };
+
+        GDL_CALL_VIRTUAL (item, GDL_DOCK_ITEM_GET_CLASS, set_orientation, (item, orientation));
+        g_object_notify (G_OBJECT (item), "orientation");
+    }
+}
+
+GtkWidget *
+gdl_dock_item_get_tablabel (GdlDockItem *item)
+{
+    g_return_val_if_fail (item != NULL, NULL);
+    g_return_val_if_fail (GDL_IS_DOCK_ITEM (item), NULL);
+
+    return item->_priv->tab_label;
+}
+
+void
+gdl_dock_item_set_tablabel (GdlDockItem *item,
+                            GtkWidget   *tablabel)
+{
+    g_return_if_fail (item != NULL);
+
+    if (item->_priv->tab_label) {
+        /* disconnect and unref the previous tablabel */
+        if (GDL_IS_DOCK_TABLABEL (item->_priv->tab_label)) {
+            g_signal_handlers_disconnect_matched (item->_priv->tab_label,
+                                                  G_SIGNAL_MATCH_DATA,
+                                                  0, 0, NULL,
+                                                  NULL, item);
+            g_object_set (item->_priv->tab_label, "item", NULL, NULL);
+        }
+        gtk_widget_unref (item->_priv->tab_label);
+        item->_priv->tab_label = NULL;
+    }
+    
+    if (tablabel) {
+        gtk_widget_ref (tablabel);
+        gtk_object_sink (GTK_OBJECT (tablabel));
+        item->_priv->tab_label = tablabel;
+        if (GDL_IS_DOCK_TABLABEL (tablabel)) {
+            g_object_set (tablabel, "item", item, NULL);
+            /* connect to tablabel signal */
+            g_signal_connect (tablabel, "button_pressed_handle",
+                              G_CALLBACK (gdl_dock_item_tab_button), item);
+        }
+    }
+}
+
+void 
+gdl_dock_item_hide_grip (GdlDockItem *item)
+{
+    g_return_if_fail (item != NULL);
+    if (item->_priv->grip_shown) {
+        item->_priv->grip_shown = FALSE;
+        gdl_dock_item_showhide_grip (item);
+    };
+    g_warning ("Grips always show unless GDL_DOCK_ITEM_BEH_NO_GRIP is set\n" );
+}
+
+void
+gdl_dock_item_show_grip (GdlDockItem *item)
+{
+    g_return_if_fail (item != NULL);
+    if (!item->_priv->grip_shown) {
+        item->_priv->grip_shown = TRUE;
+        gdl_dock_item_showhide_grip (item);
+    };
+}
+
+/* convenient function (and to preserve source compat) */
+void
+gdl_dock_item_bind (GdlDockItem *item,
+                    GtkWidget   *dock)
+{
+    g_return_if_fail (item != NULL);
+    g_return_if_fail (dock == NULL || GDL_IS_DOCK (dock));
+    
+    gdl_dock_object_bind (GDL_DOCK_OBJECT (item),
+                          G_OBJECT (GDL_DOCK_OBJECT_GET_MASTER (dock)));
+}
+
+/* convenient function (and to preserve source compat) */
+void
+gdl_dock_item_unbind (GdlDockItem *item)
+{
+    g_return_if_fail (item != NULL);
+
+    gdl_dock_object_unbind (GDL_DOCK_OBJECT (item));
+}
+
+void
+gdl_dock_item_hide_item (GdlDockItem *item)
+{
+    g_return_if_fail (item != NULL);
+
+    if (!GDL_DOCK_OBJECT_ATTACHED (item))
+        /* already hidden/detached */
+        return;
+       
+    /* if the object is manual, create a new placeholder to be able to
+       restore the position later */
+    if (!GDL_DOCK_OBJECT_AUTOMATIC (item)) {
+        if (item->_priv->ph)
+            g_object_unref (item->_priv->ph); 
+        
+        gboolean isFloating = FALSE;
+        gint width=0, height=0, x=0, y = 0;
+        
+        if (GDL_IS_DOCK (gdl_dock_object_get_parent_object (GDL_DOCK_OBJECT (item))))
+        {
+            GdlDock* dock = GDL_DOCK (gdl_dock_object_get_parent_object (GDL_DOCK_OBJECT (item)));
+            g_object_get (dock,
+                          "floating", &isFloating, 
+                          "width", &width,
+                          "height",&height,
+                          "floatx",&x,
+                          "floaty",&y,
+                          NULL);
+        } else {
+            item->_priv->preferred_width=GTK_WIDGET (item)->allocation.width;
+            item->_priv->preferred_height=GTK_WIDGET (item)->allocation.height;
+        }
+        item->_priv->ph = GDL_DOCK_PLACEHOLDER (
+            g_object_new (GDL_TYPE_DOCK_PLACEHOLDER,
+                          "sticky", FALSE,
+                          "host", item,
+                          "width", width,
+                          "height", height,
+                          "floating", isFloating,
+                          "floatx", x,
+                          "floaty", y,
+                          NULL));
+        g_object_ref (item->_priv->ph);
+        gtk_object_sink (GTK_OBJECT (item->_priv->ph));
+    }
+    
+    gdl_dock_object_freeze (GDL_DOCK_OBJECT (item));
+    
+    /* hide our children first, so they can also set placeholders */
+    if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT (item))) 
+        gtk_container_foreach (GTK_CONTAINER (item),
+                               (GtkCallback) gdl_dock_item_hide_item,
+                               NULL);
+    
+    /* detach the item recursively */
+    gdl_dock_object_detach (GDL_DOCK_OBJECT (item), TRUE);
+
+    gtk_widget_hide (GTK_WIDGET (item));
+
+    gdl_dock_object_thaw (GDL_DOCK_OBJECT (item));
+}
+
+void
+gdl_dock_item_iconify_item (GdlDockItem *item)
+{
+    g_return_if_fail (item != NULL);
+    
+    GDL_DOCK_OBJECT_SET_FLAGS (item, GDL_DOCK_ICONIFIED);
+    gdl_dock_item_hide_item (item);
+}
+
+void
+gdl_dock_item_show_item (GdlDockItem *item)
+{
+    g_return_if_fail (item != NULL);
+
+    GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_ICONIFIED);
+    
+    if (item->_priv->ph) {
+        gboolean isFloating=FALSE;
+        gint width = 0, height = 0, x= 0, y = 0;
+        g_object_get (G_OBJECT(item->_priv->ph),
+                      "width", &width,
+                      "height", &height,
+                      "floating",&isFloating,
+                      "floatx", &x,
+                      "floaty", &y,
+                      NULL);
+        if (isFloating) {
+            GdlDockObject *controller =
+                gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (item));
+            gdl_dock_add_floating_item (GDL_DOCK (controller),
+                                        item, x, y, width, height);
+        } else {
+            gtk_container_add (GTK_CONTAINER (item->_priv->ph),
+                               GTK_WIDGET (item));
+        }
+        g_object_unref (item->_priv->ph);
+        item->_priv->ph = NULL;
+        
+    } else if (gdl_dock_object_is_bound (GDL_DOCK_OBJECT (item))) {
+        GdlDockObject *toplevel;
+        
+        toplevel = gdl_dock_master_get_controller
+                        (GDL_DOCK_OBJECT_GET_MASTER (item));
+        
+        if (item->behavior & GDL_DOCK_ITEM_BEH_NEVER_FLOATING) {
+            g_warning("Object %s has no default position and flag GDL_DOCK_ITEM_BEH_NEVER_FLOATING is set.\n",
+                      GDL_DOCK_OBJECT(item)->name);
+        } else if (toplevel) {
+            gdl_dock_object_dock (toplevel, GDL_DOCK_OBJECT (item),
+                                  GDL_DOCK_FLOATING, NULL);
+        } else
+            g_warning("There is no toplevel window. GdlDockItem %s cannot be shown.\n", GDL_DOCK_OBJECT(item)->name);
+        
+    } else
+        g_warning("GdlDockItem %s is not bound. It cannot be shown.\n",
+                  GDL_DOCK_OBJECT(item)->name);
+    
+    gtk_widget_show (GTK_WIDGET (item));
+}
+
+void
+gdl_dock_item_lock (GdlDockItem *item)
+{
+    g_object_set (item, "locked", TRUE, NULL);
+}
+
+void
+gdl_dock_item_unlock (GdlDockItem *item)
+{
+    g_object_set (item, "locked", FALSE, NULL);
+}
+
+void 
+gdl_dock_item_set_default_position (GdlDockItem   *item,
+                                    GdlDockObject *reference)
+{
+    g_return_if_fail (item != NULL);
+
+    if (item->_priv->ph) {
+        g_object_unref (item->_priv->ph);
+        item->_priv->ph = NULL;
+    }
+
+    if (reference && GDL_DOCK_OBJECT_ATTACHED (reference)) {
+        if (GDL_IS_DOCK_PLACEHOLDER (reference)) {
+            g_object_ref (reference);
+            gtk_object_sink (GTK_OBJECT (reference));
+            item->_priv->ph = GDL_DOCK_PLACEHOLDER (reference);
+        } else {
+            item->_priv->ph = GDL_DOCK_PLACEHOLDER (
+                g_object_new (GDL_TYPE_DOCK_PLACEHOLDER,
+                              "sticky", TRUE,
+                              "host", reference,
+                              NULL));
+            g_object_ref (item->_priv->ph);
+            gtk_object_sink (GTK_OBJECT (item->_priv->ph));
+        }
+    }
+}
+
+void 
+gdl_dock_item_preferred_size (GdlDockItem    *item,
+                              GtkRequisition *req)
+{
+    if (!req)
+        return;
+
+    req->width = MAX (item->_priv->preferred_width,
+                      GTK_WIDGET (item)->allocation.width);
+    req->height = MAX (item->_priv->preferred_height,
+                       GTK_WIDGET (item)->allocation.height);
+}
+
+
+/* ----- gtk orientation type exporter/importer ----- */
+
+static void 
+gdl_dock_param_export_gtk_orientation (const GValue *src,
+                                       GValue       *dst)
+{
+    dst->data [0].v_pointer =
+        g_strdup_printf ("%s", (src->data [0].v_int == GTK_ORIENTATION_HORIZONTAL) ?
+                         "horizontal" : "vertical");
+}
+
+static void 
+gdl_dock_param_import_gtk_orientation (const GValue *src,
+                                       GValue       *dst)
+{
+    if (!strcmp (src->data [0].v_pointer, "horizontal"))
+        dst->data [0].v_int = GTK_ORIENTATION_HORIZONTAL;
+    else
+        dst->data [0].v_int = GTK_ORIENTATION_VERTICAL;
+}
+
diff --git a/src/libgdl/gdl-dock-item.h b/src/libgdl/gdl-dock-item.h
new file mode 100644 (file)
index 0000000..6eec28a
--- /dev/null
@@ -0,0 +1,191 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * gdl-dock-item.h
+ *
+ * Author: Gustavo Giráldez <gustavo.giraldez@gmx.net>
+ *
+ * Based on GnomeDockItem/BonoboDockItem.  Original copyright notice follows.
+ *
+ * Copyright (C) 1998 Ettore Perazzoli
+ * Copyright (C) 1998 Elliot Lee
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald 
+ * All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GDL_DOCK_ITEM_H__
+#define __GDL_DOCK_ITEM_H__
+
+#include "libgdl/gdl-dock-object.h"
+
+G_BEGIN_DECLS
+
+/* standard macros */
+#define GDL_TYPE_DOCK_ITEM            (gdl_dock_item_get_type ())
+#define GDL_DOCK_ITEM(obj)            (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_ITEM, GdlDockItem))
+#define GDL_DOCK_ITEM_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_ITEM, GdlDockItemClass))
+#define GDL_IS_DOCK_ITEM(obj)         (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_ITEM))
+#define GDL_IS_DOCK_ITEM_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_ITEM))
+#define GDL_DOCK_ITEM_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK_ITEM, GdlDockItemClass))
+
+/* data types & structures */
+typedef enum {
+    GDL_DOCK_ITEM_BEH_NORMAL           = 0,
+    GDL_DOCK_ITEM_BEH_NEVER_FLOATING   = 1 << 0,
+    GDL_DOCK_ITEM_BEH_NEVER_VERTICAL   = 1 << 1,
+    GDL_DOCK_ITEM_BEH_NEVER_HORIZONTAL = 1 << 2,
+    GDL_DOCK_ITEM_BEH_LOCKED           = 1 << 3,
+    GDL_DOCK_ITEM_BEH_CANT_DOCK_TOP    = 1 << 4,
+    GDL_DOCK_ITEM_BEH_CANT_DOCK_BOTTOM = 1 << 5,
+    GDL_DOCK_ITEM_BEH_CANT_DOCK_LEFT   = 1 << 6,
+    GDL_DOCK_ITEM_BEH_CANT_DOCK_RIGHT  = 1 << 7,
+    GDL_DOCK_ITEM_BEH_CANT_DOCK_CENTER = 1 << 8,
+    GDL_DOCK_ITEM_BEH_CANT_CLOSE       = 1 << 9,
+    GDL_DOCK_ITEM_BEH_CANT_ICONIFY     = 1 << 10,
+    GDL_DOCK_ITEM_BEH_NO_GRIP          = 1 << 11
+} GdlDockItemBehavior;
+
+typedef enum {
+    GDL_DOCK_IN_DRAG             = 1 << GDL_DOCK_OBJECT_FLAGS_SHIFT,
+    GDL_DOCK_IN_PREDRAG          = 1 << (GDL_DOCK_OBJECT_FLAGS_SHIFT + 1),
+    GDL_DOCK_ICONIFIED           = 1 << (GDL_DOCK_OBJECT_FLAGS_SHIFT + 2),
+    /* for general use: indicates the user has started an action on
+       the dock item */
+    GDL_DOCK_USER_ACTION         = 1 << (GDL_DOCK_OBJECT_FLAGS_SHIFT + 3)
+} GdlDockItemFlags;
+
+typedef struct _GdlDockItem        GdlDockItem;
+typedef struct _GdlDockItemClass   GdlDockItemClass;
+typedef struct _GdlDockItemPrivate GdlDockItemPrivate;
+
+struct _GdlDockItem {
+    GdlDockObject        object;
+
+    GtkWidget           *child;
+    GdlDockItemBehavior  behavior;
+    GtkOrientation       orientation;
+
+    guint                resize : 1;
+
+    gint                 dragoff_x, dragoff_y;    /* these need to be
+                                                     accesible from
+                                                     outside */
+    GdlDockItemPrivate  *_priv;
+};
+
+struct _GdlDockItemClass {
+    GdlDockObjectClass  parent_class;
+
+    gboolean            has_grip;
+    
+    /* virtuals */
+    void     (* dock_drag_begin)  (GdlDockItem    *item);
+    void     (* dock_drag_motion) (GdlDockItem    *item,
+                                   gint            x,
+                                   gint            y);
+    void     (* dock_drag_end)    (GdlDockItem    *item,
+                                   gboolean        cancelled);
+                                   
+    void     (* set_orientation)  (GdlDockItem    *item,
+                                   GtkOrientation  orientation);
+};
+
+/* additional macros */
+#define GDL_DOCK_ITEM_FLAGS(item)     (GDL_DOCK_OBJECT (item)->flags)
+#define GDL_DOCK_ITEM_IN_DRAG(item) \
+    ((GDL_DOCK_ITEM_FLAGS (item) & GDL_DOCK_IN_DRAG) != 0)
+#define GDL_DOCK_ITEM_IN_PREDRAG(item) \
+    ((GDL_DOCK_ITEM_FLAGS (item) & GDL_DOCK_IN_PREDRAG) != 0)
+#define GDL_DOCK_ITEM_ICONIFIED(item) \
+    ((GDL_DOCK_ITEM_FLAGS (item) & GDL_DOCK_ICONIFIED) != 0)
+#define GDL_DOCK_ITEM_USER_ACTION(item) \
+    ((GDL_DOCK_ITEM_FLAGS (item) & GDL_DOCK_USER_ACTION) != 0)
+#define GDL_DOCK_ITEM_NOT_LOCKED(item) !((item)->behavior & GDL_DOCK_ITEM_BEH_LOCKED)
+#define GDL_DOCK_ITEM_NO_GRIP(item) ((item)->behavior & GDL_DOCK_ITEM_BEH_NO_GRIP)
+
+#define GDL_DOCK_ITEM_SET_FLAGS(item,flag) \
+    G_STMT_START { (GDL_DOCK_ITEM_FLAGS (item) |= (flag)); } G_STMT_END
+#define GDL_DOCK_ITEM_UNSET_FLAGS(item,flag) \
+    G_STMT_START { (GDL_DOCK_ITEM_FLAGS (item) &= ~(flag)); } G_STMT_END
+
+#define GDL_DOCK_ITEM_HAS_GRIP(item) ((GDL_DOCK_ITEM_GET_CLASS (item)->has_grip)&& \
+               ! GDL_DOCK_ITEM_NO_GRIP (item))
+
+#define GDL_DOCK_ITEM_CANT_CLOSE(item) \
+    ((((item)->behavior & GDL_DOCK_ITEM_BEH_CANT_CLOSE) != 0)|| \
+     ! GDL_DOCK_ITEM_NOT_LOCKED(item))
+
+#define GDL_DOCK_ITEM_CANT_ICONIFY(item) \
+    ((((item)->behavior & GDL_DOCK_ITEM_BEH_CANT_ICONIFY) != 0)|| \
+     ! GDL_DOCK_ITEM_NOT_LOCKED(item))
+
+/* public interface */
+GtkWidget     *gdl_dock_item_new               (const gchar         *name,
+                                                const gchar         *long_name,
+                                                GdlDockItemBehavior  behavior);
+GtkWidget     *gdl_dock_item_new_with_stock    (const gchar         *name,
+                                                const gchar         *long_name,
+                                                const gchar         *stock_id,
+                                                GdlDockItemBehavior  behavior);
+
+GtkWidget     *gdl_dock_item_new_with_pixbuf_icon (const gchar         *name,
+                                                   const gchar         *long_name,
+                                                   const GdkPixbuf     *pixbuf_icon,
+                                                   GdlDockItemBehavior  behavior);
+
+GType          gdl_dock_item_get_type          (void);
+
+void           gdl_dock_item_dock_to           (GdlDockItem      *item,
+                                                GdlDockItem      *target,
+                                                GdlDockPlacement  position,
+                                                gint              docking_param);
+
+void           gdl_dock_item_set_orientation   (GdlDockItem    *item,
+                                                GtkOrientation  orientation);
+
+GtkWidget     *gdl_dock_item_get_tablabel      (GdlDockItem *item);
+void           gdl_dock_item_set_tablabel      (GdlDockItem *item,
+                                                GtkWidget   *tablabel);
+void           gdl_dock_item_hide_grip         (GdlDockItem *item);
+void           gdl_dock_item_show_grip         (GdlDockItem *item);
+
+/* bind and unbind items to a dock */
+void           gdl_dock_item_bind              (GdlDockItem *item,
+                                                GtkWidget   *dock);
+
+void           gdl_dock_item_unbind            (GdlDockItem *item);
+
+void           gdl_dock_item_hide_item         (GdlDockItem *item);
+
+void           gdl_dock_item_iconify_item      (GdlDockItem *item);
+
+void           gdl_dock_item_show_item         (GdlDockItem *item);
+
+void           gdl_dock_item_lock              (GdlDockItem *item);
+
+void           gdl_dock_item_unlock            (GdlDockItem *item);
+
+void        gdl_dock_item_set_default_position (GdlDockItem      *item,
+                                                GdlDockObject    *reference);
+
+void        gdl_dock_item_preferred_size       (GdlDockItem      *item,
+                                                GtkRequisition   *req);
+
+G_END_DECLS
+
+#endif
diff --git a/src/libgdl/gdl-dock-master.c b/src/libgdl/gdl-dock-master.c
new file mode 100644 (file)
index 0000000..8a6aba3
--- /dev/null
@@ -0,0 +1,991 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 
+ *
+ * gdl-dock-master.c - Object which manages a dock ring
+ *
+ * This file is part of the GNOME Devtools Libraries.
+ *
+ * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gdl-i18n.h"
+
+#include "gdl-tools.h"
+#include "gdl-dock-master.h"
+#include "gdl-dock.h"
+#include "gdl-dock-item.h"
+#include "libgdlmarshal.h"
+#include "libgdltypebuiltins.h"
+
+/* ----- Private prototypes ----- */
+
+static void     gdl_dock_master_class_init    (GdlDockMasterClass *klass);
+static void     gdl_dock_master_instance_init (GdlDockMaster      *master);
+
+static void     gdl_dock_master_dispose       (GObject            *g_object);
+static void     gdl_dock_master_set_property  (GObject            *object,
+                                               guint               prop_id,
+                                               const GValue       *value,
+                                               GParamSpec         *pspec);
+static void     gdl_dock_master_get_property  (GObject            *object,
+                                               guint               prop_id,
+                                               GValue             *value,
+                                               GParamSpec         *pspec);
+
+static void     _gdl_dock_master_remove       (GdlDockObject      *object,
+                                               GdlDockMaster      *master);
+
+static void     gdl_dock_master_drag_begin    (GdlDockItem        *item, 
+                                               gpointer            data);
+static void     gdl_dock_master_drag_end      (GdlDockItem        *item,
+                                               gboolean            cancelled,
+                                               gpointer            data);
+static void     gdl_dock_master_drag_motion   (GdlDockItem        *item, 
+                                               gint                x, 
+                                               gint                y,
+                                               gpointer            data);
+
+static void     _gdl_dock_master_foreach      (gpointer            key,
+                                               gpointer            value,
+                                               gpointer            user_data);
+
+static void     gdl_dock_master_xor_rect      (GdlDockMaster      *master);
+
+static void     gdl_dock_master_layout_changed (GdlDockMaster     *master);
+
+static void gdl_dock_master_set_switcher_style (GdlDockMaster *master,
+                                                GdlSwitcherStyle switcher_style);
+
+/* ----- Private data types and variables ----- */
+
+enum {
+    PROP_0,
+    PROP_DEFAULT_TITLE,
+    PROP_LOCKED,
+    PROP_SWITCHER_STYLE
+};
+
+enum {
+    LAYOUT_CHANGED,
+    LAST_SIGNAL
+};
+
+struct _GdlDockMasterPrivate {
+    gint            number;             /* for naming nameless manual objects */
+    gchar          *default_title;
+    
+    GdkGC          *root_xor_gc;
+    gboolean        rect_drawn;
+    GdlDock        *rect_owner;
+    
+    GdlDockRequest *drag_request;
+
+    /* source id for the idle handler to emit a layout_changed signal */
+    guint           idle_layout_changed_id;
+
+    /* hashes to quickly calculate the overall locked status: i.e.
+     * if size(unlocked_items) == 0 then locked = 1
+     * else if size(locked_items) == 0 then locked = 0
+     * else locked = -1
+     */
+    GHashTable     *locked_items;
+    GHashTable     *unlocked_items;
+    
+    GdlSwitcherStyle switcher_style;
+};
+
+#define COMPUTE_LOCKED(master)                                          \
+    (g_hash_table_size ((master)->_priv->unlocked_items) == 0 ? 1 :     \
+     (g_hash_table_size ((master)->_priv->locked_items) == 0 ? 0 : -1))
+
+static guint master_signals [LAST_SIGNAL] = { 0 };
+
+
+/* ----- Private interface ----- */
+
+GDL_CLASS_BOILERPLATE (GdlDockMaster, gdl_dock_master, GObject, G_TYPE_OBJECT);
+
+static void
+gdl_dock_master_class_init (GdlDockMasterClass *klass)
+{
+    GObjectClass      *g_object_class;
+
+    g_object_class = G_OBJECT_CLASS (klass);
+
+    g_object_class->dispose = gdl_dock_master_dispose;
+    g_object_class->set_property = gdl_dock_master_set_property;
+    g_object_class->get_property = gdl_dock_master_get_property;
+
+    g_object_class_install_property (
+        g_object_class, PROP_DEFAULT_TITLE,
+        g_param_spec_string ("default-title", _("Default title"),
+                             _("Default title for newly created floating docks"),
+                             NULL,
+                             G_PARAM_READWRITE));
+    
+    g_object_class_install_property (
+        g_object_class, PROP_LOCKED,
+        g_param_spec_int ("locked", _("Locked"),
+                          _("If is set to 1, all the dock items bound to the master "
+                            "are locked; if it's 0, all are unlocked; -1 indicates "
+                            "inconsistency among the items"),
+                          -1, 1, 0,
+                          G_PARAM_READWRITE));
+
+    g_object_class_install_property (
+        g_object_class, PROP_SWITCHER_STYLE,
+        g_param_spec_enum ("switcher-style", _("Switcher Style"),
+                           _("Switcher buttons style"),
+                           GDL_TYPE_SWITCHER_STYLE,
+                           GDL_SWITCHER_STYLE_BOTH,
+                           G_PARAM_READWRITE));
+
+    master_signals [LAYOUT_CHANGED] = 
+        g_signal_new ("layout-changed", 
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (GdlDockMasterClass, layout_changed),
+                      NULL, /* accumulator */
+                      NULL, /* accu_data */
+                      gdl_marshal_VOID__VOID,
+                      G_TYPE_NONE, /* return type */
+                      0);
+
+    klass->layout_changed = gdl_dock_master_layout_changed;
+}
+
+static void
+gdl_dock_master_instance_init (GdlDockMaster *master)
+{
+    master->dock_objects = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                  g_free, NULL);
+    master->toplevel_docks = NULL;
+    master->controller = NULL;
+    master->dock_number = 1;
+    
+    master->_priv = g_new0 (GdlDockMasterPrivate, 1);
+    master->_priv->number = 1;
+    master->_priv->switcher_style = GDL_SWITCHER_STYLE_BOTH;
+    master->_priv->locked_items = g_hash_table_new (g_direct_hash, g_direct_equal);
+    master->_priv->unlocked_items = g_hash_table_new (g_direct_hash, g_direct_equal);
+}
+
+static void
+_gdl_dock_master_remove (GdlDockObject *object,
+                         GdlDockMaster *master)
+{
+    g_return_if_fail (master != NULL && object != NULL);
+
+    if (GDL_IS_DOCK (object)) {
+        GList *found_link;
+
+        found_link = g_list_find (master->toplevel_docks, object);
+        if (found_link)
+            master->toplevel_docks = g_list_delete_link (master->toplevel_docks,
+                                                         found_link);
+        if (object == master->controller) {
+            GList *last;
+            GdlDockObject *new_controller = NULL;
+            
+            /* now find some other non-automatic toplevel to use as a
+               new controller.  start from the last dock, since it's
+               probably a non-floating and manual */
+            last = g_list_last (master->toplevel_docks);
+            while (last) {
+                if (!GDL_DOCK_OBJECT_AUTOMATIC (last->data)) {
+                    new_controller = GDL_DOCK_OBJECT (last->data);
+                    break;
+                }
+                last = last->prev;
+            };
+
+            if (new_controller) {
+                /* the new controller gets the ref (implicitly of course) */
+                master->controller = new_controller;
+            } else {
+                master->controller = NULL;
+                /* no controller, no master */
+                g_object_unref (master);
+            }
+        }
+    }
+    /* disconnect dock object signals */
+    g_signal_handlers_disconnect_matched (object, G_SIGNAL_MATCH_DATA, 
+                                          0, 0, NULL, NULL, master);
+
+    /* unref the object from the hash if it's there */
+    if (object->name) {
+        GdlDockObject *found_object;
+        found_object = g_hash_table_lookup (master->dock_objects, object->name);
+        if (found_object == object) {
+            g_hash_table_remove (master->dock_objects, object->name);
+            g_object_unref (object);
+        }
+    }
+}
+
+static void
+ht_foreach_build_slist (gpointer  key,
+                        gpointer  value,
+                        GSList  **slist)
+{
+    *slist = g_slist_prepend (*slist, value);
+}
+
+static void
+gdl_dock_master_dispose (GObject *g_object)
+{
+    GdlDockMaster *master;
+    
+    g_return_if_fail (GDL_IS_DOCK_MASTER (g_object));
+
+    master = GDL_DOCK_MASTER (g_object);
+
+    if (master->toplevel_docks) {
+        g_list_foreach (master->toplevel_docks,
+                        (GFunc) gdl_dock_object_unbind, NULL);
+        g_list_free (master->toplevel_docks);
+        master->toplevel_docks = NULL;
+    }
+    
+    if (master->dock_objects) {
+        GSList *alive_docks = NULL;
+        g_hash_table_foreach (master->dock_objects,
+                              (GHFunc) ht_foreach_build_slist, &alive_docks);
+        while (alive_docks) {
+            gdl_dock_object_unbind (GDL_DOCK_OBJECT (alive_docks->data));
+            alive_docks = g_slist_delete_link (alive_docks, alive_docks);
+        }
+        
+        g_hash_table_destroy (master->dock_objects);
+        master->dock_objects = NULL;
+    }
+    
+    if (master->_priv) {
+        if (master->_priv->idle_layout_changed_id)
+            g_source_remove (master->_priv->idle_layout_changed_id);
+        
+        if (master->_priv->root_xor_gc) {
+            g_object_unref (master->_priv->root_xor_gc);
+            master->_priv->root_xor_gc = NULL;
+        }
+        if (master->_priv->drag_request) {
+            if (G_IS_VALUE (&master->_priv->drag_request->extra))
+                g_value_unset (&master->_priv->drag_request->extra);
+            g_free (master->_priv->drag_request);
+            master->_priv->drag_request = NULL;
+        }
+        g_free (master->_priv->default_title);
+        master->_priv->default_title = NULL;
+
+        g_hash_table_destroy (master->_priv->locked_items);
+        master->_priv->locked_items = NULL;
+        g_hash_table_destroy (master->_priv->unlocked_items);
+        master->_priv->unlocked_items = NULL;
+        
+        g_free (master->_priv);
+        master->_priv = NULL;
+    }
+
+    GDL_CALL_PARENT (G_OBJECT_CLASS, dispose, (g_object));
+}
+
+static void 
+foreach_lock_unlock (GdlDockItem *item,
+                     gboolean     locked)
+{
+    if (!GDL_IS_DOCK_ITEM (item))
+        return;
+    
+    g_object_set (item, "locked", locked, NULL);
+    if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT (item)))
+        gtk_container_foreach (GTK_CONTAINER (item),
+                               (GtkCallback) foreach_lock_unlock,
+                               GINT_TO_POINTER (locked));
+}
+
+static void
+gdl_dock_master_lock_unlock (GdlDockMaster *master,
+                             gboolean       locked)
+{
+    GList *l;
+    
+    for (l = master->toplevel_docks; l; l = l->next) {
+        GdlDock *dock = GDL_DOCK (l->data);
+        if (dock->root)
+            foreach_lock_unlock (GDL_DOCK_ITEM (dock->root), locked);
+    }
+
+    /* just to be sure hidden items are set too */
+    gdl_dock_master_foreach (master,
+                             (GFunc) foreach_lock_unlock,
+                             GINT_TO_POINTER (locked));
+}
+
+static void
+gdl_dock_master_set_property  (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+    GdlDockMaster *master = GDL_DOCK_MASTER (object);
+
+    switch (prop_id) {
+        case PROP_DEFAULT_TITLE:
+            g_free (master->_priv->default_title);
+            master->_priv->default_title = g_value_dup_string (value);
+            break;
+        case PROP_LOCKED:
+            if (g_value_get_int (value) >= 0)
+                gdl_dock_master_lock_unlock (master, (g_value_get_int (value) > 0));
+            break;
+        case PROP_SWITCHER_STYLE:
+            gdl_dock_master_set_switcher_style (master, g_value_get_enum (value));
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+gdl_dock_master_get_property  (GObject      *object,
+                               guint         prop_id,
+                               GValue       *value,
+                               GParamSpec   *pspec)
+{
+    GdlDockMaster *master = GDL_DOCK_MASTER (object);
+
+    switch (prop_id) {
+        case PROP_DEFAULT_TITLE:
+            g_value_set_string (value, master->_priv->default_title);
+            break;
+        case PROP_LOCKED:
+            g_value_set_int (value, COMPUTE_LOCKED (master));
+            break;
+        case PROP_SWITCHER_STYLE:
+            g_value_set_enum (value, master->_priv->switcher_style);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+gdl_dock_master_drag_begin (GdlDockItem *item,
+                            gpointer     data)
+{
+    GdlDockMaster  *master;
+    GdlDockRequest *request;
+    
+    g_return_if_fail (data != NULL);
+    g_return_if_fail (item != NULL);
+
+    master = GDL_DOCK_MASTER (data);
+
+    if (!master->_priv->drag_request)
+        master->_priv->drag_request = g_new0 (GdlDockRequest, 1);
+
+    request = master->_priv->drag_request;
+    
+    /* Set the target to itself so it won't go floating with just a click. */
+    request->applicant = GDL_DOCK_OBJECT (item);
+    request->target = GDL_DOCK_OBJECT (item);
+    request->position = GDL_DOCK_FLOATING;
+    if (G_IS_VALUE (&request->extra))
+        g_value_unset (&request->extra);
+
+    master->_priv->rect_drawn = FALSE;
+    master->_priv->rect_owner = NULL;
+}
+
+static void
+gdl_dock_master_drag_end (GdlDockItem *item, 
+                          gboolean     cancelled,
+                          gpointer     data)
+{
+    GdlDockMaster  *master;
+    GdlDockRequest *request;
+    
+    g_return_if_fail (data != NULL);
+    g_return_if_fail (item != NULL);
+
+    master = GDL_DOCK_MASTER (data);
+    request = master->_priv->drag_request;
+    
+    g_return_if_fail (GDL_DOCK_OBJECT (item) == request->applicant);
+    
+    /* Erase previously drawn rectangle */
+    if (master->_priv->rect_drawn)
+        gdl_dock_master_xor_rect (master);
+    
+    /* cancel conditions */
+    if (cancelled || request->applicant == request->target)
+        return;
+    
+    /* dock object to the requested position */
+    gdl_dock_object_dock (request->target,
+                          request->applicant,
+                          request->position,
+                          &request->extra);
+    
+    g_signal_emit (master, master_signals [LAYOUT_CHANGED], 0);
+}
+
+static void
+gdl_dock_master_drag_motion (GdlDockItem *item, 
+                             gint         root_x, 
+                             gint         root_y,
+                             gpointer     data)
+{
+    GdlDockMaster  *master;
+    GdlDockRequest  my_request, *request;
+    GdkWindow      *window;
+    gint            win_x, win_y;
+    gint            x, y;
+    GdlDock        *dock = NULL;
+    gboolean        may_dock = FALSE;
+    
+    g_return_if_fail (item != NULL && data != NULL);
+
+    master = GDL_DOCK_MASTER (data);
+    request = master->_priv->drag_request;
+
+    g_return_if_fail (GDL_DOCK_OBJECT (item) == request->applicant);
+    
+    my_request = *request;
+
+    /* first look under the pointer */
+    window = gdk_window_at_pointer (&win_x, &win_y);
+    if (window) {
+        GtkWidget *widget;
+        /* ok, now get the widget who owns that window and see if we can
+           get to a GdlDock by walking up the hierarchy */
+        gdk_window_get_user_data (window, (gpointer) &widget);
+        if (GTK_IS_WIDGET (widget)) {
+            while (widget && (!GDL_IS_DOCK (widget) || 
+                  GDL_DOCK_OBJECT_GET_MASTER (widget) != master))
+                widget = widget->parent;
+            if (widget) {
+                gint win_w, win_h;
+                
+                /* verify that the pointer is still in that dock
+                   (the user could have moved it) */
+                gdk_window_get_geometry (widget->window,
+                                         NULL, NULL, &win_w, &win_h, NULL);
+                gdk_window_get_origin (widget->window, &win_x, &win_y);
+                if (root_x >= win_x && root_x < win_x + win_w &&
+                    root_y >= win_y && root_y < win_y + win_h)
+                    dock = GDL_DOCK (widget);
+            }
+        }
+    }
+
+    if (dock) {
+        /* translate root coordinates into dock object coordinates
+           (i.e. widget coordinates) */
+        gdk_window_get_origin (GTK_WIDGET (dock)->window, &win_x, &win_y);
+        x = root_x - win_x;
+        y = root_y - win_y;
+        may_dock = gdl_dock_object_dock_request (GDL_DOCK_OBJECT (dock),
+                                                 x, y, &my_request);
+    }
+    else {
+        GList *l;
+
+        /* try to dock the item in all the docks in the ring in turn */
+        for (l = master->toplevel_docks; l; l = l->next) {
+            dock = GDL_DOCK (l->data);
+            /* translate root coordinates into dock object coordinates
+               (i.e. widget coordinates) */
+            gdk_window_get_origin (GTK_WIDGET (dock)->window, &win_x, &win_y);
+            x = root_x - win_x;
+            y = root_y - win_y;
+            may_dock = gdl_dock_object_dock_request (GDL_DOCK_OBJECT (dock),
+                                                     x, y, &my_request);
+            if (may_dock)
+                break;
+        }
+    }
+
+  
+    if (!may_dock) {
+        GtkRequisition req;
+       /* Special case for GdlDockItems : they must respect the flags */
+       if(GDL_IS_DOCK_ITEM(item)
+       && GDL_DOCK_ITEM(item)->behavior & GDL_DOCK_ITEM_BEH_NEVER_FLOATING)
+           return;
+
+        dock = NULL;
+        my_request.target = GDL_DOCK_OBJECT (
+            gdl_dock_object_get_toplevel (request->applicant));
+        my_request.position = GDL_DOCK_FLOATING;
+
+        gdl_dock_item_preferred_size (GDL_DOCK_ITEM (request->applicant), &req);
+        my_request.rect.width = req.width;
+        my_request.rect.height = req.height;
+
+        my_request.rect.x = root_x - GDL_DOCK_ITEM (request->applicant)->dragoff_x;
+        my_request.rect.y = root_y - GDL_DOCK_ITEM (request->applicant)->dragoff_y;
+
+        /* setup extra docking information */
+        if (G_IS_VALUE (&my_request.extra))
+            g_value_unset (&my_request.extra);
+
+        g_value_init (&my_request.extra, GDK_TYPE_RECTANGLE);
+        g_value_set_boxed (&my_request.extra, &my_request.rect);
+    }
+    /* if we want to enforce GDL_DOCK_ITEM_BEH_NEVER_FLOATING          */
+    /* the item must remain attached to the controller, otherwise      */
+    /* it could be inserted in another floating dock                   */
+    /* so check for the flag at this moment                            */
+    else if(GDL_IS_DOCK_ITEM(item)
+       && GDL_DOCK_ITEM(item)->behavior & GDL_DOCK_ITEM_BEH_NEVER_FLOATING
+       && dock != GDL_DOCK(master->controller))
+           return;
+
+    if (!(my_request.rect.x == request->rect.x &&
+          my_request.rect.y == request->rect.y &&
+          my_request.rect.width == request->rect.width &&
+          my_request.rect.height == request->rect.height &&
+          dock == master->_priv->rect_owner)) {
+        
+        /* erase the previous rectangle */
+        if (master->_priv->rect_drawn)
+            gdl_dock_master_xor_rect (master);
+    }
+
+    /* set the new values */
+    *request = my_request;
+    master->_priv->rect_owner = dock;
+    
+    /* draw the previous rectangle */
+    if (~master->_priv->rect_drawn)
+        gdl_dock_master_xor_rect (master);
+}
+
+static void
+_gdl_dock_master_foreach (gpointer key,
+                          gpointer value,
+                          gpointer user_data)
+{
+    (void)key;
+    struct {
+        GFunc    function;
+        gpointer user_data;
+    } *data = user_data;
+
+    (* data->function) (GTK_WIDGET (value), data->user_data);
+}
+
+static void
+gdl_dock_master_xor_rect (GdlDockMaster *master)
+{
+    gint8         dash_list [2];
+    GdkWindow    *window;
+    GdkRectangle *rect;
+    
+    if (!master->_priv || !master->_priv->drag_request)
+        return;
+    
+    master->_priv->rect_drawn = ~master->_priv->rect_drawn;
+    
+    if (master->_priv->rect_owner) {
+        gdl_dock_xor_rect (master->_priv->rect_owner,
+                           &master->_priv->drag_request->rect);
+        return;
+    }
+    
+    rect = &master->_priv->drag_request->rect;
+    window = gdk_get_default_root_window ();
+
+    if (!master->_priv->root_xor_gc) {
+        GdkGCValues values;
+
+        values.function = GDK_INVERT;
+        values.subwindow_mode = GDK_INCLUDE_INFERIORS;
+        master->_priv->root_xor_gc = gdk_gc_new_with_values (
+            window, &values, GDK_GC_FUNCTION | GDK_GC_SUBWINDOW);
+    };
+
+    gdk_gc_set_line_attributes (master->_priv->root_xor_gc, 1,
+                                GDK_LINE_ON_OFF_DASH,
+                                GDK_CAP_NOT_LAST,
+                                GDK_JOIN_BEVEL);
+    
+    dash_list[0] = 1;
+    dash_list[1] = 1;
+    gdk_gc_set_dashes (master->_priv->root_xor_gc, 1, dash_list, 2);
+
+    gdk_draw_rectangle (window, master->_priv->root_xor_gc, 0, 
+                        rect->x, rect->y,
+                        rect->width, rect->height);
+
+    gdk_gc_set_dashes (master->_priv->root_xor_gc, 0, dash_list, 2);
+
+    gdk_draw_rectangle (window, master->_priv->root_xor_gc, 0, 
+                        rect->x + 1, rect->y + 1,
+                        rect->width - 2, rect->height - 2);
+}
+
+static void
+gdl_dock_master_layout_changed (GdlDockMaster *master)
+{
+    g_return_if_fail (GDL_IS_DOCK_MASTER (master));
+
+    /* emit "layout-changed" on the controller to notify the user who
+     * normally shouldn't have access to us */
+    if (master->controller)
+        g_signal_emit_by_name (master->controller, "layout-changed");
+
+    /* remove the idle handler if there is one */
+    if (master->_priv->idle_layout_changed_id) {
+        g_source_remove (master->_priv->idle_layout_changed_id);
+        master->_priv->idle_layout_changed_id = 0;
+    }
+}
+
+static gboolean
+idle_emit_layout_changed (gpointer user_data)
+{
+    GdlDockMaster *master = user_data;
+
+    g_return_val_if_fail (master && GDL_IS_DOCK_MASTER (master), FALSE);
+
+    master->_priv->idle_layout_changed_id = 0;
+    g_signal_emit (master, master_signals [LAYOUT_CHANGED], 0);
+    
+    return FALSE;
+}
+
+static void 
+item_dock_cb (GdlDockObject    *object,
+              GdlDockObject    *requestor,
+              GdlDockPlacement  position,
+              GValue           *other_data,
+              gpointer          user_data)
+{
+    GdlDockMaster *master = user_data;
+    
+    g_return_if_fail (requestor && GDL_IS_DOCK_OBJECT (requestor));
+    g_return_if_fail (master && GDL_IS_DOCK_MASTER (master));
+
+    /* here we are in fact interested in the requestor, since it's
+     * assumed that object will not change its visibility... for the
+     * requestor, however, could mean that it's being shown */
+    if (!GDL_DOCK_OBJECT_IN_REFLOW (requestor) &&
+        !GDL_DOCK_OBJECT_AUTOMATIC (requestor)) {
+        if (!master->_priv->idle_layout_changed_id)
+            master->_priv->idle_layout_changed_id =
+                g_idle_add (idle_emit_layout_changed, master);
+    }
+}
+
+static void 
+item_detach_cb (GdlDockObject *object,
+                gboolean       recursive,
+                gpointer       user_data)
+{
+    GdlDockMaster *master = user_data;
+    
+    g_return_if_fail (object && GDL_IS_DOCK_OBJECT (object));
+    g_return_if_fail (master && GDL_IS_DOCK_MASTER (master));
+
+    if (!GDL_DOCK_OBJECT_IN_REFLOW (object) &&
+        !GDL_DOCK_OBJECT_AUTOMATIC (object)) {
+        if (!master->_priv->idle_layout_changed_id)
+            master->_priv->idle_layout_changed_id =
+                g_idle_add (idle_emit_layout_changed, master);
+    }
+}
+
+static void
+item_notify_cb (GdlDockObject *object,
+                GParamSpec    *pspec,
+                gpointer       user_data)
+{
+    GdlDockMaster *master = user_data;
+    gint locked = COMPUTE_LOCKED (master);
+    gboolean item_locked;
+    
+    g_object_get (object, "locked", &item_locked, NULL);
+
+    if (item_locked) {
+        g_hash_table_remove (master->_priv->unlocked_items, object);
+        g_hash_table_insert (master->_priv->locked_items, object, NULL);
+    } else {
+        g_hash_table_remove (master->_priv->locked_items, object);
+        g_hash_table_insert (master->_priv->unlocked_items, object, NULL);
+    }
+    
+    if (COMPUTE_LOCKED (master) != locked)
+        g_object_notify (G_OBJECT (master), "locked");
+}
+
+/* ----- Public interface ----- */
+
+void
+gdl_dock_master_add (GdlDockMaster *master,
+                     GdlDockObject *object)
+{
+    g_return_if_fail (master != NULL && object != NULL);
+
+    if (!GDL_DOCK_OBJECT_AUTOMATIC (object)) {
+        GdlDockObject *found_object;
+        
+        /* create a name for the object if it doesn't have one */
+        if (!object->name)
+            /* directly set the name, since it's a construction only
+               property */
+            object->name = g_strdup_printf ("__dock_%u", master->_priv->number++);
+        
+        /* add the object to our hash list */
+        if ((found_object = g_hash_table_lookup (master->dock_objects, object->name))) {
+            g_warning (_("master %p: unable to add object %p[%s] to the hash.  "
+                         "There already is an item with that name (%p)."),
+                       master, object, object->name, found_object);
+        }
+        else {
+            g_object_ref (object);
+            gtk_object_sink (GTK_OBJECT (object));
+            g_hash_table_insert (master->dock_objects, g_strdup (object->name), object);
+        }
+    }
+    
+    if (GDL_IS_DOCK (object)) {
+        gboolean floating;
+        
+        /* if this is the first toplevel we are adding, name it controller */
+        if (!master->toplevel_docks)
+            /* the dock should already have the ref */
+            master->controller = object;
+        
+        /* add dock to the toplevel list */
+        g_object_get (object, "floating", &floating, NULL);
+        if (floating)
+            master->toplevel_docks = g_list_prepend (master->toplevel_docks, object);
+        else
+            master->toplevel_docks = g_list_append (master->toplevel_docks, object);
+
+        /* we are interested in the dock request this toplevel
+         * receives to update the layout */
+        g_signal_connect (object, "dock",
+                          G_CALLBACK (item_dock_cb), master);
+
+    }
+    else if (GDL_IS_DOCK_ITEM (object)) {
+        /* we need to connect the item's signals */
+        g_signal_connect (object, "dock_drag_begin",
+                          G_CALLBACK (gdl_dock_master_drag_begin), master);
+        g_signal_connect (object, "dock_drag_motion",
+                          G_CALLBACK (gdl_dock_master_drag_motion), master);
+        g_signal_connect (object, "dock_drag_end",
+                          G_CALLBACK (gdl_dock_master_drag_end), master);
+        g_signal_connect (object, "dock",
+                          G_CALLBACK (item_dock_cb), master);
+        g_signal_connect (object, "detach",
+                          G_CALLBACK (item_detach_cb), master);
+
+        /* register to "locked" notification if the item has a grip,
+         * and add the item to the corresponding hash */
+        if (GDL_DOCK_ITEM_HAS_GRIP (GDL_DOCK_ITEM (object))) {
+            g_signal_connect (object, "notify::locked",
+                              G_CALLBACK (item_notify_cb), master);
+            item_notify_cb (object, NULL, master);
+        }
+        
+        /* If the item is notebook, set the switcher style */
+        if (GDL_IS_DOCK_NOTEBOOK (object) &&
+            GDL_IS_SWITCHER (GDL_DOCK_ITEM (object)->child))
+        {
+            g_object_set (GDL_DOCK_ITEM (object)->child, "switcher-style",
+                          master->_priv->switcher_style, NULL);
+        }
+        
+        /* post a layout_changed emission if the item is not automatic
+         * (since it should be added to the items model) */
+        if (!GDL_DOCK_OBJECT_AUTOMATIC (object)) {
+            if (!master->_priv->idle_layout_changed_id)
+                master->_priv->idle_layout_changed_id =
+                    g_idle_add (idle_emit_layout_changed, master);
+        }
+    }
+}
+
+void
+gdl_dock_master_remove (GdlDockMaster *master,
+                        GdlDockObject *object)
+{
+    g_return_if_fail (master != NULL && object != NULL);
+
+    /* remove from locked/unlocked hashes and property change if
+     * that's the case */
+    if (GDL_IS_DOCK_ITEM (object) && GDL_DOCK_ITEM_HAS_GRIP (GDL_DOCK_ITEM (object))) {
+        gint locked = COMPUTE_LOCKED (master);
+        if (g_hash_table_remove (master->_priv->locked_items, object) ||
+            g_hash_table_remove (master->_priv->unlocked_items, object)) {
+            if (COMPUTE_LOCKED (master) != locked)
+                g_object_notify (G_OBJECT (master), "locked");
+        }
+    }
+        
+    /* ref the master, since removing the controller could cause master disposal */
+    g_object_ref (master);
+    
+    /* all the interesting stuff happens in _gdl_dock_master_remove */
+    _gdl_dock_master_remove (object, master);
+
+    /* post a layout_changed emission if the item is not automatic
+     * (since it should be removed from the items model) */
+    if (!GDL_DOCK_OBJECT_AUTOMATIC (object)) {
+        if (!master->_priv->idle_layout_changed_id)
+            master->_priv->idle_layout_changed_id =
+                g_idle_add (idle_emit_layout_changed, master);
+    }
+    
+    /* balance ref count */
+    g_object_unref (master);
+}
+
+void
+gdl_dock_master_foreach (GdlDockMaster *master,
+                         GFunc          function,
+                         gpointer       user_data)
+{
+    struct {
+        GFunc    function;
+        gpointer user_data;
+    } data;
+
+    g_return_if_fail (master != NULL && function != NULL);
+
+    data.function = function;
+    data.user_data = user_data;
+    g_hash_table_foreach (master->dock_objects, _gdl_dock_master_foreach, &data);
+}
+
+void
+gdl_dock_master_foreach_toplevel (GdlDockMaster *master,
+                                  gboolean       include_controller,
+                                  GFunc          function,
+                                  gpointer       user_data)
+{
+    GList *l;
+    
+    g_return_if_fail (master != NULL && function != NULL);
+
+    for (l = master->toplevel_docks; l; ) {
+        GdlDockObject *object = GDL_DOCK_OBJECT (l->data);
+        l = l->next;
+        if (object != master->controller || include_controller)
+            (* function) (GTK_WIDGET (object), user_data);
+    }
+}
+
+GdlDockObject *
+gdl_dock_master_get_object (GdlDockMaster *master,
+                            const gchar   *nick_name)
+{
+    gpointer *found;
+    
+    g_return_val_if_fail (master != NULL, NULL);
+
+    if (!nick_name)
+        return NULL;
+
+    found = g_hash_table_lookup (master->dock_objects, nick_name);
+
+    return found ? GDL_DOCK_OBJECT (found) : NULL;
+}
+
+GdlDockObject *
+gdl_dock_master_get_controller (GdlDockMaster *master)
+{
+    g_return_val_if_fail (master != NULL, NULL);
+
+    return master->controller;
+}
+
+void
+gdl_dock_master_set_controller (GdlDockMaster *master,
+                                GdlDockObject *new_controller)
+{
+    g_return_if_fail (master != NULL);
+
+    if (new_controller) {
+        if (GDL_DOCK_OBJECT_AUTOMATIC (new_controller))
+            g_warning (_("The new dock controller %p is automatic.  Only manual "
+                         "dock objects should be named controller."), new_controller);
+        
+        /* check that the controller is in the toplevel list */
+        if (!g_list_find (master->toplevel_docks, new_controller))
+            gdl_dock_master_add (master, new_controller);
+        master->controller = new_controller;
+
+    } else {
+        master->controller = NULL;
+        /* no controller, no master */
+        g_object_unref (master);
+    }
+}
+
+static void
+set_switcher_style_foreach (GtkWidget *obj, gpointer user_data)
+{
+    GdlSwitcherStyle style = GPOINTER_TO_INT (user_data);
+    
+    if (!GDL_IS_DOCK_ITEM (obj))
+        return;
+    
+    if (GDL_IS_DOCK_NOTEBOOK (obj)) {
+        
+        GtkWidget *child = GDL_DOCK_ITEM (obj)->child;
+        if (GDL_IS_SWITCHER (child)) {
+            
+            g_object_set (child, "switcher-style", style, NULL);
+        }
+    } else if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT (obj))) {
+        
+        gtk_container_foreach (GTK_CONTAINER (obj),
+                               set_switcher_style_foreach,
+                               user_data);
+    }
+}
+
+static void
+gdl_dock_master_set_switcher_style (GdlDockMaster *master,
+                                    GdlSwitcherStyle switcher_style)
+{
+    GList *l;
+    g_return_if_fail (GDL_IS_DOCK_MASTER (master));
+    
+    master->_priv->switcher_style = switcher_style;
+    for (l = master->toplevel_docks; l; l = l->next) {
+        GdlDock *dock = GDL_DOCK (l->data);
+        if (dock->root)
+            set_switcher_style_foreach (GTK_WIDGET (dock->root),
+                                        GINT_TO_POINTER (switcher_style));
+    }
+
+    /* just to be sure hidden items are set too */
+    gdl_dock_master_foreach (master, (GFunc) set_switcher_style_foreach,
+                             GINT_TO_POINTER (switcher_style));
+}
diff --git a/src/libgdl/gdl-dock-master.h b/src/libgdl/gdl-dock-master.h
new file mode 100644 (file)
index 0000000..72697b4
--- /dev/null
@@ -0,0 +1,97 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 
+ *
+ * gdl-dock-master.h - Object which manages a dock ring
+ *
+ * This file is part of the GNOME Devtools Libraries.
+ *
+ * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GDL_DOCK_MASTER_H__
+#define __GDL_DOCK_MASTER_H__
+
+#include <glib-object.h>
+#include <gtk/gtktypeutils.h>
+#include "libgdl/gdl-dock-object.h"
+
+
+G_BEGIN_DECLS
+
+/* standard macros */
+#define GDL_TYPE_DOCK_MASTER             (gdl_dock_master_get_type ())
+#define GDL_DOCK_MASTER(obj)             (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_MASTER, GdlDockMaster))
+#define GDL_DOCK_MASTER_CLASS(klass)     (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_MASTER, GdlDockMasterClass))
+#define GDL_IS_DOCK_MASTER(obj)          (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_MASTER))
+#define GDL_IS_DOCK_MASTER_CLASS(klass)  (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_MASTER))
+#define GDL_DOCK_MASTER_GET_CLASS(obj)   (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK_MASTER, GdlDockMasterClass))
+
+/* data types & structures */
+typedef struct _GdlDockMaster        GdlDockMaster;
+typedef struct _GdlDockMasterClass   GdlDockMasterClass;
+typedef struct _GdlDockMasterPrivate GdlDockMasterPrivate;
+
+struct _GdlDockMaster {
+    GObject               object;
+
+    GHashTable           *dock_objects;
+    GList                *toplevel_docks;
+    GdlDockObject        *controller;      /* GUI root object */
+    
+    gint                  dock_number;     /* for toplevel dock numbering */
+    
+    GdlDockMasterPrivate *_priv;
+};
+
+struct _GdlDockMasterClass {
+    GObjectClass parent_class;
+
+    void (* layout_changed) (GdlDockMaster *master);
+};
+
+/* additional macros */
+
+#define GDL_DOCK_OBJECT_GET_MASTER(object) \
+    (GDL_DOCK_OBJECT (object)->master ? \
+        GDL_DOCK_MASTER (GDL_DOCK_OBJECT (object)->master) : NULL)
+
+/* public interface */
+GType          gdl_dock_master_get_type         (void);
+
+void           gdl_dock_master_add              (GdlDockMaster *master,
+                                                 GdlDockObject *object);
+void           gdl_dock_master_remove           (GdlDockMaster *master,
+                                                 GdlDockObject *object);
+void           gdl_dock_master_foreach          (GdlDockMaster *master,
+                                                 GFunc          function,
+                                                 gpointer       user_data);
+
+void           gdl_dock_master_foreach_toplevel (GdlDockMaster *master,
+                                                 gboolean       include_controller,
+                                                 GFunc          function,
+                                                 gpointer       user_data);
+
+GdlDockObject *gdl_dock_master_get_object       (GdlDockMaster *master,
+                                                 const gchar   *nick_name);
+
+GdlDockObject *gdl_dock_master_get_controller   (GdlDockMaster *master);
+void           gdl_dock_master_set_controller   (GdlDockMaster *master,
+                                                 GdlDockObject *new_controller);
+
+G_END_DECLS
+
+#endif /* __GDL_DOCK_MASTER_H__ */
diff --git a/src/libgdl/gdl-dock-notebook.c b/src/libgdl/gdl-dock-notebook.c
new file mode 100644 (file)
index 0000000..6fb931a
--- /dev/null
@@ -0,0 +1,528 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This file is part of the GNOME Devtools Libraries.
+ *
+ * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gdl-i18n.h"
+#include "gdl-switcher.h"
+
+#include "gdl-tools.h"
+#include "gdl-dock-notebook.h"
+#include "gdl-dock-tablabel.h"
+
+
+/* Private prototypes */
+
+static void  gdl_dock_notebook_class_init    (GdlDockNotebookClass *klass);
+static void  gdl_dock_notebook_instance_init (GdlDockNotebook      *notebook);
+static void  gdl_dock_notebook_set_property  (GObject              *object,
+                                              guint                 prop_id,
+                                              const GValue         *value,
+                                              GParamSpec           *pspec);
+static void  gdl_dock_notebook_get_property  (GObject              *object,
+                                              guint                 prop_id,
+                                              GValue               *value,
+                                              GParamSpec           *pspec);
+
+static void  gdl_dock_notebook_destroy       (GtkObject    *object);
+
+static void  gdl_dock_notebook_add           (GtkContainer *container,
+                                             GtkWidget    *widget);
+static void  gdl_dock_notebook_forall        (GtkContainer *container,
+                                             gboolean      include_internals,
+                                             GtkCallback   callback,
+                                             gpointer      callback_data);
+static GType gdl_dock_notebook_child_type    (GtkContainer *container);
+
+static void  gdl_dock_notebook_dock          (GdlDockObject    *object,
+                                              GdlDockObject    *requestor,
+                                              GdlDockPlacement  position,
+                                              GValue           *other_data);
+
+static void  gdl_dock_notebook_switch_page_cb  (GtkNotebook     *nb,
+                                                GtkNotebookPage *page,
+                                                gint             page_num,
+                                                gpointer         data);
+
+static void  gdl_dock_notebook_set_orientation (GdlDockItem     *item,
+                                                GtkOrientation   orientation);
+                                              
+static gboolean gdl_dock_notebook_child_placement (GdlDockObject    *object,
+                                                   GdlDockObject    *child,
+                                                   GdlDockPlacement *placement);
+
+static void     gdl_dock_notebook_present         (GdlDockObject    *object,
+                                                   GdlDockObject    *child);
+
+static gboolean gdl_dock_notebook_reorder         (GdlDockObject    *object,
+                                                   GdlDockObject    *requestor,
+                                                   GdlDockPlacement  new_position,
+                                                   GValue           *other_data);
+
+
+/* Class variables and definitions */
+
+enum {
+    PROP_0,
+    PROP_PAGE
+};
+
+
+/* ----- Private functions ----- */
+
+GDL_CLASS_BOILERPLATE (GdlDockNotebook, gdl_dock_notebook, GdlDockItem, GDL_TYPE_DOCK_ITEM) ;
+
+static void
+gdl_dock_notebook_class_init (GdlDockNotebookClass *klass)
+{
+    static gboolean style_initialized = FALSE;
+    
+    GObjectClass       *g_object_class;
+    GtkObjectClass     *gtk_object_class;
+    GtkWidgetClass     *widget_class;
+    GtkContainerClass  *container_class;
+    GdlDockObjectClass *object_class;
+    GdlDockItemClass   *item_class;
+
+    g_object_class = G_OBJECT_CLASS (klass);
+    gtk_object_class = GTK_OBJECT_CLASS (klass);
+    widget_class = GTK_WIDGET_CLASS (klass);
+    container_class = GTK_CONTAINER_CLASS (klass);
+    object_class = GDL_DOCK_OBJECT_CLASS (klass);
+    item_class = GDL_DOCK_ITEM_CLASS (klass);
+
+    g_object_class->set_property = gdl_dock_notebook_set_property;
+    g_object_class->get_property = gdl_dock_notebook_get_property;
+    
+    gtk_object_class->destroy = gdl_dock_notebook_destroy;
+
+    container_class->add = gdl_dock_notebook_add;
+    container_class->forall = gdl_dock_notebook_forall;
+    container_class->child_type = gdl_dock_notebook_child_type;
+    
+    object_class->is_compound = TRUE;
+    object_class->dock = gdl_dock_notebook_dock;
+    object_class->child_placement = gdl_dock_notebook_child_placement;
+    object_class->present = gdl_dock_notebook_present;
+    object_class->reorder = gdl_dock_notebook_reorder;
+    
+    item_class->has_grip = FALSE;
+    item_class->set_orientation = gdl_dock_notebook_set_orientation;    
+    
+    g_object_class_install_property (
+        g_object_class, PROP_PAGE,
+        g_param_spec_int ("page", _("Page"),
+                          _("The index of the current page"),
+                          0, G_MAXINT,
+                          0,
+                          G_PARAM_READWRITE |
+                          GDL_DOCK_PARAM_EXPORT | GDL_DOCK_PARAM_AFTER));
+
+    if (!style_initialized) {
+        style_initialized = TRUE;
+        
+        gtk_rc_parse_string (
+            "style \"gdl-dock-notebook-default\" {\n"
+            "xthickness = 2\n"
+            "ythickness = 2\n"
+            "}\n"
+            "widget_class \"*.GtkNotebook.GdlDockItem\" "
+            "style : gtk \"gdl-dock-notebook-default\"\n");
+    }
+}
+
+static void 
+gdl_dock_notebook_notify_cb (GObject    *g_object,
+                             GParamSpec *pspec,
+                             gpointer    user_data) 
+{
+    g_return_if_fail (user_data != NULL && GDL_IS_DOCK_NOTEBOOK (user_data));
+
+    /* chain the notify signal */
+    g_object_notify (G_OBJECT (user_data), pspec->name);
+}
+
+static gboolean 
+gdl_dock_notebook_button_cb (GtkWidget      *widget,
+                             GdkEventButton *event,
+                             gpointer        user_data)
+{
+    if (event->type == GDK_BUTTON_PRESS)
+        GDL_DOCK_ITEM_SET_FLAGS (user_data, GDL_DOCK_USER_ACTION);
+    else
+        GDL_DOCK_ITEM_UNSET_FLAGS (user_data, GDL_DOCK_USER_ACTION);
+
+    return FALSE;
+}
+    
+static void
+gdl_dock_notebook_instance_init (GdlDockNotebook *notebook)
+{
+    GdlDockItem *item;
+
+    item = GDL_DOCK_ITEM (notebook);
+
+    /* create the container notebook */
+    item->child = gdl_switcher_new ();
+    gtk_widget_set_parent (item->child, GTK_WIDGET (notebook));
+    gtk_notebook_set_tab_pos (GTK_NOTEBOOK (item->child), GTK_POS_BOTTOM);
+    g_signal_connect (item->child, "switch-page",
+                      (GCallback) gdl_dock_notebook_switch_page_cb, (gpointer) item);
+    g_signal_connect (item->child, "notify::page",
+                      (GCallback) gdl_dock_notebook_notify_cb, (gpointer) item);
+    g_signal_connect (item->child, "button-press-event",
+                      (GCallback) gdl_dock_notebook_button_cb, (gpointer) item);
+    g_signal_connect (item->child, "button-release-event",
+                      (GCallback) gdl_dock_notebook_button_cb, (gpointer) item);
+    gtk_notebook_set_scrollable (GTK_NOTEBOOK (item->child), TRUE);
+    gtk_widget_show (item->child);
+}
+
+static void 
+gdl_dock_notebook_set_property (GObject      *object,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+    GdlDockItem *item = GDL_DOCK_ITEM (object);
+
+    switch (prop_id) {
+        case PROP_PAGE:
+            if (item->child && GTK_IS_NOTEBOOK (item->child)) {
+                gtk_notebook_set_current_page (GTK_NOTEBOOK (item->child),
+                                               g_value_get_int (value));
+            }
+            
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+static void 
+gdl_dock_notebook_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+    GdlDockItem *item = GDL_DOCK_ITEM (object);
+
+    switch (prop_id) {
+        case PROP_PAGE:
+            if (item->child && GTK_IS_NOTEBOOK (item->child)) {
+                g_value_set_int (value, gtk_notebook_get_current_page
+                                 (GTK_NOTEBOOK (item->child)));
+            }
+            
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+
+static void
+gdl_dock_notebook_destroy (GtkObject *object)
+{
+    GdlDockItem *item = GDL_DOCK_ITEM (object);
+
+    /* we need to call the virtual first, since in GdlDockDestroy our
+       children dock objects are detached */
+    GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
+
+    /* after that we can remove the GtkNotebook */
+    if (item->child) {
+        gtk_widget_unparent (item->child);
+        item->child = NULL;
+    };
+}
+
+static void
+gdl_dock_notebook_switch_page_cb (GtkNotebook     *nb,
+                                  GtkNotebookPage *page,
+                                  gint             page_num,
+                                  gpointer         data)
+{
+    GdlDockNotebook *notebook;
+    GtkWidget       *tablabel;
+    
+    notebook = GDL_DOCK_NOTEBOOK (data);
+
+    /* deactivate old tablabel */
+    if (nb->cur_page) {
+        tablabel = gtk_notebook_get_tab_label (
+            nb, gtk_notebook_get_nth_page (
+                nb, gtk_notebook_get_current_page (nb)));
+        if (tablabel && GDL_IS_DOCK_TABLABEL (tablabel))
+            gdl_dock_tablabel_deactivate (GDL_DOCK_TABLABEL (tablabel));
+    };
+
+    /* activate new label */
+    tablabel = gtk_notebook_get_tab_label (
+        nb, gtk_notebook_get_nth_page (nb, page_num));
+    if (tablabel && GDL_IS_DOCK_TABLABEL (tablabel))
+        gdl_dock_tablabel_activate (GDL_DOCK_TABLABEL (tablabel));
+
+    if (GDL_DOCK_ITEM_USER_ACTION (notebook) &&
+        GDL_DOCK_OBJECT (notebook)->master)
+        g_signal_emit_by_name (GDL_DOCK_OBJECT (notebook)->master,
+                               "layout-changed");
+}
+
+static void
+gdl_dock_notebook_add (GtkContainer *container,
+                      GtkWidget    *widget)
+{
+    g_return_if_fail (container != NULL && widget != NULL);
+    g_return_if_fail (GDL_IS_DOCK_NOTEBOOK (container));
+    g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
+
+    gdl_dock_object_dock (GDL_DOCK_OBJECT (container),
+                          GDL_DOCK_OBJECT (widget),
+                          GDL_DOCK_CENTER,
+                          NULL);
+}
+
+static void
+gdl_dock_notebook_forall (GtkContainer *container,
+                         gboolean      include_internals,
+                         GtkCallback   callback,
+                         gpointer      callback_data)
+{
+    GdlDockItem *item;
+
+    g_return_if_fail (container != NULL);
+    g_return_if_fail (GDL_IS_DOCK_NOTEBOOK (container));
+    g_return_if_fail (callback != NULL);
+
+    if (include_internals) {
+        /* use GdlDockItem's forall */
+        GDL_CALL_PARENT (GTK_CONTAINER_CLASS, forall, 
+                           (container, include_internals, callback, callback_data));
+    }
+    else {
+        item = GDL_DOCK_ITEM (container);
+        if (item->child)
+            gtk_container_foreach (GTK_CONTAINER (item->child), callback, callback_data);
+    }
+}
+
+static GType
+gdl_dock_notebook_child_type (GtkContainer *container)
+{
+    return GDL_TYPE_DOCK_ITEM;
+}
+    
+static void
+gdl_dock_notebook_dock_child (GdlDockObject *requestor,
+                              gpointer       user_data)
+{
+    struct {
+        GdlDockObject    *object;
+        GdlDockPlacement  position;
+        GValue           *other_data;
+    } *data = user_data;
+
+    gdl_dock_object_dock (data->object, requestor, data->position, data->other_data);
+}
+
+static void
+gdl_dock_notebook_dock (GdlDockObject    *object,
+                        GdlDockObject    *requestor,
+                        GdlDockPlacement  position,
+                        GValue           *other_data)
+{
+    g_return_if_fail (GDL_IS_DOCK_NOTEBOOK (object));
+    g_return_if_fail (GDL_IS_DOCK_ITEM (requestor));
+
+    /* we only add support for GDL_DOCK_CENTER docking strategy here... for the rest
+       use our parent class' method */
+    if (position == GDL_DOCK_CENTER) {
+        /* we can only dock simple (not compound) items */
+        if (gdl_dock_object_is_compound (requestor)) {
+            struct {
+                GdlDockObject    *object;
+                GdlDockPlacement  position;
+                GValue           *other_data;
+            } data;
+
+            gdl_dock_object_freeze (requestor);
+            
+            data.object = object;
+            data.position = position;
+            data.other_data = other_data;
+             
+            gtk_container_foreach (GTK_CONTAINER (requestor),
+                                   (GtkCallback) gdl_dock_notebook_dock_child, &data);
+
+            gdl_dock_object_thaw (requestor);
+        }
+        else {
+            GdlDockItem *item = GDL_DOCK_ITEM (object);
+            GdlDockItem *requestor_item = GDL_DOCK_ITEM (requestor);
+            gchar       *long_name, *stock_id;
+            GdkPixbuf   *pixbuf_icon;
+            GtkWidget   *label;
+            gint         position = -1;
+            
+            g_object_get (requestor_item, "long-name", &long_name,
+                          "stock-id", &stock_id, "pixbuf-icon", &pixbuf_icon, NULL);
+            label = gdl_dock_item_get_tablabel (requestor_item);
+            if (!label) {
+                label = gtk_label_new (long_name);
+                gdl_dock_item_set_tablabel (requestor_item, label);
+            }
+#if 0
+            if (GDL_IS_DOCK_TABLABEL (label)) {
+                gdl_dock_tablabel_deactivate (GDL_DOCK_TABLABEL (label));
+                /* hide the item grip, as we will use the tablabel's */
+                gdl_dock_item_hide_grip (requestor_item);
+            }
+#endif
+
+            if (other_data && G_VALUE_HOLDS (other_data, G_TYPE_INT))
+                position = g_value_get_int (other_data);
+            
+            position = gdl_switcher_insert_page (GDL_SWITCHER (item->child), 
+                                                 GTK_WIDGET (requestor), label,
+                                                 long_name, long_name,
+                                                 stock_id, pixbuf_icon, position);
+            
+            GDL_DOCK_OBJECT_SET_FLAGS (requestor, GDL_DOCK_ATTACHED);
+            
+            /* Set current page to the newly docked widget. set current page
+             * really doesn't work if the page widget is not shown
+             */
+            gtk_widget_show (GTK_WIDGET (requestor));
+            gtk_notebook_set_current_page (GTK_NOTEBOOK (item->child),
+                                           position);
+            g_free (long_name);
+            g_free (stock_id);
+        }
+    }
+    else
+        GDL_CALL_PARENT (GDL_DOCK_OBJECT_CLASS, dock,
+                           (object, requestor, position, other_data));
+}
+
+static void
+gdl_dock_notebook_set_orientation (GdlDockItem    *item,
+                                   GtkOrientation  orientation)
+{
+    if (item->child && GTK_IS_NOTEBOOK (item->child)) {
+        if (orientation == GTK_ORIENTATION_HORIZONTAL)
+            gtk_notebook_set_tab_pos (GTK_NOTEBOOK (item->child), GTK_POS_TOP);
+        else
+            gtk_notebook_set_tab_pos (GTK_NOTEBOOK (item->child), GTK_POS_LEFT);
+    }
+
+    GDL_CALL_PARENT (GDL_DOCK_ITEM_CLASS, set_orientation, (item, orientation));
+}
+
+static gboolean 
+gdl_dock_notebook_child_placement (GdlDockObject    *object,
+                                   GdlDockObject    *child,
+                                   GdlDockPlacement *placement)
+{
+    GdlDockItem      *item = GDL_DOCK_ITEM (object);
+    GdlDockPlacement  pos = GDL_DOCK_NONE;
+    
+    if (item->child) {
+        GList *children, *l;
+
+        children = gtk_container_get_children (GTK_CONTAINER (item->child));
+        for (l = children; l; l = l->next) {
+            if (l->data == (gpointer) child) {
+                pos = GDL_DOCK_CENTER;
+                break;
+            }
+        }
+        g_list_free (children);
+    }
+
+    if (pos != GDL_DOCK_NONE) {
+        if (placement)
+            *placement = pos;
+        return TRUE;
+    }
+    else
+        return FALSE;
+}
+
+static void
+gdl_dock_notebook_present (GdlDockObject *object,
+                           GdlDockObject *child)
+{
+    GdlDockItem *item = GDL_DOCK_ITEM (object);
+    int i;
+    
+    i = gtk_notebook_page_num (GTK_NOTEBOOK (item->child),
+                               GTK_WIDGET (child));
+    if (i >= 0)
+        gtk_notebook_set_current_page (GTK_NOTEBOOK (item->child), i);
+
+    GDL_CALL_PARENT (GDL_DOCK_OBJECT_CLASS, present, (object, child));
+}
+
+static gboolean 
+gdl_dock_notebook_reorder (GdlDockObject    *object,
+                           GdlDockObject    *requestor,
+                           GdlDockPlacement  new_position,
+                           GValue           *other_data)
+{
+    GdlDockItem *item = GDL_DOCK_ITEM (object);
+    gint         current_position, new_pos = -1;
+    gboolean     handled = FALSE;
+    
+    if (item->child && new_position == GDL_DOCK_CENTER) {
+        current_position = gtk_notebook_page_num (GTK_NOTEBOOK (item->child),
+                                                  GTK_WIDGET (requestor));
+        if (current_position >= 0) {
+            handled = TRUE;
+    
+            if (other_data && G_VALUE_HOLDS (other_data, G_TYPE_INT))
+                new_pos = g_value_get_int (other_data);
+            
+            gtk_notebook_reorder_child (GTK_NOTEBOOK (item->child), 
+                                        GTK_WIDGET (requestor),
+                                        new_pos);
+        }
+    }
+    return handled;
+}
+
+/* ----- Public interface ----- */
+
+GtkWidget *
+gdl_dock_notebook_new (void)
+{
+    GdlDockNotebook *notebook;
+
+    notebook = GDL_DOCK_NOTEBOOK (g_object_new (GDL_TYPE_DOCK_NOTEBOOK, NULL));
+    GDL_DOCK_OBJECT_UNSET_FLAGS (notebook, GDL_DOCK_AUTOMATIC);
+    
+    return GTK_WIDGET (notebook);
+}
+
diff --git a/src/libgdl/gdl-dock-notebook.h b/src/libgdl/gdl-dock-notebook.h
new file mode 100644 (file)
index 0000000..105da6c
--- /dev/null
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This file is part of the GNOME Devtools Libraries.
+ *
+ * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GDL_DOCK_NOTEBOOK_H__
+#define __GDL_DOCK_NOTEBOOK_H__
+
+#include "libgdl/gdl-dock-item.h"
+
+G_BEGIN_DECLS
+
+/* standard macros */
+#define GDL_TYPE_DOCK_NOTEBOOK            (gdl_dock_notebook_get_type ())
+#define GDL_DOCK_NOTEBOOK(obj)            (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_NOTEBOOK, GdlDockNotebook))
+#define GDL_DOCK_NOTEBOOK_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_NOTEBOOK, GdlDockNotebookClass))
+#define GDL_IS_DOCK_NOTEBOOK(obj)         (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_NOTEBOOK))
+#define GDL_IS_DOCK_NOTEBOOK_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_NOTEBOOK))
+#define GDL_DOCK_NOTEBOOK_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK_NOTEBOOK, GdlDockNotebookClass))
+
+/* data types & structures */
+typedef struct _GdlDockNotebook        GdlDockNotebook;
+typedef struct _GdlDockNotebookClass   GdlDockNotebookClass;
+
+struct _GdlDockNotebook {
+    GdlDockItem  item;
+};
+
+struct _GdlDockNotebookClass {
+    GdlDockItemClass  parent_class;
+};
+
+
+/* public interface */
+GtkWidget     *gdl_dock_notebook_new               (void);
+
+GType          gdl_dock_notebook_get_type          (void);
+
+G_END_DECLS
+
+#endif
+
diff --git a/src/libgdl/gdl-dock-object.c b/src/libgdl/gdl-dock-object.c
new file mode 100644 (file)
index 0000000..dadf072
--- /dev/null
@@ -0,0 +1,942 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 
+ *
+ * gdl-dock-object.c - Abstract base class for all dock related objects
+ *
+ * This file is part of the GNOME Devtools Libraries.
+ *
+ * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gdl-i18n.h"
+#include <stdlib.h>
+#include <string.h>
+
+#include "gdl-tools.h"
+#include "gdl-dock-object.h"
+#include "gdl-dock-master.h"
+#include "libgdltypebuiltins.h"
+#include "libgdlmarshal.h"
+
+/* for later use by the registry */
+#include "gdl-dock.h"
+#include "gdl-dock-item.h"
+#include "gdl-dock-paned.h"
+#include "gdl-dock-notebook.h"
+#include "gdl-dock-placeholder.h"
+
+
+/* ----- Private prototypes ----- */
+
+static void     gdl_dock_object_class_init         (GdlDockObjectClass *klass);
+static void     gdl_dock_object_instance_init      (GdlDockObject      *object);
+
+static void     gdl_dock_object_set_property       (GObject            *g_object,
+                                                    guint               prop_id,
+                                                    const GValue       *value,
+                                                    GParamSpec         *pspec);
+static void     gdl_dock_object_get_property       (GObject            *g_object,
+                                                    guint               prop_id,
+                                                    GValue             *value,
+                                                    GParamSpec         *pspec);
+static void     gdl_dock_object_finalize           (GObject            *g_object);
+
+static void     gdl_dock_object_destroy            (GtkObject          *gtk_object);
+
+static void     gdl_dock_object_show               (GtkWidget          *widget);
+static void     gdl_dock_object_hide               (GtkWidget          *widget);
+
+static void     gdl_dock_object_real_detach        (GdlDockObject      *object,
+                                                    gboolean            recursive);
+static void     gdl_dock_object_real_reduce        (GdlDockObject      *object);
+static void     gdl_dock_object_dock_unimplemented (GdlDockObject     *object,
+                                                    GdlDockObject     *requestor,
+                                                    GdlDockPlacement   position,
+                                                    GValue            *other_data);
+static void     gdl_dock_object_real_present       (GdlDockObject     *object,
+                                                    GdlDockObject     *child);
+
+
+/* ----- Private data types and variables ----- */
+
+enum {
+    PROP_0,
+    PROP_NAME,
+    PROP_LONG_NAME,
+    PROP_STOCK_ID,
+    PROP_PIXBUF_ICON,
+    PROP_MASTER,
+    PROP_EXPORT_PROPERTIES
+};
+
+enum {
+    DETACH,
+    DOCK,
+    LAST_SIGNAL
+};
+
+static guint gdl_dock_object_signals [LAST_SIGNAL] = { 0 };
+
+/* ----- Private interface ----- */
+
+GDL_CLASS_BOILERPLATE (GdlDockObject, gdl_dock_object, GtkContainer, GTK_TYPE_CONTAINER);
+
+static void
+gdl_dock_object_class_init (GdlDockObjectClass *klass)
+{
+    GObjectClass      *g_object_class;
+    GtkObjectClass    *object_class;
+    GtkWidgetClass    *widget_class;
+    GtkContainerClass *container_class;
+
+    g_object_class = G_OBJECT_CLASS (klass);
+    object_class = GTK_OBJECT_CLASS (klass);
+    widget_class = GTK_WIDGET_CLASS (klass);
+    container_class = GTK_CONTAINER_CLASS (klass);
+
+    g_object_class->set_property = gdl_dock_object_set_property;
+    g_object_class->get_property = gdl_dock_object_get_property;
+    g_object_class->finalize = gdl_dock_object_finalize;
+
+    g_object_class_install_property (
+        g_object_class, PROP_NAME,
+        g_param_spec_string (GDL_DOCK_NAME_PROPERTY, _("Name"),
+                             _("Unique name for identifying the dock object"),
+                             NULL,
+                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                             GDL_DOCK_PARAM_EXPORT));
+
+    g_object_class_install_property (
+        g_object_class, PROP_LONG_NAME,
+        g_param_spec_string ("long-name", _("Long name"),
+                             _("Human readable name for the dock object"),
+                             NULL,
+                             G_PARAM_READWRITE));
+
+    g_object_class_install_property (
+        g_object_class, PROP_STOCK_ID,
+        g_param_spec_string ("stock-id", _("Stock Icon"),
+                             _("Stock icon for the dock object"),
+                             NULL,
+                             G_PARAM_READWRITE));
+
+    g_object_class_install_property (
+        g_object_class, PROP_PIXBUF_ICON,
+        g_param_spec_pointer ("pixbuf-icon", _("Pixbuf Icon"),
+                              _("Pixbuf icon for the dock object"),
+                              G_PARAM_READWRITE));
+
+    g_object_class_install_property (
+        g_object_class, PROP_MASTER,
+        g_param_spec_object ("master", _("Dock master"),
+                             _("Dock master this dock object is bound to"),
+                             GDL_TYPE_DOCK_MASTER,
+                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+    
+    object_class->destroy = gdl_dock_object_destroy;
+    
+    widget_class->show = gdl_dock_object_show;
+    widget_class->hide = gdl_dock_object_hide;
+    
+    klass->is_compound = TRUE;
+    
+    klass->detach = gdl_dock_object_real_detach;
+    klass->reduce = gdl_dock_object_real_reduce;
+    klass->dock_request = NULL;
+    klass->dock = gdl_dock_object_dock_unimplemented;
+    klass->reorder = NULL;
+    klass->present = gdl_dock_object_real_present;
+    klass->child_placement = NULL;
+    
+    gdl_dock_object_signals [DETACH] =
+        g_signal_new ("detach",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (GdlDockObjectClass, detach),
+                      NULL,
+                      NULL,
+                      gdl_marshal_VOID__BOOLEAN,
+                      G_TYPE_NONE,
+                      1,
+                      G_TYPE_BOOLEAN);
+
+    gdl_dock_object_signals [DOCK] =
+        g_signal_new ("dock",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_FIRST,
+                      G_STRUCT_OFFSET (GdlDockObjectClass, dock),
+                      NULL,
+                      NULL,
+                      gdl_marshal_VOID__OBJECT_ENUM_BOXED,
+                      G_TYPE_NONE,
+                      3,
+                      GDL_TYPE_DOCK_OBJECT,
+                      GDL_TYPE_DOCK_PLACEMENT,
+                      G_TYPE_VALUE);
+}
+
+static void
+gdl_dock_object_instance_init (GdlDockObject *object)
+{
+    object->flags = GDL_DOCK_AUTOMATIC;
+    object->freeze_count = 0;
+}
+
+static void
+gdl_dock_object_set_property  (GObject      *g_object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+    GdlDockObject *object = GDL_DOCK_OBJECT (g_object);
+
+    switch (prop_id) {
+    case PROP_NAME:
+        g_free (object->name);
+        object->name = g_value_dup_string (value);
+        break;
+    case PROP_LONG_NAME:
+        g_free (object->long_name);
+        object->long_name = g_value_dup_string (value);
+        break;
+    case PROP_STOCK_ID:
+        g_free (object->stock_id);
+        object->stock_id = g_value_dup_string (value);
+        break;
+    case PROP_PIXBUF_ICON:
+        object->pixbuf_icon = g_value_get_pointer (value);
+        break;
+    case PROP_MASTER:
+        if (g_value_get_object (value)) 
+            gdl_dock_object_bind (object, g_value_get_object (value));
+        else
+            gdl_dock_object_unbind (object);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+gdl_dock_object_get_property  (GObject      *g_object,
+                               guint         prop_id,
+                               GValue       *value,
+                               GParamSpec   *pspec)
+{
+    GdlDockObject *object = GDL_DOCK_OBJECT (g_object);
+
+    switch (prop_id) {
+    case PROP_NAME:
+        g_value_set_string (value, object->name);
+        break;
+    case PROP_LONG_NAME:
+        g_value_set_string (value, object->long_name);
+        break;
+    case PROP_STOCK_ID:
+        g_value_set_string (value, object->stock_id);
+        break;
+    case PROP_PIXBUF_ICON:
+        g_value_set_pointer (value, object->pixbuf_icon);
+        break;
+    case PROP_MASTER:
+        g_value_set_object (value, object->master);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+gdl_dock_object_finalize (GObject *g_object)
+{
+    GdlDockObject *object;
+    
+    g_return_if_fail (g_object != NULL && GDL_IS_DOCK_OBJECT (g_object));
+
+    object = GDL_DOCK_OBJECT (g_object);
+
+    g_free (object->name);
+    object->name = NULL;
+    g_free (object->long_name);
+    object->long_name = NULL;
+    g_free (object->stock_id);
+    object->stock_id = NULL;
+    object->pixbuf_icon = NULL;
+
+    GDL_CALL_PARENT (G_OBJECT_CLASS, finalize, (g_object));
+}
+
+static void
+gdl_dock_object_foreach_detach (GdlDockObject *object,
+                                gpointer       user_data)
+{
+    gdl_dock_object_detach (object, TRUE);
+}
+
+static void
+gdl_dock_object_destroy (GtkObject *gtk_object)
+{
+    GdlDockObject *object;
+
+    g_return_if_fail (GDL_IS_DOCK_OBJECT (gtk_object));
+
+    object = GDL_DOCK_OBJECT (gtk_object);
+    if (gdl_dock_object_is_compound (object)) {
+        /* detach our dock object children if we have some, and even
+           if we are not attached, so they can get notification */
+        gdl_dock_object_freeze (object);
+        gtk_container_foreach (GTK_CONTAINER (object),
+                               (GtkCallback) gdl_dock_object_foreach_detach,
+                               NULL);
+        object->reduce_pending = FALSE;
+        gdl_dock_object_thaw (object);
+    }
+    if (GDL_DOCK_OBJECT_ATTACHED (object)) {
+        /* detach ourselves */
+        gdl_dock_object_detach (object, FALSE);
+    }
+    
+    /* finally unbind us */
+    if (object->master)
+        gdl_dock_object_unbind (object);
+        
+    GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (gtk_object));
+}
+
+static void
+gdl_dock_object_foreach_automatic (GdlDockObject *object,
+                                   gpointer       user_data)
+{
+    void (* function) (GtkWidget *) = user_data;
+
+    if (GDL_DOCK_OBJECT_AUTOMATIC (object))
+        (* function) (GTK_WIDGET (object));
+}
+
+static void
+gdl_dock_object_show (GtkWidget *widget)
+{
+    if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT (widget))) {
+        gtk_container_foreach (GTK_CONTAINER (widget),
+                               (GtkCallback) gdl_dock_object_foreach_automatic,
+                               gtk_widget_show);
+    }
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, show, (widget));
+}
+
+static void
+gdl_dock_object_hide (GtkWidget *widget)
+{
+    if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT (widget))) {
+        gtk_container_foreach (GTK_CONTAINER (widget),
+                               (GtkCallback) gdl_dock_object_foreach_automatic,
+                               gtk_widget_hide);
+    }
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, hide, (widget));
+}
+
+static void
+gdl_dock_object_real_detach (GdlDockObject *object,
+                             gboolean       recursive)
+{
+    GdlDockObject *parent;
+    GtkWidget     *widget;
+    
+    g_return_if_fail (object != NULL);
+
+    /* detach children */
+    if (recursive && gdl_dock_object_is_compound (object)) {
+        gtk_container_foreach (GTK_CONTAINER (object),
+                               (GtkCallback) gdl_dock_object_detach,
+                               GINT_TO_POINTER (recursive));
+    }
+    
+    /* detach the object itself */
+    GDL_DOCK_OBJECT_UNSET_FLAGS (object, GDL_DOCK_ATTACHED);
+    parent = gdl_dock_object_get_parent_object (object);
+    widget = GTK_WIDGET (object);
+    if (widget->parent)
+        gtk_container_remove (GTK_CONTAINER (widget->parent), widget);
+    if (parent)
+        gdl_dock_object_reduce (parent);
+}
+
+static void
+gdl_dock_object_real_reduce (GdlDockObject *object)
+{
+    GdlDockObject *parent;
+    GList         *children;
+    
+    g_return_if_fail (object != NULL);
+
+    if (!gdl_dock_object_is_compound (object))
+        return;
+
+    parent = gdl_dock_object_get_parent_object (object);
+    children = gtk_container_get_children (GTK_CONTAINER (object));
+    if (g_list_length (children) <= 1) {
+        GList *l;
+        
+        /* detach ourselves and then re-attach our children to our
+           current parent.  if we are not currently attached, the
+           children are detached */
+        if (parent)
+            gdl_dock_object_freeze (parent);
+        gdl_dock_object_freeze (object);
+        gdl_dock_object_detach (object, FALSE);
+        for (l = children; l; l = l->next) {
+            GdlDockObject *child = GDL_DOCK_OBJECT (l->data);
+
+            g_object_ref (child);
+            GDL_DOCK_OBJECT_SET_FLAGS (child, GDL_DOCK_IN_REFLOW);
+            gdl_dock_object_detach (child, FALSE);
+            if (parent)
+                gtk_container_add (GTK_CONTAINER (parent), GTK_WIDGET (child));
+            GDL_DOCK_OBJECT_UNSET_FLAGS (child, GDL_DOCK_IN_REFLOW);
+            g_object_unref (child);
+        }
+        /* sink the widget, so any automatic floating widget is destroyed */
+        gtk_object_sink (GTK_OBJECT (object));
+        /* don't reenter */
+        object->reduce_pending = FALSE;
+        gdl_dock_object_thaw (object);
+        if (parent)
+            gdl_dock_object_thaw (parent);
+    }
+    g_list_free (children);
+}
+
+static void
+gdl_dock_object_dock_unimplemented (GdlDockObject    *object,
+                                    GdlDockObject    *requestor,
+                                    GdlDockPlacement  position,
+                                    GValue           *other_data)
+{
+    g_warning (_("Call to gdl_dock_object_dock in a dock object %p "
+                 "(object type is %s) which hasn't implemented this method"),
+               object, G_OBJECT_TYPE_NAME (object));
+}
+
+static void 
+gdl_dock_object_real_present (GdlDockObject *object,
+                              GdlDockObject *child)
+{
+    gtk_widget_show (GTK_WIDGET (object));
+}
+
+
+/* ----- Public interface ----- */
+
+gboolean
+gdl_dock_object_is_compound (GdlDockObject *object)
+{
+    GdlDockObjectClass *klass;
+
+    g_return_val_if_fail (object != NULL, FALSE);
+    g_return_val_if_fail (GDL_IS_DOCK_OBJECT (object), FALSE);
+
+    klass = GDL_DOCK_OBJECT_GET_CLASS (object);
+    return klass->is_compound;
+}
+
+void
+gdl_dock_object_detach (GdlDockObject *object,
+                        gboolean       recursive)
+{
+    g_return_if_fail (object != NULL);
+
+    if (!GDL_DOCK_OBJECT_ATTACHED (object))
+        return;
+    
+    /* freeze the object to avoid reducing while detaching children */
+    gdl_dock_object_freeze (object);
+    GDL_DOCK_OBJECT_SET_FLAGS (object, GDL_DOCK_IN_DETACH);
+    g_signal_emit (object, gdl_dock_object_signals [DETACH], 0, recursive);
+    GDL_DOCK_OBJECT_UNSET_FLAGS (object, GDL_DOCK_IN_DETACH);
+    gdl_dock_object_thaw (object);
+}
+
+GdlDockObject *
+gdl_dock_object_get_parent_object (GdlDockObject *object)
+{
+    GtkWidget *parent;
+    
+    g_return_val_if_fail (object != NULL, NULL);
+
+    parent = GTK_WIDGET (object)->parent;
+    while (parent && !GDL_IS_DOCK_OBJECT (parent)) {
+        parent = parent->parent;
+    }
+    
+    return parent ? GDL_DOCK_OBJECT (parent) : NULL;
+}
+
+void
+gdl_dock_object_freeze (GdlDockObject *object)
+{
+    g_return_if_fail (object != NULL);
+    
+    if (object->freeze_count == 0) {
+        g_object_ref (object);   /* dock objects shouldn't be
+                                    destroyed if they are frozen */
+    }
+    object->freeze_count++;
+}
+
+void
+gdl_dock_object_thaw (GdlDockObject *object)
+{
+    g_return_if_fail (object != NULL);
+    g_return_if_fail (object->freeze_count > 0);
+    
+    object->freeze_count--;
+    if (object->freeze_count == 0) {
+        if (object->reduce_pending) {
+            object->reduce_pending = FALSE;
+            gdl_dock_object_reduce (object);
+        }
+        g_object_unref (object);
+    }
+}
+
+void
+gdl_dock_object_reduce (GdlDockObject *object)
+{
+    g_return_if_fail (object != NULL);
+
+    if (GDL_DOCK_OBJECT_FROZEN (object)) {
+        object->reduce_pending = TRUE;
+        return;
+    }
+
+    GDL_CALL_VIRTUAL (object, GDL_DOCK_OBJECT_GET_CLASS, reduce, (object));
+}
+
+gboolean
+gdl_dock_object_dock_request (GdlDockObject  *object,
+                              gint            x,
+                              gint            y,
+                              GdlDockRequest *request)
+{
+    g_return_val_if_fail (object != NULL && request != NULL, FALSE);
+
+    return GDL_CALL_VIRTUAL_WITH_DEFAULT (object,
+                                          GDL_DOCK_OBJECT_GET_CLASS,
+                                          dock_request,
+                                          (object, x, y, request),
+                                          FALSE);
+}
+
+void
+gdl_dock_object_dock (GdlDockObject    *object,
+                      GdlDockObject    *requestor,
+                      GdlDockPlacement  position,
+                      GValue           *other_data)
+{
+    GdlDockObject *parent;
+    
+    g_return_if_fail (object != NULL && requestor != NULL);
+        
+    if (object == requestor)
+        return;
+    
+    if (!object->master)
+        g_warning (_("Dock operation requested in a non-bound object %p. "
+                     "The application might crash"), object);
+        
+    if (!gdl_dock_object_is_bound (requestor))
+        gdl_dock_object_bind (requestor, object->master);
+
+    if (requestor->master != object->master) {
+        g_warning (_("Cannot dock %p to %p because they belong to different masters"),
+                   requestor, object);
+        return;
+    }
+
+    /* first, see if we can optimize things by reordering */
+    if (position != GDL_DOCK_NONE) {
+        parent = gdl_dock_object_get_parent_object (object);
+        if (gdl_dock_object_reorder (object, requestor, position, other_data) ||
+            (parent && gdl_dock_object_reorder (parent, requestor, position, other_data)))
+            return;
+    }
+    
+    /* freeze the object, since under some conditions it might be destroyed when
+       detaching the requestor */
+    gdl_dock_object_freeze (object);
+
+    /* detach the requestor before docking */
+    g_object_ref (requestor);
+    if (GDL_DOCK_OBJECT_ATTACHED (requestor))
+        gdl_dock_object_detach (requestor, FALSE);
+    
+    if (position != GDL_DOCK_NONE)
+        g_signal_emit (object, gdl_dock_object_signals [DOCK], 0,
+                       requestor, position, other_data);
+
+    g_object_unref (requestor);
+    gdl_dock_object_thaw (object);
+}
+
+void
+gdl_dock_object_bind (GdlDockObject *object,
+                      GObject       *master)
+{
+    g_return_if_fail (object != NULL && master != NULL);
+    g_return_if_fail (GDL_IS_DOCK_MASTER (master));
+    
+    if (object->master == master)
+        /* nothing to do here */
+        return;
+    
+    if (object->master) {
+        g_warning (_("Attempt to bind to %p an already bound dock object %p "
+                     "(current master: %p)"), master, object, object->master);
+        return;
+    }
+
+    gdl_dock_master_add (GDL_DOCK_MASTER (master), object);
+    object->master = master;
+    g_object_add_weak_pointer (master, (gpointer *) &object->master);
+
+    g_object_notify (G_OBJECT (object), "master");
+}
+
+void
+gdl_dock_object_unbind (GdlDockObject *object)
+{
+    g_return_if_fail (object != NULL);
+
+    g_object_ref (object);
+
+    /* detach the object first */
+    if (GDL_DOCK_OBJECT_ATTACHED (object))
+        gdl_dock_object_detach (object, TRUE);
+    
+    if (object->master) {
+        GObject *master = object->master;
+        g_object_remove_weak_pointer (master, (gpointer *) &object->master);
+        object->master = NULL;
+        gdl_dock_master_remove (GDL_DOCK_MASTER (master), object);
+        g_object_notify (G_OBJECT (object), "master");
+    }
+    g_object_unref (object);
+}
+
+gboolean
+gdl_dock_object_is_bound (GdlDockObject *object)
+{
+    g_return_val_if_fail (object != NULL, FALSE);
+    return (object->master != NULL);
+}
+
+gboolean
+gdl_dock_object_reorder (GdlDockObject    *object,
+                         GdlDockObject    *child,
+                         GdlDockPlacement  new_position,
+                         GValue           *other_data)
+{
+    g_return_val_if_fail (object != NULL && child != NULL, FALSE);
+
+    return GDL_CALL_VIRTUAL_WITH_DEFAULT (object,
+                                          GDL_DOCK_OBJECT_GET_CLASS,
+                                          reorder,
+                                          (object, child, new_position, other_data),
+                                          FALSE);
+}
+
+void 
+gdl_dock_object_present (GdlDockObject *object,
+                         GdlDockObject *child)
+{
+    GdlDockObject *parent;
+    
+    g_return_if_fail (object != NULL && GDL_IS_DOCK_OBJECT (object));
+
+    parent = gdl_dock_object_get_parent_object (object);
+    if (parent)
+        /* chain the call to our parent */
+        gdl_dock_object_present (parent, object);
+
+    GDL_CALL_VIRTUAL (object, GDL_DOCK_OBJECT_GET_CLASS, present, (object, child));
+}
+
+/**
+ * gdl_dock_object_child_placement:
+ * @object: the dock object we are asking for child placement
+ * @child: the child of the @object we want the placement for
+ * @placement: where to return the placement information
+ *
+ * This function returns information about placement of a child dock
+ * object inside another dock object.  The function returns %TRUE if
+ * @child is effectively a child of @object.  @placement should
+ * normally be initially setup to %GDL_DOCK_NONE.  If it's set to some
+ * other value, this function will not touch the stored value if the
+ * specified placement is "compatible" with the actual placement of
+ * the child.
+ *
+ * @placement can be %NULL, in which case the function simply tells if
+ * @child is attached to @object.
+ *
+ * Returns: %TRUE if @child is a child of @object.
+ */
+gboolean 
+gdl_dock_object_child_placement (GdlDockObject    *object,
+                                 GdlDockObject    *child,
+                                 GdlDockPlacement *placement)
+{
+    g_return_val_if_fail (object != NULL && child != NULL, FALSE);
+
+    /* simple case */
+    if (!gdl_dock_object_is_compound (object))
+        return FALSE;
+    
+    return GDL_CALL_VIRTUAL_WITH_DEFAULT (object, GDL_DOCK_OBJECT_GET_CLASS,
+                                          child_placement,
+                                          (object, child, placement),
+                                          FALSE);
+}
+
+
+/* ----- dock param type functions start here ------ */
+
+static void 
+gdl_dock_param_export_int (const GValue *src,
+                           GValue       *dst)
+{
+    dst->data [0].v_pointer = g_strdup_printf ("%d", src->data [0].v_int);
+}
+
+static void 
+gdl_dock_param_export_uint (const GValue *src,
+                            GValue       *dst)
+{
+    dst->data [0].v_pointer = g_strdup_printf ("%u", src->data [0].v_uint);
+}
+
+static void 
+gdl_dock_param_export_string (const GValue *src,
+                              GValue       *dst)
+{
+    dst->data [0].v_pointer = g_strdup (src->data [0].v_pointer);
+}
+
+static void 
+gdl_dock_param_export_bool (const GValue *src,
+                            GValue       *dst)
+{
+    dst->data [0].v_pointer = g_strdup_printf ("%s", src->data [0].v_int ? "yes" : "no");
+}
+
+static void 
+gdl_dock_param_export_placement (const GValue *src,
+                                 GValue       *dst)
+{
+    switch (src->data [0].v_int) {
+        case GDL_DOCK_NONE:
+            dst->data [0].v_pointer = g_strdup ("");
+            break;
+        case GDL_DOCK_TOP:
+            dst->data [0].v_pointer = g_strdup ("top");
+            break;
+        case GDL_DOCK_BOTTOM:
+            dst->data [0].v_pointer = g_strdup ("bottom");
+            break;
+        case GDL_DOCK_LEFT:
+            dst->data [0].v_pointer = g_strdup ("left");
+            break;
+        case GDL_DOCK_RIGHT:
+            dst->data [0].v_pointer = g_strdup ("right");
+            break;
+        case GDL_DOCK_CENTER:
+            dst->data [0].v_pointer = g_strdup ("center");
+            break;
+        case GDL_DOCK_FLOATING:
+            dst->data [0].v_pointer = g_strdup ("floating");
+            break;
+    }
+}
+
+static void 
+gdl_dock_param_import_int (const GValue *src,
+                           GValue       *dst)
+{
+    dst->data [0].v_int = atoi (src->data [0].v_pointer);
+}
+
+static void 
+gdl_dock_param_import_uint (const GValue *src,
+                            GValue       *dst)
+{
+    dst->data [0].v_uint = (guint) atoi (src->data [0].v_pointer);
+}
+
+static void 
+gdl_dock_param_import_string (const GValue *src,
+                              GValue       *dst)
+{
+    dst->data [0].v_pointer = g_strdup (src->data [0].v_pointer);
+}
+
+static void 
+gdl_dock_param_import_bool (const GValue *src,
+                            GValue       *dst)
+{
+    dst->data [0].v_int = !strcmp (src->data [0].v_pointer, "yes");
+}
+
+static void 
+gdl_dock_param_import_placement (const GValue *src,
+                                 GValue       *dst)
+{
+    if (!strcmp (src->data [0].v_pointer, "top"))
+        dst->data [0].v_int = GDL_DOCK_TOP;
+    else if (!strcmp (src->data [0].v_pointer, "bottom"))
+        dst->data [0].v_int = GDL_DOCK_BOTTOM;
+    else if (!strcmp (src->data [0].v_pointer, "center"))
+        dst->data [0].v_int = GDL_DOCK_CENTER;
+    else if (!strcmp (src->data [0].v_pointer, "left"))
+        dst->data [0].v_int = GDL_DOCK_LEFT;
+    else if (!strcmp (src->data [0].v_pointer, "right"))
+        dst->data [0].v_int = GDL_DOCK_RIGHT;
+    else if (!strcmp (src->data [0].v_pointer, "floating"))
+        dst->data [0].v_int = GDL_DOCK_FLOATING;
+    else
+        dst->data [0].v_int = GDL_DOCK_NONE;
+}
+
+GType
+gdl_dock_param_get_type (void)
+{
+    static GType our_type = 0;
+
+    if (our_type == 0) {
+        GTypeInfo tinfo = { 0, };
+        our_type = g_type_register_static (G_TYPE_STRING, "GdlDockParam", &tinfo, 0);
+
+        /* register known transform functions */
+        /* exporters */
+        g_value_register_transform_func (G_TYPE_INT, our_type, gdl_dock_param_export_int);
+        g_value_register_transform_func (G_TYPE_UINT, our_type, gdl_dock_param_export_uint);
+        g_value_register_transform_func (G_TYPE_STRING, our_type, gdl_dock_param_export_string);
+        g_value_register_transform_func (G_TYPE_BOOLEAN, our_type, gdl_dock_param_export_bool);
+        g_value_register_transform_func (GDL_TYPE_DOCK_PLACEMENT, our_type, gdl_dock_param_export_placement);
+        /* importers */
+        g_value_register_transform_func (our_type, G_TYPE_INT, gdl_dock_param_import_int);
+        g_value_register_transform_func (our_type, G_TYPE_UINT, gdl_dock_param_import_uint);
+        g_value_register_transform_func (our_type, G_TYPE_STRING, gdl_dock_param_import_string);
+        g_value_register_transform_func (our_type, G_TYPE_BOOLEAN, gdl_dock_param_import_bool);
+        g_value_register_transform_func (our_type, GDL_TYPE_DOCK_PLACEMENT, gdl_dock_param_import_placement);
+    }
+
+    return our_type;
+}
+
+/* -------------- nick <-> type conversion functions --------------- */
+
+static GRelation *dock_register = NULL;
+
+enum {
+    INDEX_NICK = 0,
+    INDEX_TYPE
+};
+
+static void
+gdl_dock_object_register_init (void)
+{
+    if (dock_register)
+        return;
+    
+    /* FIXME: i don't know if GRelation is efficient */
+    dock_register = g_relation_new (2);
+    g_relation_index (dock_register, INDEX_NICK, g_str_hash, g_str_equal);
+    g_relation_index (dock_register, INDEX_TYPE, g_direct_hash, g_direct_equal);
+
+    /* add known types */
+    g_relation_insert (dock_register, "dock", (gpointer) GDL_TYPE_DOCK);
+    g_relation_insert (dock_register, "item", (gpointer) GDL_TYPE_DOCK_ITEM);
+    g_relation_insert (dock_register, "paned", (gpointer) GDL_TYPE_DOCK_PANED);
+    g_relation_insert (dock_register, "notebook", (gpointer) GDL_TYPE_DOCK_NOTEBOOK);
+    g_relation_insert (dock_register, "placeholder", (gpointer) GDL_TYPE_DOCK_PLACEHOLDER);
+}
+
+G_CONST_RETURN gchar *
+gdl_dock_object_nick_from_type (GType type)
+{
+    GTuples *tuples;
+    gchar *nick = NULL;
+    
+    if (!dock_register)
+        gdl_dock_object_register_init ();
+
+    if (g_relation_count (dock_register, (gpointer) type, INDEX_TYPE) > 0) {
+        tuples = g_relation_select (dock_register, (gpointer) type, INDEX_TYPE);
+        nick = (gchar *) g_tuples_index (tuples, 0, INDEX_NICK);
+        g_tuples_destroy (tuples);
+    }
+    
+    return nick ? nick : g_type_name (type);
+}
+
+GType
+gdl_dock_object_type_from_nick (const gchar *nick)
+{
+    GTuples *tuples;
+    GType type = G_TYPE_NONE;
+    
+    if (!dock_register)
+        gdl_dock_object_register_init ();
+
+    if (g_relation_count (dock_register, (gpointer) nick, INDEX_NICK) > 0) {
+        tuples = g_relation_select (dock_register, (gpointer) nick, INDEX_NICK);
+        type = (GType) g_tuples_index (tuples, 0, INDEX_TYPE);
+        g_tuples_destroy (tuples);
+    }
+    else {
+        /* try searching in the glib type system */
+        type = g_type_from_name (nick);
+    }
+    
+    return type;
+}
+
+GType
+gdl_dock_object_set_type_for_nick (const gchar *nick,
+                                   GType        type)
+{
+    GType old_type = G_TYPE_NONE;
+    
+    if (!dock_register)
+        gdl_dock_object_register_init ();
+
+    g_return_val_if_fail (g_type_is_a (type, GDL_TYPE_DOCK_OBJECT), G_TYPE_NONE);
+    
+    if (g_relation_count (dock_register, (gpointer) nick, INDEX_NICK) > 0) {
+        old_type = gdl_dock_object_type_from_nick (nick);
+        g_relation_delete (dock_register, (gpointer) nick, INDEX_NICK);
+    }
+    
+    g_relation_insert (dock_register, nick, type);
+
+    return old_type;
+}
+
diff --git a/src/libgdl/gdl-dock-object.h b/src/libgdl/gdl-dock-object.h
new file mode 100644 (file)
index 0000000..f214183
--- /dev/null
@@ -0,0 +1,225 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 
+ *
+ * gdl-dock-object.h - Abstract base class for all dock related objects
+ *
+ * This file is part of the GNOME Devtools Libraries.
+ *
+ * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GDL_DOCK_OBJECT_H__
+#define __GDL_DOCK_OBJECT_H__
+
+#include <gtk/gtkcontainer.h>
+
+G_BEGIN_DECLS
+
+/* standard macros */
+#define GDL_TYPE_DOCK_OBJECT             (gdl_dock_object_get_type ())
+#define GDL_DOCK_OBJECT(obj)             (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_OBJECT, GdlDockObject))
+#define GDL_DOCK_OBJECT_CLASS(klass)     (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_OBJECT, GdlDockObjectClass))
+#define GDL_IS_DOCK_OBJECT(obj)          (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_OBJECT))
+#define GDL_IS_DOCK_OBJECT_CLASS(klass)  (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_OBJECT))
+#define GDL_DOCK_OBJECT_GET_CLASS(obj)   (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK_OBJECT, GdlDockObjectClass))
+
+/* data types & structures */
+typedef enum {
+    /* the parameter is to be exported for later layout rebuilding */
+    GDL_DOCK_PARAM_EXPORT = 1 << G_PARAM_USER_SHIFT,
+    /* the parameter must be set after adding the children objects */
+    GDL_DOCK_PARAM_AFTER  = 1 << (G_PARAM_USER_SHIFT + 1)
+} GdlDockParamFlags;
+
+#define GDL_DOCK_NAME_PROPERTY    "name"
+#define GDL_DOCK_MASTER_PROPERTY  "master"
+
+typedef enum {
+    GDL_DOCK_AUTOMATIC  = 1 << 0,
+    GDL_DOCK_ATTACHED   = 1 << 1,
+    GDL_DOCK_IN_REFLOW  = 1 << 2,
+    GDL_DOCK_IN_DETACH  = 1 << 3
+} GdlDockObjectFlags;
+
+#define GDL_DOCK_OBJECT_FLAGS_SHIFT 8
+
+typedef enum {
+    GDL_DOCK_NONE = 0,
+    GDL_DOCK_TOP,
+    GDL_DOCK_BOTTOM,
+    GDL_DOCK_RIGHT,
+    GDL_DOCK_LEFT,
+    GDL_DOCK_CENTER,
+    GDL_DOCK_FLOATING
+} GdlDockPlacement;
+
+typedef struct _GdlDockObject      GdlDockObject;
+typedef struct _GdlDockObjectClass GdlDockObjectClass;
+typedef struct _GdlDockRequest     GdlDockRequest;
+
+struct _GdlDockRequest {
+    GdlDockObject    *applicant;
+    GdlDockObject    *target;
+    GdlDockPlacement  position;
+    GdkRectangle      rect;
+    GValue            extra;
+};
+
+struct _GdlDockObject {
+    GtkContainer        container;
+
+    GdlDockObjectFlags  flags;
+    gint                freeze_count;
+    
+    GObject            *master;
+    gchar              *name;
+    gchar              *long_name;
+    gchar              *stock_id;
+    GdkPixbuf          *pixbuf_icon;
+    
+    gboolean            reduce_pending;
+};
+
+struct _GdlDockObjectClass {
+    GtkContainerClass parent_class;
+
+    gboolean          is_compound;
+    
+    void     (* detach)          (GdlDockObject    *object,
+                                  gboolean          recursive);
+    void     (* reduce)          (GdlDockObject    *object);
+
+    gboolean (* dock_request)    (GdlDockObject    *object,
+                                  gint              x,
+                                  gint              y,
+                                  GdlDockRequest   *request);
+
+    void     (* dock)            (GdlDockObject    *object,
+                                  GdlDockObject    *requestor,
+                                  GdlDockPlacement  position,
+                                  GValue           *other_data);
+    
+    gboolean (* reorder)         (GdlDockObject    *object,
+                                  GdlDockObject    *child,
+                                  GdlDockPlacement  new_position,
+                                  GValue           *other_data);
+
+    void     (* present)         (GdlDockObject    *object,
+                                  GdlDockObject    *child);
+
+    gboolean (* child_placement) (GdlDockObject    *object,
+                                  GdlDockObject    *child,
+                                  GdlDockPlacement *placement);
+};
+
+/* additional macros */
+#define GDL_DOCK_OBJECT_FLAGS(obj)  (GDL_DOCK_OBJECT (obj)->flags)
+#define GDL_DOCK_OBJECT_AUTOMATIC(obj) \
+    ((GDL_DOCK_OBJECT_FLAGS (obj) & GDL_DOCK_AUTOMATIC) != 0)
+#define GDL_DOCK_OBJECT_ATTACHED(obj) \
+    ((GDL_DOCK_OBJECT_FLAGS (obj) & GDL_DOCK_ATTACHED) != 0)
+#define GDL_DOCK_OBJECT_IN_REFLOW(obj) \
+    ((GDL_DOCK_OBJECT_FLAGS (obj) & GDL_DOCK_IN_REFLOW) != 0)
+#define GDL_DOCK_OBJECT_IN_DETACH(obj) \
+    ((GDL_DOCK_OBJECT_FLAGS (obj) & GDL_DOCK_IN_DETACH) != 0)
+
+#define GDL_DOCK_OBJECT_SET_FLAGS(obj,flag) \
+    G_STMT_START { (GDL_DOCK_OBJECT_FLAGS (obj) |= (flag)); } G_STMT_END
+#define GDL_DOCK_OBJECT_UNSET_FLAGS(obj,flag) \
+    G_STMT_START { (GDL_DOCK_OBJECT_FLAGS (obj) &= ~(flag)); } G_STMT_END
+#define GDL_DOCK_OBJECT_FROZEN(obj) (GDL_DOCK_OBJECT (obj)->freeze_count > 0)
+
+
+/* public interface */
+GType          gdl_dock_object_get_type          (void);
+
+gboolean       gdl_dock_object_is_compound       (GdlDockObject    *object);
+
+void           gdl_dock_object_detach            (GdlDockObject    *object,
+                                                  gboolean          recursive);
+
+GdlDockObject *gdl_dock_object_get_parent_object (GdlDockObject    *object);
+
+void           gdl_dock_object_freeze            (GdlDockObject    *object);
+void           gdl_dock_object_thaw              (GdlDockObject    *object);
+
+void           gdl_dock_object_reduce            (GdlDockObject    *object);
+
+gboolean       gdl_dock_object_dock_request      (GdlDockObject    *object,
+                                                  gint              x,
+                                                  gint              y,
+                                                  GdlDockRequest   *request);
+void           gdl_dock_object_dock              (GdlDockObject    *object,
+                                                  GdlDockObject    *requestor,
+                                                  GdlDockPlacement  position,
+                                                  GValue           *other_data);
+
+void           gdl_dock_object_bind              (GdlDockObject    *object,
+                                                  GObject          *master);
+void           gdl_dock_object_unbind            (GdlDockObject    *object);
+gboolean       gdl_dock_object_is_bound          (GdlDockObject    *object);
+
+gboolean       gdl_dock_object_reorder           (GdlDockObject    *object,
+                                                  GdlDockObject    *child,
+                                                  GdlDockPlacement  new_position,
+                                                  GValue           *other_data);
+
+void           gdl_dock_object_present           (GdlDockObject    *object,
+                                                  GdlDockObject    *child);
+
+gboolean       gdl_dock_object_child_placement   (GdlDockObject    *object,
+                                                  GdlDockObject    *child,
+                                                  GdlDockPlacement *placement);
+
+/* other types */
+
+/* this type derives from G_TYPE_STRING and is meant to be the basic
+   type for serializing object parameters which are exported
+   (i.e. those that are needed for layout rebuilding) */
+#define GDL_TYPE_DOCK_PARAM   (gdl_dock_param_get_type ())
+
+GType gdl_dock_param_get_type (void);
+
+/* functions for setting/retrieving nick names for serializing GdlDockObject types */
+G_CONST_RETURN gchar *gdl_dock_object_nick_from_type    (GType        type);
+GType                 gdl_dock_object_type_from_nick    (const gchar *nick);
+GType                 gdl_dock_object_set_type_for_nick (const gchar *nick,
+                                                         GType        type);
+
+
+/* helper macros */
+#define GDL_TRACE_OBJECT(object, format, args...) \
+    G_STMT_START {                            \
+    g_log (G_LOG_DOMAIN,                      \
+          G_LOG_LEVEL_DEBUG,                 \
+           "%s:%d (%s) %s [%p %d%s:%d]: "format, \
+          __FILE__,                          \
+          __LINE__,                          \
+          __PRETTY_FUNCTION__,               \
+           G_OBJECT_TYPE_NAME (object), object, \
+           G_OBJECT (object)->ref_count, \
+           (GTK_IS_OBJECT (object) && GTK_OBJECT_FLOATING (object)) ? "(float)" : "", \
+           GDL_IS_DOCK_OBJECT (object) ? GDL_DOCK_OBJECT (object)->freeze_count : -1, \
+          ##args); } G_STMT_END                   
+    
+
+
+G_END_DECLS
+
+#endif  /* __GDL_DOCK_OBJECT_H__ */
+
diff --git a/src/libgdl/gdl-dock-paned.c b/src/libgdl/gdl-dock-paned.c
new file mode 100644 (file)
index 0000000..a176f1f
--- /dev/null
@@ -0,0 +1,673 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * gdl-dock-paned.h
+ *
+ * This file is part of the GNOME Devtools Libraries.
+ *
+ * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gdl-i18n.h"
+#include <string.h>
+#include <gtk/gtkhpaned.h>
+#include <gtk/gtkvpaned.h>
+
+#include "gdl-tools.h"
+#include "gdl-dock-paned.h"
+
+
+/* Private prototypes */
+
+static void     gdl_dock_paned_class_init     (GdlDockPanedClass *klass);
+static void     gdl_dock_paned_instance_init  (GdlDockPaned      *paned);
+static GObject *gdl_dock_paned_constructor    (GType              type,
+                                               guint              n_construct_properties,
+                                               GObjectConstructParam *construct_param);
+static void     gdl_dock_paned_set_property   (GObject           *object,
+                                               guint              prop_id,
+                                               const GValue      *value,
+                                               GParamSpec        *pspec);
+static void     gdl_dock_paned_get_property   (GObject           *object,
+                                               guint              prop_id,
+                                               GValue            *value,
+                                               GParamSpec        *pspec);
+
+static void     gdl_dock_paned_destroy        (GtkObject         *object);
+
+static void     gdl_dock_paned_add            (GtkContainer      *container,
+                                               GtkWidget         *widget);
+static void     gdl_dock_paned_forall         (GtkContainer      *container,
+                                               gboolean           include_internals,
+                                               GtkCallback        callback,
+                                               gpointer           callback_data);
+static GType    gdl_dock_paned_child_type     (GtkContainer      *container);
+
+static gboolean gdl_dock_paned_dock_request   (GdlDockObject     *object, 
+                                               gint               x,
+                                               gint               y, 
+                                               GdlDockRequest    *request);
+static void     gdl_dock_paned_dock           (GdlDockObject    *object,
+                                               GdlDockObject    *requestor,
+                                               GdlDockPlacement  position,
+                                               GValue           *other_data);
+
+static void     gdl_dock_paned_set_orientation (GdlDockItem    *item,
+                                                GtkOrientation  orientation);
+
+static gboolean gdl_dock_paned_child_placement (GdlDockObject    *object,
+                                                GdlDockObject    *child,
+                                                GdlDockPlacement *placement);
+
+
+/* ----- Class variables and definitions ----- */
+
+#define SPLIT_RATIO  0.3
+
+enum {
+    PROP_0,
+    PROP_POSITION
+};
+
+
+/* ----- Private functions ----- */
+
+GDL_CLASS_BOILERPLATE (GdlDockPaned, gdl_dock_paned, GdlDockItem, GDL_TYPE_DOCK_ITEM);
+
+static void
+gdl_dock_paned_class_init (GdlDockPanedClass *klass)
+{
+    GObjectClass       *g_object_class;
+    GtkObjectClass     *gtk_object_class;
+    GtkWidgetClass     *widget_class;
+    GtkContainerClass  *container_class;
+    GdlDockObjectClass *object_class;
+    GdlDockItemClass   *item_class;
+
+    g_object_class = G_OBJECT_CLASS (klass);
+    gtk_object_class = GTK_OBJECT_CLASS (klass);
+    widget_class = GTK_WIDGET_CLASS (klass);
+    container_class = GTK_CONTAINER_CLASS (klass);
+    object_class = GDL_DOCK_OBJECT_CLASS (klass);
+    item_class = GDL_DOCK_ITEM_CLASS (klass);
+
+    g_object_class->set_property = gdl_dock_paned_set_property;
+    g_object_class->get_property = gdl_dock_paned_get_property;
+    g_object_class->constructor = gdl_dock_paned_constructor;
+    
+    gtk_object_class->destroy = gdl_dock_paned_destroy;
+
+    container_class->add = gdl_dock_paned_add;
+    container_class->forall = gdl_dock_paned_forall;
+    container_class->child_type = gdl_dock_paned_child_type;
+    
+    object_class->is_compound = TRUE;
+    
+    object_class->dock_request = gdl_dock_paned_dock_request;
+    object_class->dock = gdl_dock_paned_dock;
+    object_class->child_placement = gdl_dock_paned_child_placement;
+    
+    item_class->has_grip = FALSE;
+    item_class->set_orientation = gdl_dock_paned_set_orientation;    
+
+    g_object_class_install_property (
+        g_object_class, PROP_POSITION,
+        g_param_spec_uint ("position", _("Position"),
+                           _("Position of the divider in pixels"),
+                           0, G_MAXINT, 0,
+                           G_PARAM_READWRITE |
+                           GDL_DOCK_PARAM_EXPORT | GDL_DOCK_PARAM_AFTER));
+}
+
+static void
+gdl_dock_paned_instance_init (GdlDockPaned *paned)
+{
+    paned->position_changed = FALSE;
+}
+
+static void 
+gdl_dock_paned_notify_cb (GObject    *g_object,
+                          GParamSpec *pspec,
+                          gpointer    user_data) 
+{
+    GdlDockPaned *paned;
+    
+    g_return_if_fail (user_data != NULL && GDL_IS_DOCK_PANED (user_data));
+    
+    /* chain the notification to the GdlDockPaned */
+    g_object_notify (G_OBJECT (user_data), pspec->name);
+    
+    paned = GDL_DOCK_PANED (user_data);
+    
+    if (GDL_DOCK_ITEM_USER_ACTION (user_data) && !strcmp (pspec->name, "position"))
+        paned->position_changed = TRUE;
+}
+
+static gboolean 
+gdl_dock_paned_button_cb (GtkWidget      *widget,
+                          GdkEventButton *event,
+                          gpointer        user_data)
+{
+    GdlDockPaned *paned;
+    
+    g_return_val_if_fail (user_data != NULL && GDL_IS_DOCK_PANED (user_data), FALSE);
+    
+    paned = GDL_DOCK_PANED (user_data);
+    if (event->button == 1) {
+        if (event->type == GDK_BUTTON_PRESS)
+            GDL_DOCK_ITEM_SET_FLAGS (user_data, GDL_DOCK_USER_ACTION);
+        else {
+            GDL_DOCK_ITEM_UNSET_FLAGS (user_data, GDL_DOCK_USER_ACTION);
+            if (paned->position_changed) {
+                /* emit pending layout changed signal to track separator position */
+                if (GDL_DOCK_OBJECT (paned)->master)
+                    g_signal_emit_by_name (GDL_DOCK_OBJECT (paned)->master, "layout-changed");
+                paned->position_changed = FALSE;
+            }
+        }
+    }
+    
+    return FALSE;
+}
+
+static void 
+gdl_dock_paned_create_child (GdlDockPaned   *paned,
+                             GtkOrientation  orientation) 
+{
+    GdlDockItem *item;
+    
+    item = GDL_DOCK_ITEM (paned);
+    
+    if (item->child)
+        gtk_widget_unparent (GTK_WIDGET (item->child));
+    
+    /* create the container paned */
+    if (orientation == GTK_ORIENTATION_HORIZONTAL)
+        item->child = gtk_hpaned_new ();
+    else
+        item->child = gtk_vpaned_new ();
+    
+    /* get notification for propagation */
+    g_signal_connect (item->child, "notify::position",
+                      (GCallback) gdl_dock_paned_notify_cb, (gpointer) item);
+    g_signal_connect (item->child, "button-press-event",
+                      (GCallback) gdl_dock_paned_button_cb, (gpointer) item);
+    g_signal_connect (item->child, "button-release-event",
+                      (GCallback) gdl_dock_paned_button_cb, (gpointer) item);
+    
+    gtk_widget_set_parent (item->child, GTK_WIDGET (item));
+    gtk_widget_show (item->child);
+}
+
+static GObject *
+gdl_dock_paned_constructor (GType                  type,
+                            guint                  n_construct_properties,
+                            GObjectConstructParam *construct_param)
+{
+    GObject *g_object;
+    
+    g_object = GDL_CALL_PARENT_WITH_DEFAULT (G_OBJECT_CLASS, 
+                                               constructor, 
+                                               (type,
+                                                n_construct_properties,
+                                                construct_param),
+                                               NULL);
+    if (g_object) {
+        GdlDockItem *item = GDL_DOCK_ITEM (g_object);
+        
+        if (!item->child)
+            gdl_dock_paned_create_child (GDL_DOCK_PANED (g_object),
+                                         item->orientation);
+        /* otherwise, the orientation was set as a construction
+           parameter and the child is already created */
+    }
+    
+    return g_object;
+}
+
+static void
+gdl_dock_paned_set_property (GObject        *object,
+                             guint           prop_id,
+                             const GValue   *value,
+                             GParamSpec     *pspec)
+{
+    GdlDockItem *item = GDL_DOCK_ITEM (object);
+      
+    switch (prop_id) {
+        case PROP_POSITION:
+            if (item->child && GTK_IS_PANED (item->child))
+                gtk_paned_set_position (GTK_PANED (item->child),
+                                        g_value_get_uint (value));
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+gdl_dock_paned_get_property (GObject        *object,
+                             guint           prop_id,
+                             GValue         *value,
+                             GParamSpec     *pspec)
+{
+    GdlDockItem *item = GDL_DOCK_ITEM (object);
+      
+    switch (prop_id) {
+        case PROP_POSITION:
+            if (item->child && GTK_IS_PANED (item->child))
+                g_value_set_uint (value,
+                                  gtk_paned_get_position (GTK_PANED (item->child)));
+            else
+                g_value_set_uint (value, 0);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+gdl_dock_paned_destroy (GtkObject *object)
+{
+    GdlDockItem *item = GDL_DOCK_ITEM (object);
+
+    /* we need to call the virtual first, since in GdlDockDestroy our
+       children dock objects are detached */
+    GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
+
+    /* after that we can remove the GtkNotebook */
+    if (item->child) {
+        gtk_widget_unparent (item->child);
+        item->child = NULL;
+    };
+}
+
+static void
+gdl_dock_paned_add (GtkContainer *container,
+                    GtkWidget    *widget)
+{
+    GdlDockItem     *item;
+    GtkPaned        *paned;
+    GdlDockPlacement pos = GDL_DOCK_NONE;
+    
+    g_return_if_fail (container != NULL && widget != NULL);
+    g_return_if_fail (GDL_IS_DOCK_PANED (container));
+    g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
+
+    item = GDL_DOCK_ITEM (container);
+    g_return_if_fail (item->child != NULL);
+    paned = GTK_PANED (item->child);
+    g_return_if_fail (!paned->child1 || !paned->child2);
+
+    if (!paned->child1)
+        pos = item->orientation == GTK_ORIENTATION_HORIZONTAL ?
+            GDL_DOCK_LEFT : GDL_DOCK_TOP;
+    else if (!paned->child2)
+        pos = item->orientation == GTK_ORIENTATION_HORIZONTAL ?
+            GDL_DOCK_RIGHT : GDL_DOCK_BOTTOM;
+
+    if (pos != GDL_DOCK_NONE)
+        gdl_dock_object_dock (GDL_DOCK_OBJECT (container),
+                              GDL_DOCK_OBJECT (widget),
+                              pos, NULL);
+}
+
+static void
+gdl_dock_paned_forall (GtkContainer *container,
+                       gboolean      include_internals,
+                       GtkCallback   callback,
+                       gpointer      callback_data)
+{
+    GdlDockItem *item;
+
+    g_return_if_fail (container != NULL);
+    g_return_if_fail (GDL_IS_DOCK_PANED (container));
+    g_return_if_fail (callback != NULL);
+
+    if (include_internals) {
+        /* use GdlDockItem's forall */
+        GDL_CALL_PARENT (GTK_CONTAINER_CLASS, forall, 
+                           (container, include_internals, callback, callback_data));
+    }
+    else {
+        item = GDL_DOCK_ITEM (container);
+        if (item->child)
+            gtk_container_foreach (GTK_CONTAINER (item->child), callback, callback_data);
+    }
+}
+
+static GType
+gdl_dock_paned_child_type (GtkContainer *container)
+{
+    GdlDockItem *item = GDL_DOCK_ITEM (container);
+
+    if (gtk_container_child_type (GTK_CONTAINER (item->child)) == G_TYPE_NONE)
+        return G_TYPE_NONE;
+    else
+        return GDL_TYPE_DOCK_ITEM;
+}
+
+static void
+gdl_dock_paned_request_foreach (GdlDockObject *object,
+                                gpointer       user_data)
+{
+    struct {
+        gint            x, y;
+        GdlDockRequest *request;
+        gboolean        may_dock;
+    } *data = user_data;
+    
+    GdlDockRequest my_request;
+    gboolean       may_dock;
+    
+    my_request = *data->request;
+    may_dock = gdl_dock_object_dock_request (object, data->x, data->y, &my_request);
+    if (may_dock) {
+        data->may_dock = TRUE;
+        *data->request = my_request;
+    }
+}
+
+static gboolean
+gdl_dock_paned_dock_request (GdlDockObject  *object, 
+                             gint            x,
+                             gint            y, 
+                             GdlDockRequest *request)
+{
+    GdlDockItem        *item;
+    guint               bw;
+    gint                rel_x, rel_y;
+    GtkAllocation      *alloc;
+    gboolean            may_dock = FALSE;
+    GdlDockRequest      my_request;
+
+    g_return_val_if_fail (GDL_IS_DOCK_ITEM (object), FALSE);
+
+    /* we get (x,y) in our allocation coordinates system */
+    
+    item = GDL_DOCK_ITEM (object);
+    
+    /* Get item's allocation. */
+    alloc = &(GTK_WIDGET (object)->allocation);
+    bw = GTK_CONTAINER (object)->border_width;
+
+    /* Get coordinates relative to our window. */
+    rel_x = x - alloc->x;
+    rel_y = y - alloc->y;
+
+    if (request)
+        my_request = *request;
+        
+    /* Check if coordinates are inside the widget. */
+    if (rel_x > 0 && rel_x < alloc->width &&
+        rel_y > 0 && rel_y < alloc->height) {
+        GtkRequisition my, other;
+        gint divider = -1;
+        
+        gdl_dock_item_preferred_size (GDL_DOCK_ITEM (my_request.applicant), &other);
+        gdl_dock_item_preferred_size (GDL_DOCK_ITEM (object), &my);
+
+        /* It's inside our area. */
+        may_dock = TRUE;
+
+       /* Set docking indicator rectangle to the widget size. */
+        my_request.rect.x = bw;
+        my_request.rect.y = bw;
+        my_request.rect.width = alloc->width - 2*bw;
+        my_request.rect.height = alloc->height - 2*bw;
+
+        my_request.target = object;
+
+        /* See if it's in the border_width band. */
+        if (rel_x < bw) {
+            my_request.position = GDL_DOCK_LEFT;
+            my_request.rect.width *= SPLIT_RATIO;
+            divider = other.width;
+        } else if (rel_x > alloc->width - bw) {
+            my_request.position = GDL_DOCK_RIGHT;
+            my_request.rect.x += my_request.rect.width * (1 - SPLIT_RATIO);
+            my_request.rect.width *= SPLIT_RATIO;
+            divider = MAX (0, my.width - other.width);
+        } else if (rel_y < bw) {
+            my_request.position = GDL_DOCK_TOP;
+            my_request.rect.height *= SPLIT_RATIO;
+            divider = other.height;
+        } else if (rel_y > alloc->height - bw) {
+            my_request.position = GDL_DOCK_BOTTOM;
+            my_request.rect.y += my_request.rect.height * (1 - SPLIT_RATIO);
+            my_request.rect.height *= SPLIT_RATIO;
+            divider = MAX (0, my.height - other.height);
+            
+        } else { /* Otherwise try our children. */
+            struct {
+                gint            x, y;
+                GdlDockRequest *request;
+                gboolean        may_dock;
+            } data;
+
+            /* give them coordinates in their allocation system... the
+               GtkPaned has no window, so our children allocation
+               coordinates are our window coordinates */
+            data.x = rel_x;
+            data.y = rel_y;
+            data.request = &my_request;
+            data.may_dock = FALSE;
+            
+            gtk_container_foreach (GTK_CONTAINER (object),
+                                   (GtkCallback) gdl_dock_paned_request_foreach,
+                                   &data);
+
+            may_dock = data.may_dock;
+            if (!may_dock) {
+                /* the pointer is on the handle, so snap to top/bottom
+                   or left/right */
+                may_dock = TRUE;
+                if (item->orientation == GTK_ORIENTATION_HORIZONTAL) {
+                    if (rel_y < alloc->height / 2) {
+                        my_request.position = GDL_DOCK_TOP;
+                        my_request.rect.height *= SPLIT_RATIO;
+                        divider = other.height;
+                    } else {
+                        my_request.position = GDL_DOCK_BOTTOM;
+                        my_request.rect.y += my_request.rect.height * (1 - SPLIT_RATIO);
+                        my_request.rect.height *= SPLIT_RATIO;
+                        divider = MAX (0, my.height - other.height);
+                    }
+                } else {
+                    if (rel_x < alloc->width / 2) {
+                        my_request.position = GDL_DOCK_LEFT;
+                        my_request.rect.width *= SPLIT_RATIO;
+                        divider = other.width;
+                    } else {
+                        my_request.position = GDL_DOCK_RIGHT;
+                        my_request.rect.x += my_request.rect.width * (1 - SPLIT_RATIO);
+                        my_request.rect.width *= SPLIT_RATIO;
+                        divider = MAX (0, my.width - other.width);
+                    }
+                }
+            }
+        }
+
+        if (divider >= 0 && my_request.position != GDL_DOCK_CENTER) {
+            if (G_IS_VALUE (&my_request.extra))
+                g_value_unset (&my_request.extra);
+            g_value_init (&my_request.extra, G_TYPE_UINT);
+            g_value_set_uint (&my_request.extra, (guint) divider);
+        }
+        
+        if (may_dock) {
+            /* adjust returned coordinates so they are relative to
+               our allocation */
+            my_request.rect.x += alloc->x;
+            my_request.rect.y += alloc->y;
+        }
+    }
+
+    if (may_dock && request)
+        *request = my_request;
+    
+    return may_dock;
+}
+
+static void
+gdl_dock_paned_dock (GdlDockObject    *object,
+                     GdlDockObject    *requestor,
+                     GdlDockPlacement  position,
+                     GValue           *other_data)
+{
+    GtkPaned *paned;
+    gboolean  done = FALSE;
+    gboolean  hresize = FALSE;
+    gboolean  wresize = FALSE;
+    gint      temp = 0;
+    
+    g_return_if_fail (GDL_IS_DOCK_PANED (object));
+    g_return_if_fail (GDL_DOCK_ITEM (object)->child != NULL);
+
+    paned = GTK_PANED (GDL_DOCK_ITEM (object)->child);
+
+    if (GDL_IS_DOCK_ITEM (requestor)) {
+        g_object_get (G_OBJECT (requestor), "preferred_height", &temp, NULL);
+        if (temp == -2)
+            hresize = TRUE;
+        temp = 0;
+        g_object_get (G_OBJECT (requestor), "preferred_width", &temp, NULL);
+        if (temp == -2)
+            wresize = TRUE;
+    }
+
+    /* see if we can dock the item in our paned */
+    switch (GDL_DOCK_ITEM (object)->orientation) {
+        case GTK_ORIENTATION_HORIZONTAL:
+            if (!paned->child1 && position == GDL_DOCK_LEFT) {
+                gtk_paned_pack1 (paned, GTK_WIDGET (requestor), FALSE, FALSE);
+                done = TRUE;
+            } else if (!paned->child2 && position == GDL_DOCK_RIGHT) {
+                gtk_paned_pack2 (paned, GTK_WIDGET (requestor), TRUE, FALSE);
+                done = TRUE;
+            }
+            break;
+        case GTK_ORIENTATION_VERTICAL:
+            if (!paned->child1 && position == GDL_DOCK_TOP) {
+                gtk_paned_pack1 (paned, GTK_WIDGET (requestor), hresize, FALSE);
+                done = TRUE;
+            } else if (!paned->child2 && position == GDL_DOCK_BOTTOM) {
+                gtk_paned_pack2 (paned, GTK_WIDGET (requestor), hresize, FALSE);
+                done = TRUE;
+            }
+            break;
+        default:
+            break;
+    }
+
+    if (!done) {
+        /* this will create another paned and reparent us there */
+        GDL_CALL_PARENT (GDL_DOCK_OBJECT_CLASS, dock, (object, requestor, position,
+                                                         other_data));
+    }
+    else {
+        gdl_dock_item_show_grip (GDL_DOCK_ITEM (requestor));
+        GDL_DOCK_OBJECT_SET_FLAGS (requestor, GDL_DOCK_ATTACHED);
+    }
+}
+
+static void
+gdl_dock_paned_set_orientation (GdlDockItem    *item,
+                                GtkOrientation  orientation)
+{
+    GtkPaned    *old_paned = NULL, *new_paned;
+    GtkWidget   *child1, *child2;
+    
+    g_return_if_fail (GDL_IS_DOCK_PANED (item));
+
+    if (item->child) {
+        old_paned = GTK_PANED (item->child);
+        g_object_ref (old_paned);
+        gtk_widget_unparent (GTK_WIDGET (old_paned));
+        item->child = NULL;
+    }
+    
+    gdl_dock_paned_create_child (GDL_DOCK_PANED (item), orientation);
+    
+    if (old_paned) {
+        new_paned = GTK_PANED (item->child);
+        child1 = old_paned->child1;
+        child2 = old_paned->child2;
+    
+        if (child1) {
+            g_object_ref (child1);
+            gtk_container_remove (GTK_CONTAINER (old_paned), child1);
+            gtk_paned_pack1 (new_paned, child1, TRUE, FALSE);
+            g_object_unref (child1);
+        }
+        if (child2) {
+            g_object_ref (child2);
+            gtk_container_remove (GTK_CONTAINER (old_paned), child2);
+            gtk_paned_pack1 (new_paned, child2, TRUE, FALSE);
+            g_object_unref (child2);
+        }
+    }
+    
+    GDL_CALL_PARENT (GDL_DOCK_ITEM_CLASS, set_orientation, (item, orientation));
+}
+
+static gboolean 
+gdl_dock_paned_child_placement (GdlDockObject    *object,
+                                GdlDockObject    *child,
+                                GdlDockPlacement *placement)
+{
+    GdlDockItem      *item = GDL_DOCK_ITEM (object);
+    GtkPaned         *paned;
+    GdlDockPlacement  pos = GDL_DOCK_NONE;
+    
+    if (item->child) {
+        paned = GTK_PANED (item->child);
+        if (GTK_WIDGET (child) == paned->child1)
+            pos = item->orientation == GTK_ORIENTATION_HORIZONTAL ?
+                GDL_DOCK_LEFT : GDL_DOCK_TOP;
+        else if (GTK_WIDGET (child) == paned->child2)
+            pos = item->orientation == GTK_ORIENTATION_HORIZONTAL ?
+                GDL_DOCK_RIGHT : GDL_DOCK_BOTTOM;
+    }
+
+    if (pos != GDL_DOCK_NONE) {
+        if (placement)
+            *placement = pos;
+        return TRUE;
+    }
+    else
+        return FALSE;
+}
+
+
+/* ----- Public interface ----- */
+
+GtkWidget *
+gdl_dock_paned_new (GtkOrientation orientation)
+{
+    GdlDockPaned *paned;
+
+    paned = GDL_DOCK_PANED (g_object_new (GDL_TYPE_DOCK_PANED,
+                                          "orientation", orientation, NULL));
+    GDL_DOCK_OBJECT_UNSET_FLAGS (paned, GDL_DOCK_AUTOMATIC);
+    
+    return GTK_WIDGET (paned);
+}
diff --git a/src/libgdl/gdl-dock-paned.h b/src/libgdl/gdl-dock-paned.h
new file mode 100644 (file)
index 0000000..41107a5
--- /dev/null
@@ -0,0 +1,64 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * gdl-dock-paned.h
+ *
+ * This file is part of the GNOME Devtools Libraries.
+ *
+ * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GDL_DOCK_PANED_H__
+#define __GDL_DOCK_PANED_H__
+
+#include "libgdl/gdl-dock-item.h"
+
+G_BEGIN_DECLS
+
+/* standard macros */
+#define GDL_TYPE_DOCK_PANED                  (gdl_dock_paned_get_type ())
+#define GDL_DOCK_PANED(obj)                  (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_PANED, GdlDockPaned))
+#define GDL_DOCK_PANED_CLASS(klass)          (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_PANED, GdlDockPanedClass))
+#define GDL_IS_DOCK_PANED(obj)               (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_PANED))
+#define GDL_IS_DOCK_PANED_CLASS(klass)       (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_PANED))
+#define GDL_DOCK_PANED_GET_CLASS(obj)        (GTK_CHECK_GET_CLASS ((obj), GDL_TYE_DOCK_PANED, GdlDockPanedClass))
+
+/* data types & structures */
+typedef struct _GdlDockPaned      GdlDockPaned;
+typedef struct _GdlDockPanedClass GdlDockPanedClass;
+
+struct _GdlDockPaned {
+    GdlDockItem  dock_item;
+
+    gboolean     position_changed;
+};
+
+struct _GdlDockPanedClass {
+    GdlDockItemClass parent_class;
+};
+
+
+/* public interface */
+GType      gdl_dock_paned_get_type        (void);
+
+GtkWidget *gdl_dock_paned_new             (GtkOrientation orientation);
+
+
+G_END_DECLS
+
+#endif /* __GDL_DOCK_PANED_H__ */
+
diff --git a/src/libgdl/gdl-dock-placeholder.c b/src/libgdl/gdl-dock-placeholder.c
new file mode 100644 (file)
index 0000000..df068d0
--- /dev/null
@@ -0,0 +1,834 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 
+ *
+ * gdl-dock-placeholder.c - Placeholders for docking items
+ *
+ * This file is part of the GNOME Devtools Libraries.
+ *
+ * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gdl-i18n.h"
+
+#include "gdl-tools.h"
+#include "gdl-dock-placeholder.h"
+#include "gdl-dock-item.h"
+#include "gdl-dock-master.h"
+#include "libgdltypebuiltins.h"
+
+
+#undef PLACEHOLDER_DEBUG
+
+/* ----- Private prototypes ----- */
+
+static void     gdl_dock_placeholder_class_init     (GdlDockPlaceholderClass *klass);
+static void     gdl_dock_placeholder_instance_init  (GdlDockPlaceholder      *ph);
+
+static void     gdl_dock_placeholder_set_property   (GObject                 *g_object,
+                                                     guint                    prop_id,
+                                                     const GValue            *value,
+                                                     GParamSpec              *pspec);
+static void     gdl_dock_placeholder_get_property   (GObject                 *g_object,
+                                                     guint                    prop_id,
+                                                     GValue                  *value,
+                                                     GParamSpec              *pspec);
+
+static void     gdl_dock_placeholder_destroy        (GtkObject               *object);
+
+static void     gdl_dock_placeholder_add            (GtkContainer            *container,
+                                                     GtkWidget               *widget);
+
+static void     gdl_dock_placeholder_detach         (GdlDockObject           *object,
+                                                     gboolean                 recursive);
+static void     gdl_dock_placeholder_reduce         (GdlDockObject           *object);
+static void     gdl_dock_placeholder_dock           (GdlDockObject           *object,
+                                                     GdlDockObject           *requestor,
+                                                     GdlDockPlacement         position,
+                                                     GValue                  *other_data);
+
+static void     gdl_dock_placeholder_weak_notify    (gpointer                 data,
+                                                     GObject                 *old_object);
+
+static void     disconnect_host                     (GdlDockPlaceholder      *ph);
+static void     connect_host                        (GdlDockPlaceholder      *ph,
+                                                     GdlDockObject           *new_host);
+static void     do_excursion                        (GdlDockPlaceholder      *ph);
+
+static void     gdl_dock_placeholder_present        (GdlDockObject           *object,
+                                                     GdlDockObject           *child);
+
+static void     detach_cb                           (GdlDockObject           *object,
+                                                     gboolean                 recursive,
+                                                     gpointer                 user_data);
+
+/* ----- Private variables and data structures ----- */
+
+enum {
+    PROP_0,
+    PROP_STICKY,
+    PROP_HOST,
+    PROP_NEXT_PLACEMENT,
+    PROP_WIDTH,
+    PROP_HEIGHT,
+       PROP_FLOATING,
+       PROP_FLOAT_X,
+       PROP_FLOAT_Y
+};
+
+struct _GdlDockPlaceholderPrivate {
+    /* current object this placeholder is pinned to */
+    GdlDockObject    *host;
+    gboolean          sticky;
+    
+    /* when the placeholder is moved up the hierarchy, this stack
+       keeps track of the necessary dock positions needed to get the
+       placeholder to the original position */
+    GSList           *placement_stack;
+
+    /* Width and height of the attachments */
+    gint              width;
+    gint              height;
+    
+    /* connected signal handlers */
+    guint             host_detach_handler;
+    guint             host_dock_handler;
+       
+       /* Window Coordinates if Dock was floating */
+       gboolean        floating;
+       gint              floatx;
+       gint              floaty;
+};
+
+
+/* ----- Private interface ----- */
+
+GDL_CLASS_BOILERPLATE (GdlDockPlaceholder, gdl_dock_placeholder,
+                       GdlDockObject, GDL_TYPE_DOCK_OBJECT);
+
+static void 
+gdl_dock_placeholder_class_init (GdlDockPlaceholderClass *klass)
+{
+    GObjectClass       *g_object_class;
+    GtkObjectClass     *gtk_object_class;
+    GtkContainerClass  *container_class;
+    GdlDockObjectClass *object_class;
+    
+    g_object_class = G_OBJECT_CLASS (klass);
+    gtk_object_class = GTK_OBJECT_CLASS (klass);
+    container_class = GTK_CONTAINER_CLASS (klass);
+    object_class = GDL_DOCK_OBJECT_CLASS (klass);
+
+    g_object_class->get_property = gdl_dock_placeholder_get_property;
+    g_object_class->set_property = gdl_dock_placeholder_set_property;
+    
+    g_object_class_install_property (
+        g_object_class, PROP_STICKY,
+        g_param_spec_boolean ("sticky", _("Sticky"),
+                                               _("Whether the placeholder will stick to its host or "
+                                       "move up the hierarchy when the host is redocked"),
+                                                       FALSE,
+                                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+    
+    g_object_class_install_property (
+       g_object_class, PROP_HOST,
+        g_param_spec_object ("host", _("Host"),
+                            _("The dock object this placeholder is attached to"),
+                            GDL_TYPE_DOCK_OBJECT,
+                            G_PARAM_READWRITE));
+    
+    /* this will return the top of the placement stack */
+    g_object_class_install_property (
+        g_object_class, PROP_NEXT_PLACEMENT,
+        g_param_spec_enum ("next-placement", _("Next placement"),
+                                               _("The position an item will be docked to our host if a "
+                                               "request is made to dock to us"),
+                               GDL_TYPE_DOCK_PLACEMENT,
+                               GDL_DOCK_CENTER,
+                               G_PARAM_READWRITE |
+                               GDL_DOCK_PARAM_EXPORT | GDL_DOCK_PARAM_AFTER));
+    
+    g_object_class_install_property (
+        g_object_class, PROP_WIDTH,
+        g_param_spec_int ("width", _("Width"),
+                               _("Width for the widget when it's attached to the placeholder"),
+                               -1, G_MAXINT, -1,
+                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+                               GDL_DOCK_PARAM_EXPORT));
+    
+    g_object_class_install_property (
+        g_object_class, PROP_HEIGHT,
+        g_param_spec_int ("height", _("Height"),
+                               _("Height for the widget when it's attached to the placeholder"),
+                               -1, G_MAXINT, -1,
+                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+                               GDL_DOCK_PARAM_EXPORT));
+       g_object_class_install_property (
+        g_object_class, PROP_FLOATING,
+               g_param_spec_boolean ("floating", _("Floating Toplevel"),
+                            _("Whether the placeholder is standing in for a "
+                            "floating toplevel dock"),
+                            FALSE,
+                            G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+       g_object_class_install_property (
+        g_object_class, PROP_FLOAT_X,
+        g_param_spec_int ("floatx", _("X-Coordinate"),
+                               _("X-Coordinate fow dock when floating"),
+                               -1, G_MAXINT, -1,
+                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                               GDL_DOCK_PARAM_EXPORT));
+       g_object_class_install_property (
+        g_object_class, PROP_FLOAT_Y,
+        g_param_spec_int ("floaty", _("Y-Coordinate"),
+                               _("Y-Coordinate fow dock when floating"),
+                               -1, G_MAXINT, -1,
+                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                               GDL_DOCK_PARAM_EXPORT));
+    
+       
+    gtk_object_class->destroy = gdl_dock_placeholder_destroy;
+    container_class->add = gdl_dock_placeholder_add;
+    
+    object_class->is_compound = FALSE;
+    object_class->detach = gdl_dock_placeholder_detach;
+    object_class->reduce = gdl_dock_placeholder_reduce;
+    object_class->dock = gdl_dock_placeholder_dock;
+    object_class->present = gdl_dock_placeholder_present;
+}
+
+static void 
+gdl_dock_placeholder_instance_init (GdlDockPlaceholder *ph)
+{
+    GTK_WIDGET_SET_FLAGS (ph, GTK_NO_WINDOW);
+    GTK_WIDGET_UNSET_FLAGS (ph, GTK_CAN_FOCUS);
+    
+    ph->_priv = g_new0 (GdlDockPlaceholderPrivate, 1);
+}
+
+static void 
+gdl_dock_placeholder_set_property (GObject      *g_object,
+                                   guint         prop_id,
+                                   const GValue *value,
+                                   GParamSpec   *pspec)
+{
+    GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (g_object);
+
+    switch (prop_id) {
+        case PROP_STICKY:
+            if (ph->_priv)
+                ph->_priv->sticky = g_value_get_boolean (value);
+            break;
+        case PROP_HOST:
+            gdl_dock_placeholder_attach (ph, g_value_get_object (value));
+            break;
+        case PROP_NEXT_PLACEMENT:
+            if (ph->_priv) {
+               ph->_priv->placement_stack =
+                               g_slist_prepend (ph->_priv->placement_stack,
+                                     GINT_TO_POINTER (g_value_get_enum (value)));
+            }
+            break;
+        case PROP_WIDTH:
+            ph->_priv->width = g_value_get_int (value);
+            break;
+        case PROP_HEIGHT:
+            ph->_priv->height = g_value_get_int (value);
+            break;
+               case PROP_FLOATING:
+                       ph->_priv->floating = g_value_get_boolean (value);
+                       break;
+               case PROP_FLOAT_X:
+                       ph->_priv->floatx = g_value_get_int (value);
+                       break;
+               case PROP_FLOAT_Y:
+                       ph->_priv->floaty = g_value_get_int (value);
+                       break;
+        default:
+                       G_OBJECT_WARN_INVALID_PROPERTY_ID (g_object, prop_id, pspec);
+                       break;
+    }
+}
+
+static void 
+gdl_dock_placeholder_get_property (GObject    *g_object,
+                                   guint       prop_id,
+                                   GValue     *value,
+                                   GParamSpec *pspec)
+{
+    GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (g_object);
+
+    switch (prop_id) {
+        case PROP_STICKY:
+            if (ph->_priv)
+                g_value_set_boolean (value, ph->_priv->sticky);
+            else
+                g_value_set_boolean (value, FALSE);
+            break;
+        case PROP_HOST:
+            if (ph->_priv)
+                g_value_set_object (value, ph->_priv->host);
+            else
+                g_value_set_object (value, NULL);
+            break;
+        case PROP_NEXT_PLACEMENT:
+            if (ph->_priv && ph->_priv->placement_stack)
+                g_value_set_enum (value, (GdlDockPlacement) ph->_priv->placement_stack->data);
+            else
+                g_value_set_enum (value, GDL_DOCK_CENTER);
+            break;
+        case PROP_WIDTH:
+            g_value_set_int (value, ph->_priv->width);
+            break;
+        case PROP_HEIGHT:
+            g_value_set_int (value, ph->_priv->height);
+            break;
+               case PROP_FLOATING:
+                       g_value_set_boolean (value, ph->_priv->floating);
+                       break;
+               case PROP_FLOAT_X:
+            g_value_set_int (value, ph->_priv->floatx);
+            break;
+        case PROP_FLOAT_Y:
+            g_value_set_int (value, ph->_priv->floaty);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (g_object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+gdl_dock_placeholder_destroy (GtkObject *object)
+{
+    GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (object);
+
+    if (ph->_priv) {
+        if (ph->_priv->host)
+            gdl_dock_placeholder_detach (GDL_DOCK_OBJECT (object), FALSE);
+        g_free (ph->_priv);
+        ph->_priv = NULL;
+    }
+
+    GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
+}
+
+static void 
+gdl_dock_placeholder_add (GtkContainer *container,
+                          GtkWidget    *widget)
+{
+    GdlDockPlaceholder *ph;
+    GdlDockPlacement    pos = GDL_DOCK_CENTER;   /* default position */
+    
+    g_return_if_fail (GDL_IS_DOCK_PLACEHOLDER (container));
+    g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
+
+    ph = GDL_DOCK_PLACEHOLDER (container);
+    if (ph->_priv->placement_stack)
+        pos = (GdlDockPlacement) ph->_priv->placement_stack->data;
+    
+    gdl_dock_object_dock (GDL_DOCK_OBJECT (ph), GDL_DOCK_OBJECT (widget),
+                          pos, NULL);
+}
+
+static void
+gdl_dock_placeholder_detach (GdlDockObject *object,
+                             gboolean       recursive)
+{
+    GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (object);
+
+    /* disconnect handlers */
+    disconnect_host (ph);
+    
+    /* free the placement stack */
+    g_slist_free (ph->_priv->placement_stack);
+    ph->_priv->placement_stack = NULL;
+
+    GDL_DOCK_OBJECT_UNSET_FLAGS (object, GDL_DOCK_ATTACHED);
+}
+
+static void 
+gdl_dock_placeholder_reduce (GdlDockObject *object)
+{
+    /* placeholders are not reduced */
+    return;
+}
+
+static void
+find_biggest_dock_item (GtkContainer *container, GtkWidget **biggest_child,
+                        gint *biggest_child_area)
+{
+    GList *children, *child;
+    
+    children = gtk_container_get_children (GTK_CONTAINER (container));
+    child = children;
+    while (child) {
+        gint area;
+        GtkWidget *child_widget;
+        
+        child_widget = GTK_WIDGET (child->data);
+        
+        if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT(child_widget))) {
+            find_biggest_dock_item (GTK_CONTAINER (child_widget),
+                                    biggest_child, biggest_child_area);
+            child = g_list_next (child);
+            continue;
+        }
+        area = child_widget->allocation.width * child_widget->allocation.height;
+        
+        if (area > *biggest_child_area) {
+            *biggest_child_area = area;
+            *biggest_child = child_widget;
+        }
+        child = g_list_next (child);
+    }
+}
+
+static void
+attempt_to_dock_on_host (GdlDockPlaceholder *ph, GdlDockObject *host,
+                         GdlDockObject *requestor, GdlDockPlacement placement,
+                         gpointer other_data)
+{
+    GdlDockObject *parent;
+    gint host_width = GTK_WIDGET (host)->allocation.width;
+    gint host_height = GTK_WIDGET (host)->allocation.height;
+    
+    if (placement != GDL_DOCK_CENTER || !GDL_IS_DOCK_PANED (host)) {
+        /* we simply act as a proxy for our host */
+        gdl_dock_object_dock (host, requestor,
+                              placement, other_data);
+    } else {
+        /* If the requested pos is center, we have to make sure that it
+         * does not colapses existing paned items. Find the larget item
+         * which is not a paned item to dock to.
+         */
+        GtkWidget *biggest_child = NULL;
+        gint biggest_child_area = 0;
+        
+        find_biggest_dock_item (GTK_CONTAINER (host), &biggest_child,
+                                &biggest_child_area);
+        
+        if (biggest_child) {
+            /* we simply act as a proxy for our host */
+            gdl_dock_object_dock (GDL_DOCK_OBJECT (biggest_child), requestor,
+                                  placement, other_data);
+        } else {
+            g_warning ("No suitable child found! Should not be here!");
+            /* we simply act as a proxy for our host */
+            gdl_dock_object_dock (GDL_DOCK_OBJECT (host), requestor,
+                                  placement, other_data);
+        }
+    }
+    
+    parent = gdl_dock_object_get_parent_object (requestor);
+    
+    /* Restore dock item's dimention */
+    switch (placement) {
+        case GDL_DOCK_LEFT:
+            if (ph->_priv->width > 0) {
+                g_object_set (G_OBJECT (parent), "position",
+                              ph->_priv->width, NULL);
+            }
+            break;
+        case GDL_DOCK_RIGHT:
+            if (ph->_priv->width > 0) {
+                gint complementary_width = host_width - ph->_priv->width;
+                
+                if (complementary_width > 0)
+                    g_object_set (G_OBJECT (parent), "position",
+                                  complementary_width, NULL);
+            }
+            break;
+        case GDL_DOCK_TOP:
+            if (ph->_priv->height > 0) {
+                g_object_set (G_OBJECT (parent), "position",
+                              ph->_priv->height, NULL);
+            }
+            break;
+        case GDL_DOCK_BOTTOM:
+            if (ph->_priv->height > 0) {
+                gint complementary_height = host_height - ph->_priv->height;
+                
+                if (complementary_height > 0)
+                    g_object_set (G_OBJECT (parent), "position",
+                                  complementary_height, NULL);
+            }
+            break;
+        default:
+            /* nothing */
+            break;
+    }
+}
+
+static void 
+gdl_dock_placeholder_dock (GdlDockObject    *object,
+                           GdlDockObject    *requestor,
+                           GdlDockPlacement  position,
+                           GValue           *other_data)
+{
+    GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (object);
+    
+    if (ph->_priv->host) {
+        attempt_to_dock_on_host (ph, ph->_priv->host, requestor,
+                                 position, other_data);
+    }
+    else {
+        GdlDockObject *toplevel;
+        
+        if (!gdl_dock_object_is_bound (GDL_DOCK_OBJECT (ph))) {
+            g_warning (_("Attempt to dock a dock object to an unbound placeholder"));
+            return;
+        }
+        
+        /* dock the item as a floating of the controller */
+        toplevel = gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (ph));
+        gdl_dock_object_dock (toplevel, requestor,
+                              GDL_DOCK_FLOATING, NULL);
+    }
+}
+
+#ifdef PLACEHOLDER_DEBUG
+static void
+print_placement_stack (GdlDockPlaceholder *ph)
+{
+    GSList *s = ph->_priv->placement_stack;
+    GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_ref (GDL_TYPE_DOCK_PLACEMENT));
+    GEnumValue *enum_value;
+    gchar *name;
+    GString *message;
+
+    message = g_string_new (NULL);
+    g_string_printf (message, "[%p] host: %p (%s), stack: ",
+                     ph, ph->_priv->host, G_OBJECT_TYPE_NAME (ph->_priv->host));
+    for (; s; s = s->next) {
+        enum_value = g_enum_get_value (enum_class, (GdlDockPlacement) s->data);
+        name = enum_value ? enum_value->value_name : NULL;
+        g_string_append_printf (message, "%s, ", name);
+    }
+    g_message ("%s", message->str);
+    
+    g_string_free (message, TRUE);
+    g_type_class_unref (enum_class);
+}
+#endif
+
+static void 
+gdl_dock_placeholder_present (GdlDockObject *object,
+                              GdlDockObject *child)
+{
+    /* do nothing */
+    return;
+}
+
+/* ----- Public interface ----- */ 
+                                                                  
+GtkWidget * 
+gdl_dock_placeholder_new (gchar            *name,
+                          GdlDockObject    *object,
+                          GdlDockPlacement  position,
+                          gboolean          sticky)
+{
+    GdlDockPlaceholder *ph;
+
+    ph = GDL_DOCK_PLACEHOLDER (g_object_new (GDL_TYPE_DOCK_PLACEHOLDER,
+                                             "name", name,
+                                             "sticky", sticky,
+                                             NULL));
+    GDL_DOCK_OBJECT_UNSET_FLAGS (ph, GDL_DOCK_AUTOMATIC);
+
+    if (object) {
+        gdl_dock_placeholder_attach (ph, object);
+        if (position == GDL_DOCK_NONE)
+            position = GDL_DOCK_CENTER;
+        g_object_set (G_OBJECT (ph), "next-placement", position, NULL);
+        if (GDL_IS_DOCK (object)) {
+            /* the top placement will be consumed by the toplevel
+               dock, so add a dummy placement */
+            g_object_set (G_OBJECT (ph), "next-placement", GDL_DOCK_CENTER, NULL);
+        }
+        /* try a recursion */
+        do_excursion (ph);
+    }
+    
+    return GTK_WIDGET (ph);
+}
+
+static void 
+gdl_dock_placeholder_weak_notify (gpointer data,
+                                  GObject *old_object)
+{
+    GdlDockPlaceholder *ph;
+    
+    g_return_if_fail (data != NULL && GDL_IS_DOCK_PLACEHOLDER (data));
+
+    ph = GDL_DOCK_PLACEHOLDER (data);
+    
+#ifdef PLACEHOLDER_DEBUG
+    g_message ("The placeholder just lost its host, ph = %p", ph);
+#endif
+    
+    /* we shouldn't get here, so perform an emergency detach. instead
+       we should have gotten a detach signal from our host */
+    ph->_priv->host = NULL;
+    
+    /* We didn't get a detach signal from the host. Detach from the 
+    supposedly dead host (consequently attaching to the controller) */
+    
+    detach_cb (NULL, TRUE, data);
+#if 0
+    /* free the placement stack */
+    g_slist_free (ph->_priv->placement_stack);
+    ph->_priv->placement_stack = NULL;
+    GDL_DOCK_OBJECT_UNSET_FLAGS (ph, GDL_DOCK_ATTACHED);
+#endif
+}
+
+static void
+detach_cb (GdlDockObject *object,
+           gboolean       recursive,
+           gpointer       user_data)
+{
+    GdlDockPlaceholder *ph;
+    GdlDockObject      *new_host, *obj;
+
+    g_return_if_fail (user_data != NULL && GDL_IS_DOCK_PLACEHOLDER (user_data));
+    
+    /* we go up in the hierarchy and we store the hinted placement in
+     * the placement stack so we can rebuild the docking layout later
+     * when we get the host's dock signal.  */
+
+    ph = GDL_DOCK_PLACEHOLDER (user_data);
+    obj = ph->_priv->host;
+    if (obj != object) {
+        g_warning (_("Got a detach signal from an object (%p) who is not "
+                     "our host %p"), object, ph->_priv->host);
+        return;
+    }
+    
+    /* skip sticky objects */
+    if (ph->_priv->sticky)
+        return;
+    
+    if (obj)
+        /* go up in the hierarchy */
+        new_host = gdl_dock_object_get_parent_object (obj);
+    else
+        /* Detaching from the dead host */
+        new_host = NULL;
+    
+    while (new_host) {
+        GdlDockPlacement pos = GDL_DOCK_NONE;
+        
+        /* get placement hint from the new host */
+        if (gdl_dock_object_child_placement (new_host, obj, &pos)) {
+            ph->_priv->placement_stack = g_slist_prepend (
+                ph->_priv->placement_stack, (gpointer) pos);
+        }
+        else {
+            g_warning (_("Something weird happened while getting the child "
+                         "placement for %p from parent %p"), obj, new_host);
+        }
+
+        if (!GDL_DOCK_OBJECT_IN_DETACH (new_host))
+            /* we found a "stable" dock object */
+            break;
+        
+        obj = new_host;
+        new_host = gdl_dock_object_get_parent_object (obj);
+    }
+
+    /* disconnect host */
+    disconnect_host (ph);
+
+    if (!new_host) {
+#ifdef PLACEHOLDER_DEBUG
+        g_message ("Detaching from the toplevel. Assignaing to controller");
+#endif
+        /* the toplevel was detached: we attach ourselves to the
+           controller with an initial placement of floating */
+        new_host = gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (ph));
+        
+        /*
+        ph->_priv->placement_stack = g_slist_prepend (
+        ph->_priv->placement_stack, (gpointer) GDL_DOCK_FLOATING);
+        */
+    }
+    if (new_host)
+        connect_host (ph, new_host);
+
+#ifdef PLACEHOLDER_DEBUG
+    print_placement_stack (ph);
+#endif
+}
+
+/**
+ * do_excursion:
+ * @ph: placeholder object
+ *
+ * Tries to shrink the placement stack by examining the host's
+ * children and see if any of them matches the placement which is at
+ * the top of the stack.  If this is the case, it tries again with the
+ * new host.
+ **/
+static void
+do_excursion (GdlDockPlaceholder *ph)
+{
+    if (ph->_priv->host &&
+        !ph->_priv->sticky &&
+        ph->_priv->placement_stack &&
+        gdl_dock_object_is_compound (ph->_priv->host)) {
+
+        GdlDockPlacement pos, stack_pos =
+            (GdlDockPlacement) ph->_priv->placement_stack->data;
+        GList           *children, *l;
+        GdlDockObject   *host = ph->_priv->host;
+        
+        children = gtk_container_get_children (GTK_CONTAINER (host));
+        for (l = children; l; l = l->next) {
+            pos = stack_pos;
+            gdl_dock_object_child_placement (GDL_DOCK_OBJECT (host),
+                                             GDL_DOCK_OBJECT (l->data),
+                                             &pos);
+            if (pos == stack_pos) {
+                /* remove the stack position */
+                ph->_priv->placement_stack =
+                    g_slist_remove_link (ph->_priv->placement_stack,
+                                         ph->_priv->placement_stack);
+                
+                /* connect to the new host */
+                disconnect_host (ph);
+                connect_host (ph, GDL_DOCK_OBJECT (l->data));
+
+                /* recurse... */
+                if (!GDL_DOCK_OBJECT_IN_REFLOW (l->data))
+                    do_excursion (ph);
+                
+                break;
+            }
+        }
+        g_list_free (children);
+    }
+}
+
+static void 
+dock_cb (GdlDockObject    *object,
+         GdlDockObject    *requestor,
+         GdlDockPlacement  position,
+         GValue           *other_data,
+         gpointer          user_data)
+{
+    GdlDockPlacement    pos = GDL_DOCK_NONE;
+    GdlDockPlaceholder *ph;
+    
+    g_return_if_fail (user_data != NULL && GDL_IS_DOCK_PLACEHOLDER (user_data));
+    ph = GDL_DOCK_PLACEHOLDER (user_data);
+    g_return_if_fail (ph->_priv->host == object);
+    
+    /* see if the given position is compatible for the stack's top
+       element */
+    if (!ph->_priv->sticky && ph->_priv->placement_stack) {
+        pos = (GdlDockPlacement) ph->_priv->placement_stack->data;
+        if (gdl_dock_object_child_placement (object, requestor, &pos)) {
+            if (pos == (GdlDockPlacement) ph->_priv->placement_stack->data) {
+                /* the position is compatible: excurse down */
+                do_excursion (ph);
+            }
+        }
+    }
+#ifdef PLACEHOLDER_DEBUG
+    print_placement_stack (ph);
+#endif
+}
+
+static void
+disconnect_host (GdlDockPlaceholder *ph)
+{
+    if (!ph->_priv->host)
+        return;
+    
+    if (ph->_priv->host_detach_handler)
+        g_signal_handler_disconnect (ph->_priv->host, ph->_priv->host_detach_handler);
+    if (ph->_priv->host_dock_handler)
+        g_signal_handler_disconnect (ph->_priv->host, ph->_priv->host_dock_handler);
+    ph->_priv->host_detach_handler = 0;
+    ph->_priv->host_dock_handler = 0;
+
+    /* remove weak ref to object */
+    g_object_weak_unref (G_OBJECT (ph->_priv->host),
+                         gdl_dock_placeholder_weak_notify, ph);
+    ph->_priv->host = NULL;
+    
+#ifdef PLACEHOLDER_DEBUG
+    g_message ("Host just disconnected!, ph = %p", ph);
+#endif
+}
+
+static void
+connect_host (GdlDockPlaceholder *ph,
+              GdlDockObject      *new_host)
+{
+    if (ph->_priv->host)
+        disconnect_host (ph);
+    
+    ph->_priv->host = new_host;
+    g_object_weak_ref (G_OBJECT (ph->_priv->host),
+                       gdl_dock_placeholder_weak_notify, ph);
+
+    ph->_priv->host_detach_handler =
+        g_signal_connect (ph->_priv->host,
+                          "detach",
+                          (GCallback) detach_cb,
+                          (gpointer) ph);
+    
+    ph->_priv->host_dock_handler =
+        g_signal_connect (ph->_priv->host,
+                          "dock",
+                          (GCallback) dock_cb,
+                          (gpointer) ph);
+    
+#ifdef PLACEHOLDER_DEBUG
+    g_message ("Host just connected!, ph = %p", ph);
+#endif
+}
+
+void
+gdl_dock_placeholder_attach (GdlDockPlaceholder *ph,
+                             GdlDockObject      *object)
+{
+    g_return_if_fail (ph != NULL && GDL_IS_DOCK_PLACEHOLDER (ph));
+    g_return_if_fail (ph->_priv != NULL);
+    g_return_if_fail (object != NULL);
+    
+    /* object binding */
+    if (!gdl_dock_object_is_bound (GDL_DOCK_OBJECT (ph)))
+        gdl_dock_object_bind (GDL_DOCK_OBJECT (ph), object->master);
+
+    g_return_if_fail (GDL_DOCK_OBJECT (ph)->master == object->master);
+        
+    gdl_dock_object_freeze (GDL_DOCK_OBJECT (ph));
+    
+    /* detach from previous host first */
+    if (ph->_priv->host)
+        gdl_dock_object_detach (GDL_DOCK_OBJECT (ph), FALSE);
+
+    connect_host (ph, object);
+    
+    GDL_DOCK_OBJECT_SET_FLAGS (ph, GDL_DOCK_ATTACHED);
+    
+    gdl_dock_object_thaw (GDL_DOCK_OBJECT (ph));
+}
diff --git a/src/libgdl/gdl-dock-placeholder.h b/src/libgdl/gdl-dock-placeholder.h
new file mode 100644 (file)
index 0000000..4a7035b
--- /dev/null
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 
+ *
+ * gdl-dock-placeholder.h - Placeholders for docking items
+ *
+ * This file is part of the GNOME Devtools Libraries.
+ *
+ * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GDL_DOCK_PLACEHOLDER_H__
+#define __GDL_DOCK_PLACEHOLDER_H__
+
+#include "libgdl/gdl-dock-object.h"
+
+G_BEGIN_DECLS
+
+/* standard macros */
+#define GDL_TYPE_DOCK_PLACEHOLDER             (gdl_dock_placeholder_get_type ())
+#define GDL_DOCK_PLACEHOLDER(obj)             (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_PLACEHOLDER, GdlDockPlaceholder))
+#define GDL_DOCK_PLACEHOLDER_CLASS(klass)     (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_PLACEHOLDER, GdlDockPlaceholderClass))
+#define GDL_IS_DOCK_PLACEHOLDER(obj)          (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_PLACEHOLDER))
+#define GDL_IS_DOCK_PLACEHOLDER_CLASS(klass)  (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_PLACEHOLDER))
+#define GDL_DOCK_PLACEHOLDER_GET_CLASS(obj)   (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK_PLACEHOLDER, GdlDockPlaceholderClass))
+
+/* data types & structures */
+typedef struct _GdlDockPlaceholder        GdlDockPlaceholder;
+typedef struct _GdlDockPlaceholderClass   GdlDockPlaceholderClass;
+typedef struct _GdlDockPlaceholderPrivate GdlDockPlaceholderPrivate;
+
+struct _GdlDockPlaceholder {
+    GdlDockObject              object;
+
+    GdlDockPlaceholderPrivate *_priv;
+};
+
+struct _GdlDockPlaceholderClass {
+    GdlDockObjectClass parent_class;
+};
+
+/* public interface */
+
+GType       gdl_dock_placeholder_get_type (void);
+
+GtkWidget  *gdl_dock_placeholder_new      (gchar              *name,
+                                           GdlDockObject      *object,
+                                           GdlDockPlacement    position,
+                                           gboolean            sticky);
+
+void        gdl_dock_placeholder_attach   (GdlDockPlaceholder *ph,
+                                           GdlDockObject      *object);
+
+
+G_END_DECLS
+
+#endif /* __GDL_DOCK_PLACEHOLDER_H__ */
diff --git a/src/libgdl/gdl-dock-tablabel.c b/src/libgdl/gdl-dock-tablabel.c
new file mode 100644 (file)
index 0000000..bd75589
--- /dev/null
@@ -0,0 +1,621 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * gdl-dock-tablabel.c
+ *
+ * This file is part of the GNOME Devtools Libraries.
+ *
+ * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gdl-i18n.h"
+#include <gtk/gtk.h>
+
+#include "gdl-dock-tablabel.h"
+#include "gdl-tools.h"
+#include "gdl-dock-item.h"
+#include "libgdlmarshal.h"
+
+
+/* ----- Private prototypes ----- */
+
+static void  gdl_dock_tablabel_class_init    (GdlDockTablabelClass *klass);
+static void  gdl_dock_tablabel_instance_init (GdlDockTablabel      *tablabel);
+
+static void  gdl_dock_tablabel_set_property  (GObject              *object,
+                                              guint                 prop_id,
+                                              const GValue         *value,
+                                              GParamSpec           *pspec);
+static void  gdl_dock_tablabel_get_property  (GObject              *object,
+                                              guint                 prop_id,
+                                              GValue               *value,
+                                              GParamSpec           *pspec);
+
+static void  gdl_dock_tablabel_item_notify   (GObject            *master,
+                                              GParamSpec         *pspec,
+                                              gpointer            data);
+
+static void  gdl_dock_tablabel_size_request  (GtkWidget          *widget,
+                                              GtkRequisition     *requisition);
+static void  gdl_dock_tablabel_size_allocate (GtkWidget          *widget,
+                                              GtkAllocation      *allocation);
+                                              
+static void  gdl_dock_tablabel_paint         (GtkWidget      *widget,
+                                              GdkEventExpose *event);
+static gint  gdl_dock_tablabel_expose        (GtkWidget      *widget,
+                                              GdkEventExpose *event);
+
+static gboolean gdl_dock_tablabel_button_event  (GtkWidget      *widget,
+                                                 GdkEventButton *event);
+static gboolean gdl_dock_tablabel_motion_event  (GtkWidget      *widget,
+                                                 GdkEventMotion *event);
+
+static void  gdl_dock_tablabel_realize (GtkWidget *widget);
+static void  gdl_dock_tablabel_unrealize (GtkWidget *widget);
+static void  gdl_dock_tablabel_map (GtkWidget *widget);
+static void  gdl_dock_tablabel_unmap (GtkWidget *widget);
+
+/* ----- Private data types and variables ----- */
+
+#define DEFAULT_DRAG_HANDLE_SIZE 10
+#define HANDLE_RATIO 1.0
+
+enum {
+    BUTTON_PRESSED_HANDLE,
+    LAST_SIGNAL
+};
+
+enum {
+    PROP_0,
+    PROP_ITEM
+};
+
+
+static guint dock_tablabel_signals [LAST_SIGNAL] = { 0 };
+
+
+/* ----- Private interface ----- */
+
+GDL_CLASS_BOILERPLATE (GdlDockTablabel, gdl_dock_tablabel,
+                       GtkBin, GTK_TYPE_BIN);
+
+static void
+gdl_dock_tablabel_class_init (GdlDockTablabelClass *klass)
+{
+    GObjectClass      *g_object_class;
+    GtkObjectClass    *object_class;
+    GtkWidgetClass    *widget_class;
+    GtkContainerClass *container_class;
+
+    g_object_class = G_OBJECT_CLASS (klass);
+    object_class = GTK_OBJECT_CLASS (klass);
+    widget_class = GTK_WIDGET_CLASS (klass);
+    container_class = GTK_CONTAINER_CLASS (klass);
+    
+    g_object_class->set_property = gdl_dock_tablabel_set_property;
+    g_object_class->get_property = gdl_dock_tablabel_get_property;
+
+    widget_class->size_request = gdl_dock_tablabel_size_request;
+    widget_class->size_allocate = gdl_dock_tablabel_size_allocate;
+    widget_class->expose_event = gdl_dock_tablabel_expose;
+    widget_class->button_press_event = gdl_dock_tablabel_button_event;
+    widget_class->button_release_event = gdl_dock_tablabel_button_event;
+    widget_class->motion_notify_event = gdl_dock_tablabel_motion_event;
+    widget_class->realize = gdl_dock_tablabel_realize;
+    widget_class->unrealize = gdl_dock_tablabel_unrealize;
+    widget_class->map = gdl_dock_tablabel_map;
+    widget_class->unmap = gdl_dock_tablabel_unmap;
+
+    g_object_class_install_property (
+        g_object_class, PROP_ITEM,
+        g_param_spec_object ("item", _("Controlling dock item"),
+                             _("Dockitem which 'owns' this tablabel"),
+                             GDL_TYPE_DOCK_ITEM,
+                             G_PARAM_READWRITE));
+
+    dock_tablabel_signals [BUTTON_PRESSED_HANDLE] =
+        g_signal_new ("button_pressed_handle",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (GdlDockTablabelClass, 
+                                       button_pressed_handle),
+                      NULL, NULL,
+                      gdl_marshal_VOID__BOXED,
+                      G_TYPE_NONE,
+                      1,
+                      GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+    klass->button_pressed_handle = NULL;
+}
+
+static void
+gdl_dock_tablabel_instance_init (GdlDockTablabel *tablabel)
+{
+    GtkWidget *widget;
+    GtkWidget *label_widget;
+
+    widget = GTK_WIDGET (tablabel);
+
+    tablabel->drag_handle_size = DEFAULT_DRAG_HANDLE_SIZE;
+    tablabel->item = NULL;
+
+    label_widget = gtk_label_new ("Dock item");
+    gtk_container_add (GTK_CONTAINER (tablabel), label_widget);
+    gtk_widget_show (label_widget);
+
+    tablabel->active = FALSE;
+    gtk_widget_set_state (GTK_WIDGET (tablabel), GTK_STATE_ACTIVE);
+}
+
+static void
+gdl_dock_tablabel_set_property (GObject      *object,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+    GdlDockTablabel *tablabel;
+    GtkBin          *bin;
+
+    tablabel = GDL_DOCK_TABLABEL (object);
+
+    switch (prop_id) {
+        case PROP_ITEM:
+            if (tablabel->item) {
+                g_object_remove_weak_pointer (G_OBJECT (tablabel->item), 
+                                              (gpointer *) &tablabel->item);
+                g_signal_handlers_disconnect_by_func (
+                    tablabel->item, gdl_dock_tablabel_item_notify, tablabel);
+            };
+
+            tablabel->item = g_value_get_object (value);
+            if (tablabel->item) {
+                gboolean locked;
+                gchar   *long_name;
+                
+                g_object_add_weak_pointer (G_OBJECT (tablabel->item), 
+                                           (gpointer *) &tablabel->item);
+
+                g_signal_connect (tablabel->item, "notify::locked",
+                                  G_CALLBACK (gdl_dock_tablabel_item_notify),
+                                  tablabel);
+                g_signal_connect (tablabel->item, "notify::long_name",
+                                  G_CALLBACK (gdl_dock_tablabel_item_notify),
+                                  tablabel);
+                g_signal_connect (tablabel->item, "notify::grip_size",
+                                  G_CALLBACK (gdl_dock_tablabel_item_notify),
+                                  tablabel);
+
+                g_object_get (tablabel->item,
+                              "locked", &locked,
+                              "long-name", &long_name,
+                              "grip-size", &tablabel->drag_handle_size,
+                              NULL);
+
+                if (locked)
+                    tablabel->drag_handle_size = 0;
+                
+                bin = GTK_BIN (tablabel);
+                if (bin->child && g_object_class_find_property (
+                    G_OBJECT_GET_CLASS (bin->child), "label"))
+                    g_object_set (bin->child, "label", long_name, NULL);
+                g_free (long_name);
+            };
+            break;
+            
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+gdl_dock_tablabel_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+    GdlDockTablabel *tablabel;
+
+    tablabel = GDL_DOCK_TABLABEL (object);
+
+    switch (prop_id) {
+        case PROP_ITEM:
+            g_value_set_object (value, tablabel->item);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+gdl_dock_tablabel_item_notify (GObject    *master,
+                               GParamSpec *pspec,
+                               gpointer    data)
+{
+    GdlDockTablabel *tablabel = GDL_DOCK_TABLABEL (data);
+    gboolean         locked;
+    gchar           *label;
+    GtkBin          *bin;
+    
+    g_object_get (master,
+                  "locked", &locked,
+                  "grip-size", &tablabel->drag_handle_size,
+                  "long-name", &label,
+                  NULL);
+
+    if (locked)
+        tablabel->drag_handle_size = 0;
+
+    bin = GTK_BIN (tablabel);
+    if (bin->child && g_object_class_find_property (
+        G_OBJECT_GET_CLASS (bin->child), "label"))
+        g_object_set (bin->child, "label", label, NULL);
+    g_free (label);
+
+    gtk_widget_queue_resize (GTK_WIDGET (tablabel));
+}
+
+static void
+gdl_dock_tablabel_size_request (GtkWidget      *widget,
+                                GtkRequisition *requisition)
+{
+    GtkBin          *bin;
+    GtkRequisition   child_req;
+    GdlDockTablabel *tablabel;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (GDL_IS_DOCK_TABLABEL (widget));
+    g_return_if_fail (requisition != NULL);
+
+    tablabel = GDL_DOCK_TABLABEL (widget);
+    bin = GTK_BIN (widget);
+
+    requisition->width = tablabel->drag_handle_size;
+    requisition->height = 0;
+
+    if (bin->child)
+        gtk_widget_size_request (bin->child, &child_req);
+    else
+        child_req.width = child_req.height = 0;
+        
+    requisition->width += child_req.width;
+    requisition->height += child_req.height;
+
+    requisition->width += GTK_CONTAINER (widget)->border_width * 2;
+    requisition->height += GTK_CONTAINER (widget)->border_width * 2;
+
+    widget->requisition = *requisition;
+}
+
+static void
+gdl_dock_tablabel_size_allocate (GtkWidget     *widget,
+                                 GtkAllocation *allocation)
+{
+    GtkBin          *bin;
+    GdlDockTablabel *tablabel;
+    gint             border_width;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (GDL_IS_DOCK_TABLABEL (widget));
+    g_return_if_fail (allocation != NULL);
+  
+    bin = GTK_BIN (widget);
+    tablabel = GDL_DOCK_TABLABEL (widget);
+
+    border_width = GTK_CONTAINER (widget)->border_width;
+  
+    widget->allocation = *allocation;
+
+    if (GTK_WIDGET_REALIZED (widget))
+        gdk_window_move_resize (tablabel->event_window, 
+                                allocation->x, 
+                                allocation->y,
+                                allocation->width, 
+                                allocation->height);
+
+    if (bin->child && GTK_WIDGET_VISIBLE (bin->child)) {
+        GtkAllocation  child_allocation;
+
+        child_allocation.x = widget->allocation.x + border_width;
+        child_allocation.y = widget->allocation.y + border_width;
+
+        allocation->width = MAX (1, (int) allocation->width - 
+                                 (int) tablabel->drag_handle_size);
+        child_allocation.x += tablabel->drag_handle_size;
+
+        child_allocation.width = 
+            MAX (1, (int) allocation->width - 2 * border_width);
+        child_allocation.height = 
+            MAX (1, (int) allocation->height - 2 * border_width);
+
+        gtk_widget_size_allocate (bin->child, &child_allocation);
+    }
+}
+
+static void
+gdl_dock_tablabel_paint (GtkWidget      *widget,
+                         GdkEventExpose *event)
+{
+    GdkRectangle     dest, rect;
+    GtkBin          *bin;
+    GdlDockTablabel *tablabel;
+    gint             border_width;
+
+    bin = GTK_BIN (widget);
+    tablabel = GDL_DOCK_TABLABEL (widget);
+    border_width = GTK_CONTAINER (widget)->border_width;
+
+    rect.x = widget->allocation.x + border_width;
+    rect.y = widget->allocation.y + border_width;
+    rect.width = tablabel->drag_handle_size * HANDLE_RATIO;
+    rect.height = widget->allocation.height - 2*border_width;
+
+    if (gdk_rectangle_intersect (&event->area, &rect, &dest)) {
+        gtk_paint_handle (widget->style, widget->window, 
+                          tablabel->active ? GTK_STATE_NORMAL : GTK_STATE_ACTIVE, 
+                          GTK_SHADOW_NONE,
+                          &dest, widget, "dock-tablabel",
+                          rect.x, rect.y, rect.width, rect.height,
+                          GTK_ORIENTATION_VERTICAL);
+    };
+}
+
+static gint
+gdl_dock_tablabel_expose (GtkWidget      *widget,
+                          GdkEventExpose *event)
+{
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (GDL_IS_DOCK_TABLABEL (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget)) {
+        GDL_CALL_PARENT_GBOOLEAN(GTK_WIDGET_CLASS, expose_event, (widget,event));
+        gdl_dock_tablabel_paint (widget, event);
+    };
+  
+    return FALSE;
+}
+
+static gboolean 
+gdl_dock_tablabel_button_event (GtkWidget      *widget,
+                                GdkEventButton *event)
+{
+    GdlDockTablabel *tablabel;
+    gboolean         event_handled;
+  
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (GDL_IS_DOCK_TABLABEL (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+    
+    tablabel = GDL_DOCK_TABLABEL (widget);
+    
+    event_handled = FALSE;
+
+    if (event->window != tablabel->event_window)
+        return FALSE;
+    
+    switch (event->type) {
+        case GDK_BUTTON_PRESS:
+            if (tablabel->active) {
+                gboolean in_handle;
+                gint     rel_x, rel_y;
+                guint    border_width;
+                GtkBin  *bin;
+
+                bin = GTK_BIN (widget);
+                border_width = GTK_CONTAINER (widget)->border_width;
+
+                rel_x = event->x - border_width;
+                rel_y = event->y - border_width;
+
+                /* Check if user clicked on the drag handle. */      
+                in_handle = (rel_x < tablabel->drag_handle_size * HANDLE_RATIO) &&
+                    (rel_x > 0);
+
+                if (event->button == 1) {
+                    tablabel->pre_drag = TRUE;
+                    tablabel->drag_start_event = *event;
+                }
+                else {
+                    g_signal_emit (widget, 
+                                   dock_tablabel_signals [BUTTON_PRESSED_HANDLE],
+                                   0,
+                                   event);
+                }
+                
+                event_handled = TRUE;
+            }
+            break;
+
+        case GDK_BUTTON_RELEASE:
+            tablabel->pre_drag = FALSE;
+            break;
+
+        default:
+            break;
+    }
+    
+    if (!event_handled) {
+        /* propagate the event to the parent's gdkwindow */
+        GdkEventButton e;
+
+        e = *event;
+        e.window = gtk_widget_get_parent_window (widget);
+        e.x += widget->allocation.x;
+        e.y += widget->allocation.y;
+        
+        gdk_event_put ((GdkEvent *) &e);
+    };
+
+    return event_handled;
+}
+
+static gboolean 
+gdl_dock_tablabel_motion_event (GtkWidget      *widget,
+                                GdkEventMotion *event)
+{
+    GdlDockTablabel *tablabel;
+    gboolean         event_handled;
+  
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (GDL_IS_DOCK_TABLABEL (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+    
+    tablabel = GDL_DOCK_TABLABEL (widget);
+    
+    event_handled = FALSE;
+
+    if (event->window != tablabel->event_window)
+        return FALSE;
+    
+    if (tablabel->pre_drag) {
+        if (gtk_drag_check_threshold (widget,
+                                      tablabel->drag_start_event.x,
+                                      tablabel->drag_start_event.y,
+                                      event->x,
+                                      event->y)) {
+            tablabel->pre_drag = FALSE;
+            g_signal_emit (widget, 
+                           dock_tablabel_signals [BUTTON_PRESSED_HANDLE],
+                           0,
+                           &tablabel->drag_start_event);
+            event_handled = TRUE;
+        }
+    }
+    
+    if (!event_handled) {
+        /* propagate the event to the parent's gdkwindow */
+        GdkEventMotion e;
+
+        e = *event;
+        e.window = gtk_widget_get_parent_window (widget);
+        e.x += widget->allocation.x;
+        e.y += widget->allocation.y;
+        
+        gdk_event_put ((GdkEvent *) &e);
+    };
+
+    return event_handled;
+}
+
+static void   
+gdl_dock_tablabel_realize (GtkWidget *widget)
+{
+    GdlDockTablabel *tablabel;
+    GdkWindowAttr attributes;
+    int attributes_mask;
+    
+    tablabel = GDL_DOCK_TABLABEL (widget);
+    
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.wclass = GDK_INPUT_ONLY;
+    attributes.event_mask = gtk_widget_get_events (widget);
+    attributes.event_mask |= (GDK_EXPOSURE_MASK | 
+                              GDK_BUTTON_PRESS_MASK |
+                              GDK_BUTTON_RELEASE_MASK | 
+                              GDK_ENTER_NOTIFY_MASK | 
+                              GDK_POINTER_MOTION_MASK | 
+                              GDK_LEAVE_NOTIFY_MASK);
+    attributes_mask = GDK_WA_X | GDK_WA_Y;
+    
+    widget->window = gtk_widget_get_parent_window (widget);
+    g_object_ref (widget->window);
+    
+    tablabel->event_window = 
+        gdk_window_new (gtk_widget_get_parent_window (widget),
+                        &attributes, attributes_mask);
+    gdk_window_set_user_data (tablabel->event_window, widget);
+    
+    widget->style = gtk_style_attach (widget->style, widget->window);
+    
+    GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+}
+
+static void   
+gdl_dock_tablabel_unrealize (GtkWidget *widget)
+{
+    GdlDockTablabel *tablabel = GDL_DOCK_TABLABEL (widget);
+    
+    if (tablabel->event_window) {
+        gdk_window_set_user_data (tablabel->event_window, NULL);
+        gdk_window_destroy (tablabel->event_window);
+        tablabel->event_window = NULL;
+    }
+    
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, unrealize, (widget));
+}
+
+static void  
+gdl_dock_tablabel_map (GtkWidget *widget)
+{
+    GdlDockTablabel *tablabel = GDL_DOCK_TABLABEL (widget);
+    
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, map, (widget));
+    
+    gdk_window_show (tablabel->event_window);
+}
+
+static void   
+gdl_dock_tablabel_unmap (GtkWidget *widget)
+{
+    GdlDockTablabel *tablabel = GDL_DOCK_TABLABEL (widget);
+
+    gdk_window_hide (tablabel->event_window);
+
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, unmap, (widget));
+}
+
+/* ----- Public interface ----- */
+
+GtkWidget *
+gdl_dock_tablabel_new (GdlDockItem *item)
+{
+    GdlDockTablabel *tablabel;
+
+    tablabel = GDL_DOCK_TABLABEL (g_object_new (GDL_TYPE_DOCK_TABLABEL,
+                                                "item", item,
+                                                NULL));
+    
+    return GTK_WIDGET (tablabel);
+}
+
+void
+gdl_dock_tablabel_activate (GdlDockTablabel *tablabel)
+{
+    g_return_if_fail (tablabel != NULL);
+
+    tablabel->active = TRUE;
+    gtk_widget_set_state (GTK_WIDGET (tablabel), GTK_STATE_NORMAL);
+}
+
+void
+gdl_dock_tablabel_deactivate (GdlDockTablabel *tablabel)
+{
+    g_return_if_fail (tablabel != NULL);
+
+    tablabel->active = FALSE;
+    /* yeah, i know it contradictive */
+    gtk_widget_set_state (GTK_WIDGET (tablabel), GTK_STATE_ACTIVE);
+}
diff --git a/src/libgdl/gdl-dock-tablabel.h b/src/libgdl/gdl-dock-tablabel.h
new file mode 100644 (file)
index 0000000..8cf3470
--- /dev/null
@@ -0,0 +1,74 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * gdl-dock-tablabel.h
+ *
+ * This file is part of the GNOME Devtools Libraries.
+ *
+ * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GDL_DOCK_TABLABEL_H__
+#define __GDL_DOCK_TABLABEL_H__
+
+#include <gtk/gtk.h>
+#include "libgdl/gdl-dock-item.h"
+
+
+G_BEGIN_DECLS
+
+/* standard macros */
+#define GDL_TYPE_DOCK_TABLABEL            (gdl_dock_tablabel_get_type ())
+#define GDL_DOCK_TABLABEL(obj)            (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_TABLABEL, GdlDockTablabel))
+#define GDL_DOCK_TABLABEL_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_TABLABEL, GdlDockTablabelClass))
+#define GDL_IS_DOCK_TABLABEL(obj)         (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_TABLABEL))
+#define GDL_IS_DOCK_TABLABEL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_TABLABEL))
+#define GDL_DOCK_TABLABEL_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK_TABLABEL, GdlDockTablabelClass))
+
+/* data types & structures */
+typedef struct _GdlDockTablabel      GdlDockTablabel;
+typedef struct _GdlDockTablabelClass GdlDockTablabelClass;
+
+struct _GdlDockTablabel {
+    GtkBin          parent;
+
+    guint           drag_handle_size;
+    GtkWidget      *item;
+    GdkWindow      *event_window;
+    gboolean        active;
+
+    GdkEventButton  drag_start_event;
+    gboolean        pre_drag;
+};
+
+struct _GdlDockTablabelClass {
+    GtkBinClass      parent_class;
+
+    void            (*button_pressed_handle)  (GdlDockTablabel *tablabel,
+                                               GdkEventButton  *event);
+};
+
+/* public interface */
+GtkWidget     *gdl_dock_tablabel_new           (GdlDockItem *item);
+GType          gdl_dock_tablabel_get_type      (void);
+
+void           gdl_dock_tablabel_activate      (GdlDockTablabel *tablabel);
+void           gdl_dock_tablabel_deactivate    (GdlDockTablabel *tablabel);
+
+G_END_DECLS
+
+#endif
diff --git a/src/libgdl/gdl-dock.c b/src/libgdl/gdl-dock.c
new file mode 100644 (file)
index 0000000..131a7bb
--- /dev/null
@@ -0,0 +1,1372 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 
+ *
+ * This file is part of the GNOME Devtools Libraries.
+ *
+ * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
+ *               2007 Naba Kumar  <naba@gnome.org>
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gdl-i18n.h"
+#include <stdlib.h>
+#include <string.h>
+
+#include "gdl-tools.h"
+#include "gdl-dock.h"
+#include "gdl-dock-master.h"
+#include "gdl-dock-paned.h"
+#include "gdl-dock-notebook.h"
+#include "gdl-dock-placeholder.h"
+
+#include "libgdlmarshal.h"
+
+
+/* ----- Private prototypes ----- */
+
+static void  gdl_dock_class_init      (GdlDockClass *class);
+static void  gdl_dock_instance_init   (GdlDock *dock);
+
+static GObject *gdl_dock_constructor  (GType                  type,
+                                       guint                  n_construct_properties,
+                                       GObjectConstructParam *construct_param);
+static void  gdl_dock_set_property    (GObject      *object,
+                                       guint         prop_id,
+                                       const GValue *value,
+                                       GParamSpec   *pspec);
+static void  gdl_dock_get_property    (GObject      *object,
+                                       guint         prop_id,
+                                       GValue       *value,
+                                       GParamSpec   *pspec);
+static void  gdl_dock_notify_cb       (GObject      *object,
+                                       GParamSpec   *pspec,
+                                       gpointer      user_data);
+
+static void  gdl_dock_set_title       (GdlDock      *dock);
+
+static void  gdl_dock_destroy         (GtkObject    *object);
+
+static void  gdl_dock_size_request    (GtkWidget      *widget,
+                                       GtkRequisition *requisition);
+static void  gdl_dock_size_allocate   (GtkWidget      *widget,
+                                       GtkAllocation  *allocation);
+static void  gdl_dock_map             (GtkWidget      *widget);
+static void  gdl_dock_unmap           (GtkWidget      *widget);
+static void  gdl_dock_show            (GtkWidget      *widget);
+static void  gdl_dock_hide            (GtkWidget      *widget);
+
+static void  gdl_dock_add             (GtkContainer *container,
+                                       GtkWidget    *widget);
+static void  gdl_dock_remove          (GtkContainer *container,
+                                       GtkWidget    *widget);
+static void  gdl_dock_forall          (GtkContainer *container,
+                                       gboolean      include_internals,
+                                       GtkCallback   callback,
+                                       gpointer      callback_data);
+static GtkType  gdl_dock_child_type   (GtkContainer *container);
+
+static void     gdl_dock_detach       (GdlDockObject    *object,
+                                       gboolean          recursive);
+static void     gdl_dock_reduce       (GdlDockObject    *object);
+static gboolean gdl_dock_dock_request (GdlDockObject    *object,
+                                       gint              x,
+                                       gint              y,
+                                       GdlDockRequest   *request);
+static void     gdl_dock_dock         (GdlDockObject    *object,
+                                       GdlDockObject    *requestor,
+                                       GdlDockPlacement  position,
+                                       GValue           *other_data);
+static gboolean gdl_dock_reorder      (GdlDockObject    *object,
+                                       GdlDockObject    *requestor,
+                                       GdlDockPlacement  new_position,
+                                       GValue           *other_data);
+
+static gboolean gdl_dock_floating_window_delete_event_cb (GtkWidget *widget);
+
+static gboolean gdl_dock_child_placement  (GdlDockObject    *object,
+                                           GdlDockObject    *child,
+                                           GdlDockPlacement *placement);
+
+static void     gdl_dock_present          (GdlDockObject    *object,
+                                           GdlDockObject    *child);
+
+
+/* ----- Class variables and definitions ----- */
+
+struct _GdlDockPrivate
+{
+    /* for floating docks */
+    gboolean            floating;
+    GtkWidget          *window;
+    gboolean            auto_title;
+    
+    gint                float_x;
+    gint                float_y;
+    gint                width;
+    gint                height;
+
+    /* auxiliary fields */
+    GdkGC              *xor_gc;
+};
+
+enum {
+    LAYOUT_CHANGED,
+    LAST_SIGNAL
+};
+
+enum {
+    PROP_0,
+    PROP_FLOATING,
+    PROP_DEFAULT_TITLE,
+    PROP_WIDTH,
+    PROP_HEIGHT,
+    PROP_FLOAT_X,
+    PROP_FLOAT_Y
+};
+
+static guint dock_signals [LAST_SIGNAL] = { 0 };
+
+#define SPLIT_RATIO  0.3
+
+
+/* ----- Private functions ----- */
+
+GDL_CLASS_BOILERPLATE (GdlDock, gdl_dock, GdlDockObject, GDL_TYPE_DOCK_OBJECT);
+
+static void
+gdl_dock_class_init (GdlDockClass *klass)
+{
+    GObjectClass       *g_object_class;
+    GtkObjectClass     *gtk_object_class;
+    GtkWidgetClass     *widget_class;
+    GtkContainerClass  *container_class;
+    GdlDockObjectClass *object_class;
+    
+    g_object_class = G_OBJECT_CLASS (klass);
+    gtk_object_class = GTK_OBJECT_CLASS (klass);
+    widget_class = GTK_WIDGET_CLASS (klass);
+    container_class = GTK_CONTAINER_CLASS (klass);
+    object_class = GDL_DOCK_OBJECT_CLASS (klass);
+    
+    g_object_class->constructor = gdl_dock_constructor;
+    g_object_class->set_property = gdl_dock_set_property;
+    g_object_class->get_property = gdl_dock_get_property;
+    
+    /* properties */
+
+    g_object_class_install_property (
+        g_object_class, PROP_FLOATING,
+        g_param_spec_boolean ("floating", _("Floating"),
+                              _("Whether the dock is floating in its own window"),
+                              FALSE,
+                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                              GDL_DOCK_PARAM_EXPORT));
+    
+    g_object_class_install_property (
+        g_object_class, PROP_DEFAULT_TITLE,
+        g_param_spec_string ("default-title", _("Default title"),
+                             _("Default title for the newly created floating docks"),
+                             NULL,
+                             G_PARAM_READWRITE));
+    
+    g_object_class_install_property (
+        g_object_class, PROP_WIDTH,
+        g_param_spec_int ("width", _("Width"),
+                          _("Width for the dock when it's of floating type"),
+                          -1, G_MAXINT, -1,
+                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+                          GDL_DOCK_PARAM_EXPORT));
+    
+    g_object_class_install_property (
+        g_object_class, PROP_HEIGHT,
+        g_param_spec_int ("height", _("Height"),
+                          _("Height for the dock when it's of floating type"),
+                          -1, G_MAXINT, -1,
+                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+                          GDL_DOCK_PARAM_EXPORT));
+    
+    g_object_class_install_property (
+        g_object_class, PROP_FLOAT_X,
+        g_param_spec_int ("floatx", _("Float X"),
+                          _("X coordinate for a floating dock"),
+                          G_MININT, G_MAXINT, 0,
+                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+                          GDL_DOCK_PARAM_EXPORT));
+    
+    g_object_class_install_property (
+        g_object_class, PROP_FLOAT_Y,
+        g_param_spec_int ("floaty", _("Float Y"),
+                          _("Y coordinate for a floating dock"),
+                          G_MININT, G_MAXINT, 0,
+                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+                          GDL_DOCK_PARAM_EXPORT));
+    
+    gtk_object_class->destroy = gdl_dock_destroy;
+
+    widget_class->size_request = gdl_dock_size_request;
+    widget_class->size_allocate = gdl_dock_size_allocate;
+    widget_class->map = gdl_dock_map;
+    widget_class->unmap = gdl_dock_unmap;
+    widget_class->show = gdl_dock_show;
+    widget_class->hide = gdl_dock_hide;
+    
+    container_class->add = gdl_dock_add;
+    container_class->remove = gdl_dock_remove;
+    container_class->forall = gdl_dock_forall;
+    container_class->child_type = gdl_dock_child_type;
+    
+    object_class->is_compound = TRUE;
+    
+    object_class->detach = gdl_dock_detach;
+    object_class->reduce = gdl_dock_reduce;
+    object_class->dock_request = gdl_dock_dock_request;
+    object_class->dock = gdl_dock_dock;
+    object_class->reorder = gdl_dock_reorder;    
+    object_class->child_placement = gdl_dock_child_placement;
+    object_class->present = gdl_dock_present;
+    
+    /* signals */
+
+    dock_signals [LAYOUT_CHANGED] = 
+        g_signal_new ("layout-changed", 
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (GdlDockClass, layout_changed),
+                      NULL, /* accumulator */
+                      NULL, /* accu_data */
+                      gdl_marshal_VOID__VOID,
+                      G_TYPE_NONE, /* return type */
+                      0);
+
+    klass->layout_changed = NULL;
+}
+
+static void
+gdl_dock_instance_init (GdlDock *dock)
+{
+    GTK_WIDGET_SET_FLAGS (GTK_WIDGET (dock), GTK_NO_WINDOW);
+
+    dock->root = NULL;
+    dock->_priv = g_new0 (GdlDockPrivate, 1);
+    dock->_priv->width = -1;
+    dock->_priv->height = -1;
+}
+
+static gboolean 
+gdl_dock_floating_configure_event_cb (GtkWidget         *widget,
+                                      GdkEventConfigure *event,
+                                      gpointer           user_data)
+{
+    GdlDock *dock;
+
+    g_return_val_if_fail (user_data != NULL && GDL_IS_DOCK (user_data), TRUE);
+
+    dock = GDL_DOCK (user_data);
+    dock->_priv->float_x = event->x;
+    dock->_priv->float_y = event->y;
+    dock->_priv->width = event->width;
+    dock->_priv->height = event->height;
+
+    return FALSE;
+}
+
+static GObject *
+gdl_dock_constructor (GType                  type,
+                      guint                  n_construct_properties,
+                      GObjectConstructParam *construct_param)
+{
+    GObject *g_object;
+    
+    g_object = GDL_CALL_PARENT_WITH_DEFAULT (G_OBJECT_CLASS, 
+                                               constructor, 
+                                               (type,
+                                                n_construct_properties,
+                                                construct_param),
+                                               NULL);
+    if (g_object) {
+        GdlDock *dock = GDL_DOCK (g_object);
+        GdlDockMaster *master;
+        
+        /* create a master for the dock if none was provided in the construction */
+        master = GDL_DOCK_OBJECT_GET_MASTER (GDL_DOCK_OBJECT (dock));
+        if (!master) {
+            GDL_DOCK_OBJECT_UNSET_FLAGS (dock, GDL_DOCK_AUTOMATIC);
+            master = g_object_new (GDL_TYPE_DOCK_MASTER, NULL);
+            /* the controller owns the master ref */
+            gdl_dock_object_bind (GDL_DOCK_OBJECT (dock), G_OBJECT (master));
+        }
+
+        if (dock->_priv->floating) {
+            GdlDockObject *controller;
+            
+            /* create floating window for this dock */
+            dock->_priv->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+            g_object_set_data (G_OBJECT (dock->_priv->window), "dock", dock);
+            
+            /* set position and default size */
+            gtk_window_set_position (GTK_WINDOW (dock->_priv->window),
+                                     GTK_WIN_POS_MOUSE);
+            gtk_window_set_default_size (GTK_WINDOW (dock->_priv->window),
+                                         dock->_priv->width,
+                                         dock->_priv->height);
+            gtk_window_set_type_hint (GTK_WINDOW (dock->_priv->window),
+                                      GDK_WINDOW_TYPE_HINT_NORMAL);
+            
+            gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dock->_priv->window), 
+                                              TRUE);
+
+            /* metacity ignores this */
+            gtk_window_move (GTK_WINDOW (dock->_priv->window),
+                             dock->_priv->float_x,
+                             dock->_priv->float_y);
+            
+            /* connect to the configure event so we can track down window geometry */
+            g_signal_connect (dock->_priv->window, "configure_event",
+                              (GCallback) gdl_dock_floating_configure_event_cb,
+                              dock);
+            
+            /* set the title and connect to the long_name notify queue
+               so we can reset the title when this prop changes */
+            gdl_dock_set_title (dock);
+            g_signal_connect (dock, "notify::long-name",
+                              (GCallback) gdl_dock_notify_cb, NULL);
+            
+            /* set transient for the first dock if that is a non-floating dock */
+            controller = gdl_dock_master_get_controller (master);
+            if (controller && GDL_IS_DOCK (controller)) {
+                gboolean first_is_floating;
+                g_object_get (controller, "floating", &first_is_floating, NULL);
+                if (!first_is_floating) {
+                    GtkWidget *toplevel =
+                        gtk_widget_get_toplevel (GTK_WIDGET (controller));
+
+                    if (GTK_IS_WINDOW (toplevel))
+                        gtk_window_set_transient_for (GTK_WINDOW (dock->_priv->window),
+                                                      GTK_WINDOW (toplevel));
+                }
+            }
+
+            gtk_container_add (GTK_CONTAINER (dock->_priv->window), GTK_WIDGET (dock));
+    
+            g_signal_connect (dock->_priv->window, "delete_event",
+                              G_CALLBACK (gdl_dock_floating_window_delete_event_cb), 
+                              NULL);
+        }
+        GDL_DOCK_OBJECT_SET_FLAGS (dock, GDL_DOCK_ATTACHED);
+    }
+    
+    return g_object;
+}
+
+static void
+gdl_dock_set_property  (GObject      *object,
+                        guint         prop_id,
+                        const GValue *value,
+                        GParamSpec   *pspec)
+{
+    GdlDock *dock = GDL_DOCK (object);
+    
+    switch (prop_id) {
+        case PROP_FLOATING:
+            dock->_priv->floating = g_value_get_boolean (value);
+            break;
+        case PROP_DEFAULT_TITLE:
+            if (GDL_DOCK_OBJECT (object)->master)
+                g_object_set (GDL_DOCK_OBJECT (object)->master,
+                              "default-title", g_value_get_string (value),
+                              NULL);
+            break;
+        case PROP_WIDTH:
+            dock->_priv->width = g_value_get_int (value);
+            break;
+        case PROP_HEIGHT:
+            dock->_priv->height = g_value_get_int (value);
+            break;
+        case PROP_FLOAT_X:
+            dock->_priv->float_x = g_value_get_int (value);
+            break;
+        case PROP_FLOAT_Y:
+            dock->_priv->float_y = g_value_get_int (value);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+
+    switch (prop_id) {
+        case PROP_WIDTH:
+        case PROP_HEIGHT:
+        case PROP_FLOAT_X:
+        case PROP_FLOAT_Y:
+            if (dock->_priv->floating && dock->_priv->window) {
+                gtk_window_resize (GTK_WINDOW (dock->_priv->window),
+                                   dock->_priv->width,
+                                   dock->_priv->height);
+            }
+            break;
+    }
+}
+
+static void
+gdl_dock_get_property  (GObject      *object,
+                        guint         prop_id,
+                        GValue       *value,
+                        GParamSpec   *pspec)
+{
+    GdlDock *dock = GDL_DOCK (object);
+
+    switch (prop_id) {
+        case PROP_FLOATING:
+            g_value_set_boolean (value, dock->_priv->floating);
+            break;
+        case PROP_DEFAULT_TITLE:
+            if (GDL_DOCK_OBJECT (object)->master) {
+                gchar *default_title;
+                g_object_get (GDL_DOCK_OBJECT (object)->master,
+                              "default-title", &default_title,
+                              NULL);
+#if GLIB_CHECK_VERSION(2,3,0)
+                g_value_take_string (value, default_title);
+#else
+                g_value_set_string_take_ownership (value, default_title);
+#endif
+            }
+            else
+                g_value_set_string (value, NULL);
+            break;
+        case PROP_WIDTH:
+            g_value_set_int (value, dock->_priv->width);
+            break;
+        case PROP_HEIGHT:
+            g_value_set_int (value, dock->_priv->height);
+            break;
+        case PROP_FLOAT_X:
+            g_value_set_int (value, dock->_priv->float_x);
+            break;
+        case PROP_FLOAT_Y:
+            g_value_set_int (value, dock->_priv->float_y);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+gdl_dock_set_title (GdlDock *dock)
+{
+    GdlDockObject *object = GDL_DOCK_OBJECT (dock);
+    gchar         *title = NULL;
+    gboolean       free_title = FALSE;
+    
+    if (!dock->_priv->window)
+        return;
+    
+    if (!dock->_priv->auto_title && object->long_name) {
+        title = object->long_name;
+    }
+    else if (object->master) {
+        g_object_get (object->master, "default-title", &title, NULL);
+        free_title = TRUE;
+    }
+
+    if (!title && dock->root) {
+        g_object_get (dock->root, "long-name", &title, NULL);
+        free_title = TRUE;
+    }
+    
+    if (!title) {
+        /* set a default title in the long_name */
+        dock->_priv->auto_title = TRUE;
+        free_title = FALSE;
+        title = object->long_name = g_strdup_printf (
+            _("Dock #%d"), GDL_DOCK_MASTER (object->master)->dock_number++);
+    }
+
+    gtk_window_set_title (GTK_WINDOW (dock->_priv->window), title);
+    if (free_title)
+        g_free (title);
+}
+
+static void
+gdl_dock_notify_cb (GObject    *object,
+                    GParamSpec *pspec,
+                    gpointer    user_data)
+{
+    GdlDock *dock;
+    
+    g_return_if_fail (object != NULL || GDL_IS_DOCK (object));
+    
+    dock = GDL_DOCK (object);
+    dock->_priv->auto_title = FALSE;
+    gdl_dock_set_title (dock);
+}
+
+static void
+gdl_dock_destroy (GtkObject *object)
+{
+    GdlDock *dock = GDL_DOCK (object);
+
+    if (dock->_priv) {
+        GdlDockPrivate *priv = dock->_priv;
+        dock->_priv = NULL;
+
+        if (priv->window) {
+            gtk_widget_destroy (priv->window);
+            priv->floating = FALSE;
+            priv->window = NULL;
+        }
+        
+        /* destroy the xor gc */
+        if (priv->xor_gc) {
+            g_object_unref (priv->xor_gc);
+            priv->xor_gc = NULL;
+        }
+
+        g_free (priv);
+    }
+    
+    GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
+}
+
+static void
+gdl_dock_size_request (GtkWidget      *widget,
+                       GtkRequisition *requisition)
+{
+    GdlDock       *dock;
+    GtkContainer  *container;
+    guint          border_width;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (GDL_IS_DOCK (widget));
+
+    dock = GDL_DOCK (widget);
+    container = GTK_CONTAINER (widget);
+    border_width = container->border_width;
+
+    /* make request to root */
+    if (dock->root && GTK_WIDGET_VISIBLE (dock->root))
+        gtk_widget_size_request (GTK_WIDGET (dock->root), requisition);
+    else {
+        requisition->width = 0;
+        requisition->height = 0;
+    };
+
+    requisition->width += 2 * border_width;
+    requisition->height += 2 * border_width;
+
+    widget->requisition = *requisition;
+}
+
+static void
+gdl_dock_size_allocate (GtkWidget     *widget,
+                        GtkAllocation *allocation)
+{
+    GdlDock       *dock;
+    GtkContainer  *container;
+    guint          border_width;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (GDL_IS_DOCK (widget));
+    
+    dock = GDL_DOCK (widget);
+    container = GTK_CONTAINER (widget);
+    border_width = container->border_width;
+
+    widget->allocation = *allocation;
+
+    /* reduce allocation by border width */
+    allocation->x += border_width;
+    allocation->y += border_width;
+    allocation->width = MAX (1, allocation->width - 2 * border_width);
+    allocation->height = MAX (1, allocation->height - 2 * border_width);
+
+    if (dock->root && GTK_WIDGET_VISIBLE (dock->root))
+        gtk_widget_size_allocate (GTK_WIDGET (dock->root), allocation);
+}
+
+static void
+gdl_dock_map (GtkWidget *widget)
+{
+    GtkWidget *child;
+    GdlDock   *dock;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (GDL_IS_DOCK (widget));
+
+    dock = GDL_DOCK (widget);
+
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, map, (widget));
+
+    if (dock->root) {
+        child = GTK_WIDGET (dock->root);
+        if (GTK_WIDGET_VISIBLE (child) && !GTK_WIDGET_MAPPED (child))
+            gtk_widget_map (child);
+    }
+}
+
+static void
+gdl_dock_unmap (GtkWidget *widget)
+{
+    GtkWidget *child;
+    GdlDock   *dock;
+    
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (GDL_IS_DOCK (widget));
+
+    dock = GDL_DOCK (widget);
+
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, unmap, (widget));
+
+    if (dock->root) {
+        child = GTK_WIDGET (dock->root);
+        if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_MAPPED (child))
+            gtk_widget_unmap (child);
+    }
+    
+    if (dock->_priv->window)
+        gtk_widget_unmap (dock->_priv->window);
+}
+
+static void
+gdl_dock_foreach_automatic (GdlDockObject *object,
+                            gpointer       user_data)
+{
+    void (* function) (GtkWidget *) = user_data;
+
+    if (GDL_DOCK_OBJECT_AUTOMATIC (object))
+        (* function) (GTK_WIDGET (object));
+}
+
+static void
+gdl_dock_show (GtkWidget *widget)
+{
+    GdlDock *dock;
+    
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (GDL_IS_DOCK (widget));
+    
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, show, (widget));
+    
+    dock = GDL_DOCK (widget);
+    if (dock->_priv->floating && dock->_priv->window)
+        gtk_widget_show (dock->_priv->window);
+
+    if (GDL_DOCK_IS_CONTROLLER (dock)) {
+        gdl_dock_master_foreach_toplevel (GDL_DOCK_OBJECT_GET_MASTER (dock),
+                                          FALSE, (GFunc) gdl_dock_foreach_automatic,
+                                          gtk_widget_show);
+    }
+}
+
+static void
+gdl_dock_hide (GtkWidget *widget)
+{
+    GdlDock *dock;
+    
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (GDL_IS_DOCK (widget));
+    
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, hide, (widget));
+    
+    dock = GDL_DOCK (widget);
+    if (dock->_priv->floating && dock->_priv->window)
+        gtk_widget_hide (dock->_priv->window);
+
+    if (GDL_DOCK_IS_CONTROLLER (dock)) {
+        gdl_dock_master_foreach_toplevel (GDL_DOCK_OBJECT_GET_MASTER (dock),
+                                          FALSE, (GFunc) gdl_dock_foreach_automatic,
+                                          gtk_widget_hide);
+    }
+}
+
+static void
+gdl_dock_add (GtkContainer *container,
+              GtkWidget    *widget)
+{
+    g_return_if_fail (container != NULL);
+    g_return_if_fail (GDL_IS_DOCK (container));
+    g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
+
+    gdl_dock_add_item (GDL_DOCK (container), 
+                       GDL_DOCK_ITEM (widget), 
+                       GDL_DOCK_TOP);  /* default position */
+}
+
+static void
+gdl_dock_remove (GtkContainer *container,
+                 GtkWidget    *widget)
+{
+    GdlDock  *dock;
+    gboolean  was_visible;
+
+    g_return_if_fail (container != NULL);
+    g_return_if_fail (widget != NULL);
+
+    dock = GDL_DOCK (container);
+    was_visible = GTK_WIDGET_VISIBLE (widget);
+
+    if (GTK_WIDGET (dock->root) == widget) {
+        dock->root = NULL;
+        GDL_DOCK_OBJECT_UNSET_FLAGS (widget, GDL_DOCK_ATTACHED);
+        gtk_widget_unparent (widget);
+
+        if (was_visible && GTK_WIDGET_VISIBLE (GTK_WIDGET (container)))
+            gtk_widget_queue_resize (GTK_WIDGET (dock));
+    }
+}
+
+static void
+gdl_dock_forall (GtkContainer *container,
+                 gboolean      include_internals,
+                 GtkCallback   callback,
+                 gpointer      callback_data)
+{
+    GdlDock *dock;
+
+    g_return_if_fail (container != NULL);
+    g_return_if_fail (GDL_IS_DOCK (container));
+    g_return_if_fail (callback != NULL);
+
+    dock = GDL_DOCK (container);
+
+    if (dock->root)
+        (*callback) (GTK_WIDGET (dock->root), callback_data);
+}
+
+static GtkType
+gdl_dock_child_type (GtkContainer *container)
+{
+    return GDL_TYPE_DOCK_ITEM;
+}
+
+static void
+gdl_dock_detach (GdlDockObject *object,
+                 gboolean       recursive)
+{
+    GdlDock *dock = GDL_DOCK (object);
+    
+    /* detach children */
+    if (recursive && dock->root) {
+        gdl_dock_object_detach (dock->root, recursive);
+    }
+    GDL_DOCK_OBJECT_UNSET_FLAGS (object, GDL_DOCK_ATTACHED);
+}
+
+static void
+gdl_dock_reduce (GdlDockObject *object)
+{
+    GdlDock *dock = GDL_DOCK (object);
+    
+    if (dock->root)
+        return;
+    
+    if (GDL_DOCK_OBJECT_AUTOMATIC (dock)) {
+        gtk_widget_destroy (GTK_WIDGET (dock));
+
+    } else if (!GDL_DOCK_OBJECT_ATTACHED (dock)) {
+        /* if the user explicitly detached the object */
+        if (dock->_priv->floating)
+            gtk_widget_hide (GTK_WIDGET (dock));
+        else {
+            GtkWidget *widget = GTK_WIDGET (object);
+            if (widget->parent) 
+                gtk_container_remove (GTK_CONTAINER (widget->parent), widget);
+        }
+    }
+}
+
+static gboolean
+gdl_dock_dock_request (GdlDockObject  *object,
+                       gint            x,
+                       gint            y,
+                       GdlDockRequest *request)
+{
+    GdlDock            *dock;
+    guint               bw;
+    gint                rel_x, rel_y;
+    GtkAllocation      *alloc;
+    gboolean            may_dock = FALSE;
+    GdlDockRequest      my_request;
+
+    g_return_val_if_fail (GDL_IS_DOCK (object), FALSE);
+
+    /* we get (x,y) in our allocation coordinates system */
+    
+    dock = GDL_DOCK (object);
+    
+    /* Get dock size. */
+    alloc = &(GTK_WIDGET (dock)->allocation);
+    bw = GTK_CONTAINER (dock)->border_width;
+
+    /* Get coordinates relative to our allocation area. */
+    rel_x = x - alloc->x;
+    rel_y = y - alloc->y;
+
+    if (request)
+        my_request = *request;
+        
+    /* Check if coordinates are in GdlDock widget. */
+    if (rel_x > 0 && rel_x < alloc->width &&
+        rel_y > 0 && rel_y < alloc->height) {
+
+        /* It's inside our area. */
+        may_dock = TRUE;
+
+       /* Set docking indicator rectangle to the GdlDock size. */
+        my_request.rect.x = alloc->x + bw;
+        my_request.rect.y = alloc->y + bw;
+        my_request.rect.width = alloc->width - 2*bw;
+        my_request.rect.height = alloc->height - 2*bw;
+
+       /* If GdlDock has no root item yet, set the dock itself as 
+          possible target. */
+        if (!dock->root) {
+            my_request.position = GDL_DOCK_TOP;
+            my_request.target = object;
+        } else {
+            my_request.target = dock->root;
+
+            /* See if it's in the border_width band. */
+            if (rel_x < bw) {
+                my_request.position = GDL_DOCK_LEFT;
+                my_request.rect.width *= SPLIT_RATIO;
+            } else if (rel_x > alloc->width - bw) {
+                my_request.position = GDL_DOCK_RIGHT;
+                my_request.rect.x += my_request.rect.width * (1 - SPLIT_RATIO);
+                my_request.rect.width *= SPLIT_RATIO;
+            } else if (rel_y < bw) {
+                my_request.position = GDL_DOCK_TOP;
+                my_request.rect.height *= SPLIT_RATIO;
+            } else if (rel_y > alloc->height - bw) {
+                my_request.position = GDL_DOCK_BOTTOM;
+                my_request.rect.y += my_request.rect.height * (1 - SPLIT_RATIO);
+                my_request.rect.height *= SPLIT_RATIO;
+            } else {
+                /* Otherwise try our children. */
+                /* give them allocation coordinates (we are a
+                   GTK_NO_WINDOW) widget */
+                may_dock = gdl_dock_object_dock_request (GDL_DOCK_OBJECT (dock->root), 
+                                                         x, y, &my_request);
+            }
+        }
+    }
+
+    if (may_dock && request)
+        *request = my_request;
+    
+    return may_dock;
+}
+
+static void
+gdl_dock_dock (GdlDockObject    *object,
+               GdlDockObject    *requestor,
+               GdlDockPlacement  position,
+               GValue           *user_data)
+{
+    GdlDock *dock;
+    
+    g_return_if_fail (GDL_IS_DOCK (object));
+    /* only dock items allowed at this time */
+    g_return_if_fail (GDL_IS_DOCK_ITEM (requestor));
+
+    dock = GDL_DOCK (object);
+    
+    if (position == GDL_DOCK_FLOATING) {
+        GdlDockItem *item = GDL_DOCK_ITEM (requestor);
+        gint x, y, width, height;
+
+        if (user_data && G_VALUE_HOLDS (user_data, GDK_TYPE_RECTANGLE)) {
+            GdkRectangle *rect;
+
+            rect = g_value_get_boxed (user_data);
+            x = rect->x;
+            y = rect->y;
+            width = rect->width;
+            height = rect->height;
+        }
+        else {
+            x = y = 0;
+            width = height = -1;
+        }
+        
+        gdl_dock_add_floating_item (dock, item,
+                                    x, y, width, height);
+    }
+    else if (dock->root) {
+        /* This is somewhat a special case since we know which item to
+           pass the request on because we only have on child */
+        gdl_dock_object_dock (dock->root, requestor, position, NULL);
+        gdl_dock_set_title (dock);
+        
+    }
+    else { /* Item about to be added is root item. */
+        GtkWidget *widget = GTK_WIDGET (requestor);
+        
+        dock->root = requestor;
+        GDL_DOCK_OBJECT_SET_FLAGS (requestor, GDL_DOCK_ATTACHED);
+        gtk_widget_set_parent (widget, GTK_WIDGET (dock));
+        
+        gdl_dock_item_show_grip (GDL_DOCK_ITEM (requestor));
+
+        /* Realize the item (create its corresponding GdkWindow) when 
+           GdlDock has been realized. */
+        if (GTK_WIDGET_REALIZED (dock))
+            gtk_widget_realize (widget);
+        
+        /* Map the widget if it's visible and the parent is visible and has 
+           been mapped. This is done to make sure that the GdkWindow is 
+           visible. */
+        if (GTK_WIDGET_VISIBLE (dock) && 
+            GTK_WIDGET_VISIBLE (widget)) {
+            if (GTK_WIDGET_MAPPED (dock))
+                gtk_widget_map (widget);
+            
+            /* Make the widget resize. */
+            gtk_widget_queue_resize (widget);
+        }
+        gdl_dock_set_title (dock);
+    }
+}
+    
+static gboolean
+gdl_dock_floating_window_delete_event_cb (GtkWidget *widget)
+{
+    GdlDock *dock;
+    
+    g_return_val_if_fail (GTK_IS_WINDOW (widget), FALSE);
+    
+    dock = GDL_DOCK (g_object_get_data (G_OBJECT (widget), "dock"));
+    if (dock->root) {
+        /* this will call reduce on ourselves, hiding the window if appropiate */
+        gdl_dock_item_hide_item (GDL_DOCK_ITEM (dock->root));
+    }
+
+    return TRUE;
+}
+
+static void
+_gdl_dock_foreach_build_list (GdlDockObject *object,
+                              gpointer       user_data)
+{
+    GList **l = (GList **) user_data;
+
+    if (GDL_IS_DOCK_ITEM (object))
+        *l = g_list_prepend (*l, object);
+}
+
+static gboolean
+gdl_dock_reorder (GdlDockObject    *object,
+                  GdlDockObject    *requestor,
+                  GdlDockPlacement  new_position,
+                  GValue           *other_data)
+{
+    GdlDock *dock = GDL_DOCK (object);
+    gboolean handled = FALSE;
+    
+    if (dock->_priv->floating &&
+        new_position == GDL_DOCK_FLOATING &&
+        dock->root == requestor) {
+        
+        if (other_data && G_VALUE_HOLDS (other_data, GDK_TYPE_RECTANGLE)) {
+            GdkRectangle *rect;
+
+            rect = g_value_get_boxed (other_data);
+            gtk_window_move (GTK_WINDOW (dock->_priv->window),
+                             rect->x,
+                             rect->y);
+            handled = TRUE;
+        }
+    }
+    
+    return handled;
+}
+
+static gboolean 
+gdl_dock_child_placement (GdlDockObject    *object,
+                          GdlDockObject    *child,
+                          GdlDockPlacement *placement)
+{
+    GdlDock *dock = GDL_DOCK (object);
+    gboolean retval = TRUE;
+    
+    if (dock->root == child) {
+        if (placement) {
+            if (*placement == GDL_DOCK_NONE || *placement == GDL_DOCK_FLOATING)
+                *placement = GDL_DOCK_TOP;
+        }
+    } else 
+        retval = FALSE;
+
+    return retval;
+}
+
+static void 
+gdl_dock_present (GdlDockObject *object,
+                  GdlDockObject *child)
+{
+    GdlDock *dock = GDL_DOCK (object);
+
+    if (dock->_priv->floating)
+        gtk_window_present (GTK_WINDOW (dock->_priv->window));
+}
+
+
+/* ----- Public interface ----- */
+
+GtkWidget *
+gdl_dock_new (void)
+{
+    GObject *dock;
+
+    dock = g_object_new (GDL_TYPE_DOCK, NULL);
+    GDL_DOCK_OBJECT_UNSET_FLAGS (dock, GDL_DOCK_AUTOMATIC);
+    
+    return GTK_WIDGET (dock);
+}
+
+GtkWidget *
+gdl_dock_new_from (GdlDock  *original,
+                   gboolean  floating)
+{
+    GObject *new_dock;
+    
+    g_return_val_if_fail (original != NULL, NULL);
+    
+    new_dock = g_object_new (GDL_TYPE_DOCK, 
+                             "master", GDL_DOCK_OBJECT_GET_MASTER (original), 
+                             "floating", floating,
+                             NULL);
+    GDL_DOCK_OBJECT_UNSET_FLAGS (new_dock, GDL_DOCK_AUTOMATIC);
+    
+    return GTK_WIDGET (new_dock);
+}
+
+/* Depending on where the dock item (where new item will be docked) locates
+ * in the dock, we might need to change the docking placement. If the
+ * item is does not touches the center of dock, the new-item-to-dock would
+ * require a center dock on this item.
+ */
+static GdlDockPlacement
+gdl_dock_refine_placement (GdlDock *dock, GdlDockItem *dock_item,
+                           GdlDockPlacement placement)
+{
+    GtkRequisition object_size;
+    
+    gdl_dock_item_preferred_size (dock_item, &object_size);
+    g_return_val_if_fail (GTK_WIDGET (dock)->allocation.width > 0, placement);
+    g_return_val_if_fail (GTK_WIDGET (dock)->allocation.height > 0, placement);
+    g_return_val_if_fail (object_size.width > 0, placement);
+    g_return_val_if_fail (object_size.height > 0, placement);
+
+    if (placement == GDL_DOCK_LEFT || placement == GDL_DOCK_RIGHT) {
+        /* Check if dock_object touches center in terms of width */
+        if (GTK_WIDGET (dock)->allocation.width/2 > object_size.width) {
+            return GDL_DOCK_CENTER;
+        }
+    } else if (placement == GDL_DOCK_TOP || placement == GDL_DOCK_BOTTOM) {
+        /* Check if dock_object touches center in terms of height */
+        if (GTK_WIDGET (dock)->allocation.height/2 > object_size.height) {
+            return GDL_DOCK_CENTER;
+        }
+    }
+    return placement;
+}
+
+/* Determines the larger item of the two based on the placement:
+ * for left/right placement, height determines it.
+ * for top/bottom placement, width determines it.
+ * for center placement, area determines it.
+ */
+static GdlDockItem*
+gdl_dock_select_larger_item (GdlDockItem *dock_item_1,
+                             GdlDockItem *dock_item_2,
+                             GdlDockPlacement placement,
+                             gint level /* for debugging */)
+{
+    GtkRequisition size_1, size_2;
+    
+    g_return_val_if_fail (dock_item_1 != NULL, dock_item_2);
+    g_return_val_if_fail (dock_item_2 != NULL, dock_item_1);
+    
+    gdl_dock_item_preferred_size (dock_item_1, &size_1);
+    gdl_dock_item_preferred_size (dock_item_2, &size_2);
+    
+    g_return_val_if_fail (size_1.width > 0, dock_item_2);
+    g_return_val_if_fail (size_1.height > 0, dock_item_2);
+    g_return_val_if_fail (size_2.width > 0, dock_item_1);
+    g_return_val_if_fail (size_2.height > 0, dock_item_1);
+    
+    if (placement == GDL_DOCK_LEFT || placement == GDL_DOCK_RIGHT)
+    {
+        /* For left/right placement, height is what matters */
+        return (size_1.height >= size_2.height?
+                    dock_item_1 : dock_item_2);
+    } else if (placement == GDL_DOCK_TOP || placement == GDL_DOCK_BOTTOM)
+    {
+        /* For top/bottom placement, width is what matters */
+        return (size_1.width >= size_2.width?
+                    dock_item_1 : dock_item_2);
+    } else if (placement == GDL_DOCK_CENTER) {
+        /* For center place, area is what matters */
+        return ((size_1.width * size_1.height)
+                    >= (size_2.width * size_2.height)?
+                    dock_item_1 : dock_item_2);
+    } else {
+        g_warning ("Should not reach here: %s:%d", __FUNCTION__, __LINE__);
+    }
+    return dock_item_1;
+}
+
+/* Determines the best dock item to dock a new item with the given placement.
+ * It traverses the dock tree and (based on the placement) tries to find
+ * the best located item wrt to the placement. The approach is to find the
+ * largest item on/around the placement side (for side placements) and to
+ * find the largest item for center placement. In most situations, this is
+ * what user wants and the heuristic should be therefore sufficient.
+ */
+static GdlDockItem*
+gdl_dock_find_best_placement_item (GdlDockItem *dock_item,
+                                   GdlDockPlacement placement,
+                                   gint level /* for debugging */)
+{
+    GdlDockItem *ret_item = NULL;
+    
+    if (GDL_IS_DOCK_PANED (dock_item))
+    {
+        GtkOrientation orientation;
+        GdlDockItem *dock_item_1, *dock_item_2;
+        GList* children;
+        
+        children = gtk_container_get_children (GTK_CONTAINER (dock_item));
+        
+        g_assert (g_list_length (children) == 2);
+        
+        g_object_get (dock_item, "orientation", &orientation, NULL);
+        if ((orientation == GTK_ORIENTATION_HORIZONTAL &&
+             placement == GDL_DOCK_LEFT) ||
+            (orientation == GTK_ORIENTATION_VERTICAL &&
+             placement == GDL_DOCK_TOP)) {
+            /* Return left or top pane widget */
+            ret_item =
+                gdl_dock_find_best_placement_item (GDL_DOCK_ITEM
+                                                   (children->data),
+                                                   placement, level + 1);
+        } else if ((orientation == GTK_ORIENTATION_HORIZONTAL &&
+                    placement == GDL_DOCK_RIGHT) ||
+                   (orientation == GTK_ORIENTATION_VERTICAL &&
+                    placement == GDL_DOCK_BOTTOM)) {
+                        /* Return right or top pane widget */
+            ret_item =
+                gdl_dock_find_best_placement_item (GDL_DOCK_ITEM
+                                                   (children->next->data),
+                                                   placement, level + 1);
+        } else {
+            /* Evaluate which of the two sides is bigger */
+            dock_item_1 =
+                gdl_dock_find_best_placement_item (GDL_DOCK_ITEM
+                                                   (children->data),
+                                                   placement, level + 1);
+            dock_item_2 =
+                gdl_dock_find_best_placement_item (GDL_DOCK_ITEM
+                                                   (children->next->data),
+                                                   placement, level + 1);
+            ret_item = gdl_dock_select_larger_item (dock_item_1,
+                                                    dock_item_2,
+                                                    placement, level);
+        }
+        g_list_free (children);
+    }
+    else if (GDL_IS_DOCK_ITEM (dock_item))
+    {
+        ret_item = dock_item;
+    }
+    else
+    {
+        /* should not be here */
+        g_warning ("Should not reach here: %s:%d", __FUNCTION__, __LINE__);
+    }
+    return ret_item;
+}
+
+void
+gdl_dock_add_item (GdlDock          *dock,
+                   GdlDockItem      *item,
+                   GdlDockPlacement  placement)
+{
+    g_return_if_fail (dock != NULL);
+    g_return_if_fail (item != NULL);
+
+    if (placement == GDL_DOCK_FLOATING)
+        /* Add the item to a new floating dock */
+        gdl_dock_add_floating_item (dock, item, 0, 0, -1, -1);
+
+    else {
+        GdlDockItem *best_dock_item;
+        /* Non-floating item. */
+        if (dock->root) {
+            GdlDockPlacement local_placement;
+            GtkRequisition preferred_size;
+            
+            best_dock_item =
+                gdl_dock_find_best_placement_item (GDL_DOCK_ITEM (dock->root),
+                                                   placement, 0);
+            local_placement = gdl_dock_refine_placement (dock, best_dock_item,
+                                                         placement);
+            gdl_dock_object_dock (GDL_DOCK_OBJECT (best_dock_item),
+                                  GDL_DOCK_OBJECT (item),
+                                  local_placement, NULL);
+        } else {
+            gdl_dock_object_dock (GDL_DOCK_OBJECT (dock),
+                                  GDL_DOCK_OBJECT (item),
+                                  placement, NULL);
+        }
+    }
+}
+
+void
+gdl_dock_add_floating_item (GdlDock        *dock,
+                            GdlDockItem    *item,
+                            gint            x,
+                            gint            y,
+                            gint            width,
+                            gint            height)
+{
+    GdlDock *new_dock;
+    
+    g_return_if_fail (dock != NULL);
+    g_return_if_fail (item != NULL);
+    
+    new_dock = GDL_DOCK (g_object_new (GDL_TYPE_DOCK, 
+                                       "master", GDL_DOCK_OBJECT_GET_MASTER (dock), 
+                                       "floating", TRUE,
+                                       "width", width,
+                                       "height", height,
+                                       "floatx", x,
+                                       "floaty", y,
+                                       NULL));
+    
+    if (GTK_WIDGET_VISIBLE (dock)) {
+        gtk_widget_show (GTK_WIDGET (new_dock));
+        if (GTK_WIDGET_MAPPED (dock))
+            gtk_widget_map (GTK_WIDGET (new_dock));
+        
+        /* Make the widget resize. */
+        gtk_widget_queue_resize (GTK_WIDGET (new_dock));
+    }
+
+    gdl_dock_add_item (GDL_DOCK (new_dock), item, GDL_DOCK_TOP);
+}
+
+GdlDockItem *
+gdl_dock_get_item_by_name (GdlDock     *dock,
+                           const gchar *name)
+{
+    GdlDockObject *found;
+    
+    g_return_val_if_fail (dock != NULL && name != NULL, NULL);
+    
+    /* proxy the call to our master */
+    found = gdl_dock_master_get_object (GDL_DOCK_OBJECT_GET_MASTER (dock), name);
+
+    return (found && GDL_IS_DOCK_ITEM (found)) ? GDL_DOCK_ITEM (found) : NULL;
+}
+
+GdlDockPlaceholder *
+gdl_dock_get_placeholder_by_name (GdlDock     *dock,
+                                  const gchar *name)
+{
+    GdlDockObject *found;
+    
+    g_return_val_if_fail (dock != NULL && name != NULL, NULL);
+    
+    /* proxy the call to our master */
+    found = gdl_dock_master_get_object (GDL_DOCK_OBJECT_GET_MASTER (dock), name);
+
+    return (found && GDL_IS_DOCK_PLACEHOLDER (found)) ?
+        GDL_DOCK_PLACEHOLDER (found) : NULL;
+}
+
+GList *
+gdl_dock_get_named_items (GdlDock *dock)
+{
+    GList *list = NULL;
+    
+    g_return_val_if_fail (dock != NULL, NULL);
+
+    gdl_dock_master_foreach (GDL_DOCK_OBJECT_GET_MASTER (dock),
+                             (GFunc) _gdl_dock_foreach_build_list, &list);
+
+    return list;
+}
+
+GdlDock *
+gdl_dock_object_get_toplevel (GdlDockObject *object)
+{
+    GdlDockObject *parent = object;
+    
+    g_return_val_if_fail (object != NULL, NULL);
+
+    while (parent && !GDL_IS_DOCK (parent))
+        parent = gdl_dock_object_get_parent_object (parent);
+
+    return parent ? GDL_DOCK (parent) : NULL;
+}
+
+void
+gdl_dock_xor_rect (GdlDock      *dock,
+                   GdkRectangle *rect)
+{
+    GtkWidget *widget;
+    gint8      dash_list [2];
+
+    widget = GTK_WIDGET (dock);
+
+    if (!dock->_priv->xor_gc) {
+        if (GTK_WIDGET_REALIZED (widget)) {
+            GdkGCValues values;
+
+            values.function = GDK_INVERT;
+            values.subwindow_mode = GDK_INCLUDE_INFERIORS;
+            dock->_priv->xor_gc = gdk_gc_new_with_values 
+                (widget->window, &values, GDK_GC_FUNCTION | GDK_GC_SUBWINDOW);
+        } else 
+            return;
+    };
+
+    gdk_gc_set_line_attributes (dock->_priv->xor_gc, 1,
+                                GDK_LINE_ON_OFF_DASH,
+                                GDK_CAP_NOT_LAST,
+                                GDK_JOIN_BEVEL);
+    
+    dash_list [0] = 1;
+    dash_list [1] = 1;
+    
+    gdk_gc_set_dashes (dock->_priv->xor_gc, 1, dash_list, 2);
+
+    gdk_draw_rectangle (widget->window, dock->_priv->xor_gc, 0, 
+                        rect->x, rect->y,
+                        rect->width, rect->height);
+
+    gdk_gc_set_dashes (dock->_priv->xor_gc, 0, dash_list, 2);
+
+    gdk_draw_rectangle (widget->window, dock->_priv->xor_gc, 0, 
+                        rect->x + 1, rect->y + 1,
+                        rect->width - 2, rect->height - 2);
+}
diff --git a/src/libgdl/gdl-dock.h b/src/libgdl/gdl-dock.h
new file mode 100644 (file)
index 0000000..7508fee
--- /dev/null
@@ -0,0 +1,99 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This file is part of the GNOME Devtools Libraries.
+ *
+ * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GDL_DOCK_H__
+#define __GDL_DOCK_H__
+
+#include <gtk/gtk.h>
+#include "libgdl/gdl-dock-object.h"
+#include "libgdl/gdl-dock-item.h"
+#include "libgdl/gdl-dock-placeholder.h"
+
+G_BEGIN_DECLS
+
+/* standard macros */
+#define GDL_TYPE_DOCK            (gdl_dock_get_type ())
+#define GDL_DOCK(obj)            (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK, GdlDock))
+#define GDL_DOCK_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK, GdlDockClass))
+#define GDL_IS_DOCK(obj)         (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK))
+#define GDL_IS_DOCK_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK))
+#define GDL_DOCK_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK, GdlDockClass))
+
+/* data types & structures */
+typedef struct _GdlDock        GdlDock;
+typedef struct _GdlDockClass   GdlDockClass;
+typedef struct _GdlDockPrivate GdlDockPrivate;
+
+struct _GdlDock {
+    GdlDockObject    object;
+
+    GdlDockObject   *root;
+
+    GdlDockPrivate  *_priv;
+};
+
+struct _GdlDockClass {
+    GdlDockObjectClass parent_class;
+
+    void  (* layout_changed)  (GdlDock *dock);    /* proxy signal for the master */
+};
+
+/* additional macros */
+#define GDL_DOCK_IS_CONTROLLER(dock)  \
+    (gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (dock)) == \
+     GDL_DOCK_OBJECT (dock))
+
+/* public interface */
+GtkWidget     *gdl_dock_new               (void);
+
+GtkWidget     *gdl_dock_new_from          (GdlDock          *original,
+                                           gboolean          floating);
+
+GType          gdl_dock_get_type          (void);
+
+void           gdl_dock_add_item          (GdlDock          *dock,
+                                           GdlDockItem      *item,
+                                           GdlDockPlacement  place);
+
+void           gdl_dock_add_floating_item (GdlDock        *dock,
+                                           GdlDockItem    *item,
+                                           gint            x,
+                                           gint            y,
+                                           gint            width,
+                                           gint            height);
+
+GdlDockItem   *gdl_dock_get_item_by_name  (GdlDock     *dock,
+                                           const gchar *name);
+
+GdlDockPlaceholder *gdl_dock_get_placeholder_by_name (GdlDock     *dock,
+                                                      const gchar *name);
+
+GList         *gdl_dock_get_named_items   (GdlDock    *dock);
+
+GdlDock       *gdl_dock_object_get_toplevel (GdlDockObject *object);
+
+void           gdl_dock_xor_rect            (GdlDock       *dock,
+                                             GdkRectangle  *rect);
+
+G_END_DECLS
+
+#endif
diff --git a/src/libgdl/gdl-i18n.c b/src/libgdl/gdl-i18n.c
new file mode 100644 (file)
index 0000000..5f92b66
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Devtools Library.
+ *
+ * The Gnome Devtools Library is free software; you can redistribute
+ * it and/or modify it under the terms of the GNU Library General
+ * Public License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * The Gnome Devtools Library is distributed in the hope that it will
+ * be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gdl-i18n.h"
+
+char *
+gdl_gettext (const char *msgid)
+{
+       static gboolean initialized = FALSE;
+
+       if (!initialized) {
+/*             bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); */
+               bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+               initialized = TRUE;
+       }
+
+       return dgettext (GETTEXT_PACKAGE, msgid);
+}
+
+
diff --git a/src/libgdl/gdl-i18n.h b/src/libgdl/gdl-i18n.h
new file mode 100644 (file)
index 0000000..1582e95
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Devtools Library.
+ *
+ * The Gnome Devtools Library is free software; you can redistribute
+ * it and/or modify it under the terms of the GNU Library General
+ * Public License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * The Gnome Devtools Library is distributed in the hope that it will
+ * be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+
+/*
+ * Handles all of the internationalization configuration options.
+ * Author: Tom Tromey <tromey@creche.cygnus.com>
+ */
+
+#ifndef __GDL_18N_H__
+#define __GDL_18N_H__ 1
+
+#include <glib.h>
+
+
+G_BEGIN_DECLS
+
+#ifdef ENABLE_NLS
+#    include <libintl.h>
+#    undef _
+#    define _(String) gdl_gettext (String)
+#    ifdef gettext_noop
+#        define N_(String) gettext_noop (String)
+#    else
+#        define N_(String) (String)
+#    endif
+#else
+/* Stubs that do something close enough.  */
+#    undef textdomain
+#    define textdomain(String) (String)
+#    undef gettext
+#    define gettext(String) (String)
+#    undef dgettext
+#    define dgettext(Domain,Message) (Message)
+#    undef dcgettext
+#    define dcgettext(Domain,Message,Type) (Message)
+#    undef bindtextdomain
+#    define bindtextdomain(Domain,Directory) (Domain)
+#    undef bind_textdomain_codeset
+#    define bind_textdomain_codeset(Domain,CodeSet) (Domain)
+#    undef _
+#    define _(String) (String)
+#    undef N_
+#    define N_(String) (String)
+#endif
+
+char *gdl_gettext (const char *msgid);
+
+G_END_DECLS
+
+#endif /* __GDL_I18N_H__ */
diff --git a/src/libgdl/gdl-stock-icons.h b/src/libgdl/gdl-stock-icons.h
new file mode 100644 (file)
index 0000000..2c760ef
--- /dev/null
@@ -0,0 +1,135 @@
+/* GdkPixbuf RGBA C-Source image dump */
+
+#ifdef __SUNPRO_C
+#pragma align 4 (stock_close_icon)
+#endif
+#ifdef __GNUC__
+static const guint8 stock_close_icon[] __attribute__ ((__aligned__ (4))) = 
+#else
+static const guint8 stock_close_icon[] = 
+#endif
+{ ""
+  /* Pixbuf magic (0x47646b50) */
+  "GdkP"
+  /* length: header (24) + pixel_data (576) */
+  "\0\0\2X"
+  /* pixdata_type (0x1010002) */
+  "\1\1\0\2"
+  /* rowstride (48) */
+  "\0\0\0""0"
+  /* width (12) */
+  "\0\0\0\14"
+  /* height (12) */
+  "\0\0\0\14"
+  /* pixel_data: */
+  "\0\0\0\27\0\0\0c\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\211\0"
+  "\0\0\211\0\0\0\211\0\0\0c\0\0\0\27\0\0\0\0\0\0\0c\0\0\0E\0\0\0\0\0\0"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0E\0\0\0c\0\0\0\0\0"
+  "\0\0\211\0\0\0\0\0\0\0:\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\211\0"
+  "\0\0:\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\211"
+  "\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\0\0\0\0\211\0"
+  "\0\0\0\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0"
+  "\211\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0"
+  "\0\0\0\0\0\0\0\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\211\0\0"
+  "\0\211\0\0\0\211\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211"
+  "\0\0\0\0\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\211\0"
+  "\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0:\0\0\0\211"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0:\0\0\0\0\0\0\0\211\0\0\0\0\0"
+  "\0\0c\0\0\0L\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0E\0\0\0c\0\0\0\0\0\0\0\27\0\0\0c\0\0\0\211\0\0\0\211\0\0\0\211"
+  "\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0c\0\0\0\27\0\0\0\0\0\0"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0\0\0\0\0\0\0\0\0\0"};
+
+
+/* GdkPixbuf RGBA C-Source image dump */
+
+#ifdef __SUNPRO_C
+#pragma align 4 (stock_menu_left_icon)
+#endif
+#ifdef __GNUC__
+static const guint8 stock_menu_left_icon[] __attribute__ ((__aligned__ (4))) = 
+#else
+static const guint8 stock_menu_left_icon[] = 
+#endif
+{ ""
+  /* Pixbuf magic (0x47646b50) */
+  "GdkP"
+  /* length: header (24) + pixel_data (576) */
+  "\0\0\2X"
+  /* pixdata_type (0x1010002) */
+  "\1\1\0\2"
+  /* rowstride (48) */
+  "\0\0\0""0"
+  /* width (12) */
+  "\0\0\0\14"
+  /* height (12) */
+  "\0\0\0\14"
+  /* pixel_data: */
+  "\0\0\0\27\0\0\0c\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\211\0"
+  "\0\0\211\0\0\0\211\0\0\0c\0\0\0\27\0\0\0\0\0\0\0c\0\0\0E\0\0\0\0\0\0"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0E\0\0\0c\0\0\0\0\0"
+  "\0\0\211\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0\0\0\17\0\0\0""5\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\0\0"
+  "\0\0\211\0\0\0\0\0\0\0\0\0\0\0\17\0\0\0""5\0\0\0\211\0\0\0\211\0\0\0"
+  "\211\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\14\0\0"
+  "\0\211\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0"
+  "\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\17\0\0\0""5\0\0\0\211\0"
+  "\0\0\211\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\17\0\0\0""5\0\0\0\211\0\0\0\0\0\0\0"
+  "\0\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0c\0\0\0L\0"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0E\0\0\0c"
+  "\0\0\0\0\0\0\0\27\0\0\0c\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\211\0\0"
+  "\0\211\0\0\0\211\0\0\0\211\0\0\0c\0\0\0\27\0\0\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0\0\0"};
+
+
+/* GdkPixbuf RGBA C-Source image dump */
+
+#ifdef __SUNPRO_C
+#pragma align 4 (stock_menu_right_icon)
+#endif
+#ifdef __GNUC__
+static const guint8 stock_menu_right_icon[] __attribute__ ((__aligned__ (4))) = 
+#else
+static const guint8 stock_menu_right_icon[] = 
+#endif
+{ ""
+  /* Pixbuf magic (0x47646b50) */
+  "GdkP"
+  /* length: header (24) + pixel_data (576) */
+  "\0\0\2X"
+  /* pixdata_type (0x1010002) */
+  "\1\1\0\2"
+  /* rowstride (48) */
+  "\0\0\0""0"
+  /* width (12) */
+  "\0\0\0\14"
+  /* height (12) */
+  "\0\0\0\14"
+  /* pixel_data: */
+  "\0\0\0\0\0\0\0\27\0\0\0c\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\211\0\0"
+  "\0\211\0\0\0\211\0\0\0\211\0\0\0c\0\0\0\27\0\0\0\0\0\0\0c\0\0\0E\0\0"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0E\0\0\0c\0"
+  "\0\0\0\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\0\0\0"
+  "\0\211\0\0\0""5\0\0\0\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\211\0"
+  "\0\0\0\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0"
+  "5\0\0\0\17\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0"
+  "\0\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\14\0\0\0\0"
+  "\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\211\0\0"
+  "\0\211\0\0\0""5\0\0\0\17\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211"
+  "\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0""5\0\0\0\17\0\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0c\0\0"
+  "\0E\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0L\0"
+  "\0\0c\0\0\0\0\0\0\0\27\0\0\0c\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\211"
+  "\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0c\0\0\0\27\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0"};
+
+
diff --git a/src/libgdl/gdl-stock.c b/src/libgdl/gdl-stock.c
new file mode 100644 (file)
index 0000000..7bcdd6b
--- /dev/null
@@ -0,0 +1,127 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- 
+ * gdl-stock.c
+ * 
+ * Copyright (C) 2003 Jeroen Zwartepoorte
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <gtk/gtkiconfactory.h>
+#include "gdl-stock.h"
+#include "gdl-stock-icons.h"
+
+static GtkIconFactory *gdl_stock_factory = NULL;
+
+static struct {
+       const gchar *stock_id;
+       const guint8 *icon_data;
+       const guint data_size;
+}
+gdl_icons[] = 
+{
+       { GDL_STOCK_CLOSE, stock_close_icon, sizeof (stock_close_icon) },
+       { GDL_STOCK_MENU_LEFT, stock_menu_left_icon, sizeof (stock_menu_left_icon) },
+       { GDL_STOCK_MENU_RIGHT, stock_menu_right_icon, sizeof (stock_menu_right_icon) }
+};
+
+static void
+icon_set_from_data (GtkIconSet       *set,
+                   const guint8     *icon_data,
+                   const guint       data_size,
+                   GtkIconSize       size,
+                   gboolean          fallback)
+{
+       GtkIconSource *source;
+       GdkPixbuf     *pixbuf;
+       GError        *err = NULL;
+
+       source = gtk_icon_source_new ();
+
+       gtk_icon_source_set_size (source, size);
+       gtk_icon_source_set_size_wildcarded (source, FALSE);
+       
+       pixbuf = gdk_pixbuf_new_from_inline (data_size, icon_data, FALSE, &err);
+       if (err) {
+           g_warning (err->message);
+           g_error_free (err);
+           err = NULL;
+           g_object_unref (source);
+           return;
+       }
+       
+       gtk_icon_source_set_pixbuf (source, pixbuf);
+       
+       g_object_unref (pixbuf);
+       
+       gtk_icon_set_add_source (set, source);
+       
+       if (fallback) {
+               gtk_icon_source_set_size_wildcarded (source, TRUE);
+               gtk_icon_set_add_source (set, source);
+       }
+       
+       gtk_icon_source_free (source);
+}
+
+static void
+add_icon (GtkIconFactory *factory,
+         const gchar    *stock_id,
+         const guint8   *icon_data, 
+         const guint     data_size)
+{
+       GtkIconSet *set;
+       gboolean    fallback = FALSE;
+
+       set = gtk_icon_factory_lookup (factory, stock_id);
+
+       if (!set) {
+               set = gtk_icon_set_new ();
+               gtk_icon_factory_add (factory, stock_id, set);
+               gtk_icon_set_unref (set);
+
+               fallback = TRUE;
+       }
+       
+       icon_set_from_data (set, icon_data, data_size, GTK_ICON_SIZE_MENU, fallback);
+}
+
+void
+gdl_stock_init (void)
+{
+       static gboolean initialized = FALSE;
+       gint i;
+
+       if (initialized)
+               return;
+
+       gdl_stock_factory = gtk_icon_factory_new ();
+
+       for (i = 0; i < G_N_ELEMENTS (gdl_icons); i++) {
+               add_icon (gdl_stock_factory,
+                         gdl_icons[i].stock_id,
+                         gdl_icons[i].icon_data, 
+                         gdl_icons[i].data_size);
+       }
+
+       gtk_icon_factory_add_default (gdl_stock_factory);
+
+       initialized = TRUE;
+}
diff --git a/src/libgdl/gdl-stock.h b/src/libgdl/gdl-stock.h
new file mode 100644 (file)
index 0000000..3ada63f
--- /dev/null
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- 
+ * gdl-stock.h
+ * 
+ * Copyright (C) 2003 Jeroen Zwartepoorte
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GDL_STOCK_H__
+#define __GDL_STOCK_H__
+
+G_BEGIN_DECLS
+
+#define GDL_STOCK_CLOSE                        "gdl-close"
+#define GDL_STOCK_MENU_LEFT            "gdl-menu-left"
+#define GDL_STOCK_MENU_RIGHT           "gdl-menu-right"
+
+void gdl_stock_init (void);
+
+G_END_DECLS
+
+#endif /* __GDL_STOCK_H__ */
diff --git a/src/libgdl/gdl-switcher.c b/src/libgdl/gdl-switcher.c
new file mode 100644 (file)
index 0000000..23f05b9
--- /dev/null
@@ -0,0 +1,1081 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 8 -*- */
+/* gdl-switcher.c
+ *
+ * Copyright (C) 2003  Ettore Perazzoli,
+ *               2007  Naba Kumar
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Copied and adapted from ESidebar.[ch] from evolution
+ * 
+ * Authors: Ettore Perazzoli <ettore@ximian.com>
+ *          Naba Kumar  <naba@gnome.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gdl-i18n.h"
+#include "gdl-switcher.h"
+#include "gdl-tools.h"
+#include "libgdlmarshal.h"
+#include "libgdltypebuiltins.h"
+
+#include <gtk/gtk.h>
+#include <gtk/gtkhbox.h>
+#include <gtk/gtkimage.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtktogglebutton.h>
+
+#if HAVE_GNOME
+#include <gconf/gconf-client.h>
+#include <libgnome/gnome-gconf.h>
+#endif
+
+static void gdl_switcher_set_property  (GObject            *object,
+                                        guint               prop_id,
+                                        const GValue       *value,
+                                        GParamSpec         *pspec);
+static void gdl_switcher_get_property  (GObject            *object,
+                                        guint               prop_id,
+                                        GValue             *value,
+                                        GParamSpec         *pspec);
+
+static void gdl_switcher_add_button  (GdlSwitcher *switcher,
+                                      const gchar *label,
+                                      const gchar *tooltips,
+                                      const gchar *stock_id,
+                                      const GdkPixbuf *pixbuf_icon,
+                                      gint switcher_id);
+static void gdl_switcher_remove_button (GdlSwitcher *switcher, gint switcher_id);
+static void gdl_switcher_select_page (GdlSwitcher *switcher, gint switcher_id);
+static void gdl_switcher_select_button (GdlSwitcher *switcher, gint switcher_id);
+static void gdl_switcher_set_show_buttons (GdlSwitcher *switcher, gboolean show);
+static void gdl_switcher_set_style (GdlSwitcher *switcher,
+                                    GdlSwitcherStyle switcher_style);
+static GdlSwitcherStyle gdl_switcher_get_style (GdlSwitcher *switcher);
+
+enum {
+    PROP_0,
+    PROP_SWITCHER_STYLE
+};
+
+typedef struct {
+    GtkWidget *button_widget;
+    GtkWidget *label;
+    GtkWidget *icon;
+    GtkWidget *arrow;
+    GtkWidget *hbox;
+    GtkTooltips *tooltips;
+    int id;
+} Button;
+
+struct _GdlSwitcherPrivate {
+    GdlSwitcherStyle switcher_style;
+    GdlSwitcherStyle toolbar_style;
+    
+    gboolean show;
+    GSList *buttons;
+
+    guint style_changed_id;
+    gint buttons_height_request;
+    gboolean in_toggle;
+};
+
+GDL_CLASS_BOILERPLATE (GdlSwitcher, gdl_switcher, GtkNotebook, GTK_TYPE_NOTEBOOK)
+
+#define INTERNAL_MODE(switcher)  (switcher->priv->switcher_style == \
+            GDL_SWITCHER_STYLE_TOOLBAR ? switcher->priv->toolbar_style : \
+            switcher->priv->switcher_style)
+
+#define H_PADDING 2
+#define V_PADDING 2
+
+/* Utility functions.  */
+
+static Button *
+button_new (GtkWidget *button_widget, GtkWidget *label, GtkWidget *icon,
+            GtkTooltips *tooltips, GtkWidget *arrow, GtkWidget *hbox, int id)
+{
+    Button *button = g_new (Button, 1);
+
+    button->button_widget = button_widget;
+    button->label = label;
+    button->icon = icon;
+    button->arrow = arrow;
+    button->hbox = hbox;
+    button->tooltips = tooltips;
+    button->id = id;
+
+    g_object_ref (button_widget);
+    g_object_ref (label);
+    g_object_ref (icon);
+    g_object_ref (arrow);
+    g_object_ref (hbox);
+    g_object_ref (tooltips);
+
+    return button;
+}
+
+static void
+button_free (Button *button)
+{
+    g_object_unref (button->button_widget);
+    g_object_unref (button->label);
+    g_object_unref (button->icon);
+    g_object_unref (button->hbox);
+    g_object_unref (button->tooltips);
+    g_free (button);
+}
+
+static gint
+gdl_switcher_get_page_id (GtkWidget *widget)
+{
+    static gint switcher_id_count = 0;
+    gint switcher_id;
+    switcher_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
+                                                      "__switcher_id"));
+    if (switcher_id <= 0) {
+        switcher_id = ++switcher_id_count;
+        g_object_set_data (G_OBJECT (widget), "__switcher_id",
+                           GINT_TO_POINTER (switcher_id));
+    }
+    return switcher_id;
+}
+
+static void
+update_buttons (GdlSwitcher *switcher, int new_selected_id)
+{
+    GSList *p;
+
+    switcher->priv->in_toggle = TRUE;
+
+    for (p = switcher->priv->buttons; p != NULL; p = p->next) {
+        Button *button = p->data;
+
+        if (button->id == new_selected_id) {
+            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
+                                          (button->button_widget), TRUE);
+            gtk_widget_set_sensitive (button->arrow, TRUE);
+        } else {
+            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
+                                          (button->button_widget), FALSE);
+            gtk_widget_set_sensitive (button->arrow, FALSE);
+        }
+    }
+
+    switcher->priv->in_toggle = FALSE;
+}
+
+/* Callbacks.  */
+
+static void
+button_toggled_callback (GtkToggleButton *toggle_button,
+                         GdlSwitcher *switcher)
+{
+    int id = 0;
+    gboolean is_active = FALSE;
+    GSList *p;
+
+    if (switcher->priv->in_toggle)
+        return;
+
+    switcher->priv->in_toggle = TRUE;
+
+    if (gtk_toggle_button_get_active (toggle_button))
+        is_active = TRUE;
+
+    for (p = switcher->priv->buttons; p != NULL; p = p->next) {
+        Button *button = p->data;
+
+        if (button->button_widget != GTK_WIDGET (toggle_button)) {
+            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
+                                          (button->button_widget), FALSE);
+            gtk_widget_set_sensitive (button->arrow, FALSE);
+        } else {
+            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
+                                          (button->button_widget), TRUE);
+            gtk_widget_set_sensitive (button->arrow, TRUE);
+            id = button->id;
+        }
+    }
+    
+    switcher->priv->in_toggle = FALSE;
+
+    if (is_active)
+    {
+        gdl_switcher_select_page (switcher, id);
+    }
+}
+
+/* Returns -1 if layout didn't happen because a resize request was queued */
+static int
+layout_buttons (GdlSwitcher *switcher)
+{
+    GtkRequisition client_requisition;
+    GtkAllocation *allocation = & GTK_WIDGET (switcher)->allocation;
+    GdlSwitcherStyle switcher_style;
+    gboolean icons_only;
+    int num_btns = g_slist_length (switcher->priv->buttons);
+    int btns_per_row;
+    GSList **rows, *p;
+    Button *button;
+    int row_number;
+    int max_btn_width = 0, max_btn_height = 0;
+    int optimal_layout_width = 0;
+    int row_last;
+    int x, y;
+    int i;
+    int rows_count;
+    int last_buttons_height;
+    
+    last_buttons_height = switcher->priv->buttons_height_request;
+    
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, size_request,
+                     (GTK_WIDGET (switcher), &client_requisition));
+
+    y = allocation->y + allocation->height - V_PADDING - 1;
+
+    if (num_btns == 0)
+        return y;
+
+    switcher_style = INTERNAL_MODE (switcher);
+    icons_only = (switcher_style == GDL_SWITCHER_STYLE_ICON);
+    
+    /* Figure out the max width and height */
+    optimal_layout_width = H_PADDING;
+    for (p = switcher->priv->buttons; p != NULL; p = p->next) {
+        GtkRequisition requisition;
+
+        button = p->data;
+        gtk_widget_size_request (GTK_WIDGET (button->button_widget),
+                                 &requisition);
+        optimal_layout_width += requisition.width + H_PADDING;
+        max_btn_height = MAX (max_btn_height, requisition.height);
+        max_btn_width = MAX (max_btn_width, requisition.width);    
+    }
+
+    /* Figure out how many rows and columns we'll use. */
+    btns_per_row = allocation->width / (max_btn_width + H_PADDING);
+    
+    /* If all the buttons could fit in the single row, have it so */
+    if (allocation->width >= optimal_layout_width)
+    {
+        btns_per_row = num_btns;
+    }
+    if (!icons_only) {
+        /* If using text buttons, we want to try to have a
+         * completely filled-in grid, but if we can't, we want
+         * the odd row to have just a single button.
+         */
+        while (num_btns % btns_per_row > 1)
+            btns_per_row--;
+    }
+
+    rows_count = num_btns / btns_per_row;
+    if (num_btns % btns_per_row != 0)
+        rows_count++;
+     
+    /* Assign buttons to rows */
+    rows = g_new0 (GSList *, rows_count);
+
+    if (!icons_only && num_btns % btns_per_row != 0) {
+        button = switcher->priv->buttons->data;
+        rows [0] = g_slist_append (rows [0], button->button_widget);
+
+        p = switcher->priv->buttons->next;
+        row_number = p ? 1 : 0;
+    } else {
+        p = switcher->priv->buttons;
+        row_number = 0;
+    }
+
+    for (; p != NULL; p = p->next) {
+        button = p->data;
+
+        if (g_slist_length (rows [row_number]) == btns_per_row)
+            row_number ++;
+
+        rows [row_number] = g_slist_append (rows [row_number],
+                                            button->button_widget);
+    }
+
+    row_last = row_number;
+
+    /* If there are more than 1 row of buttons, save the current height
+     * requirement for subsequent size requests.
+     */
+    if (row_last > 0)
+    {
+        switcher->priv->buttons_height_request =
+            (row_last + 1) * (max_btn_height + V_PADDING) + 1;
+    } else { /* Otherwize clear it */
+        if (last_buttons_height >= 0) {
+
+            switcher->priv->buttons_height_request = -1;
+        }
+    }
+    
+    /* If it turns out that we now require smaller height for the buttons
+     * than it was last time, make a resize request to ensure our
+     * size requisition is properly communicated to the parent (otherwise
+     * parent tend to keep assuming the older size).
+     */
+    if (last_buttons_height > switcher->priv->buttons_height_request)
+    {
+        gtk_widget_queue_resize (GTK_WIDGET (switcher));
+        return -1;
+    }
+    
+    /* Layout the buttons. */
+    for (i = row_last; i >= 0; i --) {
+        int len, extra_width;
+        
+        y -= max_btn_height;
+
+        /* Check for possible size over flow (taking into account client
+         * requisition
+         */
+        if (y < (allocation->y + client_requisition.height)) {
+            /* We have an overflow: Insufficient allocation */
+            if (last_buttons_height < switcher->priv->buttons_height_request) {
+                /* Request for a new resize */
+                gtk_widget_queue_resize (GTK_WIDGET (switcher));
+                return -1;
+            }
+        }
+        x = H_PADDING + allocation->x;
+        len = g_slist_length (rows[i]);
+        if (switcher_style == GDL_SWITCHER_STYLE_TEXT ||
+            switcher_style == GDL_SWITCHER_STYLE_BOTH)
+            extra_width = (allocation->width - (len * max_btn_width )
+                           - (len * H_PADDING)) / len;
+        else
+            extra_width = 0;
+        for (p = rows [i]; p != NULL; p = p->next) {
+            GtkAllocation child_allocation;
+            
+            child_allocation.x = x;
+            child_allocation.y = y;
+            if (rows_count == 1 && row_number == 0)
+            {
+                GtkRequisition child_requisition;
+                gtk_widget_size_request (GTK_WIDGET (p->data),
+                                         &child_requisition);
+                child_allocation.width = child_requisition.width;
+            }
+            else
+            {
+                child_allocation.width = max_btn_width + extra_width;
+            }
+            child_allocation.height = max_btn_height;
+
+            gtk_widget_size_allocate (GTK_WIDGET (p->data), &child_allocation);
+
+            x += child_allocation.width + H_PADDING;
+        }
+
+        y -= V_PADDING;
+    }
+    
+    for (i = 0; i <= row_last; i ++)
+        g_slist_free (rows [i]);
+    g_free (rows);
+
+    return y;
+}
+
+static void
+do_layout (GdlSwitcher *switcher)
+{
+    GtkAllocation *allocation = & GTK_WIDGET (switcher)->allocation;
+    GtkAllocation child_allocation;
+    int y;
+
+    if (switcher->priv->show) {
+        y = layout_buttons (switcher);
+        if (y < 0) /* Layout did not happen and a resize was requested */
+            return;
+    }
+    else
+        y = allocation->y + allocation->height;
+    
+    /* Place the parent widget.  */
+    child_allocation.x = allocation->x;
+    child_allocation.y = allocation->y;
+    child_allocation.width = allocation->width;
+    child_allocation.height = y - allocation->y;
+    
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, size_allocate,
+                     (GTK_WIDGET (switcher), &child_allocation));
+}
+
+/* GtkContainer methods.  */
+
+static void
+gdl_switcher_forall (GtkContainer *container, gboolean include_internals,
+                     GtkCallback callback, void *callback_data)
+{
+    GdlSwitcher *switcher =
+        GDL_SWITCHER (container);
+    GSList *p;
+    
+    GDL_CALL_PARENT (GTK_CONTAINER_CLASS, forall,
+                     (GTK_CONTAINER (switcher), include_internals,
+                      callback, callback_data));
+    if (include_internals) {
+        for (p = switcher->priv->buttons; p != NULL; p = p->next) {
+            GtkWidget *widget = ((Button *) p->data)->button_widget;
+            (* callback) (widget, callback_data);
+        }
+    }
+}
+
+static void
+gdl_switcher_remove (GtkContainer *container, GtkWidget *widget)
+{
+    gint switcher_id;
+    GdlSwitcher *switcher =
+        GDL_SWITCHER (container);
+    GSList *p;
+    
+    switcher_id = gdl_switcher_get_page_id (widget);
+    for (p = switcher->priv->buttons; p != NULL; p = p->next) {
+        Button *b = (Button *) p->data;
+
+        if (b->id == switcher_id) {
+            gtk_widget_unparent (b->button_widget);
+            switcher->priv->buttons =
+                g_slist_remove_link (switcher->priv->buttons, p);
+            button_free (b);
+            gtk_widget_queue_resize (GTK_WIDGET (switcher));
+            break;
+        }
+    }
+    GDL_CALL_PARENT (GTK_CONTAINER_CLASS, remove,
+                     (GTK_CONTAINER (switcher), widget));
+}
+
+/* GtkWidget methods.  */
+
+static void
+gdl_switcher_size_request (GtkWidget *widget, GtkRequisition *requisition)
+{
+    GdlSwitcher *switcher = GDL_SWITCHER (widget);
+    GSList *p;
+    gint button_height = 0;
+    
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, size_request,
+                     (GTK_WIDGET (switcher), requisition));
+
+    if (!switcher->priv->show)
+        return;
+    
+    for (p = switcher->priv->buttons; p != NULL; p = p->next) {
+        gint button_width;
+        Button *button = p->data;
+        GtkRequisition button_requisition;
+
+        gtk_widget_size_request (button->button_widget, &button_requisition);
+        button_width = button_requisition.width + 2 * H_PADDING;
+        requisition->width = MAX (requisition->width, button_width);
+        button_height = MAX (button_height,
+                             button_requisition.height + 2 * V_PADDING);
+    }
+    
+    if (switcher->priv->buttons_height_request > 0) {
+        requisition->height += switcher->priv->buttons_height_request;
+    } else {
+        requisition->height += button_height + V_PADDING;
+    }
+}
+
+static void
+gdl_switcher_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+    widget->allocation = *allocation;
+    do_layout (GDL_SWITCHER (widget));
+}
+
+static gint
+gdl_switcher_expose (GtkWidget *widget, GdkEventExpose *event)
+{
+    GSList *p;
+    GdlSwitcher *switcher = GDL_SWITCHER (widget);
+    if (switcher->priv->show) {
+        for (p = switcher->priv->buttons; p != NULL; p = p->next) {
+            GtkWidget *button = ((Button *) p->data)->button_widget;
+            gtk_container_propagate_expose (GTK_CONTAINER (widget),
+                                            button, event);
+        }
+    }
+    GDL_CALL_PARENT_WITH_DEFAULT (GTK_WIDGET_CLASS, expose_event,
+                                  (widget, event), FALSE);
+}
+
+static void
+gdl_switcher_map (GtkWidget *widget)
+{
+    GSList *p;
+    GdlSwitcher *switcher = GDL_SWITCHER (widget);
+    
+    if (switcher->priv->show) {
+        for (p = switcher->priv->buttons; p != NULL; p = p->next) {
+            GtkWidget *button = ((Button *) p->data)->button_widget;
+            gtk_widget_map (button);
+        }
+    }
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, map, (widget));
+}
+
+/* GObject methods.  */
+
+static void
+gdl_switcher_set_property  (GObject      *object,
+                            guint         prop_id,
+                            const GValue *value,
+                            GParamSpec   *pspec)
+{
+    GdlSwitcher *switcher = GDL_SWITCHER (object);
+
+    switch (prop_id) {
+        case PROP_SWITCHER_STYLE:
+            gdl_switcher_set_style (switcher, g_value_get_enum (value));
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+gdl_switcher_get_property  (GObject      *object,
+                            guint         prop_id,
+                            GValue       *value,
+                            GParamSpec   *pspec)
+{
+    GdlSwitcher *switcher = GDL_SWITCHER (object);
+
+    switch (prop_id) {
+        case PROP_SWITCHER_STYLE:
+            g_value_set_enum (value, gdl_switcher_get_style (switcher));
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+gdl_switcher_dispose (GObject *object)
+{
+    GdlSwitcherPrivate *priv = GDL_SWITCHER (object)->priv;
+    
+#if HAVE_GNOME
+    GConfClient *gconf_client = gconf_client_get_default ();
+    
+    if (priv->style_changed_id) {
+        gconf_client_notify_remove (gconf_client, priv->style_changed_id);
+        priv->style_changed_id = 0;
+    }
+    g_object_unref (gconf_client);
+#endif
+    
+    g_slist_foreach (priv->buttons, (GFunc) button_free, NULL);
+    g_slist_free (priv->buttons);
+    priv->buttons = NULL;
+
+    GDL_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
+}
+
+static void
+gdl_switcher_finalize (GObject *object)
+{
+    GdlSwitcherPrivate *priv = GDL_SWITCHER (object)->priv;
+
+    g_free (priv);
+
+    GDL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
+}
+
+/* Signal handlers */
+
+static void 
+gdl_switcher_notify_cb (GObject *g_object, GParamSpec *pspec,
+                        GdlSwitcher *switcher) 
+{
+    gboolean show_tabs;
+    g_return_if_fail (switcher != NULL && GDL_IS_SWITCHER (switcher));
+    show_tabs = gtk_notebook_get_show_tabs (GTK_NOTEBOOK (switcher));
+    gdl_switcher_set_show_buttons (switcher, !show_tabs);
+}
+
+static void
+gdl_switcher_switch_page_cb (GtkNotebook *nb, GtkNotebookPage *page,
+                             gint page_num, GdlSwitcher *switcher)
+{
+    GtkWidget       *page_widget;
+    GtkWidget       *tablabel;
+    gint             switcher_id;
+    
+    /* Change switcher button */
+    page_widget = gtk_notebook_get_nth_page (nb, page_num);
+    switcher_id = gdl_switcher_get_page_id (page_widget);
+    gdl_switcher_select_button (GDL_SWITCHER (switcher), switcher_id);
+}
+
+static void
+gdl_switcher_page_added_cb (GtkNotebook *nb, GtkWidget *page,
+                            gint page_num, GdlSwitcher *switcher)
+{
+    gint         switcher_id;
+    switcher_id = gdl_switcher_get_page_id (page);
+    
+    gdl_switcher_add_button (GDL_SWITCHER (switcher), NULL, NULL, NULL, NULL,
+                             switcher_id);
+    gdl_switcher_select_button (GDL_SWITCHER (switcher), switcher_id);
+}
+
+static void
+gdl_switcher_select_page (GdlSwitcher *switcher, gint id)
+{
+    GList *children, *node;
+    children = gtk_container_get_children (GTK_CONTAINER (switcher));
+    node = children;
+    while (node)
+    {
+        gint switcher_id;
+        switcher_id = gdl_switcher_get_page_id (GTK_WIDGET (node->data));
+        if (switcher_id == id)
+        {
+            gint page_num;
+            page_num = gtk_notebook_page_num (GTK_NOTEBOOK (switcher),
+                                              GTK_WIDGET (node->data));
+            g_signal_handlers_block_by_func (switcher,
+                                             gdl_switcher_switch_page_cb,
+                                             switcher);
+            gtk_notebook_set_current_page (GTK_NOTEBOOK (switcher), page_num);
+            g_signal_handlers_unblock_by_func (switcher,
+                                               gdl_switcher_switch_page_cb,
+                                               switcher);
+            break;
+        }
+        node = g_list_next (node);
+    }
+    g_list_free (children);
+}
+
+/* Initialization.  */
+
+static void
+gdl_switcher_class_init (GdlSwitcherClass *klass)
+{
+    GtkNotebookClass *notebook_class = GTK_NOTEBOOK_CLASS (klass);
+    GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    container_class->forall = gdl_switcher_forall;
+    container_class->remove = gdl_switcher_remove;
+
+    widget_class->size_request = gdl_switcher_size_request;
+    widget_class->size_allocate = gdl_switcher_size_allocate;
+    widget_class->expose_event = gdl_switcher_expose;
+    widget_class->map = gdl_switcher_map;
+    
+    object_class->dispose  = gdl_switcher_dispose;
+    object_class->finalize = gdl_switcher_finalize;
+    object_class->set_property = gdl_switcher_set_property;
+    object_class->get_property = gdl_switcher_get_property;
+    
+    g_object_class_install_property (
+        object_class, PROP_SWITCHER_STYLE,
+        g_param_spec_enum ("switcher-style", _("Switcher Style"),
+                           _("Switcher buttons style"),
+                           GDL_TYPE_SWITCHER_STYLE,
+                           GDL_SWITCHER_STYLE_BOTH,
+                           G_PARAM_READWRITE));
+}
+
+static void
+gdl_switcher_instance_init (GdlSwitcher *switcher)
+{
+    GdlSwitcherPrivate *priv;
+
+    GTK_WIDGET_SET_FLAGS (switcher, GTK_NO_WINDOW);
+  
+    priv = g_new0 (GdlSwitcherPrivate, 1);
+    switcher->priv = priv;
+
+    priv->show = TRUE;
+    priv->buttons_height_request = -1;
+
+    gtk_notebook_set_tab_pos (GTK_NOTEBOOK (switcher), GTK_POS_BOTTOM);
+    gtk_notebook_set_show_tabs (GTK_NOTEBOOK (switcher), FALSE);
+    gtk_notebook_set_show_border (GTK_NOTEBOOK (switcher), FALSE);
+    gdl_switcher_set_style (switcher, GDL_SWITCHER_STYLE_BOTH);
+    
+    /* notebook signals */
+    g_signal_connect (switcher, "switch-page",
+                      G_CALLBACK (gdl_switcher_switch_page_cb), switcher);
+    g_signal_connect (switcher, "page-added",
+                      G_CALLBACK (gdl_switcher_page_added_cb), switcher);
+    g_signal_connect (switcher, "notify::show-tabs",
+                      G_CALLBACK (gdl_switcher_notify_cb), switcher);
+}
+
+GtkWidget *
+gdl_switcher_new (void)
+{
+    GdlSwitcher *switcher = g_object_new (gdl_switcher_get_type (), NULL);
+    return GTK_WIDGET (switcher);
+}
+
+void
+gdl_switcher_add_button (GdlSwitcher *switcher, const gchar *label,
+                         const gchar *tooltips, const gchar *stock_id,
+                         const GdkPixbuf *pixbuf_icon, gint switcher_id)
+{
+    GtkWidget *button_widget;
+    GtkWidget *hbox;
+    GtkWidget *icon_widget;
+    GtkWidget *label_widget;
+    GtkWidget *arrow;
+    GtkTooltips *button_tooltips;
+    
+    button_widget = gtk_toggle_button_new ();
+    if (switcher->priv->show)
+        gtk_widget_show (button_widget);
+    g_signal_connect (button_widget, "toggled",
+                      G_CALLBACK (button_toggled_callback),
+                      switcher);
+    hbox = gtk_hbox_new (FALSE, 3);
+    gtk_container_set_border_width (GTK_CONTAINER (hbox), 0);
+    gtk_container_add (GTK_CONTAINER (button_widget), hbox);
+    gtk_widget_show (hbox);
+
+    if (stock_id)
+        icon_widget = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
+    else if (pixbuf_icon)
+        icon_widget = gtk_image_new_from_pixbuf (pixbuf_icon);
+    else
+        icon_widget = gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_BUTTON);
+
+    gtk_widget_show (icon_widget);
+    
+    if (!label) {
+        gchar *text = g_strdup_printf ("Item %d", switcher_id);
+        label_widget = gtk_label_new (text);
+        g_free (text);
+    } else {
+        label_widget = gtk_label_new (label);
+    }
+    gtk_misc_set_alignment (GTK_MISC (label_widget), 0.0, 0.5);
+    gtk_widget_show (label_widget);
+    button_tooltips = gtk_tooltips_new();
+    gtk_tooltips_set_tip (GTK_TOOLTIPS (button_tooltips), button_widget,
+                          tooltips, NULL);        
+
+    switch (INTERNAL_MODE (switcher)) {
+    case GDL_SWITCHER_STYLE_TEXT:
+        gtk_box_pack_start (GTK_BOX (hbox), label_widget, TRUE, TRUE, 0);
+        gtk_tooltips_disable (button_tooltips);
+        break;
+    case GDL_SWITCHER_STYLE_ICON:
+        gtk_box_pack_start (GTK_BOX (hbox), icon_widget, TRUE, TRUE, 0);
+        gtk_tooltips_enable (button_tooltips);
+        break;
+    case GDL_SWITCHER_STYLE_BOTH:
+    default:
+        gtk_box_pack_start (GTK_BOX (hbox), icon_widget, FALSE, TRUE, 0);
+        gtk_box_pack_start (GTK_BOX (hbox), label_widget, TRUE, TRUE, 0);
+        gtk_tooltips_disable (button_tooltips);
+        break;
+    }
+    arrow = gtk_arrow_new (GTK_ARROW_UP, GTK_SHADOW_NONE);
+    gtk_widget_show (arrow);
+    gtk_box_pack_start (GTK_BOX (hbox), arrow, FALSE, FALSE, 0);
+    
+    switcher->priv->buttons =
+        g_slist_append (switcher->priv->buttons,
+                        button_new (button_widget, label_widget,
+                                    icon_widget, button_tooltips,
+                                    arrow, hbox, switcher_id));
+    gtk_widget_set_parent (button_widget, GTK_WIDGET (switcher));
+
+    gtk_widget_queue_resize (GTK_WIDGET (switcher));
+}
+
+static void
+gdl_switcher_remove_button (GdlSwitcher *switcher, gint switcher_id)
+{
+    GSList *p;
+
+    for (p = switcher->priv->buttons; p != NULL; p = p->next) {
+        Button *button = p->data;
+
+        if (button->id == switcher_id)
+        {
+            gtk_container_remove (GTK_CONTAINER (switcher),
+                                  button->button_widget);
+            break;
+        }            
+    }
+    gtk_widget_queue_resize (GTK_WIDGET (switcher));
+}
+
+static void
+gdl_switcher_select_button (GdlSwitcher *switcher, gint switcher_id)
+{
+    update_buttons (switcher, switcher_id);
+    
+    /* Select the notebook page associated with this button */
+    gdl_switcher_select_page (switcher, switcher_id);
+}
+
+gint
+gdl_switcher_insert_page (GdlSwitcher *switcher, GtkWidget *page,
+                          GtkWidget *tab_widget, const gchar *label,
+                          const gchar *tooltips, const gchar *stock_id,
+                          const GdkPixbuf *pixbuf_icon, gint position)
+{
+    gint ret_position;
+    gint switcher_id;
+    g_signal_handlers_block_by_func (switcher,
+                                     gdl_switcher_page_added_cb,
+                                     switcher);
+    
+    if (!tab_widget) {
+        tab_widget = gtk_label_new (label);
+        gtk_widget_show (tab_widget);
+    }
+    switcher_id = gdl_switcher_get_page_id (page);
+    gdl_switcher_add_button (switcher, label, tooltips, stock_id, pixbuf_icon, switcher_id);
+    ret_position = gtk_notebook_insert_page (GTK_NOTEBOOK (switcher), page,
+                                             tab_widget, position);
+    g_signal_handlers_unblock_by_func (switcher,
+                                       gdl_switcher_page_added_cb,
+                                       switcher);
+    return ret_position;
+}
+
+static void
+set_switcher_style_internal (GdlSwitcher *switcher,
+                             GdlSwitcherStyle switcher_style )
+{
+    GSList *p;
+    
+    if (switcher_style == GDL_SWITCHER_STYLE_TABS &&
+        switcher->priv->show == FALSE)
+        return;
+
+    if (switcher_style == GDL_SWITCHER_STYLE_TABS)
+    {
+        gtk_notebook_set_show_tabs (GTK_NOTEBOOK (switcher), TRUE);
+        return;
+    }
+    
+    gtk_notebook_set_show_tabs (GTK_NOTEBOOK (switcher), FALSE);
+    
+    if (switcher_style == INTERNAL_MODE (switcher))
+        return;
+    
+    for (p = switcher->priv->buttons; p != NULL; p = p->next) {
+        Button *button = p->data;
+
+        gtk_container_remove (GTK_CONTAINER (button->hbox), button->arrow);
+        switch (switcher_style) {
+        case GDL_SWITCHER_STYLE_TEXT:
+            gtk_container_remove (GTK_CONTAINER (button->hbox), button->icon);
+            if (INTERNAL_MODE (switcher)
+                == GDL_SWITCHER_STYLE_ICON) {
+                gtk_box_pack_start (GTK_BOX (button->hbox), button->label,
+                                    TRUE, TRUE, 0);
+                gtk_widget_show (button->label);
+                gtk_tooltips_disable (button->tooltips);
+            }
+            break;
+        case GDL_SWITCHER_STYLE_ICON:
+            gtk_container_remove(GTK_CONTAINER (button->hbox), button->label);
+            if (INTERNAL_MODE (switcher)
+                == GDL_SWITCHER_STYLE_TEXT) {
+                gtk_box_pack_start (GTK_BOX (button->hbox), button->icon,
+                                    TRUE, TRUE, 0);
+                gtk_widget_show (button->icon);
+            } else
+                gtk_container_child_set (GTK_CONTAINER (button->hbox),
+                                         button->icon, "expand", TRUE, NULL);
+            gtk_tooltips_enable (button->tooltips);
+            break;
+        case GDL_SWITCHER_STYLE_BOTH:
+            if (INTERNAL_MODE (switcher)
+                == GDL_SWITCHER_STYLE_TEXT) {
+                gtk_container_remove (GTK_CONTAINER (button->hbox),
+                                      button->label);
+                gtk_box_pack_start (GTK_BOX (button->hbox), button->icon,
+                                    FALSE, TRUE, 0);
+                gtk_widget_show (button->icon);
+            } else {
+                gtk_container_child_set (GTK_CONTAINER (button->hbox),
+                                         button->icon, "expand", FALSE, NULL);
+            }
+
+            gtk_tooltips_disable (button->tooltips);
+            gtk_box_pack_start (GTK_BOX (button->hbox), button->label, TRUE,
+                                TRUE, 0);
+            gtk_widget_show (button->label);
+            break;
+        default:
+            break;
+        }
+        gtk_box_pack_start (GTK_BOX (button->hbox), button->arrow, FALSE,
+                            FALSE, 0);
+    }
+}
+
+#if HAVE_GNOME
+static GConfEnumStringPair toolbar_styles[] = {
+    { GDL_SWITCHER_STYLE_TEXT, "text" },
+    { GDL_SWITCHER_STYLE_ICON, "icons" },
+    { GDL_SWITCHER_STYLE_BOTH, "both" },
+    { GDL_SWITCHER_STYLE_BOTH, "both-horiz" },
+    { GDL_SWITCHER_STYLE_BOTH, "both_horiz" },
+    { -1, NULL }
+};
+
+static void
+style_changed_notify (GConfClient *gconf, guint id, GConfEntry *entry,
+                      void *data)
+{
+    GdlSwitcher *switcher = data;
+    char *val;
+    int switcher_style;    
+    
+    val = gconf_client_get_string (gconf,
+                                   "/desktop/gnome/interface/toolbar_style",
+                                   NULL);
+    if (val == NULL || !gconf_string_to_enum (toolbar_styles, val,
+                                              &switcher_style))
+        switcher_style = GDL_SWITCHER_STYLE_BOTH;
+    g_free(val);
+
+    set_switcher_style_internal (GDL_SWITCHER (switcher), switcher_style);
+    switcher->priv->toolbar_style = switcher_style;
+
+    gtk_widget_queue_resize (GTK_WIDGET (switcher));
+}
+
+static void
+gdl_switcher_set_style (GdlSwitcher *switcher, GdlSwitcherStyle switcher_style)
+{
+    GConfClient *gconf_client = gconf_client_get_default ();
+    
+    if (switcher_style == GDL_SWITCHER_STYLE_TABS &&
+        switcher->priv->show == FALSE)
+        return;
+    
+    if (switcher->priv->switcher_style == switcher_style &&
+        switcher->priv->show == TRUE)
+        return;
+
+    if (switcher->priv->switcher_style == GDL_SWITCHER_STYLE_TOOLBAR) {
+        if (switcher->priv->style_changed_id) {
+            gconf_client_notify_remove (gconf_client,
+                                switcher->priv->style_changed_id);
+            switcher->priv->style_changed_id = 0;
+        }        
+    }
+    
+    if (switcher_style != GDL_SWITCHER_STYLE_TOOLBAR) {
+        set_switcher_style_internal (switcher, switcher_style);
+
+        gtk_widget_queue_resize (GTK_WIDGET (switcher));
+    } else {
+        /* This is a little bit tricky, toolbar style is more
+         * of a meta-style where the actual style is dictated by
+         * the gnome toolbar setting, so that is why we have
+         * the is_toolbar_style bool - it tracks the toolbar
+         * style while the switcher_style member is the actual look and
+         * feel */
+        switcher->priv->style_changed_id =
+            gconf_client_notify_add (gconf_client,
+                                     "/desktop/gnome/interface/toolbar_style",
+                                     style_changed_notify, switcher,
+                                     NULL, NULL);
+        style_changed_notify (gconf_client, 0, NULL, switcher);
+    }
+    
+    g_object_unref (gconf_client);
+
+    if (switcher_style != GDL_SWITCHER_STYLE_TABS)
+        switcher->priv->switcher_style = switcher_style;
+}
+
+#else /* HAVE_GNOME */
+
+static void
+gdl_switcher_set_style (GdlSwitcher *switcher, GdlSwitcherStyle switcher_style)
+{
+    if (switcher_style == GDL_SWITCHER_STYLE_TABS &&
+        switcher->priv->show == FALSE)
+        return;
+    
+    if (switcher->priv->switcher_style == switcher_style &&
+        switcher->priv->show == TRUE)
+        return;
+
+    set_switcher_style_internal (switcher,
+                                 ((switcher_style ==
+                                   GDL_SWITCHER_STYLE_TOOLBAR)?
+                                  GDL_SWITCHER_STYLE_BOTH : switcher_style));
+    gtk_widget_queue_resize (GTK_WIDGET (switcher));
+    
+    if (switcher_style != GDL_SWITCHER_STYLE_TABS)
+        switcher->priv->switcher_style = switcher_style;
+}
+
+#endif /* HAVE_GNOME */
+
+static void
+gdl_switcher_set_show_buttons (GdlSwitcher *switcher, gboolean show)
+{
+    GSList *p;
+
+    if (switcher->priv->show == show)
+        return;
+    
+    for (p = switcher->priv->buttons; p != NULL; p = p->next) {
+        Button *button = p->data;
+
+        if (show)
+            gtk_widget_show (button->button_widget);
+        else
+            gtk_widget_hide (button->button_widget);
+    }
+
+    switcher->priv->show = show;
+
+    gtk_widget_queue_resize (GTK_WIDGET (switcher));
+}
+
+static GdlSwitcherStyle
+gdl_switcher_get_style (GdlSwitcher *switcher)
+{
+    if (!switcher->priv->show)
+        return GDL_SWITCHER_STYLE_TABS;
+    return switcher->priv->switcher_style;
+}
diff --git a/src/libgdl/gdl-switcher.h b/src/libgdl/gdl-switcher.h
new file mode 100644 (file)
index 0000000..be4b179
--- /dev/null
@@ -0,0 +1,73 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 8 -*- */
+/* gdl-switcher.h
+ *
+ * Copyright (C) 2003  Ettore Perazzoli
+ *               2007  Naba Kumar
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Ettore Perazzoli <ettore@ximian.com>
+ *          Naba Kumar  <naba@gnome.org>
+ */
+
+#ifndef _GDL_SWITCHER_H_
+#define _GDL_SWITCHER_H_
+
+#include <gtk/gtknotebook.h>
+
+G_BEGIN_DECLS
+
+#define GDL_TYPE_SWITCHER            (gdl_switcher_get_type ())
+#define GDL_SWITCHER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDL_TYPE_SWITCHER, GdlSwitcher))
+#define GDL_SWITCHER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GDL_TYPE_SWITCHER, GdlSwitcherClass))
+#define GDL_IS_SWITCHER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDL_TYPE_SWITCHER))
+#define GDL_IS_SWITCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), GDL_TYPE_SWITCHER))
+
+typedef struct _GdlSwitcher        GdlSwitcher;
+typedef struct _GdlSwitcherPrivate GdlSwitcherPrivate;
+typedef struct _GdlSwitcherClass   GdlSwitcherClass;
+
+typedef enum {
+    GDL_SWITCHER_STYLE_TEXT,
+    GDL_SWITCHER_STYLE_ICON,
+    GDL_SWITCHER_STYLE_BOTH,
+    GDL_SWITCHER_STYLE_TOOLBAR,
+    GDL_SWITCHER_STYLE_TABS
+} GdlSwitcherStyle;
+
+struct _GdlSwitcher {
+    GtkNotebook parent;
+
+    GdlSwitcherPrivate *priv;
+};
+
+struct _GdlSwitcherClass {
+    GtkNotebookClass parent_class;
+};
+
+GType      gdl_switcher_get_type     (void);
+GtkWidget *gdl_switcher_new          (void);
+
+gint       gdl_switcher_insert_page  (GdlSwitcher *switcher,
+                                      GtkWidget   *page,
+                                      GtkWidget   *tab_widget,
+                                      const gchar *label,
+                                      const gchar *tooltips,
+                                      const gchar *stock_id,
+                                      const GdkPixbuf *pixbuf_icon,
+                                      gint position);
+G_END_DECLS
+
+#endif /* _GDL_SWITCHER_H_ */
diff --git a/src/libgdl/gdl-tools.h b/src/libgdl/gdl-tools.h
new file mode 100644 (file)
index 0000000..32c2e4a
--- /dev/null
@@ -0,0 +1,184 @@
+/*  -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 
+ * This file is part of the GNOME Devtool Libraries
+ * 
+ * Copyright (C) 1999-2000 Dave Camp <dave@helixcode.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/* Miscellaneous GDL tools/macros */
+
+#ifndef __GDL_TOOLS_H__
+#define __GDL_TOOLS_H__
+
+#include <glib.h>
+#include <gtk/gtkwidget.h>
+
+/* FIXME: Toggle this */
+
+G_BEGIN_DECLS
+
+#define DO_GDL_TRACE
+
+#ifdef DO_GDL_TRACE
+
+#ifdef __GNUC__
+
+#define GDL_TRACE()  G_STMT_START {             \
+    g_log (G_LOG_DOMAIN,                      \
+          G_LOG_LEVEL_DEBUG,                 \
+          "file %s: line %d (%s)",           \
+          __FILE__,                          \
+          __LINE__,                          \
+          __PRETTY_FUNCTION__); } G_STMT_END 
+
+#define GDL_TRACE_EXTRA(format, args...) G_STMT_START {     \
+    g_log (G_LOG_DOMAIN,                      \
+          G_LOG_LEVEL_DEBUG,                 \
+          "file %s: line %d (%s): "format,   \
+          __FILE__,                          \
+          __LINE__,                          \
+          __PRETTY_FUNCTION__,               \
+          ##args); } G_STMT_END                   
+    
+#else /* __GNUC__ */
+
+#define GDL_TRACE()  G_STMT_START {             \
+    g_log (G_LOG_DOMAIN,                      \
+          G_LOG_LEVEL_DEBUG,                 \
+          "file %s: line %d",                \
+          __FILE__,                          \
+          __LINE__); } G_STMT_END 
+
+#define GDL_TRACE_EXTRA(format, args...) G_STMT_START {     \
+    g_log (G_LOG_DOMAIN,                      \
+          G_LOG_LEVEL_DEBUG,                 \
+          "file %s: line %d: "format,        \
+          __FILE__,                          \
+          __LINE__,                          \
+          ##args); } G_STMT_END                       
+#endif /* __GNUC__ */
+
+#else /* DO_GDL_TRACE */
+
+#define GDL_TRACE()
+#define GDL_TRACE_EXTRA()
+
+#endif /* DO_GDL_TRACE */
+
+/**
+ * Class boilerplate and base class call macros copied from
+ * bonobo/bonobo-macros.h.  Original copyright follows.
+ *
+ * Author:
+ *   Darin Adler <darin@bentspoon.com>
+ *
+ * Copyright 2001 Ben Tea Spoons, Inc.
+ */
+
+/* Macros for defining classes.  Ideas taken from Nautilus and GOB. */
+
+/* Define the boilerplate type stuff to reduce typos and code size.  Defines
+ * the get_type method and the parent_class static variable. */
+
+#define GDL_BOILERPLATE(type, type_as_function, corba_type,                     \
+                       parent_type, parent_type_macro,                         \
+                       register_type_macro)                                    \
+static void type_as_function ## _class_init    (type ## Class *klass);          \
+static void type_as_function ## _instance_init (type          *object);         \
+static parent_type ## Class *parent_class = NULL;                               \
+static void                                                                     \
+type_as_function ## _class_init_trampoline (gpointer klass,                     \
+                                           gpointer data)                      \
+{                                                                               \
+       parent_class = (parent_type ## Class *)g_type_class_ref (               \
+               parent_type_macro);                                             \
+       type_as_function ## _class_init ((type ## Class *)klass);               \
+}                                                                               \
+GType                                                                           \
+type_as_function ## _get_type (void)                                            \
+{                                                                               \
+       static GType object_type = 0;                                           \
+       if (object_type == 0) {                                                 \
+               static const GTypeInfo object_info = {                          \
+                   sizeof (type ## Class),                                     \
+                   NULL,               /* base_init */                         \
+                   NULL,               /* base_finalize */                     \
+                   type_as_function ## _class_init_trampoline,                 \
+                   NULL,               /* class_finalize */                    \
+                   NULL,               /* class_data */                        \
+                   sizeof (type),                                              \
+                   0,                  /* n_preallocs */                       \
+                   (GInstanceInitFunc) type_as_function ## _instance_init      \
+               };                                                              \
+               object_type = register_type_macro                               \
+                       (type, type_as_function, corba_type,                    \
+                        parent_type, parent_type_macro);                       \
+       }                                                                       \
+       return object_type;                                                     \
+}
+
+/* Just call the parent handler.  This assumes that there is a variable
+ * named parent_class that points to the (duh!) parent class.  Note that
+ * this macro is not to be used with things that return something, use
+ * the _WITH_DEFAULT version for that */
+#define GDL_CALL_PARENT(parent_class_cast, name, args)          \
+       ((parent_class_cast(parent_class)->name != NULL) ?      \
+        parent_class_cast(parent_class)->name args : (void)0)
+
+#define GDL_CALL_PARENT_GBOOLEAN(parent_class_cast, name, args)          \
+       ((parent_class_cast(parent_class)->name != NULL) ?      \
+        parent_class_cast(parent_class)->name args : (gboolean)0)
+
+
+/* Same as above, but in case there is no implementation, it evaluates
+ * to def_return */
+#define GDL_CALL_PARENT_WITH_DEFAULT(parent_class_cast,                 \
+                                    name, args, def_return)            \
+       ((parent_class_cast(parent_class)->name != NULL) ?              \
+        parent_class_cast(parent_class)->name args : def_return)
+
+/* Define the boilerplate type stuff to reduce typos and code size.  Defines
+ * the get_type method and the parent_class static variable. */
+#define GDL_CLASS_BOILERPLATE(type, type_as_function,           \
+                               parent_type, parent_type_macro) \
+       GDL_BOILERPLATE(type, type_as_function, type,           \
+                         parent_type, parent_type_macro,       \
+                         GDL_REGISTER_TYPE)
+#define GDL_REGISTER_TYPE(type, type_as_function, corba_type,                   \
+                         parent_type, parent_type_macro)                       \
+       g_type_register_static (parent_type_macro, #type, &object_info, 0)
+
+
+#define GDL_CALL_VIRTUAL(object, get_class_cast, method, args) \
+    (get_class_cast (object)->method ? (* get_class_cast (object)->method) args : (void)0)
+#define GDL_CALL_VIRTUAL_WITH_DEFAULT(object, get_class_cast, method, args, default) \
+    (get_class_cast (object)->method ? (* get_class_cast (object)->method) args : default)
+
+/* GdlPixmap structure and method have been copied from Evolution. */
+typedef struct _GdlPixmap {
+       const char *path;
+       const char *fname;
+       char       *pixbuf;
+} GdlPixmap;
+
+#define GDL_PIXMAP(path,fname) { (path), (fname), NULL }
+#define GDL_PIXMAP_END         { NULL, NULL, NULL }
+
+G_END_DECLS
+
+#endif
+
diff --git a/src/libgdl/layout.glade b/src/libgdl/layout.glade
new file mode 100644 (file)
index 0000000..87b7f9f
--- /dev/null
@@ -0,0 +1,283 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+<requires lib="gnome"/>
+
+<widget class="GtkDialog" id="layout_dialog">
+  <property name="border_width">4</property>
+  <property name="title" translatable="yes">Layout Managment</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">True</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="has_separator">True</property>
+
+  <child internal-child="vbox">
+    <widget class="GtkVBox" id="dialog-vbox1">
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">8</property>
+
+      <child internal-child="action_area">
+       <widget class="GtkHButtonBox" id="dialog-action_area1">
+         <property name="visible">True</property>
+         <property name="layout_style">GTK_BUTTONBOX_END</property>
+       </widget>
+       <packing>
+         <property name="padding">0</property>
+         <property name="expand">False</property>
+         <property name="fill">True</property>
+         <property name="pack_type">GTK_PACK_END</property>
+       </packing>
+      </child>
+
+      <child>
+       <widget class="GtkNotebook" id="layout_container">
+         <property name="visible">True</property>
+         <property name="can_focus">True</property>
+         <property name="show_tabs">True</property>
+         <property name="show_border">True</property>
+         <property name="tab_pos">GTK_POS_TOP</property>
+         <property name="scrollable">False</property>
+         <property name="enable_popup">False</property>
+
+         <child>
+           <widget class="GtkVBox" id="items_vbox">
+             <property name="visible">True</property>
+             <property name="homogeneous">False</property>
+             <property name="spacing">6</property>
+
+             <child>
+               <widget class="GtkScrolledWindow" id="scrolledwindow3">
+                 <property name="height_request">100</property>
+                 <property name="visible">True</property>
+                 <property name="can_focus">True</property>
+                 <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+                 <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                 <property name="shadow_type">GTK_SHADOW_IN</property>
+                 <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+                 <child>
+                   <widget class="GtkTreeView" id="items_list">
+                     <property name="visible">True</property>
+                     <property name="can_focus">True</property>
+                     <property name="headers_visible">True</property>
+                     <property name="rules_hint">True</property>
+                     <property name="reorderable">False</property>
+                     <property name="enable_search">True</property>
+                   </widget>
+                 </child>
+               </widget>
+               <packing>
+                 <property name="padding">0</property>
+                 <property name="expand">True</property>
+                 <property name="fill">True</property>
+               </packing>
+             </child>
+
+             <child>
+               <widget class="GtkCheckButton" id="locked_check">
+                 <property name="visible">True</property>
+                 <property name="can_focus">True</property>
+                 <property name="label" translatable="yes">_Lock dock items</property>
+                 <property name="use_underline">True</property>
+                 <property name="relief">GTK_RELIEF_NORMAL</property>
+                 <property name="active">False</property>
+                 <property name="inconsistent">False</property>
+                 <property name="draw_indicator">True</property>
+               </widget>
+               <packing>
+                 <property name="padding">0</property>
+                 <property name="expand">False</property>
+                 <property name="fill">False</property>
+               </packing>
+             </child>
+           </widget>
+           <packing>
+             <property name="tab_expand">False</property>
+             <property name="tab_fill">True</property>
+           </packing>
+         </child>
+
+         <child>
+           <widget class="GtkLabel" id="label1">
+             <property name="visible">True</property>
+             <property name="label" translatable="yes">Dock items</property>
+             <property name="use_underline">False</property>
+             <property name="use_markup">False</property>
+             <property name="justify">GTK_JUSTIFY_CENTER</property>
+             <property name="wrap">False</property>
+             <property name="selectable">False</property>
+             <property name="xalign">0.5</property>
+             <property name="yalign">0.5</property>
+             <property name="xpad">0</property>
+             <property name="ypad">0</property>
+           </widget>
+           <packing>
+             <property name="type">tab</property>
+           </packing>
+         </child>
+
+         <child>
+           <widget class="GtkVBox" id="layouts_vbox">
+             <property name="visible">True</property>
+             <property name="homogeneous">False</property>
+             <property name="spacing">6</property>
+
+             <child>
+               <widget class="GtkScrolledWindow" id="scrolledwindow4">
+                 <property name="height_request">100</property>
+                 <property name="visible">True</property>
+                 <property name="can_focus">True</property>
+                 <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+                 <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                 <property name="shadow_type">GTK_SHADOW_IN</property>
+                 <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+                 <child>
+                   <widget class="GtkTreeView" id="layouts_list">
+                     <property name="visible">True</property>
+                     <property name="can_focus">True</property>
+                     <property name="headers_visible">True</property>
+                     <property name="rules_hint">False</property>
+                     <property name="reorderable">False</property>
+                     <property name="enable_search">True</property>
+                   </widget>
+                 </child>
+               </widget>
+               <packing>
+                 <property name="padding">0</property>
+                 <property name="expand">True</property>
+                 <property name="fill">True</property>
+               </packing>
+             </child>
+
+             <child>
+               <widget class="GtkHButtonBox" id="hbuttonbox1">
+                 <property name="visible">True</property>
+                 <property name="layout_style">GTK_BUTTONBOX_END</property>
+                 <property name="spacing">6</property>
+
+                 <child>
+                   <widget class="GtkButton" id="delete_button">
+                     <property name="visible">True</property>
+                     <property name="can_default">True</property>
+                     <property name="can_focus">True</property>
+                     <property name="label">gtk-delete</property>
+                     <property name="use_stock">True</property>
+                     <property name="relief">GTK_RELIEF_NORMAL</property>
+                     <signal name="clicked" handler="on_delete_button_clicked" last_modification_time="Thu, 29 May 2003 18:32:55 GMT"/>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkButton" id="load_button">
+                     <property name="visible">True</property>
+                     <property name="can_default">True</property>
+                     <property name="can_focus">True</property>
+                     <property name="relief">GTK_RELIEF_NORMAL</property>
+                     <signal name="clicked" handler="on_load_button_clicked" last_modification_time="Fri, 30 May 2003 11:34:24 GMT"/>
+
+                     <child>
+                       <widget class="GtkAlignment" id="alignment2">
+                         <property name="visible">True</property>
+                         <property name="xalign">0.5</property>
+                         <property name="yalign">0.5</property>
+                         <property name="xscale">0</property>
+                         <property name="yscale">0</property>
+
+                         <child>
+                           <widget class="GtkHBox" id="hbox3">
+                             <property name="visible">True</property>
+                             <property name="homogeneous">False</property>
+                             <property name="spacing">2</property>
+
+                             <child>
+                               <widget class="GtkImage" id="image2">
+                                 <property name="visible">True</property>
+                                 <property name="stock">gtk-open</property>
+                                 <property name="icon_size">4</property>
+                                 <property name="xalign">0.5</property>
+                                 <property name="yalign">0.5</property>
+                                 <property name="xpad">0</property>
+                                 <property name="ypad">0</property>
+                               </widget>
+                               <packing>
+                                 <property name="padding">0</property>
+                                 <property name="expand">False</property>
+                                 <property name="fill">False</property>
+                               </packing>
+                             </child>
+
+                             <child>
+                               <widget class="GtkLabel" id="label4">
+                                 <property name="visible">True</property>
+                                 <property name="label" translatable="yes">_Load</property>
+                                 <property name="use_underline">True</property>
+                                 <property name="use_markup">False</property>
+                                 <property name="justify">GTK_JUSTIFY_LEFT</property>
+                                 <property name="wrap">False</property>
+                                 <property name="selectable">False</property>
+                                 <property name="xalign">0.5</property>
+                                 <property name="yalign">0.5</property>
+                                 <property name="xpad">0</property>
+                                 <property name="ypad">0</property>
+                               </widget>
+                               <packing>
+                                 <property name="padding">0</property>
+                                 <property name="expand">False</property>
+                                 <property name="fill">False</property>
+                               </packing>
+                             </child>
+                           </widget>
+                         </child>
+                       </widget>
+                     </child>
+                   </widget>
+                 </child>
+               </widget>
+               <packing>
+                 <property name="padding">0</property>
+                 <property name="expand">False</property>
+                 <property name="fill">True</property>
+               </packing>
+             </child>
+           </widget>
+           <packing>
+             <property name="tab_expand">False</property>
+             <property name="tab_fill">True</property>
+           </packing>
+         </child>
+
+         <child>
+           <widget class="GtkLabel" id="label2">
+             <property name="visible">True</property>
+             <property name="label" translatable="yes">Saved layouts</property>
+             <property name="use_underline">False</property>
+             <property name="use_markup">False</property>
+             <property name="justify">GTK_JUSTIFY_CENTER</property>
+             <property name="wrap">False</property>
+             <property name="selectable">False</property>
+             <property name="xalign">0.5</property>
+             <property name="yalign">0.5</property>
+             <property name="xpad">0</property>
+             <property name="ypad">0</property>
+           </widget>
+           <packing>
+             <property name="type">tab</property>
+           </packing>
+         </child>
+       </widget>
+       <packing>
+         <property name="padding">0</property>
+         <property name="expand">True</property>
+         <property name="fill">True</property>
+       </packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+</glade-interface>
diff --git a/src/libgdl/libgdl.h b/src/libgdl/libgdl.h
new file mode 100644 (file)
index 0000000..33f5207
--- /dev/null
@@ -0,0 +1,38 @@
+/*  -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 
+ * This file is part of the GNOME Devtools Libraries.
+ * 
+ * Copyright (C) 1999-2000 Dave Camp <dave@helixcode.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#ifndef __GDL_H__
+#define __GDL_H__
+
+#include "libgdl/gdl-tools.h"
+#include "libgdl/gdl-dock-object.h"
+#include "libgdl/gdl-dock-master.h"
+#include "libgdl/gdl-dock.h"
+#include "libgdl/gdl-dock-item.h"
+#include "libgdl/gdl-dock-paned.h"
+#include "libgdl/gdl-dock-notebook.h"
+#include "libgdl/gdl-dock-tablabel.h"
+#include "libgdl/gdl-dock-bar.h"
+#include "libgdl/gdl-combo-button.h"
+#include "libgdl/gdl-switcher.h"
+
+#endif
diff --git a/src/libgdl/libgdlmarshal.c b/src/libgdl/libgdlmarshal.c
new file mode 100644 (file)
index 0000000..06a4e75
--- /dev/null
@@ -0,0 +1,169 @@
+#include "libgdlmarshal.h"
+
+#include       <glib-object.h>
+
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v)     g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v)      g_value_get_int (v)
+#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
+#define g_marshal_value_peek_long(v)     g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
+#define g_marshal_value_peek_float(v)    g_value_get_float (v)
+#define g_marshal_value_peek_double(v)   g_value_get_double (v)
+#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v)    g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v)   g_value_get_object (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ *          Do not access GValues directly in your code. Instead, use the
+ *          g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
+#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
+#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
+#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* VOID:VOID (./libgdlmarshal.list:1) */
+
+/* VOID:INT,INT (./libgdlmarshal.list:2) */
+void
+gdl_marshal_VOID__INT_INT (GClosure     *closure,
+                           GValue       *return_value,
+                           guint         n_param_values,
+                           const GValue *param_values,
+                           gpointer      invocation_hint,
+                           gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__INT_INT) (gpointer     data1,
+                                              gint         arg_1,
+                                              gint         arg_2,
+                                              gpointer     data2);
+  register GMarshalFunc_VOID__INT_INT callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 3);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_VOID__INT_INT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_int (param_values + 1),
+            g_marshal_value_peek_int (param_values + 2),
+            data2);
+}
+
+/* VOID:UINT,UINT (./libgdlmarshal.list:3) */
+void
+gdl_marshal_VOID__UINT_UINT (GClosure     *closure,
+                             GValue       *return_value,
+                             guint         n_param_values,
+                             const GValue *param_values,
+                             gpointer      invocation_hint,
+                             gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__UINT_UINT) (gpointer     data1,
+                                                guint        arg_1,
+                                                guint        arg_2,
+                                                gpointer     data2);
+  register GMarshalFunc_VOID__UINT_UINT callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 3);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_VOID__UINT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_uint (param_values + 1),
+            g_marshal_value_peek_uint (param_values + 2),
+            data2);
+}
+
+/* VOID:BOOLEAN (./libgdlmarshal.list:4) */
+
+/* VOID:OBJECT,ENUM,BOXED (./libgdlmarshal.list:5) */
+void
+gdl_marshal_VOID__OBJECT_ENUM_BOXED (GClosure     *closure,
+                                     GValue       *return_value,
+                                     guint         n_param_values,
+                                     const GValue *param_values,
+                                     gpointer      invocation_hint,
+                                     gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__OBJECT_ENUM_BOXED) (gpointer     data1,
+                                                        gpointer     arg_1,
+                                                        gint         arg_2,
+                                                        gpointer     arg_3,
+                                                        gpointer     data2);
+  register GMarshalFunc_VOID__OBJECT_ENUM_BOXED callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 4);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_VOID__OBJECT_ENUM_BOXED) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_object (param_values + 1),
+            g_marshal_value_peek_enum (param_values + 2),
+            g_marshal_value_peek_boxed (param_values + 3),
+            data2);
+}
+
+/* VOID:BOXED (./libgdlmarshal.list:6) */
+
diff --git a/src/libgdl/libgdlmarshal.h b/src/libgdl/libgdlmarshal.h
new file mode 100644 (file)
index 0000000..3e0116e
--- /dev/null
@@ -0,0 +1,45 @@
+
+#ifndef __gdl_marshal_MARSHAL_H__
+#define __gdl_marshal_MARSHAL_H__
+
+#include       <glib-object.h>
+
+G_BEGIN_DECLS
+
+/* VOID:VOID (./libgdlmarshal.list:1) */
+#define gdl_marshal_VOID__VOID g_cclosure_marshal_VOID__VOID
+
+/* VOID:INT,INT (./libgdlmarshal.list:2) */
+extern void gdl_marshal_VOID__INT_INT (GClosure     *closure,
+                                       GValue       *return_value,
+                                       guint         n_param_values,
+                                       const GValue *param_values,
+                                       gpointer      invocation_hint,
+                                       gpointer      marshal_data);
+
+/* VOID:UINT,UINT (./libgdlmarshal.list:3) */
+extern void gdl_marshal_VOID__UINT_UINT (GClosure     *closure,
+                                         GValue       *return_value,
+                                         guint         n_param_values,
+                                         const GValue *param_values,
+                                         gpointer      invocation_hint,
+                                         gpointer      marshal_data);
+
+/* VOID:BOOLEAN (./libgdlmarshal.list:4) */
+#define gdl_marshal_VOID__BOOLEAN      g_cclosure_marshal_VOID__BOOLEAN
+
+/* VOID:OBJECT,ENUM,BOXED (./libgdlmarshal.list:5) */
+extern void gdl_marshal_VOID__OBJECT_ENUM_BOXED (GClosure     *closure,
+                                                 GValue       *return_value,
+                                                 guint         n_param_values,
+                                                 const GValue *param_values,
+                                                 gpointer      invocation_hint,
+                                                 gpointer      marshal_data);
+
+/* VOID:BOXED (./libgdlmarshal.list:6) */
+#define gdl_marshal_VOID__BOXED        g_cclosure_marshal_VOID__BOXED
+
+G_END_DECLS
+
+#endif /* __gdl_marshal_MARSHAL_H__ */
+
diff --git a/src/libgdl/libgdlmarshal.list b/src/libgdl/libgdlmarshal.list
new file mode 100644 (file)
index 0000000..f3200d6
--- /dev/null
@@ -0,0 +1,6 @@
+VOID:VOID
+VOID:INT,INT
+VOID:UINT,UINT
+VOID:BOOLEAN
+VOID:OBJECT,ENUM,BOXED
+VOID:BOXED
diff --git a/src/libgdl/libgdltypebuiltins.c b/src/libgdl/libgdltypebuiltins.c
new file mode 100644 (file)
index 0000000..5ec9ec8
--- /dev/null
@@ -0,0 +1,161 @@
+
+/* Generated data (by glib-mkenums) */
+
+#include <glib-object.h>
+#include "libgdltypebuiltins.h"
+
+
+/* enumerations from "gdl-dock-object.h" */
+static const GFlagsValue _gdl_dock_param_flags_values[] = {
+  { GDL_DOCK_PARAM_EXPORT, "GDL_DOCK_PARAM_EXPORT", "export" },
+  { GDL_DOCK_PARAM_AFTER, "GDL_DOCK_PARAM_AFTER", "after" },
+  { 0, NULL, NULL }
+};
+
+GType
+gdl_dock_param_flags_get_type (void)
+{
+  static GType type = 0;
+
+  if (!type)
+    type = g_flags_register_static ("GdlDockParamFlags", _gdl_dock_param_flags_values);
+
+  return type;
+}
+
+static const GFlagsValue _gdl_dock_object_flags_values[] = {
+  { GDL_DOCK_AUTOMATIC, "GDL_DOCK_AUTOMATIC", "automatic" },
+  { GDL_DOCK_ATTACHED, "GDL_DOCK_ATTACHED", "attached" },
+  { GDL_DOCK_IN_REFLOW, "GDL_DOCK_IN_REFLOW", "in-reflow" },
+  { GDL_DOCK_IN_DETACH, "GDL_DOCK_IN_DETACH", "in-detach" },
+  { 0, NULL, NULL }
+};
+
+GType
+gdl_dock_object_flags_get_type (void)
+{
+  static GType type = 0;
+
+  if (!type)
+    type = g_flags_register_static ("GdlDockObjectFlags", _gdl_dock_object_flags_values);
+
+  return type;
+}
+
+static const GEnumValue _gdl_dock_placement_values[] = {
+  { GDL_DOCK_NONE, "GDL_DOCK_NONE", "none" },
+  { GDL_DOCK_TOP, "GDL_DOCK_TOP", "top" },
+  { GDL_DOCK_BOTTOM, "GDL_DOCK_BOTTOM", "bottom" },
+  { GDL_DOCK_RIGHT, "GDL_DOCK_RIGHT", "right" },
+  { GDL_DOCK_LEFT, "GDL_DOCK_LEFT", "left" },
+  { GDL_DOCK_CENTER, "GDL_DOCK_CENTER", "center" },
+  { GDL_DOCK_FLOATING, "GDL_DOCK_FLOATING", "floating" },
+  { 0, NULL, NULL }
+};
+
+GType
+gdl_dock_placement_get_type (void)
+{
+  static GType type = 0;
+
+  if (!type)
+    type = g_enum_register_static ("GdlDockPlacement", _gdl_dock_placement_values);
+
+  return type;
+}
+
+
+/* enumerations from "gdl-dock-item.h" */
+static const GFlagsValue _gdl_dock_item_behavior_values[] = {
+  { GDL_DOCK_ITEM_BEH_NORMAL, "GDL_DOCK_ITEM_BEH_NORMAL", "normal" },
+  { GDL_DOCK_ITEM_BEH_NEVER_FLOATING, "GDL_DOCK_ITEM_BEH_NEVER_FLOATING", "never-floating" },
+  { GDL_DOCK_ITEM_BEH_NEVER_VERTICAL, "GDL_DOCK_ITEM_BEH_NEVER_VERTICAL", "never-vertical" },
+  { GDL_DOCK_ITEM_BEH_NEVER_HORIZONTAL, "GDL_DOCK_ITEM_BEH_NEVER_HORIZONTAL", "never-horizontal" },
+  { GDL_DOCK_ITEM_BEH_LOCKED, "GDL_DOCK_ITEM_BEH_LOCKED", "locked" },
+  { GDL_DOCK_ITEM_BEH_CANT_DOCK_TOP, "GDL_DOCK_ITEM_BEH_CANT_DOCK_TOP", "cant-dock-top" },
+  { GDL_DOCK_ITEM_BEH_CANT_DOCK_BOTTOM, "GDL_DOCK_ITEM_BEH_CANT_DOCK_BOTTOM", "cant-dock-bottom" },
+  { GDL_DOCK_ITEM_BEH_CANT_DOCK_LEFT, "GDL_DOCK_ITEM_BEH_CANT_DOCK_LEFT", "cant-dock-left" },
+  { GDL_DOCK_ITEM_BEH_CANT_DOCK_RIGHT, "GDL_DOCK_ITEM_BEH_CANT_DOCK_RIGHT", "cant-dock-right" },
+  { GDL_DOCK_ITEM_BEH_CANT_DOCK_CENTER, "GDL_DOCK_ITEM_BEH_CANT_DOCK_CENTER", "cant-dock-center" },
+  { GDL_DOCK_ITEM_BEH_CANT_CLOSE, "GDL_DOCK_ITEM_BEH_CANT_CLOSE", "cant-close" },
+  { GDL_DOCK_ITEM_BEH_CANT_ICONIFY, "GDL_DOCK_ITEM_BEH_CANT_ICONIFY", "cant-iconify" },
+  { GDL_DOCK_ITEM_BEH_NO_GRIP, "GDL_DOCK_ITEM_BEH_NO_GRIP", "no-grip" },
+  { 0, NULL, NULL }
+};
+
+GType
+gdl_dock_item_behavior_get_type (void)
+{
+  static GType type = 0;
+
+  if (!type)
+    type = g_flags_register_static ("GdlDockItemBehavior", _gdl_dock_item_behavior_values);
+
+  return type;
+}
+
+static const GFlagsValue _gdl_dock_item_flags_values[] = {
+  { GDL_DOCK_IN_DRAG, "GDL_DOCK_IN_DRAG", "in-drag" },
+  { GDL_DOCK_IN_PREDRAG, "GDL_DOCK_IN_PREDRAG", "in-predrag" },
+  { GDL_DOCK_ICONIFIED, "GDL_DOCK_ICONIFIED", "iconified" },
+  { GDL_DOCK_USER_ACTION, "GDL_DOCK_USER_ACTION", "user-action" },
+  { 0, NULL, NULL }
+};
+
+GType
+gdl_dock_item_flags_get_type (void)
+{
+  static GType type = 0;
+
+  if (!type)
+    type = g_flags_register_static ("GdlDockItemFlags", _gdl_dock_item_flags_values);
+
+  return type;
+}
+
+
+/* enumerations from "gdl-dock-bar.h" */
+static const GEnumValue _gdl_dock_bar_style_values[] = {
+  { GDL_DOCK_BAR_ICONS, "GDL_DOCK_BAR_ICONS", "icons" },
+  { GDL_DOCK_BAR_TEXT, "GDL_DOCK_BAR_TEXT", "text" },
+  { GDL_DOCK_BAR_BOTH, "GDL_DOCK_BAR_BOTH", "both" },
+  { GDL_DOCK_BAR_AUTO, "GDL_DOCK_BAR_AUTO", "auto" },
+  { 0, NULL, NULL }
+};
+
+GType
+gdl_dock_bar_style_get_type (void)
+{
+  static GType type = 0;
+
+  if (!type)
+    type = g_enum_register_static ("GdlDockBarStyle", _gdl_dock_bar_style_values);
+
+  return type;
+}
+
+
+/* enumerations from "gdl-switcher.h" */
+static const GEnumValue _gdl_switcher_style_values[] = {
+  { GDL_SWITCHER_STYLE_TEXT, "GDL_SWITCHER_STYLE_TEXT", "text" },
+  { GDL_SWITCHER_STYLE_ICON, "GDL_SWITCHER_STYLE_ICON", "icon" },
+  { GDL_SWITCHER_STYLE_BOTH, "GDL_SWITCHER_STYLE_BOTH", "both" },
+  { GDL_SWITCHER_STYLE_TOOLBAR, "GDL_SWITCHER_STYLE_TOOLBAR", "toolbar" },
+  { GDL_SWITCHER_STYLE_TABS, "GDL_SWITCHER_STYLE_TABS", "tabs" },
+  { 0, NULL, NULL }
+};
+
+GType
+gdl_switcher_style_get_type (void)
+{
+  static GType type = 0;
+
+  if (!type)
+    type = g_enum_register_static ("GdlSwitcherStyle", _gdl_switcher_style_values);
+
+  return type;
+}
+
+
+/* Generated data ends here */
+
diff --git a/src/libgdl/libgdltypebuiltins.h b/src/libgdl/libgdltypebuiltins.h
new file mode 100644 (file)
index 0000000..22b28cf
--- /dev/null
@@ -0,0 +1,38 @@
+
+/* Generated data (by glib-mkenums) */
+
+#ifndef __LIBGDLTYPEBUILTINS_H__
+#define __LIBGDLTYPEBUILTINS_H__ 1
+
+#include "libgdl/libgdl.h"
+
+G_BEGIN_DECLS
+
+
+/* --- gdl-dock-object.h --- */
+#define GDL_TYPE_DOCK_PARAM_FLAGS gdl_dock_param_flags_get_type()
+GType gdl_dock_param_flags_get_type (void);
+#define GDL_TYPE_DOCK_OBJECT_FLAGS gdl_dock_object_flags_get_type()
+GType gdl_dock_object_flags_get_type (void);
+#define GDL_TYPE_DOCK_PLACEMENT gdl_dock_placement_get_type()
+GType gdl_dock_placement_get_type (void);
+
+/* --- gdl-dock-item.h --- */
+#define GDL_TYPE_DOCK_ITEM_BEHAVIOR gdl_dock_item_behavior_get_type()
+GType gdl_dock_item_behavior_get_type (void);
+#define GDL_TYPE_DOCK_ITEM_FLAGS gdl_dock_item_flags_get_type()
+GType gdl_dock_item_flags_get_type (void);
+
+/* --- gdl-dock-bar.h --- */
+#define GDL_TYPE_DOCK_BAR_STYLE gdl_dock_bar_style_get_type()
+GType gdl_dock_bar_style_get_type (void);
+
+/* --- gdl-switcher.h --- */
+#define GDL_TYPE_SWITCHER_STYLE gdl_switcher_style_get_type()
+GType gdl_switcher_style_get_type (void);
+G_END_DECLS
+
+#endif /* __LIBGDLTYPEBUILTINS_H__ */
+
+/* Generated data ends here */
+
diff --git a/src/libgdl/makefile b/src/libgdl/makefile
new file mode 100644 (file)
index 0000000..abe183f
--- /dev/null
@@ -0,0 +1,17 @@
+# Convenience stub makefile to call the real Makefile.
+
+
+
+# Explicit so that it's the default rule.
+all:
+       cd .. && $(MAKE) libgdl/all
+
+clean %.a %.o:
+       cd .. && $(MAKE) libgdl/$@
+
+.PHONY: all clean
+
+OBJEXT = o
+
+.SUFFIXES:
+.SUFFIXES: .a .$(OBJEXT)
diff --git a/src/libgdl/makefile.in b/src/libgdl/makefile.in
new file mode 100644 (file)
index 0000000..ebaf339
--- /dev/null
@@ -0,0 +1,17 @@
+# Convenience stub makefile to call the real Makefile.
+
+@SET_MAKE@
+
+# Explicit so that it's the default rule.
+all:
+       cd .. && $(MAKE) libgdl/all
+
+clean %.a %.o:
+       cd .. && $(MAKE) libgdl/$@
+
+.PHONY: all clean
+
+OBJEXT = @OBJEXT@
+
+.SUFFIXES:
+.SUFFIXES: .a .$(OBJEXT)
index 457fa692631ff73d074c156bd059120262ef0f3f..208b822f239fe5860f01e7ce4010695fddd758be 100644 (file)
@@ -134,14 +134,14 @@ static char const preferences_skeleton[] =
 "  <group id=\"dialogs\">\n"
 "    <group id=\"toolbox\"/>\n"
 "    <group id=\"fillstroke\"/>\n"
+"    <group id=\"filtereffects\"/>\n"
 "    <group id=\"textandfont\"/>\n"
 "    <group id=\"transformation\" applyseparately=\"0\"/>\n"
 "    <group id=\"align\"/>\n"
 "    <group id=\"xml\"/>\n"
 "    <group id=\"find\"/>\n"
-"    <group \n"
-"      id=\"documentoptions\" />\n"
-"    <group id=\"preferences\"/>\n"
+"    <group id=\"documentoptions\" state=\"1\"/>\n"
+"    <group id=\"preferences\" state=\"1\"/>\n"
 "    <group id=\"gradienteditor\"/>\n"
 "    <group id=\"object\"/>\n"
 "    <group id=\"export\">\n"
@@ -162,7 +162,7 @@ static char const preferences_skeleton[] =
 "    <group id=\"treeeditor\" />\n"
 "    <group id=\"layers\" maxDepth=\"20\"/>\n"
 "    <group id=\"extensioneditor\" />\n"
-"    <group id=\"trace\" />\n"
+"    <group id=\"trace\" state=\"1\" />\n"
 "    <group id=\"script\" />\n"
 "    <group id=\"input\" />\n"
 "    <group id=\"colorpickerwindow\" />\n"
@@ -195,6 +195,7 @@ static char const preferences_skeleton[] =
 "    <group id=\"autoscrollspeed\" value=\"0.7\"/>\n"
 "    <group id=\"autoscrolldistance\" value=\"-10\"/>\n"
 "    <group id=\"simplifythreshold\" value=\"0.002\"/>\n"
+"    <group id=\"dialogtype\" value=\"1\"/>\n"
 "    <group id=\"dialogsskiptaskbar\" value=\"1\"/>\n"
 #ifdef WIN32 // FIXME: Temporary Win32 special code to enable transient dialogs
 "    <group id=\"dialogsontopwin32\" value=\"0\"/>\n"
index d3bb8b62c1fecbe67e97607d78c24ce85002c57e..6679e99deca0f8725a7b99cb3b91d40b893b6644 100644 (file)
@@ -10,12 +10,17 @@ ui_dialog_libuidialog_a_SOURCES =           \
        ui/dialog/dialog-manager.h              \
        ui/dialog/dialog.cpp                    \
        ui/dialog/dialog.h                      \
+       ui/dialog/behavior.h                    \
+       ui/dialog/dock-behavior.h               \
+       ui/dialog/dock-behavior.cpp             \
+       ui/dialog/floating-behavior.h           \
+       ui/dialog/floating-behavior.cpp         \
        ui/dialog/align-and-distribute.cpp      \
        ui/dialog/align-and-distribute.h        \
        ui/dialog/document-metadata.cpp         \
        ui/dialog/document-metadata.h           \
        ui/dialog/document-properties.cpp       \
-       ui/dialog/document-properties.h \
+       ui/dialog/document-properties.h         \
        ui/dialog/export.cpp                    \
        ui/dialog/export.h                      \
        ui/dialog/extension-editor.cpp          \
index 34fa910ccd710199ea8d4ab700c1ead725bd4bb2..924a987907a6591dd8d664e291845dcc3c134ae8 100644 (file)
@@ -774,8 +774,8 @@ void on_selection_changed(Inkscape::Application *inkscape, Inkscape::Selection *
 
 
 
-AlignAndDistribute::AlignAndDistribute() 
-    : Dialog ("dialogs.align", SP_VERB_DIALOG_ALIGN_DISTRIBUTE),
+AlignAndDistribute::AlignAndDistribute(Behavior::BehaviorFactory behavior_factory
+    : Dialog (behavior_factory, "dialogs.align", SP_VERB_DIALOG_ALIGN_DISTRIBUTE),
       randomize_bbox(NR::Nothing()),
       _alignFrame(_("Align")),
       _distributeFrame(_("Distribute")),
index 6d22071f3a532b148e9a0a5b69ebb11a577090dc..0559a11e92a0a6b713af85c7a0dfd6860a6d311b 100644 (file)
@@ -46,10 +46,11 @@ class Action;
 
 class AlignAndDistribute : public Dialog {
 public:
-    AlignAndDistribute();
+    AlignAndDistribute(Behavior::BehaviorFactory behavior_factory);
     virtual ~AlignAndDistribute();
 
-    static AlignAndDistribute *create() { return new AlignAndDistribute(); }
+    static AlignAndDistribute *create(Behavior::BehaviorFactory behavior_factory) 
+    { return new AlignAndDistribute(behavior_factory); }
 
     enum AlignTarget { LAST=0, FIRST, BIGGEST, SMALLEST, PAGE, DRAWING, SELECTION };
 
diff --git a/src/ui/dialog/behavior.h b/src/ui/dialog/behavior.h
new file mode 100644 (file)
index 0000000..b7455bb
--- /dev/null
@@ -0,0 +1,108 @@
+/**
+ * \brief Dialog behavior interface
+ *
+ * Author:
+ *   Gustav Broberg <broberg@kth.se>
+ *
+ * Copyright (C) 2007 Authors
+ *
+ * Released under GNU GPL.  Read the file 'COPYING' for more information.
+ */
+
+#ifndef INKSCAPE_UI_DIALOG_BEHAVIOR_H
+#define INKSCAPE_UI_DIALOG_BEHAVIOR_H
+
+#include <gtkmm/button.h>
+#include <gtkmm/box.h>
+
+class SPDesktop;
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+class Dialog;
+
+namespace Behavior {
+
+class Behavior;
+
+typedef Behavior *(*BehaviorFactory)(Dialog& dialog);
+
+template <typename T>
+Behavior *create(Dialog& dialog)
+{
+    return T::create(dialog);
+}
+
+
+class Behavior {
+
+public:
+    virtual ~Behavior() { }
+
+    /** Gtk::Dialog methods */
+    virtual operator Gtk::Widget&() =0;
+    virtual GtkWidget *gobj() =0;
+    virtual void present() =0;
+    virtual Gtk::VBox *get_vbox() =0;
+    virtual void show() =0;
+    virtual void hide() =0;
+    virtual void show_all_children() =0;
+    virtual void resize(int width, int height) =0;
+    virtual void move(int x, int y) =0;
+    virtual void set_position(Gtk::WindowPosition) =0;
+    virtual void set_size_request(int width, int height) =0;
+    virtual void size_request(Gtk::Requisition& requisition) =0;
+    virtual void get_position(int& x, int& y) =0;
+    virtual void get_size(int& width, int& height) =0;
+    virtual void set_title(Glib::ustring title) =0;
+    virtual void set_response_sensitive(int response_id, bool setting) =0;
+    virtual void set_sensitive(bool sensitive) =0;
+    virtual Gtk::Button *add_button(const Glib::ustring& button_text, int response_id) =0;
+    virtual Gtk::Button *add_button(const Gtk::StockID& stock_id, int response_id) =0;
+    virtual void set_default_response(int response_id) =0;
+
+    /** Gtk::Dialog signal proxies */
+    virtual Glib::SignalProxy0<void> signal_show() =0;
+    virtual Glib::SignalProxy0<void> signal_hide() =0;
+    virtual Glib::SignalProxy1<bool, GdkEventAny *> signal_delete_event() =0;
+    virtual Glib::SignalProxy1<void, int> signal_response() =0;
+
+    /** Custom signal handlers */
+    virtual void onHideF12() =0;
+    virtual void onShowF12() =0;
+    virtual void onShutdown() =0;
+    virtual void onDesktopActivated(SPDesktop *desktop) =0;
+
+protected:
+    Behavior(Dialog& dialog)
+        : _dialog (dialog)
+    { }
+        
+    Dialog& _dialog;  //< reference to the owner
+
+private:
+    Behavior(); // no constructor without params
+    Behavior(const Behavior&);            // no copy
+    Behavior& operator=(const Behavior&); // no assign
+};
+
+} // namespace Behavior
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+
+
+#endif //INKSCAPE_UI_DIALOG_BEHAVIOR_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
index 23c64f1d5fa01c3f63d8a7f253060807f0892c7c..e5d326efe9a87e1c051bbc5beb0ab056138ad1d0 100644 (file)
@@ -2,11 +2,12 @@
  * \brief Object for managing a set of dialogs, including their signals and
  *        construction/caching/destruction of them.
  *
- * Author:
+ * Authors:
  *   Bryce W. Harrington <bryce@bryceharrington.org>
  *   Jon Phillips <jon@rejon.org>
+ *   Gustav Broberg <broberg@kth.se>
  *
- * Copyright (C) 2004, 2005 Authors
+ * Copyright (C) 2004--2007 Authors
  *
  * Released under GNU GPL.  Read the file 'COPYING' for more information.
  */
 #include "ui/dialog/undo-history.h"
 #include "ui/dialog/xml-editor.h"
 
+#include "dialogs/layers-panel.h"
 #include "dialogs/tiledialog.h"
+#include "dialogs/iconpreview.h"
+
+#include "ui/dialog/floating-behavior.h"
+#include "ui/dialog/dock-behavior.h"
 
 namespace Inkscape {
 namespace UI {
@@ -45,8 +51,8 @@ namespace Dialog {
 
 namespace {
 
-template <typename T>
-Dialog *create() { return T::create(); }
+template <typename T, typename B>
+inline Dialog *create() { return T::create(&B::create); }
 
 }
 
@@ -71,26 +77,63 @@ Dialog *create() { return T::create(); }
  *
  */
 DialogManager::DialogManager() {
-    registerFactory("AlignAndDistribute",  &create<AlignAndDistribute>);
-    registerFactory("DocumentMetadata",    &create<DocumentMetadata>);
-    registerFactory("DocumentProperties",  &create<DocumentProperties>);
-    registerFactory("Export",              &create<Export>);
-    registerFactory("ExtensionEditor",     &create<ExtensionEditor>);
-    registerFactory("FillAndStroke",       &create<FillAndStroke>);
-    registerFactory("FilterEffectsDialog", &create<FilterEffectsDialog>);
-    registerFactory("Find",                &create<Find>);
-    registerFactory("InkscapePreferences", &create<InkscapePreferences>);
-    registerFactory("LayerEditor",         &create<LayerEditor>);
-    registerFactory("LivePathEffect",      &create<LivePathEffectEditor>);
-    registerFactory("Memory",              &create<Memory>);
-    registerFactory("Messages",            &create<Messages>);
-    registerFactory("Script",              &create<ScriptDialog>);
-    registerFactory("TextProperties",      &create<TextProperties>);
-    registerFactory("TileDialog",          &create<TileDialog>);
-    registerFactory("Trace",               &create<TraceDialog>);
-    registerFactory("Transformation",      &create<Transformation>);
-    registerFactory("UndoHistory",         &create<UndoHistory>);
-    registerFactory("XmlEditor",           &create<XmlEditor>);
+
+    using namespace Behavior;
+    using namespace Inkscape::UI::Dialogs; // temporary
+
+    int dialogs_type = prefs_get_int_attribute_limited ("options.dialogtype", "value", DOCK, 0, 1);
+
+    if (dialogs_type == FLOATING) {
+
+        registerFactory("AlignAndDistribute",  &create<AlignAndDistribute,   FloatingBehavior>);
+        registerFactory("DocumentMetadata",    &create<DocumentMetadata,     FloatingBehavior>);
+        registerFactory("DocumentProperties",  &create<DocumentProperties,   FloatingBehavior>);
+        registerFactory("Export",              &create<Export,               FloatingBehavior>);
+        registerFactory("ExtensionEditor",     &create<ExtensionEditor,      FloatingBehavior>);
+        registerFactory("FillAndStroke",       &create<FillAndStroke,        FloatingBehavior>);
+        registerFactory("FilterEffectsDialog", &create<FilterEffectsDialog,  FloatingBehavior>);
+        registerFactory("Find",                &create<Find,                 FloatingBehavior>);
+        registerFactory("IconPreviewPanel",    &create<IconPreviewPanel,     FloatingBehavior>);
+        registerFactory("InkscapePreferences", &create<InkscapePreferences,  FloatingBehavior>);
+        registerFactory("LayerEditor",         &create<LayerEditor,          FloatingBehavior>);
+        registerFactory("LayersPanel",         &create<LayersPanel,          FloatingBehavior>);
+        registerFactory("LivePathEffect",      &create<LivePathEffectEditor, FloatingBehavior>);
+        registerFactory("Memory",              &create<Memory,               FloatingBehavior>);
+        registerFactory("Messages",            &create<Messages,             FloatingBehavior>);
+        registerFactory("Script",              &create<ScriptDialog,         FloatingBehavior>);
+        registerFactory("TextProperties",      &create<TextProperties,       FloatingBehavior>);
+        registerFactory("TileDialog",          &create<TileDialog,           FloatingBehavior>);
+        registerFactory("Trace",               &create<TraceDialog,          FloatingBehavior>);
+        registerFactory("Transformation",      &create<Transformation,       FloatingBehavior>);
+        registerFactory("UndoHistory",         &create<UndoHistory,          FloatingBehavior>);
+        registerFactory("XmlEditor",           &create<XmlEditor,            FloatingBehavior>);
+
+    } else {
+
+        registerFactory("AlignAndDistribute",  &create<AlignAndDistribute,   DockBehavior>);
+        registerFactory("DocumentMetadata",    &create<DocumentMetadata,     DockBehavior>);
+        registerFactory("DocumentProperties",  &create<DocumentProperties,   DockBehavior>);
+        registerFactory("Export",              &create<Export,               DockBehavior>);
+        registerFactory("ExtensionEditor",     &create<ExtensionEditor,      DockBehavior>);
+        registerFactory("FillAndStroke",       &create<FillAndStroke,        DockBehavior>);
+        registerFactory("FilterEffectsDialog", &create<FilterEffectsDialog,  DockBehavior>);
+        registerFactory("Find",                &create<Find,                 DockBehavior>);
+        registerFactory("IconPreviewPanel",    &create<IconPreviewPanel,     DockBehavior>);
+        registerFactory("InkscapePreferences", &create<InkscapePreferences,  DockBehavior>);
+        registerFactory("LayerEditor",         &create<LayerEditor,          DockBehavior>);
+        registerFactory("LayersPanel",         &create<LayersPanel,          DockBehavior>);
+        registerFactory("LivePathEffect",      &create<LivePathEffectEditor, DockBehavior>);
+        registerFactory("Memory",              &create<Memory,               DockBehavior>);
+        registerFactory("Messages",            &create<Messages,             DockBehavior>);
+        registerFactory("Script",              &create<ScriptDialog,         DockBehavior>);
+        registerFactory("TextProperties",      &create<TextProperties,       DockBehavior>);
+        registerFactory("TileDialog",          &create<TileDialog,           DockBehavior>);
+        registerFactory("Trace",               &create<TraceDialog,          DockBehavior>);
+        registerFactory("Transformation",      &create<Transformation,       DockBehavior>);
+        registerFactory("UndoHistory",         &create<UndoHistory,          DockBehavior>);
+        registerFactory("XmlEditor",           &create<XmlEditor,            DockBehavior>);
+
+    }
 }
 
 DialogManager::~DialogManager() {
@@ -163,7 +206,6 @@ void DialogManager::showDialog(GQuark name) {
     Dialog *dialog=getDialog(name);
     if (dialog) {
         dialog->present();
-        dialog->read_geometry();
     }
 }
 
index ac147d65652230b5fbf53e55f01206de641ade64..16124d9f534b31c964e73cf5ecc1f7b1cb29d01f 100644 (file)
@@ -2,12 +2,13 @@
  * \brief Base class for dialogs in Inkscape.  This class provides certain
  *        common behaviors and styles wanted of all dialogs in the application.
  *
- * Author:
+ * Authors:
  *   Bryce W. Harrington <bryce@bryceharrington.org>
  *   buliabyak@gmail.com 
  *   Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
+ *   Gustav Broberg <broberg@kth.se>
  *
- * Copyright (C) 2004-2007 Authors
+ * Copyright (C) 2004--2007 Authors
  *
  * Released under GNU GPL.  Read the file 'COPYING' for more information.
  */
 #include "interface.h"
 #include "verbs.h"
 
-
 #define MIN_ONSCREEN_DISTANCE 50
 
 
-
 namespace Inkscape {
 namespace UI {
 namespace Dialog {
 
-static gboolean
-sp_retransientize_again (gpointer dlgPtr)
-{
-    Dialog *dlg = (Dialog *)dlgPtr;
-    dlg->retransientize_suppress = false;
-    return FALSE; // so that it is only called once
-}
-
 static void
 sp_retransientize (Inkscape::Application *inkscape, SPDesktop *desktop, gpointer dlgPtr)
 {
@@ -63,13 +54,6 @@ sp_dialog_shutdown (GtkObject *object, gpointer dlgPtr)
     dlg->onShutdown();
 }
 
-void 
-Dialog::present()
-{
-    _user_hidden = false;
-    Gtk::Dialog::present();
-}
-
 void
 Dialog::save_geometry()
 {
@@ -78,7 +62,7 @@ Dialog::save_geometry()
     get_position(x, y);
     get_size(w, h);
 
-//    g_print ("write %d %d %d %d\n", x, y, w, h);
+    // g_print ("write %d %d %d %d\n", x, y, w, h);
 
     if (x<0) x=0;
     if (y<0) y=0;
@@ -87,8 +71,27 @@ Dialog::save_geometry()
     prefs_set_int_attribute (_prefs_path, "y", y);
     prefs_set_int_attribute (_prefs_path, "w", w);
     prefs_set_int_attribute (_prefs_path, "h", h);
+
 }
 
+void hideCallback(GtkObject *object, gpointer dlgPtr)
+{
+    g_return_if_fail( dlgPtr != NULL );
+
+    Dialog *dlg = (Dialog *)dlgPtr;
+    dlg->onHideF12();
+}
+
+void unhideCallback(GtkObject *object, gpointer dlgPtr)
+{
+    g_return_if_fail( dlgPtr != NULL );
+
+    Dialog *dlg = (Dialog *)dlgPtr;
+    dlg->onShowF12();
+}
+
+
+
 void
 Dialog::read_geometry()
 {
@@ -99,12 +102,12 @@ Dialog::read_geometry()
     int w = prefs_get_int_attribute (_prefs_path, "w", 0);
     int h = prefs_get_int_attribute (_prefs_path, "h", 0);
 
-//    g_print ("read %d %d %d %d\n", x, y, w, h);
+    // g_print ("read %d %d %d %d\n", x, y, w, h);
 
     // If there are stored height and width values for the dialog,
     // resize the window to match; otherwise we leave it at its default
     if (w != 0 && h != 0) {
-        resize (w, h);
+        resize(w, h);
     }
     
     // If there are stored values for where the dialog should be
@@ -116,22 +119,47 @@ Dialog::read_geometry()
         // ...otherwise just put it in the middle of the screen
         set_position(Gtk::WIN_POS_CENTER);
     }
-}
-
-void hideCallback(GtkObject *object, gpointer dlgPtr)
-{
-    g_return_if_fail( dlgPtr != NULL );
 
-    Dialog *dlg = (Dialog *)dlgPtr;
-    dlg->onHideF12();
 }
 
-void unhideCallback(GtkObject *object, gpointer dlgPtr)
+inline Dialog::operator Gtk::Widget&()                           { return *_behavior; }
+inline GtkWidget *Dialog::gobj()                                 { return _behavior->gobj(); }
+inline void Dialog::present()                                    { _behavior->present(); }
+inline Gtk::VBox *Dialog::get_vbox()                             {  return _behavior->get_vbox(); }
+inline void Dialog::show_all_children()                          { _behavior->show_all_children(); }
+inline void Dialog::hide()                                       { _behavior->hide(); }
+inline void Dialog::show()                                       { _behavior->show(); }
+inline void Dialog::set_size_request(int width, int height)      { _behavior->set_size_request(width, height); }
+inline void Dialog::size_request(Gtk::Requisition& requisition)  { _behavior->size_request(requisition); }
+inline void Dialog::get_position(int& x, int& y)                 { _behavior->get_position(x, y); }
+inline void Dialog::get_size(int& width, int& height)            { _behavior->get_size(width, height); }
+inline void Dialog::resize(int width, int height)                { _behavior->resize(width, height); }
+inline void Dialog::move(int x, int y)                           { _behavior->move(x, y); }
+inline void Dialog::set_position(Gtk::WindowPosition position)   { _behavior->set_position(position); }
+inline void Dialog::set_title(Glib::ustring title)               { _behavior->set_title(title); }
+inline void Dialog::set_sensitive(bool sensitive)                { _behavior->set_sensitive(sensitive); }
+
+inline void Dialog::set_response_sensitive(int response_id, bool setting)
+{ _behavior->set_response_sensitive(response_id, setting); }
+
+void Dialog::set_resizable(bool) { }
+void Dialog::set_default(Gtk::Widget&) { }
+
+inline void Dialog::set_default_response(int response_id) { _behavior->set_default_response(response_id); }
+
+Glib::SignalProxy0<void> Dialog::signal_show () { return _behavior->signal_show(); }
+Glib::SignalProxy0<void> Dialog::signal_hide () { return _behavior->signal_hide(); }
+Glib::SignalProxy1<void, int> Dialog::signal_response () { return _behavior->signal_response(); }
+
+Gtk::Button* Dialog::add_button (const Glib::ustring& button_text, int response_id) 
+{ return _behavior->add_button(button_text, response_id); }
+
+Gtk::Button* Dialog::add_button (const Gtk::StockID& stock_id, int response_id)
+{ return _behavior->add_button(stock_id, response_id); }
+
+Dialog::Dialog(const char *prefs_path, int verb_num, const char *apply_label)
 {
-    g_return_if_fail( dlgPtr != NULL );
 
-    Dialog *dlg = (Dialog *)dlgPtr;
-    dlg->onShowF12();
 }
 
 //=====================================================================
@@ -146,34 +174,24 @@ void unhideCallback(GtkObject *object, gpointer dlgPtr)
  * It also provides some general purpose signal handlers for things like
  * showing and hiding all dialogs.
  */
-Dialog::Dialog(const char *prefs_path, int verb_num, const char *apply_label)
+Dialog::Dialog(Behavior::BehaviorFactory behavior_factory, const char *prefs_path, int verb_num,
+               const char *apply_label) 
+    : _hiddenF12 (false),
+      _prefs_path (prefs_path),
+      _verb_num(verb_num),
+      _apply_label (apply_label)
 {
-    hide();
-    set_has_separator(false);
-
-    _prefs_path = prefs_path;
-
-    if (prefs_get_int_attribute ("dialogs", "showclose", 0) || apply_label) {
-        // TODO: make the order of buttons obey the global preference
-        if (apply_label) {
-            add_button(Glib::ustring(apply_label), Gtk::RESPONSE_APPLY);
-            set_default_response(Gtk::RESPONSE_APPLY);
-        }
-       add_button(Gtk::Stock::CLOSE, Gtk::RESPONSE_CLOSE);
-    }
-
-    GtkWidget *dlg = GTK_WIDGET(gobj());
+    gchar title[500];
 
     if (verb_num)
-    {
-        gchar title[500];
         sp_ui_dialog_title_string (Inkscape::Verb::get(verb_num), title);
-        set_title(title);
-    }
 
-    gtk_signal_connect( GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC(sp_dialog_event_handler), dlg );
+    _title = title;
+
+    _behavior = behavior_factory(*this);
+
+    gtk_signal_connect( GTK_OBJECT (gobj()), "event", GTK_SIGNAL_FUNC(sp_dialog_event_handler), gobj() );
 
-    _hiddenF12 = false;
     if (Inkscape::NSApplication::Application::getNewGui())
     {
         _desktop_activated_connection = Inkscape::NSApplication::Editor::connectDesktopActivated (sigc::mem_fun (*this, &Dialog::onDesktopActivated));
@@ -183,23 +201,25 @@ Dialog::Dialog(const char *prefs_path, int verb_num, const char *apply_label)
     }
     else
     {
-        g_signal_connect   (G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_retransientize), (void *)this);
-        g_signal_connect( G_OBJECT(INKSCAPE), "dialogs_hide", G_CALLBACK(hideCallback), (void *)this );
-        g_signal_connect( G_OBJECT(INKSCAPE), "dialogs_unhide", G_CALLBACK(unhideCallback), (void *)this );
-        g_signal_connect   (G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_dialog_shutdown), (void *)this);
+        g_signal_connect (G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_retransientize), (void *)this);
+        g_signal_connect ( G_OBJECT(INKSCAPE), "dialogs_hide", G_CALLBACK(hideCallback), (void *)this );
+        g_signal_connect ( G_OBJECT(INKSCAPE), "dialogs_unhide", G_CALLBACK(unhideCallback), (void *)this );
+        g_signal_connect (G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_dialog_shutdown), (void *)this);
     }
 
-    g_signal_connect_after( gobj(), "key_press_event", (GCallback)windowKeyPress, NULL );
+    Glib::wrap(gobj())->signal_key_press_event().connect(sigc::ptr_fun(&windowKeyPress));
+
+    if (prefs_get_int_attribute ("dialogs", "showclose", 0) || apply_label) {
+        // TODO: make the order of buttons obey the global preference
+        if (apply_label) {
+            add_button(Glib::ustring(apply_label), Gtk::RESPONSE_APPLY);
+            set_default_response(Gtk::RESPONSE_APPLY);
+        }
+        add_button(Gtk::Stock::CLOSE, Gtk::RESPONSE_CLOSE);
+    }
 
     read_geometry();
     present();
-    sp_transientize(dlg);
-    retransientize_suppress = false;
-}
-
-Dialog::Dialog(BaseObjectType *gobj)
-    : Gtk::Dialog(gobj)
-{
 }
 
 Dialog::~Dialog()
@@ -213,10 +233,11 @@ Dialog::~Dialog()
     }
     
     save_geometry();
+    delete _behavior;
 }
 
 
-bool Dialog::windowKeyPress( GtkWidget *widget, GdkEventKey *event )
+bool Dialog::windowKeyPress(GdkEventKey *event)
 {
     unsigned int shortcut = 0;
     shortcut = get_group0_keyval (event) |
@@ -226,6 +247,7 @@ bool Dialog::windowKeyPress( GtkWidget *widget, GdkEventKey *event )
           SP_SHORTCUT_CONTROL_MASK : 0 ) |
         ( event->state & GDK_MOD1_MASK ?
           SP_SHORTCUT_ALT_MASK : 0 );
+
     return sp_shortcut_invoke( shortcut, SP_ACTIVE_DESKTOP );
 }
 
@@ -259,8 +281,7 @@ void
 Dialog::onHideF12()
 {
     _hiddenF12 = true;
-    save_geometry();
-    hide();
+    _behavior->onHideF12();
 }
 
 void
@@ -270,8 +291,7 @@ Dialog::onShowF12()
         return;
 
     if (_hiddenF12) {
-        show();
-        read_geometry();
+        _behavior->onShowF12();
     }
 
     _hiddenF12 = false;
@@ -282,61 +302,15 @@ Dialog::onShutdown()
 {
     save_geometry();
     _user_hidden = true;
+    _behavior->onShutdown();
 }
 
 void
-Dialog::onDesktopActivated (SPDesktop *desktop)
+Dialog::onDesktopActivated(SPDesktop *desktop)
 {
-    gint transient_policy = prefs_get_int_attribute_limited ( "options.transientpolicy", "value", 1, 0, 2);
-
-#ifdef WIN32 // FIXME: Temporary Win32 special code to enable transient dialogs
-    if (prefs_get_int_attribute ( "options.dialogsontopwin32", "value", 0))
-        transient_policy = 2;
-    else    
-        return;
-#endif        
-
-    if (!transient_policy) 
-        return;
-
-
-
-    GtkWindow *dialog_win = GTK_WINDOW(gobj());
-
-    if (retransientize_suppress) {
-         /* if retransientizing of this dialog is still forbidden after
-          * previous call warning turned off because it was confusingly fired
-          * when loading many files from command line
-          */
-
-         // g_warning("Retranzientize aborted! You're switching windows too fast!");
-        return;
-    }
-
-    if (dialog_win)
-    {
-        retransientize_suppress = true; // disallow other attempts to retranzientize this dialog
-
-        desktop->setWindowTransient (dialog_win);
-
-        /*
-         * This enables "aggressive" transientization,
-         * i.e. dialogs always emerging on top when you switch documents. Note
-         * however that this breaks "click to raise" policy of a window
-         * manager because the switched-to document will be raised at once
-         * (so that its transients also could raise)
-         */
-        if (transient_policy == 2 && !_hiddenF12 && !_user_hidden) {
-            // without this, a transient window not always emerges on top
-            gtk_window_present (dialog_win);
-        }
-    }
-
-    // we're done, allow next retransientizing not sooner than after 120 msec
-    gtk_timeout_add (120, (GtkFunction) sp_retransientize_again, (gpointer) this);
+    _behavior->onDesktopActivated(desktop);
 }
 
-
 Inkscape::Selection*
 Dialog::_getSelection()
 {
@@ -352,20 +326,26 @@ Dialog::_apply()
 void
 Dialog::_close()
 {
-    GtkWidget *dlg = GTK_WIDGET(gobj());
-
-                        /* this code sends a delete_event to the dialog,
-                         * instead of just destroying it, so that the
-                         * dialog can do some housekeeping, such as remember
-                         * its position.
-                         */
-                        GdkEventAny event;
-                        event.type = GDK_DELETE;
-                        event.window = dlg->window;
-                        event.send_event = TRUE;
-                        g_object_ref (G_OBJECT (event.window));
-                        gtk_main_do_event ((GdkEvent*)&event);
-                        g_object_unref (G_OBJECT (event.window));
+    GtkWidget *dlg = GTK_WIDGET(_behavior->gobj());
+
+    /* this code sends a delete_event to the dialog,
+     * instead of just destroying it, so that the
+     * dialog can do some housekeeping, such as remember
+     * its position.
+     */
+
+    GdkEventAny event;
+    event.type = GDK_DELETE;
+    event.window = dlg->window;
+    event.send_event = TRUE;
+
+    if (event.window) 
+        g_object_ref (G_OBJECT (event.window));
+
+    gtk_main_do_event ((GdkEvent*)&event);
+
+    if (event.window) 
+        g_object_unref (G_OBJECT (event.window));
 }
 
 } // namespace Dialog
index dd3cdaafe43c57c992aeb60bd514d4766eed8435..7530b9a35c31543ec96301a29b707c96442ec210 100644 (file)
@@ -1,11 +1,13 @@
 /**
- * \brief Base class for dialogs in Inkscape.  This class provides certain
- *        common behaviors and styles wanted of all dialogs in the application.
+ * \brief Base class for dialogs in Inkscape.  This class provides certain common behaviors and
+ *        styles wanted of all dialogs in the application.  Fundamental parts of the dialog's
+ *        behavior is controlled by a Dialog::Behavior subclass instance connected to the dialog.
  *
  * Author:
  *   Bryce W. Harrington <bryce@bryceharrington.org>
+ *   Gustav Broberg <broberg@kth.se>
  *
- * Copyright (C) 2004, 2005 Authors
+ * Copyright (C) 2004--2007 Authors
  *
  * Released under GNU GPL.  Read the file 'COPYING' for more information.
  */
@@ -16,6 +18,9 @@
 #include <gtkmm/dialog.h>
 #include <gtkmm/tooltips.h>
 
+#include "dock-behavior.h"
+#include "floating-behavior.h"
+
 namespace Inkscape { class Selection; }
 class SPDesktop;
 
@@ -23,11 +28,15 @@ namespace Inkscape {
 namespace UI {
 namespace Dialog {
 
-class Dialog : public Gtk::Dialog {
+enum BehaviorType { FLOATING, DOCK };
+
+class Dialog {
 public:
-    Dialog(BaseObjectType *gobj); // fixme: remove this
 
-    Dialog(const char *prefs_path, int verb_num = 0, const char *apply_label = NULL);
+    Dialog(const char *prefs_path = NULL, int verb_num = 0, const char *apply_label = NULL);
+
+    Dialog(Behavior::BehaviorFactory behavior_factory, const char *prefs_path = NULL, 
+           int verb_num = 0, const char *apply_label = NULL);
 
     virtual ~Dialog();
 
@@ -37,20 +46,50 @@ public:
     virtual void present();
 
     /** Hide and show dialogs */
-    virtual void   onHideF12();
-    virtual void   onShowF12();
+    virtual void onHideF12();
+    virtual void onShowF12();
+
+    virtual operator Gtk::Widget&();
+    virtual GtkWidget *gobj();
+    virtual Gtk::VBox *get_vbox();
+    virtual void show();
+    virtual void hide();
+    virtual void show_all_children();
+    virtual void set_resizable(bool);
+    virtual void set_sensitive(bool sensitive=true);
+    virtual void set_default(Gtk::Widget&);
+    virtual void set_size_request(int, int);
+    virtual void size_request(Gtk::Requisition&);
+    virtual void get_position(int& x, int& y);
+    virtual void get_size(int& width, int& height);
+    virtual void resize(int width, int height);
+    virtual void move(int x, int y);
+    virtual void set_position(Gtk::WindowPosition position);
+    virtual void set_title(Glib::ustring title);
+
+    virtual void set_response_sensitive(int response_id, bool setting);
+    virtual Glib::SignalProxy0<void> signal_show();
+    virtual Glib::SignalProxy0<void> signal_hide();
+    virtual Glib::SignalProxy1<void, int> signal_response();
+
+    virtual Gtk::Button* add_button (const Glib::ustring& button_text, int response_id);
+    virtual Gtk::Button* add_button (const Gtk::StockID& stock_id, int response_id);
+    
+    virtual void set_default_response(int response_id);
 
-    bool           _hiddenF12;
     bool           _user_hidden; // when it is closed by the user, to prevent repopping on f12
+    bool           _hiddenF12;
 
     void           read_geometry();
     void           save_geometry();
 
-    const char           *_prefs_path;
-
     bool retransientize_suppress; // when true, do not retransientize (prevents races when switching new windows too fast)
 
 protected:
+    const char    *_prefs_path;
+    int            _verb_num;
+    Glib::ustring  _title;
+    const char    *_apply_label;
 
     /**
      * Tooltips object for all descendants to use
@@ -62,7 +101,7 @@ protected:
     virtual void   _apply();
     virtual void   _close();
 
-    static bool windowKeyPress( GtkWidget *widget, GdkEventKey *event );
+    static bool windowKeyPress(GdkEventKey *event);
 
     Inkscape::Selection*   _getSelection();
 
@@ -72,10 +111,15 @@ protected:
     sigc::connection _shutdown_connection;
 
 private:
+    Behavior::Behavior* _behavior;
+
     Dialog(); // no constructor without params
 
     Dialog(Dialog const &d);            // no copy
     Dialog& operator=(Dialog const &d); // no assign
+
+    friend class Behavior::FloatingBehavior;
+    friend class Behavior::DockBehavior;
 };
 
 } // namespace Dialog
diff --git a/src/ui/dialog/dock-behavior.cpp b/src/ui/dialog/dock-behavior.cpp
new file mode 100644 (file)
index 0000000..188fc6f
--- /dev/null
@@ -0,0 +1,308 @@
+/**
+ * \brief A dockable dialog implementation.
+ *
+ * Author:
+ *   Gustav Broberg <broberg@kth.se>
+ *
+ * Copyright (C) 2007 Authors
+ *
+ * Released under GNU GPL.  Read the file 'COPYING' for more information.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "dock-behavior.h"
+#include "inkscape.h"
+#include "desktop.h"
+#include "interface.h"
+#include "widgets/icon.h"
+#include "ui/widget/dock.h"
+#include "verbs.h"
+#include "dialog.h"
+#include "prefs-utils.h"
+
+#include <gtkmm/invisible.h>
+#include <gtkmm/label.h>
+#include <gtkmm/stock.h>
+
+#include <gtk/gtk.h>
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+namespace Behavior {
+
+
+DockBehavior::DockBehavior(Dialog& dialog) :
+    Behavior(dialog),
+    _dock_item(*SP_ACTIVE_DESKTOP->getDock(),
+               Inkscape::Verb::get(dialog._verb_num)->get_id(), dialog._title.c_str(),
+               (Inkscape::Verb::get(dialog._verb_num)->get_image() ? 
+                Inkscape::Verb::get(dialog._verb_num)->get_image() : ""),
+               static_cast<Widget::DockItem::State>(
+                   prefs_get_int_attribute (_dialog._prefs_path, "state", 
+                                            UI::Widget::DockItem::DOCKED_STATE)))
+{
+    // Connect signals
+    _signal_hide_connection = signal_hide().connect(sigc::mem_fun(*this, &Inkscape::UI::Dialog::Behavior::DockBehavior::_onHide));
+    signal_response().connect(sigc::mem_fun(_dialog, &Inkscape::UI::Dialog::Dialog::on_response));
+    signal_drag_end().connect(sigc::mem_fun(*this, &Inkscape::UI::Dialog::Behavior::DockBehavior::_onDragEnd));
+}
+
+DockBehavior::~DockBehavior()
+{
+}
+
+
+Behavior *
+DockBehavior::create(Dialog& dialog)
+{
+    return new DockBehavior(dialog);
+}
+
+
+DockBehavior::operator Gtk::Widget&()
+{
+    return _dock_item.getWidget();
+}
+
+GtkWidget *
+DockBehavior::gobj()
+{
+    return _dock_item.gobj();
+}
+
+Gtk::VBox *
+DockBehavior::get_vbox()
+{
+    return _dock_item.get_vbox();
+}
+
+void
+DockBehavior::present() 
+{
+    bool was_attached = _dock_item.isAttached();
+
+    _dock_item.present();
+
+    if (!was_attached)
+        _dialog.read_geometry();
+}
+
+void
+DockBehavior::hide()
+{
+    _signal_hide_connection.block();
+    _dock_item.hide();
+    _signal_hide_connection.unblock();
+}
+
+void
+DockBehavior::show() 
+{ 
+    _dock_item.show();
+}
+
+void 
+DockBehavior::show_all_children()
+{
+    get_vbox()->show_all_children();
+}
+
+void 
+DockBehavior::get_position(int& x, int& y)
+{ 
+    _dock_item.get_position(x, y);
+}
+
+void 
+DockBehavior::get_size(int& width, int& height)
+{ 
+    _dock_item.get_size(width, height);
+}
+
+void
+DockBehavior::resize(int width, int height) 
+{
+    _dock_item.resize(width, height);
+}
+
+void
+DockBehavior::move(int x, int y)
+{
+    _dock_item.move(x, y);
+}
+
+void
+DockBehavior::set_position(Gtk::WindowPosition position)
+{
+    _dock_item.set_position(position);
+}
+
+void
+DockBehavior::set_size_request(int width, int height)
+{
+    _dock_item.set_size_request(width, height);
+}
+
+void 
+DockBehavior::size_request(Gtk::Requisition& requisition)
+{ 
+    _dock_item.size_request(requisition);
+}
+
+void
+DockBehavior::set_title(Glib::ustring title)
+{
+    _dock_item.set_title(title);
+}
+
+void
+DockBehavior::set_response_sensitive(int response_id, bool setting)
+{
+    if (_response_map[response_id])
+        _response_map[response_id]->set_sensitive(setting);
+}
+
+void
+DockBehavior::set_sensitive(bool sensitive)
+{
+    get_vbox()->set_sensitive();
+}
+
+Gtk::Button * 
+DockBehavior::add_button(const Glib::ustring& button_text, int response_id)
+{
+    Gtk::Button *button = new Gtk::Button(button_text);
+    _addButton(button, response_id);
+    return button;
+}
+
+Gtk::Button *
+DockBehavior::add_button(const Gtk::StockID& stock_id, int response_id)
+{
+    Gtk::Button *button = new Gtk::Button(stock_id);
+    _addButton(button, response_id);
+    return button;
+}
+
+void
+DockBehavior::_addButton(Gtk::Button *button, int response_id)
+{
+    _dock_item.addButton(button, response_id);
+
+    if (response_id != 0) {
+
+        /* Pass the signal_clicked signals onto a our own signal handler that can re-emit them as
+         * signal_response signals
+         */
+        button->signal_clicked().connect( 
+            sigc::bind<int>(sigc::mem_fun(*this, 
+                            &Inkscape::UI::Dialog::Behavior::DockBehavior::_onResponse),
+                            response_id));
+
+        _response_map[response_id] = button;
+    }
+}
+
+void
+DockBehavior::set_default_response(int response_id)
+{
+    ResponseMap::iterator widget_found;
+    widget_found = _response_map.find(response_id);
+
+    if (widget_found != _response_map.end()) {
+        widget_found->second->activate();
+        widget_found->second->property_can_default() = true;
+        widget_found->second->grab_default();
+    }
+}
+
+
+void
+DockBehavior::_onHide()
+{
+    _dialog.save_geometry();
+    prefs_set_int_attribute (_dialog._prefs_path, "state", _dock_item.getPrevState());
+}
+
+void
+DockBehavior::_onDragEnd(bool)
+{
+    Widget::DockItem::State prev_state = _dock_item.getPrevState(), state = _dock_item.getState();
+    
+    if (prev_state != state) {
+        prefs_set_int_attribute (_dialog._prefs_path, "state", state);
+    }
+}
+
+void
+DockBehavior::_onResponse(int response_id)
+{
+    g_signal_emit_by_name (_dock_item.gobj(), "signal_response", response_id);
+}
+
+void
+DockBehavior::onHideF12()
+{
+    _dialog.save_geometry();
+    hide();
+}
+
+void
+DockBehavior::onShowF12()
+{
+    present();
+}
+
+void
+DockBehavior::onShutdown()
+{
+    prefs_set_int_attribute (_dialog._prefs_path, "state", _dock_item.getPrevState());
+}
+
+void
+DockBehavior::onDesktopActivated(SPDesktop *desktop)
+{
+}
+
+
+/* Signal wrappers */
+
+Glib::SignalProxy0<void> 
+DockBehavior::signal_show() { return _dock_item.signal_show(); }
+
+Glib::SignalProxy0<void> 
+DockBehavior::signal_hide() { return _dock_item.signal_hide(); }
+
+Glib::SignalProxy1<void, int> 
+DockBehavior::signal_response() { return _dock_item.signal_response(); }
+
+Glib::SignalProxy1<bool, GdkEventAny *> 
+DockBehavior::signal_delete_event() { return _dock_item.signal_delete_event(); }
+
+Glib::SignalProxy0<void>
+DockBehavior::signal_drag_begin() { return _dock_item.signal_drag_begin(); }
+
+Glib::SignalProxy1<void, bool>
+DockBehavior::signal_drag_end() { return _dock_item.signal_drag_end(); }
+
+
+} // namespace Behavior
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/ui/dialog/dock-behavior.h b/src/ui/dialog/dock-behavior.h
new file mode 100644 (file)
index 0000000..75bd14b
--- /dev/null
@@ -0,0 +1,119 @@
+/**
+ * \brief A dockable dialog implementation.
+ *
+ * Author:
+ *   Gustav Broberg <broberg@kth.se>
+ *
+ * Copyright (C) 2007 Authors
+ *
+ * Released under GNU GPL.  Read the file 'COPYING' for more information.
+ */
+
+
+#ifndef INKSCAPE_UI_DIALOG_DOCK_BEHAVIOR_H
+#define INKSCAPE_UI_DIALOG_DOCK_BEHAVIOR_H
+
+#include <map>
+
+#include <gtkmm/buttonbox.h>
+#include <gtkmm/frame.h>
+#include <gtkmm/paned.h>
+
+#include "ui/widget/dock-item.h"
+
+#include "libgdl/libgdl.h"
+
+#include "behavior.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+namespace Behavior {
+
+class DockBehavior : public Behavior {
+
+public:
+    static Behavior *create(Dialog& dialog);
+
+    ~DockBehavior();
+
+    /** Gtk::Dialog methods */
+    operator Gtk::Widget&();
+    GtkWidget *gobj();
+    void present();
+    Gtk::VBox *get_vbox();
+    void show();
+    void hide();
+    void show_all_children();
+    void resize(int width, int height);
+    void move(int x, int y);
+    void set_position(Gtk::WindowPosition);
+    void set_size_request(int width, int height);
+    void size_request(Gtk::Requisition& requisition);
+    void get_position(int& x, int& y);
+    void get_size(int& width, int& height);
+    void set_title(Glib::ustring title);
+    void set_response_sensitive(int response_id, bool setting);
+    void set_sensitive(bool sensitive);
+    Gtk::Button *add_button(const Glib::ustring& button_text, int response_id);
+    Gtk::Button *add_button(const Gtk::StockID& stock_id, int response_id);
+    void set_default_response(int response_id);
+
+    /** Gtk::Dialog signal proxies */
+    Glib::SignalProxy0<void> signal_show();
+    Glib::SignalProxy0<void> signal_hide();
+    Glib::SignalProxy1<bool, GdkEventAny *> signal_delete_event();
+    Glib::SignalProxy0<void> signal_drag_begin();
+    Glib::SignalProxy1<void, bool> signal_drag_end();
+    Glib::SignalProxy1<void, int> signal_response();
+
+    /** Custom signal handlers */
+    void onHideF12();
+    void onShowF12();
+    void onDesktopActivated(SPDesktop *desktop);
+    void onShutdown();
+
+private:
+    Widget::DockItem _dock_item;
+
+    DockBehavior(Dialog& dialog);
+
+    /** A map to store which widget that emits a certain response signal */
+    typedef std::map<int, Gtk::Widget *> ResponseMap;
+    ResponseMap _response_map;
+
+    /** Internal helpers */
+    void _addButton(Gtk::Button *button, int response_id);
+    Gtk::Paned *_getPaned();              //< gives the parent pane, if the dock item has one
+    void _requestHeight(int height);      //< tries to resize the dock item to the requested hieght
+
+    /** Internal signal handlers */
+    void _onHide();
+    bool _onDeleteEvent(GdkEventAny *event);
+    void _onResponse(int response_id);
+    void _onDragBegin();
+    void _onDragEnd(bool cancelled);
+    bool _onKeyPress(GdkEventKey *event);
+
+    sigc::connection _signal_hide_connection;
+    sigc::connection _signal_key_press_event_connection;
+
+};
+
+} // namespace Behavior
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+
+#endif // INKSCAPE_UI_DIALOG_DOCK_BEHAVIOR_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
index a3e87b31b5fd1c3c97c983d1642459d116b91931..ca2da205e5ecc44526203b360fe187206561ce53 100644 (file)
@@ -63,10 +63,10 @@ static Inkscape::XML::NodeEventVector const _repr_events = {
 
 
 DocumentMetadata*
-DocumentMetadata::create()
+DocumentMetadata::create(Behavior::BehaviorFactory behavior_factory)
 {
     if (_instance) return _instance;
-    _instance = new DocumentMetadata;
+    _instance = new DocumentMetadata(behavior_factory);
     _instance->init();
     return _instance;
 }
@@ -81,8 +81,8 @@ DocumentMetadata::destroy()
     }
 }
 
-DocumentMetadata::DocumentMetadata() 
-    : Dialog ("dialogs.documentmetadata", SP_VERB_DIALOG_METADATA),
+DocumentMetadata::DocumentMetadata(Behavior::BehaviorFactory behavior_factory
+    : Dialog (behavior_factory, "dialogs.documentmetadata", SP_VERB_DIALOG_METADATA),
       _page_metadata1(1, 1), _page_metadata2(1, 1),
       _prefs_path("dialogs.documentmetadata")
 {
index c3cba2d34a8f9e3652308bb7bac859d0f93cbd61..478f9334e764b68386e187ec87966ec03ff57f95 100644 (file)
@@ -40,7 +40,7 @@ typedef std::list<EntityEntry*> RDElist;
 class DocumentMetadata : public Inkscape::UI::Dialog::Dialog {
 public:
     void  update();
-    static DocumentMetadata *create();
+    static DocumentMetadata *create(Behavior::BehaviorFactory behavior_factory);
     static void destroy();
     sigc::connection _doc_replaced_connection;
 
@@ -62,7 +62,7 @@ protected:
     Registry _wr;
 
 private:
-    DocumentMetadata();
+    DocumentMetadata(Behavior::BehaviorFactory behavior_factory);
     virtual ~DocumentMetadata();
 };
 
index 141a0c8cba857e5c79daec723c9a85b0e22c9d7b..e2dc2c0b9156ad6fb403c462d699bff94b10ca26 100644 (file)
@@ -72,10 +72,10 @@ static Inkscape::XML::NodeEventVector const _repr_events = {
 
 
 DocumentProperties*
-DocumentProperties::create()
+DocumentProperties::create(Behavior::BehaviorFactory behavior_factory)
 {
     if (_instance) return _instance;
-    _instance = new DocumentProperties;
+    _instance = new DocumentProperties(behavior_factory);
     _instance->init();
     return _instance;
 }
@@ -90,8 +90,8 @@ DocumentProperties::destroy()
     }
 }
 
-DocumentProperties::DocumentProperties()
-    : Dialog ("dialogs.documentoptions", SP_VERB_DIALOG_NAMEDVIEW),
+DocumentProperties::DocumentProperties(Behavior::BehaviorFactory behavior_factory) 
+    : Dialog (behavior_factory, "dialogs.documentoptions", SP_VERB_DIALOG_NAMEDVIEW),
       _page_page(1, 1), _page_guides(1, 1),
       _page_snap(1, 1), _page_grids(1, 1),
       _grids_button_new(_("_New"), _("Create new grid.")),
index 846d9534179af581d04dfdfc0a39181955ce4a1b..aa6e9c03b765a8c502562795cea33e68a9609184 100644 (file)
@@ -38,7 +38,7 @@ namespace Inkscape {
 class DocumentProperties : public Inkscape::UI::Dialog::Dialog {
 public:
     void  update();
-    static DocumentProperties *create();
+    static DocumentProperties *create(Behavior::BehaviorFactory behavior_factory);
     static void destroy();
     sigc::connection _doc_replaced_connection;
 
@@ -88,7 +88,7 @@ protected:
     Registry _wr;
 
 private:
-    DocumentProperties();
+    DocumentProperties(Behavior::BehaviorFactory behavior_factory);
     virtual ~DocumentProperties();
 
     // callback methods for buttons on grids page.
index d9ea2d25d51ac38ccb90ce15a8f53241b555f89a..4e4efafb5755f1a399b289db0e1595ae2a503d70 100644 (file)
@@ -20,8 +20,8 @@ namespace Inkscape {
 namespace UI {
 namespace Dialog {
 
-Export::Export() 
-    : Dialog ("dialogs.export", SP_VERB_FILE_EXPORT),
+Export::Export(Behavior::BehaviorFactory behavior_factory
+    : Dialog (behavior_factory, "dialogs.export", SP_VERB_FILE_EXPORT),
       _page_export(1, 1)
 {
     // Top level vbox
index 567eae10dca24621c369bbf8d09c8e3ed6edf0e2..c47c7c8cc81d68d64d89ca2afad614cfa4b83022 100644 (file)
@@ -26,10 +26,11 @@ namespace Dialog {
 
 class Export : public Dialog {
 public:
-    Export();
+    Export(Behavior::BehaviorFactory behavior_factory);
     virtual ~Export();
 
-    static Export *create() { return new Export(); }
+    static Export *create(Behavior::BehaviorFactory behavior_factory) 
+    { return new Export(behavior_factory); }
 
 protected:
     Gtk::Notebook  _notebook;
index 5f6386046a49bdca088a4f3dd65e6616d1ce6c7c..3a62cb14481b317edda67427a18685b570c273f0 100644 (file)
@@ -42,8 +42,8 @@ namespace Dialog {
     about the selected extension.  A handler is set up so that when
     a new extension is selected, the notebooks are changed appropriately.
 */
-ExtensionEditor::ExtensionEditor()
-    : Dialog ("dialogs.extensioneditor", SP_VERB_DIALOG_EXTENSIONEDITOR)
+ExtensionEditor::ExtensionEditor(Behavior::BehaviorFactory behavior_factory)
+    : Dialog (behavior_factory, "dialogs.extensioneditor", SP_VERB_DIALOG_EXTENSIONEDITOR)
 {
     _notebook_info.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
     _notebook_help.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
index b6c331eae5796a015ae12d32c01eba41cc6dc671..284a3651dac06c30a7eec54013ae2515dc8987f6 100644 (file)
@@ -31,10 +31,11 @@ namespace Dialog {
 
 class ExtensionEditor : public Dialog {
 public:
-    ExtensionEditor();
+    ExtensionEditor(Behavior::BehaviorFactory behavior_factory);
     virtual ~ExtensionEditor();
 
-    static ExtensionEditor *create() { return new ExtensionEditor(); }
+    static ExtensionEditor *create(Behavior::BehaviorFactory behavior_factory)
+    { return new ExtensionEditor(behavior_factory); }
 
     static void show_help (gchar const * extension_id);
 
index 8cf6bcfb484839b26b19ce18f8ec5415ca657f80..ad9a9f031d932a6af1298802019213332600d1d6 100644 (file)
@@ -55,8 +55,8 @@ void on_selection_modified(Inkscape::Application *inkscape,
 }
 
 
-FillAndStroke::FillAndStroke() 
-    : Dialog ("dialogs.fillstroke", SP_VERB_DIALOG_FILL_STROKE),
+FillAndStroke::FillAndStroke(Behavior::BehaviorFactory behavior_factory
+    : Dialog (behavior_factory, "dialogs.fillstroke", SP_VERB_DIALOG_FILL_STROKE),
       _page_fill(1, 1, true, true),
       _page_stroke_paint(1, 1, true, true),
       _page_stroke_style(1, 1, true, true),
index b573161d99d0d1fade8d1a609d60c01c3ebee2db..87d5181dba1aafe33a046eeadc5dc6b1b5e5f463 100644 (file)
@@ -33,10 +33,11 @@ namespace Dialog {
 
 class FillAndStroke : public Dialog {
 public:
-    FillAndStroke();
+    FillAndStroke(Behavior::BehaviorFactory behavior_factory);
     virtual ~FillAndStroke();
 
-    static FillAndStroke *create() { return new FillAndStroke(); }
+    static FillAndStroke *create(Behavior::BehaviorFactory behavior_factory)
+    { return new FillAndStroke(behavior_factory); }
 
     void selectionChanged(Inkscape::Application *inkscape,
                           Inkscape::Selection *selection);
index 3fa7d73d416320ede7df5736d103716170562f35..3bffb12859bc273a8198f4f427fb998080cdf4eb 100644 (file)
@@ -1166,7 +1166,8 @@ void FilterEffectsDialog::FilterModifier::duplicate_filter()
 void FilterEffectsDialog::FilterModifier::rename_filter()
 {
     SPFilter* filter = get_selected_filter();
-    Gtk::Dialog m("", _dialog, true);
+    Gtk::Window *window = dynamic_cast<Gtk::Window *>(_dialog.get_vbox()->get_ancestor(GTK_TYPE_WINDOW));
+    Gtk::Dialog m("", *window, true);
     m.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
     m.add_button(_("_Rename"), Gtk::RESPONSE_OK);
     m.set_default_response(Gtk::RESPONSE_OK);
@@ -1843,8 +1844,8 @@ int FilterEffectsDialog::PrimitiveList::primitive_count() const
 
 /*** FilterEffectsDialog ***/
 
-FilterEffectsDialog::FilterEffectsDialog() 
-    : Dialog ("dialogs.filtereffects", SP_VERB_DIALOG_FILTER_EFFECTS),
+FilterEffectsDialog::FilterEffectsDialog(Behavior::BehaviorFactory behavior_factory
+    : Dialog (behavior_factory, "dialogs.filtereffects", SP_VERB_DIALOG_FILTER_EFFECTS),
       _filter_modifier(*this),
       _primitive_list(*this),
       _add_primitive_type(FPConverter),
index 1a4285dd9300cf24882fa6e70dddbbaee640766b..743ce02395784cb6ebbed8950366a7e6c6fdd18a 100644 (file)
@@ -40,11 +40,14 @@ class DualSpinButton;
 class MultiSpinButton;
 class FilterEffectsDialog : public Dialog {
 public:
+
+    FilterEffectsDialog(Behavior::BehaviorFactory behavior_factory);
     ~FilterEffectsDialog();
 
-    void set_attrs_locked(const bool);
+    static FilterEffectsDialog *create(Behavior::BehaviorFactory behavior_factory)
+    { return new FilterEffectsDialog(behavior_factory); }
 
-    static FilterEffectsDialog *create() { return new FilterEffectsDialog(); }
+    void set_attrs_locked(const bool);
 private:
     class SignalObserver;
 
index 22fa608bf25d684b38f9643c4c8f27a359ace07d..0a0538c30ab4efcf512b455bcd9af609dd2cc79c 100644 (file)
@@ -56,8 +56,8 @@ namespace Inkscape {
 namespace UI {
 namespace Dialog {
 
-Find::Find(
-    : Dialog ("dialogs.find", SP_VERB_DIALOG_FIND),
+Find::Find(Behavior::BehaviorFactory behavior_factory)
+    : Dialog (behavior_factory, "dialogs.find", SP_VERB_DIALOG_FIND),
       _entry_text(_("_Text: "), _("Find objects by their text content (exact or partial match)")),
       _entry_id(_("_ID: "), _("Find objects by the value of the id attribute (exact or partial match)")),
       _entry_style(_("_Style: "), _("Find objects by the value of the style attribute (exact or partial match)")),
index 9594669fa59d5e7763901822a1a3c300a650d660..fb52b2c8e2ebc19a730af7ae1a7eff0acffb7d2b 100644 (file)
@@ -59,10 +59,11 @@ namespace Dialog {
 
 class Find : public Dialog {
 public:
-    Find();
+    Find(Behavior::BehaviorFactory behavior_factory);
     virtual ~Find();
 
-    static Find *create() { return new Find(); }
+    static Find *create(Behavior::BehaviorFactory behavior_factory) 
+    { return new Find(behavior_factory); }
 
 protected:
     // Widgets:
diff --git a/src/ui/dialog/floating-behavior.cpp b/src/ui/dialog/floating-behavior.cpp
new file mode 100644 (file)
index 0000000..b6b6949
--- /dev/null
@@ -0,0 +1,182 @@
+/**
+ * \brief A floating dialog implementation.
+ *
+ * Author:
+ *   Gustav Broberg <broberg@kth.se>
+ *
+ * Copyright (C) 2007 Authors
+ *
+ * Released under GNU GPL.  Read the file 'COPYING' for more information.
+ */
+
+#include <gtkmm/stock.h>
+#include <gtk/gtk.h>
+
+#include "floating-behavior.h"
+#include "dialog.h"
+
+#include "application/application.h"
+#include "application/editor.h"
+#include "inkscape.h"
+#include "desktop.h"
+#include "dialogs/dialog-events.h"
+#include "interface.h"
+#include "prefs-utils.h"
+#include "verbs.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+namespace Behavior {
+
+static gboolean
+sp_retransientize_again (gpointer dlgPtr)
+{
+    Dialog *dlg = (Dialog *)dlgPtr;
+    dlg->retransientize_suppress = false;
+    return FALSE; // so that it is only called once
+}
+
+
+FloatingBehavior::FloatingBehavior(Dialog& dialog) :
+    Behavior(dialog),
+    _d (new Gtk::Dialog(_dialog._title))
+{
+    hide();
+    _d->set_has_separator(false);
+
+    signal_response().connect(sigc::mem_fun(_dialog, &Inkscape::UI::Dialog::Dialog::on_response));
+    _d->signal_delete_event().connect(sigc::mem_fun(_dialog, &Inkscape::UI::Dialog::Dialog::on_delete_event));
+
+    sp_transientize(GTK_WIDGET(_d->gobj()));
+    _dialog.retransientize_suppress = false;
+}
+
+FloatingBehavior::~FloatingBehavior() 
+{ 
+    delete _d;
+}
+
+Behavior *
+FloatingBehavior::create(Dialog& dialog)
+{
+    return new FloatingBehavior(dialog);
+}
+
+inline FloatingBehavior::operator Gtk::Widget&()                          { return *_d; }
+inline GtkWidget *FloatingBehavior::gobj()                                { return GTK_WIDGET(_d->gobj()); }
+inline Gtk::VBox* FloatingBehavior::get_vbox()                            { return _d->get_vbox(); }
+inline void FloatingBehavior::present()                                   { _d->present(); }
+inline void FloatingBehavior::hide()                                      { _d->hide(); }
+inline void FloatingBehavior::show()                                      { _d->show(); }
+inline void FloatingBehavior::show_all_children()                         { _d->show_all_children(); }
+inline void FloatingBehavior::resize(int width, int height)               { _d->resize(width, height); }
+inline void FloatingBehavior::move(int x, int y)                          { _d->move(x, y); }
+inline void FloatingBehavior::set_position(Gtk::WindowPosition position)  { _d->set_position(position); }
+inline void FloatingBehavior::set_size_request(int width, int height)     { _d->set_size_request(width, height); }
+inline void FloatingBehavior::size_request(Gtk::Requisition& requisition) { _d->size_request(requisition); }
+inline void FloatingBehavior::get_position(int& x, int& y)                { _d->get_position(x, y); }
+inline void FloatingBehavior::get_size(int& width, int& height)           { _d->get_size(width, height); }
+inline void FloatingBehavior::set_title(Glib::ustring title)              { _d->set_title(title); }
+inline void FloatingBehavior::set_sensitive(bool sensitive)               { _d->set_sensitive(sensitive); }
+
+void FloatingBehavior::set_response_sensitive(int response_id, bool setting) 
+{ _d->set_response_sensitive(response_id, setting); }
+
+Gtk::Button *FloatingBehavior::add_button(const Glib::ustring& button_text, int response_id)
+{ return _d->add_button(button_text, response_id); }
+
+Gtk::Button *FloatingBehavior::add_button(const Gtk::StockID& stock_id, int response_id)
+{ return _d->add_button(stock_id, response_id); }
+
+inline void FloatingBehavior::set_default_response(int response_id) { _d->set_default_response(response_id); }
+
+Glib::SignalProxy0<void> FloatingBehavior::signal_show() { return _d->signal_show(); }
+Glib::SignalProxy0<void> FloatingBehavior::signal_hide() { return _d->signal_hide(); }
+Glib::SignalProxy1<bool, GdkEventAny *> FloatingBehavior::signal_delete_event () { return _d->signal_delete_event(); }
+Glib::SignalProxy1<void, int> FloatingBehavior::signal_response () { return _d->signal_response(); }
+
+
+void
+FloatingBehavior::onHideF12()
+{
+    _dialog.save_geometry();
+    hide();
+}
+
+void
+FloatingBehavior::onShowF12()
+{
+    show();
+    _dialog.read_geometry();
+}
+
+void
+FloatingBehavior::onShutdown() {}
+
+void
+FloatingBehavior::onDesktopActivated (SPDesktop *desktop)
+{
+    gint transient_policy = prefs_get_int_attribute_limited ( "options.transientpolicy", "value", 1, 0, 2);
+
+#ifdef WIN32 // FIXME: Temporary Win32 special code to enable transient dialogs
+    if (prefs_get_int_attribute ( "options.dialogsontopwin32", "value", 0))
+        transient_policy = 2;
+    else    
+        return;
+#endif        
+
+    if (!transient_policy) 
+        return;
+
+    GtkWindow *dialog_win = GTK_WINDOW(_d->gobj());
+
+    if (_dialog.retransientize_suppress) {
+         /* if retransientizing of this dialog is still forbidden after
+          * previous call warning turned off because it was confusingly fired
+          * when loading many files from command line
+          */
+
+         // g_warning("Retranzientize aborted! You're switching windows too fast!");
+        return;
+    }
+
+    if (dialog_win)
+    {
+        _dialog.retransientize_suppress = true; // disallow other attempts to retranzientize this dialog
+
+        desktop->setWindowTransient (dialog_win);
+
+        /*
+         * This enables "aggressive" transientization,
+         * i.e. dialogs always emerging on top when you switch documents. Note
+         * however that this breaks "click to raise" policy of a window
+         * manager because the switched-to document will be raised at once
+         * (so that its transients also could raise)
+         */
+        if (transient_policy == 2 && ! _dialog._hiddenF12 && !_dialog._user_hidden) {
+            // without this, a transient window not always emerges on top
+            gtk_window_present (dialog_win);
+        }
+    }
+
+    // we're done, allow next retransientizing not sooner than after 120 msec
+    gtk_timeout_add (120, (GtkFunction) sp_retransientize_again, (gpointer) _d);
+}
+
+
+} // namespace Behavior
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/ui/dialog/floating-behavior.h b/src/ui/dialog/floating-behavior.h
new file mode 100644 (file)
index 0000000..354987d
--- /dev/null
@@ -0,0 +1,88 @@
+/**
+ * \brief A floating dialog implementation.
+ *
+ * Author:
+ *   Gustav Broberg <broberg@kth.se>
+ *
+ * Copyright (C) 2007 Authors
+ *
+ * Released under GNU GPL.  Read the file 'COPYING' for more information.
+ */
+
+
+#ifndef INKSCAPE_UI_DIALOG_FLOATING_BEHAVIOR_H
+#define INKSCAPE_UI_DIALOG_FLOATING_BEHAVIOR_H
+
+#include <gtkmm/dialog.h>
+#include "behavior.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+namespace Behavior {
+
+class FloatingBehavior : public Behavior {
+
+public:
+    static Behavior *create(Dialog& dialog);
+
+    ~FloatingBehavior();
+
+    /** Gtk::Dialog methods */
+    operator Gtk::Widget&();
+    GtkWidget *gobj();
+    void present();
+    Gtk::VBox *get_vbox();
+    void show();
+    void hide();
+    void show_all_children();
+    void resize(int width, int height);
+    void move(int x, int y);
+    void set_position(Gtk::WindowPosition);
+    void set_size_request(int width, int height);
+    void size_request(Gtk::Requisition& requisition);
+    void get_position(int& x, int& y);
+    void get_size(int& width, int& height);
+    void set_title(Glib::ustring title);
+    void set_response_sensitive(int response_id, bool setting);
+    void set_sensitive(bool sensitive);
+    Gtk::Button *add_button(const Glib::ustring& button_text, int response_id);
+    Gtk::Button *add_button(const Gtk::StockID& stock_id, int response_id);
+    void set_default_response(int response_id);
+
+    /** Gtk::Dialog signal proxies */
+    Glib::SignalProxy0<void> signal_show();
+    Glib::SignalProxy0<void> signal_hide();
+    Glib::SignalProxy1<bool, GdkEventAny *> signal_delete_event();
+    Glib::SignalProxy1<void, int> signal_response();
+
+    /** Custom signal handlers */
+    void onHideF12();
+    void onShowF12();
+    void onDesktopActivated(SPDesktop *desktop);
+    void onShutdown();
+
+private:
+    FloatingBehavior(Dialog& dialog);
+
+    Gtk::Dialog *_d;   //< the actual dialog
+
+};
+
+} // namespace Behavior
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+
+#endif // INKSCAPE_UI_DIALOG_FLOATING_BEHAVIOR_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
index 744d4f5553bb7a320750fc53de7428b6ffad7be9..138f36c2992cdd84b358c1b74427de1e1b3ea312 100644 (file)
@@ -42,8 +42,8 @@ namespace Inkscape {
 namespace UI {
 namespace Dialog {
 
-InkscapePreferences::InkscapePreferences()
-    : Dialog ("dialogs.preferences", SP_VERB_DIALOG_DISPLAY),
+InkscapePreferences::InkscapePreferences(Behavior::BehaviorFactory behavior_factory)
+    : Dialog (behavior_factory, "dialogs.preferences", SP_VERB_DIALOG_DISPLAY),
       _max_dialog_width(0), 
       _max_dialog_height(0),
       _current_page(0)
@@ -429,6 +429,9 @@ void InkscapePreferences::initPageWindows()
     _win_save_geom_prefs.init ( _("Remember and use last window's geometry"), "options.savewindowgeometry", "value", 2, false, &_win_save_geom);
     _win_save_geom_off.init ( _("Don't save window geometry"), "options.savewindowgeometry", "value", 0, false, &_win_save_geom);
 
+    _win_dockable.init ( _("Dockable"), "options.dialogtype", "value", 1, true, 0);
+    _win_floating.init ( _("Floating"), "options.dialogtype", "value", 0, false, &_win_dockable);
+
     _win_hide_task.init ( _("Dialogs are hidden in taskbar"), "options.dialogsskiptaskbar", "value", true);
     _win_zoom_resize.init ( _("Zoom when window is resized"), "options.stickyzoom", "value", false);
     _win_show_close.init ( _("Show close button on dialogs"), "dialogs", "showclose", false);
@@ -449,6 +452,12 @@ void InkscapePreferences::initPageWindows()
     _page_windows.add_line( false, "", _win_save_geom, "", 
                             _("Save and restore window geometry for each document (saves geometry in the document)"));
 
+    _page_windows.add_group_header( _("Dialog behavior (requires restart):"));
+    _page_windows.add_line( true, "", _win_dockable, "", 
+                            _("Dockable"));
+    _page_windows.add_line( true, "", _win_floating, "", 
+                            _("Floating"));
+
     _page_windows.add_group_header( _("Dialogs on top:"));
 #ifndef WIN32 // FIXME: Temporary Win32 special code to enable transient dialogs
     _page_windows.add_line( true, "", _win_ontop_none, "", 
index 48cec934120b5155a3c73ffc28817053d63a28ce..6798de21d40e9f8f5649fc5951a84e9bd2109582 100644 (file)
@@ -76,7 +76,9 @@ class InkscapePreferences : public Dialog {
 public:
     virtual ~InkscapePreferences();
 
-    static InkscapePreferences *create() {return new InkscapePreferences(); }
+    static InkscapePreferences *create(Behavior::BehaviorFactory behavior_factory) 
+    { return new InkscapePreferences(behavior_factory); }
+
     void present();
 
 protected:
@@ -123,6 +125,7 @@ protected:
 
     PrefSpinButton  _t_pencil_tolerance;
 
+    PrefRadioButton _win_dockable, _win_floating;
     PrefRadioButton _win_ontop_none, _win_ontop_normal, _win_ontop_agressive;
     PrefRadioButton _win_save_geom_off, _win_save_geom, _win_save_geom_prefs;
     PrefCheckButton _win_hide_task, _win_zoom_resize , _win_show_close;
@@ -191,7 +194,7 @@ protected:
     void initPageMisc();
 
 private:
-    InkscapePreferences();
+    InkscapePreferences(Behavior::BehaviorFactory behavior_factory);
     InkscapePreferences(InkscapePreferences const &d);
     InkscapePreferences operator=(InkscapePreferences const &d);
 };
index 79c4fbcc2cf98ccf3c8b1701df899331959f3085..cb4b8a0b4122680e972896f07c56557f599bbeec 100644 (file)
@@ -20,8 +20,8 @@ namespace Inkscape {
 namespace UI {
 namespace Dialog {
 
-LayerEditor::LayerEditor() 
-    : Dialog ("dialogs.layers", SP_VERB_NONE /*FIXME*/)
+LayerEditor::LayerEditor(Behavior::BehaviorFactory behavior_factory
+    : Dialog (behavior_factory, "dialogs.layers", SP_VERB_NONE /*FIXME*/)
 {
     // TODO:  Insert widgets
 
index 3d68a7fcf0c4039a2dd2a8d1ba0312564d7b3943..3c25c7bc081b81129930a96be7dee4d4bd2905f5 100644 (file)
@@ -22,10 +22,11 @@ namespace Dialog {
 
 class LayerEditor : public Dialog {
 public:
-    LayerEditor();
+    LayerEditor(Behavior::BehaviorFactory behavior_factory);
     virtual ~LayerEditor();
 
-    static LayerEditor *create() { return new LayerEditor(); }
+    static LayerEditor *create(Behavior::BehaviorFactory behavior_factory)
+    { return new LayerEditor(behavior_factory); }
 
 protected:
 
index 5f765b4204233f390c96f2d091cfd598a8d4332e..ee010eeca4a12597eefc8f09e6f6cbf0d6114180 100644 (file)
@@ -66,8 +66,8 @@ static void lpeeditor_desktop_change(Inkscape::Application*, SPDesktop* desktop,
 /*#######################\r
  * LivePathEffectEditor\r
  */\r
-LivePathEffectEditor::LivePathEffectEditor() \r
-    : Dialog ("dialogs.livepatheffect", SP_VERB_DIALOG_LIVE_PATH_EFFECT),\r
+LivePathEffectEditor::LivePathEffectEditor(Behavior::BehaviorFactory behavior_factory\r
+    : Dialog (behavior_factory, "dialogs.livepatheffect", SP_VERB_DIALOG_LIVE_PATH_EFFECT),\r
       combo_effecttype(Inkscape::LivePathEffect::LPETypeConverter),\r
       button_apply(_("_Apply"), _("Apply chosen effect to selection")),\r
       button_remove(_("_Remove"), _("Remove effect from selection")),\r
index 5476f8a1d6acd4ff02acf4c0354eadefb9736cb8..d99177fdd61a7533d219110ae542586fe76ffc19 100644 (file)
@@ -30,10 +30,11 @@ namespace Dialog {
 \r
 class LivePathEffectEditor : public Dialog {\r
 public:\r
-    LivePathEffectEditor();\r
+    LivePathEffectEditor(Behavior::BehaviorFactory behavior_factory);\r
     virtual ~LivePathEffectEditor();\r
 \r
-    static LivePathEffectEditor *create() { return new LivePathEffectEditor(); }\r
+    static LivePathEffectEditor *create(Behavior::BehaviorFactory behavior_factory)\r
+    { return new LivePathEffectEditor(behavior_factory); }\r
 \r
     void onSelectionChanged(Inkscape::Selection *sel);\r
     void setDesktop(SPDesktop *desktop);\r
index e81db17d83127ddaee9129b4fa014cb7a3f2431c..eb61aaf9f67ee47d7c746dae8c47d65552adc2bd 100644 (file)
@@ -203,8 +203,8 @@ void Memory::Private::stop_update_task() {
     update_task.disconnect();
 }
 
-Memory::Memory() 
-    : Dialog ("dialogs.memory", SP_VERB_HELP_MEMORY, _("Recalculate")),
+Memory::Memory(Behavior::BehaviorFactory behavior_factory
+    : Dialog (behavior_factory, "dialogs.memory", SP_VERB_HELP_MEMORY, _("Recalculate")),
       _private(*(new Memory::Private())) 
 {
     get_vbox()->add(_private.view);
index bec6c8fd064bd1ac6f062918bb84d8562e032e96..0fe7f87c575da149ac5c7e2f4e4494f2844c37cc 100644 (file)
@@ -20,10 +20,11 @@ namespace Dialog {
 
 class Memory : public Dialog {
 public:
-    Memory();
+    Memory(Behavior::BehaviorFactory behavior_factory);
     ~Memory();
 
-    static Memory *create() { return new Memory(); }
+    static Memory *create(Behavior::BehaviorFactory behavior_factory) 
+    { return new Memory(behavior_factory); }
 
 protected:
     void _apply();
index 9c7434eddfc6a341d1602434164201155524067c..9e78903c9ea5347ba142083faa217939bb642681 100644 (file)
@@ -45,8 +45,8 @@ void Messages::clear()
 /**
  * Constructor
  */
-Messages::Messages()
-       : Dialog ("dialogs.messages", SP_VERB_DIALOG_DEBUG)
+Messages::Messages(Behavior::BehaviorFactory behavior_factory)
+    : Dialog (behavior_factory, "dialogs.messages", SP_VERB_DIALOG_DEBUG)
 {
     Gtk::VBox *mainVBox = get_vbox();
 
index 3ce7b5a87440bc2952b3664ce01988e0b6c0671e..85a7c4f0ffa08b03aae49c30bd98da45b5779797 100644 (file)
@@ -35,10 +35,11 @@ namespace Dialog {
 
 class Messages : public Dialog {
 public:
-    Messages();
+    Messages(Behavior::BehaviorFactory behavior_factory);
     virtual ~Messages();
 
-    static Messages *create() { return new Messages(); }
+    static Messages *create(Behavior::BehaviorFactory behavior_factory) 
+    { return new Messages(behavior_factory); }
 
     /**
      * Clear all information from the dialog
index 07f26b71f92618bb625f6dc3a1f7253b4e65f1b8..304362f60b3d2b84583c3ac07de8da11a1d5b0ac 100644 (file)
@@ -43,7 +43,7 @@ class ScriptDialogImpl : public ScriptDialog
     /**
      * Constructor
      */
-    ScriptDialogImpl();
+    ScriptDialogImpl(Behavior::BehaviorFactory behavior_factory);
 
     /**
      * Destructor
@@ -192,7 +192,8 @@ void ScriptDialogImpl::executePerl()
 /**
  * Constructor
  */
-ScriptDialogImpl::ScriptDialogImpl()
+ScriptDialogImpl::ScriptDialogImpl(Behavior::BehaviorFactory behavior_factory) :
+    ScriptDialog(behavior_factory)
 {
     Gtk::VBox *mainVBox = get_vbox();
 
@@ -249,9 +250,9 @@ ScriptDialogImpl::ScriptDialogImpl()
 /**
  * Factory method.  Use this to create a new ScriptDialog
  */
-ScriptDialog *ScriptDialog::create()
+ScriptDialog *ScriptDialog::create(Behavior::BehaviorFactory behavior_factory)
 {
-    ScriptDialog *dialog = new ScriptDialogImpl();
+    ScriptDialog *dialog = new ScriptDialogImpl(behavior_factory);
     return dialog;
 }
 
index b66aa93e79cf0b3dfc098952c2b0ae9ddcc08373..00680d4312330410d134e3e775c6777b35ee65c0 100644 (file)
@@ -34,14 +34,15 @@ class ScriptDialog : public Dialog
     /**
      * Constructor
      */
-    ScriptDialog() : Dialog ("dialogs.script", SP_VERB_DIALOG_SCRIPT)
+    ScriptDialog(Behavior::BehaviorFactory behavior_factory) : 
+     Dialog (behavior_factory, "dialogs.script", SP_VERB_DIALOG_SCRIPT)
     {}
 
 
     /**
      * Factory method
      */
-    static ScriptDialog *create();
+    static ScriptDialog *create(Behavior::BehaviorFactory behavior_factory);
 
     /**
      * Destructor
index a7999267778f86b582cc76f2f95b22e24ff60e01..e6194ab564fb885726ede53d12ca91ffa6a3be75 100644 (file)
@@ -20,8 +20,8 @@ namespace Inkscape {
 namespace UI {
 namespace Dialog {
 
-TextProperties::TextProperties() 
-    : Dialog ("dialogs.textandfont", SP_VERB_DIALOG_TEXT),
+TextProperties::TextProperties(Behavior::BehaviorFactory behavior_factory
+    : Dialog (behavior_factory, "dialogs.textandfont", SP_VERB_DIALOG_TEXT),
       _page_font(1, 1),
       _page_text(1, 1)
 {
index 2d5a03b9e7c6d818b059bf00124e7814b0d9a335..393ca63b2db72a3e8f52c2f28cd2bf6ecf3ea24b 100644 (file)
@@ -26,10 +26,11 @@ namespace Dialog {
 
 class TextProperties : public Dialog {
 public:
-    TextProperties();
+    TextProperties(Behavior::BehaviorFactory behavior_factory);
     virtual ~TextProperties();
 
-    static TextProperties *create() { return new TextProperties(); }
+    static TextProperties *create(Behavior::BehaviorFactory behavior_factory) 
+    { return new TextProperties(behavior_factory); }
 
 protected:
     Gtk::Notebook  _notebook;
index 2c041864f0940fc441578ea31db0120a4783854d..b7602b36a4efd29af196d0797a57a3323eb40569 100644 (file)
@@ -50,7 +50,7 @@ class TraceDialogImpl : public TraceDialog
     /**
      * Constructor
      */
-    TraceDialogImpl();
+    TraceDialogImpl(Behavior::BehaviorFactory behavior_factory);
 
     /**
      * Destructor
@@ -376,7 +376,8 @@ void TraceDialogImpl::responseCallback(int response_id)
 /**
  * Constructor
  */
-TraceDialogImpl::TraceDialogImpl()
+TraceDialogImpl::TraceDialogImpl(Behavior::BehaviorFactory behavior_factory) :
+    TraceDialog(behavior_factory)
 {
 
     Gtk::VBox *mainVBox = get_vbox();
@@ -685,9 +686,9 @@ TraceDialogImpl::TraceDialogImpl()
 /**
  * Factory method.  Use this to create a new TraceDialog
  */
-TraceDialog *TraceDialog::create()
+TraceDialog *TraceDialog::create(Behavior::BehaviorFactory behavior_factory)
 {
-    TraceDialog *dialog = new TraceDialogImpl();
+    TraceDialog *dialog = new TraceDialogImpl(behavior_factory);
     return dialog;
 }
 
index 80311bb154f210a7e96844811a66b1087ce8598d..0e352ce10be751a51f136af3c53265c3cf2fb537 100644 (file)
@@ -34,14 +34,15 @@ public:
     /**
      * Constructor
      */
-    TraceDialog() : Dialog ("dialogs.trace", SP_VERB_SELECTION_TRACE)
+    TraceDialog(Behavior::BehaviorFactory behavior_factory) : 
+       Dialog (behavior_factory, "dialogs.trace", SP_VERB_SELECTION_TRACE)
         {}
 
 
     /**
      * Factory method
      */
-    static TraceDialog *create();
+    static TraceDialog *create(Behavior::BehaviorFactory behavior_factory);
 
     /**
      * Destructor
index be5a8bb03faf32a1bd6df1f5abd805d9d8ae124e..b2385a29cde5e8b7ee1587df92bfcfcd2f09f4ac 100644 (file)
@@ -70,8 +70,8 @@ void on_selection_modified ( Inkscape::Application *inkscape,
  * we use the ScalarUnit class for this.
  *
  */
-Transformation::Transformation()
-    : Dialog ("dialogs.transformation", SP_VERB_DIALOG_TRANSFORM),
+Transformation::Transformation(Behavior::BehaviorFactory behavior_factory)
+    : Dialog (behavior_factory, "dialogs.transformation", SP_VERB_DIALOG_TRANSFORM),
       _page_move              (4, 2),
       _page_scale             (4, 2),
       _page_rotate            (4, 2),
@@ -181,8 +181,8 @@ void
 Transformation::presentPage(Transformation::PageType page)
 {
     _notebook.set_current_page(page);
-    Gtk::Dialog::show();
-    Gtk::Dialog::present();
+    show();
+    present();
 }
 
 
index 0c7833c9e72703f4a7d75a0e9aba381989dd5a87..361b30a04c088cb8e71b37c46e371e41a81fa35a 100644 (file)
@@ -46,7 +46,7 @@ public:
     /**
      * Create a new transform
      */
-    Transformation();
+    Transformation(Behavior::BehaviorFactory behavior_factory);
 
     /**
      * Cleanup
@@ -57,8 +57,8 @@ public:
     /**
      * Factory method.  Create an instance of this class/interface
      */
-    static Transformation *create()
-        { return new Transformation(); }
+    static Transformation *create(Behavior::BehaviorFactory behavior_factory)
+        { return new Transformation(behavior_factory); }
 
 
     /**
index 98f8cc0cc961edeb119b0d029a94aa8c2783b194..344efc557b4e7caf03ebd4c2304cd89b092cd5ac 100644 (file)
@@ -103,10 +103,10 @@ static void on_activate_desktop(Inkscape::Application*, SPDesktop* desktop, void
 static void on_deactivate_desktop(Inkscape::Application*, SPDesktop* desktop, void*);
 
 UndoHistory*
-UndoHistory::create()
+UndoHistory::create(Behavior::BehaviorFactory behavior_factory)
 {
     if (_instance) return _instance;
-    _instance = new UndoHistory;
+    _instance = new UndoHistory(behavior_factory);
     return _instance;
 }
 
@@ -131,8 +131,8 @@ UndoHistory::setDesktop(SPDesktop* desktop)
     _callback_connections[EventLog::CALLB_SELECTION_CHANGE].block(false);
 }
 
-UndoHistory::UndoHistory()
-    : Dialog ("dialogs.undo-history", SP_VERB_DIALOG_UNDO_HISTORY),
+UndoHistory::UndoHistory(Behavior::BehaviorFactory behavior_factory)
+    : Dialog (behavior_factory, "dialogs.undo-history", SP_VERB_DIALOG_UNDO_HISTORY),
       _desktop (SP_ACTIVE_DESKTOP),
       _document (SP_ACTIVE_DOCUMENT),
       _event_log (_desktop ? _desktop->event_log : NULL),
@@ -141,7 +141,7 @@ UndoHistory::UndoHistory()
 { 
     if( !_document || !_event_log || !_columns ) return;
 
-    set_size_request(300, 400);
+    set_size_request(300, 200);
 
     get_vbox()->pack_start(_scrolled_window);
     _scrolled_window.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
index 7b900fde5c1d67e41aef6e36773bf1d46be6e608..d7b00f343edc970f290ac7b35636e768942483d4 100644 (file)
@@ -120,7 +120,7 @@ class UndoHistory : public Dialog {
 public:
     virtual ~UndoHistory();
 
-    static UndoHistory *create();
+    static UndoHistory *create(Behavior::BehaviorFactory behavior_factory);
     void setDesktop(SPDesktop* desktop);
 
     sigc::connection _document_replaced_connection;
@@ -146,9 +146,9 @@ protected:
     void _onCollapseEvent(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path);
 
 private:
+    UndoHistory(Behavior::BehaviorFactory behavior_factory);
   
     // no default constructor, noncopyable, nonassignable
-    UndoHistory();
     UndoHistory(UndoHistory const &d);
     UndoHistory operator=(UndoHistory const &d);
 
index b8abf8bfd533ab84150ddaf81b26d6261d75a82b..53f3b4c2cc76cb12ce3e39fd0d15f13ba0c4d01b 100644 (file)
@@ -20,8 +20,8 @@ namespace Inkscape {
 namespace UI {
 namespace Dialog {
 
-XmlEditor::XmlEditor() 
-    : Dialog ("dialogs.xml", SP_VERB_DIALOG_XML_EDITOR)
+XmlEditor::XmlEditor(Behavior::BehaviorFactory behavior_factory
+    : Dialog (behavior_factory, "dialogs.xml", SP_VERB_DIALOG_XML_EDITOR)
 {
     // TODO:  Insert widgets
 
index b494ade1fe078c7bcb6680a29eb149509450279a..65f25423f54de6ade15352b164601bc77b07d710 100644 (file)
@@ -22,10 +22,11 @@ namespace Dialog {
 
 class XmlEditor : public Dialog {
 public:
-    XmlEditor();
+    XmlEditor(Behavior::BehaviorFactory behavior_factory);
     virtual ~XmlEditor();
 
-    static XmlEditor *create() { return new XmlEditor(); }
+    static XmlEditor *create(Behavior::BehaviorFactory behavior_factory) 
+    { return new XmlEditor(behavior_factory); }
 
 protected:
 
index 8e388529eccb9650ce4005412b0ac266615c5d2c..c8219b7b24509e974fa3712bd86840e74ffa15b7 100644 (file)
 
 #include "libnr/nr-point.h"
 #include "message.h"
-
 #include <gtkmm/window.h>
 
+namespace Inkscape { namespace UI { namespace Widget { class Dock; } } }
+
 namespace Inkscape {
 namespace UI {
 namespace View {
@@ -135,6 +136,8 @@ struct EditWidgetInterface
     
     /// Open yes/no dialog with warning text and confirmation question.
     virtual bool warnDialog (gchar*) = 0;
+
+    virtual Inkscape::UI::Widget::Dock* getDock () = 0;
 };
 
 } // namespace View
index b87250e47420e5b96af13017e6d06a3bc9181d40..066d4d0ba2a68fd6334845f29072a0e92ac82c9e 100644 (file)
@@ -65,6 +65,8 @@
 #include "interface.h"
 #include "extension/db.h"
 
+#include "ui/dialog/dialog-manager.h"
+
 using namespace Inkscape::UI;
 using namespace Inkscape::UI::Widget;
 
@@ -152,7 +154,6 @@ EditWidget::initLayout()
     _viewport_table.attach(_svg_canvas.widget(), 1, 2, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
     _svg_canvas.widget().show_all();
 
-
     // The statusbar comes last and appears at the bottom of _main_window_table
     initStatusbar();
 
@@ -1532,6 +1533,12 @@ EditWidget::warnDialog (gchar* msg)
 }
 
 
+Inkscape::UI::Widget::Dock*
+EditWidget::getDock ()
+{
+    return &_dock;
+}
+
 void EditWidget::_namedview_modified (SPObject *obj, guint flags) {
     SPNamedView *nv = static_cast<SPNamedView *>(obj);
     if (flags & SP_OBJECT_MODIFIED_FLAG) {
index 443b10ac871090a576af4938cd77c8e33c0f0103..4341f667cb2981aeb3793d59cd42f5a423a9344f 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "ui/dialog/dialog-manager.h"
 #include "ui/view/edit-widget-interface.h"
+#include "ui/widget/dock.h"
 #include "ui/widget/selected-style.h"
 #include "ui/widget/ruler.h"
 #include "ui/widget/toolbox.h"
@@ -132,6 +133,8 @@ public:
     virtual void setMessage (Inkscape::MessageType type, gchar const* msg);
     virtual bool warnDialog (gchar*);
 
+    virtual Inkscape::UI::Widget::Dock* getDock ();
+
 protected:
     void _namedview_modified(SPObject *namedview, guint);
 
@@ -157,6 +160,7 @@ protected:
     Gtk::ToggleButton    _sticky_zoom;
     UI::Widget::SVGCanvas _svg_canvas;
     Gtk::HBox            _statusbar;
+    UI::Widget::Dock _dock;
     UI::Widget::SelectedStyle _selected_style_status;
     UI::Widget::ZoomStatus _zoom_status;
     Inkscape::Widgets::LayerSelector _layer_selector;
index 178e954a41208b710ea881d10c181569d055867e..7113857c84a03c00a8e1220e7e3862dd2a42df25 100644 (file)
@@ -15,6 +15,10 @@ ui_widget_libuiwidget_a_SOURCES =    \
        ui/widget/combo-enums.h \
        ui/widget/combo-text.cpp        \
        ui/widget/combo-text.h          \
+       ui/widget/dock.h                \
+       ui/widget/dock.cpp              \
+       ui/widget/dock-item.h           \
+       ui/widget/dock-item.cpp         \
        ui/widget/entity-entry.cpp      \
        ui/widget/entity-entry.h        \
        ui/widget/entry.cpp      \
diff --git a/src/ui/widget/dock-item.cpp b/src/ui/widget/dock-item.cpp
new file mode 100644 (file)
index 0000000..1e232cb
--- /dev/null
@@ -0,0 +1,539 @@
+/**
+ * \brief A custom Inkscape wrapper around gdl_dock_item
+ *
+ * Author:
+ *   Gustav Broberg <broberg@kth.se>
+ *
+ * Copyright (C) 2007 Authors
+ *
+ * Released under GNU GPL.  Read the file 'COPYING' for more information.
+ */
+
+#include "dock-item.h"
+#include "desktop.h"
+#include "inkscape.h"
+#include "ui/widget/dock.h"
+#include "widgets/icon.h"
+
+#include <gtk/gtk.h>
+
+#include <gtkmm/invisible.h>
+#include <gtkmm/stock.h>
+
+namespace Inkscape {
+namespace UI {
+namespace Widget {
+
+DockItem::DockItem(Dock& dock, const Glib::ustring& name, const Glib::ustring& long_name,
+                   const Glib::ustring& icon_name, State state) :
+    _dock (dock),
+    _prev_state (state),
+    _window (NULL),
+    _dock_item_action_area (NULL)
+{
+    /* Add a "signal_response" signal to the GdlDockItem, make sure it is
+     * only done once for the class.
+     */
+    static guint response_signal = 0;
+
+    if (response_signal == 0) {
+        response_signal = g_signal_new ("signal_response",
+                                        GDL_TYPE_DOCK_ITEM,
+                                        G_SIGNAL_RUN_FIRST,
+                                        0,
+                                        NULL, NULL,
+                                        g_cclosure_marshal_VOID__INT,
+                                        G_TYPE_NONE, 1, G_TYPE_INT);
+    }
+
+
+    if (!icon_name.empty()) {
+        Gtk::Widget *icon = sp_icon_get_icon(icon_name, Inkscape::ICON_SIZE_MENU);
+        if (icon) {
+            // check icon type (inkscape, gtk, none) 
+            if ( SP_IS_ICON(icon->gobj()) ) { 
+                SPIcon* sp_icon = SP_ICON(icon->gobj());
+                sp_icon_fetch_pixbuf(sp_icon);
+                _icon_pixbuf = Glib::wrap(sp_icon->pb, true);
+            } else if ( GTK_IS_IMAGE(icon->gobj()) ) {
+                _icon_pixbuf = Gtk::Invisible().render_icon(Gtk::StockID(icon_name),
+                                                            Gtk::ICON_SIZE_MENU);
+            }
+            delete icon;
+
+            _gdl_dock_item = 
+                gdl_dock_item_new_with_pixbuf_icon(name.c_str(), long_name.c_str(), _icon_pixbuf->gobj(),
+                                                   GDL_DOCK_ITEM_BEH_NORMAL);
+        }
+    } else {
+        _gdl_dock_item = 
+            gdl_dock_item_new(name.c_str(), long_name.c_str(), GDL_DOCK_ITEM_BEH_NORMAL);
+    }
+
+    _frame.set_shadow_type(Gtk::SHADOW_IN);
+    gtk_container_add (GTK_CONTAINER (_gdl_dock_item), GTK_WIDGET (_frame.gobj()));
+    _frame.add(_dock_item_box);
+    _dock_item_box.set_border_width(3);
+
+    signal_drag_begin().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onDragBegin));
+    signal_drag_end().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onDragEnd));
+    signal_hide().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onHide), false);
+    signal_show().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onShow), false);
+    signal_state_changed().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onStateChanged));
+    signal_delete_event().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onDeleteEvent));
+
+    _dock.addItem(*this, (_prev_state == FLOATING_STATE ? FLOATING : TOP));
+
+    show_all();
+}
+
+DockItem::~DockItem()
+{
+    g_free(_gdl_dock_item);
+}
+
+Gtk::Widget&
+DockItem::getWidget()
+{
+    return *Glib::wrap(GTK_WIDGET(_gdl_dock_item));
+}
+
+GtkWidget *
+DockItem::gobj()
+{
+    return _gdl_dock_item;
+}
+
+Gtk::VBox *
+DockItem::get_vbox()
+{
+    return &_dock_item_box;
+}
+
+
+bool
+DockItem::isAttached() const
+{
+    return GDL_DOCK_OBJECT_ATTACHED (_gdl_dock_item);
+}
+
+
+bool
+DockItem::isFloating() const
+{
+    gboolean floating = FALSE;
+    if (GDL_IS_DOCK (gdl_dock_object_get_parent_object (GDL_DOCK_OBJECT (_gdl_dock_item)))) {
+        GdlDock* dock = GDL_DOCK (gdl_dock_object_get_parent_object (GDL_DOCK_OBJECT (_gdl_dock_item)));
+        g_object_get (dock,
+                      "floating", &floating,
+                      NULL);
+    }
+    return floating;
+}
+
+bool
+DockItem::isIconified() const
+{
+    return GDL_DOCK_ITEM_ICONIFIED (_gdl_dock_item);
+}
+
+DockItem::State
+DockItem::getState() const
+{
+    return (isAttached() ? (isFloating() ? FLOATING_STATE : DOCKED_STATE) : UNATTACHED);
+}
+
+DockItem::State
+DockItem::getPrevState() const
+{
+    return _prev_state;
+}
+
+DockItem::Placement
+DockItem::getPlacement() const
+{
+    GdlDockPlacement placement = (GdlDockPlacement)NONE;
+    gdl_dock_object_child_placement(gdl_dock_object_get_parent_object (GDL_DOCK_OBJECT(_gdl_dock_item)),
+                                    GDL_DOCK_OBJECT(_gdl_dock_item),
+                                    &placement);
+    return (Placement)placement;
+}
+
+
+void
+DockItem::addButton(Gtk::Button* button, int response_id)
+{
+    // Create a button box for the response buttons if it's the first button to be added
+    if (!_dock_item_action_area) {
+        _dock_item_action_area = new Gtk::HButtonBox(Gtk::BUTTONBOX_END, 6);
+        _dock_item_box.pack_end(*_dock_item_action_area, Gtk::PACK_SHRINK, 0);
+        _dock_item_action_area->set_border_width(6);
+    }
+
+    _dock_item_action_area->pack_start(*button);
+}
+
+void
+DockItem::hide()
+{
+    gdl_dock_item_hide_item (GDL_DOCK_ITEM(_gdl_dock_item));
+}
+
+void
+DockItem::show()
+{
+    gdl_dock_item_show_item (GDL_DOCK_ITEM(_gdl_dock_item));
+}
+
+void
+DockItem::show_all()
+{
+    gtk_widget_show_all(_gdl_dock_item);
+}
+
+void
+DockItem::present()
+{
+    // iconified
+    if (isIconified()) {
+        show();
+        return;
+    }
+    
+    // unattached
+    if (!isAttached()) {
+        gdl_dock_item_show_item (GDL_DOCK_ITEM(_gdl_dock_item));
+        return;
+    }
+
+    // tabbed
+    if (getPlacement() == CENTER) {
+        int i = gtk_notebook_page_num (GTK_NOTEBOOK (_gdl_dock_item->parent),
+                                       GTK_WIDGET (_gdl_dock_item));
+        if (i >= 0)
+            gtk_notebook_set_current_page (GTK_NOTEBOOK (_gdl_dock_item->parent), i);
+        return;
+    }
+
+    // we're already present, do nothing
+}
+
+
+void 
+DockItem::get_position(int& x, int& y)
+{ 
+    if (_getWindow()) {
+        _getWindow()->get_position(x, y);
+    } else {
+        x = _x;
+        y = _y;
+    }
+}
+
+void 
+DockItem::get_size(int& width, int& height)
+{ 
+    if (_window) {
+        _window->get_size(width, height);
+    } else {
+        width = get_vbox()->get_width();
+        height = get_vbox()->get_height();
+    }
+}
+
+
+void
+DockItem::resize(int width, int height) 
+{
+    if (_window)
+        _window->resize(width, height);
+}
+
+
+void
+DockItem::move(int x, int y)
+{
+    if (_window)
+        _window->move(x, y);
+}
+
+void
+DockItem::set_position(Gtk::WindowPosition position)
+{
+    if (_window)
+        _window->set_position(position);
+}
+
+void
+DockItem::set_size_request(int width, int height)
+{
+    getWidget().set_size_request(width, height);
+}
+
+void 
+DockItem::size_request(Gtk::Requisition& requisition)
+{ 
+    getWidget().size_request(requisition);
+}
+
+
+void
+DockItem::set_title(Glib::ustring title)
+{
+    g_object_set (_gdl_dock_item,
+                  "long-name", title.c_str(),
+                  NULL);
+
+    gdl_dock_item_set_tablabel(GDL_DOCK_ITEM(_gdl_dock_item),
+                               gtk_label_new (title.c_str()));
+}
+
+/* Signal wrappers */
+
+Glib::SignalProxy0<void>
+DockItem::signal_show()
+{ 
+    return Glib::SignalProxy0<void>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)), 
+                                    &_signal_show_proxy);
+}
+
+Glib::SignalProxy0<void>
+DockItem::signal_hide()
+{ 
+    return Glib::SignalProxy0<void>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)), 
+                                    &_signal_hide_proxy);
+}
+
+Glib::SignalProxy1<bool, GdkEventAny *>
+DockItem::signal_delete_event()
+{
+    return Glib::SignalProxy1<bool, GdkEventAny *>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)),
+                                                  &_signal_delete_event_proxy);
+}
+
+Glib::SignalProxy1<void, int>
+DockItem::signal_response()
+{
+    return Glib::SignalProxy1<void, int>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)), 
+                                         &_signal_response_proxy);
+}
+
+Glib::SignalProxy0<void>
+DockItem::signal_drag_begin()
+{
+    return Glib::SignalProxy0<void>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)),
+                                    &_signal_drag_begin_proxy);
+}
+
+Glib::SignalProxy1<void, bool>
+DockItem::signal_drag_end()
+{
+    return Glib::SignalProxy1<void, bool>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)),
+                                          &_signal_drag_end_proxy);
+}
+
+sigc::signal<void, DockItem::State, DockItem::State>
+DockItem::signal_state_changed()
+{
+    return _signal_state_changed;
+}
+
+void
+DockItem::_onHideWindow()
+{
+    if (_window)
+        _window->get_position(_x, _y);
+}
+
+void
+DockItem::_onHide()
+{
+    _signal_state_changed.emit(UNATTACHED, getState());
+}
+
+void
+DockItem::_onShow()
+{
+    _signal_state_changed.emit(UNATTACHED, getState());
+}
+
+void
+DockItem::_onDragBegin()
+{
+    _prev_state = getState();
+    if (_prev_state == FLOATING_STATE)
+        _dock.toggleDockable(getWidget().get_width(), getWidget().get_height());
+}
+
+void
+DockItem::_onDragEnd(bool)
+{
+    State state = getState();
+
+    if (state != _prev_state)
+        _signal_state_changed.emit(_prev_state, state);
+
+    if (state == FLOATING_STATE) {
+        if (_prev_state == FLOATING_STATE)
+            _dock.toggleDockable();
+    }
+
+    _prev_state = state;
+}
+
+bool
+DockItem::_onKeyPress(GdkEventKey *event)
+{
+    gboolean return_value;
+    g_signal_emit_by_name (_gdl_dock_item, "key_press_event", event, &return_value);
+    return return_value;
+}
+
+void
+DockItem::_onStateChanged(State prev_state, State new_state)
+{
+    _window = _getWindow();
+
+    if (new_state == FLOATING_STATE) {
+        _window->signal_hide().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onHideWindow));
+        _signal_key_press_event_connection = 
+            _window->signal_key_press_event().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onKeyPress));
+    }
+}
+
+
+bool
+DockItem::_onDeleteEvent(GdkEventAny *event)
+{
+    hide();
+    return false;
+}
+
+
+Gtk::Window *
+DockItem::_getWindow()
+{
+    g_return_val_if_fail(_gdl_dock_item, 0);
+    Gtk::Container *parent = getWidget().get_parent();
+    parent = (parent ? parent->get_parent() : 0);
+    return (parent ? dynamic_cast<Gtk::Window *>(parent) : 0);
+}
+
+const Glib::SignalProxyInfo
+DockItem::_signal_show_proxy = 
+{
+    "show",
+    (GCallback) &Glib::SignalProxyNormal::slot0_void_callback,
+    (GCallback) &Glib::SignalProxyNormal::slot0_void_callback
+};
+
+const Glib::SignalProxyInfo
+DockItem::_signal_hide_proxy = 
+{
+    "hide",
+    (GCallback) &Glib::SignalProxyNormal::slot0_void_callback,
+    (GCallback) &Glib::SignalProxyNormal::slot0_void_callback
+};
+
+
+const Glib::SignalProxyInfo
+DockItem::_signal_delete_event_proxy = 
+{
+    "delete_event",
+    (GCallback) &_signal_delete_event_callback,
+    (GCallback) &_signal_delete_event_callback
+};
+
+
+const Glib::SignalProxyInfo
+DockItem::_signal_response_proxy = 
+{
+    "signal_response",
+    (GCallback) &_signal_response_callback,
+    (GCallback) &_signal_response_callback
+};
+
+const Glib::SignalProxyInfo
+DockItem::_signal_drag_begin_proxy = 
+{
+    "dock-drag-begin",
+    (GCallback) &Glib::SignalProxyNormal::slot0_void_callback,
+    (GCallback) &Glib::SignalProxyNormal::slot0_void_callback
+};
+
+
+const Glib::SignalProxyInfo
+DockItem::_signal_drag_end_proxy = 
+{
+    "dock_drag_end",
+    (GCallback) &_signal_drag_end_callback,
+    (GCallback) &_signal_drag_end_callback
+};
+
+
+gboolean
+DockItem::_signal_delete_event_callback(GtkWidget *self, GdkEventAny *event, void *data)
+{
+    using namespace Gtk;
+    typedef sigc::slot<bool, GdkEventAny *> SlotType;
+
+    if (Glib::ObjectBase::_get_current_wrapper((GObject *) self)) {
+        try {
+            if(sigc::slot_base *const slot = Glib::SignalProxyNormal::data_to_slot(data))
+                return static_cast<int>( (*static_cast<SlotType*>(slot))(event) );
+        } catch(...) {
+            Glib::exception_handlers_invoke();
+        }
+    }
+
+    typedef gboolean RType;
+    return RType();
+}
+
+void 
+DockItem::_signal_response_callback(GtkWidget *self, gint response_id, void *data)
+{
+    using namespace Gtk;
+    typedef sigc::slot<void, int> SlotType;
+
+    if (Glib::ObjectBase::_get_current_wrapper((GObject *) self)) {
+        try {
+            if(sigc::slot_base *const slot = Glib::SignalProxyNormal::data_to_slot(data))
+                (*static_cast<SlotType *>(slot))(response_id);
+        } catch(...) {
+            Glib::exception_handlers_invoke();
+        }
+    }
+}
+
+void 
+DockItem::_signal_drag_end_callback(GtkWidget *self, gboolean cancelled, void *data)
+{
+    using namespace Gtk;
+    typedef sigc::slot<void, bool> SlotType;
+
+    if (Glib::ObjectBase::_get_current_wrapper((GObject *) self)) {
+        try {
+            if(sigc::slot_base *const slot = Glib::SignalProxyNormal::data_to_slot(data))
+                (*static_cast<SlotType *>(slot))(cancelled);
+        } catch(...) {
+            Glib::exception_handlers_invoke();
+        }
+    }
+}
+
+
+} // namespace Widget
+} // namespace UI
+} // namespace Inkscape
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/ui/widget/dock-item.h b/src/ui/widget/dock-item.h
new file mode 100644 (file)
index 0000000..38d13a5
--- /dev/null
@@ -0,0 +1,165 @@
+/**
+ * \brief A custom wrapper around gdl-dock-item
+ *
+ * Author:
+ *   Gustav Broberg <broberg@kth.se>
+ *
+ * Copyright (C) 2007 Authors
+ *
+ * Released under GNU GPL.  Read the file 'COPYING' for more information.
+ */
+
+
+#ifndef INKSCAPE_UI_WIGET_DOCK_ITEM_H
+#define INKSCAPE_UI_WIGET_DOCK_ITEM_H
+
+#include <gtkmm/button.h>
+#include <gtkmm/buttonbox.h>
+#include <gtkmm/frame.h>
+#include <gtkmm/paned.h>
+#include <gtkmm/window.h>
+
+#include "libgdl/libgdl.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Widget {
+
+class Dock;
+
+class DockItem {
+
+public:
+
+    enum State { UNATTACHED, FLOATING_STATE, DOCKED_STATE };
+
+    enum Placement { 
+        NONE     = GDL_DOCK_NONE,
+        TOP      = GDL_DOCK_TOP,
+        BOTTOM   = GDL_DOCK_BOTTOM,
+        RIGHT    = GDL_DOCK_RIGHT,
+        LEFT     = GDL_DOCK_LEFT,
+        CENTER   = GDL_DOCK_CENTER,
+        FLOATING = GDL_DOCK_FLOATING
+    };
+
+    DockItem(Dock& dock, const Glib::ustring& name, const Glib::ustring& long_name, 
+             const Glib::ustring& icon_name, State state);
+
+    ~DockItem();
+
+    Gtk::Widget& getWidget();
+    GtkWidget *gobj();
+
+    Gtk::VBox *get_vbox();
+
+    void get_position(int& x, int& y);
+    void get_size(int& width, int& height);
+
+    void resize(int width, int height);
+    void move(int x, int y);
+    void set_position(Gtk::WindowPosition);
+    void set_size_request(int width, int height);
+    void size_request(Gtk::Requisition& requisition);
+    void set_title(Glib::ustring title);
+
+    bool isAttached() const;
+    bool isFloating() const;
+    bool isIconified() const;
+    State getState() const;
+    State getPrevState() const;
+    Placement getPlacement() const;
+
+    void addButton(Gtk::Button *button, int response_id);
+
+    void hide();
+    void show();
+    void show_all();
+
+    void present();
+
+    Glib::SignalProxy0<void> signal_show();
+    Glib::SignalProxy0<void> signal_hide();
+    Glib::SignalProxy1<bool, GdkEventAny *> signal_delete_event();
+    Glib::SignalProxy1<void, int> signal_response();
+    Glib::SignalProxy0<void> signal_drag_begin();
+    Glib::SignalProxy1<void, bool> signal_drag_end();
+
+    sigc::signal<void, State, State> signal_state_changed();
+
+private:
+    Dock &_dock;              //< parent dock
+
+    State _prev_state;        //< last known state
+
+    int _prev_position;
+
+    Gtk::Window *_window;     //< reference to floating window, if any 
+    int _x, _y;               //< last known position of window, if floating
+
+    GtkWidget *_gdl_dock_item;
+    Glib::RefPtr<Gdk::Pixbuf> _icon_pixbuf;
+
+    /** Interface widgets, will be packed like 
+     * gdl_dock_item -> _frame -> _dock_item_box -> (_dock_item_action_area) 
+     */
+    Gtk::Frame _frame;
+    Gtk::VBox _dock_item_box;
+    Gtk::HButtonBox *_dock_item_action_area;
+
+    /** Internal signal handlers */
+    void _onHide();
+    void _onHideWindow();
+    void _onShow();
+    void _onResponse(int response_id);
+    void _onDragBegin();
+    void _onDragEnd(bool cancelled);
+    bool _onKeyPress(GdkEventKey *event);
+    void _onStateChanged(State prev_state, State new_state);
+    bool _onDeleteEvent(GdkEventAny *event);
+
+    void _onFoo();
+
+    sigc::connection _signal_key_press_event_connection;
+
+    /** GdlDockItem signal proxy structures */
+    static const Glib::SignalProxyInfo _signal_show_proxy;
+    static const Glib::SignalProxyInfo _signal_hide_proxy;
+    static const Glib::SignalProxyInfo _signal_delete_event_proxy;
+    static const Glib::SignalProxyInfo _signal_response_proxy;
+    static const Glib::SignalProxyInfo _signal_drag_begin_proxy;
+    static const Glib::SignalProxyInfo _signal_drag_end_proxy;
+
+    static gboolean _signal_delete_event_callback(GtkWidget *self, GdkEventAny *event, void *data);
+    static void _signal_drag_end_callback(GtkWidget* self, gboolean p0, void* data);
+
+    /** Internal helpers */
+    Gtk::Window *_getWindow();   //< gives the parent window, if the dock item has one (i.e. it's floating)
+
+    /** In order to emulate a signal_response signal like the one for Gtk::Dialog we inject a new
+     * signal into GdlDockItem. This signal will be emitted when a button in the dock item added
+     * through the addButton(..., response_id) method, is clicked. 
+     */
+    static void _signal_response_callback(GtkWidget* self, gint p0, void* data);
+
+    sigc::signal<void, State, State> _signal_state_changed;
+
+    DockItem();
+};
+
+} // namespace Widget
+} // namespace UI
+} // namespace Inkscape
+
+#endif // INKSCAPE_UI_WIGET_DOCK_ITEM_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/ui/widget/dock.cpp b/src/ui/widget/dock.cpp
new file mode 100644 (file)
index 0000000..828b704
--- /dev/null
@@ -0,0 +1,235 @@
+/**
+ * \brief A desktop dock pane to dock dialogs.
+ *
+ * Author:
+ *   Gustav Broberg <broberg@kth.se>
+ *
+ * Copyright (C) 2007 Authors
+ *
+ * Released under GNU GPL.  Read the file 'COPYING' for more information.
+ */
+
+#include "inkscape.h"
+#include "desktop.h"
+
+#include "dock.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Widget {
+
+namespace {
+
+void hideCallback(GtkObject *object, gpointer dock_ptr)
+{
+    g_return_if_fail( dock_ptr != NULL );
+
+    Dock *dock = (Dock *)dock_ptr;
+    dock->hide();
+}
+
+void unhideCallback(GtkObject *object, gpointer dock_ptr)
+{
+    g_return_if_fail( dock_ptr != NULL );
+
+    Dock *dock = (Dock *)dock_ptr;
+    dock->show();
+}
+
+}
+
+const int Dock::_default_empty_width = 0;
+const int Dock::_default_dock_bar_width = 36;
+
+
+Dock::Dock(Gtk::Orientation orientation)
+    : _gdl_dock (GDL_DOCK (gdl_dock_new())),
+      _gdl_dock_bar (GDL_DOCK_BAR (gdl_dock_bar_new(GDL_DOCK(_gdl_dock)))),
+      _scrolled_window (Gtk::manage(new Gtk::ScrolledWindow))
+{
+    gdl_dock_bar_set_orientation(_gdl_dock_bar, static_cast<GtkOrientation>(orientation));
+
+    switch (orientation) {
+        case Gtk::ORIENTATION_VERTICAL:
+            _dock_box = Gtk::manage(new Gtk::HBox());
+            _paned = Gtk::manage(new Gtk::VPaned());
+            break;
+        case Gtk::ORIENTATION_HORIZONTAL:
+            _dock_box = Gtk::manage(new Gtk::VBox());
+            _paned = Gtk::manage(new Gtk::HPaned());
+    }
+
+    _scrolled_window->add(*_dock_box);
+    _scrolled_window->set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
+
+    _paned->pack1(*Glib::wrap(GTK_WIDGET(_gdl_dock)), false, false);
+    _paned->pack2(_filler, true, false);
+
+    _dock_box->pack_start(*_paned, Gtk::PACK_EXPAND_WIDGET);
+    _dock_box->pack_end(*Gtk::manage(Glib::wrap(GTK_WIDGET(_gdl_dock_bar))), Gtk::PACK_SHRINK);
+    _dock_box->get_parent()->set_resize_mode(Gtk::RESIZE_PARENT);
+
+    _scrolled_window->set_size_request(0);
+
+    g_object_set (GDL_DOCK_OBJECT(_gdl_dock)->master,
+                  "switcher-style", GDL_SWITCHER_STYLE_BOTH,
+                  NULL);
+
+    g_signal_connect(G_OBJECT(INKSCAPE), "dialogs_hide", G_CALLBACK(hideCallback), (void *)this);
+    g_signal_connect(G_OBJECT(INKSCAPE), "dialogs_unhide", G_CALLBACK(unhideCallback), (void *)this);
+
+    signal_layout_changed().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::Dock::_onLayoutChanged));
+}    
+
+Dock::~Dock()
+{
+    g_free(_gdl_dock);
+    g_free(_gdl_dock_bar);
+}
+
+void
+Dock::addItem(DockItem& item, DockItem::Placement placement)
+{
+    _dock_items.push_back(&item);
+    gdl_dock_add_item(_gdl_dock, GDL_DOCK_ITEM(item.gobj()), (GdlDockPlacement)placement);
+
+    // FIXME: This is a hack to prevent the dock from expanding the main window, this can't be done
+    // initially as the paned doesn't exist.
+    if (Gtk::Paned *paned = getParentPaned())
+       paned->set_resize_mode(Gtk::RESIZE_QUEUE);
+}
+
+Gtk::Widget&
+Dock::getWidget()
+{
+     return *_scrolled_window;
+}
+
+Gtk::Paned *
+Dock::getParentPaned()
+{
+    g_return_val_if_fail(_dock_box, 0);
+    Gtk::Container *parent = getWidget().get_parent();
+    return (parent != 0 ? dynamic_cast<Gtk::Paned *>(parent) : 0);
+}
+
+
+Gtk::Paned *
+Dock::getPaned()
+{
+    return _paned;
+}
+
+
+bool
+Dock::isEmpty() const
+{
+    std::list<const DockItem *>::const_iterator
+       i = _dock_items.begin(),
+       e = _dock_items.end();
+
+    for (; i != e; ++i)
+       if ((*i)->getState() == DockItem::DOCKED_STATE)
+           return false;
+
+    return true;
+}
+
+bool
+Dock::hasIconifiedItems() const
+{
+    std::list<const DockItem *>::const_iterator
+       i = _dock_items.begin(),
+       e = _dock_items.end();
+
+    for (; i != e; ++i)
+       if ((*i)->isIconified())
+           return true;
+    
+    return false;
+}
+
+void
+Dock::hide()
+{
+    getWidget().hide();
+}
+
+void 
+Dock::show()
+{
+    getWidget().show();
+}
+
+void
+Dock::toggleDockable(int width, int height)
+{
+    static int prev_horizontal_position, prev_vertical_position;
+
+    Gtk::Paned *parent_paned = getParentPaned();
+
+    if (width > 0 && height > 0) {
+       prev_horizontal_position = parent_paned->get_position();
+       prev_vertical_position = _paned->get_position();
+
+       if (getWidget().get_width() < width)
+           parent_paned->set_position(parent_paned->get_width() - width);
+       
+       if (_paned->get_position() < height)
+           _paned->set_position(height);
+
+    } else {
+       parent_paned->set_position(prev_horizontal_position);
+       _paned->set_position(prev_vertical_position);
+    }
+
+}
+
+Glib::SignalProxy0<void>
+Dock::signal_layout_changed()
+{
+    return Glib::SignalProxy0<void>(Glib::wrap(GTK_WIDGET(_gdl_dock)), 
+                                    &_signal_layout_changed_proxy);
+}
+
+void
+Dock::_onLayoutChanged()
+{
+    if (isEmpty()) {
+
+       if (hasIconifiedItems())
+           _scrolled_window->set_size_request(_default_dock_bar_width);
+       else
+           _scrolled_window->set_size_request(_default_empty_width);
+
+        getParentPaned()->set_position(INT_MAX);
+    } else {
+       _scrolled_window->set_size_request(-1);
+    }
+}
+
+
+const Glib::SignalProxyInfo
+Dock::_signal_layout_changed_proxy = 
+{
+    "layout-changed",
+    (GCallback) &Glib::SignalProxyNormal::slot0_void_callback,
+    (GCallback) &Glib::SignalProxyNormal::slot0_void_callback
+};
+
+
+} // namespace Widget
+} // namespace UI
+} // namespace Inkscape
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 
diff --git a/src/ui/widget/dock.h b/src/ui/widget/dock.h
new file mode 100644 (file)
index 0000000..be0c4ef
--- /dev/null
@@ -0,0 +1,97 @@
+/**
+ * \brief A desktop dock pane to dock dialogs, a custom wrapper around gdl-dock.
+ *
+ * Author:
+ *   Gustav Broberg <broberg@kth.se>
+ *
+ * Copyright (C) 2007 Authors
+ *
+ * Released under GNU GPL.  Read the file 'COPYING' for more information.
+ */
+
+#ifndef INKSCAPE_UI_WIDGET_DOCK_H
+#define INKSCAPE_UI_WIDGET_DOCK_H
+
+#include <gtkmm/scrolledwindow.h>
+#include <gtkmm/box.h>
+#include <gtkmm/paned.h>
+
+#include <list>
+
+#include "ui/widget/dock-item.h"
+
+#include "libgdl/libgdl.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Widget {
+
+class Dock {
+
+public:
+
+    Dock(Gtk::Orientation orientation=Gtk::ORIENTATION_VERTICAL);
+    ~Dock();
+
+    void addItem(DockItem& item, DockItem::Placement placement);
+
+    Gtk::Widget& getWidget();     //< return the top widget
+    Gtk::Paned *getParentPaned();
+
+    Gtk::Paned *getPaned();
+
+    bool isEmpty() const;     //< true iff none of the dock's items are in state != UNATTACHED
+    bool hasIconifiedItems() const;
+
+    Glib::SignalProxy0<void> signal_layout_changed();
+
+    void hide();
+    void show();
+
+    /** Toggle size of dock between the previous dimensions and the ones sent as parameters */
+    void toggleDockable(int width=0, int height=0);
+
+protected:
+
+    std::list<const DockItem *> _dock_items;   //< added dock items
+
+    /** Interface widgets, will be packed like 
+     * _scrolled_window -> (_dock_box -> (_paned -> (_dock -> _filler) | _dock_bar))
+     */
+    Gtk::Box *_dock_box;
+    Gtk::Paned* _paned;
+    GdlDock *_gdl_dock;
+    GdlDockBar *_gdl_dock_bar;
+    Gtk::VBox _filler;
+    Gtk::ScrolledWindow *_scrolled_window;
+
+    /** Internal signal handlers */
+    void _onLayoutChanged();
+
+    void _onFoo();
+
+    /** GdlDock signal proxy structures */
+    static const Glib::SignalProxyInfo _signal_layout_changed_proxy;
+
+    /** Standard widths */
+    static const int _default_empty_width;
+    static const int _default_dock_bar_width;
+};
+
+} // namespace Widget
+} // namespace UI
+} // namespace Inkscape
+
+#endif //INKSCAPE_UI_DIALOG_BEHAVIOUR_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 
+
index 5f7b51a0303c33b9914e18781cb3b1c40ef4e365..8d2d4e69f239aa2ac31aab98e84202b7674549ce 100644 (file)
@@ -107,7 +107,6 @@ sp_action_get_title(SPAction const *action)
 
 } // end of sp_action_get_title()
 
-
 namespace Inkscape {
 
 /// \todo !!!FIXME:: kill this, use DialogManager instead!!!
@@ -115,7 +114,12 @@ namespace Inkscape {
 class PanelDialog : public Inkscape::UI::Dialog::Dialog
 {
 public:
-    PanelDialog(char const *prefs_path, int const verb_num) : Dialog(prefs_path, verb_num) {}
+    PanelDialog(char const *prefs_path, int const verb_num) : 
+        Dialog(
+            (prefs_get_int_attribute_limited ("options.dialogtype", "value", UI::Dialog::DOCK, 0, 1) == UI::Dialog::FLOATING ?
+             &UI::Dialog::Behavior::FloatingBehavior::create :
+             &UI::Dialog::Behavior::DockBehavior::create),
+            prefs_path, verb_num) {}
 /*
     virtual Glib::ustring getName() const {return "foo";}
     virtual Glib::ustring getDesc() const {return "bar";}
@@ -146,6 +150,7 @@ static void show_panel( Inkscape::UI::Widget::Panel &panel, char const *prefs_pa
     }
 }
 
+
 /** \brief A class to encompass all of the verbs which deal with
            file operations. */
 class FileVerb : public Verb {
@@ -1001,6 +1006,8 @@ SelectionVerb::perform(SPAction *action, void *data, void *pdata)
     if (!dt)
         return;
 
+    g_assert(dt->_dlg_mgr != NULL);
+
     switch (reinterpret_cast<std::size_t>(data)) {
         case SP_VERB_SELECTION_TO_FRONT:
             sp_selection_raise_to_top();
@@ -1088,6 +1095,7 @@ SelectionVerb::perform(SPAction *action, void *data, void *pdata)
             sp_selected_path_reverse();
             break;
         case SP_VERB_SELECTION_TRACE:
+            inkscape_dialogs_unhide();
             dt->_dlg_mgr->showDialog("Trace");
             break;
         case SP_VERB_SELECTION_CREATE_BITMAP:
@@ -1101,6 +1109,7 @@ SelectionVerb::perform(SPAction *action, void *data, void *pdata)
             sp_selected_path_break_apart();
             break;
         case SP_VERB_SELECTION_GRIDTILE:
+            inkscape_dialogs_unhide();
             dt->_dlg_mgr->showDialog("TileDialog");
             break;
         default:
@@ -1652,7 +1661,8 @@ ZoomVerb::perform(SPAction *action, void *data, void *pdata)
             dt->displayModeToggle();
             break;
         case SP_VERB_VIEW_ICON_PREVIEW:
-            show_panel( Inkscape::UI::Dialogs::IconPreviewPanel::getInstance(), "dialogs.iconpreview", SP_VERB_VIEW_ICON_PREVIEW );
+            inkscape_dialogs_unhide();
+            dt->_dlg_mgr->showDialog("IconPreviewPanel");
             break;
         default:
             break;
@@ -1688,12 +1698,12 @@ DialogVerb::perform(SPAction *action, void *data, void *pdata)
             dt->_dlg_mgr->showDialog("DocumentProperties");
             break;
         case SP_VERB_DIALOG_FILL_STROKE:
-            sp_object_properties_dialog();
-            // dt->_dlg_mgr->showDialog("FillAndStroke");
+            // sp_object_properties_dialog();
+            dt->_dlg_mgr->showDialog("FillAndStroke");
             break;
         case SP_VERB_DIALOG_SWATCHES:
             show_panel( Inkscape::UI::Dialogs::SwatchesPanel::getInstance(), "dialogs.swatches", SP_VERB_DIALOG_SWATCHES);
-            break;
+             break;
         case SP_VERB_DIALOG_TRANSFORM:
             dt->_dlg_mgr->showDialog("Transformation");
             break;
@@ -1743,7 +1753,7 @@ DialogVerb::perform(SPAction *action, void *data, void *pdata)
             dt->_dlg_mgr->showDialog("ExtensionEditor");
             break;
         case SP_VERB_DIALOG_LAYERS:
-            show_panel( Inkscape::UI::Dialogs::LayersPanel::getInstance(), "dialogs.layers", SP_VERB_DIALOG_LAYERS );
+            dt->_dlg_mgr->showDialog("LayersPanel");
             break;
         case SP_VERB_DIALOG_LIVE_PATH_EFFECT:
             dt->_dlg_mgr->showDialog("LivePathEffect");
@@ -1788,6 +1798,7 @@ HelpVerb::perform(SPAction *action, void *data, void *pdata)
         */
 
         case SP_VERB_HELP_MEMORY:
+            inkscape_dialogs_unhide();
             dt->_dlg_mgr->showDialog("Memory");
             break;
         default:
index 4b744e3dffc1db2349036de76302c24333c951e3..f33950f70b98bd44ef75a8a6d8431c20c17fb2af 100644 (file)
@@ -48,6 +48,8 @@
 #include "widgets/spw-utilities.h"
 #include "widgets/spinbutton-events.h"
 #include "widgets/layer-selector.h"
+#include "ui/dialog/dialog-manager.h"
+#include "ui/widget/dock.h"
 #include "ui/widget/selected-style.h"
 #include "sp-item.h"
 #include "dialogs/swatches.h"
@@ -174,6 +176,7 @@ sp_desktop_widget_init (SPDesktopWidget *dtw)
 {
     GtkWidget *widget;
     GtkWidget *tbl;
+    GtkWidget *canvas_tbl;
 
     GtkWidget *hbox;
     GtkWidget *eventbox;
@@ -221,9 +224,11 @@ sp_desktop_widget_init (SPDesktopWidget *dtw)
     dtw->tool_toolbox = sp_tool_toolbox_new ();
     gtk_box_pack_start (GTK_BOX (hbox), dtw->tool_toolbox, FALSE, TRUE, 0);
 
-    tbl = gtk_table_new (4, 3, FALSE);
+    tbl = gtk_table_new (2, 3, FALSE);
     gtk_box_pack_start (GTK_BOX (hbox), tbl, TRUE, TRUE, 1);
 
+    canvas_tbl = gtk_table_new (3, 3, FALSE);
+
     /* Horizontal ruler */
     eventbox = gtk_event_box_new ();
     dtw->hruler = sp_hruler_new ();
@@ -231,7 +236,7 @@ sp_desktop_widget_init (SPDesktopWidget *dtw)
     sp_ruler_set_metric (GTK_RULER (dtw->hruler), SP_PT);
     gtk_tooltips_set_tip (dtw->tt, dtw->hruler_box, gettext(sp_unit_get_plural (&sp_unit_get_by_id(SP_UNIT_PT))), NULL);
     gtk_container_add (GTK_CONTAINER (eventbox), dtw->hruler);
-    gtk_table_attach (GTK_TABLE (tbl), eventbox, 1, 2, 0, 1, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(GTK_FILL), widget->style->xthickness, 0);
+    gtk_table_attach (GTK_TABLE (canvas_tbl), eventbox, 1, 2, 0, 1, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(GTK_FILL), widget->style->xthickness, 0);
     g_signal_connect (G_OBJECT (eventbox), "button_press_event", G_CALLBACK (sp_dt_hruler_event), dtw);
     g_signal_connect (G_OBJECT (eventbox), "button_release_event", G_CALLBACK (sp_dt_hruler_event), dtw);
     g_signal_connect (G_OBJECT (eventbox), "motion_notify_event", G_CALLBACK (sp_dt_hruler_event), dtw);
@@ -243,7 +248,7 @@ sp_desktop_widget_init (SPDesktopWidget *dtw)
     sp_ruler_set_metric (GTK_RULER (dtw->vruler), SP_PT);
     gtk_tooltips_set_tip (dtw->tt, dtw->vruler_box, gettext(sp_unit_get_plural (&sp_unit_get_by_id(SP_UNIT_PT))), NULL);
     gtk_container_add (GTK_CONTAINER (eventbox), GTK_WIDGET (dtw->vruler));
-    gtk_table_attach (GTK_TABLE (tbl), eventbox, 0, 1, 1, 2, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(GTK_FILL), 0, widget->style->ythickness);
+    gtk_table_attach (GTK_TABLE (canvas_tbl), eventbox, 0, 1, 1, 2, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(GTK_FILL), 0, widget->style->ythickness);
     g_signal_connect (G_OBJECT (eventbox), "button_press_event", G_CALLBACK (sp_dt_vruler_event), dtw);
     g_signal_connect (G_OBJECT (eventbox), "button_release_event", G_CALLBACK (sp_dt_vruler_event), dtw);
     g_signal_connect (G_OBJECT (eventbox), "motion_notify_event", G_CALLBACK (sp_dt_vruler_event), dtw);
@@ -251,7 +256,8 @@ sp_desktop_widget_init (SPDesktopWidget *dtw)
     /* Horizontal scrollbar */
     dtw->hadj = (GtkAdjustment *) gtk_adjustment_new (0.0, -4000.0, 4000.0, 10.0, 100.0, 4.0);
     dtw->hscrollbar = gtk_hscrollbar_new (GTK_ADJUSTMENT (dtw->hadj));
-    gtk_table_attach (GTK_TABLE (tbl), dtw->hscrollbar, 1, 2, 2, 3, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_FILL), 0, 0);
+    gtk_table_attach (GTK_TABLE (canvas_tbl), dtw->hscrollbar, 1, 2, 2, 3, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(GTK_SHRINK), 0, 0);
+
     /* Vertical scrollbar and the sticky zoom button */
     dtw->vscrollbar_box = gtk_vbox_new (FALSE, 0);
     dtw->sticky_zoom = sp_button_new_from_data ( Inkscape::ICON_SIZE_DECORATION,
@@ -265,8 +271,8 @@ sp_desktop_widget_init (SPDesktopWidget *dtw)
     dtw->vadj = (GtkAdjustment *) gtk_adjustment_new (0.0, -4000.0, 4000.0, 10.0, 100.0, 4.0);
     dtw->vscrollbar = gtk_vscrollbar_new (GTK_ADJUSTMENT (dtw->vadj));
     gtk_box_pack_start (GTK_BOX (dtw->vscrollbar_box), dtw->vscrollbar, TRUE, TRUE, 0);
-    gtk_table_attach (GTK_TABLE (tbl), dtw->vscrollbar_box, 2, 3, 0, 2, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
-
+    gtk_table_attach (GTK_TABLE (canvas_tbl), dtw->vscrollbar_box, 2, 3, 0, 2, (GtkAttachOptions)(GTK_SHRINK), (GtkAttachOptions)(GTK_FILL), 0, 0);
+   
     /* Canvas */
     dtw->canvas = SP_CANVAS (sp_canvas_new_aa ());
     GTK_WIDGET_SET_FLAGS (GTK_WIDGET (dtw->canvas), GTK_CAN_FOCUS);
@@ -275,7 +281,27 @@ sp_desktop_widget_init (SPDesktopWidget *dtw)
     gtk_widget_set_style (GTK_WIDGET (dtw->canvas), style);
     gtk_widget_set_extension_events(GTK_WIDGET (dtw->canvas) , GDK_EXTENSION_EVENTS_ALL);
     g_signal_connect (G_OBJECT (dtw->canvas), "event", G_CALLBACK (sp_desktop_widget_event), dtw);
-    gtk_table_attach (GTK_TABLE (tbl), GTK_WIDGET (dtw->canvas), 1, 2, 1, 2, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
+    gtk_table_attach (GTK_TABLE (canvas_tbl), GTK_WIDGET(dtw->canvas), 1, 2, 1, 2, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), 0, 0);
+
+    /* Dock */
+    bool create_dock = 
+        prefs_get_int_attribute_limited ("options.dialogtype", "value", Inkscape::UI::Dialog::FLOATING, 0, 1) == 
+        Inkscape::UI::Dialog::DOCK;
+    
+    if (create_dock)
+    {
+        dtw->dock = new Inkscape::UI::Widget::Dock();
+
+        Gtk::HPaned *paned = new Gtk::HPaned();
+        paned->pack1(*Glib::wrap(canvas_tbl));
+        paned->pack2(dtw->dock->getWidget(), Gtk::FILL);
+        gtk_table_attach (GTK_TABLE (tbl), GTK_WIDGET (paned->gobj()), 1, 2, 1, 2, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 
+                          (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
+
+    } else {
+        gtk_table_attach (GTK_TABLE (tbl), GTK_WIDGET (canvas_tbl), 1, 2, 1, 2, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 
+                          (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
+    }
 
     dtw->selected_style = new Inkscape::UI::Widget::SelectedStyle(true);
     GtkHBox *ss_ = dtw->selected_style->gobj();
@@ -421,6 +447,12 @@ SPDesktopWidget::updateTitle(gchar const* uri)
     }
 }
 
+Inkscape::UI::Widget::Dock*
+SPDesktopWidget::getDock()
+{
+    return dock;
+}
+
 /**
  * Callback to allocate space for desktop widget.
  */
index bb9d7ef860d78c2f3a186084218a94aee1e8cbd0..18348e594b9bf20d69c5aa9d906ada49dc23d7e6 100644 (file)
@@ -90,6 +90,8 @@ struct SPDesktopWidget {
     GtkWidget *zoom_status;
     gulong zoom_update;
 
+    Inkscape::UI::Widget::Dock *dock;
+
     Inkscape::UI::Widget::SelectedStyle *selected_style;
 
     gint coord_status_id, select_status_id;
@@ -182,6 +184,8 @@ struct SPDesktopWidget {
             { _dtw->setMessage (type, msg); }
         virtual bool warnDialog (gchar* text)
             { return _dtw->warnDialog (text); }
+        virtual Inkscape::UI::Widget::Dock* getDock ()
+            { return _dtw->getDock(); }
     };
 
     WidgetStub *stub;
@@ -207,8 +211,10 @@ struct SPDesktopWidget {
     void enableInteraction();
     void disableInteraction();
     void updateTitle(gchar const *uri);
-       
-       bool onFocusInEvent(GdkEventFocus*);
+    bool onFocusInEvent(GdkEventFocus*);
+
+    Inkscape::UI::Widget::Dock* getDock();
+
 };
 
 /// The SPDesktopWidget vtable