summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: d1f10c6)
raw | patch | inline | side by side (parent: d1f10c6)
author | gustav_b <gustav_b@users.sourceforge.net> | |
Wed, 29 Aug 2007 21:27:07 +0000 (21:27 +0000) | ||
committer | gustav_b <gustav_b@users.sourceforge.net> | |
Wed, 29 Aug 2007 21:27:07 +0000 (21:27 +0000) |
110 files changed:
diff --git a/configure.ac b/configure.ac
index 0210bd507d0d81cc370a434e220b8d4f1c495d5f..360fd875e24c3b0c19fcf6948e6df65bc150c463 100644 (file)
--- a/configure.ac
+++ b/configure.ac
src/inkjar/makefile
src/io/makefile
src/libcroco/makefile
+src/libgdl/makefile
src/libnr/makefile
src/libnrtype/makefile
src/libavoid/makefile
diff --git a/src/Makefile.am b/src/Makefile.am
index 260280a409a55e1d08e2857f0a04307b95c11418..7e6cd1f263ded0a0f3dabdf03cc23cc46b8bb987 100644 (file)
--- a/src/Makefile.am
+++ b/src/Makefile.am
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
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 \
io/doc2html.xsl \
pedro/makefile.in \
jabber_whiteboard/makefile.in \
+ libgdl/makefile.in \
libcroco/makefile.in \
libnr/makefile.in \
libnrtype/makefile.in \
diff --git a/src/Makefile_insert b/src/Makefile_insert
index ebba357ef4bfa0baf800a20df76a867c2858f0db..f0b6166263ed0e4899916425e5d81cf018525eb9 100644 (file)
--- a/src/Makefile_insert
+++ b/src/Makefile_insert
ui/view/libuiview.a \
ui/libui.a \
ui/widget/libuiwidget.a \
+ libgdl/libgdl.a \
graphlayout/libgraphlayout.a \
removeoverlap/libremoveoverlap.a \
libcola/libcola.a \
diff --git a/src/desktop.h b/src/desktop.h
index 10509c2749402aee91cdb08400db10c79d305ce0..bc80336de4eb0b9d8de05e972dd4e7cbd6f64286 100644 (file)
--- a/src/desktop.h
+++ b/src/desktop.h
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)
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;
}
//#########################################################################
hot = which;
updateMagnify();
- queue_draw();
+ get_vbox()->queue_draw();
}
}
/**
* 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)
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 );
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;
/**
* 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)
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 {
/**
* 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),
_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) );
show_all_children();
- restorePanelPrefs();
+ // restorePanelPrefs();
}
LayersPanel::~LayersPanel()
_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)
#include <gtkmm/buttonbox.h>
#include <gtkmm/spinbutton.h>
-#include "ui/widget/panel.h"
//#include "ui/previewholder.h"
+#include "ui/dialog/dialog.h"
class SPObject;
/**
* 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)
--- a/src/dialogs/tiledialog.h
+++ b/src/dialogs/tiledialog.h
/**
* 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
--- /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
--- /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
--- /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
--- /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
--- /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
--- /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
--- /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
--- /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
--- /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
--- /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
--- /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
--- /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
--- /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
--- /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
--- /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
--- /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
--- /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
--- /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
--- /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
--- /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
--- /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
--- /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
--- /dev/null
+++ b/src/libgdl/gdl-dock.c
@@ -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
--- /dev/null
+++ b/src/libgdl/gdl-dock.h
@@ -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
--- /dev/null
+++ b/src/libgdl/gdl-i18n.c
@@ -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
--- /dev/null
+++ b/src/libgdl/gdl-i18n.h
@@ -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
--- /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
--- /dev/null
+++ b/src/libgdl/gdl-stock.c
@@ -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
--- /dev/null
+++ b/src/libgdl/gdl-stock.h
@@ -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
--- /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
--- /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
--- /dev/null
+++ b/src/libgdl/gdl-tools.h
@@ -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
--- /dev/null
+++ b/src/libgdl/layout.glade
@@ -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
--- /dev/null
+++ b/src/libgdl/libgdl.h
@@ -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
--- /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
--- /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
--- /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
--- /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
--- /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
--- /dev/null
+++ b/src/libgdl/makefile
@@ -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
--- /dev/null
+++ b/src/libgdl/makefile.in
@@ -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)
" <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"
" <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"
" <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)
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)
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
--- /dev/null
+++ b/src/ui/dialog/behavior.h
@@ -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)
* \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 {
namespace {
-template <typename T>
-Dialog *create() { return T::create(); }
+template <typename T, typename B>
+inline Dialog *create() { return T::create(&B::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() {
Dialog *dialog=getDialog(name);
if (dialog) {
dialog->present();
- dialog->read_geometry();
}
}
index ac147d65652230b5fbf53e55f01206de641ade64..16124d9f534b31c964e73cf5ecc1f7b1cb29d01f 100644 (file)
--- a/src/ui/dialog/dialog.cpp
+++ b/src/ui/dialog/dialog.cpp
* \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)
{
dlg->onShutdown();
}
-void
-Dialog::present()
-{
- _user_hidden = false;
- Gtk::Dialog::present();
-}
-
void
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;
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()
{
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
// ...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();
}
//=====================================================================
* 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));
}
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()
}
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) |
SP_SHORTCUT_CONTROL_MASK : 0 ) |
( event->state & GDK_MOD1_MASK ?
SP_SHORTCUT_ALT_MASK : 0 );
+
return sp_shortcut_invoke( shortcut, SP_ACTIVE_DESKTOP );
}
Dialog::onHideF12()
{
_hiddenF12 = true;
- save_geometry();
- hide();
+ _behavior->onHideF12();
}
void
return;
if (_hiddenF12) {
- show();
- read_geometry();
+ _behavior->onShowF12();
}
_hiddenF12 = false;
{
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()
{
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
diff --git a/src/ui/dialog/dialog.h b/src/ui/dialog/dialog.h
index dd3cdaafe43c57c992aeb60bd514d4766eed8435..7530b9a35c31543ec96301a29b707c96442ec210 100644 (file)
--- a/src/ui/dialog/dialog.h
+++ b/src/ui/dialog/dialog.h
/**
- * \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.
*/
#include <gtkmm/dialog.h>
#include <gtkmm/tooltips.h>
+#include "dock-behavior.h"
+#include "floating-behavior.h"
+
namespace Inkscape { class Selection; }
class SPDesktop;
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();
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
virtual void _apply();
virtual void _close();
- static bool windowKeyPress( GtkWidget *widget, GdkEventKey *event );
+ static bool windowKeyPress(GdkEventKey *event);
Inkscape::Selection* _getSelection();
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
--- /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
--- /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)
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;
}
}
}
-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)
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;
Registry _wr;
private:
- DocumentMetadata();
+ DocumentMetadata(Behavior::BehaviorFactory behavior_factory);
virtual ~DocumentMetadata();
};
index 141a0c8cba857e5c79daec723c9a85b0e22c9d7b..e2dc2c0b9156ad6fb403c462d699bff94b10ca26 100644 (file)
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;
}
}
}
-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)
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;
Registry _wr;
private:
- DocumentProperties();
+ DocumentProperties(Behavior::BehaviorFactory behavior_factory);
virtual ~DocumentProperties();
// callback methods for buttons on grids page.
index d9ea2d25d51ac38ccb90ce15a8f53241b555f89a..4e4efafb5755f1a399b289db0e1595ae2a503d70 100644 (file)
--- a/src/ui/dialog/export.cpp
+++ b/src/ui/dialog/export.cpp
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
diff --git a/src/ui/dialog/export.h b/src/ui/dialog/export.h
index 567eae10dca24621c369bbf8d09c8e3ed6edf0e2..c47c7c8cc81d68d64d89ca2afad614cfa4b83022 100644 (file)
--- a/src/ui/dialog/export.h
+++ b/src/ui/dialog/export.h
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)
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)
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)
}
-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)
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)
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);
/*** 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)
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;
diff --git a/src/ui/dialog/find.cpp b/src/ui/dialog/find.cpp
index 22fa608bf25d684b38f9643c4c8f27a359ace07d..0a0538c30ab4efcf512b455bcd9af609dd2cc79c 100644 (file)
--- a/src/ui/dialog/find.cpp
+++ b/src/ui/dialog/find.cpp
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)")),
diff --git a/src/ui/dialog/find.h b/src/ui/dialog/find.h
index 9594669fa59d5e7763901822a1a3c300a650d660..fb52b2c8e2ebc19a730af7ae1a7eff0acffb7d2b 100644 (file)
--- a/src/ui/dialog/find.h
+++ b/src/ui/dialog/find.h
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
--- /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
--- /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)
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)
_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);
_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)
public:
virtual ~InkscapePreferences();
- static InkscapePreferences *create() {return new InkscapePreferences(); }
+ static InkscapePreferences *create(Behavior::BehaviorFactory behavior_factory)
+ { return new InkscapePreferences(behavior_factory); }
+
void present();
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;
void initPageMisc();
private:
- InkscapePreferences();
+ InkscapePreferences(Behavior::BehaviorFactory behavior_factory);
InkscapePreferences(InkscapePreferences const &d);
InkscapePreferences operator=(InkscapePreferences const &d);
};
index 79c4fbcc2cf98ccf3c8b1701df899331959f3085..cb4b8a0b4122680e972896f07c56557f599bbeec 100644 (file)
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)
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)
/*#######################\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)
\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)
--- a/src/ui/dialog/memory.cpp
+++ b/src/ui/dialog/memory.cpp
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);
diff --git a/src/ui/dialog/memory.h b/src/ui/dialog/memory.h
index bec6c8fd064bd1ac6f062918bb84d8562e032e96..0fe7f87c575da149ac5c7e2f4e4494f2844c37cc 100644 (file)
--- a/src/ui/dialog/memory.h
+++ b/src/ui/dialog/memory.h
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)
/**
* 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)
--- a/src/ui/dialog/messages.h
+++ b/src/ui/dialog/messages.h
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)
/**
* Constructor
*/
- ScriptDialogImpl();
+ ScriptDialogImpl(Behavior::BehaviorFactory behavior_factory);
/**
* Destructor
/**
* Constructor
*/
-ScriptDialogImpl::ScriptDialogImpl()
+ScriptDialogImpl::ScriptDialogImpl(Behavior::BehaviorFactory behavior_factory) :
+ ScriptDialog(behavior_factory)
{
Gtk::VBox *mainVBox = get_vbox();
/**
* 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)
/**
* 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)
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)
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)
/**
* Constructor
*/
- TraceDialogImpl();
+ TraceDialogImpl(Behavior::BehaviorFactory behavior_factory);
/**
* Destructor
/**
* Constructor
*/
-TraceDialogImpl::TraceDialogImpl()
+TraceDialogImpl::TraceDialogImpl(Behavior::BehaviorFactory behavior_factory) :
+ TraceDialog(behavior_factory)
{
Gtk::VBox *mainVBox = get_vbox();
/**
* 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)
/**
* 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)
* 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),
Transformation::presentPage(Transformation::PageType page)
{
_notebook.set_current_page(page);
- Gtk::Dialog::show();
- Gtk::Dialog::present();
+ show();
+ present();
}
index 0c7833c9e72703f4a7d75a0e9aba381989dd5a87..361b30a04c088cb8e71b37c46e371e41a81fa35a 100644 (file)
/**
* Create a new transform
*/
- Transformation();
+ Transformation(Behavior::BehaviorFactory behavior_factory);
/**
* Cleanup
/**
* 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;
}
_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),
{
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)
public:
virtual ~UndoHistory();
- static UndoHistory *create();
+ static UndoHistory *create(Behavior::BehaviorFactory behavior_factory);
void setDesktop(SPDesktop* desktop);
sigc::connection _document_replaced_connection;
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)
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)
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 {
/// 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)
#include "interface.h"
#include "extension/db.h"
+#include "ui/dialog/dialog-manager.h"
+
using namespace Inkscape::UI;
using namespace Inkscape::UI::Widget;
_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();
}
+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)
#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"
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);
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)
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
--- /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
--- /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
--- /dev/null
+++ b/src/ui/widget/dock.cpp
@@ -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
--- /dev/null
+++ b/src/ui/widget/dock.h
@@ -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
+
diff --git a/src/verbs.cpp b/src/verbs.cpp
index 5f7b51a0303c33b9914e18781cb3b1c40ef4e365..8d2d4e69f239aa2ac31aab98e84202b7674549ce 100644 (file)
--- a/src/verbs.cpp
+++ b/src/verbs.cpp
} // end of sp_action_get_title()
-
namespace Inkscape {
/// \todo !!!FIXME:: kill this, use DialogManager instead!!!
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 {
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();
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:
sp_selected_path_break_apart();
break;
case SP_VERB_SELECTION_GRIDTILE:
+ inkscape_dialogs_unhide();
dt->_dlg_mgr->showDialog("TileDialog");
break;
default:
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;
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;
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");
*/
case SP_VERB_HELP_MEMORY:
+ inkscape_dialogs_unhide();
dt->_dlg_mgr->showDialog("Memory");
break;
default:
index 4b744e3dffc1db2349036de76302c24333c951e3..f33950f70b98bd44ef75a8a6d8431c20c17fb2af 100644 (file)
#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"
{
GtkWidget *widget;
GtkWidget *tbl;
+ GtkWidget *canvas_tbl;
GtkWidget *hbox;
GtkWidget *eventbox;
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 ();
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);
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);
/* 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,
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);
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();
}
}
+Inkscape::UI::Widget::Dock*
+SPDesktopWidget::getDock()
+{
+ return dock;
+}
+
/**
* Callback to allocate space for desktop widget.
*/
index bb9d7ef860d78c2f3a186084218a94aee1e8cbd0..18348e594b9bf20d69c5aa9d906ada49dc23d7e6 100644 (file)
GtkWidget *zoom_status;
gulong zoom_update;
+ Inkscape::UI::Widget::Dock *dock;
+
Inkscape::UI::Widget::SelectedStyle *selected_style;
gint coord_status_id, select_status_id;
{ _dtw->setMessage (type, msg); }
virtual bool warnDialog (gchar* text)
{ return _dtw->warnDialog (text); }
+ virtual Inkscape::UI::Widget::Dock* getDock ()
+ { return _dtw->getDock(); }
};
WidgetStub *stub;
void enableInteraction();
void disableInteraction();
void updateTitle(gchar const *uri);
-
- bool onFocusInEvent(GdkEventFocus*);
+ bool onFocusInEvent(GdkEventFocus*);
+
+ Inkscape::UI::Widget::Dock* getDock();
+
};
/// The SPDesktopWidget vtable