From a0df1b8dd5b14367c583ce2f72a2ca6bf1cde799 Mon Sep 17 00:00:00 2001 From: gustav_b Date: Wed, 29 Aug 2007 21:27:07 +0000 Subject: [PATCH] Dockable dialogs patch applied (https://sourceforge.net/tracker/?func=detail&atid=604308&aid=1688508&group_id=93438) --- configure.ac | 1 + src/Makefile.am | 3 + src/Makefile_insert | 1 + src/desktop.h | 2 + src/dialogs/iconpreview.cpp | 18 +- src/dialogs/iconpreview.h | 9 +- src/dialogs/layers-panel.cpp | 21 +- src/dialogs/layers-panel.h | 8 +- src/dialogs/tiledialog.cpp | 4 +- src/dialogs/tiledialog.h | 6 +- src/libgdl/Makefile_insert | 43 + src/libgdl/README.gdl-dock | 184 +++ src/libgdl/gdl-combo-button.c | 383 +++++ src/libgdl/gdl-combo-button.h | 65 + src/libgdl/gdl-dock-bar.c | 976 ++++++++++++ src/libgdl/gdl-dock-bar.h | 74 + src/libgdl/gdl-dock-item-grip.c | 711 +++++++++ src/libgdl/gdl-dock-item-grip.h | 56 + src/libgdl/gdl-dock-item.c | 1939 +++++++++++++++++++++++ src/libgdl/gdl-dock-item.h | 191 +++ src/libgdl/gdl-dock-master.c | 991 ++++++++++++ src/libgdl/gdl-dock-master.h | 97 ++ src/libgdl/gdl-dock-notebook.c | 528 ++++++ src/libgdl/gdl-dock-notebook.h | 59 + src/libgdl/gdl-dock-object.c | 942 +++++++++++ src/libgdl/gdl-dock-object.h | 225 +++ src/libgdl/gdl-dock-paned.c | 673 ++++++++ src/libgdl/gdl-dock-paned.h | 64 + src/libgdl/gdl-dock-placeholder.c | 834 ++++++++++ src/libgdl/gdl-dock-placeholder.h | 69 + src/libgdl/gdl-dock-tablabel.c | 621 ++++++++ src/libgdl/gdl-dock-tablabel.h | 74 + src/libgdl/gdl-dock.c | 1372 ++++++++++++++++ src/libgdl/gdl-dock.h | 99 ++ src/libgdl/gdl-i18n.c | 43 + src/libgdl/gdl-i18n.h | 72 + src/libgdl/gdl-stock-icons.h | 135 ++ src/libgdl/gdl-stock.c | 127 ++ src/libgdl/gdl-stock.h | 35 + src/libgdl/gdl-switcher.c | 1081 +++++++++++++ src/libgdl/gdl-switcher.h | 73 + src/libgdl/gdl-tools.h | 184 +++ src/libgdl/layout.glade | 283 ++++ src/libgdl/libgdl.h | 38 + src/libgdl/libgdlmarshal.c | 169 ++ src/libgdl/libgdlmarshal.h | 45 + src/libgdl/libgdlmarshal.list | 6 + src/libgdl/libgdltypebuiltins.c | 161 ++ src/libgdl/libgdltypebuiltins.h | 38 + src/libgdl/makefile | 17 + src/libgdl/makefile.in | 17 + src/preferences-skeleton.h | 9 +- src/ui/dialog/Makefile_insert | 7 +- src/ui/dialog/align-and-distribute.cpp | 4 +- src/ui/dialog/align-and-distribute.h | 5 +- src/ui/dialog/behavior.h | 108 ++ src/ui/dialog/dialog-manager.cpp | 92 +- src/ui/dialog/dialog.cpp | 250 ++- src/ui/dialog/dialog.h | 68 +- src/ui/dialog/dock-behavior.cpp | 308 ++++ src/ui/dialog/dock-behavior.h | 119 ++ src/ui/dialog/document-metadata.cpp | 8 +- src/ui/dialog/document-metadata.h | 4 +- src/ui/dialog/document-properties.cpp | 8 +- src/ui/dialog/document-properties.h | 4 +- src/ui/dialog/export.cpp | 4 +- src/ui/dialog/export.h | 5 +- src/ui/dialog/extension-editor.cpp | 4 +- src/ui/dialog/extension-editor.h | 5 +- src/ui/dialog/fill-and-stroke.cpp | 4 +- src/ui/dialog/fill-and-stroke.h | 5 +- src/ui/dialog/filter-effects-dialog.cpp | 7 +- src/ui/dialog/filter-effects-dialog.h | 7 +- src/ui/dialog/find.cpp | 4 +- src/ui/dialog/find.h | 5 +- src/ui/dialog/floating-behavior.cpp | 182 +++ src/ui/dialog/floating-behavior.h | 88 + src/ui/dialog/inkscape-preferences.cpp | 13 +- src/ui/dialog/inkscape-preferences.h | 7 +- src/ui/dialog/layer-editor.cpp | 4 +- src/ui/dialog/layer-editor.h | 5 +- src/ui/dialog/livepatheffect-editor.cpp | 4 +- src/ui/dialog/livepatheffect-editor.h | 5 +- src/ui/dialog/memory.cpp | 4 +- src/ui/dialog/memory.h | 5 +- src/ui/dialog/messages.cpp | 4 +- src/ui/dialog/messages.h | 5 +- src/ui/dialog/scriptdialog.cpp | 9 +- src/ui/dialog/scriptdialog.h | 5 +- src/ui/dialog/text-properties.cpp | 4 +- src/ui/dialog/text-properties.h | 5 +- src/ui/dialog/tracedialog.cpp | 9 +- src/ui/dialog/tracedialog.h | 5 +- src/ui/dialog/transformation.cpp | 8 +- src/ui/dialog/transformation.h | 6 +- src/ui/dialog/undo-history.cpp | 10 +- src/ui/dialog/undo-history.h | 4 +- src/ui/dialog/xml-editor.cpp | 4 +- src/ui/dialog/xml-editor.h | 5 +- src/ui/view/edit-widget-interface.h | 5 +- src/ui/view/edit-widget.cpp | 9 +- src/ui/view/edit-widget.h | 4 + src/ui/widget/Makefile_insert | 4 + src/ui/widget/dock-item.cpp | 539 +++++++ src/ui/widget/dock-item.h | 165 ++ src/ui/widget/dock.cpp | 235 +++ src/ui/widget/dock.h | 97 ++ src/verbs.cpp | 25 +- src/widgets/desktop-widget.cpp | 46 +- src/widgets/desktop-widget.h | 10 +- 110 files changed, 16132 insertions(+), 318 deletions(-) create mode 100644 src/libgdl/Makefile_insert create mode 100644 src/libgdl/README.gdl-dock create mode 100644 src/libgdl/gdl-combo-button.c create mode 100644 src/libgdl/gdl-combo-button.h create mode 100644 src/libgdl/gdl-dock-bar.c create mode 100644 src/libgdl/gdl-dock-bar.h create mode 100644 src/libgdl/gdl-dock-item-grip.c create mode 100644 src/libgdl/gdl-dock-item-grip.h create mode 100644 src/libgdl/gdl-dock-item.c create mode 100644 src/libgdl/gdl-dock-item.h create mode 100644 src/libgdl/gdl-dock-master.c create mode 100644 src/libgdl/gdl-dock-master.h create mode 100644 src/libgdl/gdl-dock-notebook.c create mode 100644 src/libgdl/gdl-dock-notebook.h create mode 100644 src/libgdl/gdl-dock-object.c create mode 100644 src/libgdl/gdl-dock-object.h create mode 100644 src/libgdl/gdl-dock-paned.c create mode 100644 src/libgdl/gdl-dock-paned.h create mode 100644 src/libgdl/gdl-dock-placeholder.c create mode 100644 src/libgdl/gdl-dock-placeholder.h create mode 100644 src/libgdl/gdl-dock-tablabel.c create mode 100644 src/libgdl/gdl-dock-tablabel.h create mode 100644 src/libgdl/gdl-dock.c create mode 100644 src/libgdl/gdl-dock.h create mode 100644 src/libgdl/gdl-i18n.c create mode 100644 src/libgdl/gdl-i18n.h create mode 100644 src/libgdl/gdl-stock-icons.h create mode 100644 src/libgdl/gdl-stock.c create mode 100644 src/libgdl/gdl-stock.h create mode 100644 src/libgdl/gdl-switcher.c create mode 100644 src/libgdl/gdl-switcher.h create mode 100644 src/libgdl/gdl-tools.h create mode 100644 src/libgdl/layout.glade create mode 100644 src/libgdl/libgdl.h create mode 100644 src/libgdl/libgdlmarshal.c create mode 100644 src/libgdl/libgdlmarshal.h create mode 100644 src/libgdl/libgdlmarshal.list create mode 100644 src/libgdl/libgdltypebuiltins.c create mode 100644 src/libgdl/libgdltypebuiltins.h create mode 100644 src/libgdl/makefile create mode 100644 src/libgdl/makefile.in create mode 100644 src/ui/dialog/behavior.h create mode 100644 src/ui/dialog/dock-behavior.cpp create mode 100644 src/ui/dialog/dock-behavior.h create mode 100644 src/ui/dialog/floating-behavior.cpp create mode 100644 src/ui/dialog/floating-behavior.h create mode 100644 src/ui/widget/dock-item.cpp create mode 100644 src/ui/widget/dock-item.h create mode 100644 src/ui/widget/dock.cpp create mode 100644 src/ui/widget/dock.h diff --git a/configure.ac b/configure.ac index 0210bd507..360fd875e 100644 --- a/configure.ac +++ b/configure.ac @@ -886,6 +886,7 @@ src/helper/makefile src/inkjar/makefile src/io/makefile src/libcroco/makefile +src/libgdl/makefile src/libnr/makefile src/libnrtype/makefile src/libavoid/makefile diff --git a/src/Makefile.am b/src/Makefile.am index 260280a40..7e6cd1f26 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -43,6 +43,7 @@ include io/Makefile_insert include pedro/Makefile_insert include jabber_whiteboard/Makefile_insert include libcroco/Makefile_insert +include libgdl/Makefile_insert include libnr/Makefile_insert include libnrtype/Makefile_insert include libavoid/Makefile_insert @@ -87,6 +88,7 @@ noinst_LIBRARIES = \ helper/libspchelp.a \ io/libio.a \ libcroco/libcroco.a \ + libgdl/libgdl.a \ live_effects/liblive_effects.a \ live_effects/parameter/liblpeparam.a \ ui/libui.a \ @@ -144,6 +146,7 @@ EXTRA_DIST = \ io/doc2html.xsl \ pedro/makefile.in \ jabber_whiteboard/makefile.in \ + libgdl/makefile.in \ libcroco/makefile.in \ libnr/makefile.in \ libnrtype/makefile.in \ diff --git a/src/Makefile_insert b/src/Makefile_insert index ebba357ef..f0b616626 100644 --- a/src/Makefile_insert +++ b/src/Makefile_insert @@ -339,6 +339,7 @@ inkscape_private_libs = \ ui/view/libuiview.a \ ui/libui.a \ ui/widget/libuiwidget.a \ + libgdl/libgdl.a \ graphlayout/libgraphlayout.a \ removeoverlap/libremoveoverlap.a \ libcola/libcola.a \ diff --git a/src/desktop.h b/src/desktop.h index 10509c274..bc80336de 100644 --- a/src/desktop.h +++ b/src/desktop.h @@ -181,6 +181,8 @@ struct SPDesktop : public Inkscape::UI::View::View int displayMode; int getMode() const { return displayMode; } + Inkscape::UI::Widget::Dock* getDock() { return _widget->getDock(); } + void set_active (bool new_active); SPObject *currentRoot() const; SPObject *currentLayer() const; diff --git a/src/dialogs/iconpreview.cpp b/src/dialogs/iconpreview.cpp index 90f48a4a6..d0b8b4a8f 100644 --- a/src/dialogs/iconpreview.cpp +++ b/src/dialogs/iconpreview.cpp @@ -49,16 +49,16 @@ namespace Dialogs { IconPreviewPanel* IconPreviewPanel::instance = 0; - -IconPreviewPanel& IconPreviewPanel::getInstance() +IconPreviewPanel* +IconPreviewPanel::create(Inkscape::UI::Dialog::Behavior::BehaviorFactory behavior_factory) { if ( !instance ) { - instance = new IconPreviewPanel(); + instance = new IconPreviewPanel(behavior_factory); } instance->refreshPreview(); - return *instance; + return instance; } //######################################################################### @@ -72,7 +72,7 @@ void IconPreviewPanel::on_button_clicked(int which) hot = which; updateMagnify(); - queue_draw(); + get_vbox()->queue_draw(); } } @@ -85,8 +85,8 @@ void IconPreviewPanel::on_button_clicked(int which) /** * Constructor */ -IconPreviewPanel::IconPreviewPanel() : - Panel(), +IconPreviewPanel::IconPreviewPanel(Inkscape::UI::Dialog::Behavior::BehaviorFactory behavior_factory) : + Inkscape::UI::Dialog::Dialog(behavior_factory, "dialogs.iconpreview", SP_VERB_VIEW_ICON_PREVIEW), hot(1), refreshButton(0), selectionButton(0) @@ -186,7 +186,7 @@ IconPreviewPanel::IconPreviewPanel() : Gtk::HButtonBox* holder = new Gtk::HButtonBox( Gtk::BUTTONBOX_END ); - _getContents()->pack_end( *holder, false, false ); + get_vbox()->pack_end( *holder, false, false ); selectionButton = new Gtk::ToggleButton(_("Selection")); // , GTK_RESPONSE_APPLY holder->pack_start( *selectionButton, false, false ); @@ -202,7 +202,7 @@ IconPreviewPanel::IconPreviewPanel() : refreshButton->signal_clicked().connect( sigc::mem_fun(*this, &IconPreviewPanel::refreshPreview) ); - _getContents()->pack_start(iconBox, Gtk::PACK_EXPAND_WIDGET); + get_vbox()->pack_start(iconBox, Gtk::PACK_EXPAND_WIDGET); show_all_children(); } diff --git a/src/dialogs/iconpreview.h b/src/dialogs/iconpreview.h index e4be831d3..9cfe81b11 100644 --- a/src/dialogs/iconpreview.h +++ b/src/dialogs/iconpreview.h @@ -18,9 +18,10 @@ #include #include #include +#include #include -#include "ui/widget/panel.h" +#include "ui/dialog/dialog.h" struct SPObject; @@ -32,13 +33,13 @@ namespace Dialogs { /** * A panel that displays an icon preview */ -class IconPreviewPanel : public Inkscape::UI::Widget::Panel +class IconPreviewPanel : public Inkscape::UI::Dialog::Dialog { public: - IconPreviewPanel(); + IconPreviewPanel(Inkscape::UI::Dialog::Behavior::BehaviorFactory behavior_factory); //IconPreviewPanel(Glib::ustring const &label); - static IconPreviewPanel& getInstance(); + static IconPreviewPanel *create(Inkscape::UI::Dialog::Behavior::BehaviorFactory behavior_factory); void refreshPreview(); void modeToggled(); diff --git a/src/dialogs/layers-panel.cpp b/src/dialogs/layers-panel.cpp index 32a52a373..383d11f57 100644 --- a/src/dialogs/layers-panel.cpp +++ b/src/dialogs/layers-panel.cpp @@ -46,13 +46,14 @@ namespace Dialogs { LayersPanel* LayersPanel::instance = 0; -LayersPanel& LayersPanel::getInstance() +LayersPanel* +LayersPanel::create(Inkscape::UI::Dialog::Behavior::BehaviorFactory behavior_factory) { if ( !instance ) { - instance = new LayersPanel(); + instance = new LayersPanel(behavior_factory); } - return *instance; + return instance; } enum { @@ -707,8 +708,8 @@ void LayersPanel::_opacityChanged() /** * Constructor */ -LayersPanel::LayersPanel() : - Inkscape::UI::Widget::Panel( Glib::ustring(), "dialogs.layers" ), +LayersPanel::LayersPanel(Inkscape::UI::Dialog::Behavior::BehaviorFactory behavior_factory) : + Inkscape::UI::Dialog::Dialog(behavior_factory, "dialogs.layers", SP_VERB_DIALOG_LAYERS), _maxNestDepth(20), _mgr(0), _desktop(0), @@ -775,10 +776,10 @@ LayersPanel::LayersPanel() : _opacityBox.pack_end( _spinBtn, Gtk::PACK_SHRINK ); _watching.push_back( &_opacityBox ); - _getContents()->pack_start( _scroller, Gtk::PACK_EXPAND_WIDGET ); + get_vbox()->pack_start( _scroller, Gtk::PACK_EXPAND_WIDGET ); - _getContents()->pack_end(_opacityBox, Gtk::PACK_SHRINK); - _getContents()->pack_end(_buttonsRow, Gtk::PACK_SHRINK); + get_vbox()->pack_end(_opacityBox, Gtk::PACK_SHRINK); + get_vbox()->pack_end(_buttonsRow, Gtk::PACK_SHRINK); _opacityConnection = _opacity.get_adjustment()->signal_value_changed().connect( sigc::mem_fun(*this, &LayersPanel::_opacityChanged) ); @@ -859,7 +860,7 @@ LayersPanel::LayersPanel() : show_all_children(); - restorePanelPrefs(); + // restorePanelPrefs(); } LayersPanel::~LayersPanel() @@ -892,7 +893,7 @@ void LayersPanel::setDesktop( SPDesktop* desktop ) _desktop = SP_ACTIVE_DESKTOP; if ( _desktop ) { - setLabel( _desktop->doc()->name ); + //setLabel( _desktop->doc()->name ); _mgr = _desktop->layer_manager; if ( _mgr ) { diff --git a/src/dialogs/layers-panel.h b/src/dialogs/layers-panel.h index b253aae27..83c5089fc 100644 --- a/src/dialogs/layers-panel.h +++ b/src/dialogs/layers-panel.h @@ -21,8 +21,8 @@ #include #include -#include "ui/widget/panel.h" //#include "ui/previewholder.h" +#include "ui/dialog/dialog.h" class SPObject; @@ -37,14 +37,14 @@ namespace Dialogs { /** * A panel that displays layers. */ -class LayersPanel : public Inkscape::UI::Widget::Panel +class LayersPanel : public Inkscape::UI::Dialog::Dialog { public: - LayersPanel(); + LayersPanel(Inkscape::UI::Dialog::Behavior::BehaviorFactory behavior_factory); virtual ~LayersPanel(); - static LayersPanel& getInstance(); //virtual void setOrientation( Gtk::AnchorType how ); + static LayersPanel *create(Inkscape::UI::Dialog::Behavior::BehaviorFactory behavior_factory); void setDesktop( SPDesktop* desktop ); diff --git a/src/dialogs/tiledialog.cpp b/src/dialogs/tiledialog.cpp index 75311ddd0..e46b6e127 100644 --- a/src/dialogs/tiledialog.cpp +++ b/src/dialogs/tiledialog.cpp @@ -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; diff --git a/src/dialogs/tiledialog.h b/src/dialogs/tiledialog.h index 69a5ea3e2..5fe114d83 100644 --- a/src/dialogs/tiledialog.h +++ b/src/dialogs/tiledialog.h @@ -40,13 +40,13 @@ public: /** * Constructor */ - TileDialog() ; - + TileDialog(Behavior::BehaviorFactory behavior_factory) ; /** * Factory method */ - static TileDialog *create() { return new TileDialog(); } + static TileDialog *create(Behavior::BehaviorFactory behavior_factory) + { return new TileDialog(behavior_factory); } /** * Destructor diff --git a/src/libgdl/Makefile_insert b/src/libgdl/Makefile_insert new file mode 100644 index 000000000..b4a7209e4 --- /dev/null +++ b/src/libgdl/Makefile_insert @@ -0,0 +1,43 @@ +## Makefile.am fragment sourced by src/Makefile.am. + +libgdl/all: libgdl/libgdl.a + +libgdl/clean: + rm -f libgdl/libgdl.a $(libgdl_gdl_a_OBJECTS) + +libgdl_libgdl_a_SOURCES = \ + libgdl/gdl-tools.h \ + libgdl/gdl-dock-object.h \ + libgdl/gdl-dock-master.h \ + libgdl/gdl-dock.h \ + libgdl/gdl-dock-item.h \ + libgdl/gdl-dock-notebook.h \ + libgdl/gdl-dock-paned.h \ + libgdl/gdl-dock-tablabel.h \ + libgdl/gdl-dock-placeholder.h \ + libgdl/gdl-dock-bar.h \ + libgdl/gdl-combo-button.h \ + libgdl/gdl-stock.h \ + libgdl/gdl-stock-icons.h \ + libgdl/gdl-i18n.h \ + libgdl/gdl-i18n.c \ + libgdl/gdl-dock-object.c \ + libgdl/gdl-dock-master.c \ + libgdl/gdl-dock.c \ + libgdl/gdl-dock-item.c \ + libgdl/gdl-dock-item-grip.h \ + libgdl/gdl-dock-item-grip.c \ + libgdl/gdl-dock-notebook.c \ + libgdl/gdl-dock-paned.c \ + libgdl/gdl-dock-tablabel.c \ + libgdl/gdl-dock-placeholder.c \ + libgdl/gdl-dock-bar.c \ + libgdl/gdl-combo-button.c \ + libgdl/gdl-stock.c \ + libgdl/gdl-switcher.h \ + libgdl/gdl-switcher.c \ + libgdl/libgdltypebuiltins.h \ + libgdl/libgdltypebuiltins.c \ + libgdl/libgdlmarshal.h \ + libgdl/libgdlmarshal.c \ + libgdl/libgdl.h diff --git a/src/libgdl/README.gdl-dock b/src/libgdl/README.gdl-dock new file mode 100644 index 000000000..113926dbe --- /dev/null +++ b/src/libgdl/README.gdl-dock @@ -0,0 +1,184 @@ +This file is meant to contain a little documentation about the GdlDock +and related widgets. It's incomplete and probably a bit out of date. +And it probably belongs to a doc/ subdirectory. + +Please send comments to the devtools list (gnome-devtools@gnome.org) +and report bugs to bugzilla (bugzilla.gnome.org). Also check the todo +list at the end of this document. + +Have fun, +Gustavo + + +Overview +-------- + +The GdlDock is a hierarchical based docking widget. It is composed of +widgets derived from the abstract class GdlDockObject which defines +the basic interface for docking widgets on top of others. + +The toplevel entries for docks are GdlDock widgets, which in turn hold +a tree of GdlDockItem widgets. For the toplevel docks to be able to +interact with each other (when the user drags items from one place to +another) they're all kept in a user-invisible and automatic object +called the master. To participate in docking operations every +GdlDockObject must have the same master (the binding to the master is +done automatically). The master also keeps track of the manual items +(mostly those created with gdl_dock_*_new functions) which are in the +dock. + +Layout loading/saving service is provided by a separate object called +GdlDockLayout. Currently it stores information in XML format, but +another backend could be easily written. To keep the external XML +file in sync with the dock, monitor the "dirty" property of the layout +object and call gdl_dock_layout_save_to_file when this changes to +TRUE. No other action is required (the layout_changed is monitored by +the layout object for you now). + + +GdlDockObject +============= + +A DockObject has: + +- a master, which manages all the objects in a dock ring ("master" + property, construct only). + +- a name. If the object is manual the name can be used to recall the + object from any other object in the ring ("name" property). + +- a long, descriptive name ("long-name" property). + +A DockObject can be: + +- automatic or manual. If it's automatic it means the lifecycle of + the object is entirely managed by the dock master. If it's manual + in general means the user specified such object, and so it's not to + be destroyed automatically at any time. + +- frozen. In this case any call to reduce on the object has no + immediate effect. When the object is later thawn, any pending + reduce calls are made (maybe leading to the destruction of the + object). + +- attached or detached. In general for dock items, being attached + will mean the item has its widget->parent set. For docks it will + mean they belong to some dock master's ring. In general, automatic + objects which are detached will be destroyed (unless frozen). + +- bound to a master or free. In order to participate in dock + operations, each dock object must be bound to a dock master (which + is a separate, non-gui object). In general, bindings are treated + automatically by the object, and this is entirely true for automatic + objects. For manual objects, the master holds an additional + reference and has structures to store and recall them by nick names. + Normally, a manual object will only be destroyed when it's unbound + from the master. + +- simple or compound. This actually depends on the subclass of the + dock object. The difference is made so we can put restrictions in + how the objects are docked on top of another (e.g. you can't dock a + compound object inside a notebook). If you look at the whole + docking layout as a tree, simple objects are the leaves, while all + the interior nodes are compound. + +- reduced. This is only meaningful for compound objects. If the + number of contained items has decreased to one the compound type + object is no longer necessary to hold the child. In this case the + child is reattached to the object's parent. If the number of + contained items has reached zero, the object is detached and reduce + is called on its parent. For toplevel docks, the object is only + detached if it's automatic. In any case, the future of the object + itself depends on whether it's automatic or manual. + +- requested to possibly dock another object. Depending on the + type's behavior, the object can accept or reject this request. If + it accepts it, it should fill in some additional information + regarding how it will host the peer object. + +- asked to dock another object. Depending on the object's internal + structure and behavior two options can be taken: to dock the object + directly (e.g. a notebook docking another object); or to create an + automatic compound object which will be attached in place of the + actual object, and will host both the original object and the + requestor (e.g. a simple item docking another simple item, which + should create a paned/notebook). The type of the new object will be + decided by the original objet based on the docking position. + + +DETACHING: the action by which an object is unparented. The object is +then ready to be docked in some other place. Newly created objects +are always detached, except for toplevels (which are created attached +by default). An automatic object which is detached gets destroyed +afterwards, since its ref count drops to zero. Floating automatic +toplevels never reach a zero ref count when detached, since the +GtkWindow always keeps a reference to it (and the GtkWindow has a user +reference). That's why floating automatic toplevels are destroyed +when reduced. + +REDUCING: for compound objects, when the number of contained children +drops to one or zero, the container is no longer necessary. In this +case, the object is detached, and any remaining child is reattached to +the object's former parent. The limit for toplevels is one for +automatic objects and zero for manual (i.e. they can even be empty). +For simple (not compound) objects reducing doesn't make sense. + +UNBINDING: to participate in a dock ring, every object must be bound +to a master. The master connects to dock item signals and keeps a +list of bound toplevels. Additionally, a reference is kept for manual +objects (this is so the user doesn't need to keep track of them, but +can perform operations like hiding and such). + + + +GdlDock +======= + +- Properties: + + "floating" (gboolean, construct-only): whether the dock is floating in + its own window or not. + + "default-title" (gchar, read-write): title for new floating docks. + This property is proxied to the master, which truly holds it. + +The title for the floating docks is: the user supplied title +(GdlDockObject's long_name property) if it's set, the default title +(from the master) or an automatically generated title. + + +- Signals: + + "layout-changed": emitted when the user changed the layout of the + dock somehow. + + +TODO LIST +========= + +- Functionality for the item grip: provide a11y + +- Implement notebook tab reordering + +- Implement dock bands for toolbars and menus. + +- A dock-related thing is to build resizable toolbars (something like + the ones Windows have, where the buttons are reflowed according to + the space available). + +- Redesign paneds so they can hold more than two items and resize all + of them at once by using the handle (see if gimpdock does that). + +- Find a way to allow the merging of menu items to the item's popup + menu. Also, there should be a way for the master to insert some + menu items. + +- Bonobo UI synchronizer. + +- Item behavoirs: implement the ones missing and maybe think more of + them (e.g. positions where it's possible to dock the item, etc.) + +- Make a nicer dragbar for the items, with buttons for undocking, + closing, hidding, etc. (See sodipodi, kdevelop) + + diff --git a/src/libgdl/gdl-combo-button.c b/src/libgdl/gdl-combo-button.c new file mode 100644 index 000000000..6414a8110 --- /dev/null +++ b/src/libgdl/gdl-combo-button.c @@ -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 +#endif + +#include +#include "gdl-tools.h" +#include "gdl-combo-button.h" + +struct _GdlComboButtonPrivate { + GtkWidget *default_button; + GtkWidget *image; + GtkWidget *label; + GtkWidget *menu_button; + GtkWidget *menu; + gboolean menu_popped_up; +}; + +GDL_CLASS_BOILERPLATE (GdlComboButton, gdl_combo_button, GtkHBox, GTK_TYPE_HBOX); + +static void +default_button_clicked_cb (GtkButton *button, + gpointer user_data) +{ + GdlComboButton *combo; + GdlComboButtonPrivate *priv; + + combo = GDL_COMBO_BUTTON (user_data); + priv = combo->priv; + + if (!priv->menu_popped_up) + g_signal_emit_by_name (G_OBJECT (combo), + "activate-default", NULL); +} + +static gboolean +default_button_press_event_cb (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) +{ + GdlComboButton *combo_button; + GdlComboButtonPrivate *priv; + + combo_button = GDL_COMBO_BUTTON (user_data); + priv = combo_button->priv; + + if (event->type == GDK_BUTTON_PRESS && event->button == 1) { + GTK_BUTTON (priv->menu_button)->button_down = TRUE; + gtk_button_pressed (GTK_BUTTON (priv->menu_button)); + } + + return FALSE; +} + +static gboolean +default_button_release_event_cb (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) +{ + GdlComboButton *combo_button; + GdlComboButtonPrivate *priv; + + combo_button = GDL_COMBO_BUTTON (user_data); + priv = combo_button->priv; + + if (event->button == 1) { + gtk_button_released (GTK_BUTTON (priv->menu_button)); + } + + return FALSE; +} + +static gboolean +button_enter_notify_cb (GtkWidget *widget, + GdkEventCrossing *event, + gpointer user_data) +{ + GdlComboButton *combo_button; + GdlComboButtonPrivate *priv; + + combo_button = GDL_COMBO_BUTTON (user_data); + priv = combo_button->priv; + + if (event->detail != GDK_NOTIFY_INFERIOR) { + GTK_BUTTON (priv->default_button)->in_button = TRUE; + GTK_BUTTON (priv->menu_button)->in_button = TRUE; + gtk_button_enter (GTK_BUTTON (priv->default_button)); + gtk_button_enter (GTK_BUTTON (priv->menu_button)); + } + + return TRUE; +} + +static gboolean +button_leave_notify_cb (GtkWidget *widget, + GdkEventCrossing *event, + gpointer user_data) +{ + GdlComboButton *combo_button; + GdlComboButtonPrivate *priv; + + combo_button = GDL_COMBO_BUTTON (user_data); + priv = combo_button->priv; + + if (priv->menu_popped_up) + return TRUE; + + if (event->detail != GDK_NOTIFY_INFERIOR) { + GTK_BUTTON (priv->default_button)->in_button = FALSE; + GTK_BUTTON (priv->menu_button)->in_button = FALSE; + gtk_button_leave (GTK_BUTTON (priv->default_button)); + gtk_button_leave (GTK_BUTTON (priv->menu_button)); + } + + return TRUE; +} + +static void +menu_position_func (GtkMenu *menu, + gint *x_return, + gint *y_return, + gboolean *push_in, + gpointer user_data) +{ + GdlComboButton *combo_button; + GdlComboButtonPrivate *priv; + GtkAllocation *allocation; + + combo_button = GDL_COMBO_BUTTON (user_data); + priv = combo_button->priv; + allocation = &(priv->default_button->allocation); + + gdk_window_get_origin (priv->default_button->window, x_return, y_return); + + *x_return += allocation->x; + *y_return += allocation->height; +} + +static gboolean +menu_button_press_event_cb (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) +{ + GdlComboButton *combo_button; + GdlComboButtonPrivate *priv; + + combo_button = GDL_COMBO_BUTTON (user_data); + priv = combo_button->priv; + + if (event->type == GDK_BUTTON_PRESS && + (event->button == 1 || event->button == 3)) { + GTK_BUTTON (priv->menu_button)->button_down = TRUE; + + gtk_button_pressed (GTK_BUTTON (priv->menu_button)); + + priv->menu_popped_up = TRUE; + gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL, + menu_position_func, combo_button, + event->button, event->time); + } + + return TRUE; +} + +static void +menu_deactivate_cb (GtkMenuShell *menu_shell, + gpointer user_data) +{ + GdlComboButton *combo_button; + GdlComboButtonPrivate *priv; + + combo_button = GDL_COMBO_BUTTON (user_data); + priv = combo_button->priv; + + priv->menu_popped_up = FALSE; + + GTK_BUTTON (priv->menu_button)->button_down = FALSE; + GTK_BUTTON (priv->menu_button)->in_button = FALSE; + GTK_BUTTON (priv->default_button)->in_button = FALSE; + gtk_button_leave (GTK_BUTTON (priv->menu_button)); + gtk_button_leave (GTK_BUTTON (priv->default_button)); + gtk_button_clicked (GTK_BUTTON (priv->menu_button)); +} + +static void +menu_detacher (GtkWidget *widget, + GtkMenu *menu) +{ + GdlComboButton *combo_button; + + combo_button = GDL_COMBO_BUTTON (widget); + + g_signal_handlers_disconnect_by_func (G_OBJECT (menu), + menu_deactivate_cb, + combo_button); + combo_button->priv->menu = NULL; +} + +static void +gdl_combo_button_destroy (GtkObject *object) +{ + GdlComboButton *combo_button; + GdlComboButtonPrivate *priv; + + combo_button = GDL_COMBO_BUTTON (object); + priv = combo_button->priv; + + if (priv) { + g_free (priv); + combo_button->priv = NULL; + } + + (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); +} + +static void +gdl_combo_button_class_init (GdlComboButtonClass *klass) +{ + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + + parent_class = g_type_class_peek_parent (klass); + object_class = GTK_OBJECT_CLASS (klass); + widget_class = GTK_WIDGET_CLASS (klass); + + object_class->destroy = gdl_combo_button_destroy; + + g_signal_new ("activate-default", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GdlComboButtonClass, activate_default), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static void +gdl_combo_button_instance_init (GdlComboButton *combo_button) +{ + GdlComboButtonPrivate *priv; + GtkWidget *hbox, *align, *arrow; + + priv = g_new (GdlComboButtonPrivate, 1); + combo_button->priv = priv; + + priv->menu = NULL; + priv->menu_popped_up = FALSE; + + priv->default_button = gtk_button_new (); + gtk_button_set_relief (GTK_BUTTON (priv->default_button), GTK_RELIEF_NONE); + + /* Following code copied from gtk_button_construct_child. */ + priv->label = gtk_label_new (""); + gtk_label_set_use_underline (GTK_LABEL (priv->label), TRUE); + gtk_label_set_mnemonic_widget (GTK_LABEL (priv->label), + priv->default_button); + + priv->image = gtk_image_new (); + hbox = gtk_hbox_new (FALSE, 2); + + align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); + + gtk_box_pack_start (GTK_BOX (hbox), priv->image, FALSE, FALSE, 0); + gtk_box_pack_end (GTK_BOX (hbox), priv->label, FALSE, FALSE, 0); + + gtk_container_add (GTK_CONTAINER (priv->default_button), align); + gtk_container_add (GTK_CONTAINER (align), hbox); + /* End copied block. */ + + gtk_box_pack_start (GTK_BOX (combo_button), priv->default_button, + FALSE, FALSE, 0); + gtk_widget_show_all (priv->default_button); + + priv->menu_button = gtk_button_new (); + gtk_button_set_relief (GTK_BUTTON (priv->menu_button), GTK_RELIEF_NONE); + arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE); + gtk_container_add (GTK_CONTAINER (priv->menu_button), arrow); + gtk_box_pack_start (GTK_BOX (combo_button), priv->menu_button, FALSE, + FALSE, 0); + gtk_widget_show_all (priv->menu_button); + + /* Default button. */ + g_signal_connect (G_OBJECT (priv->default_button), "clicked", + G_CALLBACK (default_button_clicked_cb), combo_button); + g_signal_connect (G_OBJECT (priv->default_button), "button_press_event", + G_CALLBACK (default_button_press_event_cb), combo_button); + g_signal_connect (G_OBJECT (priv->default_button), "button_release_event", + G_CALLBACK (default_button_release_event_cb), combo_button); + g_signal_connect (G_OBJECT (priv->default_button), "enter_notify_event", + G_CALLBACK (button_enter_notify_cb), combo_button); + g_signal_connect (G_OBJECT (priv->default_button), "leave_notify_event", + G_CALLBACK (button_leave_notify_cb), combo_button); + + /* Menu button. */ + g_signal_connect (G_OBJECT (priv->menu_button), "button_press_event", + G_CALLBACK (menu_button_press_event_cb), combo_button); + g_signal_connect (G_OBJECT (priv->menu_button), "enter_notify_event", + G_CALLBACK (button_enter_notify_cb), combo_button); + g_signal_connect (G_OBJECT (priv->menu_button), "leave_notify_event", + G_CALLBACK (button_leave_notify_cb), combo_button); +} + +GtkWidget * +gdl_combo_button_new (void) +{ + GtkWidget *combo_button; + + combo_button = GTK_WIDGET (g_object_new (GDL_TYPE_COMBO_BUTTON, NULL)); + + return combo_button; +} + +void +gdl_combo_button_set_icon (GdlComboButton *combo_button, + GdkPixbuf *pixbuf) +{ + GdlComboButtonPrivate *priv; + + g_return_if_fail (GDL_IS_COMBO_BUTTON (combo_button)); + g_return_if_fail (GDK_IS_PIXBUF (pixbuf)); + + priv = combo_button->priv; + + gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf); +} + +void +gdl_combo_button_set_label (GdlComboButton *combo_button, + const gchar *label) +{ + GdlComboButtonPrivate *priv; + + g_return_if_fail (GDL_IS_COMBO_BUTTON (combo_button)); + g_return_if_fail (label != NULL); + + priv = combo_button->priv; + + gtk_label_set_text (GTK_LABEL (priv->label), label); +} + +void +gdl_combo_button_set_menu (GdlComboButton *combo_button, + GtkMenu *menu) +{ + GdlComboButtonPrivate *priv; + + g_return_if_fail (GDL_IS_COMBO_BUTTON (combo_button)); + g_return_if_fail (GTK_IS_MENU (menu)); + + priv = combo_button->priv; + + if (priv->menu != NULL) + gtk_menu_detach (GTK_MENU (priv->menu)); + + priv->menu = GTK_WIDGET (menu); + if (menu == NULL) + return; + + gtk_menu_attach_to_widget (menu, GTK_WIDGET (combo_button), menu_detacher); + + g_signal_connect (G_OBJECT (menu), "deactivate", + G_CALLBACK (menu_deactivate_cb), combo_button); +} diff --git a/src/libgdl/gdl-combo-button.h b/src/libgdl/gdl-combo-button.h new file mode 100644 index 000000000..6e80af0b6 --- /dev/null +++ b/src/libgdl/gdl-combo-button.h @@ -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 +#include +#include + +G_BEGIN_DECLS + +#define GDL_TYPE_COMBO_BUTTON (gdl_combo_button_get_type ()) +#define GDL_COMBO_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDL_TYPE_COMBO_BUTTON, GdlComboButton)) +#define GDL_COMBO_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDL_TYPE_COMBO_BUTTON, GdlComboButtonClass)) +#define GDL_IS_COMBO_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDL_TYPE_COMBO_BUTTON)) +#define GDL_IS_COMBO_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), GDL_TYPE_COMBO_BUTTON)) + +typedef struct _GdlComboButton GdlComboButton; +typedef struct _GdlComboButtonPrivate GdlComboButtonPrivate; +typedef struct _GdlComboButtonClass GdlComboButtonClass; + +struct _GdlComboButton { + GtkHBox parent; + + GdlComboButtonPrivate *priv; +}; + +struct _GdlComboButtonClass { + GtkHBoxClass parent_class; + + /* Signals. */ + void (* activate_default) (GdlComboButton *combo_button); +}; + +GType gdl_combo_button_get_type (void); +GtkWidget *gdl_combo_button_new (void); + +void gdl_combo_button_set_icon (GdlComboButton *combo_button, + GdkPixbuf *pixbuf); +void gdl_combo_button_set_label (GdlComboButton *combo_button, + const gchar *label); +void gdl_combo_button_set_menu (GdlComboButton *combo_button, + GtkMenu *menu); + +G_END_DECLS + +#endif /* _GDL_COMBO_BUTTON_H_ */ diff --git a/src/libgdl/gdl-dock-bar.c b/src/libgdl/gdl-dock-bar.c new file mode 100644 index 000000000..c4882e7ad --- /dev/null +++ b/src/libgdl/gdl-dock-bar.c @@ -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 + * + * 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 +#endif + +#include "gdl-i18n.h" +#include +#include + +#include "gdl-tools.h" +#include "gdl-dock.h" +#include "gdl-dock-master.h" +#include "gdl-dock-bar.h" +#include "libgdltypebuiltins.h" + +enum { + PROP_0, + PROP_MASTER, + PROP_DOCKBAR_STYLE +}; + +/* ----- Private prototypes ----- */ + +static void gdl_dock_bar_class_init (GdlDockBarClass *klass); +static void gdl_dock_bar_instance_init (GdlDockBar *dockbar); + +static void gdl_dock_bar_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void gdl_dock_bar_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); + +static void gdl_dock_bar_destroy (GtkObject *object); + +static void gdl_dock_bar_attach (GdlDockBar *dockbar, + GdlDockMaster *master); +static void gdl_dock_bar_remove_item (GdlDockBar *dockbar, + GdlDockItem *item); + +/* ----- Class variables and definitions ----- */ + +struct _GdlDockBarPrivate { + GdlDockMaster *master; + GSList *items; + GtkTooltips *tooltips; + GtkOrientation orientation; + GdlDockBarStyle dockbar_style; +}; + +/* ----- Private functions ----- */ + +GDL_CLASS_BOILERPLATE (GdlDockBar, gdl_dock_bar, GtkBox, GTK_TYPE_BOX) + +static void gdl_dock_bar_size_request (GtkWidget *widget, + GtkRequisition *requisition ); +static void gdl_dock_bar_size_allocate (GtkWidget *widget, + GtkAllocation *allocation ); +static void gdl_dock_bar_size_vrequest (GtkWidget *widget, + GtkRequisition *requisition ); +static void gdl_dock_bar_size_vallocate (GtkWidget *widget, + GtkAllocation *allocation ); +static void gdl_dock_bar_size_hrequest (GtkWidget *widget, + GtkRequisition *requisition ); +static void gdl_dock_bar_size_hallocate (GtkWidget *widget, + GtkAllocation *allocation ); +static void update_dock_items (GdlDockBar *dockbar, gboolean full_update); + +void +gdl_dock_bar_class_init (GdlDockBarClass *klass) +{ + GObjectClass *g_object_class; + GtkObjectClass *gtk_object_class; + GtkWidgetClass *widget_class; + + g_object_class = G_OBJECT_CLASS (klass); + gtk_object_class = GTK_OBJECT_CLASS (klass); + + g_object_class->get_property = gdl_dock_bar_get_property; + g_object_class->set_property = gdl_dock_bar_set_property; + + gtk_object_class->destroy = gdl_dock_bar_destroy; + + g_object_class_install_property ( + g_object_class, PROP_MASTER, + g_param_spec_object ("master", _("Master"), + _("GdlDockMaster object which the dockbar widget " + "is attached to"), + GDL_TYPE_DOCK_MASTER, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + g_object_class, PROP_DOCKBAR_STYLE, + g_param_spec_enum ("dockbar-style", _("Dockbar style"), + _("Dockbar style to show items on it"), + GDL_TYPE_DOCK_BAR_STYLE, + GDL_DOCK_BAR_BOTH, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + widget_class = GTK_WIDGET_CLASS (klass); + widget_class->size_request = gdl_dock_bar_size_request; + widget_class->size_allocate = gdl_dock_bar_size_allocate; +} + +static void +gdl_dock_bar_instance_init (GdlDockBar *dockbar) +{ + dockbar->_priv = g_new0 (GdlDockBarPrivate, 1); + dockbar->_priv->master = NULL; + dockbar->_priv->items = NULL; + dockbar->_priv->tooltips = gtk_tooltips_new (); + dockbar->_priv->orientation = GTK_ORIENTATION_VERTICAL; + dockbar->_priv->dockbar_style = GDL_DOCK_BAR_BOTH; + g_object_ref (dockbar->_priv->tooltips); + gtk_object_sink (GTK_OBJECT (dockbar->_priv->tooltips)); +} + +static void +gdl_dock_bar_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdlDockBar *dockbar = GDL_DOCK_BAR (object); + + switch (prop_id) { + case PROP_MASTER: + g_value_set_object (value, dockbar->_priv->master); + break; + case PROP_DOCKBAR_STYLE: + g_value_set_enum (value, dockbar->_priv->dockbar_style); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + }; +} + +static void +gdl_dock_bar_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdlDockBar *dockbar = GDL_DOCK_BAR (object); + + switch (prop_id) { + case PROP_MASTER: + gdl_dock_bar_attach (dockbar, g_value_get_object (value)); + break; + case PROP_DOCKBAR_STYLE: + dockbar->_priv->dockbar_style = g_value_get_enum (value); + update_dock_items (dockbar, TRUE); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + }; +} + +static void +on_dock_item_foreach_disconnect (GdlDockItem *item, GdlDockBar *dock_bar) +{ + g_signal_handlers_disconnect_by_func (item, gdl_dock_bar_remove_item, + dock_bar); +} + +static void +gdl_dock_bar_destroy (GtkObject *object) +{ + GdlDockBar *dockbar = GDL_DOCK_BAR (object); + + if (dockbar->_priv) { + GdlDockBarPrivate *priv = dockbar->_priv; + + if (priv->items) { + g_slist_foreach (priv->items, + (GFunc) on_dock_item_foreach_disconnect, + object); + g_slist_free (priv->items); + } + + if (priv->master) { + g_signal_handlers_disconnect_matched (priv->master, + G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, dockbar); + g_object_unref (priv->master); + priv->master = NULL; + } + + if (priv->tooltips) { + g_object_unref (priv->tooltips); + priv->tooltips = NULL; + } + + dockbar->_priv = NULL; + + g_free (priv); + } + + GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object)); +} + +static void +gdl_dock_bar_remove_item (GdlDockBar *dockbar, + GdlDockItem *item) +{ + GdlDockBarPrivate *priv; + GtkWidget *button; + + g_return_if_fail (GDL_IS_DOCK_BAR (dockbar)); + g_return_if_fail (GDL_IS_DOCK_ITEM (item)); + + priv = dockbar->_priv; + + if (g_slist_index (priv->items, item) == -1) { + g_warning ("Item has not been added to the dockbar"); + return; + } + + priv->items = g_slist_remove (priv->items, item); + + button = g_object_get_data (G_OBJECT (item), "GdlDockBarButton"); + g_assert (button != NULL); + gtk_container_remove (GTK_CONTAINER (dockbar), button); + g_object_set_data (G_OBJECT (item), "GdlDockBarButton", NULL); + g_signal_handlers_disconnect_by_func (item, + G_CALLBACK (gdl_dock_bar_remove_item), + dockbar); +} + +static void +gdl_dock_bar_item_clicked (GtkWidget *button, + GdlDockItem *item) +{ + GdlDockBar *dockbar; + GdlDockObject *controller; + + g_return_if_fail (item != NULL); + + dockbar = g_object_get_data (G_OBJECT (item), "GdlDockBar"); + g_assert (dockbar != NULL); + g_object_set_data (G_OBJECT (item), "GdlDockBar", NULL); + + controller = gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (item)); + + GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_ICONIFIED); + gdl_dock_item_show_item (item); + gdl_dock_bar_remove_item (dockbar, item); + gtk_widget_queue_resize (GTK_WIDGET (controller)); +} + +static void +gdl_dock_bar_add_item (GdlDockBar *dockbar, + GdlDockItem *item) +{ + GdlDockBarPrivate *priv; + GtkWidget *button; + gchar *stock_id; + gchar *name; + GdkPixbuf *pixbuf_icon; + GtkWidget *image, *box, *label; + + g_return_if_fail (GDL_IS_DOCK_BAR (dockbar)); + g_return_if_fail (GDL_IS_DOCK_ITEM (item)); + + priv = dockbar->_priv; + + if (g_slist_index (priv->items, item) != -1) { + g_warning ("Item has already been added to the dockbar"); + return; + } + + priv->items = g_slist_append (priv->items, item); + + /* Create a button for the item. */ + button = gtk_button_new (); + gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); + + if (dockbar->_priv->orientation == GTK_ORIENTATION_HORIZONTAL) + box = gtk_hbox_new (FALSE, 0); + else + box = gtk_vbox_new (FALSE, 0); + + g_object_get (item, "stock-id", &stock_id, "pixbuf-icon", &pixbuf_icon, + "long-name", &name, NULL); + + if (dockbar->_priv->dockbar_style == GDL_DOCK_BAR_TEXT || + dockbar->_priv->dockbar_style == GDL_DOCK_BAR_BOTH) { + label = gtk_label_new (name); + if (dockbar->_priv->orientation == GTK_ORIENTATION_VERTICAL) + gtk_label_set_angle (GTK_LABEL (label), 90); + gtk_box_pack_start_defaults (GTK_BOX (box), label); + } + + /* FIXME: For now AUTO behaves same as BOTH */ + + if (dockbar->_priv->dockbar_style == GDL_DOCK_BAR_ICONS || + dockbar->_priv->dockbar_style == GDL_DOCK_BAR_BOTH || + dockbar->_priv->dockbar_style == GDL_DOCK_BAR_AUTO) { + if (stock_id) { + image = gtk_image_new_from_stock (stock_id, + GTK_ICON_SIZE_SMALL_TOOLBAR); + g_free (stock_id); + } else if (pixbuf_icon) { + image = gtk_image_new_from_pixbuf (pixbuf_icon); + } else { + image = gtk_image_new_from_stock (GTK_STOCK_NEW, + GTK_ICON_SIZE_SMALL_TOOLBAR); + } + gtk_box_pack_start_defaults (GTK_BOX (box), image); + } + + gtk_container_add (GTK_CONTAINER (button), box); + gtk_box_pack_start (GTK_BOX (dockbar), button, FALSE, FALSE, 0); + + gtk_tooltips_set_tip (priv->tooltips, button, name, name); + g_free (name); + + g_object_set_data (G_OBJECT (item), "GdlDockBar", dockbar); + g_object_set_data (G_OBJECT (item), "GdlDockBarButton", button); + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (gdl_dock_bar_item_clicked), item); + + gtk_widget_show_all (button); + + /* Set up destroy notify */ + g_signal_connect_swapped (item, "destroy", + G_CALLBACK (gdl_dock_bar_remove_item), + dockbar); +} + +static void +build_list (GdlDockObject *object, GList **list) +{ + /* add only items, not toplevels */ + if (GDL_IS_DOCK_ITEM (object)) + *list = g_list_prepend (*list, object); +} + +static void +update_dock_items (GdlDockBar *dockbar, gboolean full_update) +{ + GdlDockMaster *master; + GList *items, *l; + + g_return_if_fail (dockbar != NULL); + + if (!dockbar->_priv->master) + return; + + master = dockbar->_priv->master; + + /* build items list */ + items = NULL; + gdl_dock_master_foreach (master, (GFunc) build_list, &items); + + if (!full_update) { + for (l = items; l != NULL; l = l->next) { + GdlDockItem *item = GDL_DOCK_ITEM (l->data); + + if (g_slist_index (dockbar->_priv->items, item) != -1 && + !GDL_DOCK_ITEM_ICONIFIED (item)) + gdl_dock_bar_remove_item (dockbar, item); + else if (g_slist_index (dockbar->_priv->items, item) == -1 && + GDL_DOCK_ITEM_ICONIFIED (item)) + gdl_dock_bar_add_item (dockbar, item); + } + } else { + for (l = items; l != NULL; l = l->next) { + GdlDockItem *item = GDL_DOCK_ITEM (l->data); + + if (g_slist_index (dockbar->_priv->items, item) != -1) + gdl_dock_bar_remove_item (dockbar, item); + if (GDL_DOCK_ITEM_ICONIFIED (item)) + gdl_dock_bar_add_item (dockbar, item); + } + } + g_list_free (items); +} + +static void +gdl_dock_bar_layout_changed_cb (GdlDockMaster *master, + GdlDockBar *dockbar) +{ + update_dock_items (dockbar, FALSE); +} + +static void +gdl_dock_bar_attach (GdlDockBar *dockbar, + GdlDockMaster *master) +{ + g_return_if_fail (dockbar != NULL); + g_return_if_fail (master == NULL || GDL_IS_DOCK_MASTER (master)); + + if (dockbar->_priv->master) { + g_signal_handlers_disconnect_matched (dockbar->_priv->master, + G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, dockbar); + g_object_unref (dockbar->_priv->master); + } + + dockbar->_priv->master = master; + if (dockbar->_priv->master) { + g_object_ref (dockbar->_priv->master); + g_signal_connect (dockbar->_priv->master, "layout-changed", + G_CALLBACK (gdl_dock_bar_layout_changed_cb), + dockbar); + } + + update_dock_items (dockbar, FALSE); +} + +static void gdl_dock_bar_size_request (GtkWidget *widget, + GtkRequisition *requisition ) +{ + GdlDockBar *dockbar; + + dockbar = GDL_DOCK_BAR (widget); + + /* default to vertical for unknown values */ + switch (dockbar->_priv->orientation) { + case GTK_ORIENTATION_HORIZONTAL: + gdl_dock_bar_size_hrequest (widget, requisition); + break; + case GTK_ORIENTATION_VERTICAL: + default: + gdl_dock_bar_size_vrequest (widget, requisition); + break; + } +} + +static void gdl_dock_bar_size_allocate (GtkWidget *widget, + GtkAllocation *allocation ) +{ + GdlDockBar *dockbar; + + dockbar = GDL_DOCK_BAR (widget); + + /* default to vertical for unknown values */ + switch (dockbar->_priv->orientation) { + case GTK_ORIENTATION_HORIZONTAL: + gdl_dock_bar_size_hallocate (widget, allocation); + break; + case GTK_ORIENTATION_VERTICAL: + default: + gdl_dock_bar_size_vallocate (widget, allocation); + break; + } +} + +static void gdl_dock_bar_size_vrequest (GtkWidget *widget, + GtkRequisition *requisition ) +{ + GtkBox *box; + GtkBoxChild *child; + GtkRequisition child_requisition; + GList *children; + gint nvis_children; + gint height; + + box = GTK_BOX (widget); + requisition->width = 0; + requisition->height = 0; + nvis_children = 0; + + children = box->children; + while (children) + { + child = children->data; + children = children->next; + + if (GTK_WIDGET_VISIBLE (child->widget)) + { + gtk_widget_size_request (child->widget, &child_requisition); + + if (box->homogeneous) + { + height = child_requisition.height + child->padding * 2; + requisition->height = MAX (requisition->height, height); + } + else + { + requisition->height += child_requisition.height + child->padding * 2; + } + + requisition->width = MAX (requisition->width, child_requisition.width); + + nvis_children += 1; + } + } + + if (nvis_children > 0) + { + if (box->homogeneous) + requisition->height *= nvis_children; + requisition->height += (nvis_children - 1) * box->spacing; + } + + requisition->width += GTK_CONTAINER (box)->border_width * 2; + requisition->height += GTK_CONTAINER (box)->border_width * 2; + +} + +static void gdl_dock_bar_size_vallocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkBox *box; + GtkBoxChild *child; + GList *children; + GtkAllocation child_allocation; + gint nvis_children; + gint nexpand_children; + gint child_height; + gint height; + gint extra; + gint y; + + box = GTK_BOX (widget); + widget->allocation = *allocation; + + nvis_children = 0; + nexpand_children = 0; + children = box->children; + + while (children) + { + child = children->data; + children = children->next; + + if (GTK_WIDGET_VISIBLE (child->widget)) + { + nvis_children += 1; + if (child->expand) + nexpand_children += 1; + } + } + + if (nvis_children > 0) + { + if (box->homogeneous) + { + height = (allocation->height - + GTK_CONTAINER (box)->border_width * 2 - + (nvis_children - 1) * box->spacing); + extra = height / nvis_children; + } + else if (nexpand_children > 0) + { + height = (gint) allocation->height - (gint) widget->requisition.height; + extra = height / nexpand_children; + } + else + { + height = 0; + extra = 0; + } + + y = allocation->y + GTK_CONTAINER (box)->border_width; + child_allocation.x = allocation->x + GTK_CONTAINER (box)->border_width; + child_allocation.width = MAX (1, (gint) allocation->width - (gint) GTK_CONTAINER (box)->border_width * 2); + + children = box->children; + while (children) + { + child = children->data; + children = children->next; + + if ((child->pack == GTK_PACK_START) && GTK_WIDGET_VISIBLE (child->widget)) + { + if (box->homogeneous) + { + if (nvis_children == 1) + child_height = height; + else + child_height = extra; + + nvis_children -= 1; + height -= extra; + } + else + { + GtkRequisition child_requisition; + + gtk_widget_get_child_requisition (child->widget, &child_requisition); + child_height = child_requisition.height + child->padding * 2; + + if (child->expand) + { + if (nexpand_children == 1) + child_height += height; + else + child_height += extra; + + nexpand_children -= 1; + height -= extra; + } + } + + if (child->fill) + { + child_allocation.height = MAX (1, child_height - (gint)child->padding * 2); + child_allocation.y = y + child->padding; + } + else + { + GtkRequisition child_requisition; + + gtk_widget_get_child_requisition (child->widget, &child_requisition); + child_allocation.height = child_requisition.height; + child_allocation.y = y + (child_height - child_allocation.height) / 2; + } + + gtk_widget_size_allocate (child->widget, &child_allocation); + + y += child_height + box->spacing; + } + } + + y = allocation->y + allocation->height - GTK_CONTAINER (box)->border_width; + + children = box->children; + while (children) + { + child = children->data; + children = children->next; + + if ((child->pack == GTK_PACK_END) && GTK_WIDGET_VISIBLE (child->widget)) + { + GtkRequisition child_requisition; + gtk_widget_get_child_requisition (child->widget, &child_requisition); + + if (box->homogeneous) + { + if (nvis_children == 1) + child_height = height; + else + child_height = extra; + + nvis_children -= 1; + height -= extra; + } + else + { + child_height = child_requisition.height + child->padding * 2; + + if (child->expand) + { + if (nexpand_children == 1) + child_height += height; + else + child_height += extra; + + nexpand_children -= 1; + height -= extra; + } + } + + if (child->fill) + { + child_allocation.height = MAX (1, child_height - (gint)child->padding * 2); + child_allocation.y = y + child->padding - child_height; + } + else + { + child_allocation.height = child_requisition.height; + child_allocation.y = y + (child_height - child_allocation.height) / 2 - child_height; + } + + gtk_widget_size_allocate (child->widget, &child_allocation); + + y -= (child_height + box->spacing); + } + } + } +} + +static void gdl_dock_bar_size_hrequest (GtkWidget *widget, + GtkRequisition *requisition ) +{ + GtkBox *box; + GtkBoxChild *child; + GList *children; + gint nvis_children; + gint width; + + box = GTK_BOX (widget); + requisition->width = 0; + requisition->height = 0; + nvis_children = 0; + + children = box->children; + while (children) + { + child = children->data; + children = children->next; + + if (GTK_WIDGET_VISIBLE (child->widget)) + { + GtkRequisition child_requisition; + + gtk_widget_size_request (child->widget, &child_requisition); + + if (box->homogeneous) + { + width = child_requisition.width + child->padding * 2; + requisition->width = MAX (requisition->width, width); + } + else + { + requisition->width += child_requisition.width + child->padding * 2; + } + + requisition->height = MAX (requisition->height, child_requisition.height); + + nvis_children += 1; + } + } + + if (nvis_children > 0) + { + if (box->homogeneous) + requisition->width *= nvis_children; + requisition->width += (nvis_children - 1) * box->spacing; + } + + requisition->width += GTK_CONTAINER (box)->border_width * 2; + requisition->height += GTK_CONTAINER (box)->border_width * 2; +} + +static void gdl_dock_bar_size_hallocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkBox *box; + GtkBoxChild *child; + GList *children; + GtkAllocation child_allocation; + gint nvis_children; + gint nexpand_children; + gint child_width; + gint width; + gint extra; + gint x; + GtkTextDirection direction; + + box = GTK_BOX (widget); + widget->allocation = *allocation; + + direction = gtk_widget_get_direction (widget); + + nvis_children = 0; + nexpand_children = 0; + children = box->children; + + while (children) + { + child = children->data; + children = children->next; + + if (GTK_WIDGET_VISIBLE (child->widget)) + { + nvis_children += 1; + if (child->expand) + nexpand_children += 1; + } + } + + if (nvis_children > 0) + { + if (box->homogeneous) + { + width = (allocation->width - + GTK_CONTAINER (box)->border_width * 2 - + (nvis_children - 1) * box->spacing); + extra = width / nvis_children; + } + else if (nexpand_children > 0) + { + width = (gint) allocation->width - (gint) widget->requisition.width; + extra = width / nexpand_children; + } + else + { + width = 0; + extra = 0; + } + + x = allocation->x + GTK_CONTAINER (box)->border_width; + child_allocation.y = allocation->y + GTK_CONTAINER (box)->border_width; + child_allocation.height = MAX (1, (gint) allocation->height - (gint) GTK_CONTAINER (box)->border_width * 2); + + children = box->children; + while (children) + { + child = children->data; + children = children->next; + + if ((child->pack == GTK_PACK_START) && GTK_WIDGET_VISIBLE (child->widget)) + { + if (box->homogeneous) + { + if (nvis_children == 1) + child_width = width; + else + child_width = extra; + + nvis_children -= 1; + width -= extra; + } + else + { + GtkRequisition child_requisition; + + gtk_widget_get_child_requisition (child->widget, &child_requisition); + + child_width = child_requisition.width + child->padding * 2; + + if (child->expand) + { + if (nexpand_children == 1) + child_width += width; + else + child_width += extra; + + nexpand_children -= 1; + width -= extra; + } + } + + if (child->fill) + { + child_allocation.width = MAX (1, (gint) child_width - (gint) child->padding * 2); + child_allocation.x = x + child->padding; + } + else + { + GtkRequisition child_requisition; + + gtk_widget_get_child_requisition (child->widget, &child_requisition); + child_allocation.width = child_requisition.width; + child_allocation.x = x + (child_width - child_allocation.width) / 2; + } + + if (direction == GTK_TEXT_DIR_RTL) + child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width; + + gtk_widget_size_allocate (child->widget, &child_allocation); + + x += child_width + box->spacing; + } + } + + x = allocation->x + allocation->width - GTK_CONTAINER (box)->border_width; + + children = box->children; + while (children) + { + child = children->data; + children = children->next; + + if ((child->pack == GTK_PACK_END) && GTK_WIDGET_VISIBLE (child->widget)) + { + GtkRequisition child_requisition; + gtk_widget_get_child_requisition (child->widget, &child_requisition); + + if (box->homogeneous) + { + if (nvis_children == 1) + child_width = width; + else + child_width = extra; + + nvis_children -= 1; + width -= extra; + } + else + { + child_width = child_requisition.width + child->padding * 2; + + if (child->expand) + { + if (nexpand_children == 1) + child_width += width; + else + child_width += extra; + + nexpand_children -= 1; + width -= extra; + } + } + + if (child->fill) + { + child_allocation.width = MAX (1, (gint)child_width - (gint)child->padding * 2); + child_allocation.x = x + child->padding - child_width; + } + else + { + child_allocation.width = child_requisition.width; + child_allocation.x = x + (child_width - child_allocation.width) / 2 - child_width; + } + + if (direction == GTK_TEXT_DIR_RTL) + child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width; + + gtk_widget_size_allocate (child->widget, &child_allocation); + + x -= (child_width + box->spacing); + } + } + } +} + +GtkWidget * +gdl_dock_bar_new (GdlDock *dock) +{ + GdlDockMaster *master = NULL; + + /* get the master of the given dock */ + if (dock) + master = GDL_DOCK_OBJECT_GET_MASTER (dock); + + return g_object_new (GDL_TYPE_DOCK_BAR, + "master", master, NULL); +} + +GtkOrientation gdl_dock_bar_get_orientation (GdlDockBar *dockbar) +{ + g_return_val_if_fail (GDL_IS_DOCK_BAR (dockbar), + GTK_ORIENTATION_VERTICAL); + + return dockbar->_priv->orientation; +} + +void gdl_dock_bar_set_orientation (GdlDockBar *dockbar, + GtkOrientation orientation) +{ + g_return_if_fail (GDL_IS_DOCK_BAR (dockbar)); + + dockbar->_priv->orientation = orientation; + + gtk_widget_queue_resize (GTK_WIDGET (dockbar)); +} + +void gdl_dock_bar_set_style(GdlDockBar* dockbar, + GdlDockBarStyle style) +{ + g_object_set(G_OBJECT(dockbar), "dockbar-style", style, NULL); +} + +GdlDockBarStyle gdl_dock_bar_get_style(GdlDockBar* dockbar) +{ + GdlDockBarStyle style; + g_object_get(G_OBJECT(dockbar), "dockbar-style", &style, NULL); + return style; +} diff --git a/src/libgdl/gdl-dock-bar.h b/src/libgdl/gdl-dock-bar.h new file mode 100644 index 000000000..22ba94dd1 --- /dev/null +++ b/src/libgdl/gdl-dock-bar.h @@ -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 + * + * 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 + +G_BEGIN_DECLS + +/* standard macros */ +#define GDL_TYPE_DOCK_BAR (gdl_dock_bar_get_type ()) +#define GDL_DOCK_BAR(obj) (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_BAR, GdlDockBar)) +#define GDL_DOCK_BAR_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_BAR, GdlDockBarClass)) +#define GDL_IS_DOCK_BAR(obj) (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_BAR)) +#define GDL_IS_DOCK_BAR_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_BAR)) +#define GDL_DOCK_BAR_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK_BAR, GdlDockBarClass)) + +/* data types & structures */ +typedef struct _GdlDockBar GdlDockBar; +typedef struct _GdlDockBarClass GdlDockBarClass; +typedef struct _GdlDockBarPrivate GdlDockBarPrivate; + +typedef enum { + GDL_DOCK_BAR_ICONS, + GDL_DOCK_BAR_TEXT, + GDL_DOCK_BAR_BOTH, + GDL_DOCK_BAR_AUTO +} GdlDockBarStyle; + +struct _GdlDockBar { + GtkBox parent; + + GdlDock *dock; + + GdlDockBarPrivate *_priv; +}; + +struct _GdlDockBarClass { + GtkVBoxClass parent_class; +}; + +GType gdl_dock_bar_get_type (void); + +GtkWidget *gdl_dock_bar_new (GdlDock *dock); + +GtkOrientation gdl_dock_bar_get_orientation (GdlDockBar *dockbar); +void gdl_dock_bar_set_orientation (GdlDockBar *dockbar, + GtkOrientation orientation); +void gdl_dock_bar_set_style (GdlDockBar *dockbar, + GdlDockBarStyle style); +GdlDockBarStyle gdl_dock_bar_get_style (GdlDockBar *dockbar); + +G_END_DECLS + +#endif /* __GDL_DOCK_BAR_H__ */ diff --git a/src/libgdl/gdl-dock-item-grip.c b/src/libgdl/gdl-dock-item-grip.c new file mode 100644 index 000000000..0a8007d49 --- /dev/null +++ b/src/libgdl/gdl-dock-item-grip.c @@ -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 +#endif + +#include "gdl-i18n.h" +#include +#include +#include +#include +#include +#include "gdl-dock-item.h" +#include "gdl-dock-item-grip.h" +#include "gdl-stock.h" +#include "gdl-tools.h" + +#define ALIGN_BORDER 5 + +enum { + PROP_0, + PROP_ITEM +}; + +struct _GdlDockItemGripPrivate { + GtkWidget *close_button; + GtkWidget *iconify_button; + GtkTooltips *tooltips; + + gboolean icon_pixbuf_valid; + GdkPixbuf *icon_pixbuf; + + gchar *title; + PangoLayout *title_layout; +}; + +GDL_CLASS_BOILERPLATE (GdlDockItemGrip, gdl_dock_item_grip, + GtkContainer, GTK_TYPE_CONTAINER); + +/* must be called after size_allocate */ +static void +gdl_dock_item_grip_get_title_area (GdlDockItemGrip *grip, + GdkRectangle *area) +{ + GtkWidget *widget = GTK_WIDGET (grip); + gint border = GTK_CONTAINER (grip)->border_width; + gint alloc_height; + + area->width = (widget->allocation.width - 2 * border - ALIGN_BORDER); + + pango_layout_get_pixel_size (grip->_priv->title_layout, NULL, &alloc_height); + + alloc_height = MAX (grip->_priv->close_button->allocation.height, alloc_height); + alloc_height = MAX (grip->_priv->iconify_button->allocation.height, alloc_height); + if (GTK_WIDGET_VISIBLE (grip->_priv->close_button)) { + area->width -= grip->_priv->close_button->allocation.width; + } + if (GTK_WIDGET_VISIBLE (grip->_priv->iconify_button)) { + area->width -= grip->_priv->iconify_button->allocation.width; + } + + area->x = widget->allocation.x + border + ALIGN_BORDER; + area->y = widget->allocation.y + border; + area->height = alloc_height; + + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) + area->x += (widget->allocation.width - 2 * border) - area->width; +} + +static void +ensure_title_and_icon_pixbuf (GdlDockItemGrip *grip) +{ + gchar *stock_id; + GdkPixbuf *pixbuf; + + g_return_if_fail (GDL_IS_DOCK_ITEM_GRIP (grip)); + + /* get long name property from the dock object */ + if (!grip->_priv->title) { + g_object_get (G_OBJECT (grip->item), "long-name", &grip->_priv->title, NULL); + if (!grip->_priv->title) + grip->_priv->title = g_strdup (""); + } + + /* retrieve stock pixbuf, if any */ + if (!grip->_priv->icon_pixbuf_valid) { + g_object_get (G_OBJECT (grip->item), "stock-id", &stock_id, NULL); + + if (stock_id) { + grip->_priv->icon_pixbuf = gtk_widget_render_icon (GTK_WIDGET (grip), + stock_id, + GTK_ICON_SIZE_MENU, ""); + g_free (stock_id); + grip->_priv->icon_pixbuf_valid = TRUE; + } + } + + /* retrieve pixbuf icon, if any */ + if (!grip->_priv->icon_pixbuf_valid) { + g_object_get (G_OBJECT (grip->item), "pixbuf-icon", &pixbuf, NULL); + + if (pixbuf) { + grip->_priv->icon_pixbuf = pixbuf; + grip->_priv->icon_pixbuf_valid = TRUE; + } + } + + /* create layout: the actual text is reset at size_allocate */ + if (!grip->_priv->title_layout) { + grip->_priv->title_layout = gtk_widget_create_pango_layout (GTK_WIDGET (grip), + grip->_priv->title); + pango_layout_set_single_paragraph_mode (grip->_priv->title_layout, TRUE); + } +} + +static gint +gdl_dock_item_grip_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + GdlDockItemGrip *grip; + GdkRectangle title_area; + GdkRectangle expose_area; + gint layout_width; + gint layout_height; + gint text_x; + gint text_y; + + grip = GDL_DOCK_ITEM_GRIP (widget); + gdl_dock_item_grip_get_title_area (grip, &title_area); + + if (grip->_priv->icon_pixbuf) { + GdkRectangle pixbuf_rect; + + pixbuf_rect.width = gdk_pixbuf_get_width (grip->_priv->icon_pixbuf); + pixbuf_rect.height = gdk_pixbuf_get_height (grip->_priv->icon_pixbuf); + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) { + pixbuf_rect.x = title_area.x + title_area.width - pixbuf_rect.width; + } else { + pixbuf_rect.x = title_area.x; + title_area.x += pixbuf_rect.width + 1; + } + /* shrink title area by the pixbuf width plus a 1px spacing */ + title_area.width -= pixbuf_rect.width + 1; + pixbuf_rect.y = title_area.y + (title_area.height - pixbuf_rect.height) / 2; + + if (gdk_rectangle_intersect (&event->area, &pixbuf_rect, &expose_area)) { + GdkGC *gc; + GtkStyle *style; + + style = gtk_widget_get_style (widget); + gc = style->bg_gc[widget->state]; + gdk_draw_pixbuf (GDK_DRAWABLE (widget->window), gc, + grip->_priv->icon_pixbuf, + 0, 0, pixbuf_rect.x, pixbuf_rect.y, + pixbuf_rect.width, pixbuf_rect.height, + GDK_RGB_DITHER_NONE, 0, 0); + } + } + + if (gdk_rectangle_intersect (&title_area, &event->area, &expose_area)) { + pango_layout_get_pixel_size (grip->_priv->title_layout, &layout_width, + &layout_height); + + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) + text_x = title_area.x + title_area.width - layout_width; + else + text_x = title_area.x; + + text_y = title_area.y + (title_area.height - layout_height) / 2; + + gtk_paint_layout (widget->style, widget->window, widget->state, TRUE, + &expose_area, widget, NULL, text_x, text_y, + grip->_priv->title_layout); + } + + return GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event); +} + +static void +gdl_dock_item_grip_item_notify (GObject *master, + GParamSpec *pspec, + gpointer data) +{ + GdlDockItemGrip *grip; + gboolean cursor; + + grip = GDL_DOCK_ITEM_GRIP (data); + + if (strcmp (pspec->name, "stock-id") == 0) { + if (grip->_priv->icon_pixbuf) { + g_object_unref (grip->_priv->icon_pixbuf); + grip->_priv->icon_pixbuf = NULL; + } + grip->_priv->icon_pixbuf_valid = FALSE; + ensure_title_and_icon_pixbuf (grip); + + } else if (strcmp (pspec->name, "long-name") == 0) { + g_free (grip->_priv->title); + g_object_unref (grip->_priv->title_layout); + grip->_priv->title_layout = NULL; + grip->_priv->title = NULL; + ensure_title_and_icon_pixbuf (grip); + gtk_widget_queue_draw (GTK_WIDGET (grip)); + } else if (strcmp (pspec->name, "behavior") == 0) { + cursor = FALSE; + if (grip->_priv->close_button) { + if (GDL_DOCK_ITEM_CANT_CLOSE (grip->item)) { + gtk_widget_hide (GTK_WIDGET (grip->_priv->close_button)); + } else { + gtk_widget_show (GTK_WIDGET (grip->_priv->close_button)); + cursor = TRUE; + } + } + if (grip->_priv->iconify_button) { + if (GDL_DOCK_ITEM_CANT_ICONIFY (grip->item)) { + gtk_widget_hide (GTK_WIDGET (grip->_priv->iconify_button)); + } else { + gtk_widget_show (GTK_WIDGET (grip->_priv->iconify_button)); + cursor = TRUE; + } + } + if (grip->title_window && !cursor) + gdk_window_set_cursor (grip->title_window, NULL); + + } +} + +static void +gdl_dock_item_grip_destroy (GtkObject *object) +{ + GdlDockItemGrip *grip = GDL_DOCK_ITEM_GRIP (object); + + if (grip->_priv) { + GdlDockItemGripPrivate *priv = grip->_priv; + + if (priv->title_layout) { + g_object_unref (priv->title_layout); + priv->title_layout = NULL; + } + g_free (priv->title); + priv->title = NULL; + + if (priv->icon_pixbuf) { + g_object_unref (priv->icon_pixbuf); + priv->icon_pixbuf = NULL; + } + + if (priv->tooltips) { + g_object_unref (priv->tooltips); + priv->tooltips = NULL; + } + + if (grip->item) + g_signal_handlers_disconnect_by_func (grip->item, + gdl_dock_item_grip_item_notify, + grip); + grip->item = NULL; + + grip->_priv = NULL; + g_free (priv); + } + + GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object)); +} + +static void +gdl_dock_item_grip_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdlDockItemGrip *grip; + + g_return_if_fail (GDL_IS_DOCK_ITEM_GRIP (object)); + + grip = GDL_DOCK_ITEM_GRIP (object); + + switch (prop_id) { + case PROP_ITEM: + grip->item = g_value_get_object (value); + if (grip->item) { + g_signal_connect (grip->item, "notify::long_name", + G_CALLBACK (gdl_dock_item_grip_item_notify), + grip); + g_signal_connect (grip->item, "notify::stock_id", + G_CALLBACK (gdl_dock_item_grip_item_notify), + grip); + g_signal_connect (grip->item, "notify::behavior", + G_CALLBACK (gdl_dock_item_grip_item_notify), + grip); + + if (!GDL_DOCK_ITEM_CANT_CLOSE (grip->item) && grip->_priv->close_button) + gtk_widget_show (grip->_priv->close_button); + if (!GDL_DOCK_ITEM_CANT_ICONIFY (grip->item) && grip->_priv->iconify_button) + gtk_widget_show (grip->_priv->iconify_button); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdl_dock_item_grip_close_clicked (GtkWidget *widget, + GdlDockItemGrip *grip) +{ + g_return_if_fail (grip->item != NULL); + + gdl_dock_item_hide_item (grip->item); +} + +static void +gdl_dock_item_grip_iconify_clicked (GtkWidget *widget, + GdlDockItemGrip *grip) +{ + g_return_if_fail (grip->item != NULL); + + gdl_dock_item_iconify_item (grip->item); + + /* Workaround to unhighlight the iconify button. */ + GTK_BUTTON (grip->_priv->iconify_button)->in_button = FALSE; + gtk_button_leave (GTK_BUTTON (grip->_priv->iconify_button)); +} + +static void +gdl_dock_item_grip_instance_init (GdlDockItemGrip *grip) +{ + GtkWidget *image; + + GTK_WIDGET_SET_FLAGS (grip, GTK_NO_WINDOW); + + grip->_priv = g_new0 (GdlDockItemGripPrivate, 1); + grip->_priv->icon_pixbuf_valid = FALSE; + grip->_priv->icon_pixbuf = NULL; + grip->_priv->title_layout = NULL; + + gtk_widget_push_composite_child (); + grip->_priv->close_button = gtk_button_new (); + gtk_widget_pop_composite_child (); + + GTK_WIDGET_UNSET_FLAGS (grip->_priv->close_button, GTK_CAN_FOCUS); + gtk_widget_set_parent (grip->_priv->close_button, GTK_WIDGET (grip)); + gtk_button_set_relief (GTK_BUTTON (grip->_priv->close_button), GTK_RELIEF_NONE); + gtk_widget_show (grip->_priv->close_button); + + image = gtk_image_new_from_stock (GDL_STOCK_CLOSE, GTK_ICON_SIZE_MENU); + gtk_container_add (GTK_CONTAINER (grip->_priv->close_button), image); + gtk_widget_show (image); + + g_signal_connect (G_OBJECT (grip->_priv->close_button), "clicked", + G_CALLBACK (gdl_dock_item_grip_close_clicked), grip); + + gtk_widget_push_composite_child (); + grip->_priv->iconify_button = gtk_button_new (); + gtk_widget_pop_composite_child (); + + GTK_WIDGET_UNSET_FLAGS (grip->_priv->iconify_button, GTK_CAN_FOCUS); + gtk_widget_set_parent (grip->_priv->iconify_button, GTK_WIDGET (grip)); + gtk_button_set_relief (GTK_BUTTON (grip->_priv->iconify_button), GTK_RELIEF_NONE); + gtk_widget_show (grip->_priv->iconify_button); + + image = gtk_image_new_from_stock (GDL_STOCK_MENU_LEFT, GTK_ICON_SIZE_MENU); + gtk_container_add (GTK_CONTAINER (grip->_priv->iconify_button), image); + gtk_widget_show (image); + + g_signal_connect (G_OBJECT (grip->_priv->iconify_button), "clicked", + G_CALLBACK (gdl_dock_item_grip_iconify_clicked), grip); + + grip->_priv->tooltips = gtk_tooltips_new (); + g_object_ref (grip->_priv->tooltips); + gtk_object_sink (GTK_OBJECT (grip->_priv->tooltips)); + gtk_tooltips_set_tip (grip->_priv->tooltips, grip->_priv->iconify_button, + _("Iconify"), _("Iconify this dock")); + gtk_tooltips_set_tip (grip->_priv->tooltips, grip->_priv->close_button, + _("Close"), _("Close this dock")); +} + +static void +gdl_dock_item_grip_realize (GtkWidget *widget) +{ + GdlDockItemGrip *grip = GDL_DOCK_ITEM_GRIP (widget); + + GTK_WIDGET_CLASS (parent_class)->realize (widget); + + if (!grip->title_window) { + GdkWindowAttr attributes; + GdkRectangle area; + GdkCursor *cursor; + + ensure_title_and_icon_pixbuf (grip); + gdl_dock_item_grip_get_title_area (grip, &area); + + attributes.x = area.x; + attributes.y = area.y; + attributes.width = area.width; + attributes.height = area.height; + attributes.window_type = GDK_WINDOW_TEMP; + attributes.wclass = GDK_INPUT_ONLY; + attributes.override_redirect = TRUE; + attributes.event_mask = (GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_BUTTON_MOTION_MASK | + gtk_widget_get_events (widget)); + + grip->title_window = gdk_window_new (gtk_widget_get_parent_window (widget), + &attributes, + (GDK_WA_X | + GDK_WA_Y | + GDK_WA_NOREDIR)); + + gdk_window_set_user_data (grip->title_window, widget); + + if (GDL_DOCK_ITEM_CANT_CLOSE (grip->item)) + cursor = NULL; + else if (GDL_DOCK_ITEM_CANT_ICONIFY (grip->item)) + cursor = NULL; + else + cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), + GDK_HAND2); + gdk_window_set_cursor (grip->title_window, cursor); + if (cursor) + gdk_cursor_unref (cursor); + } +} + +static void +gdl_dock_item_grip_unrealize (GtkWidget *widget) +{ + GdlDockItemGrip *grip = GDL_DOCK_ITEM_GRIP (widget); + + if (grip->title_window) { + gdk_window_set_user_data (grip->title_window, NULL); + gdk_window_destroy (grip->title_window); + grip->title_window = NULL; + } + + GTK_WIDGET_CLASS (parent_class)->unrealize (widget); +} + +static void +gdl_dock_item_grip_map (GtkWidget *widget) +{ + GdlDockItemGrip *grip = GDL_DOCK_ITEM_GRIP (widget); + + GTK_WIDGET_CLASS (parent_class)->map (widget); + + if (grip->title_window) + gdk_window_show (grip->title_window); +} + +static void +gdl_dock_item_grip_unmap (GtkWidget *widget) +{ + GdlDockItemGrip *grip = GDL_DOCK_ITEM_GRIP (widget); + + if (grip->title_window) + gdk_window_hide (grip->title_window); + + GTK_WIDGET_CLASS (parent_class)->unmap (widget); +} + +static void +gdl_dock_item_grip_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + GtkRequisition child_requisition; + GtkContainer *container; + GdlDockItemGrip *grip; + gint layout_height; + + g_return_if_fail (GDL_IS_DOCK_ITEM_GRIP (widget)); + g_return_if_fail (requisition != NULL); + + container = GTK_CONTAINER (widget); + grip = GDL_DOCK_ITEM_GRIP (widget); + + requisition->width = container->border_width * 2 + ALIGN_BORDER; + requisition->height = container->border_width * 2; + + ensure_title_and_icon_pixbuf (grip); + pango_layout_get_pixel_size (grip->_priv->title_layout, NULL, &layout_height); + + gtk_widget_size_request (grip->_priv->close_button, &child_requisition); + + requisition->width += child_requisition.width; + layout_height = MAX (layout_height, child_requisition.height); + + gtk_widget_size_request (grip->_priv->iconify_button, &child_requisition); + + requisition->width += child_requisition.width; + layout_height = MAX (layout_height, child_requisition.height); + + requisition->height += layout_height; + + if (grip->_priv->icon_pixbuf) { + requisition->width += gdk_pixbuf_get_width (grip->_priv->icon_pixbuf) + 1; + } +} + +#define ELLIPSIS "..." + +static void +ellipsize_layout (PangoLayout *layout, gint width) +{ + PangoLayoutLine *line; + PangoLayout *ell; + gint h, w, ell_w, x; + GString *text; + + if (width <= 0) { + pango_layout_set_text (layout, "", -1); + return; + } + + pango_layout_get_pixel_size (layout, &w, &h); + if (w <= width) return; + + /* calculate ellipsis width */ + ell = pango_layout_copy (layout); + pango_layout_set_text (ell, ELLIPSIS, -1); + pango_layout_get_pixel_size (ell, &ell_w, NULL); + g_object_unref (ell); + + if (width < ell_w) { + /* not even ellipsis fits, so hide the text */ + pango_layout_set_text (layout, "", -1); + return; + } + + /* shrink total available width by the width of the ellipsis */ + width -= ell_w; + line = pango_layout_get_line (layout, 0); + text = g_string_new (pango_layout_get_text (layout)); + if (pango_layout_line_x_to_index (line, width * PANGO_SCALE, &x, NULL)) { + g_string_set_size (text, x); + g_string_append (text, ELLIPSIS); + pango_layout_set_text (layout, text->str, -1); + } + g_string_free (text, TRUE); +} + +static void +gdl_dock_item_grip_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GdlDockItemGrip *grip; + GtkContainer *container; + GtkRequisition button_requisition = { 0, }; + GtkAllocation child_allocation; + + g_return_if_fail (GDL_IS_DOCK_ITEM_GRIP (widget)); + g_return_if_fail (allocation != NULL); + + grip = GDL_DOCK_ITEM_GRIP (widget); + container = GTK_CONTAINER (widget); + + GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation); + + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) + child_allocation.x = allocation->x + container->border_width + ALIGN_BORDER; + else + child_allocation.x = allocation->x + allocation->width - container->border_width; + child_allocation.y = allocation->y + container->border_width; + + gtk_widget_size_request (grip->_priv->close_button, &button_requisition); + + if (gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL) + child_allocation.x -= button_requisition.width; + + child_allocation.width = button_requisition.width; + child_allocation.height = button_requisition.height; + + gtk_widget_size_allocate (grip->_priv->close_button, &child_allocation); + + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) + child_allocation.x += button_requisition.width; + + + gtk_widget_size_request (grip->_priv->iconify_button, &button_requisition); + + if (gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL) + child_allocation.x -= button_requisition.width; + + child_allocation.width = button_requisition.width; + child_allocation.height = button_requisition.height; + + gtk_widget_size_allocate (grip->_priv->iconify_button, &child_allocation); + + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) + child_allocation.x += button_requisition.width; + + + if (grip->title_window) { + GdkRectangle area; + + /* set layout text */ + ensure_title_and_icon_pixbuf (grip); + pango_layout_set_text (grip->_priv->title_layout, grip->_priv->title, -1); + + gdl_dock_item_grip_get_title_area (grip, &area); + + gdk_window_move_resize (grip->title_window, + area.x, area.y, area.width, area.height); + + if (grip->_priv->icon_pixbuf) + area.width -= gdk_pixbuf_get_width (grip->_priv->icon_pixbuf) + 1; + + /* ellipsize title if it doesn't fit the title area */ + ellipsize_layout (grip->_priv->title_layout, area.width); + } +} + +static void +gdl_dock_item_grip_add (GtkContainer *container, + GtkWidget *widget) +{ + g_warning ("gtk_container_add not implemented for GdlDockItemGrip"); +} + +static void +gdl_dock_item_grip_remove (GtkContainer *container, + GtkWidget *widget) +{ + g_warning ("gtk_container_remove not implemented for GdlDockItemGrip"); +} + +static void +gdl_dock_item_grip_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) +{ + GdlDockItemGrip *grip; + + g_return_if_fail (GDL_IS_DOCK_ITEM_GRIP (container)); + + grip = GDL_DOCK_ITEM_GRIP (container); + + if (include_internals) { + (* callback) (grip->_priv->close_button, callback_data); + (* callback) (grip->_priv->iconify_button, callback_data); + } +} + +static GtkType +gdl_dock_item_grip_child_type (GtkContainer *container) +{ + return G_TYPE_NONE; +} + +static void +gdl_dock_item_grip_class_init (GdlDockItemGripClass *klass) +{ + GObjectClass *gobject_class; + GtkObjectClass *gtk_object_class; + GtkWidgetClass *widget_class; + GtkContainerClass *container_class; + + parent_class = g_type_class_peek_parent (klass); + gobject_class = G_OBJECT_CLASS (klass); + gtk_object_class = GTK_OBJECT_CLASS (klass); + widget_class = GTK_WIDGET_CLASS (klass); + container_class = GTK_CONTAINER_CLASS (klass); + + gobject_class->set_property = gdl_dock_item_grip_set_property; + + gtk_object_class->destroy = gdl_dock_item_grip_destroy; + + widget_class->expose_event = gdl_dock_item_grip_expose; + widget_class->realize = gdl_dock_item_grip_realize; + widget_class->unrealize = gdl_dock_item_grip_unrealize; + widget_class->map = gdl_dock_item_grip_map; + widget_class->unmap = gdl_dock_item_grip_unmap; + widget_class->size_request = gdl_dock_item_grip_size_request; + widget_class->size_allocate = gdl_dock_item_grip_size_allocate; + + container_class->add = gdl_dock_item_grip_add; + container_class->remove = gdl_dock_item_grip_remove; + container_class->forall = gdl_dock_item_grip_forall; + container_class->child_type = gdl_dock_item_grip_child_type; + + g_object_class_install_property ( + gobject_class, PROP_ITEM, + g_param_spec_object ("item", _("Controlling dock item"), + _("Dockitem which 'owns' this grip"), + GDL_TYPE_DOCK_ITEM, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); + + /* initialize stock images */ + gdl_stock_init (); +} + +GtkWidget * +gdl_dock_item_grip_new (GdlDockItem *item) +{ + GdlDockItemGrip *grip = g_object_new (GDL_TYPE_DOCK_ITEM_GRIP, "item", item, + NULL); + + return GTK_WIDGET (grip); +} diff --git a/src/libgdl/gdl-dock-item-grip.h b/src/libgdl/gdl-dock-item-grip.h new file mode 100644 index 000000000..377ea1470 --- /dev/null +++ b/src/libgdl/gdl-dock-item-grip.h @@ -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 +#include "libgdl/gdl-dock-item.h" + +G_BEGIN_DECLS + +#define GDL_TYPE_DOCK_ITEM_GRIP (gdl_dock_item_grip_get_type()) +#define GDL_DOCK_ITEM_GRIP(obj) \ + (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_ITEM_GRIP, GdlDockItemGrip)) +#define GDL_DOCK_ITEM_GRIP_CLASS(klass) \ + (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_ITEM_GRIP, GdlDockItemGripClass)) +#define GDL_IS_DOCK_ITEM_GRIP(obj) \ + (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_ITEM_GRIP)) +#define GDL_IS_DOCK_ITEM_GRIP_CLASS(klass) \ + (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_ITEM_GRIP)) +#define GDL_DOCK_ITEM_GRIP_GET_CLASS(obj) \ + (GTK_CHECK_GET_CLASS ((obj), GDL_TYPE_DOCK_ITEM_GRIP, GdlDockItemGripClass)) + +typedef struct _GdlDockItemGrip GdlDockItemGrip; +typedef struct _GdlDockItemGripClass GdlDockItemGripClass; +typedef struct _GdlDockItemGripPrivate GdlDockItemGripPrivate; + +struct _GdlDockItemGrip { + GtkContainer parent; + + GdlDockItem *item; + + GdkWindow *title_window; + + GdlDockItemGripPrivate *_priv; +}; + +struct _GdlDockItemGripClass { + GtkContainerClass parent_class; +}; + +GType gdl_dock_item_grip_get_type (void); +GtkWidget *gdl_dock_item_grip_new (GdlDockItem *item); + +G_END_DECLS + +#endif /* _GDL_DOCK_ITEM_GRIP_H_ */ diff --git a/src/libgdl/gdl-dock-item.c b/src/libgdl/gdl-dock-item.c new file mode 100644 index 000000000..d9f805ffe --- /dev/null +++ b/src/libgdl/gdl-dock-item.c @@ -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 + * Naba Kumar + * + * 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 +#endif + +#include "gdl-i18n.h" +#include +#include + +#include "gdl-tools.h" +#include "gdl-dock.h" +#include "gdl-dock-item.h" +#include "gdl-dock-item-grip.h" +#include "gdl-dock-notebook.h" +#include "gdl-dock-paned.h" +#include "gdl-dock-tablabel.h" +#include "gdl-dock-placeholder.h" +#include "gdl-dock-master.h" +#include "libgdltypebuiltins.h" +#include "libgdlmarshal.h" + +#define NEW_DOCK_ITEM_RATIO 0.3 + +/* ----- Private prototypes ----- */ + +static void gdl_dock_item_class_init (GdlDockItemClass *class); +static void gdl_dock_item_instance_init (GdlDockItem *item); + +static GObject *gdl_dock_item_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_param); + +static void gdl_dock_item_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gdl_dock_item_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static void gdl_dock_item_destroy (GtkObject *object); + +static void gdl_dock_item_add (GtkContainer *container, + GtkWidget *widget); +static void gdl_dock_item_remove (GtkContainer *container, + GtkWidget *widget); +static void gdl_dock_item_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data); +static GtkType gdl_dock_item_child_type (GtkContainer *container); + +static void gdl_dock_item_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void gdl_dock_item_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); +static void gdl_dock_item_map (GtkWidget *widget); +static void gdl_dock_item_unmap (GtkWidget *widget); +static void gdl_dock_item_realize (GtkWidget *widget); +static void gdl_dock_item_style_set (GtkWidget *widget, + GtkStyle *previous_style); +static gint gdl_dock_item_expose (GtkWidget *widget, + GdkEventExpose *event); + +static gint gdl_dock_item_button_changed (GtkWidget *widget, + GdkEventButton *event); +static gint gdl_dock_item_motion (GtkWidget *widget, + GdkEventMotion *event); +static gboolean gdl_dock_item_key_press (GtkWidget *widget, + GdkEventKey *event); + +static gboolean gdl_dock_item_dock_request (GdlDockObject *object, + gint x, + gint y, + GdlDockRequest *request); +static void gdl_dock_item_dock (GdlDockObject *object, + GdlDockObject *requestor, + GdlDockPlacement position, + GValue *other_data); + +static void gdl_dock_item_popup_menu (GdlDockItem *item, + guint button, + guint32 time); +static void gdl_dock_item_drag_start (GdlDockItem *item); +static void gdl_dock_item_drag_end (GdlDockItem *item, + gboolean cancel); + +static void gdl_dock_item_tab_button (GtkWidget *widget, + GdkEventButton *event, + gpointer data); + +static void gdl_dock_item_hide_cb (GtkWidget *widget, + GdlDockItem *item); + +static void gdl_dock_item_lock_cb (GtkWidget *widget, + GdlDockItem *item); + +static void gdl_dock_item_unlock_cb (GtkWidget *widget, + GdlDockItem *item); + +static void gdl_dock_item_showhide_grip (GdlDockItem *item); + +static void gdl_dock_item_real_set_orientation (GdlDockItem *item, + GtkOrientation orientation); + +static void gdl_dock_param_export_gtk_orientation (const GValue *src, + GValue *dst); +static void gdl_dock_param_import_gtk_orientation (const GValue *src, + GValue *dst); + + + +/* ----- Class variables and definitions ----- */ + +enum { + PROP_0, + PROP_ORIENTATION, + PROP_RESIZE, + PROP_BEHAVIOR, + PROP_LOCKED, + PROP_PREFERRED_WIDTH, + PROP_PREFERRED_HEIGHT +}; + +enum { + DOCK_DRAG_BEGIN, + DOCK_DRAG_MOTION, + DOCK_DRAG_END, + LAST_SIGNAL +}; + +static guint gdl_dock_item_signals [LAST_SIGNAL] = { 0 }; + +#define GDL_DOCK_ITEM_GRIP_SHOWN(item) \ + (GDL_DOCK_ITEM_HAS_GRIP (item)) + +struct _GdlDockItemPrivate { + GtkWidget *menu; + + gboolean grip_shown; + GtkWidget *grip; + guint grip_size; + + GtkWidget *tab_label; + + gint preferred_width; + gint preferred_height; + + GdlDockPlaceholder *ph; + + gint start_x, start_y; +}; + +/* FIXME: implement the rest of the behaviors */ + +#define SPLIT_RATIO 0.4 + + +/* ----- Private functions ----- */ + +GDL_CLASS_BOILERPLATE (GdlDockItem, gdl_dock_item, GdlDockObject, GDL_TYPE_DOCK_OBJECT); + +static void +gdl_dock_item_class_init (GdlDockItemClass *klass) +{ + static gboolean style_initialized = FALSE; + + GObjectClass *g_object_class; + GtkObjectClass *gtk_object_class; + GtkWidgetClass *widget_class; + GtkContainerClass *container_class; + GdlDockObjectClass *object_class; + + g_object_class = G_OBJECT_CLASS (klass); + gtk_object_class = GTK_OBJECT_CLASS (klass); + widget_class = GTK_WIDGET_CLASS (klass); + container_class = GTK_CONTAINER_CLASS (klass); + object_class = GDL_DOCK_OBJECT_CLASS (klass); + + g_object_class->constructor = gdl_dock_item_constructor; + g_object_class->set_property = gdl_dock_item_set_property; + g_object_class->get_property = gdl_dock_item_get_property; + + gtk_object_class->destroy = gdl_dock_item_destroy; + + widget_class->realize = gdl_dock_item_realize; + widget_class->map = gdl_dock_item_map; + widget_class->unmap = gdl_dock_item_unmap; + widget_class->size_request = gdl_dock_item_size_request; + widget_class->size_allocate = gdl_dock_item_size_allocate; + widget_class->style_set = gdl_dock_item_style_set; + widget_class->expose_event = gdl_dock_item_expose; + widget_class->button_press_event = gdl_dock_item_button_changed; + widget_class->button_release_event = gdl_dock_item_button_changed; + widget_class->motion_notify_event = gdl_dock_item_motion; + widget_class->key_press_event = gdl_dock_item_key_press; + + container_class->add = gdl_dock_item_add; + container_class->remove = gdl_dock_item_remove; + container_class->forall = gdl_dock_item_forall; + container_class->child_type = gdl_dock_item_child_type; + + object_class->is_compound = FALSE; + + object_class->dock_request = gdl_dock_item_dock_request; + object_class->dock = gdl_dock_item_dock; + + /* properties */ + + g_object_class_install_property ( + g_object_class, PROP_ORIENTATION, + g_param_spec_enum ("orientation", _("Orientation"), + _("Orientation of the docking item"), + GTK_TYPE_ORIENTATION, + GTK_ORIENTATION_VERTICAL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | + GDL_DOCK_PARAM_EXPORT)); + + /* --- register exporter/importer for GTK_ORIENTATION */ + g_value_register_transform_func (GTK_TYPE_ORIENTATION, GDL_TYPE_DOCK_PARAM, + gdl_dock_param_export_gtk_orientation); + g_value_register_transform_func (GDL_TYPE_DOCK_PARAM, GTK_TYPE_ORIENTATION, + gdl_dock_param_import_gtk_orientation); + /* --- end of registration */ + + g_object_class_install_property ( + g_object_class, PROP_RESIZE, + g_param_spec_boolean ("resize", _("Resizable"), + _("If set, the dock item can be resized when " + "docked in a paned"), + TRUE, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + g_object_class, PROP_BEHAVIOR, + g_param_spec_flags ("behavior", _("Item behavior"), + _("General behavior for the dock item (i.e. " + "whether it can float, if it's locked, etc.)"), + GDL_TYPE_DOCK_ITEM_BEHAVIOR, + GDL_DOCK_ITEM_BEH_NORMAL, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + g_object_class, PROP_LOCKED, + g_param_spec_boolean ("locked", _("Locked"), + _("If set, the dock item cannot be dragged around " + "and it doesn't show a grip"), + FALSE, + G_PARAM_READWRITE | + GDL_DOCK_PARAM_EXPORT)); + + g_object_class_install_property ( + g_object_class, PROP_PREFERRED_WIDTH, + g_param_spec_int ("preferred-width", _("Preferred width"), + _("Preferred width for the dock item"), + -1, G_MAXINT, -1, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + g_object_class, PROP_PREFERRED_HEIGHT, + g_param_spec_int ("preferred-height", _("Preferred height"), + _("Preferred height for the dock item"), + -1, G_MAXINT, -1, + G_PARAM_READWRITE)); + + /* signals */ + + gdl_dock_item_signals [DOCK_DRAG_BEGIN] = + g_signal_new ("dock-drag-begin", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GdlDockItemClass, dock_drag_begin), + NULL, /* accumulator */ + NULL, /* accu_data */ + gdl_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + gdl_dock_item_signals [DOCK_DRAG_MOTION] = + g_signal_new ("dock-drag-motion", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GdlDockItemClass, dock_drag_motion), + NULL, /* accumulator */ + NULL, /* accu_data */ + gdl_marshal_VOID__INT_INT, + G_TYPE_NONE, + 2, + G_TYPE_INT, + G_TYPE_INT); + + gdl_dock_item_signals [DOCK_DRAG_END] = + g_signal_new ("dock_drag_end", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GdlDockItemClass, dock_drag_end), + NULL, /* accumulator */ + NULL, /* accu_data */ + gdl_marshal_VOID__BOOLEAN, + G_TYPE_NONE, + 1, + G_TYPE_BOOLEAN); + + klass->has_grip = TRUE; + klass->dock_drag_begin = NULL; + klass->dock_drag_motion = NULL; + klass->dock_drag_end = NULL; + klass->set_orientation = gdl_dock_item_real_set_orientation; + + if (!style_initialized) + { + style_initialized = TRUE; + gtk_rc_parse_string ( + "style \"gdl-dock-item-default\" {\n" + "xthickness = 0\n" + "ythickness = 0\n" + "}\n" + "class \"GdlDockItem\" " + "style : gtk \"gdl-dock-item-default\"\n"); + } +} + +static void +gdl_dock_item_instance_init (GdlDockItem *item) +{ + GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (item), GTK_NO_WINDOW); + + item->child = NULL; + + item->orientation = GTK_ORIENTATION_VERTICAL; + item->behavior = GDL_DOCK_ITEM_BEH_NORMAL; + + item->resize = TRUE; + + item->dragoff_x = item->dragoff_y = 0; + + item->_priv = g_new0 (GdlDockItemPrivate, 1); + item->_priv->menu = NULL; + + item->_priv->preferred_width = item->_priv->preferred_height = -1; + item->_priv->tab_label = NULL; + + item->_priv->ph = NULL; +} + +static GObject * +gdl_dock_item_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_param) +{ + GObject *g_object; + + g_object = GDL_CALL_PARENT_WITH_DEFAULT (G_OBJECT_CLASS, + constructor, + (type, + n_construct_properties, + construct_param), + NULL); + if (g_object) { + GdlDockItem *item = GDL_DOCK_ITEM (g_object); + + if (GDL_DOCK_ITEM_HAS_GRIP (item)) { + item->_priv->grip_shown = TRUE; + item->_priv->grip = gdl_dock_item_grip_new (item); + gtk_widget_set_parent (item->_priv->grip, GTK_WIDGET (item)); + gtk_widget_show (item->_priv->grip); + } + else { + item->_priv->grip_shown = FALSE; + } + } + + return g_object; +} + +static void +gdl_dock_item_set_property (GObject *g_object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdlDockItem *item = GDL_DOCK_ITEM (g_object); + + switch (prop_id) { + case PROP_ORIENTATION: + gdl_dock_item_set_orientation (item, g_value_get_enum (value)); + break; + case PROP_RESIZE: + item->resize = g_value_get_boolean (value); + gtk_widget_queue_resize (GTK_WIDGET (item)); + break; + case PROP_BEHAVIOR: + { + GdlDockItemBehavior old_beh = item->behavior; + item->behavior = g_value_get_flags (value); + + if ((old_beh ^ item->behavior) & GDL_DOCK_ITEM_BEH_LOCKED) { + if (GDL_DOCK_OBJECT_GET_MASTER (item)) + g_signal_emit_by_name (GDL_DOCK_OBJECT_GET_MASTER (item), + "layout-changed"); + g_object_notify (g_object, "locked"); + gdl_dock_item_showhide_grip (item); + } + + break; + } + case PROP_LOCKED: + { + GdlDockItemBehavior old_beh = item->behavior; + + if (g_value_get_boolean (value)) + item->behavior |= GDL_DOCK_ITEM_BEH_LOCKED; + else + item->behavior &= ~GDL_DOCK_ITEM_BEH_LOCKED; + + if (old_beh ^ item->behavior) { + gdl_dock_item_showhide_grip (item); + g_object_notify (g_object, "behavior"); + + if (GDL_DOCK_OBJECT_GET_MASTER (item)) + g_signal_emit_by_name (GDL_DOCK_OBJECT_GET_MASTER (item), + "layout-changed"); + } + break; + } + case PROP_PREFERRED_WIDTH: + item->_priv->preferred_width = g_value_get_int (value); + break; + case PROP_PREFERRED_HEIGHT: + item->_priv->preferred_height = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (g_object, prop_id, pspec); + break; + } +} + +static void +gdl_dock_item_get_property (GObject *g_object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdlDockItem *item = GDL_DOCK_ITEM (g_object); + + switch (prop_id) { + case PROP_ORIENTATION: + g_value_set_enum (value, item->orientation); + break; + case PROP_RESIZE: + g_value_set_boolean (value, item->resize); + break; + case PROP_BEHAVIOR: + g_value_set_flags (value, item->behavior); + break; + case PROP_LOCKED: + g_value_set_boolean (value, !GDL_DOCK_ITEM_NOT_LOCKED (item)); + break; + case PROP_PREFERRED_WIDTH: + g_value_set_int (value, item->_priv->preferred_width); + break; + case PROP_PREFERRED_HEIGHT: + g_value_set_int (value, item->_priv->preferred_height); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (g_object, prop_id, pspec); + break; + } +} + +static void +gdl_dock_item_destroy (GtkObject *object) +{ + GdlDockItem *item = GDL_DOCK_ITEM (object); + + if (item->_priv) { + GdlDockItemPrivate *priv = item->_priv; + + if (priv->tab_label) { + gdl_dock_item_set_tablabel (item, NULL); + }; + if (priv->menu) { + gtk_menu_detach (GTK_MENU (priv->menu)); + priv->menu = NULL; + }; + if (priv->grip) { + gtk_container_remove (GTK_CONTAINER (item), priv->grip); + priv->grip = NULL; + } + if (priv->ph) { + g_object_unref (priv->ph); + priv->ph = NULL; + } + + item->_priv = NULL; + g_free (priv); + } + + GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object)); +} + +static void +gdl_dock_item_add (GtkContainer *container, + GtkWidget *widget) +{ + GdlDockItem *item; + + g_return_if_fail (GDL_IS_DOCK_ITEM (container)); + + item = GDL_DOCK_ITEM (container); + if (GDL_IS_DOCK_OBJECT (widget)) { + g_warning (_("You can't add a dock object (%p of type %s) inside a %s. " + "Use a GdlDock or some other compound dock object."), + widget, G_OBJECT_TYPE_NAME (widget), G_OBJECT_TYPE_NAME (item)); + return; + } + + if (item->child != NULL) { + g_warning (_("Attempting to add a widget with type %s to a %s, " + "but it can only contain one widget at a time; " + "it already contains a widget of type %s"), + G_OBJECT_TYPE_NAME (widget), + G_OBJECT_TYPE_NAME (item), + G_OBJECT_TYPE_NAME (item->child)); + return; + } + + gtk_widget_set_parent (widget, GTK_WIDGET (item)); + item->child = widget; +} + +static void +gdl_dock_item_remove (GtkContainer *container, + GtkWidget *widget) +{ + GdlDockItem *item; + gboolean was_visible; + + g_return_if_fail (GDL_IS_DOCK_ITEM (container)); + + item = GDL_DOCK_ITEM (container); + if (item->_priv && widget == item->_priv->grip) { + gboolean grip_was_visible = GTK_WIDGET_VISIBLE (widget); + gtk_widget_unparent (widget); + item->_priv->grip = NULL; + if (grip_was_visible) + gtk_widget_queue_resize (GTK_WIDGET (item)); + return; + } + + if (GDL_DOCK_ITEM_IN_DRAG (item)) { + gdl_dock_item_drag_end (item, TRUE); + } + + g_return_if_fail (item->child == widget); + + was_visible = GTK_WIDGET_VISIBLE (widget); + + gtk_widget_unparent (widget); + item->child = NULL; + + if (was_visible) + gtk_widget_queue_resize (GTK_WIDGET (container)); +} + +static void +gdl_dock_item_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) +{ + GdlDockItem *item = (GdlDockItem *) container; + + g_return_if_fail (callback != NULL); + + if (include_internals && item->_priv->grip) + (* callback) (item->_priv->grip, callback_data); + + if (item->child) + (* callback) (item->child, callback_data); +} + +static GtkType +gdl_dock_item_child_type (GtkContainer *container) +{ + g_return_val_if_fail (GDL_IS_DOCK_ITEM (container), G_TYPE_NONE); + + if (!GDL_DOCK_ITEM (container)->child) + return GTK_TYPE_WIDGET; + else + return G_TYPE_NONE; +} + +static void +gdl_dock_item_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + GtkRequisition child_requisition; + GtkRequisition grip_requisition; + GdlDockItem *item; + + g_return_if_fail (GDL_IS_DOCK_ITEM (widget)); + g_return_if_fail (requisition != NULL); + + item = GDL_DOCK_ITEM (widget); + + /* If our child is not visible, we still request its size, since + we won't have any useful hint for our size otherwise. */ + if (item->child) + gtk_widget_size_request (item->child, &child_requisition); + else { + child_requisition.width = 0; + child_requisition.height = 0; + } + + if (item->orientation == GTK_ORIENTATION_HORIZONTAL) { + if (GDL_DOCK_ITEM_GRIP_SHOWN (item)) { + gtk_widget_size_request (item->_priv->grip, &grip_requisition); + requisition->width = grip_requisition.width; + } else { + requisition->width = 0; + } + + if (item->child) { + requisition->width += child_requisition.width; + requisition->height = child_requisition.height; + } else + requisition->height = 0; + } else { + if (GDL_DOCK_ITEM_GRIP_SHOWN (item)) { + gtk_widget_size_request (item->_priv->grip, &grip_requisition); + requisition->height = grip_requisition.height; + } else { + requisition->height = 0; + } + + if (item->child) { + requisition->width = child_requisition.width; + requisition->height += child_requisition.height; + } else + requisition->width = 0; + } + + requisition->width += (GTK_CONTAINER (widget)->border_width + widget->style->xthickness) * 2; + requisition->height += (GTK_CONTAINER (widget)->border_width + widget->style->ythickness) * 2; + + widget->requisition = *requisition; +} + +static void +gdl_dock_item_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GdlDockItem *item; + + g_return_if_fail (GDL_IS_DOCK_ITEM (widget)); + g_return_if_fail (allocation != NULL); + + item = GDL_DOCK_ITEM (widget); + + widget->allocation = *allocation; + + /* Once size is allocated, preferred size is no longer necessary */ + item->_priv->preferred_height = -1; + item->_priv->preferred_width = -1; + + if (GTK_WIDGET_REALIZED (widget)) + gdk_window_move_resize (widget->window, + widget->allocation.x, + widget->allocation.y, + widget->allocation.width, + widget->allocation.height); + + if (item->child && GTK_WIDGET_VISIBLE (item->child)) { + GtkAllocation child_allocation; + int border_width; + + border_width = GTK_CONTAINER (widget)->border_width; + + child_allocation.x = border_width + widget->style->xthickness; + child_allocation.y = border_width + widget->style->ythickness; + child_allocation.width = allocation->width + - 2 * (border_width + widget->style->xthickness); + child_allocation.height = allocation->height + - 2 * (border_width + widget->style->ythickness); + + if (GDL_DOCK_ITEM_GRIP_SHOWN (item)) { + GtkAllocation grip_alloc = child_allocation; + GtkRequisition grip_req; + + gtk_widget_size_request (item->_priv->grip, &grip_req); + + if (item->orientation == GTK_ORIENTATION_HORIZONTAL) { + child_allocation.x += grip_req.width; + child_allocation.width -= grip_req.width; + grip_alloc.width = grip_req.width; + } else { + child_allocation.y += grip_req.height; + child_allocation.height -= grip_req.height; + grip_alloc.height = grip_req.height; + } + if (item->_priv->grip) + gtk_widget_size_allocate (item->_priv->grip, &grip_alloc); + } + /* Allocation can't be negative */ + if (child_allocation.width < 0) + child_allocation.width = 0; + if (child_allocation.height < 0) + child_allocation.height = 0; + gtk_widget_size_allocate (item->child, &child_allocation); + } +} + +static void +gdl_dock_item_map (GtkWidget *widget) +{ + GdlDockItem *item; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GDL_IS_DOCK_ITEM (widget)); + + GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED); + + item = GDL_DOCK_ITEM (widget); + + gdk_window_show (widget->window); + + if (item->child + && GTK_WIDGET_VISIBLE (item->child) + && !GTK_WIDGET_MAPPED (item->child)) + gtk_widget_map (item->child); + + if (item->_priv->grip + && GTK_WIDGET_VISIBLE (item->_priv->grip) + && !GTK_WIDGET_MAPPED (item->_priv->grip)) + gtk_widget_map (item->_priv->grip); +} + +static void +gdl_dock_item_unmap (GtkWidget *widget) +{ + GdlDockItem *item; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GDL_IS_DOCK_ITEM (widget)); + + GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED); + + item = GDL_DOCK_ITEM (widget); + + gdk_window_hide (widget->window); + + if (item->_priv->grip) + gtk_widget_unmap (item->_priv->grip); +} + +static void +gdl_dock_item_realize (GtkWidget *widget) +{ + GdkWindowAttr attributes; + gint attributes_mask; + GdlDockItem *item; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GDL_IS_DOCK_ITEM (widget)); + + item = GDL_DOCK_ITEM (widget); + + GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); + + /* widget window */ + attributes.x = widget->allocation.x; + attributes.y = widget->allocation.y; + attributes.width = widget->allocation.width; + attributes.height = widget->allocation.height; + attributes.window_type = GDK_WINDOW_CHILD; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = gtk_widget_get_visual (widget); + attributes.colormap = gtk_widget_get_colormap (widget); + attributes.event_mask = (gtk_widget_get_events (widget) | + GDK_EXPOSURE_MASK | + GDK_BUTTON1_MOTION_MASK | + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK); + attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; + widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), + &attributes, attributes_mask); + gdk_window_set_user_data (widget->window, widget); + + widget->style = gtk_style_attach (widget->style, widget->window); + gtk_style_set_background (widget->style, widget->window, + GTK_WIDGET_STATE (item)); + gdk_window_set_back_pixmap (widget->window, NULL, TRUE); + + if (item->child) + gtk_widget_set_parent_window (item->child, widget->window); + + if (item->_priv->grip) + gtk_widget_set_parent_window (item->_priv->grip, widget->window); +} + +static void +gdl_dock_item_style_set (GtkWidget *widget, + GtkStyle *previous_style) +{ + g_return_if_fail (widget != NULL); + g_return_if_fail (GDL_IS_DOCK_ITEM (widget)); + + if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_NO_WINDOW (widget)) { + gtk_style_set_background (widget->style, widget->window, + widget->state); + if (GTK_WIDGET_DRAWABLE (widget)) + gdk_window_clear (widget->window); + } +} + +static void +gdl_dock_item_paint (GtkWidget *widget, + GdkEventExpose *event) +{ + GdlDockItem *item; + + item = GDL_DOCK_ITEM (widget); + + gtk_paint_box (widget->style, + widget->window, + GTK_WIDGET_STATE (widget), + GTK_SHADOW_NONE, + &event->area, widget, + "dockitem", + 0, 0, -1, -1); +} + +static gint +gdl_dock_item_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GDL_IS_DOCK_ITEM (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + if (GTK_WIDGET_DRAWABLE (widget) && event->window == widget->window) { + gdl_dock_item_paint (widget, event); + GDL_CALL_PARENT_GBOOLEAN(GTK_WIDGET_CLASS, expose_event, (widget,event)); + } + + return FALSE; +} + +#define EVENT_IN_GRIP_EVENT_WINDOW(ev,gr) \ + ((gr) != NULL && (ev)->window == GDL_DOCK_ITEM_GRIP (gr)->title_window) + +#define EVENT_IN_TABLABEL_EVENT_WINDOW(ev,tl) \ + ((tl) != NULL && (ev)->window == GDL_DOCK_TABLABEL (tl)->event_window) + +static gint +gdl_dock_item_button_changed (GtkWidget *widget, + GdkEventButton *event) +{ + GdlDockItem *item; + gboolean locked; + gboolean event_handled; + gboolean in_handle; + GdkCursor *cursor; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GDL_IS_DOCK_ITEM (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + item = GDL_DOCK_ITEM (widget); + + if (!EVENT_IN_GRIP_EVENT_WINDOW (event, item->_priv->grip)) + return FALSE; + + locked = !GDL_DOCK_ITEM_NOT_LOCKED (item); + + event_handled = FALSE; + + /* Check if user clicked on the drag handle. */ + switch (item->orientation) { + case GTK_ORIENTATION_HORIZONTAL: + in_handle = event->x < item->_priv->grip->allocation.width; + break; + case GTK_ORIENTATION_VERTICAL: + in_handle = event->y < item->_priv->grip->allocation.height; + break; + default: + in_handle = FALSE; + break; + } + + /* Left mousebutton click on dockitem. */ + if (!locked && event->button == 1 && event->type == GDK_BUTTON_PRESS) { + /* Set in_drag flag, grab pointer and call begin drag operation. */ + if (in_handle) { + item->_priv->start_x = event->x; + item->_priv->start_y = event->y; + + GDL_DOCK_ITEM_SET_FLAGS (item, GDL_DOCK_IN_PREDRAG); + + cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), + GDK_FLEUR); + gdk_window_set_cursor (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window, + cursor); + gdk_cursor_unref (cursor); + + event_handled = TRUE; + }; + + } else if (!locked &&event->type == GDK_BUTTON_RELEASE && event->button == 1) { + if (GDL_DOCK_ITEM_IN_DRAG (item)) { + /* User dropped widget somewhere. */ + gdl_dock_item_drag_end (item, FALSE); + event_handled = TRUE; + } + else if (GDL_DOCK_ITEM_IN_PREDRAG (item)) { + GDL_DOCK_ITEM_UNSET_FLAGS (item, GDL_DOCK_IN_PREDRAG); + event_handled = TRUE; + } + + /* we check the window since if the item was redocked it's + been unrealized and maybe it's not realized again yet */ + if (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window) { + cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), + GDK_HAND2); + gdk_window_set_cursor (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window, + cursor); + gdk_cursor_unref (cursor); + } + + } else if (event->button == 3 && event->type == GDK_BUTTON_PRESS && in_handle) { + gdl_dock_item_popup_menu (item, event->button, event->time); + event_handled = TRUE; + } + + return event_handled; +} + +static gint +gdl_dock_item_motion (GtkWidget *widget, + GdkEventMotion *event) +{ + GdlDockItem *item; + gint new_x, new_y; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GDL_IS_DOCK_ITEM (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + item = GDL_DOCK_ITEM (widget); + + if (!EVENT_IN_GRIP_EVENT_WINDOW (event, item->_priv->grip)) + return FALSE; + + if (GDL_DOCK_ITEM_IN_PREDRAG (item)) { + if (gtk_drag_check_threshold (widget, + item->_priv->start_x, + item->_priv->start_y, + event->x, + event->y)) { + GDL_DOCK_ITEM_UNSET_FLAGS (item, GDL_DOCK_IN_PREDRAG); + item->dragoff_x = item->_priv->start_x; + item->dragoff_y = item->_priv->start_y; + + gdl_dock_item_drag_start (item); + } + } + + if (!GDL_DOCK_ITEM_IN_DRAG (item)) + return FALSE; + + new_x = event->x_root; + new_y = event->y_root; + + g_signal_emit (item, gdl_dock_item_signals [DOCK_DRAG_MOTION], + 0, new_x, new_y); + + return TRUE; +} + +static gboolean +gdl_dock_item_key_press (GtkWidget *widget, + GdkEventKey *event) +{ + gboolean event_handled = FALSE; + if (GDL_DOCK_ITEM_IN_DRAG (widget)) { + if (event->keyval == GDK_Escape) { + gdl_dock_item_drag_end (GDL_DOCK_ITEM (widget), TRUE); + event_handled = TRUE; + } + } + + if (event_handled) + return TRUE; + else + return GDL_CALL_PARENT_WITH_DEFAULT (GTK_WIDGET_CLASS, + key_press_event, + (widget, event), + FALSE); +} + +static gboolean +gdl_dock_item_dock_request (GdlDockObject *object, + gint x, + gint y, + GdlDockRequest *request) +{ + GtkAllocation *alloc; + gint rel_x, rel_y; + + /* we get (x,y) in our allocation coordinates system */ + + /* Get item's allocation. */ + alloc = &(GTK_WIDGET (object)->allocation); + + /* Get coordinates relative to our window. */ + rel_x = x - alloc->x; + rel_y = y - alloc->y; + + /* Location is inside. */ + if (rel_x > 0 && rel_x < alloc->width && + rel_y > 0 && rel_y < alloc->height) { + float rx, ry; + GtkRequisition my, other; + gint divider = -1; + + /* this are for calculating the extra docking parameter */ + gdl_dock_item_preferred_size (GDL_DOCK_ITEM (request->applicant), &other); + gdl_dock_item_preferred_size (GDL_DOCK_ITEM (object), &my); + + /* Calculate location in terms of the available space (0-100%). */ + rx = (float) rel_x / alloc->width; + ry = (float) rel_y / alloc->height; + + /* Determine dock location. */ + if (rx < SPLIT_RATIO) { + request->position = GDL_DOCK_LEFT; + divider = other.width; + } + else if (rx > (1 - SPLIT_RATIO)) { + request->position = GDL_DOCK_RIGHT; + rx = 1 - rx; + divider = MAX (0, my.width - other.width); + } + else if (ry < SPLIT_RATIO && ry < rx) { + request->position = GDL_DOCK_TOP; + divider = other.height; + } + else if (ry > (1 - SPLIT_RATIO) && (1 - ry) < rx) { + request->position = GDL_DOCK_BOTTOM; + divider = MAX (0, my.height - other.height); + } + else + request->position = GDL_DOCK_CENTER; + + /* Reset rectangle coordinates to entire item. */ + request->rect.x = 0; + request->rect.y = 0; + request->rect.width = alloc->width; + request->rect.height = alloc->height; + + GdlDockItemBehavior behavior = GDL_DOCK_ITEM(object)->behavior; + + /* Calculate docking indicator rectangle size for new locations. Only + do this when we're not over the item's current location. */ + if (request->applicant != object) { + switch (request->position) { + case GDL_DOCK_TOP: + if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_TOP) + return FALSE; + request->rect.height *= SPLIT_RATIO; + break; + case GDL_DOCK_BOTTOM: + if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_BOTTOM) + return FALSE; + request->rect.y += request->rect.height * (1 - SPLIT_RATIO); + request->rect.height *= SPLIT_RATIO; + break; + case GDL_DOCK_LEFT: + if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_LEFT) + return FALSE; + request->rect.width *= SPLIT_RATIO; + break; + case GDL_DOCK_RIGHT: + if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_RIGHT) + return FALSE; + request->rect.x += request->rect.width * (1 - SPLIT_RATIO); + request->rect.width *= SPLIT_RATIO; + break; + case GDL_DOCK_CENTER: + if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_CENTER) + return FALSE; + request->rect.x = request->rect.width * SPLIT_RATIO/2; + request->rect.y = request->rect.height * SPLIT_RATIO/2; + request->rect.width = (request->rect.width * + (1 - SPLIT_RATIO/2)) - request->rect.x; + request->rect.height = (request->rect.height * + (1 - SPLIT_RATIO/2)) - request->rect.y; + break; + default: + break; + } + } + + /* adjust returned coordinates so they are have the same + origin as our window */ + request->rect.x += alloc->x; + request->rect.y += alloc->y; + + /* Set possible target location and return TRUE. */ + request->target = object; + + /* fill-in other dock information */ + if (request->position != GDL_DOCK_CENTER && divider >= 0) { + if (G_IS_VALUE (&request->extra)) + g_value_unset (&request->extra); + g_value_init (&request->extra, G_TYPE_UINT); + g_value_set_uint (&request->extra, (guint) divider); + } + + return TRUE; + } + else /* No docking possible at this location. */ + return FALSE; +} + +static void +gdl_dock_item_dock (GdlDockObject *object, + GdlDockObject *requestor, + GdlDockPlacement position, + GValue *other_data) +{ + GdlDockObject *new_parent, *parent; + gboolean add_ourselves_first; + + guint available_space=0; + gint pref_size=-1; + guint splitpos=0; + GtkRequisition req, object_req, parent_req; + + parent = gdl_dock_object_get_parent_object (object); + gdl_dock_item_preferred_size (GDL_DOCK_ITEM (requestor), &req); + gdl_dock_item_preferred_size (GDL_DOCK_ITEM (object), &object_req); + if (GDL_IS_DOCK_ITEM (parent)) + gdl_dock_item_preferred_size (GDL_DOCK_ITEM (parent), &parent_req); + else + { + parent_req.height = GTK_WIDGET (parent)->allocation.height; + parent_req.width = GTK_WIDGET (parent)->allocation.width; + } + + /* If preferred size is not set on the requestor (perhaps a new item), + * then estimate and set it. The default value (either 0 or 1 pixels) is + * not any good. + */ + switch (position) { + case GDL_DOCK_TOP: + case GDL_DOCK_BOTTOM: + if (req.width < 2) + { + req.width = object_req.width; + g_object_set (requestor, "preferred-width", req.width, NULL); + } + if (req.height < 2) + { + req.height = NEW_DOCK_ITEM_RATIO * object_req.height; + g_object_set (requestor, "preferred-height", req.height, NULL); + } + if (req.width > 1) + g_object_set (object, "preferred-width", req.width, NULL); + if (req.height > 1) + g_object_set (object, "preferred-height", + object_req.height - req.height, NULL); + break; + case GDL_DOCK_LEFT: + case GDL_DOCK_RIGHT: + if (req.height < 2) + { + req.height = object_req.height; + g_object_set (requestor, "preferred-height", req.height, NULL); + } + if (req.width < 2) + { + req.width = NEW_DOCK_ITEM_RATIO * object_req.width; + g_object_set (requestor, "preferred-width", req.width, NULL); + } + if (req.height > 1) + g_object_set (object, "preferred-height", req.height, NULL); + if (req.width > 1) + g_object_set (object, "preferred-width", + object_req.width - req.width, NULL); + break; + case GDL_DOCK_CENTER: + if (req.height < 2) + { + req.height = object_req.height; + g_object_set (requestor, "preferred-height", req.height, NULL); + } + if (req.width < 2) + { + req.width = object_req.width; + g_object_set (requestor, "preferred-width", req.width, NULL); + } + if (req.height > 1) + g_object_set (object, "preferred-height", req.height, NULL); + if (req.width > 1) + g_object_set (object, "preferred-width", req.width, NULL); + break; + default: + { + GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_ref (GDL_TYPE_DOCK_PLACEMENT)); + GEnumValue *enum_value = g_enum_get_value (enum_class, position); + const gchar *name = enum_value ? enum_value->value_name : NULL; + + g_warning (_("Unsupported docking strategy %s in dock object of type %s"), + name, G_OBJECT_TYPE_NAME (object)); + g_type_class_unref (enum_class); + return; + } + } + switch (position) { + case GDL_DOCK_TOP: + case GDL_DOCK_BOTTOM: + /* get a paned style dock object */ + new_parent = g_object_new (gdl_dock_object_type_from_nick ("paned"), + "orientation", GTK_ORIENTATION_VERTICAL, + "preferred-width", object_req.width, + "preferred-height", object_req.height, + NULL); + add_ourselves_first = (position == GDL_DOCK_BOTTOM); + if (parent) + available_space = parent_req.height; + pref_size = req.height; + break; + case GDL_DOCK_LEFT: + case GDL_DOCK_RIGHT: + new_parent = g_object_new (gdl_dock_object_type_from_nick ("paned"), + "orientation", GTK_ORIENTATION_HORIZONTAL, + "preferred-width", object_req.width, + "preferred-height", object_req.height, + NULL); + add_ourselves_first = (position == GDL_DOCK_RIGHT); + if(parent) + available_space = parent_req.width; + pref_size = req.width; + break; + case GDL_DOCK_CENTER: + new_parent = g_object_new (gdl_dock_object_type_from_nick ("notebook"), + "preferred-width", object_req.width, + "preferred-height", object_req.height, + NULL); + add_ourselves_first = TRUE; + break; + default: + { + GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_ref (GDL_TYPE_DOCK_PLACEMENT)); + GEnumValue *enum_value = g_enum_get_value (enum_class, position); + const gchar *name = enum_value ? enum_value->value_name : NULL; + + g_warning (_("Unsupported docking strategy %s in dock object of type %s"), + name, G_OBJECT_TYPE_NAME (object)); + g_type_class_unref (enum_class); + return; + } + } + + /* freeze the parent so it doesn't reduce automatically */ + if (parent) + gdl_dock_object_freeze (parent); + + /* ref ourselves since we could be destroyed when detached */ + g_object_ref (object); + GDL_DOCK_OBJECT_SET_FLAGS (object, GDL_DOCK_IN_REFLOW); + gdl_dock_object_detach (object, FALSE); + + /* freeze the new parent, so reduce won't get called before it's + actually added to our parent */ + gdl_dock_object_freeze (new_parent); + + /* bind the new parent to our master, so the following adds work */ + gdl_dock_object_bind (new_parent, G_OBJECT (GDL_DOCK_OBJECT_GET_MASTER (object))); + + /* add the objects */ + if (add_ourselves_first) { + gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (object)); + gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (requestor)); + splitpos = available_space - pref_size; + } else { + gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (requestor)); + gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (object)); + splitpos = pref_size; + } + + /* add the new parent to the parent */ + if (parent) + gtk_container_add (GTK_CONTAINER (parent), GTK_WIDGET (new_parent)); + + /* show automatic object */ + if (GTK_WIDGET_VISIBLE (object)) + gtk_widget_show (GTK_WIDGET (new_parent)); + + /* use extra docking parameter */ + if (position != GDL_DOCK_CENTER && other_data && + G_VALUE_HOLDS (other_data, G_TYPE_UINT)) { + + g_object_set (G_OBJECT (new_parent), + "position", g_value_get_uint (other_data), + NULL); + } else if (splitpos > 0 && splitpos < available_space) { + g_object_set (G_OBJECT (new_parent), "position", splitpos, NULL); + } + + GDL_DOCK_OBJECT_UNSET_FLAGS (object, GDL_DOCK_IN_REFLOW); + g_object_unref (object); + + gdl_dock_object_thaw (new_parent); + if (parent) + gdl_dock_object_thaw (parent); +} + +static void +gdl_dock_item_detach_menu (GtkWidget *widget, + GtkMenu *menu) +{ + GdlDockItem *item; + + item = GDL_DOCK_ITEM (widget); + item->_priv->menu = NULL; +} + +static void +gdl_dock_item_popup_menu (GdlDockItem *item, + guint button, + guint32 time) +{ + GtkWidget *mitem; + + if (!item->_priv->menu) { + /* Create popup menu and attach it to the dock item */ + item->_priv->menu = gtk_menu_new (); + gtk_menu_attach_to_widget (GTK_MENU (item->_priv->menu), + GTK_WIDGET (item), + gdl_dock_item_detach_menu); + + if (item->behavior & GDL_DOCK_ITEM_BEH_LOCKED) { + /* UnLock menuitem */ + mitem = gtk_menu_item_new_with_label (_("UnLock")); + gtk_menu_shell_append (GTK_MENU_SHELL (item->_priv->menu), + mitem); + g_signal_connect (mitem, "activate", + G_CALLBACK (gdl_dock_item_unlock_cb), item); + } else { + /* Hide menuitem. */ + mitem = gtk_menu_item_new_with_label (_("Hide")); + gtk_menu_shell_append (GTK_MENU_SHELL (item->_priv->menu), mitem); + g_signal_connect (mitem, "activate", + G_CALLBACK (gdl_dock_item_hide_cb), item); + /* Lock menuitem */ + mitem = gtk_menu_item_new_with_label (_("Lock")); + gtk_menu_shell_append (GTK_MENU_SHELL (item->_priv->menu), mitem); + g_signal_connect (mitem, "activate", + G_CALLBACK (gdl_dock_item_lock_cb), item); + } + } + + /* Show popup menu. */ + gtk_widget_show_all (item->_priv->menu); + gtk_menu_popup (GTK_MENU (item->_priv->menu), NULL, NULL, NULL, NULL, + button, time); +} + +static void +gdl_dock_item_drag_start (GdlDockItem *item) +{ + GdkCursor *fleur; + + if (!GTK_WIDGET_REALIZED (item)) + gtk_widget_realize (GTK_WIDGET (item)); + + GDL_DOCK_ITEM_SET_FLAGS (item, GDL_DOCK_IN_DRAG); + + /* grab the pointer so we receive all mouse events */ + fleur = gdk_cursor_new (GDK_FLEUR); + + /* grab the keyboard & pointer */ + gtk_grab_add (GTK_WIDGET (item)); + + gdk_cursor_unref (fleur); + + g_signal_emit (item, gdl_dock_item_signals [DOCK_DRAG_BEGIN], 0); +} + +static void +gdl_dock_item_drag_end (GdlDockItem *item, + gboolean cancel) +{ + /* Release pointer & keyboard. */ + gtk_grab_remove (gtk_grab_get_current ()); + + g_signal_emit (item, gdl_dock_item_signals [DOCK_DRAG_END], 0, cancel); + + GDL_DOCK_ITEM_UNSET_FLAGS (item, GDL_DOCK_IN_DRAG); +} + +static void +gdl_dock_item_tab_button (GtkWidget *widget, + GdkEventButton *event, + gpointer data) +{ + GdlDockItem *item; + + item = GDL_DOCK_ITEM (data); + + if (!GDL_DOCK_ITEM_NOT_LOCKED (item)) + return; + + switch (event->button) { + case 1: + /* set dragoff_{x,y} as we the user clicked on the middle of the + drag handle */ + switch (item->orientation) { + case GTK_ORIENTATION_HORIZONTAL: + /*item->dragoff_x = item->_priv->grip_size / 2;*/ + item->dragoff_y = GTK_WIDGET (data)->allocation.height / 2; + break; + case GTK_ORIENTATION_VERTICAL: + /*item->dragoff_x = GTK_WIDGET (data)->allocation.width / 2;*/ + item->dragoff_y = item->_priv->grip_size / 2; + break; + }; + gdl_dock_item_drag_start (item); + break; + + case 3: + gdl_dock_item_popup_menu (item, event->button, event->time); + break; + + default: + break; + }; +} + +static void +gdl_dock_item_hide_cb (GtkWidget *widget, + GdlDockItem *item) +{ + GdlDockMaster *master; + + g_return_if_fail (item != NULL); + + master = GDL_DOCK_OBJECT_GET_MASTER (item); + gdl_dock_item_hide_item (item); +} + +static void +gdl_dock_item_lock_cb (GtkWidget *widget, + GdlDockItem *item) +{ + g_return_if_fail (item != NULL); + + gdl_dock_item_lock (item); +} + +static void +gdl_dock_item_unlock_cb (GtkWidget *widget, + GdlDockItem *item) +{ + g_return_if_fail (item != NULL); + + gdl_dock_item_unlock (item); +} + +static void +gdl_dock_item_showhide_grip (GdlDockItem *item) +{ + GdkDisplay *display; + GdkCursor *cursor; + + gdl_dock_item_detach_menu (GTK_WIDGET (item), NULL); + display = gtk_widget_get_display (GTK_WIDGET (item)); + cursor = NULL; + + if (item->_priv->grip) { + if (GDL_DOCK_ITEM_GRIP_SHOWN (item) && + GDL_DOCK_ITEM_NOT_LOCKED(item)) + cursor = gdk_cursor_new_for_display (display, GDK_HAND2); + } + if (item->_priv->grip && GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window) + gdk_window_set_cursor (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window, cursor); + + if (cursor) + gdk_cursor_unref (cursor); + + gtk_widget_queue_resize (GTK_WIDGET (item)); +} + +static void +gdl_dock_item_real_set_orientation (GdlDockItem *item, + GtkOrientation orientation) +{ + item->orientation = orientation; + + if (GTK_WIDGET_DRAWABLE (item)) + gtk_widget_queue_draw (GTK_WIDGET (item)); + gtk_widget_queue_resize (GTK_WIDGET (item)); +} + + +/* ----- Public interface ----- */ + +GtkWidget * +gdl_dock_item_new (const gchar *name, + const gchar *long_name, + GdlDockItemBehavior behavior) +{ + GdlDockItem *item; + + item = GDL_DOCK_ITEM (g_object_new (GDL_TYPE_DOCK_ITEM, + "name", name, + "long-name", long_name, + "behavior", behavior, + NULL)); + GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_AUTOMATIC); + gdl_dock_item_set_tablabel (item, gtk_label_new (long_name)); + return GTK_WIDGET (item); +} + +GtkWidget * +gdl_dock_item_new_with_stock (const gchar *name, + const gchar *long_name, + const gchar *stock_id, + GdlDockItemBehavior behavior) +{ + GdlDockItem *item; + + item = GDL_DOCK_ITEM (g_object_new (GDL_TYPE_DOCK_ITEM, + "name", name, + "long-name", long_name, + "stock-id", stock_id, + "behavior", behavior, + NULL)); + GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_AUTOMATIC); + gdl_dock_item_set_tablabel (item, gtk_label_new (long_name)); + + return GTK_WIDGET (item); +} + +GtkWidget * +gdl_dock_item_new_with_pixbuf_icon (const gchar *name, + const gchar *long_name, + const GdkPixbuf *pixbuf_icon, + GdlDockItemBehavior behavior) +{ + GdlDockItem *item; + + item = GDL_DOCK_ITEM (g_object_new (GDL_TYPE_DOCK_ITEM, + "name", name, + "long-name", long_name, + "pixbuf-icon", pixbuf_icon, + "behavior", behavior, + NULL)); + + GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_AUTOMATIC); + gdl_dock_item_set_tablabel (item, gtk_label_new (long_name)); + + return GTK_WIDGET (item); +} + +/* convenient function (and to preserve source compat) */ +void +gdl_dock_item_dock_to (GdlDockItem *item, + GdlDockItem *target, + GdlDockPlacement position, + gint docking_param) +{ + g_return_if_fail (item != NULL); + g_return_if_fail (item != target); + g_return_if_fail (target != NULL || position == GDL_DOCK_FLOATING); + g_return_if_fail ((item->behavior & GDL_DOCK_ITEM_BEH_NEVER_FLOATING) == 0 || position != GDL_DOCK_FLOATING); + + if (position == GDL_DOCK_FLOATING || !target) { + GdlDockObject *controller; + + if (!gdl_dock_object_is_bound (GDL_DOCK_OBJECT (item))) { + g_warning (_("Attempt to bind an unbound item %p"), item); + return; + } + + controller = gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (item)); + + /* FIXME: save previous docking position for later + re-docking... does this make sense now? */ + + /* Create new floating dock for widget. */ + item->dragoff_x = item->dragoff_y = 0; + gdl_dock_add_floating_item (GDL_DOCK (controller), + item, 0, 0, -1, -1); + + } else + gdl_dock_object_dock (GDL_DOCK_OBJECT (target), + GDL_DOCK_OBJECT (item), + position, NULL); +} + +void +gdl_dock_item_set_orientation (GdlDockItem *item, + GtkOrientation orientation) +{ + GParamSpec *pspec; + + g_return_if_fail (item != NULL); + + if (item->orientation != orientation) { + /* push the property down the hierarchy if our child supports it */ + if (item->child != NULL) { + pspec = g_object_class_find_property ( + G_OBJECT_GET_CLASS (item->child), "orientation"); + if (pspec && pspec->value_type == GTK_TYPE_ORIENTATION) + g_object_set (G_OBJECT (item->child), + "orientation", orientation, + NULL); + }; + + GDL_CALL_VIRTUAL (item, GDL_DOCK_ITEM_GET_CLASS, set_orientation, (item, orientation)); + g_object_notify (G_OBJECT (item), "orientation"); + } +} + +GtkWidget * +gdl_dock_item_get_tablabel (GdlDockItem *item) +{ + g_return_val_if_fail (item != NULL, NULL); + g_return_val_if_fail (GDL_IS_DOCK_ITEM (item), NULL); + + return item->_priv->tab_label; +} + +void +gdl_dock_item_set_tablabel (GdlDockItem *item, + GtkWidget *tablabel) +{ + g_return_if_fail (item != NULL); + + if (item->_priv->tab_label) { + /* disconnect and unref the previous tablabel */ + if (GDL_IS_DOCK_TABLABEL (item->_priv->tab_label)) { + g_signal_handlers_disconnect_matched (item->_priv->tab_label, + G_SIGNAL_MATCH_DATA, + 0, 0, NULL, + NULL, item); + g_object_set (item->_priv->tab_label, "item", NULL, NULL); + } + gtk_widget_unref (item->_priv->tab_label); + item->_priv->tab_label = NULL; + } + + if (tablabel) { + gtk_widget_ref (tablabel); + gtk_object_sink (GTK_OBJECT (tablabel)); + item->_priv->tab_label = tablabel; + if (GDL_IS_DOCK_TABLABEL (tablabel)) { + g_object_set (tablabel, "item", item, NULL); + /* connect to tablabel signal */ + g_signal_connect (tablabel, "button_pressed_handle", + G_CALLBACK (gdl_dock_item_tab_button), item); + } + } +} + +void +gdl_dock_item_hide_grip (GdlDockItem *item) +{ + g_return_if_fail (item != NULL); + if (item->_priv->grip_shown) { + item->_priv->grip_shown = FALSE; + gdl_dock_item_showhide_grip (item); + }; + g_warning ("Grips always show unless GDL_DOCK_ITEM_BEH_NO_GRIP is set\n" ); +} + +void +gdl_dock_item_show_grip (GdlDockItem *item) +{ + g_return_if_fail (item != NULL); + if (!item->_priv->grip_shown) { + item->_priv->grip_shown = TRUE; + gdl_dock_item_showhide_grip (item); + }; +} + +/* convenient function (and to preserve source compat) */ +void +gdl_dock_item_bind (GdlDockItem *item, + GtkWidget *dock) +{ + g_return_if_fail (item != NULL); + g_return_if_fail (dock == NULL || GDL_IS_DOCK (dock)); + + gdl_dock_object_bind (GDL_DOCK_OBJECT (item), + G_OBJECT (GDL_DOCK_OBJECT_GET_MASTER (dock))); +} + +/* convenient function (and to preserve source compat) */ +void +gdl_dock_item_unbind (GdlDockItem *item) +{ + g_return_if_fail (item != NULL); + + gdl_dock_object_unbind (GDL_DOCK_OBJECT (item)); +} + +void +gdl_dock_item_hide_item (GdlDockItem *item) +{ + g_return_if_fail (item != NULL); + + if (!GDL_DOCK_OBJECT_ATTACHED (item)) + /* already hidden/detached */ + return; + + /* if the object is manual, create a new placeholder to be able to + restore the position later */ + if (!GDL_DOCK_OBJECT_AUTOMATIC (item)) { + if (item->_priv->ph) + g_object_unref (item->_priv->ph); + + gboolean isFloating = FALSE; + gint width=0, height=0, x=0, y = 0; + + if (GDL_IS_DOCK (gdl_dock_object_get_parent_object (GDL_DOCK_OBJECT (item)))) + { + GdlDock* dock = GDL_DOCK (gdl_dock_object_get_parent_object (GDL_DOCK_OBJECT (item))); + g_object_get (dock, + "floating", &isFloating, + "width", &width, + "height",&height, + "floatx",&x, + "floaty",&y, + NULL); + } else { + item->_priv->preferred_width=GTK_WIDGET (item)->allocation.width; + item->_priv->preferred_height=GTK_WIDGET (item)->allocation.height; + } + item->_priv->ph = GDL_DOCK_PLACEHOLDER ( + g_object_new (GDL_TYPE_DOCK_PLACEHOLDER, + "sticky", FALSE, + "host", item, + "width", width, + "height", height, + "floating", isFloating, + "floatx", x, + "floaty", y, + NULL)); + g_object_ref (item->_priv->ph); + gtk_object_sink (GTK_OBJECT (item->_priv->ph)); + } + + gdl_dock_object_freeze (GDL_DOCK_OBJECT (item)); + + /* hide our children first, so they can also set placeholders */ + if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT (item))) + gtk_container_foreach (GTK_CONTAINER (item), + (GtkCallback) gdl_dock_item_hide_item, + NULL); + + /* detach the item recursively */ + gdl_dock_object_detach (GDL_DOCK_OBJECT (item), TRUE); + + gtk_widget_hide (GTK_WIDGET (item)); + + gdl_dock_object_thaw (GDL_DOCK_OBJECT (item)); +} + +void +gdl_dock_item_iconify_item (GdlDockItem *item) +{ + g_return_if_fail (item != NULL); + + GDL_DOCK_OBJECT_SET_FLAGS (item, GDL_DOCK_ICONIFIED); + gdl_dock_item_hide_item (item); +} + +void +gdl_dock_item_show_item (GdlDockItem *item) +{ + g_return_if_fail (item != NULL); + + GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_ICONIFIED); + + if (item->_priv->ph) { + gboolean isFloating=FALSE; + gint width = 0, height = 0, x= 0, y = 0; + g_object_get (G_OBJECT(item->_priv->ph), + "width", &width, + "height", &height, + "floating",&isFloating, + "floatx", &x, + "floaty", &y, + NULL); + if (isFloating) { + GdlDockObject *controller = + gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (item)); + gdl_dock_add_floating_item (GDL_DOCK (controller), + item, x, y, width, height); + } else { + gtk_container_add (GTK_CONTAINER (item->_priv->ph), + GTK_WIDGET (item)); + } + g_object_unref (item->_priv->ph); + item->_priv->ph = NULL; + + } else if (gdl_dock_object_is_bound (GDL_DOCK_OBJECT (item))) { + GdlDockObject *toplevel; + + toplevel = gdl_dock_master_get_controller + (GDL_DOCK_OBJECT_GET_MASTER (item)); + + if (item->behavior & GDL_DOCK_ITEM_BEH_NEVER_FLOATING) { + g_warning("Object %s has no default position and flag GDL_DOCK_ITEM_BEH_NEVER_FLOATING is set.\n", + GDL_DOCK_OBJECT(item)->name); + } else if (toplevel) { + gdl_dock_object_dock (toplevel, GDL_DOCK_OBJECT (item), + GDL_DOCK_FLOATING, NULL); + } else + g_warning("There is no toplevel window. GdlDockItem %s cannot be shown.\n", GDL_DOCK_OBJECT(item)->name); + + } else + g_warning("GdlDockItem %s is not bound. It cannot be shown.\n", + GDL_DOCK_OBJECT(item)->name); + + gtk_widget_show (GTK_WIDGET (item)); +} + +void +gdl_dock_item_lock (GdlDockItem *item) +{ + g_object_set (item, "locked", TRUE, NULL); +} + +void +gdl_dock_item_unlock (GdlDockItem *item) +{ + g_object_set (item, "locked", FALSE, NULL); +} + +void +gdl_dock_item_set_default_position (GdlDockItem *item, + GdlDockObject *reference) +{ + g_return_if_fail (item != NULL); + + if (item->_priv->ph) { + g_object_unref (item->_priv->ph); + item->_priv->ph = NULL; + } + + if (reference && GDL_DOCK_OBJECT_ATTACHED (reference)) { + if (GDL_IS_DOCK_PLACEHOLDER (reference)) { + g_object_ref (reference); + gtk_object_sink (GTK_OBJECT (reference)); + item->_priv->ph = GDL_DOCK_PLACEHOLDER (reference); + } else { + item->_priv->ph = GDL_DOCK_PLACEHOLDER ( + g_object_new (GDL_TYPE_DOCK_PLACEHOLDER, + "sticky", TRUE, + "host", reference, + NULL)); + g_object_ref (item->_priv->ph); + gtk_object_sink (GTK_OBJECT (item->_priv->ph)); + } + } +} + +void +gdl_dock_item_preferred_size (GdlDockItem *item, + GtkRequisition *req) +{ + if (!req) + return; + + req->width = MAX (item->_priv->preferred_width, + GTK_WIDGET (item)->allocation.width); + req->height = MAX (item->_priv->preferred_height, + GTK_WIDGET (item)->allocation.height); +} + + +/* ----- gtk orientation type exporter/importer ----- */ + +static void +gdl_dock_param_export_gtk_orientation (const GValue *src, + GValue *dst) +{ + dst->data [0].v_pointer = + g_strdup_printf ("%s", (src->data [0].v_int == GTK_ORIENTATION_HORIZONTAL) ? + "horizontal" : "vertical"); +} + +static void +gdl_dock_param_import_gtk_orientation (const GValue *src, + GValue *dst) +{ + if (!strcmp (src->data [0].v_pointer, "horizontal")) + dst->data [0].v_int = GTK_ORIENTATION_HORIZONTAL; + else + dst->data [0].v_int = GTK_ORIENTATION_VERTICAL; +} + diff --git a/src/libgdl/gdl-dock-item.h b/src/libgdl/gdl-dock-item.h new file mode 100644 index 000000000..6eec28aeb --- /dev/null +++ b/src/libgdl/gdl-dock-item.h @@ -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 + * + * Based on GnomeDockItem/BonoboDockItem. Original copyright notice follows. + * + * Copyright (C) 1998 Ettore Perazzoli + * Copyright (C) 1998 Elliot Lee + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GDL_DOCK_ITEM_H__ +#define __GDL_DOCK_ITEM_H__ + +#include "libgdl/gdl-dock-object.h" + +G_BEGIN_DECLS + +/* standard macros */ +#define GDL_TYPE_DOCK_ITEM (gdl_dock_item_get_type ()) +#define GDL_DOCK_ITEM(obj) (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_ITEM, GdlDockItem)) +#define GDL_DOCK_ITEM_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_ITEM, GdlDockItemClass)) +#define GDL_IS_DOCK_ITEM(obj) (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_ITEM)) +#define GDL_IS_DOCK_ITEM_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_ITEM)) +#define GDL_DOCK_ITEM_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK_ITEM, GdlDockItemClass)) + +/* data types & structures */ +typedef enum { + GDL_DOCK_ITEM_BEH_NORMAL = 0, + GDL_DOCK_ITEM_BEH_NEVER_FLOATING = 1 << 0, + GDL_DOCK_ITEM_BEH_NEVER_VERTICAL = 1 << 1, + GDL_DOCK_ITEM_BEH_NEVER_HORIZONTAL = 1 << 2, + GDL_DOCK_ITEM_BEH_LOCKED = 1 << 3, + GDL_DOCK_ITEM_BEH_CANT_DOCK_TOP = 1 << 4, + GDL_DOCK_ITEM_BEH_CANT_DOCK_BOTTOM = 1 << 5, + GDL_DOCK_ITEM_BEH_CANT_DOCK_LEFT = 1 << 6, + GDL_DOCK_ITEM_BEH_CANT_DOCK_RIGHT = 1 << 7, + GDL_DOCK_ITEM_BEH_CANT_DOCK_CENTER = 1 << 8, + GDL_DOCK_ITEM_BEH_CANT_CLOSE = 1 << 9, + GDL_DOCK_ITEM_BEH_CANT_ICONIFY = 1 << 10, + GDL_DOCK_ITEM_BEH_NO_GRIP = 1 << 11 +} GdlDockItemBehavior; + +typedef enum { + GDL_DOCK_IN_DRAG = 1 << GDL_DOCK_OBJECT_FLAGS_SHIFT, + GDL_DOCK_IN_PREDRAG = 1 << (GDL_DOCK_OBJECT_FLAGS_SHIFT + 1), + GDL_DOCK_ICONIFIED = 1 << (GDL_DOCK_OBJECT_FLAGS_SHIFT + 2), + /* for general use: indicates the user has started an action on + the dock item */ + GDL_DOCK_USER_ACTION = 1 << (GDL_DOCK_OBJECT_FLAGS_SHIFT + 3) +} GdlDockItemFlags; + +typedef struct _GdlDockItem GdlDockItem; +typedef struct _GdlDockItemClass GdlDockItemClass; +typedef struct _GdlDockItemPrivate GdlDockItemPrivate; + +struct _GdlDockItem { + GdlDockObject object; + + GtkWidget *child; + GdlDockItemBehavior behavior; + GtkOrientation orientation; + + guint resize : 1; + + gint dragoff_x, dragoff_y; /* these need to be + accesible from + outside */ + GdlDockItemPrivate *_priv; +}; + +struct _GdlDockItemClass { + GdlDockObjectClass parent_class; + + gboolean has_grip; + + /* virtuals */ + void (* dock_drag_begin) (GdlDockItem *item); + void (* dock_drag_motion) (GdlDockItem *item, + gint x, + gint y); + void (* dock_drag_end) (GdlDockItem *item, + gboolean cancelled); + + void (* set_orientation) (GdlDockItem *item, + GtkOrientation orientation); +}; + +/* additional macros */ +#define GDL_DOCK_ITEM_FLAGS(item) (GDL_DOCK_OBJECT (item)->flags) +#define GDL_DOCK_ITEM_IN_DRAG(item) \ + ((GDL_DOCK_ITEM_FLAGS (item) & GDL_DOCK_IN_DRAG) != 0) +#define GDL_DOCK_ITEM_IN_PREDRAG(item) \ + ((GDL_DOCK_ITEM_FLAGS (item) & GDL_DOCK_IN_PREDRAG) != 0) +#define GDL_DOCK_ITEM_ICONIFIED(item) \ + ((GDL_DOCK_ITEM_FLAGS (item) & GDL_DOCK_ICONIFIED) != 0) +#define GDL_DOCK_ITEM_USER_ACTION(item) \ + ((GDL_DOCK_ITEM_FLAGS (item) & GDL_DOCK_USER_ACTION) != 0) +#define GDL_DOCK_ITEM_NOT_LOCKED(item) !((item)->behavior & GDL_DOCK_ITEM_BEH_LOCKED) +#define GDL_DOCK_ITEM_NO_GRIP(item) ((item)->behavior & GDL_DOCK_ITEM_BEH_NO_GRIP) + +#define GDL_DOCK_ITEM_SET_FLAGS(item,flag) \ + G_STMT_START { (GDL_DOCK_ITEM_FLAGS (item) |= (flag)); } G_STMT_END +#define GDL_DOCK_ITEM_UNSET_FLAGS(item,flag) \ + G_STMT_START { (GDL_DOCK_ITEM_FLAGS (item) &= ~(flag)); } G_STMT_END + +#define GDL_DOCK_ITEM_HAS_GRIP(item) ((GDL_DOCK_ITEM_GET_CLASS (item)->has_grip)&& \ + ! GDL_DOCK_ITEM_NO_GRIP (item)) + +#define GDL_DOCK_ITEM_CANT_CLOSE(item) \ + ((((item)->behavior & GDL_DOCK_ITEM_BEH_CANT_CLOSE) != 0)|| \ + ! GDL_DOCK_ITEM_NOT_LOCKED(item)) + +#define GDL_DOCK_ITEM_CANT_ICONIFY(item) \ + ((((item)->behavior & GDL_DOCK_ITEM_BEH_CANT_ICONIFY) != 0)|| \ + ! GDL_DOCK_ITEM_NOT_LOCKED(item)) + +/* public interface */ + +GtkWidget *gdl_dock_item_new (const gchar *name, + const gchar *long_name, + GdlDockItemBehavior behavior); +GtkWidget *gdl_dock_item_new_with_stock (const gchar *name, + const gchar *long_name, + const gchar *stock_id, + GdlDockItemBehavior behavior); + +GtkWidget *gdl_dock_item_new_with_pixbuf_icon (const gchar *name, + const gchar *long_name, + const GdkPixbuf *pixbuf_icon, + GdlDockItemBehavior behavior); + +GType gdl_dock_item_get_type (void); + +void gdl_dock_item_dock_to (GdlDockItem *item, + GdlDockItem *target, + GdlDockPlacement position, + gint docking_param); + +void gdl_dock_item_set_orientation (GdlDockItem *item, + GtkOrientation orientation); + +GtkWidget *gdl_dock_item_get_tablabel (GdlDockItem *item); +void gdl_dock_item_set_tablabel (GdlDockItem *item, + GtkWidget *tablabel); +void gdl_dock_item_hide_grip (GdlDockItem *item); +void gdl_dock_item_show_grip (GdlDockItem *item); + +/* bind and unbind items to a dock */ +void gdl_dock_item_bind (GdlDockItem *item, + GtkWidget *dock); + +void gdl_dock_item_unbind (GdlDockItem *item); + +void gdl_dock_item_hide_item (GdlDockItem *item); + +void gdl_dock_item_iconify_item (GdlDockItem *item); + +void gdl_dock_item_show_item (GdlDockItem *item); + +void gdl_dock_item_lock (GdlDockItem *item); + +void gdl_dock_item_unlock (GdlDockItem *item); + +void gdl_dock_item_set_default_position (GdlDockItem *item, + GdlDockObject *reference); + +void gdl_dock_item_preferred_size (GdlDockItem *item, + GtkRequisition *req); + +G_END_DECLS + +#endif diff --git a/src/libgdl/gdl-dock-master.c b/src/libgdl/gdl-dock-master.c new file mode 100644 index 000000000..8a6aba315 --- /dev/null +++ b/src/libgdl/gdl-dock-master.c @@ -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 + * + * 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 +#endif + +#include "gdl-i18n.h" + +#include "gdl-tools.h" +#include "gdl-dock-master.h" +#include "gdl-dock.h" +#include "gdl-dock-item.h" +#include "libgdlmarshal.h" +#include "libgdltypebuiltins.h" + +/* ----- Private prototypes ----- */ + +static void gdl_dock_master_class_init (GdlDockMasterClass *klass); +static void gdl_dock_master_instance_init (GdlDockMaster *master); + +static void gdl_dock_master_dispose (GObject *g_object); +static void gdl_dock_master_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gdl_dock_master_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static void _gdl_dock_master_remove (GdlDockObject *object, + GdlDockMaster *master); + +static void gdl_dock_master_drag_begin (GdlDockItem *item, + gpointer data); +static void gdl_dock_master_drag_end (GdlDockItem *item, + gboolean cancelled, + gpointer data); +static void gdl_dock_master_drag_motion (GdlDockItem *item, + gint x, + gint y, + gpointer data); + +static void _gdl_dock_master_foreach (gpointer key, + gpointer value, + gpointer user_data); + +static void gdl_dock_master_xor_rect (GdlDockMaster *master); + +static void gdl_dock_master_layout_changed (GdlDockMaster *master); + +static void gdl_dock_master_set_switcher_style (GdlDockMaster *master, + GdlSwitcherStyle switcher_style); + +/* ----- Private data types and variables ----- */ + +enum { + PROP_0, + PROP_DEFAULT_TITLE, + PROP_LOCKED, + PROP_SWITCHER_STYLE +}; + +enum { + LAYOUT_CHANGED, + LAST_SIGNAL +}; + +struct _GdlDockMasterPrivate { + gint number; /* for naming nameless manual objects */ + gchar *default_title; + + GdkGC *root_xor_gc; + gboolean rect_drawn; + GdlDock *rect_owner; + + GdlDockRequest *drag_request; + + /* source id for the idle handler to emit a layout_changed signal */ + guint idle_layout_changed_id; + + /* hashes to quickly calculate the overall locked status: i.e. + * if size(unlocked_items) == 0 then locked = 1 + * else if size(locked_items) == 0 then locked = 0 + * else locked = -1 + */ + GHashTable *locked_items; + GHashTable *unlocked_items; + + GdlSwitcherStyle switcher_style; +}; + +#define COMPUTE_LOCKED(master) \ + (g_hash_table_size ((master)->_priv->unlocked_items) == 0 ? 1 : \ + (g_hash_table_size ((master)->_priv->locked_items) == 0 ? 0 : -1)) + +static guint master_signals [LAST_SIGNAL] = { 0 }; + + +/* ----- Private interface ----- */ + +GDL_CLASS_BOILERPLATE (GdlDockMaster, gdl_dock_master, GObject, G_TYPE_OBJECT); + +static void +gdl_dock_master_class_init (GdlDockMasterClass *klass) +{ + GObjectClass *g_object_class; + + g_object_class = G_OBJECT_CLASS (klass); + + g_object_class->dispose = gdl_dock_master_dispose; + g_object_class->set_property = gdl_dock_master_set_property; + g_object_class->get_property = gdl_dock_master_get_property; + + g_object_class_install_property ( + g_object_class, PROP_DEFAULT_TITLE, + g_param_spec_string ("default-title", _("Default title"), + _("Default title for newly created floating docks"), + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + g_object_class, PROP_LOCKED, + g_param_spec_int ("locked", _("Locked"), + _("If is set to 1, all the dock items bound to the master " + "are locked; if it's 0, all are unlocked; -1 indicates " + "inconsistency among the items"), + -1, 1, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + g_object_class, PROP_SWITCHER_STYLE, + g_param_spec_enum ("switcher-style", _("Switcher Style"), + _("Switcher buttons style"), + GDL_TYPE_SWITCHER_STYLE, + GDL_SWITCHER_STYLE_BOTH, + G_PARAM_READWRITE)); + + master_signals [LAYOUT_CHANGED] = + g_signal_new ("layout-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GdlDockMasterClass, layout_changed), + NULL, /* accumulator */ + NULL, /* accu_data */ + gdl_marshal_VOID__VOID, + G_TYPE_NONE, /* return type */ + 0); + + klass->layout_changed = gdl_dock_master_layout_changed; +} + +static void +gdl_dock_master_instance_init (GdlDockMaster *master) +{ + master->dock_objects = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + master->toplevel_docks = NULL; + master->controller = NULL; + master->dock_number = 1; + + master->_priv = g_new0 (GdlDockMasterPrivate, 1); + master->_priv->number = 1; + master->_priv->switcher_style = GDL_SWITCHER_STYLE_BOTH; + master->_priv->locked_items = g_hash_table_new (g_direct_hash, g_direct_equal); + master->_priv->unlocked_items = g_hash_table_new (g_direct_hash, g_direct_equal); +} + +static void +_gdl_dock_master_remove (GdlDockObject *object, + GdlDockMaster *master) +{ + g_return_if_fail (master != NULL && object != NULL); + + if (GDL_IS_DOCK (object)) { + GList *found_link; + + found_link = g_list_find (master->toplevel_docks, object); + if (found_link) + master->toplevel_docks = g_list_delete_link (master->toplevel_docks, + found_link); + if (object == master->controller) { + GList *last; + GdlDockObject *new_controller = NULL; + + /* now find some other non-automatic toplevel to use as a + new controller. start from the last dock, since it's + probably a non-floating and manual */ + last = g_list_last (master->toplevel_docks); + while (last) { + if (!GDL_DOCK_OBJECT_AUTOMATIC (last->data)) { + new_controller = GDL_DOCK_OBJECT (last->data); + break; + } + last = last->prev; + }; + + if (new_controller) { + /* the new controller gets the ref (implicitly of course) */ + master->controller = new_controller; + } else { + master->controller = NULL; + /* no controller, no master */ + g_object_unref (master); + } + } + } + /* disconnect dock object signals */ + g_signal_handlers_disconnect_matched (object, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, master); + + /* unref the object from the hash if it's there */ + if (object->name) { + GdlDockObject *found_object; + found_object = g_hash_table_lookup (master->dock_objects, object->name); + if (found_object == object) { + g_hash_table_remove (master->dock_objects, object->name); + g_object_unref (object); + } + } +} + +static void +ht_foreach_build_slist (gpointer key, + gpointer value, + GSList **slist) +{ + *slist = g_slist_prepend (*slist, value); +} + +static void +gdl_dock_master_dispose (GObject *g_object) +{ + GdlDockMaster *master; + + g_return_if_fail (GDL_IS_DOCK_MASTER (g_object)); + + master = GDL_DOCK_MASTER (g_object); + + if (master->toplevel_docks) { + g_list_foreach (master->toplevel_docks, + (GFunc) gdl_dock_object_unbind, NULL); + g_list_free (master->toplevel_docks); + master->toplevel_docks = NULL; + } + + if (master->dock_objects) { + GSList *alive_docks = NULL; + g_hash_table_foreach (master->dock_objects, + (GHFunc) ht_foreach_build_slist, &alive_docks); + while (alive_docks) { + gdl_dock_object_unbind (GDL_DOCK_OBJECT (alive_docks->data)); + alive_docks = g_slist_delete_link (alive_docks, alive_docks); + } + + g_hash_table_destroy (master->dock_objects); + master->dock_objects = NULL; + } + + if (master->_priv) { + if (master->_priv->idle_layout_changed_id) + g_source_remove (master->_priv->idle_layout_changed_id); + + if (master->_priv->root_xor_gc) { + g_object_unref (master->_priv->root_xor_gc); + master->_priv->root_xor_gc = NULL; + } + if (master->_priv->drag_request) { + if (G_IS_VALUE (&master->_priv->drag_request->extra)) + g_value_unset (&master->_priv->drag_request->extra); + g_free (master->_priv->drag_request); + master->_priv->drag_request = NULL; + } + g_free (master->_priv->default_title); + master->_priv->default_title = NULL; + + g_hash_table_destroy (master->_priv->locked_items); + master->_priv->locked_items = NULL; + g_hash_table_destroy (master->_priv->unlocked_items); + master->_priv->unlocked_items = NULL; + + g_free (master->_priv); + master->_priv = NULL; + } + + GDL_CALL_PARENT (G_OBJECT_CLASS, dispose, (g_object)); +} + +static void +foreach_lock_unlock (GdlDockItem *item, + gboolean locked) +{ + if (!GDL_IS_DOCK_ITEM (item)) + return; + + g_object_set (item, "locked", locked, NULL); + if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT (item))) + gtk_container_foreach (GTK_CONTAINER (item), + (GtkCallback) foreach_lock_unlock, + GINT_TO_POINTER (locked)); +} + +static void +gdl_dock_master_lock_unlock (GdlDockMaster *master, + gboolean locked) +{ + GList *l; + + for (l = master->toplevel_docks; l; l = l->next) { + GdlDock *dock = GDL_DOCK (l->data); + if (dock->root) + foreach_lock_unlock (GDL_DOCK_ITEM (dock->root), locked); + } + + /* just to be sure hidden items are set too */ + gdl_dock_master_foreach (master, + (GFunc) foreach_lock_unlock, + GINT_TO_POINTER (locked)); +} + +static void +gdl_dock_master_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdlDockMaster *master = GDL_DOCK_MASTER (object); + + switch (prop_id) { + case PROP_DEFAULT_TITLE: + g_free (master->_priv->default_title); + master->_priv->default_title = g_value_dup_string (value); + break; + case PROP_LOCKED: + if (g_value_get_int (value) >= 0) + gdl_dock_master_lock_unlock (master, (g_value_get_int (value) > 0)); + break; + case PROP_SWITCHER_STYLE: + gdl_dock_master_set_switcher_style (master, g_value_get_enum (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdl_dock_master_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdlDockMaster *master = GDL_DOCK_MASTER (object); + + switch (prop_id) { + case PROP_DEFAULT_TITLE: + g_value_set_string (value, master->_priv->default_title); + break; + case PROP_LOCKED: + g_value_set_int (value, COMPUTE_LOCKED (master)); + break; + case PROP_SWITCHER_STYLE: + g_value_set_enum (value, master->_priv->switcher_style); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdl_dock_master_drag_begin (GdlDockItem *item, + gpointer data) +{ + GdlDockMaster *master; + GdlDockRequest *request; + + g_return_if_fail (data != NULL); + g_return_if_fail (item != NULL); + + master = GDL_DOCK_MASTER (data); + + if (!master->_priv->drag_request) + master->_priv->drag_request = g_new0 (GdlDockRequest, 1); + + request = master->_priv->drag_request; + + /* Set the target to itself so it won't go floating with just a click. */ + request->applicant = GDL_DOCK_OBJECT (item); + request->target = GDL_DOCK_OBJECT (item); + request->position = GDL_DOCK_FLOATING; + if (G_IS_VALUE (&request->extra)) + g_value_unset (&request->extra); + + master->_priv->rect_drawn = FALSE; + master->_priv->rect_owner = NULL; +} + +static void +gdl_dock_master_drag_end (GdlDockItem *item, + gboolean cancelled, + gpointer data) +{ + GdlDockMaster *master; + GdlDockRequest *request; + + g_return_if_fail (data != NULL); + g_return_if_fail (item != NULL); + + master = GDL_DOCK_MASTER (data); + request = master->_priv->drag_request; + + g_return_if_fail (GDL_DOCK_OBJECT (item) == request->applicant); + + /* Erase previously drawn rectangle */ + if (master->_priv->rect_drawn) + gdl_dock_master_xor_rect (master); + + /* cancel conditions */ + if (cancelled || request->applicant == request->target) + return; + + /* dock object to the requested position */ + gdl_dock_object_dock (request->target, + request->applicant, + request->position, + &request->extra); + + g_signal_emit (master, master_signals [LAYOUT_CHANGED], 0); +} + +static void +gdl_dock_master_drag_motion (GdlDockItem *item, + gint root_x, + gint root_y, + gpointer data) +{ + GdlDockMaster *master; + GdlDockRequest my_request, *request; + GdkWindow *window; + gint win_x, win_y; + gint x, y; + GdlDock *dock = NULL; + gboolean may_dock = FALSE; + + g_return_if_fail (item != NULL && data != NULL); + + master = GDL_DOCK_MASTER (data); + request = master->_priv->drag_request; + + g_return_if_fail (GDL_DOCK_OBJECT (item) == request->applicant); + + my_request = *request; + + /* first look under the pointer */ + window = gdk_window_at_pointer (&win_x, &win_y); + if (window) { + GtkWidget *widget; + /* ok, now get the widget who owns that window and see if we can + get to a GdlDock by walking up the hierarchy */ + gdk_window_get_user_data (window, (gpointer) &widget); + if (GTK_IS_WIDGET (widget)) { + while (widget && (!GDL_IS_DOCK (widget) || + GDL_DOCK_OBJECT_GET_MASTER (widget) != master)) + widget = widget->parent; + if (widget) { + gint win_w, win_h; + + /* verify that the pointer is still in that dock + (the user could have moved it) */ + gdk_window_get_geometry (widget->window, + NULL, NULL, &win_w, &win_h, NULL); + gdk_window_get_origin (widget->window, &win_x, &win_y); + if (root_x >= win_x && root_x < win_x + win_w && + root_y >= win_y && root_y < win_y + win_h) + dock = GDL_DOCK (widget); + } + } + } + + if (dock) { + /* translate root coordinates into dock object coordinates + (i.e. widget coordinates) */ + gdk_window_get_origin (GTK_WIDGET (dock)->window, &win_x, &win_y); + x = root_x - win_x; + y = root_y - win_y; + may_dock = gdl_dock_object_dock_request (GDL_DOCK_OBJECT (dock), + x, y, &my_request); + } + else { + GList *l; + + /* try to dock the item in all the docks in the ring in turn */ + for (l = master->toplevel_docks; l; l = l->next) { + dock = GDL_DOCK (l->data); + /* translate root coordinates into dock object coordinates + (i.e. widget coordinates) */ + gdk_window_get_origin (GTK_WIDGET (dock)->window, &win_x, &win_y); + x = root_x - win_x; + y = root_y - win_y; + may_dock = gdl_dock_object_dock_request (GDL_DOCK_OBJECT (dock), + x, y, &my_request); + if (may_dock) + break; + } + } + + + if (!may_dock) { + GtkRequisition req; + /* Special case for GdlDockItems : they must respect the flags */ + if(GDL_IS_DOCK_ITEM(item) + && GDL_DOCK_ITEM(item)->behavior & GDL_DOCK_ITEM_BEH_NEVER_FLOATING) + return; + + dock = NULL; + my_request.target = GDL_DOCK_OBJECT ( + gdl_dock_object_get_toplevel (request->applicant)); + my_request.position = GDL_DOCK_FLOATING; + + gdl_dock_item_preferred_size (GDL_DOCK_ITEM (request->applicant), &req); + my_request.rect.width = req.width; + my_request.rect.height = req.height; + + my_request.rect.x = root_x - GDL_DOCK_ITEM (request->applicant)->dragoff_x; + my_request.rect.y = root_y - GDL_DOCK_ITEM (request->applicant)->dragoff_y; + + /* setup extra docking information */ + if (G_IS_VALUE (&my_request.extra)) + g_value_unset (&my_request.extra); + + g_value_init (&my_request.extra, GDK_TYPE_RECTANGLE); + g_value_set_boxed (&my_request.extra, &my_request.rect); + } + /* if we want to enforce GDL_DOCK_ITEM_BEH_NEVER_FLOATING */ + /* the item must remain attached to the controller, otherwise */ + /* it could be inserted in another floating dock */ + /* so check for the flag at this moment */ + else if(GDL_IS_DOCK_ITEM(item) + && GDL_DOCK_ITEM(item)->behavior & GDL_DOCK_ITEM_BEH_NEVER_FLOATING + && dock != GDL_DOCK(master->controller)) + return; + + if (!(my_request.rect.x == request->rect.x && + my_request.rect.y == request->rect.y && + my_request.rect.width == request->rect.width && + my_request.rect.height == request->rect.height && + dock == master->_priv->rect_owner)) { + + /* erase the previous rectangle */ + if (master->_priv->rect_drawn) + gdl_dock_master_xor_rect (master); + } + + /* set the new values */ + *request = my_request; + master->_priv->rect_owner = dock; + + /* draw the previous rectangle */ + if (~master->_priv->rect_drawn) + gdl_dock_master_xor_rect (master); +} + +static void +_gdl_dock_master_foreach (gpointer key, + gpointer value, + gpointer user_data) +{ + (void)key; + struct { + GFunc function; + gpointer user_data; + } *data = user_data; + + (* data->function) (GTK_WIDGET (value), data->user_data); +} + +static void +gdl_dock_master_xor_rect (GdlDockMaster *master) +{ + gint8 dash_list [2]; + GdkWindow *window; + GdkRectangle *rect; + + if (!master->_priv || !master->_priv->drag_request) + return; + + master->_priv->rect_drawn = ~master->_priv->rect_drawn; + + if (master->_priv->rect_owner) { + gdl_dock_xor_rect (master->_priv->rect_owner, + &master->_priv->drag_request->rect); + return; + } + + rect = &master->_priv->drag_request->rect; + window = gdk_get_default_root_window (); + + if (!master->_priv->root_xor_gc) { + GdkGCValues values; + + values.function = GDK_INVERT; + values.subwindow_mode = GDK_INCLUDE_INFERIORS; + master->_priv->root_xor_gc = gdk_gc_new_with_values ( + window, &values, GDK_GC_FUNCTION | GDK_GC_SUBWINDOW); + }; + + gdk_gc_set_line_attributes (master->_priv->root_xor_gc, 1, + GDK_LINE_ON_OFF_DASH, + GDK_CAP_NOT_LAST, + GDK_JOIN_BEVEL); + + dash_list[0] = 1; + dash_list[1] = 1; + gdk_gc_set_dashes (master->_priv->root_xor_gc, 1, dash_list, 2); + + gdk_draw_rectangle (window, master->_priv->root_xor_gc, 0, + rect->x, rect->y, + rect->width, rect->height); + + gdk_gc_set_dashes (master->_priv->root_xor_gc, 0, dash_list, 2); + + gdk_draw_rectangle (window, master->_priv->root_xor_gc, 0, + rect->x + 1, rect->y + 1, + rect->width - 2, rect->height - 2); +} + +static void +gdl_dock_master_layout_changed (GdlDockMaster *master) +{ + g_return_if_fail (GDL_IS_DOCK_MASTER (master)); + + /* emit "layout-changed" on the controller to notify the user who + * normally shouldn't have access to us */ + if (master->controller) + g_signal_emit_by_name (master->controller, "layout-changed"); + + /* remove the idle handler if there is one */ + if (master->_priv->idle_layout_changed_id) { + g_source_remove (master->_priv->idle_layout_changed_id); + master->_priv->idle_layout_changed_id = 0; + } +} + +static gboolean +idle_emit_layout_changed (gpointer user_data) +{ + GdlDockMaster *master = user_data; + + g_return_val_if_fail (master && GDL_IS_DOCK_MASTER (master), FALSE); + + master->_priv->idle_layout_changed_id = 0; + g_signal_emit (master, master_signals [LAYOUT_CHANGED], 0); + + return FALSE; +} + +static void +item_dock_cb (GdlDockObject *object, + GdlDockObject *requestor, + GdlDockPlacement position, + GValue *other_data, + gpointer user_data) +{ + GdlDockMaster *master = user_data; + + g_return_if_fail (requestor && GDL_IS_DOCK_OBJECT (requestor)); + g_return_if_fail (master && GDL_IS_DOCK_MASTER (master)); + + /* here we are in fact interested in the requestor, since it's + * assumed that object will not change its visibility... for the + * requestor, however, could mean that it's being shown */ + if (!GDL_DOCK_OBJECT_IN_REFLOW (requestor) && + !GDL_DOCK_OBJECT_AUTOMATIC (requestor)) { + if (!master->_priv->idle_layout_changed_id) + master->_priv->idle_layout_changed_id = + g_idle_add (idle_emit_layout_changed, master); + } +} + +static void +item_detach_cb (GdlDockObject *object, + gboolean recursive, + gpointer user_data) +{ + GdlDockMaster *master = user_data; + + g_return_if_fail (object && GDL_IS_DOCK_OBJECT (object)); + g_return_if_fail (master && GDL_IS_DOCK_MASTER (master)); + + if (!GDL_DOCK_OBJECT_IN_REFLOW (object) && + !GDL_DOCK_OBJECT_AUTOMATIC (object)) { + if (!master->_priv->idle_layout_changed_id) + master->_priv->idle_layout_changed_id = + g_idle_add (idle_emit_layout_changed, master); + } +} + +static void +item_notify_cb (GdlDockObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + GdlDockMaster *master = user_data; + gint locked = COMPUTE_LOCKED (master); + gboolean item_locked; + + g_object_get (object, "locked", &item_locked, NULL); + + if (item_locked) { + g_hash_table_remove (master->_priv->unlocked_items, object); + g_hash_table_insert (master->_priv->locked_items, object, NULL); + } else { + g_hash_table_remove (master->_priv->locked_items, object); + g_hash_table_insert (master->_priv->unlocked_items, object, NULL); + } + + if (COMPUTE_LOCKED (master) != locked) + g_object_notify (G_OBJECT (master), "locked"); +} + +/* ----- Public interface ----- */ + +void +gdl_dock_master_add (GdlDockMaster *master, + GdlDockObject *object) +{ + g_return_if_fail (master != NULL && object != NULL); + + if (!GDL_DOCK_OBJECT_AUTOMATIC (object)) { + GdlDockObject *found_object; + + /* create a name for the object if it doesn't have one */ + if (!object->name) + /* directly set the name, since it's a construction only + property */ + object->name = g_strdup_printf ("__dock_%u", master->_priv->number++); + + /* add the object to our hash list */ + if ((found_object = g_hash_table_lookup (master->dock_objects, object->name))) { + g_warning (_("master %p: unable to add object %p[%s] to the hash. " + "There already is an item with that name (%p)."), + master, object, object->name, found_object); + } + else { + g_object_ref (object); + gtk_object_sink (GTK_OBJECT (object)); + g_hash_table_insert (master->dock_objects, g_strdup (object->name), object); + } + } + + if (GDL_IS_DOCK (object)) { + gboolean floating; + + /* if this is the first toplevel we are adding, name it controller */ + if (!master->toplevel_docks) + /* the dock should already have the ref */ + master->controller = object; + + /* add dock to the toplevel list */ + g_object_get (object, "floating", &floating, NULL); + if (floating) + master->toplevel_docks = g_list_prepend (master->toplevel_docks, object); + else + master->toplevel_docks = g_list_append (master->toplevel_docks, object); + + /* we are interested in the dock request this toplevel + * receives to update the layout */ + g_signal_connect (object, "dock", + G_CALLBACK (item_dock_cb), master); + + } + else if (GDL_IS_DOCK_ITEM (object)) { + /* we need to connect the item's signals */ + g_signal_connect (object, "dock_drag_begin", + G_CALLBACK (gdl_dock_master_drag_begin), master); + g_signal_connect (object, "dock_drag_motion", + G_CALLBACK (gdl_dock_master_drag_motion), master); + g_signal_connect (object, "dock_drag_end", + G_CALLBACK (gdl_dock_master_drag_end), master); + g_signal_connect (object, "dock", + G_CALLBACK (item_dock_cb), master); + g_signal_connect (object, "detach", + G_CALLBACK (item_detach_cb), master); + + /* register to "locked" notification if the item has a grip, + * and add the item to the corresponding hash */ + if (GDL_DOCK_ITEM_HAS_GRIP (GDL_DOCK_ITEM (object))) { + g_signal_connect (object, "notify::locked", + G_CALLBACK (item_notify_cb), master); + item_notify_cb (object, NULL, master); + } + + /* If the item is notebook, set the switcher style */ + if (GDL_IS_DOCK_NOTEBOOK (object) && + GDL_IS_SWITCHER (GDL_DOCK_ITEM (object)->child)) + { + g_object_set (GDL_DOCK_ITEM (object)->child, "switcher-style", + master->_priv->switcher_style, NULL); + } + + /* post a layout_changed emission if the item is not automatic + * (since it should be added to the items model) */ + if (!GDL_DOCK_OBJECT_AUTOMATIC (object)) { + if (!master->_priv->idle_layout_changed_id) + master->_priv->idle_layout_changed_id = + g_idle_add (idle_emit_layout_changed, master); + } + } +} + +void +gdl_dock_master_remove (GdlDockMaster *master, + GdlDockObject *object) +{ + g_return_if_fail (master != NULL && object != NULL); + + /* remove from locked/unlocked hashes and property change if + * that's the case */ + if (GDL_IS_DOCK_ITEM (object) && GDL_DOCK_ITEM_HAS_GRIP (GDL_DOCK_ITEM (object))) { + gint locked = COMPUTE_LOCKED (master); + if (g_hash_table_remove (master->_priv->locked_items, object) || + g_hash_table_remove (master->_priv->unlocked_items, object)) { + if (COMPUTE_LOCKED (master) != locked) + g_object_notify (G_OBJECT (master), "locked"); + } + } + + /* ref the master, since removing the controller could cause master disposal */ + g_object_ref (master); + + /* all the interesting stuff happens in _gdl_dock_master_remove */ + _gdl_dock_master_remove (object, master); + + /* post a layout_changed emission if the item is not automatic + * (since it should be removed from the items model) */ + if (!GDL_DOCK_OBJECT_AUTOMATIC (object)) { + if (!master->_priv->idle_layout_changed_id) + master->_priv->idle_layout_changed_id = + g_idle_add (idle_emit_layout_changed, master); + } + + /* balance ref count */ + g_object_unref (master); +} + +void +gdl_dock_master_foreach (GdlDockMaster *master, + GFunc function, + gpointer user_data) +{ + struct { + GFunc function; + gpointer user_data; + } data; + + g_return_if_fail (master != NULL && function != NULL); + + data.function = function; + data.user_data = user_data; + g_hash_table_foreach (master->dock_objects, _gdl_dock_master_foreach, &data); +} + +void +gdl_dock_master_foreach_toplevel (GdlDockMaster *master, + gboolean include_controller, + GFunc function, + gpointer user_data) +{ + GList *l; + + g_return_if_fail (master != NULL && function != NULL); + + for (l = master->toplevel_docks; l; ) { + GdlDockObject *object = GDL_DOCK_OBJECT (l->data); + l = l->next; + if (object != master->controller || include_controller) + (* function) (GTK_WIDGET (object), user_data); + } +} + +GdlDockObject * +gdl_dock_master_get_object (GdlDockMaster *master, + const gchar *nick_name) +{ + gpointer *found; + + g_return_val_if_fail (master != NULL, NULL); + + if (!nick_name) + return NULL; + + found = g_hash_table_lookup (master->dock_objects, nick_name); + + return found ? GDL_DOCK_OBJECT (found) : NULL; +} + +GdlDockObject * +gdl_dock_master_get_controller (GdlDockMaster *master) +{ + g_return_val_if_fail (master != NULL, NULL); + + return master->controller; +} + +void +gdl_dock_master_set_controller (GdlDockMaster *master, + GdlDockObject *new_controller) +{ + g_return_if_fail (master != NULL); + + if (new_controller) { + if (GDL_DOCK_OBJECT_AUTOMATIC (new_controller)) + g_warning (_("The new dock controller %p is automatic. Only manual " + "dock objects should be named controller."), new_controller); + + /* check that the controller is in the toplevel list */ + if (!g_list_find (master->toplevel_docks, new_controller)) + gdl_dock_master_add (master, new_controller); + master->controller = new_controller; + + } else { + master->controller = NULL; + /* no controller, no master */ + g_object_unref (master); + } +} + +static void +set_switcher_style_foreach (GtkWidget *obj, gpointer user_data) +{ + GdlSwitcherStyle style = GPOINTER_TO_INT (user_data); + + if (!GDL_IS_DOCK_ITEM (obj)) + return; + + if (GDL_IS_DOCK_NOTEBOOK (obj)) { + + GtkWidget *child = GDL_DOCK_ITEM (obj)->child; + if (GDL_IS_SWITCHER (child)) { + + g_object_set (child, "switcher-style", style, NULL); + } + } else if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT (obj))) { + + gtk_container_foreach (GTK_CONTAINER (obj), + set_switcher_style_foreach, + user_data); + } +} + +static void +gdl_dock_master_set_switcher_style (GdlDockMaster *master, + GdlSwitcherStyle switcher_style) +{ + GList *l; + g_return_if_fail (GDL_IS_DOCK_MASTER (master)); + + master->_priv->switcher_style = switcher_style; + for (l = master->toplevel_docks; l; l = l->next) { + GdlDock *dock = GDL_DOCK (l->data); + if (dock->root) + set_switcher_style_foreach (GTK_WIDGET (dock->root), + GINT_TO_POINTER (switcher_style)); + } + + /* just to be sure hidden items are set too */ + gdl_dock_master_foreach (master, (GFunc) set_switcher_style_foreach, + GINT_TO_POINTER (switcher_style)); +} diff --git a/src/libgdl/gdl-dock-master.h b/src/libgdl/gdl-dock-master.h new file mode 100644 index 000000000..72697b484 --- /dev/null +++ b/src/libgdl/gdl-dock-master.h @@ -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 + * + * 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 +#include +#include "libgdl/gdl-dock-object.h" + + +G_BEGIN_DECLS + +/* standard macros */ +#define GDL_TYPE_DOCK_MASTER (gdl_dock_master_get_type ()) +#define GDL_DOCK_MASTER(obj) (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_MASTER, GdlDockMaster)) +#define GDL_DOCK_MASTER_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_MASTER, GdlDockMasterClass)) +#define GDL_IS_DOCK_MASTER(obj) (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_MASTER)) +#define GDL_IS_DOCK_MASTER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_MASTER)) +#define GDL_DOCK_MASTER_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK_MASTER, GdlDockMasterClass)) + +/* data types & structures */ +typedef struct _GdlDockMaster GdlDockMaster; +typedef struct _GdlDockMasterClass GdlDockMasterClass; +typedef struct _GdlDockMasterPrivate GdlDockMasterPrivate; + +struct _GdlDockMaster { + GObject object; + + GHashTable *dock_objects; + GList *toplevel_docks; + GdlDockObject *controller; /* GUI root object */ + + gint dock_number; /* for toplevel dock numbering */ + + GdlDockMasterPrivate *_priv; +}; + +struct _GdlDockMasterClass { + GObjectClass parent_class; + + void (* layout_changed) (GdlDockMaster *master); +}; + +/* additional macros */ + +#define GDL_DOCK_OBJECT_GET_MASTER(object) \ + (GDL_DOCK_OBJECT (object)->master ? \ + GDL_DOCK_MASTER (GDL_DOCK_OBJECT (object)->master) : NULL) + +/* public interface */ + +GType gdl_dock_master_get_type (void); + +void gdl_dock_master_add (GdlDockMaster *master, + GdlDockObject *object); +void gdl_dock_master_remove (GdlDockMaster *master, + GdlDockObject *object); +void gdl_dock_master_foreach (GdlDockMaster *master, + GFunc function, + gpointer user_data); + +void gdl_dock_master_foreach_toplevel (GdlDockMaster *master, + gboolean include_controller, + GFunc function, + gpointer user_data); + +GdlDockObject *gdl_dock_master_get_object (GdlDockMaster *master, + const gchar *nick_name); + +GdlDockObject *gdl_dock_master_get_controller (GdlDockMaster *master); +void gdl_dock_master_set_controller (GdlDockMaster *master, + GdlDockObject *new_controller); + +G_END_DECLS + +#endif /* __GDL_DOCK_MASTER_H__ */ diff --git a/src/libgdl/gdl-dock-notebook.c b/src/libgdl/gdl-dock-notebook.c new file mode 100644 index 000000000..6fb931ac7 --- /dev/null +++ b/src/libgdl/gdl-dock-notebook.c @@ -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 + * + * 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 +#endif + +#include "gdl-i18n.h" +#include "gdl-switcher.h" + +#include "gdl-tools.h" +#include "gdl-dock-notebook.h" +#include "gdl-dock-tablabel.h" + + +/* Private prototypes */ + +static void gdl_dock_notebook_class_init (GdlDockNotebookClass *klass); +static void gdl_dock_notebook_instance_init (GdlDockNotebook *notebook); +static void gdl_dock_notebook_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gdl_dock_notebook_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static void gdl_dock_notebook_destroy (GtkObject *object); + +static void gdl_dock_notebook_add (GtkContainer *container, + GtkWidget *widget); +static void gdl_dock_notebook_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data); +static GType gdl_dock_notebook_child_type (GtkContainer *container); + +static void gdl_dock_notebook_dock (GdlDockObject *object, + GdlDockObject *requestor, + GdlDockPlacement position, + GValue *other_data); + +static void gdl_dock_notebook_switch_page_cb (GtkNotebook *nb, + GtkNotebookPage *page, + gint page_num, + gpointer data); + +static void gdl_dock_notebook_set_orientation (GdlDockItem *item, + GtkOrientation orientation); + +static gboolean gdl_dock_notebook_child_placement (GdlDockObject *object, + GdlDockObject *child, + GdlDockPlacement *placement); + +static void gdl_dock_notebook_present (GdlDockObject *object, + GdlDockObject *child); + +static gboolean gdl_dock_notebook_reorder (GdlDockObject *object, + GdlDockObject *requestor, + GdlDockPlacement new_position, + GValue *other_data); + + +/* Class variables and definitions */ + +enum { + PROP_0, + PROP_PAGE +}; + + +/* ----- Private functions ----- */ + +GDL_CLASS_BOILERPLATE (GdlDockNotebook, gdl_dock_notebook, GdlDockItem, GDL_TYPE_DOCK_ITEM) ; + +static void +gdl_dock_notebook_class_init (GdlDockNotebookClass *klass) +{ + static gboolean style_initialized = FALSE; + + GObjectClass *g_object_class; + GtkObjectClass *gtk_object_class; + GtkWidgetClass *widget_class; + GtkContainerClass *container_class; + GdlDockObjectClass *object_class; + GdlDockItemClass *item_class; + + g_object_class = G_OBJECT_CLASS (klass); + gtk_object_class = GTK_OBJECT_CLASS (klass); + widget_class = GTK_WIDGET_CLASS (klass); + container_class = GTK_CONTAINER_CLASS (klass); + object_class = GDL_DOCK_OBJECT_CLASS (klass); + item_class = GDL_DOCK_ITEM_CLASS (klass); + + g_object_class->set_property = gdl_dock_notebook_set_property; + g_object_class->get_property = gdl_dock_notebook_get_property; + + gtk_object_class->destroy = gdl_dock_notebook_destroy; + + container_class->add = gdl_dock_notebook_add; + container_class->forall = gdl_dock_notebook_forall; + container_class->child_type = gdl_dock_notebook_child_type; + + object_class->is_compound = TRUE; + object_class->dock = gdl_dock_notebook_dock; + object_class->child_placement = gdl_dock_notebook_child_placement; + object_class->present = gdl_dock_notebook_present; + object_class->reorder = gdl_dock_notebook_reorder; + + item_class->has_grip = FALSE; + item_class->set_orientation = gdl_dock_notebook_set_orientation; + + g_object_class_install_property ( + g_object_class, PROP_PAGE, + g_param_spec_int ("page", _("Page"), + _("The index of the current page"), + 0, G_MAXINT, + 0, + G_PARAM_READWRITE | + GDL_DOCK_PARAM_EXPORT | GDL_DOCK_PARAM_AFTER)); + + if (!style_initialized) { + style_initialized = TRUE; + + gtk_rc_parse_string ( + "style \"gdl-dock-notebook-default\" {\n" + "xthickness = 2\n" + "ythickness = 2\n" + "}\n" + "widget_class \"*.GtkNotebook.GdlDockItem\" " + "style : gtk \"gdl-dock-notebook-default\"\n"); + } +} + +static void +gdl_dock_notebook_notify_cb (GObject *g_object, + GParamSpec *pspec, + gpointer user_data) +{ + g_return_if_fail (user_data != NULL && GDL_IS_DOCK_NOTEBOOK (user_data)); + + /* chain the notify signal */ + g_object_notify (G_OBJECT (user_data), pspec->name); +} + +static gboolean +gdl_dock_notebook_button_cb (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) +{ + if (event->type == GDK_BUTTON_PRESS) + GDL_DOCK_ITEM_SET_FLAGS (user_data, GDL_DOCK_USER_ACTION); + else + GDL_DOCK_ITEM_UNSET_FLAGS (user_data, GDL_DOCK_USER_ACTION); + + return FALSE; +} + +static void +gdl_dock_notebook_instance_init (GdlDockNotebook *notebook) +{ + GdlDockItem *item; + + item = GDL_DOCK_ITEM (notebook); + + /* create the container notebook */ + item->child = gdl_switcher_new (); + gtk_widget_set_parent (item->child, GTK_WIDGET (notebook)); + gtk_notebook_set_tab_pos (GTK_NOTEBOOK (item->child), GTK_POS_BOTTOM); + g_signal_connect (item->child, "switch-page", + (GCallback) gdl_dock_notebook_switch_page_cb, (gpointer) item); + g_signal_connect (item->child, "notify::page", + (GCallback) gdl_dock_notebook_notify_cb, (gpointer) item); + g_signal_connect (item->child, "button-press-event", + (GCallback) gdl_dock_notebook_button_cb, (gpointer) item); + g_signal_connect (item->child, "button-release-event", + (GCallback) gdl_dock_notebook_button_cb, (gpointer) item); + gtk_notebook_set_scrollable (GTK_NOTEBOOK (item->child), TRUE); + gtk_widget_show (item->child); +} + +static void +gdl_dock_notebook_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdlDockItem *item = GDL_DOCK_ITEM (object); + + switch (prop_id) { + case PROP_PAGE: + if (item->child && GTK_IS_NOTEBOOK (item->child)) { + gtk_notebook_set_current_page (GTK_NOTEBOOK (item->child), + g_value_get_int (value)); + } + + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdl_dock_notebook_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdlDockItem *item = GDL_DOCK_ITEM (object); + + switch (prop_id) { + case PROP_PAGE: + if (item->child && GTK_IS_NOTEBOOK (item->child)) { + g_value_set_int (value, gtk_notebook_get_current_page + (GTK_NOTEBOOK (item->child))); + } + + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +gdl_dock_notebook_destroy (GtkObject *object) +{ + GdlDockItem *item = GDL_DOCK_ITEM (object); + + /* we need to call the virtual first, since in GdlDockDestroy our + children dock objects are detached */ + GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object)); + + /* after that we can remove the GtkNotebook */ + if (item->child) { + gtk_widget_unparent (item->child); + item->child = NULL; + }; +} + +static void +gdl_dock_notebook_switch_page_cb (GtkNotebook *nb, + GtkNotebookPage *page, + gint page_num, + gpointer data) +{ + GdlDockNotebook *notebook; + GtkWidget *tablabel; + + notebook = GDL_DOCK_NOTEBOOK (data); + + /* deactivate old tablabel */ + if (nb->cur_page) { + tablabel = gtk_notebook_get_tab_label ( + nb, gtk_notebook_get_nth_page ( + nb, gtk_notebook_get_current_page (nb))); + if (tablabel && GDL_IS_DOCK_TABLABEL (tablabel)) + gdl_dock_tablabel_deactivate (GDL_DOCK_TABLABEL (tablabel)); + }; + + /* activate new label */ + tablabel = gtk_notebook_get_tab_label ( + nb, gtk_notebook_get_nth_page (nb, page_num)); + if (tablabel && GDL_IS_DOCK_TABLABEL (tablabel)) + gdl_dock_tablabel_activate (GDL_DOCK_TABLABEL (tablabel)); + + if (GDL_DOCK_ITEM_USER_ACTION (notebook) && + GDL_DOCK_OBJECT (notebook)->master) + g_signal_emit_by_name (GDL_DOCK_OBJECT (notebook)->master, + "layout-changed"); +} + +static void +gdl_dock_notebook_add (GtkContainer *container, + GtkWidget *widget) +{ + g_return_if_fail (container != NULL && widget != NULL); + g_return_if_fail (GDL_IS_DOCK_NOTEBOOK (container)); + g_return_if_fail (GDL_IS_DOCK_ITEM (widget)); + + gdl_dock_object_dock (GDL_DOCK_OBJECT (container), + GDL_DOCK_OBJECT (widget), + GDL_DOCK_CENTER, + NULL); +} + +static void +gdl_dock_notebook_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) +{ + GdlDockItem *item; + + g_return_if_fail (container != NULL); + g_return_if_fail (GDL_IS_DOCK_NOTEBOOK (container)); + g_return_if_fail (callback != NULL); + + if (include_internals) { + /* use GdlDockItem's forall */ + GDL_CALL_PARENT (GTK_CONTAINER_CLASS, forall, + (container, include_internals, callback, callback_data)); + } + else { + item = GDL_DOCK_ITEM (container); + if (item->child) + gtk_container_foreach (GTK_CONTAINER (item->child), callback, callback_data); + } +} + +static GType +gdl_dock_notebook_child_type (GtkContainer *container) +{ + return GDL_TYPE_DOCK_ITEM; +} + +static void +gdl_dock_notebook_dock_child (GdlDockObject *requestor, + gpointer user_data) +{ + struct { + GdlDockObject *object; + GdlDockPlacement position; + GValue *other_data; + } *data = user_data; + + gdl_dock_object_dock (data->object, requestor, data->position, data->other_data); +} + +static void +gdl_dock_notebook_dock (GdlDockObject *object, + GdlDockObject *requestor, + GdlDockPlacement position, + GValue *other_data) +{ + g_return_if_fail (GDL_IS_DOCK_NOTEBOOK (object)); + g_return_if_fail (GDL_IS_DOCK_ITEM (requestor)); + + /* we only add support for GDL_DOCK_CENTER docking strategy here... for the rest + use our parent class' method */ + if (position == GDL_DOCK_CENTER) { + /* we can only dock simple (not compound) items */ + if (gdl_dock_object_is_compound (requestor)) { + struct { + GdlDockObject *object; + GdlDockPlacement position; + GValue *other_data; + } data; + + gdl_dock_object_freeze (requestor); + + data.object = object; + data.position = position; + data.other_data = other_data; + + gtk_container_foreach (GTK_CONTAINER (requestor), + (GtkCallback) gdl_dock_notebook_dock_child, &data); + + gdl_dock_object_thaw (requestor); + } + else { + GdlDockItem *item = GDL_DOCK_ITEM (object); + GdlDockItem *requestor_item = GDL_DOCK_ITEM (requestor); + gchar *long_name, *stock_id; + GdkPixbuf *pixbuf_icon; + GtkWidget *label; + gint position = -1; + + g_object_get (requestor_item, "long-name", &long_name, + "stock-id", &stock_id, "pixbuf-icon", &pixbuf_icon, NULL); + label = gdl_dock_item_get_tablabel (requestor_item); + if (!label) { + label = gtk_label_new (long_name); + gdl_dock_item_set_tablabel (requestor_item, label); + } +#if 0 + if (GDL_IS_DOCK_TABLABEL (label)) { + gdl_dock_tablabel_deactivate (GDL_DOCK_TABLABEL (label)); + /* hide the item grip, as we will use the tablabel's */ + gdl_dock_item_hide_grip (requestor_item); + } +#endif + + if (other_data && G_VALUE_HOLDS (other_data, G_TYPE_INT)) + position = g_value_get_int (other_data); + + position = gdl_switcher_insert_page (GDL_SWITCHER (item->child), + GTK_WIDGET (requestor), label, + long_name, long_name, + stock_id, pixbuf_icon, position); + + GDL_DOCK_OBJECT_SET_FLAGS (requestor, GDL_DOCK_ATTACHED); + + /* Set current page to the newly docked widget. set current page + * really doesn't work if the page widget is not shown + */ + gtk_widget_show (GTK_WIDGET (requestor)); + gtk_notebook_set_current_page (GTK_NOTEBOOK (item->child), + position); + g_free (long_name); + g_free (stock_id); + } + } + else + GDL_CALL_PARENT (GDL_DOCK_OBJECT_CLASS, dock, + (object, requestor, position, other_data)); +} + +static void +gdl_dock_notebook_set_orientation (GdlDockItem *item, + GtkOrientation orientation) +{ + if (item->child && GTK_IS_NOTEBOOK (item->child)) { + if (orientation == GTK_ORIENTATION_HORIZONTAL) + gtk_notebook_set_tab_pos (GTK_NOTEBOOK (item->child), GTK_POS_TOP); + else + gtk_notebook_set_tab_pos (GTK_NOTEBOOK (item->child), GTK_POS_LEFT); + } + + GDL_CALL_PARENT (GDL_DOCK_ITEM_CLASS, set_orientation, (item, orientation)); +} + +static gboolean +gdl_dock_notebook_child_placement (GdlDockObject *object, + GdlDockObject *child, + GdlDockPlacement *placement) +{ + GdlDockItem *item = GDL_DOCK_ITEM (object); + GdlDockPlacement pos = GDL_DOCK_NONE; + + if (item->child) { + GList *children, *l; + + children = gtk_container_get_children (GTK_CONTAINER (item->child)); + for (l = children; l; l = l->next) { + if (l->data == (gpointer) child) { + pos = GDL_DOCK_CENTER; + break; + } + } + g_list_free (children); + } + + if (pos != GDL_DOCK_NONE) { + if (placement) + *placement = pos; + return TRUE; + } + else + return FALSE; +} + +static void +gdl_dock_notebook_present (GdlDockObject *object, + GdlDockObject *child) +{ + GdlDockItem *item = GDL_DOCK_ITEM (object); + int i; + + i = gtk_notebook_page_num (GTK_NOTEBOOK (item->child), + GTK_WIDGET (child)); + if (i >= 0) + gtk_notebook_set_current_page (GTK_NOTEBOOK (item->child), i); + + GDL_CALL_PARENT (GDL_DOCK_OBJECT_CLASS, present, (object, child)); +} + +static gboolean +gdl_dock_notebook_reorder (GdlDockObject *object, + GdlDockObject *requestor, + GdlDockPlacement new_position, + GValue *other_data) +{ + GdlDockItem *item = GDL_DOCK_ITEM (object); + gint current_position, new_pos = -1; + gboolean handled = FALSE; + + if (item->child && new_position == GDL_DOCK_CENTER) { + current_position = gtk_notebook_page_num (GTK_NOTEBOOK (item->child), + GTK_WIDGET (requestor)); + if (current_position >= 0) { + handled = TRUE; + + if (other_data && G_VALUE_HOLDS (other_data, G_TYPE_INT)) + new_pos = g_value_get_int (other_data); + + gtk_notebook_reorder_child (GTK_NOTEBOOK (item->child), + GTK_WIDGET (requestor), + new_pos); + } + } + return handled; +} + +/* ----- Public interface ----- */ + +GtkWidget * +gdl_dock_notebook_new (void) +{ + GdlDockNotebook *notebook; + + notebook = GDL_DOCK_NOTEBOOK (g_object_new (GDL_TYPE_DOCK_NOTEBOOK, NULL)); + GDL_DOCK_OBJECT_UNSET_FLAGS (notebook, GDL_DOCK_AUTOMATIC); + + return GTK_WIDGET (notebook); +} + diff --git a/src/libgdl/gdl-dock-notebook.h b/src/libgdl/gdl-dock-notebook.h new file mode 100644 index 000000000..105da6c8c --- /dev/null +++ b/src/libgdl/gdl-dock-notebook.h @@ -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 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __GDL_DOCK_NOTEBOOK_H__ +#define __GDL_DOCK_NOTEBOOK_H__ + +#include "libgdl/gdl-dock-item.h" + +G_BEGIN_DECLS + +/* standard macros */ +#define GDL_TYPE_DOCK_NOTEBOOK (gdl_dock_notebook_get_type ()) +#define GDL_DOCK_NOTEBOOK(obj) (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_NOTEBOOK, GdlDockNotebook)) +#define GDL_DOCK_NOTEBOOK_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_NOTEBOOK, GdlDockNotebookClass)) +#define GDL_IS_DOCK_NOTEBOOK(obj) (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_NOTEBOOK)) +#define GDL_IS_DOCK_NOTEBOOK_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_NOTEBOOK)) +#define GDL_DOCK_NOTEBOOK_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK_NOTEBOOK, GdlDockNotebookClass)) + +/* data types & structures */ +typedef struct _GdlDockNotebook GdlDockNotebook; +typedef struct _GdlDockNotebookClass GdlDockNotebookClass; + +struct _GdlDockNotebook { + GdlDockItem item; +}; + +struct _GdlDockNotebookClass { + GdlDockItemClass parent_class; +}; + + +/* public interface */ + +GtkWidget *gdl_dock_notebook_new (void); + +GType gdl_dock_notebook_get_type (void); + +G_END_DECLS + +#endif + diff --git a/src/libgdl/gdl-dock-object.c b/src/libgdl/gdl-dock-object.c new file mode 100644 index 000000000..dadf072a0 --- /dev/null +++ b/src/libgdl/gdl-dock-object.c @@ -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 + * + * 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 +#endif + +#include "gdl-i18n.h" +#include +#include + +#include "gdl-tools.h" +#include "gdl-dock-object.h" +#include "gdl-dock-master.h" +#include "libgdltypebuiltins.h" +#include "libgdlmarshal.h" + +/* for later use by the registry */ +#include "gdl-dock.h" +#include "gdl-dock-item.h" +#include "gdl-dock-paned.h" +#include "gdl-dock-notebook.h" +#include "gdl-dock-placeholder.h" + + +/* ----- Private prototypes ----- */ + +static void gdl_dock_object_class_init (GdlDockObjectClass *klass); +static void gdl_dock_object_instance_init (GdlDockObject *object); + +static void gdl_dock_object_set_property (GObject *g_object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gdl_dock_object_get_property (GObject *g_object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void gdl_dock_object_finalize (GObject *g_object); + +static void gdl_dock_object_destroy (GtkObject *gtk_object); + +static void gdl_dock_object_show (GtkWidget *widget); +static void gdl_dock_object_hide (GtkWidget *widget); + +static void gdl_dock_object_real_detach (GdlDockObject *object, + gboolean recursive); +static void gdl_dock_object_real_reduce (GdlDockObject *object); +static void gdl_dock_object_dock_unimplemented (GdlDockObject *object, + GdlDockObject *requestor, + GdlDockPlacement position, + GValue *other_data); +static void gdl_dock_object_real_present (GdlDockObject *object, + GdlDockObject *child); + + +/* ----- Private data types and variables ----- */ + +enum { + PROP_0, + PROP_NAME, + PROP_LONG_NAME, + PROP_STOCK_ID, + PROP_PIXBUF_ICON, + PROP_MASTER, + PROP_EXPORT_PROPERTIES +}; + +enum { + DETACH, + DOCK, + LAST_SIGNAL +}; + +static guint gdl_dock_object_signals [LAST_SIGNAL] = { 0 }; + +/* ----- Private interface ----- */ + +GDL_CLASS_BOILERPLATE (GdlDockObject, gdl_dock_object, GtkContainer, GTK_TYPE_CONTAINER); + +static void +gdl_dock_object_class_init (GdlDockObjectClass *klass) +{ + GObjectClass *g_object_class; + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + GtkContainerClass *container_class; + + g_object_class = G_OBJECT_CLASS (klass); + object_class = GTK_OBJECT_CLASS (klass); + widget_class = GTK_WIDGET_CLASS (klass); + container_class = GTK_CONTAINER_CLASS (klass); + + g_object_class->set_property = gdl_dock_object_set_property; + g_object_class->get_property = gdl_dock_object_get_property; + g_object_class->finalize = gdl_dock_object_finalize; + + g_object_class_install_property ( + g_object_class, PROP_NAME, + g_param_spec_string (GDL_DOCK_NAME_PROPERTY, _("Name"), + _("Unique name for identifying the dock object"), + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + GDL_DOCK_PARAM_EXPORT)); + + g_object_class_install_property ( + g_object_class, PROP_LONG_NAME, + g_param_spec_string ("long-name", _("Long name"), + _("Human readable name for the dock object"), + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + g_object_class, PROP_STOCK_ID, + g_param_spec_string ("stock-id", _("Stock Icon"), + _("Stock icon for the dock object"), + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + g_object_class, PROP_PIXBUF_ICON, + g_param_spec_pointer ("pixbuf-icon", _("Pixbuf Icon"), + _("Pixbuf icon for the dock object"), + G_PARAM_READWRITE)); + + g_object_class_install_property ( + g_object_class, PROP_MASTER, + g_param_spec_object ("master", _("Dock master"), + _("Dock master this dock object is bound to"), + GDL_TYPE_DOCK_MASTER, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + object_class->destroy = gdl_dock_object_destroy; + + widget_class->show = gdl_dock_object_show; + widget_class->hide = gdl_dock_object_hide; + + klass->is_compound = TRUE; + + klass->detach = gdl_dock_object_real_detach; + klass->reduce = gdl_dock_object_real_reduce; + klass->dock_request = NULL; + klass->dock = gdl_dock_object_dock_unimplemented; + klass->reorder = NULL; + klass->present = gdl_dock_object_real_present; + klass->child_placement = NULL; + + gdl_dock_object_signals [DETACH] = + g_signal_new ("detach", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GdlDockObjectClass, detach), + NULL, + NULL, + gdl_marshal_VOID__BOOLEAN, + G_TYPE_NONE, + 1, + G_TYPE_BOOLEAN); + + gdl_dock_object_signals [DOCK] = + g_signal_new ("dock", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GdlDockObjectClass, dock), + NULL, + NULL, + gdl_marshal_VOID__OBJECT_ENUM_BOXED, + G_TYPE_NONE, + 3, + GDL_TYPE_DOCK_OBJECT, + GDL_TYPE_DOCK_PLACEMENT, + G_TYPE_VALUE); +} + +static void +gdl_dock_object_instance_init (GdlDockObject *object) +{ + object->flags = GDL_DOCK_AUTOMATIC; + object->freeze_count = 0; +} + +static void +gdl_dock_object_set_property (GObject *g_object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdlDockObject *object = GDL_DOCK_OBJECT (g_object); + + switch (prop_id) { + case PROP_NAME: + g_free (object->name); + object->name = g_value_dup_string (value); + break; + case PROP_LONG_NAME: + g_free (object->long_name); + object->long_name = g_value_dup_string (value); + break; + case PROP_STOCK_ID: + g_free (object->stock_id); + object->stock_id = g_value_dup_string (value); + break; + case PROP_PIXBUF_ICON: + object->pixbuf_icon = g_value_get_pointer (value); + break; + case PROP_MASTER: + if (g_value_get_object (value)) + gdl_dock_object_bind (object, g_value_get_object (value)); + else + gdl_dock_object_unbind (object); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdl_dock_object_get_property (GObject *g_object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdlDockObject *object = GDL_DOCK_OBJECT (g_object); + + switch (prop_id) { + case PROP_NAME: + g_value_set_string (value, object->name); + break; + case PROP_LONG_NAME: + g_value_set_string (value, object->long_name); + break; + case PROP_STOCK_ID: + g_value_set_string (value, object->stock_id); + break; + case PROP_PIXBUF_ICON: + g_value_set_pointer (value, object->pixbuf_icon); + break; + case PROP_MASTER: + g_value_set_object (value, object->master); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdl_dock_object_finalize (GObject *g_object) +{ + GdlDockObject *object; + + g_return_if_fail (g_object != NULL && GDL_IS_DOCK_OBJECT (g_object)); + + object = GDL_DOCK_OBJECT (g_object); + + g_free (object->name); + object->name = NULL; + g_free (object->long_name); + object->long_name = NULL; + g_free (object->stock_id); + object->stock_id = NULL; + object->pixbuf_icon = NULL; + + GDL_CALL_PARENT (G_OBJECT_CLASS, finalize, (g_object)); +} + +static void +gdl_dock_object_foreach_detach (GdlDockObject *object, + gpointer user_data) +{ + gdl_dock_object_detach (object, TRUE); +} + +static void +gdl_dock_object_destroy (GtkObject *gtk_object) +{ + GdlDockObject *object; + + g_return_if_fail (GDL_IS_DOCK_OBJECT (gtk_object)); + + object = GDL_DOCK_OBJECT (gtk_object); + if (gdl_dock_object_is_compound (object)) { + /* detach our dock object children if we have some, and even + if we are not attached, so they can get notification */ + gdl_dock_object_freeze (object); + gtk_container_foreach (GTK_CONTAINER (object), + (GtkCallback) gdl_dock_object_foreach_detach, + NULL); + object->reduce_pending = FALSE; + gdl_dock_object_thaw (object); + } + if (GDL_DOCK_OBJECT_ATTACHED (object)) { + /* detach ourselves */ + gdl_dock_object_detach (object, FALSE); + } + + /* finally unbind us */ + if (object->master) + gdl_dock_object_unbind (object); + + GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (gtk_object)); +} + +static void +gdl_dock_object_foreach_automatic (GdlDockObject *object, + gpointer user_data) +{ + void (* function) (GtkWidget *) = user_data; + + if (GDL_DOCK_OBJECT_AUTOMATIC (object)) + (* function) (GTK_WIDGET (object)); +} + +static void +gdl_dock_object_show (GtkWidget *widget) +{ + if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT (widget))) { + gtk_container_foreach (GTK_CONTAINER (widget), + (GtkCallback) gdl_dock_object_foreach_automatic, + gtk_widget_show); + } + GDL_CALL_PARENT (GTK_WIDGET_CLASS, show, (widget)); +} + +static void +gdl_dock_object_hide (GtkWidget *widget) +{ + if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT (widget))) { + gtk_container_foreach (GTK_CONTAINER (widget), + (GtkCallback) gdl_dock_object_foreach_automatic, + gtk_widget_hide); + } + GDL_CALL_PARENT (GTK_WIDGET_CLASS, hide, (widget)); +} + +static void +gdl_dock_object_real_detach (GdlDockObject *object, + gboolean recursive) +{ + GdlDockObject *parent; + GtkWidget *widget; + + g_return_if_fail (object != NULL); + + /* detach children */ + if (recursive && gdl_dock_object_is_compound (object)) { + gtk_container_foreach (GTK_CONTAINER (object), + (GtkCallback) gdl_dock_object_detach, + GINT_TO_POINTER (recursive)); + } + + /* detach the object itself */ + GDL_DOCK_OBJECT_UNSET_FLAGS (object, GDL_DOCK_ATTACHED); + parent = gdl_dock_object_get_parent_object (object); + widget = GTK_WIDGET (object); + if (widget->parent) + gtk_container_remove (GTK_CONTAINER (widget->parent), widget); + if (parent) + gdl_dock_object_reduce (parent); +} + +static void +gdl_dock_object_real_reduce (GdlDockObject *object) +{ + GdlDockObject *parent; + GList *children; + + g_return_if_fail (object != NULL); + + if (!gdl_dock_object_is_compound (object)) + return; + + parent = gdl_dock_object_get_parent_object (object); + children = gtk_container_get_children (GTK_CONTAINER (object)); + if (g_list_length (children) <= 1) { + GList *l; + + /* detach ourselves and then re-attach our children to our + current parent. if we are not currently attached, the + children are detached */ + if (parent) + gdl_dock_object_freeze (parent); + gdl_dock_object_freeze (object); + gdl_dock_object_detach (object, FALSE); + for (l = children; l; l = l->next) { + GdlDockObject *child = GDL_DOCK_OBJECT (l->data); + + g_object_ref (child); + GDL_DOCK_OBJECT_SET_FLAGS (child, GDL_DOCK_IN_REFLOW); + gdl_dock_object_detach (child, FALSE); + if (parent) + gtk_container_add (GTK_CONTAINER (parent), GTK_WIDGET (child)); + GDL_DOCK_OBJECT_UNSET_FLAGS (child, GDL_DOCK_IN_REFLOW); + g_object_unref (child); + } + /* sink the widget, so any automatic floating widget is destroyed */ + gtk_object_sink (GTK_OBJECT (object)); + /* don't reenter */ + object->reduce_pending = FALSE; + gdl_dock_object_thaw (object); + if (parent) + gdl_dock_object_thaw (parent); + } + g_list_free (children); +} + +static void +gdl_dock_object_dock_unimplemented (GdlDockObject *object, + GdlDockObject *requestor, + GdlDockPlacement position, + GValue *other_data) +{ + g_warning (_("Call to gdl_dock_object_dock in a dock object %p " + "(object type is %s) which hasn't implemented this method"), + object, G_OBJECT_TYPE_NAME (object)); +} + +static void +gdl_dock_object_real_present (GdlDockObject *object, + GdlDockObject *child) +{ + gtk_widget_show (GTK_WIDGET (object)); +} + + +/* ----- Public interface ----- */ + +gboolean +gdl_dock_object_is_compound (GdlDockObject *object) +{ + GdlDockObjectClass *klass; + + g_return_val_if_fail (object != NULL, FALSE); + g_return_val_if_fail (GDL_IS_DOCK_OBJECT (object), FALSE); + + klass = GDL_DOCK_OBJECT_GET_CLASS (object); + return klass->is_compound; +} + +void +gdl_dock_object_detach (GdlDockObject *object, + gboolean recursive) +{ + g_return_if_fail (object != NULL); + + if (!GDL_DOCK_OBJECT_ATTACHED (object)) + return; + + /* freeze the object to avoid reducing while detaching children */ + gdl_dock_object_freeze (object); + GDL_DOCK_OBJECT_SET_FLAGS (object, GDL_DOCK_IN_DETACH); + g_signal_emit (object, gdl_dock_object_signals [DETACH], 0, recursive); + GDL_DOCK_OBJECT_UNSET_FLAGS (object, GDL_DOCK_IN_DETACH); + gdl_dock_object_thaw (object); +} + +GdlDockObject * +gdl_dock_object_get_parent_object (GdlDockObject *object) +{ + GtkWidget *parent; + + g_return_val_if_fail (object != NULL, NULL); + + parent = GTK_WIDGET (object)->parent; + while (parent && !GDL_IS_DOCK_OBJECT (parent)) { + parent = parent->parent; + } + + return parent ? GDL_DOCK_OBJECT (parent) : NULL; +} + +void +gdl_dock_object_freeze (GdlDockObject *object) +{ + g_return_if_fail (object != NULL); + + if (object->freeze_count == 0) { + g_object_ref (object); /* dock objects shouldn't be + destroyed if they are frozen */ + } + object->freeze_count++; +} + +void +gdl_dock_object_thaw (GdlDockObject *object) +{ + g_return_if_fail (object != NULL); + g_return_if_fail (object->freeze_count > 0); + + object->freeze_count--; + if (object->freeze_count == 0) { + if (object->reduce_pending) { + object->reduce_pending = FALSE; + gdl_dock_object_reduce (object); + } + g_object_unref (object); + } +} + +void +gdl_dock_object_reduce (GdlDockObject *object) +{ + g_return_if_fail (object != NULL); + + if (GDL_DOCK_OBJECT_FROZEN (object)) { + object->reduce_pending = TRUE; + return; + } + + GDL_CALL_VIRTUAL (object, GDL_DOCK_OBJECT_GET_CLASS, reduce, (object)); +} + +gboolean +gdl_dock_object_dock_request (GdlDockObject *object, + gint x, + gint y, + GdlDockRequest *request) +{ + g_return_val_if_fail (object != NULL && request != NULL, FALSE); + + return GDL_CALL_VIRTUAL_WITH_DEFAULT (object, + GDL_DOCK_OBJECT_GET_CLASS, + dock_request, + (object, x, y, request), + FALSE); +} + +void +gdl_dock_object_dock (GdlDockObject *object, + GdlDockObject *requestor, + GdlDockPlacement position, + GValue *other_data) +{ + GdlDockObject *parent; + + g_return_if_fail (object != NULL && requestor != NULL); + + if (object == requestor) + return; + + if (!object->master) + g_warning (_("Dock operation requested in a non-bound object %p. " + "The application might crash"), object); + + if (!gdl_dock_object_is_bound (requestor)) + gdl_dock_object_bind (requestor, object->master); + + if (requestor->master != object->master) { + g_warning (_("Cannot dock %p to %p because they belong to different masters"), + requestor, object); + return; + } + + /* first, see if we can optimize things by reordering */ + if (position != GDL_DOCK_NONE) { + parent = gdl_dock_object_get_parent_object (object); + if (gdl_dock_object_reorder (object, requestor, position, other_data) || + (parent && gdl_dock_object_reorder (parent, requestor, position, other_data))) + return; + } + + /* freeze the object, since under some conditions it might be destroyed when + detaching the requestor */ + gdl_dock_object_freeze (object); + + /* detach the requestor before docking */ + g_object_ref (requestor); + if (GDL_DOCK_OBJECT_ATTACHED (requestor)) + gdl_dock_object_detach (requestor, FALSE); + + if (position != GDL_DOCK_NONE) + g_signal_emit (object, gdl_dock_object_signals [DOCK], 0, + requestor, position, other_data); + + g_object_unref (requestor); + gdl_dock_object_thaw (object); +} + +void +gdl_dock_object_bind (GdlDockObject *object, + GObject *master) +{ + g_return_if_fail (object != NULL && master != NULL); + g_return_if_fail (GDL_IS_DOCK_MASTER (master)); + + if (object->master == master) + /* nothing to do here */ + return; + + if (object->master) { + g_warning (_("Attempt to bind to %p an already bound dock object %p " + "(current master: %p)"), master, object, object->master); + return; + } + + gdl_dock_master_add (GDL_DOCK_MASTER (master), object); + object->master = master; + g_object_add_weak_pointer (master, (gpointer *) &object->master); + + g_object_notify (G_OBJECT (object), "master"); +} + +void +gdl_dock_object_unbind (GdlDockObject *object) +{ + g_return_if_fail (object != NULL); + + g_object_ref (object); + + /* detach the object first */ + if (GDL_DOCK_OBJECT_ATTACHED (object)) + gdl_dock_object_detach (object, TRUE); + + if (object->master) { + GObject *master = object->master; + g_object_remove_weak_pointer (master, (gpointer *) &object->master); + object->master = NULL; + gdl_dock_master_remove (GDL_DOCK_MASTER (master), object); + g_object_notify (G_OBJECT (object), "master"); + } + g_object_unref (object); +} + +gboolean +gdl_dock_object_is_bound (GdlDockObject *object) +{ + g_return_val_if_fail (object != NULL, FALSE); + return (object->master != NULL); +} + +gboolean +gdl_dock_object_reorder (GdlDockObject *object, + GdlDockObject *child, + GdlDockPlacement new_position, + GValue *other_data) +{ + g_return_val_if_fail (object != NULL && child != NULL, FALSE); + + return GDL_CALL_VIRTUAL_WITH_DEFAULT (object, + GDL_DOCK_OBJECT_GET_CLASS, + reorder, + (object, child, new_position, other_data), + FALSE); +} + +void +gdl_dock_object_present (GdlDockObject *object, + GdlDockObject *child) +{ + GdlDockObject *parent; + + g_return_if_fail (object != NULL && GDL_IS_DOCK_OBJECT (object)); + + parent = gdl_dock_object_get_parent_object (object); + if (parent) + /* chain the call to our parent */ + gdl_dock_object_present (parent, object); + + GDL_CALL_VIRTUAL (object, GDL_DOCK_OBJECT_GET_CLASS, present, (object, child)); +} + +/** + * gdl_dock_object_child_placement: + * @object: the dock object we are asking for child placement + * @child: the child of the @object we want the placement for + * @placement: where to return the placement information + * + * This function returns information about placement of a child dock + * object inside another dock object. The function returns %TRUE if + * @child is effectively a child of @object. @placement should + * normally be initially setup to %GDL_DOCK_NONE. If it's set to some + * other value, this function will not touch the stored value if the + * specified placement is "compatible" with the actual placement of + * the child. + * + * @placement can be %NULL, in which case the function simply tells if + * @child is attached to @object. + * + * Returns: %TRUE if @child is a child of @object. + */ +gboolean +gdl_dock_object_child_placement (GdlDockObject *object, + GdlDockObject *child, + GdlDockPlacement *placement) +{ + g_return_val_if_fail (object != NULL && child != NULL, FALSE); + + /* simple case */ + if (!gdl_dock_object_is_compound (object)) + return FALSE; + + return GDL_CALL_VIRTUAL_WITH_DEFAULT (object, GDL_DOCK_OBJECT_GET_CLASS, + child_placement, + (object, child, placement), + FALSE); +} + + +/* ----- dock param type functions start here ------ */ + +static void +gdl_dock_param_export_int (const GValue *src, + GValue *dst) +{ + dst->data [0].v_pointer = g_strdup_printf ("%d", src->data [0].v_int); +} + +static void +gdl_dock_param_export_uint (const GValue *src, + GValue *dst) +{ + dst->data [0].v_pointer = g_strdup_printf ("%u", src->data [0].v_uint); +} + +static void +gdl_dock_param_export_string (const GValue *src, + GValue *dst) +{ + dst->data [0].v_pointer = g_strdup (src->data [0].v_pointer); +} + +static void +gdl_dock_param_export_bool (const GValue *src, + GValue *dst) +{ + dst->data [0].v_pointer = g_strdup_printf ("%s", src->data [0].v_int ? "yes" : "no"); +} + +static void +gdl_dock_param_export_placement (const GValue *src, + GValue *dst) +{ + switch (src->data [0].v_int) { + case GDL_DOCK_NONE: + dst->data [0].v_pointer = g_strdup (""); + break; + case GDL_DOCK_TOP: + dst->data [0].v_pointer = g_strdup ("top"); + break; + case GDL_DOCK_BOTTOM: + dst->data [0].v_pointer = g_strdup ("bottom"); + break; + case GDL_DOCK_LEFT: + dst->data [0].v_pointer = g_strdup ("left"); + break; + case GDL_DOCK_RIGHT: + dst->data [0].v_pointer = g_strdup ("right"); + break; + case GDL_DOCK_CENTER: + dst->data [0].v_pointer = g_strdup ("center"); + break; + case GDL_DOCK_FLOATING: + dst->data [0].v_pointer = g_strdup ("floating"); + break; + } +} + +static void +gdl_dock_param_import_int (const GValue *src, + GValue *dst) +{ + dst->data [0].v_int = atoi (src->data [0].v_pointer); +} + +static void +gdl_dock_param_import_uint (const GValue *src, + GValue *dst) +{ + dst->data [0].v_uint = (guint) atoi (src->data [0].v_pointer); +} + +static void +gdl_dock_param_import_string (const GValue *src, + GValue *dst) +{ + dst->data [0].v_pointer = g_strdup (src->data [0].v_pointer); +} + +static void +gdl_dock_param_import_bool (const GValue *src, + GValue *dst) +{ + dst->data [0].v_int = !strcmp (src->data [0].v_pointer, "yes"); +} + +static void +gdl_dock_param_import_placement (const GValue *src, + GValue *dst) +{ + if (!strcmp (src->data [0].v_pointer, "top")) + dst->data [0].v_int = GDL_DOCK_TOP; + else if (!strcmp (src->data [0].v_pointer, "bottom")) + dst->data [0].v_int = GDL_DOCK_BOTTOM; + else if (!strcmp (src->data [0].v_pointer, "center")) + dst->data [0].v_int = GDL_DOCK_CENTER; + else if (!strcmp (src->data [0].v_pointer, "left")) + dst->data [0].v_int = GDL_DOCK_LEFT; + else if (!strcmp (src->data [0].v_pointer, "right")) + dst->data [0].v_int = GDL_DOCK_RIGHT; + else if (!strcmp (src->data [0].v_pointer, "floating")) + dst->data [0].v_int = GDL_DOCK_FLOATING; + else + dst->data [0].v_int = GDL_DOCK_NONE; +} + +GType +gdl_dock_param_get_type (void) +{ + static GType our_type = 0; + + if (our_type == 0) { + GTypeInfo tinfo = { 0, }; + our_type = g_type_register_static (G_TYPE_STRING, "GdlDockParam", &tinfo, 0); + + /* register known transform functions */ + /* exporters */ + g_value_register_transform_func (G_TYPE_INT, our_type, gdl_dock_param_export_int); + g_value_register_transform_func (G_TYPE_UINT, our_type, gdl_dock_param_export_uint); + g_value_register_transform_func (G_TYPE_STRING, our_type, gdl_dock_param_export_string); + g_value_register_transform_func (G_TYPE_BOOLEAN, our_type, gdl_dock_param_export_bool); + g_value_register_transform_func (GDL_TYPE_DOCK_PLACEMENT, our_type, gdl_dock_param_export_placement); + /* importers */ + g_value_register_transform_func (our_type, G_TYPE_INT, gdl_dock_param_import_int); + g_value_register_transform_func (our_type, G_TYPE_UINT, gdl_dock_param_import_uint); + g_value_register_transform_func (our_type, G_TYPE_STRING, gdl_dock_param_import_string); + g_value_register_transform_func (our_type, G_TYPE_BOOLEAN, gdl_dock_param_import_bool); + g_value_register_transform_func (our_type, GDL_TYPE_DOCK_PLACEMENT, gdl_dock_param_import_placement); + } + + return our_type; +} + +/* -------------- nick <-> type conversion functions --------------- */ + +static GRelation *dock_register = NULL; + +enum { + INDEX_NICK = 0, + INDEX_TYPE +}; + +static void +gdl_dock_object_register_init (void) +{ + if (dock_register) + return; + + /* FIXME: i don't know if GRelation is efficient */ + dock_register = g_relation_new (2); + g_relation_index (dock_register, INDEX_NICK, g_str_hash, g_str_equal); + g_relation_index (dock_register, INDEX_TYPE, g_direct_hash, g_direct_equal); + + /* add known types */ + g_relation_insert (dock_register, "dock", (gpointer) GDL_TYPE_DOCK); + g_relation_insert (dock_register, "item", (gpointer) GDL_TYPE_DOCK_ITEM); + g_relation_insert (dock_register, "paned", (gpointer) GDL_TYPE_DOCK_PANED); + g_relation_insert (dock_register, "notebook", (gpointer) GDL_TYPE_DOCK_NOTEBOOK); + g_relation_insert (dock_register, "placeholder", (gpointer) GDL_TYPE_DOCK_PLACEHOLDER); +} + +G_CONST_RETURN gchar * +gdl_dock_object_nick_from_type (GType type) +{ + GTuples *tuples; + gchar *nick = NULL; + + if (!dock_register) + gdl_dock_object_register_init (); + + if (g_relation_count (dock_register, (gpointer) type, INDEX_TYPE) > 0) { + tuples = g_relation_select (dock_register, (gpointer) type, INDEX_TYPE); + nick = (gchar *) g_tuples_index (tuples, 0, INDEX_NICK); + g_tuples_destroy (tuples); + } + + return nick ? nick : g_type_name (type); +} + +GType +gdl_dock_object_type_from_nick (const gchar *nick) +{ + GTuples *tuples; + GType type = G_TYPE_NONE; + + if (!dock_register) + gdl_dock_object_register_init (); + + if (g_relation_count (dock_register, (gpointer) nick, INDEX_NICK) > 0) { + tuples = g_relation_select (dock_register, (gpointer) nick, INDEX_NICK); + type = (GType) g_tuples_index (tuples, 0, INDEX_TYPE); + g_tuples_destroy (tuples); + } + else { + /* try searching in the glib type system */ + type = g_type_from_name (nick); + } + + return type; +} + +GType +gdl_dock_object_set_type_for_nick (const gchar *nick, + GType type) +{ + GType old_type = G_TYPE_NONE; + + if (!dock_register) + gdl_dock_object_register_init (); + + g_return_val_if_fail (g_type_is_a (type, GDL_TYPE_DOCK_OBJECT), G_TYPE_NONE); + + if (g_relation_count (dock_register, (gpointer) nick, INDEX_NICK) > 0) { + old_type = gdl_dock_object_type_from_nick (nick); + g_relation_delete (dock_register, (gpointer) nick, INDEX_NICK); + } + + g_relation_insert (dock_register, nick, type); + + return old_type; +} + diff --git a/src/libgdl/gdl-dock-object.h b/src/libgdl/gdl-dock-object.h new file mode 100644 index 000000000..f21418302 --- /dev/null +++ b/src/libgdl/gdl-dock-object.h @@ -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 + * + * 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 + +G_BEGIN_DECLS + +/* standard macros */ +#define GDL_TYPE_DOCK_OBJECT (gdl_dock_object_get_type ()) +#define GDL_DOCK_OBJECT(obj) (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_OBJECT, GdlDockObject)) +#define GDL_DOCK_OBJECT_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_OBJECT, GdlDockObjectClass)) +#define GDL_IS_DOCK_OBJECT(obj) (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_OBJECT)) +#define GDL_IS_DOCK_OBJECT_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_OBJECT)) +#define GDL_DOCK_OBJECT_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK_OBJECT, GdlDockObjectClass)) + +/* data types & structures */ +typedef enum { + /* the parameter is to be exported for later layout rebuilding */ + GDL_DOCK_PARAM_EXPORT = 1 << G_PARAM_USER_SHIFT, + /* the parameter must be set after adding the children objects */ + GDL_DOCK_PARAM_AFTER = 1 << (G_PARAM_USER_SHIFT + 1) +} GdlDockParamFlags; + +#define GDL_DOCK_NAME_PROPERTY "name" +#define GDL_DOCK_MASTER_PROPERTY "master" + +typedef enum { + GDL_DOCK_AUTOMATIC = 1 << 0, + GDL_DOCK_ATTACHED = 1 << 1, + GDL_DOCK_IN_REFLOW = 1 << 2, + GDL_DOCK_IN_DETACH = 1 << 3 +} GdlDockObjectFlags; + +#define GDL_DOCK_OBJECT_FLAGS_SHIFT 8 + +typedef enum { + GDL_DOCK_NONE = 0, + GDL_DOCK_TOP, + GDL_DOCK_BOTTOM, + GDL_DOCK_RIGHT, + GDL_DOCK_LEFT, + GDL_DOCK_CENTER, + GDL_DOCK_FLOATING +} GdlDockPlacement; + +typedef struct _GdlDockObject GdlDockObject; +typedef struct _GdlDockObjectClass GdlDockObjectClass; +typedef struct _GdlDockRequest GdlDockRequest; + +struct _GdlDockRequest { + GdlDockObject *applicant; + GdlDockObject *target; + GdlDockPlacement position; + GdkRectangle rect; + GValue extra; +}; + +struct _GdlDockObject { + GtkContainer container; + + GdlDockObjectFlags flags; + gint freeze_count; + + GObject *master; + gchar *name; + gchar *long_name; + gchar *stock_id; + GdkPixbuf *pixbuf_icon; + + gboolean reduce_pending; +}; + +struct _GdlDockObjectClass { + GtkContainerClass parent_class; + + gboolean is_compound; + + void (* detach) (GdlDockObject *object, + gboolean recursive); + void (* reduce) (GdlDockObject *object); + + gboolean (* dock_request) (GdlDockObject *object, + gint x, + gint y, + GdlDockRequest *request); + + void (* dock) (GdlDockObject *object, + GdlDockObject *requestor, + GdlDockPlacement position, + GValue *other_data); + + gboolean (* reorder) (GdlDockObject *object, + GdlDockObject *child, + GdlDockPlacement new_position, + GValue *other_data); + + void (* present) (GdlDockObject *object, + GdlDockObject *child); + + gboolean (* child_placement) (GdlDockObject *object, + GdlDockObject *child, + GdlDockPlacement *placement); +}; + +/* additional macros */ +#define GDL_DOCK_OBJECT_FLAGS(obj) (GDL_DOCK_OBJECT (obj)->flags) +#define GDL_DOCK_OBJECT_AUTOMATIC(obj) \ + ((GDL_DOCK_OBJECT_FLAGS (obj) & GDL_DOCK_AUTOMATIC) != 0) +#define GDL_DOCK_OBJECT_ATTACHED(obj) \ + ((GDL_DOCK_OBJECT_FLAGS (obj) & GDL_DOCK_ATTACHED) != 0) +#define GDL_DOCK_OBJECT_IN_REFLOW(obj) \ + ((GDL_DOCK_OBJECT_FLAGS (obj) & GDL_DOCK_IN_REFLOW) != 0) +#define GDL_DOCK_OBJECT_IN_DETACH(obj) \ + ((GDL_DOCK_OBJECT_FLAGS (obj) & GDL_DOCK_IN_DETACH) != 0) + +#define GDL_DOCK_OBJECT_SET_FLAGS(obj,flag) \ + G_STMT_START { (GDL_DOCK_OBJECT_FLAGS (obj) |= (flag)); } G_STMT_END +#define GDL_DOCK_OBJECT_UNSET_FLAGS(obj,flag) \ + G_STMT_START { (GDL_DOCK_OBJECT_FLAGS (obj) &= ~(flag)); } G_STMT_END + +#define GDL_DOCK_OBJECT_FROZEN(obj) (GDL_DOCK_OBJECT (obj)->freeze_count > 0) + + +/* public interface */ + +GType gdl_dock_object_get_type (void); + +gboolean gdl_dock_object_is_compound (GdlDockObject *object); + +void gdl_dock_object_detach (GdlDockObject *object, + gboolean recursive); + +GdlDockObject *gdl_dock_object_get_parent_object (GdlDockObject *object); + +void gdl_dock_object_freeze (GdlDockObject *object); +void gdl_dock_object_thaw (GdlDockObject *object); + +void gdl_dock_object_reduce (GdlDockObject *object); + +gboolean gdl_dock_object_dock_request (GdlDockObject *object, + gint x, + gint y, + GdlDockRequest *request); +void gdl_dock_object_dock (GdlDockObject *object, + GdlDockObject *requestor, + GdlDockPlacement position, + GValue *other_data); + +void gdl_dock_object_bind (GdlDockObject *object, + GObject *master); +void gdl_dock_object_unbind (GdlDockObject *object); +gboolean gdl_dock_object_is_bound (GdlDockObject *object); + +gboolean gdl_dock_object_reorder (GdlDockObject *object, + GdlDockObject *child, + GdlDockPlacement new_position, + GValue *other_data); + +void gdl_dock_object_present (GdlDockObject *object, + GdlDockObject *child); + +gboolean gdl_dock_object_child_placement (GdlDockObject *object, + GdlDockObject *child, + GdlDockPlacement *placement); + +/* other types */ + +/* this type derives from G_TYPE_STRING and is meant to be the basic + type for serializing object parameters which are exported + (i.e. those that are needed for layout rebuilding) */ +#define GDL_TYPE_DOCK_PARAM (gdl_dock_param_get_type ()) + +GType gdl_dock_param_get_type (void); + +/* functions for setting/retrieving nick names for serializing GdlDockObject types */ +G_CONST_RETURN gchar *gdl_dock_object_nick_from_type (GType type); +GType gdl_dock_object_type_from_nick (const gchar *nick); +GType gdl_dock_object_set_type_for_nick (const gchar *nick, + GType type); + + +/* helper macros */ +#define GDL_TRACE_OBJECT(object, format, args...) \ + G_STMT_START { \ + g_log (G_LOG_DOMAIN, \ + G_LOG_LEVEL_DEBUG, \ + "%s:%d (%s) %s [%p %d%s:%d]: "format, \ + __FILE__, \ + __LINE__, \ + __PRETTY_FUNCTION__, \ + G_OBJECT_TYPE_NAME (object), object, \ + G_OBJECT (object)->ref_count, \ + (GTK_IS_OBJECT (object) && GTK_OBJECT_FLOATING (object)) ? "(float)" : "", \ + GDL_IS_DOCK_OBJECT (object) ? GDL_DOCK_OBJECT (object)->freeze_count : -1, \ + ##args); } G_STMT_END + + + +G_END_DECLS + +#endif /* __GDL_DOCK_OBJECT_H__ */ + diff --git a/src/libgdl/gdl-dock-paned.c b/src/libgdl/gdl-dock-paned.c new file mode 100644 index 000000000..a176f1f7d --- /dev/null +++ b/src/libgdl/gdl-dock-paned.c @@ -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 + * + * 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 +#endif + +#include "gdl-i18n.h" +#include +#include +#include + +#include "gdl-tools.h" +#include "gdl-dock-paned.h" + + +/* Private prototypes */ + +static void gdl_dock_paned_class_init (GdlDockPanedClass *klass); +static void gdl_dock_paned_instance_init (GdlDockPaned *paned); +static GObject *gdl_dock_paned_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_param); +static void gdl_dock_paned_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gdl_dock_paned_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static void gdl_dock_paned_destroy (GtkObject *object); + +static void gdl_dock_paned_add (GtkContainer *container, + GtkWidget *widget); +static void gdl_dock_paned_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data); +static GType gdl_dock_paned_child_type (GtkContainer *container); + +static gboolean gdl_dock_paned_dock_request (GdlDockObject *object, + gint x, + gint y, + GdlDockRequest *request); +static void gdl_dock_paned_dock (GdlDockObject *object, + GdlDockObject *requestor, + GdlDockPlacement position, + GValue *other_data); + +static void gdl_dock_paned_set_orientation (GdlDockItem *item, + GtkOrientation orientation); + +static gboolean gdl_dock_paned_child_placement (GdlDockObject *object, + GdlDockObject *child, + GdlDockPlacement *placement); + + +/* ----- Class variables and definitions ----- */ + +#define SPLIT_RATIO 0.3 + +enum { + PROP_0, + PROP_POSITION +}; + + +/* ----- Private functions ----- */ + +GDL_CLASS_BOILERPLATE (GdlDockPaned, gdl_dock_paned, GdlDockItem, GDL_TYPE_DOCK_ITEM); + +static void +gdl_dock_paned_class_init (GdlDockPanedClass *klass) +{ + GObjectClass *g_object_class; + GtkObjectClass *gtk_object_class; + GtkWidgetClass *widget_class; + GtkContainerClass *container_class; + GdlDockObjectClass *object_class; + GdlDockItemClass *item_class; + + g_object_class = G_OBJECT_CLASS (klass); + gtk_object_class = GTK_OBJECT_CLASS (klass); + widget_class = GTK_WIDGET_CLASS (klass); + container_class = GTK_CONTAINER_CLASS (klass); + object_class = GDL_DOCK_OBJECT_CLASS (klass); + item_class = GDL_DOCK_ITEM_CLASS (klass); + + g_object_class->set_property = gdl_dock_paned_set_property; + g_object_class->get_property = gdl_dock_paned_get_property; + g_object_class->constructor = gdl_dock_paned_constructor; + + gtk_object_class->destroy = gdl_dock_paned_destroy; + + container_class->add = gdl_dock_paned_add; + container_class->forall = gdl_dock_paned_forall; + container_class->child_type = gdl_dock_paned_child_type; + + object_class->is_compound = TRUE; + + object_class->dock_request = gdl_dock_paned_dock_request; + object_class->dock = gdl_dock_paned_dock; + object_class->child_placement = gdl_dock_paned_child_placement; + + item_class->has_grip = FALSE; + item_class->set_orientation = gdl_dock_paned_set_orientation; + + g_object_class_install_property ( + g_object_class, PROP_POSITION, + g_param_spec_uint ("position", _("Position"), + _("Position of the divider in pixels"), + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + GDL_DOCK_PARAM_EXPORT | GDL_DOCK_PARAM_AFTER)); +} + +static void +gdl_dock_paned_instance_init (GdlDockPaned *paned) +{ + paned->position_changed = FALSE; +} + +static void +gdl_dock_paned_notify_cb (GObject *g_object, + GParamSpec *pspec, + gpointer user_data) +{ + GdlDockPaned *paned; + + g_return_if_fail (user_data != NULL && GDL_IS_DOCK_PANED (user_data)); + + /* chain the notification to the GdlDockPaned */ + g_object_notify (G_OBJECT (user_data), pspec->name); + + paned = GDL_DOCK_PANED (user_data); + + if (GDL_DOCK_ITEM_USER_ACTION (user_data) && !strcmp (pspec->name, "position")) + paned->position_changed = TRUE; +} + +static gboolean +gdl_dock_paned_button_cb (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) +{ + GdlDockPaned *paned; + + g_return_val_if_fail (user_data != NULL && GDL_IS_DOCK_PANED (user_data), FALSE); + + paned = GDL_DOCK_PANED (user_data); + if (event->button == 1) { + if (event->type == GDK_BUTTON_PRESS) + GDL_DOCK_ITEM_SET_FLAGS (user_data, GDL_DOCK_USER_ACTION); + else { + GDL_DOCK_ITEM_UNSET_FLAGS (user_data, GDL_DOCK_USER_ACTION); + if (paned->position_changed) { + /* emit pending layout changed signal to track separator position */ + if (GDL_DOCK_OBJECT (paned)->master) + g_signal_emit_by_name (GDL_DOCK_OBJECT (paned)->master, "layout-changed"); + paned->position_changed = FALSE; + } + } + } + + return FALSE; +} + +static void +gdl_dock_paned_create_child (GdlDockPaned *paned, + GtkOrientation orientation) +{ + GdlDockItem *item; + + item = GDL_DOCK_ITEM (paned); + + if (item->child) + gtk_widget_unparent (GTK_WIDGET (item->child)); + + /* create the container paned */ + if (orientation == GTK_ORIENTATION_HORIZONTAL) + item->child = gtk_hpaned_new (); + else + item->child = gtk_vpaned_new (); + + /* get notification for propagation */ + g_signal_connect (item->child, "notify::position", + (GCallback) gdl_dock_paned_notify_cb, (gpointer) item); + g_signal_connect (item->child, "button-press-event", + (GCallback) gdl_dock_paned_button_cb, (gpointer) item); + g_signal_connect (item->child, "button-release-event", + (GCallback) gdl_dock_paned_button_cb, (gpointer) item); + + gtk_widget_set_parent (item->child, GTK_WIDGET (item)); + gtk_widget_show (item->child); +} + +static GObject * +gdl_dock_paned_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_param) +{ + GObject *g_object; + + g_object = GDL_CALL_PARENT_WITH_DEFAULT (G_OBJECT_CLASS, + constructor, + (type, + n_construct_properties, + construct_param), + NULL); + if (g_object) { + GdlDockItem *item = GDL_DOCK_ITEM (g_object); + + if (!item->child) + gdl_dock_paned_create_child (GDL_DOCK_PANED (g_object), + item->orientation); + /* otherwise, the orientation was set as a construction + parameter and the child is already created */ + } + + return g_object; +} + +static void +gdl_dock_paned_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdlDockItem *item = GDL_DOCK_ITEM (object); + + switch (prop_id) { + case PROP_POSITION: + if (item->child && GTK_IS_PANED (item->child)) + gtk_paned_set_position (GTK_PANED (item->child), + g_value_get_uint (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdl_dock_paned_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdlDockItem *item = GDL_DOCK_ITEM (object); + + switch (prop_id) { + case PROP_POSITION: + if (item->child && GTK_IS_PANED (item->child)) + g_value_set_uint (value, + gtk_paned_get_position (GTK_PANED (item->child))); + else + g_value_set_uint (value, 0); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdl_dock_paned_destroy (GtkObject *object) +{ + GdlDockItem *item = GDL_DOCK_ITEM (object); + + /* we need to call the virtual first, since in GdlDockDestroy our + children dock objects are detached */ + GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object)); + + /* after that we can remove the GtkNotebook */ + if (item->child) { + gtk_widget_unparent (item->child); + item->child = NULL; + }; +} + +static void +gdl_dock_paned_add (GtkContainer *container, + GtkWidget *widget) +{ + GdlDockItem *item; + GtkPaned *paned; + GdlDockPlacement pos = GDL_DOCK_NONE; + + g_return_if_fail (container != NULL && widget != NULL); + g_return_if_fail (GDL_IS_DOCK_PANED (container)); + g_return_if_fail (GDL_IS_DOCK_ITEM (widget)); + + item = GDL_DOCK_ITEM (container); + g_return_if_fail (item->child != NULL); + paned = GTK_PANED (item->child); + g_return_if_fail (!paned->child1 || !paned->child2); + + if (!paned->child1) + pos = item->orientation == GTK_ORIENTATION_HORIZONTAL ? + GDL_DOCK_LEFT : GDL_DOCK_TOP; + else if (!paned->child2) + pos = item->orientation == GTK_ORIENTATION_HORIZONTAL ? + GDL_DOCK_RIGHT : GDL_DOCK_BOTTOM; + + if (pos != GDL_DOCK_NONE) + gdl_dock_object_dock (GDL_DOCK_OBJECT (container), + GDL_DOCK_OBJECT (widget), + pos, NULL); +} + +static void +gdl_dock_paned_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) +{ + GdlDockItem *item; + + g_return_if_fail (container != NULL); + g_return_if_fail (GDL_IS_DOCK_PANED (container)); + g_return_if_fail (callback != NULL); + + if (include_internals) { + /* use GdlDockItem's forall */ + GDL_CALL_PARENT (GTK_CONTAINER_CLASS, forall, + (container, include_internals, callback, callback_data)); + } + else { + item = GDL_DOCK_ITEM (container); + if (item->child) + gtk_container_foreach (GTK_CONTAINER (item->child), callback, callback_data); + } +} + +static GType +gdl_dock_paned_child_type (GtkContainer *container) +{ + GdlDockItem *item = GDL_DOCK_ITEM (container); + + if (gtk_container_child_type (GTK_CONTAINER (item->child)) == G_TYPE_NONE) + return G_TYPE_NONE; + else + return GDL_TYPE_DOCK_ITEM; +} + +static void +gdl_dock_paned_request_foreach (GdlDockObject *object, + gpointer user_data) +{ + struct { + gint x, y; + GdlDockRequest *request; + gboolean may_dock; + } *data = user_data; + + GdlDockRequest my_request; + gboolean may_dock; + + my_request = *data->request; + may_dock = gdl_dock_object_dock_request (object, data->x, data->y, &my_request); + if (may_dock) { + data->may_dock = TRUE; + *data->request = my_request; + } +} + +static gboolean +gdl_dock_paned_dock_request (GdlDockObject *object, + gint x, + gint y, + GdlDockRequest *request) +{ + GdlDockItem *item; + guint bw; + gint rel_x, rel_y; + GtkAllocation *alloc; + gboolean may_dock = FALSE; + GdlDockRequest my_request; + + g_return_val_if_fail (GDL_IS_DOCK_ITEM (object), FALSE); + + /* we get (x,y) in our allocation coordinates system */ + + item = GDL_DOCK_ITEM (object); + + /* Get item's allocation. */ + alloc = &(GTK_WIDGET (object)->allocation); + bw = GTK_CONTAINER (object)->border_width; + + /* Get coordinates relative to our window. */ + rel_x = x - alloc->x; + rel_y = y - alloc->y; + + if (request) + my_request = *request; + + /* Check if coordinates are inside the widget. */ + if (rel_x > 0 && rel_x < alloc->width && + rel_y > 0 && rel_y < alloc->height) { + GtkRequisition my, other; + gint divider = -1; + + gdl_dock_item_preferred_size (GDL_DOCK_ITEM (my_request.applicant), &other); + gdl_dock_item_preferred_size (GDL_DOCK_ITEM (object), &my); + + /* It's inside our area. */ + may_dock = TRUE; + + /* Set docking indicator rectangle to the widget size. */ + my_request.rect.x = bw; + my_request.rect.y = bw; + my_request.rect.width = alloc->width - 2*bw; + my_request.rect.height = alloc->height - 2*bw; + + my_request.target = object; + + /* See if it's in the border_width band. */ + if (rel_x < bw) { + my_request.position = GDL_DOCK_LEFT; + my_request.rect.width *= SPLIT_RATIO; + divider = other.width; + } else if (rel_x > alloc->width - bw) { + my_request.position = GDL_DOCK_RIGHT; + my_request.rect.x += my_request.rect.width * (1 - SPLIT_RATIO); + my_request.rect.width *= SPLIT_RATIO; + divider = MAX (0, my.width - other.width); + } else if (rel_y < bw) { + my_request.position = GDL_DOCK_TOP; + my_request.rect.height *= SPLIT_RATIO; + divider = other.height; + } else if (rel_y > alloc->height - bw) { + my_request.position = GDL_DOCK_BOTTOM; + my_request.rect.y += my_request.rect.height * (1 - SPLIT_RATIO); + my_request.rect.height *= SPLIT_RATIO; + divider = MAX (0, my.height - other.height); + + } else { /* Otherwise try our children. */ + struct { + gint x, y; + GdlDockRequest *request; + gboolean may_dock; + } data; + + /* give them coordinates in their allocation system... the + GtkPaned has no window, so our children allocation + coordinates are our window coordinates */ + data.x = rel_x; + data.y = rel_y; + data.request = &my_request; + data.may_dock = FALSE; + + gtk_container_foreach (GTK_CONTAINER (object), + (GtkCallback) gdl_dock_paned_request_foreach, + &data); + + may_dock = data.may_dock; + if (!may_dock) { + /* the pointer is on the handle, so snap to top/bottom + or left/right */ + may_dock = TRUE; + if (item->orientation == GTK_ORIENTATION_HORIZONTAL) { + if (rel_y < alloc->height / 2) { + my_request.position = GDL_DOCK_TOP; + my_request.rect.height *= SPLIT_RATIO; + divider = other.height; + } else { + my_request.position = GDL_DOCK_BOTTOM; + my_request.rect.y += my_request.rect.height * (1 - SPLIT_RATIO); + my_request.rect.height *= SPLIT_RATIO; + divider = MAX (0, my.height - other.height); + } + } else { + if (rel_x < alloc->width / 2) { + my_request.position = GDL_DOCK_LEFT; + my_request.rect.width *= SPLIT_RATIO; + divider = other.width; + } else { + my_request.position = GDL_DOCK_RIGHT; + my_request.rect.x += my_request.rect.width * (1 - SPLIT_RATIO); + my_request.rect.width *= SPLIT_RATIO; + divider = MAX (0, my.width - other.width); + } + } + } + } + + if (divider >= 0 && my_request.position != GDL_DOCK_CENTER) { + if (G_IS_VALUE (&my_request.extra)) + g_value_unset (&my_request.extra); + g_value_init (&my_request.extra, G_TYPE_UINT); + g_value_set_uint (&my_request.extra, (guint) divider); + } + + if (may_dock) { + /* adjust returned coordinates so they are relative to + our allocation */ + my_request.rect.x += alloc->x; + my_request.rect.y += alloc->y; + } + } + + if (may_dock && request) + *request = my_request; + + return may_dock; +} + +static void +gdl_dock_paned_dock (GdlDockObject *object, + GdlDockObject *requestor, + GdlDockPlacement position, + GValue *other_data) +{ + GtkPaned *paned; + gboolean done = FALSE; + gboolean hresize = FALSE; + gboolean wresize = FALSE; + gint temp = 0; + + g_return_if_fail (GDL_IS_DOCK_PANED (object)); + g_return_if_fail (GDL_DOCK_ITEM (object)->child != NULL); + + paned = GTK_PANED (GDL_DOCK_ITEM (object)->child); + + if (GDL_IS_DOCK_ITEM (requestor)) { + g_object_get (G_OBJECT (requestor), "preferred_height", &temp, NULL); + if (temp == -2) + hresize = TRUE; + temp = 0; + g_object_get (G_OBJECT (requestor), "preferred_width", &temp, NULL); + if (temp == -2) + wresize = TRUE; + } + + /* see if we can dock the item in our paned */ + switch (GDL_DOCK_ITEM (object)->orientation) { + case GTK_ORIENTATION_HORIZONTAL: + if (!paned->child1 && position == GDL_DOCK_LEFT) { + gtk_paned_pack1 (paned, GTK_WIDGET (requestor), FALSE, FALSE); + done = TRUE; + } else if (!paned->child2 && position == GDL_DOCK_RIGHT) { + gtk_paned_pack2 (paned, GTK_WIDGET (requestor), TRUE, FALSE); + done = TRUE; + } + break; + case GTK_ORIENTATION_VERTICAL: + if (!paned->child1 && position == GDL_DOCK_TOP) { + gtk_paned_pack1 (paned, GTK_WIDGET (requestor), hresize, FALSE); + done = TRUE; + } else if (!paned->child2 && position == GDL_DOCK_BOTTOM) { + gtk_paned_pack2 (paned, GTK_WIDGET (requestor), hresize, FALSE); + done = TRUE; + } + break; + default: + break; + } + + if (!done) { + /* this will create another paned and reparent us there */ + GDL_CALL_PARENT (GDL_DOCK_OBJECT_CLASS, dock, (object, requestor, position, + other_data)); + } + else { + gdl_dock_item_show_grip (GDL_DOCK_ITEM (requestor)); + GDL_DOCK_OBJECT_SET_FLAGS (requestor, GDL_DOCK_ATTACHED); + } +} + +static void +gdl_dock_paned_set_orientation (GdlDockItem *item, + GtkOrientation orientation) +{ + GtkPaned *old_paned = NULL, *new_paned; + GtkWidget *child1, *child2; + + g_return_if_fail (GDL_IS_DOCK_PANED (item)); + + if (item->child) { + old_paned = GTK_PANED (item->child); + g_object_ref (old_paned); + gtk_widget_unparent (GTK_WIDGET (old_paned)); + item->child = NULL; + } + + gdl_dock_paned_create_child (GDL_DOCK_PANED (item), orientation); + + if (old_paned) { + new_paned = GTK_PANED (item->child); + child1 = old_paned->child1; + child2 = old_paned->child2; + + if (child1) { + g_object_ref (child1); + gtk_container_remove (GTK_CONTAINER (old_paned), child1); + gtk_paned_pack1 (new_paned, child1, TRUE, FALSE); + g_object_unref (child1); + } + if (child2) { + g_object_ref (child2); + gtk_container_remove (GTK_CONTAINER (old_paned), child2); + gtk_paned_pack1 (new_paned, child2, TRUE, FALSE); + g_object_unref (child2); + } + } + + GDL_CALL_PARENT (GDL_DOCK_ITEM_CLASS, set_orientation, (item, orientation)); +} + +static gboolean +gdl_dock_paned_child_placement (GdlDockObject *object, + GdlDockObject *child, + GdlDockPlacement *placement) +{ + GdlDockItem *item = GDL_DOCK_ITEM (object); + GtkPaned *paned; + GdlDockPlacement pos = GDL_DOCK_NONE; + + if (item->child) { + paned = GTK_PANED (item->child); + if (GTK_WIDGET (child) == paned->child1) + pos = item->orientation == GTK_ORIENTATION_HORIZONTAL ? + GDL_DOCK_LEFT : GDL_DOCK_TOP; + else if (GTK_WIDGET (child) == paned->child2) + pos = item->orientation == GTK_ORIENTATION_HORIZONTAL ? + GDL_DOCK_RIGHT : GDL_DOCK_BOTTOM; + } + + if (pos != GDL_DOCK_NONE) { + if (placement) + *placement = pos; + return TRUE; + } + else + return FALSE; +} + + +/* ----- Public interface ----- */ + +GtkWidget * +gdl_dock_paned_new (GtkOrientation orientation) +{ + GdlDockPaned *paned; + + paned = GDL_DOCK_PANED (g_object_new (GDL_TYPE_DOCK_PANED, + "orientation", orientation, NULL)); + GDL_DOCK_OBJECT_UNSET_FLAGS (paned, GDL_DOCK_AUTOMATIC); + + return GTK_WIDGET (paned); +} diff --git a/src/libgdl/gdl-dock-paned.h b/src/libgdl/gdl-dock-paned.h new file mode 100644 index 000000000..41107a51f --- /dev/null +++ b/src/libgdl/gdl-dock-paned.h @@ -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 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __GDL_DOCK_PANED_H__ +#define __GDL_DOCK_PANED_H__ + +#include "libgdl/gdl-dock-item.h" + +G_BEGIN_DECLS + +/* standard macros */ +#define GDL_TYPE_DOCK_PANED (gdl_dock_paned_get_type ()) +#define GDL_DOCK_PANED(obj) (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_PANED, GdlDockPaned)) +#define GDL_DOCK_PANED_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_PANED, GdlDockPanedClass)) +#define GDL_IS_DOCK_PANED(obj) (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_PANED)) +#define GDL_IS_DOCK_PANED_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_PANED)) +#define GDL_DOCK_PANED_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GDL_TYE_DOCK_PANED, GdlDockPanedClass)) + +/* data types & structures */ +typedef struct _GdlDockPaned GdlDockPaned; +typedef struct _GdlDockPanedClass GdlDockPanedClass; + +struct _GdlDockPaned { + GdlDockItem dock_item; + + gboolean position_changed; +}; + +struct _GdlDockPanedClass { + GdlDockItemClass parent_class; +}; + + +/* public interface */ + +GType gdl_dock_paned_get_type (void); + +GtkWidget *gdl_dock_paned_new (GtkOrientation orientation); + + +G_END_DECLS + +#endif /* __GDL_DOCK_PANED_H__ */ + diff --git a/src/libgdl/gdl-dock-placeholder.c b/src/libgdl/gdl-dock-placeholder.c new file mode 100644 index 000000000..df068d03f --- /dev/null +++ b/src/libgdl/gdl-dock-placeholder.c @@ -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 + * + * 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 +#endif + +#include "gdl-i18n.h" + +#include "gdl-tools.h" +#include "gdl-dock-placeholder.h" +#include "gdl-dock-item.h" +#include "gdl-dock-master.h" +#include "libgdltypebuiltins.h" + + +#undef PLACEHOLDER_DEBUG + +/* ----- Private prototypes ----- */ + +static void gdl_dock_placeholder_class_init (GdlDockPlaceholderClass *klass); +static void gdl_dock_placeholder_instance_init (GdlDockPlaceholder *ph); + +static void gdl_dock_placeholder_set_property (GObject *g_object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gdl_dock_placeholder_get_property (GObject *g_object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static void gdl_dock_placeholder_destroy (GtkObject *object); + +static void gdl_dock_placeholder_add (GtkContainer *container, + GtkWidget *widget); + +static void gdl_dock_placeholder_detach (GdlDockObject *object, + gboolean recursive); +static void gdl_dock_placeholder_reduce (GdlDockObject *object); +static void gdl_dock_placeholder_dock (GdlDockObject *object, + GdlDockObject *requestor, + GdlDockPlacement position, + GValue *other_data); + +static void gdl_dock_placeholder_weak_notify (gpointer data, + GObject *old_object); + +static void disconnect_host (GdlDockPlaceholder *ph); +static void connect_host (GdlDockPlaceholder *ph, + GdlDockObject *new_host); +static void do_excursion (GdlDockPlaceholder *ph); + +static void gdl_dock_placeholder_present (GdlDockObject *object, + GdlDockObject *child); + +static void detach_cb (GdlDockObject *object, + gboolean recursive, + gpointer user_data); + +/* ----- Private variables and data structures ----- */ + +enum { + PROP_0, + PROP_STICKY, + PROP_HOST, + PROP_NEXT_PLACEMENT, + PROP_WIDTH, + PROP_HEIGHT, + PROP_FLOATING, + PROP_FLOAT_X, + PROP_FLOAT_Y +}; + +struct _GdlDockPlaceholderPrivate { + /* current object this placeholder is pinned to */ + GdlDockObject *host; + gboolean sticky; + + /* when the placeholder is moved up the hierarchy, this stack + keeps track of the necessary dock positions needed to get the + placeholder to the original position */ + GSList *placement_stack; + + /* Width and height of the attachments */ + gint width; + gint height; + + /* connected signal handlers */ + guint host_detach_handler; + guint host_dock_handler; + + /* Window Coordinates if Dock was floating */ + gboolean floating; + gint floatx; + gint floaty; +}; + + +/* ----- Private interface ----- */ + +GDL_CLASS_BOILERPLATE (GdlDockPlaceholder, gdl_dock_placeholder, + GdlDockObject, GDL_TYPE_DOCK_OBJECT); + +static void +gdl_dock_placeholder_class_init (GdlDockPlaceholderClass *klass) +{ + GObjectClass *g_object_class; + GtkObjectClass *gtk_object_class; + GtkContainerClass *container_class; + GdlDockObjectClass *object_class; + + g_object_class = G_OBJECT_CLASS (klass); + gtk_object_class = GTK_OBJECT_CLASS (klass); + container_class = GTK_CONTAINER_CLASS (klass); + object_class = GDL_DOCK_OBJECT_CLASS (klass); + + g_object_class->get_property = gdl_dock_placeholder_get_property; + g_object_class->set_property = gdl_dock_placeholder_set_property; + + g_object_class_install_property ( + g_object_class, PROP_STICKY, + g_param_spec_boolean ("sticky", _("Sticky"), + _("Whether the placeholder will stick to its host or " + "move up the hierarchy when the host is redocked"), + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property ( + g_object_class, PROP_HOST, + g_param_spec_object ("host", _("Host"), + _("The dock object this placeholder is attached to"), + GDL_TYPE_DOCK_OBJECT, + G_PARAM_READWRITE)); + + /* this will return the top of the placement stack */ + g_object_class_install_property ( + g_object_class, PROP_NEXT_PLACEMENT, + g_param_spec_enum ("next-placement", _("Next placement"), + _("The position an item will be docked to our host if a " + "request is made to dock to us"), + GDL_TYPE_DOCK_PLACEMENT, + GDL_DOCK_CENTER, + G_PARAM_READWRITE | + GDL_DOCK_PARAM_EXPORT | GDL_DOCK_PARAM_AFTER)); + + g_object_class_install_property ( + g_object_class, PROP_WIDTH, + g_param_spec_int ("width", _("Width"), + _("Width for the widget when it's attached to the placeholder"), + -1, G_MAXINT, -1, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | + GDL_DOCK_PARAM_EXPORT)); + + g_object_class_install_property ( + g_object_class, PROP_HEIGHT, + g_param_spec_int ("height", _("Height"), + _("Height for the widget when it's attached to the placeholder"), + -1, G_MAXINT, -1, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | + GDL_DOCK_PARAM_EXPORT)); + g_object_class_install_property ( + g_object_class, PROP_FLOATING, + g_param_spec_boolean ("floating", _("Floating Toplevel"), + _("Whether the placeholder is standing in for a " + "floating toplevel dock"), + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property ( + g_object_class, PROP_FLOAT_X, + g_param_spec_int ("floatx", _("X-Coordinate"), + _("X-Coordinate fow dock when floating"), + -1, G_MAXINT, -1, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + GDL_DOCK_PARAM_EXPORT)); + g_object_class_install_property ( + g_object_class, PROP_FLOAT_Y, + g_param_spec_int ("floaty", _("Y-Coordinate"), + _("Y-Coordinate fow dock when floating"), + -1, G_MAXINT, -1, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + GDL_DOCK_PARAM_EXPORT)); + + + gtk_object_class->destroy = gdl_dock_placeholder_destroy; + container_class->add = gdl_dock_placeholder_add; + + object_class->is_compound = FALSE; + object_class->detach = gdl_dock_placeholder_detach; + object_class->reduce = gdl_dock_placeholder_reduce; + object_class->dock = gdl_dock_placeholder_dock; + object_class->present = gdl_dock_placeholder_present; +} + +static void +gdl_dock_placeholder_instance_init (GdlDockPlaceholder *ph) +{ + GTK_WIDGET_SET_FLAGS (ph, GTK_NO_WINDOW); + GTK_WIDGET_UNSET_FLAGS (ph, GTK_CAN_FOCUS); + + ph->_priv = g_new0 (GdlDockPlaceholderPrivate, 1); +} + +static void +gdl_dock_placeholder_set_property (GObject *g_object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (g_object); + + switch (prop_id) { + case PROP_STICKY: + if (ph->_priv) + ph->_priv->sticky = g_value_get_boolean (value); + break; + case PROP_HOST: + gdl_dock_placeholder_attach (ph, g_value_get_object (value)); + break; + case PROP_NEXT_PLACEMENT: + if (ph->_priv) { + ph->_priv->placement_stack = + g_slist_prepend (ph->_priv->placement_stack, + GINT_TO_POINTER (g_value_get_enum (value))); + } + break; + case PROP_WIDTH: + ph->_priv->width = g_value_get_int (value); + break; + case PROP_HEIGHT: + ph->_priv->height = g_value_get_int (value); + break; + case PROP_FLOATING: + ph->_priv->floating = g_value_get_boolean (value); + break; + case PROP_FLOAT_X: + ph->_priv->floatx = g_value_get_int (value); + break; + case PROP_FLOAT_Y: + ph->_priv->floaty = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (g_object, prop_id, pspec); + break; + } +} + +static void +gdl_dock_placeholder_get_property (GObject *g_object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (g_object); + + switch (prop_id) { + case PROP_STICKY: + if (ph->_priv) + g_value_set_boolean (value, ph->_priv->sticky); + else + g_value_set_boolean (value, FALSE); + break; + case PROP_HOST: + if (ph->_priv) + g_value_set_object (value, ph->_priv->host); + else + g_value_set_object (value, NULL); + break; + case PROP_NEXT_PLACEMENT: + if (ph->_priv && ph->_priv->placement_stack) + g_value_set_enum (value, (GdlDockPlacement) ph->_priv->placement_stack->data); + else + g_value_set_enum (value, GDL_DOCK_CENTER); + break; + case PROP_WIDTH: + g_value_set_int (value, ph->_priv->width); + break; + case PROP_HEIGHT: + g_value_set_int (value, ph->_priv->height); + break; + case PROP_FLOATING: + g_value_set_boolean (value, ph->_priv->floating); + break; + case PROP_FLOAT_X: + g_value_set_int (value, ph->_priv->floatx); + break; + case PROP_FLOAT_Y: + g_value_set_int (value, ph->_priv->floaty); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (g_object, prop_id, pspec); + break; + } +} + +static void +gdl_dock_placeholder_destroy (GtkObject *object) +{ + GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (object); + + if (ph->_priv) { + if (ph->_priv->host) + gdl_dock_placeholder_detach (GDL_DOCK_OBJECT (object), FALSE); + g_free (ph->_priv); + ph->_priv = NULL; + } + + GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object)); +} + +static void +gdl_dock_placeholder_add (GtkContainer *container, + GtkWidget *widget) +{ + GdlDockPlaceholder *ph; + GdlDockPlacement pos = GDL_DOCK_CENTER; /* default position */ + + g_return_if_fail (GDL_IS_DOCK_PLACEHOLDER (container)); + g_return_if_fail (GDL_IS_DOCK_ITEM (widget)); + + ph = GDL_DOCK_PLACEHOLDER (container); + if (ph->_priv->placement_stack) + pos = (GdlDockPlacement) ph->_priv->placement_stack->data; + + gdl_dock_object_dock (GDL_DOCK_OBJECT (ph), GDL_DOCK_OBJECT (widget), + pos, NULL); +} + +static void +gdl_dock_placeholder_detach (GdlDockObject *object, + gboolean recursive) +{ + GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (object); + + /* disconnect handlers */ + disconnect_host (ph); + + /* free the placement stack */ + g_slist_free (ph->_priv->placement_stack); + ph->_priv->placement_stack = NULL; + + GDL_DOCK_OBJECT_UNSET_FLAGS (object, GDL_DOCK_ATTACHED); +} + +static void +gdl_dock_placeholder_reduce (GdlDockObject *object) +{ + /* placeholders are not reduced */ + return; +} + +static void +find_biggest_dock_item (GtkContainer *container, GtkWidget **biggest_child, + gint *biggest_child_area) +{ + GList *children, *child; + + children = gtk_container_get_children (GTK_CONTAINER (container)); + child = children; + while (child) { + gint area; + GtkWidget *child_widget; + + child_widget = GTK_WIDGET (child->data); + + if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT(child_widget))) { + find_biggest_dock_item (GTK_CONTAINER (child_widget), + biggest_child, biggest_child_area); + child = g_list_next (child); + continue; + } + area = child_widget->allocation.width * child_widget->allocation.height; + + if (area > *biggest_child_area) { + *biggest_child_area = area; + *biggest_child = child_widget; + } + child = g_list_next (child); + } +} + +static void +attempt_to_dock_on_host (GdlDockPlaceholder *ph, GdlDockObject *host, + GdlDockObject *requestor, GdlDockPlacement placement, + gpointer other_data) +{ + GdlDockObject *parent; + gint host_width = GTK_WIDGET (host)->allocation.width; + gint host_height = GTK_WIDGET (host)->allocation.height; + + if (placement != GDL_DOCK_CENTER || !GDL_IS_DOCK_PANED (host)) { + /* we simply act as a proxy for our host */ + gdl_dock_object_dock (host, requestor, + placement, other_data); + } else { + /* If the requested pos is center, we have to make sure that it + * does not colapses existing paned items. Find the larget item + * which is not a paned item to dock to. + */ + GtkWidget *biggest_child = NULL; + gint biggest_child_area = 0; + + find_biggest_dock_item (GTK_CONTAINER (host), &biggest_child, + &biggest_child_area); + + if (biggest_child) { + /* we simply act as a proxy for our host */ + gdl_dock_object_dock (GDL_DOCK_OBJECT (biggest_child), requestor, + placement, other_data); + } else { + g_warning ("No suitable child found! Should not be here!"); + /* we simply act as a proxy for our host */ + gdl_dock_object_dock (GDL_DOCK_OBJECT (host), requestor, + placement, other_data); + } + } + + parent = gdl_dock_object_get_parent_object (requestor); + + /* Restore dock item's dimention */ + switch (placement) { + case GDL_DOCK_LEFT: + if (ph->_priv->width > 0) { + g_object_set (G_OBJECT (parent), "position", + ph->_priv->width, NULL); + } + break; + case GDL_DOCK_RIGHT: + if (ph->_priv->width > 0) { + gint complementary_width = host_width - ph->_priv->width; + + if (complementary_width > 0) + g_object_set (G_OBJECT (parent), "position", + complementary_width, NULL); + } + break; + case GDL_DOCK_TOP: + if (ph->_priv->height > 0) { + g_object_set (G_OBJECT (parent), "position", + ph->_priv->height, NULL); + } + break; + case GDL_DOCK_BOTTOM: + if (ph->_priv->height > 0) { + gint complementary_height = host_height - ph->_priv->height; + + if (complementary_height > 0) + g_object_set (G_OBJECT (parent), "position", + complementary_height, NULL); + } + break; + default: + /* nothing */ + break; + } +} + +static void +gdl_dock_placeholder_dock (GdlDockObject *object, + GdlDockObject *requestor, + GdlDockPlacement position, + GValue *other_data) +{ + GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (object); + + if (ph->_priv->host) { + attempt_to_dock_on_host (ph, ph->_priv->host, requestor, + position, other_data); + } + else { + GdlDockObject *toplevel; + + if (!gdl_dock_object_is_bound (GDL_DOCK_OBJECT (ph))) { + g_warning (_("Attempt to dock a dock object to an unbound placeholder")); + return; + } + + /* dock the item as a floating of the controller */ + toplevel = gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (ph)); + gdl_dock_object_dock (toplevel, requestor, + GDL_DOCK_FLOATING, NULL); + } +} + +#ifdef PLACEHOLDER_DEBUG +static void +print_placement_stack (GdlDockPlaceholder *ph) +{ + GSList *s = ph->_priv->placement_stack; + GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_ref (GDL_TYPE_DOCK_PLACEMENT)); + GEnumValue *enum_value; + gchar *name; + GString *message; + + message = g_string_new (NULL); + g_string_printf (message, "[%p] host: %p (%s), stack: ", + ph, ph->_priv->host, G_OBJECT_TYPE_NAME (ph->_priv->host)); + for (; s; s = s->next) { + enum_value = g_enum_get_value (enum_class, (GdlDockPlacement) s->data); + name = enum_value ? enum_value->value_name : NULL; + g_string_append_printf (message, "%s, ", name); + } + g_message ("%s", message->str); + + g_string_free (message, TRUE); + g_type_class_unref (enum_class); +} +#endif + +static void +gdl_dock_placeholder_present (GdlDockObject *object, + GdlDockObject *child) +{ + /* do nothing */ + return; +} + +/* ----- Public interface ----- */ + +GtkWidget * +gdl_dock_placeholder_new (gchar *name, + GdlDockObject *object, + GdlDockPlacement position, + gboolean sticky) +{ + GdlDockPlaceholder *ph; + + ph = GDL_DOCK_PLACEHOLDER (g_object_new (GDL_TYPE_DOCK_PLACEHOLDER, + "name", name, + "sticky", sticky, + NULL)); + GDL_DOCK_OBJECT_UNSET_FLAGS (ph, GDL_DOCK_AUTOMATIC); + + if (object) { + gdl_dock_placeholder_attach (ph, object); + if (position == GDL_DOCK_NONE) + position = GDL_DOCK_CENTER; + g_object_set (G_OBJECT (ph), "next-placement", position, NULL); + if (GDL_IS_DOCK (object)) { + /* the top placement will be consumed by the toplevel + dock, so add a dummy placement */ + g_object_set (G_OBJECT (ph), "next-placement", GDL_DOCK_CENTER, NULL); + } + /* try a recursion */ + do_excursion (ph); + } + + return GTK_WIDGET (ph); +} + +static void +gdl_dock_placeholder_weak_notify (gpointer data, + GObject *old_object) +{ + GdlDockPlaceholder *ph; + + g_return_if_fail (data != NULL && GDL_IS_DOCK_PLACEHOLDER (data)); + + ph = GDL_DOCK_PLACEHOLDER (data); + +#ifdef PLACEHOLDER_DEBUG + g_message ("The placeholder just lost its host, ph = %p", ph); +#endif + + /* we shouldn't get here, so perform an emergency detach. instead + we should have gotten a detach signal from our host */ + ph->_priv->host = NULL; + + /* We didn't get a detach signal from the host. Detach from the + supposedly dead host (consequently attaching to the controller) */ + + detach_cb (NULL, TRUE, data); +#if 0 + /* free the placement stack */ + g_slist_free (ph->_priv->placement_stack); + ph->_priv->placement_stack = NULL; + GDL_DOCK_OBJECT_UNSET_FLAGS (ph, GDL_DOCK_ATTACHED); +#endif +} + +static void +detach_cb (GdlDockObject *object, + gboolean recursive, + gpointer user_data) +{ + GdlDockPlaceholder *ph; + GdlDockObject *new_host, *obj; + + g_return_if_fail (user_data != NULL && GDL_IS_DOCK_PLACEHOLDER (user_data)); + + /* we go up in the hierarchy and we store the hinted placement in + * the placement stack so we can rebuild the docking layout later + * when we get the host's dock signal. */ + + ph = GDL_DOCK_PLACEHOLDER (user_data); + obj = ph->_priv->host; + if (obj != object) { + g_warning (_("Got a detach signal from an object (%p) who is not " + "our host %p"), object, ph->_priv->host); + return; + } + + /* skip sticky objects */ + if (ph->_priv->sticky) + return; + + if (obj) + /* go up in the hierarchy */ + new_host = gdl_dock_object_get_parent_object (obj); + else + /* Detaching from the dead host */ + new_host = NULL; + + while (new_host) { + GdlDockPlacement pos = GDL_DOCK_NONE; + + /* get placement hint from the new host */ + if (gdl_dock_object_child_placement (new_host, obj, &pos)) { + ph->_priv->placement_stack = g_slist_prepend ( + ph->_priv->placement_stack, (gpointer) pos); + } + else { + g_warning (_("Something weird happened while getting the child " + "placement for %p from parent %p"), obj, new_host); + } + + if (!GDL_DOCK_OBJECT_IN_DETACH (new_host)) + /* we found a "stable" dock object */ + break; + + obj = new_host; + new_host = gdl_dock_object_get_parent_object (obj); + } + + /* disconnect host */ + disconnect_host (ph); + + if (!new_host) { +#ifdef PLACEHOLDER_DEBUG + g_message ("Detaching from the toplevel. Assignaing to controller"); +#endif + /* the toplevel was detached: we attach ourselves to the + controller with an initial placement of floating */ + new_host = gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (ph)); + + /* + ph->_priv->placement_stack = g_slist_prepend ( + ph->_priv->placement_stack, (gpointer) GDL_DOCK_FLOATING); + */ + } + if (new_host) + connect_host (ph, new_host); + +#ifdef PLACEHOLDER_DEBUG + print_placement_stack (ph); +#endif +} + +/** + * do_excursion: + * @ph: placeholder object + * + * Tries to shrink the placement stack by examining the host's + * children and see if any of them matches the placement which is at + * the top of the stack. If this is the case, it tries again with the + * new host. + **/ +static void +do_excursion (GdlDockPlaceholder *ph) +{ + if (ph->_priv->host && + !ph->_priv->sticky && + ph->_priv->placement_stack && + gdl_dock_object_is_compound (ph->_priv->host)) { + + GdlDockPlacement pos, stack_pos = + (GdlDockPlacement) ph->_priv->placement_stack->data; + GList *children, *l; + GdlDockObject *host = ph->_priv->host; + + children = gtk_container_get_children (GTK_CONTAINER (host)); + for (l = children; l; l = l->next) { + pos = stack_pos; + gdl_dock_object_child_placement (GDL_DOCK_OBJECT (host), + GDL_DOCK_OBJECT (l->data), + &pos); + if (pos == stack_pos) { + /* remove the stack position */ + ph->_priv->placement_stack = + g_slist_remove_link (ph->_priv->placement_stack, + ph->_priv->placement_stack); + + /* connect to the new host */ + disconnect_host (ph); + connect_host (ph, GDL_DOCK_OBJECT (l->data)); + + /* recurse... */ + if (!GDL_DOCK_OBJECT_IN_REFLOW (l->data)) + do_excursion (ph); + + break; + } + } + g_list_free (children); + } +} + +static void +dock_cb (GdlDockObject *object, + GdlDockObject *requestor, + GdlDockPlacement position, + GValue *other_data, + gpointer user_data) +{ + GdlDockPlacement pos = GDL_DOCK_NONE; + GdlDockPlaceholder *ph; + + g_return_if_fail (user_data != NULL && GDL_IS_DOCK_PLACEHOLDER (user_data)); + ph = GDL_DOCK_PLACEHOLDER (user_data); + g_return_if_fail (ph->_priv->host == object); + + /* see if the given position is compatible for the stack's top + element */ + if (!ph->_priv->sticky && ph->_priv->placement_stack) { + pos = (GdlDockPlacement) ph->_priv->placement_stack->data; + if (gdl_dock_object_child_placement (object, requestor, &pos)) { + if (pos == (GdlDockPlacement) ph->_priv->placement_stack->data) { + /* the position is compatible: excurse down */ + do_excursion (ph); + } + } + } +#ifdef PLACEHOLDER_DEBUG + print_placement_stack (ph); +#endif +} + +static void +disconnect_host (GdlDockPlaceholder *ph) +{ + if (!ph->_priv->host) + return; + + if (ph->_priv->host_detach_handler) + g_signal_handler_disconnect (ph->_priv->host, ph->_priv->host_detach_handler); + if (ph->_priv->host_dock_handler) + g_signal_handler_disconnect (ph->_priv->host, ph->_priv->host_dock_handler); + ph->_priv->host_detach_handler = 0; + ph->_priv->host_dock_handler = 0; + + /* remove weak ref to object */ + g_object_weak_unref (G_OBJECT (ph->_priv->host), + gdl_dock_placeholder_weak_notify, ph); + ph->_priv->host = NULL; + +#ifdef PLACEHOLDER_DEBUG + g_message ("Host just disconnected!, ph = %p", ph); +#endif +} + +static void +connect_host (GdlDockPlaceholder *ph, + GdlDockObject *new_host) +{ + if (ph->_priv->host) + disconnect_host (ph); + + ph->_priv->host = new_host; + g_object_weak_ref (G_OBJECT (ph->_priv->host), + gdl_dock_placeholder_weak_notify, ph); + + ph->_priv->host_detach_handler = + g_signal_connect (ph->_priv->host, + "detach", + (GCallback) detach_cb, + (gpointer) ph); + + ph->_priv->host_dock_handler = + g_signal_connect (ph->_priv->host, + "dock", + (GCallback) dock_cb, + (gpointer) ph); + +#ifdef PLACEHOLDER_DEBUG + g_message ("Host just connected!, ph = %p", ph); +#endif +} + +void +gdl_dock_placeholder_attach (GdlDockPlaceholder *ph, + GdlDockObject *object) +{ + g_return_if_fail (ph != NULL && GDL_IS_DOCK_PLACEHOLDER (ph)); + g_return_if_fail (ph->_priv != NULL); + g_return_if_fail (object != NULL); + + /* object binding */ + if (!gdl_dock_object_is_bound (GDL_DOCK_OBJECT (ph))) + gdl_dock_object_bind (GDL_DOCK_OBJECT (ph), object->master); + + g_return_if_fail (GDL_DOCK_OBJECT (ph)->master == object->master); + + gdl_dock_object_freeze (GDL_DOCK_OBJECT (ph)); + + /* detach from previous host first */ + if (ph->_priv->host) + gdl_dock_object_detach (GDL_DOCK_OBJECT (ph), FALSE); + + connect_host (ph, object); + + GDL_DOCK_OBJECT_SET_FLAGS (ph, GDL_DOCK_ATTACHED); + + gdl_dock_object_thaw (GDL_DOCK_OBJECT (ph)); +} diff --git a/src/libgdl/gdl-dock-placeholder.h b/src/libgdl/gdl-dock-placeholder.h new file mode 100644 index 000000000..4a7035b82 --- /dev/null +++ b/src/libgdl/gdl-dock-placeholder.h @@ -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 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __GDL_DOCK_PLACEHOLDER_H__ +#define __GDL_DOCK_PLACEHOLDER_H__ + +#include "libgdl/gdl-dock-object.h" + +G_BEGIN_DECLS + +/* standard macros */ +#define GDL_TYPE_DOCK_PLACEHOLDER (gdl_dock_placeholder_get_type ()) +#define GDL_DOCK_PLACEHOLDER(obj) (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_PLACEHOLDER, GdlDockPlaceholder)) +#define GDL_DOCK_PLACEHOLDER_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_PLACEHOLDER, GdlDockPlaceholderClass)) +#define GDL_IS_DOCK_PLACEHOLDER(obj) (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_PLACEHOLDER)) +#define GDL_IS_DOCK_PLACEHOLDER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_PLACEHOLDER)) +#define GDL_DOCK_PLACEHOLDER_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK_PLACEHOLDER, GdlDockPlaceholderClass)) + +/* data types & structures */ +typedef struct _GdlDockPlaceholder GdlDockPlaceholder; +typedef struct _GdlDockPlaceholderClass GdlDockPlaceholderClass; +typedef struct _GdlDockPlaceholderPrivate GdlDockPlaceholderPrivate; + +struct _GdlDockPlaceholder { + GdlDockObject object; + + GdlDockPlaceholderPrivate *_priv; +}; + +struct _GdlDockPlaceholderClass { + GdlDockObjectClass parent_class; +}; + +/* public interface */ + +GType gdl_dock_placeholder_get_type (void); + +GtkWidget *gdl_dock_placeholder_new (gchar *name, + GdlDockObject *object, + GdlDockPlacement position, + gboolean sticky); + +void gdl_dock_placeholder_attach (GdlDockPlaceholder *ph, + GdlDockObject *object); + + +G_END_DECLS + +#endif /* __GDL_DOCK_PLACEHOLDER_H__ */ diff --git a/src/libgdl/gdl-dock-tablabel.c b/src/libgdl/gdl-dock-tablabel.c new file mode 100644 index 000000000..bd755893b --- /dev/null +++ b/src/libgdl/gdl-dock-tablabel.c @@ -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 + * + * 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 +#endif + +#include "gdl-i18n.h" +#include + +#include "gdl-dock-tablabel.h" +#include "gdl-tools.h" +#include "gdl-dock-item.h" +#include "libgdlmarshal.h" + + +/* ----- Private prototypes ----- */ + +static void gdl_dock_tablabel_class_init (GdlDockTablabelClass *klass); +static void gdl_dock_tablabel_instance_init (GdlDockTablabel *tablabel); + +static void gdl_dock_tablabel_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gdl_dock_tablabel_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static void gdl_dock_tablabel_item_notify (GObject *master, + GParamSpec *pspec, + gpointer data); + +static void gdl_dock_tablabel_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void gdl_dock_tablabel_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); + +static void gdl_dock_tablabel_paint (GtkWidget *widget, + GdkEventExpose *event); +static gint gdl_dock_tablabel_expose (GtkWidget *widget, + GdkEventExpose *event); + +static gboolean gdl_dock_tablabel_button_event (GtkWidget *widget, + GdkEventButton *event); +static gboolean gdl_dock_tablabel_motion_event (GtkWidget *widget, + GdkEventMotion *event); + +static void gdl_dock_tablabel_realize (GtkWidget *widget); +static void gdl_dock_tablabel_unrealize (GtkWidget *widget); +static void gdl_dock_tablabel_map (GtkWidget *widget); +static void gdl_dock_tablabel_unmap (GtkWidget *widget); + +/* ----- Private data types and variables ----- */ + +#define DEFAULT_DRAG_HANDLE_SIZE 10 +#define HANDLE_RATIO 1.0 + +enum { + BUTTON_PRESSED_HANDLE, + LAST_SIGNAL +}; + +enum { + PROP_0, + PROP_ITEM +}; + + +static guint dock_tablabel_signals [LAST_SIGNAL] = { 0 }; + + +/* ----- Private interface ----- */ + +GDL_CLASS_BOILERPLATE (GdlDockTablabel, gdl_dock_tablabel, + GtkBin, GTK_TYPE_BIN); + +static void +gdl_dock_tablabel_class_init (GdlDockTablabelClass *klass) +{ + GObjectClass *g_object_class; + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + GtkContainerClass *container_class; + + g_object_class = G_OBJECT_CLASS (klass); + object_class = GTK_OBJECT_CLASS (klass); + widget_class = GTK_WIDGET_CLASS (klass); + container_class = GTK_CONTAINER_CLASS (klass); + + g_object_class->set_property = gdl_dock_tablabel_set_property; + g_object_class->get_property = gdl_dock_tablabel_get_property; + + widget_class->size_request = gdl_dock_tablabel_size_request; + widget_class->size_allocate = gdl_dock_tablabel_size_allocate; + widget_class->expose_event = gdl_dock_tablabel_expose; + widget_class->button_press_event = gdl_dock_tablabel_button_event; + widget_class->button_release_event = gdl_dock_tablabel_button_event; + widget_class->motion_notify_event = gdl_dock_tablabel_motion_event; + widget_class->realize = gdl_dock_tablabel_realize; + widget_class->unrealize = gdl_dock_tablabel_unrealize; + widget_class->map = gdl_dock_tablabel_map; + widget_class->unmap = gdl_dock_tablabel_unmap; + + g_object_class_install_property ( + g_object_class, PROP_ITEM, + g_param_spec_object ("item", _("Controlling dock item"), + _("Dockitem which 'owns' this tablabel"), + GDL_TYPE_DOCK_ITEM, + G_PARAM_READWRITE)); + + dock_tablabel_signals [BUTTON_PRESSED_HANDLE] = + g_signal_new ("button_pressed_handle", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GdlDockTablabelClass, + button_pressed_handle), + NULL, NULL, + gdl_marshal_VOID__BOXED, + G_TYPE_NONE, + 1, + GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + + klass->button_pressed_handle = NULL; +} + +static void +gdl_dock_tablabel_instance_init (GdlDockTablabel *tablabel) +{ + GtkWidget *widget; + GtkWidget *label_widget; + + widget = GTK_WIDGET (tablabel); + + tablabel->drag_handle_size = DEFAULT_DRAG_HANDLE_SIZE; + tablabel->item = NULL; + + label_widget = gtk_label_new ("Dock item"); + gtk_container_add (GTK_CONTAINER (tablabel), label_widget); + gtk_widget_show (label_widget); + + tablabel->active = FALSE; + gtk_widget_set_state (GTK_WIDGET (tablabel), GTK_STATE_ACTIVE); +} + +static void +gdl_dock_tablabel_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdlDockTablabel *tablabel; + GtkBin *bin; + + tablabel = GDL_DOCK_TABLABEL (object); + + switch (prop_id) { + case PROP_ITEM: + if (tablabel->item) { + g_object_remove_weak_pointer (G_OBJECT (tablabel->item), + (gpointer *) &tablabel->item); + g_signal_handlers_disconnect_by_func ( + tablabel->item, gdl_dock_tablabel_item_notify, tablabel); + }; + + tablabel->item = g_value_get_object (value); + if (tablabel->item) { + gboolean locked; + gchar *long_name; + + g_object_add_weak_pointer (G_OBJECT (tablabel->item), + (gpointer *) &tablabel->item); + + g_signal_connect (tablabel->item, "notify::locked", + G_CALLBACK (gdl_dock_tablabel_item_notify), + tablabel); + g_signal_connect (tablabel->item, "notify::long_name", + G_CALLBACK (gdl_dock_tablabel_item_notify), + tablabel); + g_signal_connect (tablabel->item, "notify::grip_size", + G_CALLBACK (gdl_dock_tablabel_item_notify), + tablabel); + + g_object_get (tablabel->item, + "locked", &locked, + "long-name", &long_name, + "grip-size", &tablabel->drag_handle_size, + NULL); + + if (locked) + tablabel->drag_handle_size = 0; + + bin = GTK_BIN (tablabel); + if (bin->child && g_object_class_find_property ( + G_OBJECT_GET_CLASS (bin->child), "label")) + g_object_set (bin->child, "label", long_name, NULL); + g_free (long_name); + }; + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdl_dock_tablabel_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdlDockTablabel *tablabel; + + tablabel = GDL_DOCK_TABLABEL (object); + + switch (prop_id) { + case PROP_ITEM: + g_value_set_object (value, tablabel->item); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdl_dock_tablabel_item_notify (GObject *master, + GParamSpec *pspec, + gpointer data) +{ + GdlDockTablabel *tablabel = GDL_DOCK_TABLABEL (data); + gboolean locked; + gchar *label; + GtkBin *bin; + + g_object_get (master, + "locked", &locked, + "grip-size", &tablabel->drag_handle_size, + "long-name", &label, + NULL); + + if (locked) + tablabel->drag_handle_size = 0; + + bin = GTK_BIN (tablabel); + if (bin->child && g_object_class_find_property ( + G_OBJECT_GET_CLASS (bin->child), "label")) + g_object_set (bin->child, "label", label, NULL); + g_free (label); + + gtk_widget_queue_resize (GTK_WIDGET (tablabel)); +} + +static void +gdl_dock_tablabel_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + GtkBin *bin; + GtkRequisition child_req; + GdlDockTablabel *tablabel; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GDL_IS_DOCK_TABLABEL (widget)); + g_return_if_fail (requisition != NULL); + + tablabel = GDL_DOCK_TABLABEL (widget); + bin = GTK_BIN (widget); + + requisition->width = tablabel->drag_handle_size; + requisition->height = 0; + + if (bin->child) + gtk_widget_size_request (bin->child, &child_req); + else + child_req.width = child_req.height = 0; + + requisition->width += child_req.width; + requisition->height += child_req.height; + + requisition->width += GTK_CONTAINER (widget)->border_width * 2; + requisition->height += GTK_CONTAINER (widget)->border_width * 2; + + widget->requisition = *requisition; +} + +static void +gdl_dock_tablabel_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkBin *bin; + GdlDockTablabel *tablabel; + gint border_width; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GDL_IS_DOCK_TABLABEL (widget)); + g_return_if_fail (allocation != NULL); + + bin = GTK_BIN (widget); + tablabel = GDL_DOCK_TABLABEL (widget); + + border_width = GTK_CONTAINER (widget)->border_width; + + widget->allocation = *allocation; + + if (GTK_WIDGET_REALIZED (widget)) + gdk_window_move_resize (tablabel->event_window, + allocation->x, + allocation->y, + allocation->width, + allocation->height); + + if (bin->child && GTK_WIDGET_VISIBLE (bin->child)) { + GtkAllocation child_allocation; + + child_allocation.x = widget->allocation.x + border_width; + child_allocation.y = widget->allocation.y + border_width; + + allocation->width = MAX (1, (int) allocation->width - + (int) tablabel->drag_handle_size); + child_allocation.x += tablabel->drag_handle_size; + + child_allocation.width = + MAX (1, (int) allocation->width - 2 * border_width); + child_allocation.height = + MAX (1, (int) allocation->height - 2 * border_width); + + gtk_widget_size_allocate (bin->child, &child_allocation); + } +} + +static void +gdl_dock_tablabel_paint (GtkWidget *widget, + GdkEventExpose *event) +{ + GdkRectangle dest, rect; + GtkBin *bin; + GdlDockTablabel *tablabel; + gint border_width; + + bin = GTK_BIN (widget); + tablabel = GDL_DOCK_TABLABEL (widget); + border_width = GTK_CONTAINER (widget)->border_width; + + rect.x = widget->allocation.x + border_width; + rect.y = widget->allocation.y + border_width; + rect.width = tablabel->drag_handle_size * HANDLE_RATIO; + rect.height = widget->allocation.height - 2*border_width; + + if (gdk_rectangle_intersect (&event->area, &rect, &dest)) { + gtk_paint_handle (widget->style, widget->window, + tablabel->active ? GTK_STATE_NORMAL : GTK_STATE_ACTIVE, + GTK_SHADOW_NONE, + &dest, widget, "dock-tablabel", + rect.x, rect.y, rect.width, rect.height, + GTK_ORIENTATION_VERTICAL); + }; +} + +static gint +gdl_dock_tablabel_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GDL_IS_DOCK_TABLABEL (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget)) { + GDL_CALL_PARENT_GBOOLEAN(GTK_WIDGET_CLASS, expose_event, (widget,event)); + gdl_dock_tablabel_paint (widget, event); + }; + + return FALSE; +} + +static gboolean +gdl_dock_tablabel_button_event (GtkWidget *widget, + GdkEventButton *event) +{ + GdlDockTablabel *tablabel; + gboolean event_handled; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GDL_IS_DOCK_TABLABEL (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + tablabel = GDL_DOCK_TABLABEL (widget); + + event_handled = FALSE; + + if (event->window != tablabel->event_window) + return FALSE; + + switch (event->type) { + case GDK_BUTTON_PRESS: + if (tablabel->active) { + gboolean in_handle; + gint rel_x, rel_y; + guint border_width; + GtkBin *bin; + + bin = GTK_BIN (widget); + border_width = GTK_CONTAINER (widget)->border_width; + + rel_x = event->x - border_width; + rel_y = event->y - border_width; + + /* Check if user clicked on the drag handle. */ + in_handle = (rel_x < tablabel->drag_handle_size * HANDLE_RATIO) && + (rel_x > 0); + + if (event->button == 1) { + tablabel->pre_drag = TRUE; + tablabel->drag_start_event = *event; + } + else { + g_signal_emit (widget, + dock_tablabel_signals [BUTTON_PRESSED_HANDLE], + 0, + event); + } + + event_handled = TRUE; + } + break; + + case GDK_BUTTON_RELEASE: + tablabel->pre_drag = FALSE; + break; + + default: + break; + } + + if (!event_handled) { + /* propagate the event to the parent's gdkwindow */ + GdkEventButton e; + + e = *event; + e.window = gtk_widget_get_parent_window (widget); + e.x += widget->allocation.x; + e.y += widget->allocation.y; + + gdk_event_put ((GdkEvent *) &e); + }; + + return event_handled; +} + +static gboolean +gdl_dock_tablabel_motion_event (GtkWidget *widget, + GdkEventMotion *event) +{ + GdlDockTablabel *tablabel; + gboolean event_handled; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GDL_IS_DOCK_TABLABEL (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + tablabel = GDL_DOCK_TABLABEL (widget); + + event_handled = FALSE; + + if (event->window != tablabel->event_window) + return FALSE; + + if (tablabel->pre_drag) { + if (gtk_drag_check_threshold (widget, + tablabel->drag_start_event.x, + tablabel->drag_start_event.y, + event->x, + event->y)) { + tablabel->pre_drag = FALSE; + g_signal_emit (widget, + dock_tablabel_signals [BUTTON_PRESSED_HANDLE], + 0, + &tablabel->drag_start_event); + event_handled = TRUE; + } + } + + if (!event_handled) { + /* propagate the event to the parent's gdkwindow */ + GdkEventMotion e; + + e = *event; + e.window = gtk_widget_get_parent_window (widget); + e.x += widget->allocation.x; + e.y += widget->allocation.y; + + gdk_event_put ((GdkEvent *) &e); + }; + + return event_handled; +} + +static void +gdl_dock_tablabel_realize (GtkWidget *widget) +{ + GdlDockTablabel *tablabel; + GdkWindowAttr attributes; + int attributes_mask; + + tablabel = GDL_DOCK_TABLABEL (widget); + + attributes.window_type = GDK_WINDOW_CHILD; + attributes.x = widget->allocation.x; + attributes.y = widget->allocation.y; + attributes.width = widget->allocation.width; + attributes.height = widget->allocation.height; + attributes.wclass = GDK_INPUT_ONLY; + attributes.event_mask = gtk_widget_get_events (widget); + attributes.event_mask |= (GDK_EXPOSURE_MASK | + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_POINTER_MOTION_MASK | + GDK_LEAVE_NOTIFY_MASK); + attributes_mask = GDK_WA_X | GDK_WA_Y; + + widget->window = gtk_widget_get_parent_window (widget); + g_object_ref (widget->window); + + tablabel->event_window = + gdk_window_new (gtk_widget_get_parent_window (widget), + &attributes, attributes_mask); + gdk_window_set_user_data (tablabel->event_window, widget); + + widget->style = gtk_style_attach (widget->style, widget->window); + + GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); +} + +static void +gdl_dock_tablabel_unrealize (GtkWidget *widget) +{ + GdlDockTablabel *tablabel = GDL_DOCK_TABLABEL (widget); + + if (tablabel->event_window) { + gdk_window_set_user_data (tablabel->event_window, NULL); + gdk_window_destroy (tablabel->event_window); + tablabel->event_window = NULL; + } + + GDL_CALL_PARENT (GTK_WIDGET_CLASS, unrealize, (widget)); +} + +static void +gdl_dock_tablabel_map (GtkWidget *widget) +{ + GdlDockTablabel *tablabel = GDL_DOCK_TABLABEL (widget); + + GDL_CALL_PARENT (GTK_WIDGET_CLASS, map, (widget)); + + gdk_window_show (tablabel->event_window); +} + +static void +gdl_dock_tablabel_unmap (GtkWidget *widget) +{ + GdlDockTablabel *tablabel = GDL_DOCK_TABLABEL (widget); + + gdk_window_hide (tablabel->event_window); + + GDL_CALL_PARENT (GTK_WIDGET_CLASS, unmap, (widget)); +} + +/* ----- Public interface ----- */ + +GtkWidget * +gdl_dock_tablabel_new (GdlDockItem *item) +{ + GdlDockTablabel *tablabel; + + tablabel = GDL_DOCK_TABLABEL (g_object_new (GDL_TYPE_DOCK_TABLABEL, + "item", item, + NULL)); + + return GTK_WIDGET (tablabel); +} + +void +gdl_dock_tablabel_activate (GdlDockTablabel *tablabel) +{ + g_return_if_fail (tablabel != NULL); + + tablabel->active = TRUE; + gtk_widget_set_state (GTK_WIDGET (tablabel), GTK_STATE_NORMAL); +} + +void +gdl_dock_tablabel_deactivate (GdlDockTablabel *tablabel) +{ + g_return_if_fail (tablabel != NULL); + + tablabel->active = FALSE; + /* yeah, i know it contradictive */ + gtk_widget_set_state (GTK_WIDGET (tablabel), GTK_STATE_ACTIVE); +} diff --git a/src/libgdl/gdl-dock-tablabel.h b/src/libgdl/gdl-dock-tablabel.h new file mode 100644 index 000000000..8cf3470eb --- /dev/null +++ b/src/libgdl/gdl-dock-tablabel.h @@ -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 + * + * 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 +#include "libgdl/gdl-dock-item.h" + + +G_BEGIN_DECLS + +/* standard macros */ +#define GDL_TYPE_DOCK_TABLABEL (gdl_dock_tablabel_get_type ()) +#define GDL_DOCK_TABLABEL(obj) (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_TABLABEL, GdlDockTablabel)) +#define GDL_DOCK_TABLABEL_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_TABLABEL, GdlDockTablabelClass)) +#define GDL_IS_DOCK_TABLABEL(obj) (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_TABLABEL)) +#define GDL_IS_DOCK_TABLABEL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_TABLABEL)) +#define GDL_DOCK_TABLABEL_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK_TABLABEL, GdlDockTablabelClass)) + +/* data types & structures */ +typedef struct _GdlDockTablabel GdlDockTablabel; +typedef struct _GdlDockTablabelClass GdlDockTablabelClass; + +struct _GdlDockTablabel { + GtkBin parent; + + guint drag_handle_size; + GtkWidget *item; + GdkWindow *event_window; + gboolean active; + + GdkEventButton drag_start_event; + gboolean pre_drag; +}; + +struct _GdlDockTablabelClass { + GtkBinClass parent_class; + + void (*button_pressed_handle) (GdlDockTablabel *tablabel, + GdkEventButton *event); +}; + +/* public interface */ + +GtkWidget *gdl_dock_tablabel_new (GdlDockItem *item); +GType gdl_dock_tablabel_get_type (void); + +void gdl_dock_tablabel_activate (GdlDockTablabel *tablabel); +void gdl_dock_tablabel_deactivate (GdlDockTablabel *tablabel); + +G_END_DECLS + +#endif diff --git a/src/libgdl/gdl-dock.c b/src/libgdl/gdl-dock.c new file mode 100644 index 000000000..131a7bbd1 --- /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 + * 2007 Naba Kumar + * + * 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 +#endif + +#include "gdl-i18n.h" +#include +#include + +#include "gdl-tools.h" +#include "gdl-dock.h" +#include "gdl-dock-master.h" +#include "gdl-dock-paned.h" +#include "gdl-dock-notebook.h" +#include "gdl-dock-placeholder.h" + +#include "libgdlmarshal.h" + + +/* ----- Private prototypes ----- */ + +static void gdl_dock_class_init (GdlDockClass *class); +static void gdl_dock_instance_init (GdlDock *dock); + +static GObject *gdl_dock_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_param); +static void gdl_dock_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gdl_dock_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void gdl_dock_notify_cb (GObject *object, + GParamSpec *pspec, + gpointer user_data); + +static void gdl_dock_set_title (GdlDock *dock); + +static void gdl_dock_destroy (GtkObject *object); + +static void gdl_dock_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void gdl_dock_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); +static void gdl_dock_map (GtkWidget *widget); +static void gdl_dock_unmap (GtkWidget *widget); +static void gdl_dock_show (GtkWidget *widget); +static void gdl_dock_hide (GtkWidget *widget); + +static void gdl_dock_add (GtkContainer *container, + GtkWidget *widget); +static void gdl_dock_remove (GtkContainer *container, + GtkWidget *widget); +static void gdl_dock_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data); +static GtkType gdl_dock_child_type (GtkContainer *container); + +static void gdl_dock_detach (GdlDockObject *object, + gboolean recursive); +static void gdl_dock_reduce (GdlDockObject *object); +static gboolean gdl_dock_dock_request (GdlDockObject *object, + gint x, + gint y, + GdlDockRequest *request); +static void gdl_dock_dock (GdlDockObject *object, + GdlDockObject *requestor, + GdlDockPlacement position, + GValue *other_data); +static gboolean gdl_dock_reorder (GdlDockObject *object, + GdlDockObject *requestor, + GdlDockPlacement new_position, + GValue *other_data); + +static gboolean gdl_dock_floating_window_delete_event_cb (GtkWidget *widget); + +static gboolean gdl_dock_child_placement (GdlDockObject *object, + GdlDockObject *child, + GdlDockPlacement *placement); + +static void gdl_dock_present (GdlDockObject *object, + GdlDockObject *child); + + +/* ----- Class variables and definitions ----- */ + +struct _GdlDockPrivate +{ + /* for floating docks */ + gboolean floating; + GtkWidget *window; + gboolean auto_title; + + gint float_x; + gint float_y; + gint width; + gint height; + + /* auxiliary fields */ + GdkGC *xor_gc; +}; + +enum { + LAYOUT_CHANGED, + LAST_SIGNAL +}; + +enum { + PROP_0, + PROP_FLOATING, + PROP_DEFAULT_TITLE, + PROP_WIDTH, + PROP_HEIGHT, + PROP_FLOAT_X, + PROP_FLOAT_Y +}; + +static guint dock_signals [LAST_SIGNAL] = { 0 }; + +#define SPLIT_RATIO 0.3 + + +/* ----- Private functions ----- */ + +GDL_CLASS_BOILERPLATE (GdlDock, gdl_dock, GdlDockObject, GDL_TYPE_DOCK_OBJECT); + +static void +gdl_dock_class_init (GdlDockClass *klass) +{ + GObjectClass *g_object_class; + GtkObjectClass *gtk_object_class; + GtkWidgetClass *widget_class; + GtkContainerClass *container_class; + GdlDockObjectClass *object_class; + + g_object_class = G_OBJECT_CLASS (klass); + gtk_object_class = GTK_OBJECT_CLASS (klass); + widget_class = GTK_WIDGET_CLASS (klass); + container_class = GTK_CONTAINER_CLASS (klass); + object_class = GDL_DOCK_OBJECT_CLASS (klass); + + g_object_class->constructor = gdl_dock_constructor; + g_object_class->set_property = gdl_dock_set_property; + g_object_class->get_property = gdl_dock_get_property; + + /* properties */ + + g_object_class_install_property ( + g_object_class, PROP_FLOATING, + g_param_spec_boolean ("floating", _("Floating"), + _("Whether the dock is floating in its own window"), + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + GDL_DOCK_PARAM_EXPORT)); + + g_object_class_install_property ( + g_object_class, PROP_DEFAULT_TITLE, + g_param_spec_string ("default-title", _("Default title"), + _("Default title for the newly created floating docks"), + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + g_object_class, PROP_WIDTH, + g_param_spec_int ("width", _("Width"), + _("Width for the dock when it's of floating type"), + -1, G_MAXINT, -1, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | + GDL_DOCK_PARAM_EXPORT)); + + g_object_class_install_property ( + g_object_class, PROP_HEIGHT, + g_param_spec_int ("height", _("Height"), + _("Height for the dock when it's of floating type"), + -1, G_MAXINT, -1, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | + GDL_DOCK_PARAM_EXPORT)); + + g_object_class_install_property ( + g_object_class, PROP_FLOAT_X, + g_param_spec_int ("floatx", _("Float X"), + _("X coordinate for a floating dock"), + G_MININT, G_MAXINT, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | + GDL_DOCK_PARAM_EXPORT)); + + g_object_class_install_property ( + g_object_class, PROP_FLOAT_Y, + g_param_spec_int ("floaty", _("Float Y"), + _("Y coordinate for a floating dock"), + G_MININT, G_MAXINT, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | + GDL_DOCK_PARAM_EXPORT)); + + gtk_object_class->destroy = gdl_dock_destroy; + + widget_class->size_request = gdl_dock_size_request; + widget_class->size_allocate = gdl_dock_size_allocate; + widget_class->map = gdl_dock_map; + widget_class->unmap = gdl_dock_unmap; + widget_class->show = gdl_dock_show; + widget_class->hide = gdl_dock_hide; + + container_class->add = gdl_dock_add; + container_class->remove = gdl_dock_remove; + container_class->forall = gdl_dock_forall; + container_class->child_type = gdl_dock_child_type; + + object_class->is_compound = TRUE; + + object_class->detach = gdl_dock_detach; + object_class->reduce = gdl_dock_reduce; + object_class->dock_request = gdl_dock_dock_request; + object_class->dock = gdl_dock_dock; + object_class->reorder = gdl_dock_reorder; + object_class->child_placement = gdl_dock_child_placement; + object_class->present = gdl_dock_present; + + /* signals */ + + dock_signals [LAYOUT_CHANGED] = + g_signal_new ("layout-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GdlDockClass, layout_changed), + NULL, /* accumulator */ + NULL, /* accu_data */ + gdl_marshal_VOID__VOID, + G_TYPE_NONE, /* return type */ + 0); + + klass->layout_changed = NULL; +} + +static void +gdl_dock_instance_init (GdlDock *dock) +{ + GTK_WIDGET_SET_FLAGS (GTK_WIDGET (dock), GTK_NO_WINDOW); + + dock->root = NULL; + dock->_priv = g_new0 (GdlDockPrivate, 1); + dock->_priv->width = -1; + dock->_priv->height = -1; +} + +static gboolean +gdl_dock_floating_configure_event_cb (GtkWidget *widget, + GdkEventConfigure *event, + gpointer user_data) +{ + GdlDock *dock; + + g_return_val_if_fail (user_data != NULL && GDL_IS_DOCK (user_data), TRUE); + + dock = GDL_DOCK (user_data); + dock->_priv->float_x = event->x; + dock->_priv->float_y = event->y; + dock->_priv->width = event->width; + dock->_priv->height = event->height; + + return FALSE; +} + +static GObject * +gdl_dock_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_param) +{ + GObject *g_object; + + g_object = GDL_CALL_PARENT_WITH_DEFAULT (G_OBJECT_CLASS, + constructor, + (type, + n_construct_properties, + construct_param), + NULL); + if (g_object) { + GdlDock *dock = GDL_DOCK (g_object); + GdlDockMaster *master; + + /* create a master for the dock if none was provided in the construction */ + master = GDL_DOCK_OBJECT_GET_MASTER (GDL_DOCK_OBJECT (dock)); + if (!master) { + GDL_DOCK_OBJECT_UNSET_FLAGS (dock, GDL_DOCK_AUTOMATIC); + master = g_object_new (GDL_TYPE_DOCK_MASTER, NULL); + /* the controller owns the master ref */ + gdl_dock_object_bind (GDL_DOCK_OBJECT (dock), G_OBJECT (master)); + } + + if (dock->_priv->floating) { + GdlDockObject *controller; + + /* create floating window for this dock */ + dock->_priv->window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + g_object_set_data (G_OBJECT (dock->_priv->window), "dock", dock); + + /* set position and default size */ + gtk_window_set_position (GTK_WINDOW (dock->_priv->window), + GTK_WIN_POS_MOUSE); + gtk_window_set_default_size (GTK_WINDOW (dock->_priv->window), + dock->_priv->width, + dock->_priv->height); + gtk_window_set_type_hint (GTK_WINDOW (dock->_priv->window), + GDK_WINDOW_TYPE_HINT_NORMAL); + + gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dock->_priv->window), + TRUE); + + /* metacity ignores this */ + gtk_window_move (GTK_WINDOW (dock->_priv->window), + dock->_priv->float_x, + dock->_priv->float_y); + + /* connect to the configure event so we can track down window geometry */ + g_signal_connect (dock->_priv->window, "configure_event", + (GCallback) gdl_dock_floating_configure_event_cb, + dock); + + /* set the title and connect to the long_name notify queue + so we can reset the title when this prop changes */ + gdl_dock_set_title (dock); + g_signal_connect (dock, "notify::long-name", + (GCallback) gdl_dock_notify_cb, NULL); + + /* set transient for the first dock if that is a non-floating dock */ + controller = gdl_dock_master_get_controller (master); + if (controller && GDL_IS_DOCK (controller)) { + gboolean first_is_floating; + g_object_get (controller, "floating", &first_is_floating, NULL); + if (!first_is_floating) { + GtkWidget *toplevel = + gtk_widget_get_toplevel (GTK_WIDGET (controller)); + + if (GTK_IS_WINDOW (toplevel)) + gtk_window_set_transient_for (GTK_WINDOW (dock->_priv->window), + GTK_WINDOW (toplevel)); + } + } + + gtk_container_add (GTK_CONTAINER (dock->_priv->window), GTK_WIDGET (dock)); + + g_signal_connect (dock->_priv->window, "delete_event", + G_CALLBACK (gdl_dock_floating_window_delete_event_cb), + NULL); + } + GDL_DOCK_OBJECT_SET_FLAGS (dock, GDL_DOCK_ATTACHED); + } + + return g_object; +} + +static void +gdl_dock_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdlDock *dock = GDL_DOCK (object); + + switch (prop_id) { + case PROP_FLOATING: + dock->_priv->floating = g_value_get_boolean (value); + break; + case PROP_DEFAULT_TITLE: + if (GDL_DOCK_OBJECT (object)->master) + g_object_set (GDL_DOCK_OBJECT (object)->master, + "default-title", g_value_get_string (value), + NULL); + break; + case PROP_WIDTH: + dock->_priv->width = g_value_get_int (value); + break; + case PROP_HEIGHT: + dock->_priv->height = g_value_get_int (value); + break; + case PROP_FLOAT_X: + dock->_priv->float_x = g_value_get_int (value); + break; + case PROP_FLOAT_Y: + dock->_priv->float_y = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + + switch (prop_id) { + case PROP_WIDTH: + case PROP_HEIGHT: + case PROP_FLOAT_X: + case PROP_FLOAT_Y: + if (dock->_priv->floating && dock->_priv->window) { + gtk_window_resize (GTK_WINDOW (dock->_priv->window), + dock->_priv->width, + dock->_priv->height); + } + break; + } +} + +static void +gdl_dock_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdlDock *dock = GDL_DOCK (object); + + switch (prop_id) { + case PROP_FLOATING: + g_value_set_boolean (value, dock->_priv->floating); + break; + case PROP_DEFAULT_TITLE: + if (GDL_DOCK_OBJECT (object)->master) { + gchar *default_title; + g_object_get (GDL_DOCK_OBJECT (object)->master, + "default-title", &default_title, + NULL); +#if GLIB_CHECK_VERSION(2,3,0) + g_value_take_string (value, default_title); +#else + g_value_set_string_take_ownership (value, default_title); +#endif + } + else + g_value_set_string (value, NULL); + break; + case PROP_WIDTH: + g_value_set_int (value, dock->_priv->width); + break; + case PROP_HEIGHT: + g_value_set_int (value, dock->_priv->height); + break; + case PROP_FLOAT_X: + g_value_set_int (value, dock->_priv->float_x); + break; + case PROP_FLOAT_Y: + g_value_set_int (value, dock->_priv->float_y); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdl_dock_set_title (GdlDock *dock) +{ + GdlDockObject *object = GDL_DOCK_OBJECT (dock); + gchar *title = NULL; + gboolean free_title = FALSE; + + if (!dock->_priv->window) + return; + + if (!dock->_priv->auto_title && object->long_name) { + title = object->long_name; + } + else if (object->master) { + g_object_get (object->master, "default-title", &title, NULL); + free_title = TRUE; + } + + if (!title && dock->root) { + g_object_get (dock->root, "long-name", &title, NULL); + free_title = TRUE; + } + + if (!title) { + /* set a default title in the long_name */ + dock->_priv->auto_title = TRUE; + free_title = FALSE; + title = object->long_name = g_strdup_printf ( + _("Dock #%d"), GDL_DOCK_MASTER (object->master)->dock_number++); + } + + gtk_window_set_title (GTK_WINDOW (dock->_priv->window), title); + if (free_title) + g_free (title); +} + +static void +gdl_dock_notify_cb (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + GdlDock *dock; + + g_return_if_fail (object != NULL || GDL_IS_DOCK (object)); + + dock = GDL_DOCK (object); + dock->_priv->auto_title = FALSE; + gdl_dock_set_title (dock); +} + +static void +gdl_dock_destroy (GtkObject *object) +{ + GdlDock *dock = GDL_DOCK (object); + + if (dock->_priv) { + GdlDockPrivate *priv = dock->_priv; + dock->_priv = NULL; + + if (priv->window) { + gtk_widget_destroy (priv->window); + priv->floating = FALSE; + priv->window = NULL; + } + + /* destroy the xor gc */ + if (priv->xor_gc) { + g_object_unref (priv->xor_gc); + priv->xor_gc = NULL; + } + + g_free (priv); + } + + GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object)); +} + +static void +gdl_dock_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + GdlDock *dock; + GtkContainer *container; + guint border_width; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GDL_IS_DOCK (widget)); + + dock = GDL_DOCK (widget); + container = GTK_CONTAINER (widget); + border_width = container->border_width; + + /* make request to root */ + if (dock->root && GTK_WIDGET_VISIBLE (dock->root)) + gtk_widget_size_request (GTK_WIDGET (dock->root), requisition); + else { + requisition->width = 0; + requisition->height = 0; + }; + + requisition->width += 2 * border_width; + requisition->height += 2 * border_width; + + widget->requisition = *requisition; +} + +static void +gdl_dock_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GdlDock *dock; + GtkContainer *container; + guint border_width; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GDL_IS_DOCK (widget)); + + dock = GDL_DOCK (widget); + container = GTK_CONTAINER (widget); + border_width = container->border_width; + + widget->allocation = *allocation; + + /* reduce allocation by border width */ + allocation->x += border_width; + allocation->y += border_width; + allocation->width = MAX (1, allocation->width - 2 * border_width); + allocation->height = MAX (1, allocation->height - 2 * border_width); + + if (dock->root && GTK_WIDGET_VISIBLE (dock->root)) + gtk_widget_size_allocate (GTK_WIDGET (dock->root), allocation); +} + +static void +gdl_dock_map (GtkWidget *widget) +{ + GtkWidget *child; + GdlDock *dock; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GDL_IS_DOCK (widget)); + + dock = GDL_DOCK (widget); + + GDL_CALL_PARENT (GTK_WIDGET_CLASS, map, (widget)); + + if (dock->root) { + child = GTK_WIDGET (dock->root); + if (GTK_WIDGET_VISIBLE (child) && !GTK_WIDGET_MAPPED (child)) + gtk_widget_map (child); + } +} + +static void +gdl_dock_unmap (GtkWidget *widget) +{ + GtkWidget *child; + GdlDock *dock; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GDL_IS_DOCK (widget)); + + dock = GDL_DOCK (widget); + + GDL_CALL_PARENT (GTK_WIDGET_CLASS, unmap, (widget)); + + if (dock->root) { + child = GTK_WIDGET (dock->root); + if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_MAPPED (child)) + gtk_widget_unmap (child); + } + + if (dock->_priv->window) + gtk_widget_unmap (dock->_priv->window); +} + +static void +gdl_dock_foreach_automatic (GdlDockObject *object, + gpointer user_data) +{ + void (* function) (GtkWidget *) = user_data; + + if (GDL_DOCK_OBJECT_AUTOMATIC (object)) + (* function) (GTK_WIDGET (object)); +} + +static void +gdl_dock_show (GtkWidget *widget) +{ + GdlDock *dock; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GDL_IS_DOCK (widget)); + + GDL_CALL_PARENT (GTK_WIDGET_CLASS, show, (widget)); + + dock = GDL_DOCK (widget); + if (dock->_priv->floating && dock->_priv->window) + gtk_widget_show (dock->_priv->window); + + if (GDL_DOCK_IS_CONTROLLER (dock)) { + gdl_dock_master_foreach_toplevel (GDL_DOCK_OBJECT_GET_MASTER (dock), + FALSE, (GFunc) gdl_dock_foreach_automatic, + gtk_widget_show); + } +} + +static void +gdl_dock_hide (GtkWidget *widget) +{ + GdlDock *dock; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GDL_IS_DOCK (widget)); + + GDL_CALL_PARENT (GTK_WIDGET_CLASS, hide, (widget)); + + dock = GDL_DOCK (widget); + if (dock->_priv->floating && dock->_priv->window) + gtk_widget_hide (dock->_priv->window); + + if (GDL_DOCK_IS_CONTROLLER (dock)) { + gdl_dock_master_foreach_toplevel (GDL_DOCK_OBJECT_GET_MASTER (dock), + FALSE, (GFunc) gdl_dock_foreach_automatic, + gtk_widget_hide); + } +} + +static void +gdl_dock_add (GtkContainer *container, + GtkWidget *widget) +{ + g_return_if_fail (container != NULL); + g_return_if_fail (GDL_IS_DOCK (container)); + g_return_if_fail (GDL_IS_DOCK_ITEM (widget)); + + gdl_dock_add_item (GDL_DOCK (container), + GDL_DOCK_ITEM (widget), + GDL_DOCK_TOP); /* default position */ +} + +static void +gdl_dock_remove (GtkContainer *container, + GtkWidget *widget) +{ + GdlDock *dock; + gboolean was_visible; + + g_return_if_fail (container != NULL); + g_return_if_fail (widget != NULL); + + dock = GDL_DOCK (container); + was_visible = GTK_WIDGET_VISIBLE (widget); + + if (GTK_WIDGET (dock->root) == widget) { + dock->root = NULL; + GDL_DOCK_OBJECT_UNSET_FLAGS (widget, GDL_DOCK_ATTACHED); + gtk_widget_unparent (widget); + + if (was_visible && GTK_WIDGET_VISIBLE (GTK_WIDGET (container))) + gtk_widget_queue_resize (GTK_WIDGET (dock)); + } +} + +static void +gdl_dock_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) +{ + GdlDock *dock; + + g_return_if_fail (container != NULL); + g_return_if_fail (GDL_IS_DOCK (container)); + g_return_if_fail (callback != NULL); + + dock = GDL_DOCK (container); + + if (dock->root) + (*callback) (GTK_WIDGET (dock->root), callback_data); +} + +static GtkType +gdl_dock_child_type (GtkContainer *container) +{ + return GDL_TYPE_DOCK_ITEM; +} + +static void +gdl_dock_detach (GdlDockObject *object, + gboolean recursive) +{ + GdlDock *dock = GDL_DOCK (object); + + /* detach children */ + if (recursive && dock->root) { + gdl_dock_object_detach (dock->root, recursive); + } + GDL_DOCK_OBJECT_UNSET_FLAGS (object, GDL_DOCK_ATTACHED); +} + +static void +gdl_dock_reduce (GdlDockObject *object) +{ + GdlDock *dock = GDL_DOCK (object); + + if (dock->root) + return; + + if (GDL_DOCK_OBJECT_AUTOMATIC (dock)) { + gtk_widget_destroy (GTK_WIDGET (dock)); + + } else if (!GDL_DOCK_OBJECT_ATTACHED (dock)) { + /* if the user explicitly detached the object */ + if (dock->_priv->floating) + gtk_widget_hide (GTK_WIDGET (dock)); + else { + GtkWidget *widget = GTK_WIDGET (object); + if (widget->parent) + gtk_container_remove (GTK_CONTAINER (widget->parent), widget); + } + } +} + +static gboolean +gdl_dock_dock_request (GdlDockObject *object, + gint x, + gint y, + GdlDockRequest *request) +{ + GdlDock *dock; + guint bw; + gint rel_x, rel_y; + GtkAllocation *alloc; + gboolean may_dock = FALSE; + GdlDockRequest my_request; + + g_return_val_if_fail (GDL_IS_DOCK (object), FALSE); + + /* we get (x,y) in our allocation coordinates system */ + + dock = GDL_DOCK (object); + + /* Get dock size. */ + alloc = &(GTK_WIDGET (dock)->allocation); + bw = GTK_CONTAINER (dock)->border_width; + + /* Get coordinates relative to our allocation area. */ + rel_x = x - alloc->x; + rel_y = y - alloc->y; + + if (request) + my_request = *request; + + /* Check if coordinates are in GdlDock widget. */ + if (rel_x > 0 && rel_x < alloc->width && + rel_y > 0 && rel_y < alloc->height) { + + /* It's inside our area. */ + may_dock = TRUE; + + /* Set docking indicator rectangle to the GdlDock size. */ + my_request.rect.x = alloc->x + bw; + my_request.rect.y = alloc->y + bw; + my_request.rect.width = alloc->width - 2*bw; + my_request.rect.height = alloc->height - 2*bw; + + /* If GdlDock has no root item yet, set the dock itself as + possible target. */ + if (!dock->root) { + my_request.position = GDL_DOCK_TOP; + my_request.target = object; + } else { + my_request.target = dock->root; + + /* See if it's in the border_width band. */ + if (rel_x < bw) { + my_request.position = GDL_DOCK_LEFT; + my_request.rect.width *= SPLIT_RATIO; + } else if (rel_x > alloc->width - bw) { + my_request.position = GDL_DOCK_RIGHT; + my_request.rect.x += my_request.rect.width * (1 - SPLIT_RATIO); + my_request.rect.width *= SPLIT_RATIO; + } else if (rel_y < bw) { + my_request.position = GDL_DOCK_TOP; + my_request.rect.height *= SPLIT_RATIO; + } else if (rel_y > alloc->height - bw) { + my_request.position = GDL_DOCK_BOTTOM; + my_request.rect.y += my_request.rect.height * (1 - SPLIT_RATIO); + my_request.rect.height *= SPLIT_RATIO; + } else { + /* Otherwise try our children. */ + /* give them allocation coordinates (we are a + GTK_NO_WINDOW) widget */ + may_dock = gdl_dock_object_dock_request (GDL_DOCK_OBJECT (dock->root), + x, y, &my_request); + } + } + } + + if (may_dock && request) + *request = my_request; + + return may_dock; +} + +static void +gdl_dock_dock (GdlDockObject *object, + GdlDockObject *requestor, + GdlDockPlacement position, + GValue *user_data) +{ + GdlDock *dock; + + g_return_if_fail (GDL_IS_DOCK (object)); + /* only dock items allowed at this time */ + g_return_if_fail (GDL_IS_DOCK_ITEM (requestor)); + + dock = GDL_DOCK (object); + + if (position == GDL_DOCK_FLOATING) { + GdlDockItem *item = GDL_DOCK_ITEM (requestor); + gint x, y, width, height; + + if (user_data && G_VALUE_HOLDS (user_data, GDK_TYPE_RECTANGLE)) { + GdkRectangle *rect; + + rect = g_value_get_boxed (user_data); + x = rect->x; + y = rect->y; + width = rect->width; + height = rect->height; + } + else { + x = y = 0; + width = height = -1; + } + + gdl_dock_add_floating_item (dock, item, + x, y, width, height); + } + else if (dock->root) { + /* This is somewhat a special case since we know which item to + pass the request on because we only have on child */ + gdl_dock_object_dock (dock->root, requestor, position, NULL); + gdl_dock_set_title (dock); + + } + else { /* Item about to be added is root item. */ + GtkWidget *widget = GTK_WIDGET (requestor); + + dock->root = requestor; + GDL_DOCK_OBJECT_SET_FLAGS (requestor, GDL_DOCK_ATTACHED); + gtk_widget_set_parent (widget, GTK_WIDGET (dock)); + + gdl_dock_item_show_grip (GDL_DOCK_ITEM (requestor)); + + /* Realize the item (create its corresponding GdkWindow) when + GdlDock has been realized. */ + if (GTK_WIDGET_REALIZED (dock)) + gtk_widget_realize (widget); + + /* Map the widget if it's visible and the parent is visible and has + been mapped. This is done to make sure that the GdkWindow is + visible. */ + if (GTK_WIDGET_VISIBLE (dock) && + GTK_WIDGET_VISIBLE (widget)) { + if (GTK_WIDGET_MAPPED (dock)) + gtk_widget_map (widget); + + /* Make the widget resize. */ + gtk_widget_queue_resize (widget); + } + gdl_dock_set_title (dock); + } +} + +static gboolean +gdl_dock_floating_window_delete_event_cb (GtkWidget *widget) +{ + GdlDock *dock; + + g_return_val_if_fail (GTK_IS_WINDOW (widget), FALSE); + + dock = GDL_DOCK (g_object_get_data (G_OBJECT (widget), "dock")); + if (dock->root) { + /* this will call reduce on ourselves, hiding the window if appropiate */ + gdl_dock_item_hide_item (GDL_DOCK_ITEM (dock->root)); + } + + return TRUE; +} + +static void +_gdl_dock_foreach_build_list (GdlDockObject *object, + gpointer user_data) +{ + GList **l = (GList **) user_data; + + if (GDL_IS_DOCK_ITEM (object)) + *l = g_list_prepend (*l, object); +} + +static gboolean +gdl_dock_reorder (GdlDockObject *object, + GdlDockObject *requestor, + GdlDockPlacement new_position, + GValue *other_data) +{ + GdlDock *dock = GDL_DOCK (object); + gboolean handled = FALSE; + + if (dock->_priv->floating && + new_position == GDL_DOCK_FLOATING && + dock->root == requestor) { + + if (other_data && G_VALUE_HOLDS (other_data, GDK_TYPE_RECTANGLE)) { + GdkRectangle *rect; + + rect = g_value_get_boxed (other_data); + gtk_window_move (GTK_WINDOW (dock->_priv->window), + rect->x, + rect->y); + handled = TRUE; + } + } + + return handled; +} + +static gboolean +gdl_dock_child_placement (GdlDockObject *object, + GdlDockObject *child, + GdlDockPlacement *placement) +{ + GdlDock *dock = GDL_DOCK (object); + gboolean retval = TRUE; + + if (dock->root == child) { + if (placement) { + if (*placement == GDL_DOCK_NONE || *placement == GDL_DOCK_FLOATING) + *placement = GDL_DOCK_TOP; + } + } else + retval = FALSE; + + return retval; +} + +static void +gdl_dock_present (GdlDockObject *object, + GdlDockObject *child) +{ + GdlDock *dock = GDL_DOCK (object); + + if (dock->_priv->floating) + gtk_window_present (GTK_WINDOW (dock->_priv->window)); +} + + +/* ----- Public interface ----- */ + +GtkWidget * +gdl_dock_new (void) +{ + GObject *dock; + + dock = g_object_new (GDL_TYPE_DOCK, NULL); + GDL_DOCK_OBJECT_UNSET_FLAGS (dock, GDL_DOCK_AUTOMATIC); + + return GTK_WIDGET (dock); +} + +GtkWidget * +gdl_dock_new_from (GdlDock *original, + gboolean floating) +{ + GObject *new_dock; + + g_return_val_if_fail (original != NULL, NULL); + + new_dock = g_object_new (GDL_TYPE_DOCK, + "master", GDL_DOCK_OBJECT_GET_MASTER (original), + "floating", floating, + NULL); + GDL_DOCK_OBJECT_UNSET_FLAGS (new_dock, GDL_DOCK_AUTOMATIC); + + return GTK_WIDGET (new_dock); +} + +/* Depending on where the dock item (where new item will be docked) locates + * in the dock, we might need to change the docking placement. If the + * item is does not touches the center of dock, the new-item-to-dock would + * require a center dock on this item. + */ +static GdlDockPlacement +gdl_dock_refine_placement (GdlDock *dock, GdlDockItem *dock_item, + GdlDockPlacement placement) +{ + GtkRequisition object_size; + + gdl_dock_item_preferred_size (dock_item, &object_size); + g_return_val_if_fail (GTK_WIDGET (dock)->allocation.width > 0, placement); + g_return_val_if_fail (GTK_WIDGET (dock)->allocation.height > 0, placement); + g_return_val_if_fail (object_size.width > 0, placement); + g_return_val_if_fail (object_size.height > 0, placement); + + if (placement == GDL_DOCK_LEFT || placement == GDL_DOCK_RIGHT) { + /* Check if dock_object touches center in terms of width */ + if (GTK_WIDGET (dock)->allocation.width/2 > object_size.width) { + return GDL_DOCK_CENTER; + } + } else if (placement == GDL_DOCK_TOP || placement == GDL_DOCK_BOTTOM) { + /* Check if dock_object touches center in terms of height */ + if (GTK_WIDGET (dock)->allocation.height/2 > object_size.height) { + return GDL_DOCK_CENTER; + } + } + return placement; +} + +/* Determines the larger item of the two based on the placement: + * for left/right placement, height determines it. + * for top/bottom placement, width determines it. + * for center placement, area determines it. + */ +static GdlDockItem* +gdl_dock_select_larger_item (GdlDockItem *dock_item_1, + GdlDockItem *dock_item_2, + GdlDockPlacement placement, + gint level /* for debugging */) +{ + GtkRequisition size_1, size_2; + + g_return_val_if_fail (dock_item_1 != NULL, dock_item_2); + g_return_val_if_fail (dock_item_2 != NULL, dock_item_1); + + gdl_dock_item_preferred_size (dock_item_1, &size_1); + gdl_dock_item_preferred_size (dock_item_2, &size_2); + + g_return_val_if_fail (size_1.width > 0, dock_item_2); + g_return_val_if_fail (size_1.height > 0, dock_item_2); + g_return_val_if_fail (size_2.width > 0, dock_item_1); + g_return_val_if_fail (size_2.height > 0, dock_item_1); + + if (placement == GDL_DOCK_LEFT || placement == GDL_DOCK_RIGHT) + { + /* For left/right placement, height is what matters */ + return (size_1.height >= size_2.height? + dock_item_1 : dock_item_2); + } else if (placement == GDL_DOCK_TOP || placement == GDL_DOCK_BOTTOM) + { + /* For top/bottom placement, width is what matters */ + return (size_1.width >= size_2.width? + dock_item_1 : dock_item_2); + } else if (placement == GDL_DOCK_CENTER) { + /* For center place, area is what matters */ + return ((size_1.width * size_1.height) + >= (size_2.width * size_2.height)? + dock_item_1 : dock_item_2); + } else { + g_warning ("Should not reach here: %s:%d", __FUNCTION__, __LINE__); + } + return dock_item_1; +} + +/* Determines the best dock item to dock a new item with the given placement. + * It traverses the dock tree and (based on the placement) tries to find + * the best located item wrt to the placement. The approach is to find the + * largest item on/around the placement side (for side placements) and to + * find the largest item for center placement. In most situations, this is + * what user wants and the heuristic should be therefore sufficient. + */ +static GdlDockItem* +gdl_dock_find_best_placement_item (GdlDockItem *dock_item, + GdlDockPlacement placement, + gint level /* for debugging */) +{ + GdlDockItem *ret_item = NULL; + + if (GDL_IS_DOCK_PANED (dock_item)) + { + GtkOrientation orientation; + GdlDockItem *dock_item_1, *dock_item_2; + GList* children; + + children = gtk_container_get_children (GTK_CONTAINER (dock_item)); + + g_assert (g_list_length (children) == 2); + + g_object_get (dock_item, "orientation", &orientation, NULL); + if ((orientation == GTK_ORIENTATION_HORIZONTAL && + placement == GDL_DOCK_LEFT) || + (orientation == GTK_ORIENTATION_VERTICAL && + placement == GDL_DOCK_TOP)) { + /* Return left or top pane widget */ + ret_item = + gdl_dock_find_best_placement_item (GDL_DOCK_ITEM + (children->data), + placement, level + 1); + } else if ((orientation == GTK_ORIENTATION_HORIZONTAL && + placement == GDL_DOCK_RIGHT) || + (orientation == GTK_ORIENTATION_VERTICAL && + placement == GDL_DOCK_BOTTOM)) { + /* Return right or top pane widget */ + ret_item = + gdl_dock_find_best_placement_item (GDL_DOCK_ITEM + (children->next->data), + placement, level + 1); + } else { + /* Evaluate which of the two sides is bigger */ + dock_item_1 = + gdl_dock_find_best_placement_item (GDL_DOCK_ITEM + (children->data), + placement, level + 1); + dock_item_2 = + gdl_dock_find_best_placement_item (GDL_DOCK_ITEM + (children->next->data), + placement, level + 1); + ret_item = gdl_dock_select_larger_item (dock_item_1, + dock_item_2, + placement, level); + } + g_list_free (children); + } + else if (GDL_IS_DOCK_ITEM (dock_item)) + { + ret_item = dock_item; + } + else + { + /* should not be here */ + g_warning ("Should not reach here: %s:%d", __FUNCTION__, __LINE__); + } + return ret_item; +} + +void +gdl_dock_add_item (GdlDock *dock, + GdlDockItem *item, + GdlDockPlacement placement) +{ + g_return_if_fail (dock != NULL); + g_return_if_fail (item != NULL); + + if (placement == GDL_DOCK_FLOATING) + /* Add the item to a new floating dock */ + gdl_dock_add_floating_item (dock, item, 0, 0, -1, -1); + + else { + GdlDockItem *best_dock_item; + /* Non-floating item. */ + if (dock->root) { + GdlDockPlacement local_placement; + GtkRequisition preferred_size; + + best_dock_item = + gdl_dock_find_best_placement_item (GDL_DOCK_ITEM (dock->root), + placement, 0); + local_placement = gdl_dock_refine_placement (dock, best_dock_item, + placement); + gdl_dock_object_dock (GDL_DOCK_OBJECT (best_dock_item), + GDL_DOCK_OBJECT (item), + local_placement, NULL); + } else { + gdl_dock_object_dock (GDL_DOCK_OBJECT (dock), + GDL_DOCK_OBJECT (item), + placement, NULL); + } + } +} + +void +gdl_dock_add_floating_item (GdlDock *dock, + GdlDockItem *item, + gint x, + gint y, + gint width, + gint height) +{ + GdlDock *new_dock; + + g_return_if_fail (dock != NULL); + g_return_if_fail (item != NULL); + + new_dock = GDL_DOCK (g_object_new (GDL_TYPE_DOCK, + "master", GDL_DOCK_OBJECT_GET_MASTER (dock), + "floating", TRUE, + "width", width, + "height", height, + "floatx", x, + "floaty", y, + NULL)); + + if (GTK_WIDGET_VISIBLE (dock)) { + gtk_widget_show (GTK_WIDGET (new_dock)); + if (GTK_WIDGET_MAPPED (dock)) + gtk_widget_map (GTK_WIDGET (new_dock)); + + /* Make the widget resize. */ + gtk_widget_queue_resize (GTK_WIDGET (new_dock)); + } + + gdl_dock_add_item (GDL_DOCK (new_dock), item, GDL_DOCK_TOP); +} + +GdlDockItem * +gdl_dock_get_item_by_name (GdlDock *dock, + const gchar *name) +{ + GdlDockObject *found; + + g_return_val_if_fail (dock != NULL && name != NULL, NULL); + + /* proxy the call to our master */ + found = gdl_dock_master_get_object (GDL_DOCK_OBJECT_GET_MASTER (dock), name); + + return (found && GDL_IS_DOCK_ITEM (found)) ? GDL_DOCK_ITEM (found) : NULL; +} + +GdlDockPlaceholder * +gdl_dock_get_placeholder_by_name (GdlDock *dock, + const gchar *name) +{ + GdlDockObject *found; + + g_return_val_if_fail (dock != NULL && name != NULL, NULL); + + /* proxy the call to our master */ + found = gdl_dock_master_get_object (GDL_DOCK_OBJECT_GET_MASTER (dock), name); + + return (found && GDL_IS_DOCK_PLACEHOLDER (found)) ? + GDL_DOCK_PLACEHOLDER (found) : NULL; +} + +GList * +gdl_dock_get_named_items (GdlDock *dock) +{ + GList *list = NULL; + + g_return_val_if_fail (dock != NULL, NULL); + + gdl_dock_master_foreach (GDL_DOCK_OBJECT_GET_MASTER (dock), + (GFunc) _gdl_dock_foreach_build_list, &list); + + return list; +} + +GdlDock * +gdl_dock_object_get_toplevel (GdlDockObject *object) +{ + GdlDockObject *parent = object; + + g_return_val_if_fail (object != NULL, NULL); + + while (parent && !GDL_IS_DOCK (parent)) + parent = gdl_dock_object_get_parent_object (parent); + + return parent ? GDL_DOCK (parent) : NULL; +} + +void +gdl_dock_xor_rect (GdlDock *dock, + GdkRectangle *rect) +{ + GtkWidget *widget; + gint8 dash_list [2]; + + widget = GTK_WIDGET (dock); + + if (!dock->_priv->xor_gc) { + if (GTK_WIDGET_REALIZED (widget)) { + GdkGCValues values; + + values.function = GDK_INVERT; + values.subwindow_mode = GDK_INCLUDE_INFERIORS; + dock->_priv->xor_gc = gdk_gc_new_with_values + (widget->window, &values, GDK_GC_FUNCTION | GDK_GC_SUBWINDOW); + } else + return; + }; + + gdk_gc_set_line_attributes (dock->_priv->xor_gc, 1, + GDK_LINE_ON_OFF_DASH, + GDK_CAP_NOT_LAST, + GDK_JOIN_BEVEL); + + dash_list [0] = 1; + dash_list [1] = 1; + + gdk_gc_set_dashes (dock->_priv->xor_gc, 1, dash_list, 2); + + gdk_draw_rectangle (widget->window, dock->_priv->xor_gc, 0, + rect->x, rect->y, + rect->width, rect->height); + + gdk_gc_set_dashes (dock->_priv->xor_gc, 0, dash_list, 2); + + gdk_draw_rectangle (widget->window, dock->_priv->xor_gc, 0, + rect->x + 1, rect->y + 1, + rect->width - 2, rect->height - 2); +} diff --git a/src/libgdl/gdl-dock.h b/src/libgdl/gdl-dock.h new file mode 100644 index 000000000..7508feef7 --- /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 + * + * 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 +#include "libgdl/gdl-dock-object.h" +#include "libgdl/gdl-dock-item.h" +#include "libgdl/gdl-dock-placeholder.h" + +G_BEGIN_DECLS + +/* standard macros */ +#define GDL_TYPE_DOCK (gdl_dock_get_type ()) +#define GDL_DOCK(obj) (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK, GdlDock)) +#define GDL_DOCK_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK, GdlDockClass)) +#define GDL_IS_DOCK(obj) (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK)) +#define GDL_IS_DOCK_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK)) +#define GDL_DOCK_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK, GdlDockClass)) + +/* data types & structures */ +typedef struct _GdlDock GdlDock; +typedef struct _GdlDockClass GdlDockClass; +typedef struct _GdlDockPrivate GdlDockPrivate; + +struct _GdlDock { + GdlDockObject object; + + GdlDockObject *root; + + GdlDockPrivate *_priv; +}; + +struct _GdlDockClass { + GdlDockObjectClass parent_class; + + void (* layout_changed) (GdlDock *dock); /* proxy signal for the master */ +}; + +/* additional macros */ +#define GDL_DOCK_IS_CONTROLLER(dock) \ + (gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (dock)) == \ + GDL_DOCK_OBJECT (dock)) + +/* public interface */ + +GtkWidget *gdl_dock_new (void); + +GtkWidget *gdl_dock_new_from (GdlDock *original, + gboolean floating); + +GType gdl_dock_get_type (void); + +void gdl_dock_add_item (GdlDock *dock, + GdlDockItem *item, + GdlDockPlacement place); + +void gdl_dock_add_floating_item (GdlDock *dock, + GdlDockItem *item, + gint x, + gint y, + gint width, + gint height); + +GdlDockItem *gdl_dock_get_item_by_name (GdlDock *dock, + const gchar *name); + +GdlDockPlaceholder *gdl_dock_get_placeholder_by_name (GdlDock *dock, + const gchar *name); + +GList *gdl_dock_get_named_items (GdlDock *dock); + +GdlDock *gdl_dock_object_get_toplevel (GdlDockObject *object); + +void gdl_dock_xor_rect (GdlDock *dock, + GdkRectangle *rect); + +G_END_DECLS + +#endif diff --git a/src/libgdl/gdl-i18n.c b/src/libgdl/gdl-i18n.c new file mode 100644 index 000000000..5f92b66c7 --- /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 new file mode 100644 index 000000000..1582e957d --- /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 + */ + +#ifndef __GDL_18N_H__ +#define __GDL_18N_H__ 1 + +#include + + +G_BEGIN_DECLS + +#ifdef ENABLE_NLS +# include +# undef _ +# define _(String) gdl_gettext (String) +# ifdef gettext_noop +# define N_(String) gettext_noop (String) +# else +# define N_(String) (String) +# endif +#else +/* Stubs that do something close enough. */ +# undef textdomain +# define textdomain(String) (String) +# undef gettext +# define gettext(String) (String) +# undef dgettext +# define dgettext(Domain,Message) (Message) +# undef dcgettext +# define dcgettext(Domain,Message,Type) (Message) +# undef bindtextdomain +# define bindtextdomain(Domain,Directory) (Domain) +# undef bind_textdomain_codeset +# define bind_textdomain_codeset(Domain,CodeSet) (Domain) +# undef _ +# define _(String) (String) +# undef N_ +# define N_(String) (String) +#endif + +char *gdl_gettext (const char *msgid); + +G_END_DECLS + +#endif /* __GDL_I18N_H__ */ diff --git a/src/libgdl/gdl-stock-icons.h b/src/libgdl/gdl-stock-icons.h new file mode 100644 index 000000000..2c760efec --- /dev/null +++ b/src/libgdl/gdl-stock-icons.h @@ -0,0 +1,135 @@ +/* GdkPixbuf RGBA C-Source image dump */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_close_icon) +#endif +#ifdef __GNUC__ +static const guint8 stock_close_icon[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_close_icon[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (576) */ + "\0\0\2X" + /* pixdata_type (0x1010002) */ + "\1\1\0\2" + /* rowstride (48) */ + "\0\0\0""0" + /* width (12) */ + "\0\0\0\14" + /* height (12) */ + "\0\0\0\14" + /* pixel_data: */ + "\0\0\0\27\0\0\0c\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\211\0" + "\0\0\211\0\0\0\211\0\0\0c\0\0\0\27\0\0\0\0\0\0\0c\0\0\0E\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0E\0\0\0c\0\0\0\0\0" + "\0\0\211\0\0\0\0\0\0\0:\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\211\0" + "\0\0:\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\211" + "\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\0\0\0\0\211\0" + "\0\0\0\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0" + "\211\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\211\0\0" + "\0\211\0\0\0\211\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211" + "\0\0\0\0\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\211\0" + "\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0:\0\0\0\211" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0:\0\0\0\0\0\0\0\211\0\0\0\0\0" + "\0\0c\0\0\0L\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0E\0\0\0c\0\0\0\0\0\0\0\27\0\0\0c\0\0\0\211\0\0\0\211\0\0\0\211" + "\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0c\0\0\0\27\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0"}; + + +/* GdkPixbuf RGBA C-Source image dump */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_menu_left_icon) +#endif +#ifdef __GNUC__ +static const guint8 stock_menu_left_icon[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_menu_left_icon[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (576) */ + "\0\0\2X" + /* pixdata_type (0x1010002) */ + "\1\1\0\2" + /* rowstride (48) */ + "\0\0\0""0" + /* width (12) */ + "\0\0\0\14" + /* height (12) */ + "\0\0\0\14" + /* pixel_data: */ + "\0\0\0\27\0\0\0c\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\211\0" + "\0\0\211\0\0\0\211\0\0\0c\0\0\0\27\0\0\0\0\0\0\0c\0\0\0E\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0E\0\0\0c\0\0\0\0\0" + "\0\0\211\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\17\0\0\0""5\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\0\0" + "\0\0\211\0\0\0\0\0\0\0\0\0\0\0\17\0\0\0""5\0\0\0\211\0\0\0\211\0\0\0" + "\211\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\14\0\0" + "\0\211\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0" + "\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\17\0\0\0""5\0\0\0\211\0" + "\0\0\211\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\17\0\0\0""5\0\0\0\211\0\0\0\0\0\0\0" + "\0\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0c\0\0\0L\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0E\0\0\0c" + "\0\0\0\0\0\0\0\27\0\0\0c\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\211\0\0" + "\0\211\0\0\0\211\0\0\0\211\0\0\0c\0\0\0\27\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0"}; + + +/* GdkPixbuf RGBA C-Source image dump */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_menu_right_icon) +#endif +#ifdef __GNUC__ +static const guint8 stock_menu_right_icon[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_menu_right_icon[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (576) */ + "\0\0\2X" + /* pixdata_type (0x1010002) */ + "\1\1\0\2" + /* rowstride (48) */ + "\0\0\0""0" + /* width (12) */ + "\0\0\0\14" + /* height (12) */ + "\0\0\0\14" + /* pixel_data: */ + "\0\0\0\0\0\0\0\27\0\0\0c\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\211\0\0" + "\0\211\0\0\0\211\0\0\0\211\0\0\0c\0\0\0\27\0\0\0\0\0\0\0c\0\0\0E\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0E\0\0\0c\0" + "\0\0\0\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\0\0\0" + "\0\211\0\0\0""5\0\0\0\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\211\0" + "\0\0\0\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0" + "5\0\0\0\17\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0" + "\0\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\14\0\0\0\0" + "\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\211\0\0" + "\0\211\0\0\0""5\0\0\0\17\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211" + "\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0""5\0\0\0\17\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\211\0\0\0\0\0\0\0c\0\0" + "\0E\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0L\0" + "\0\0c\0\0\0\0\0\0\0\27\0\0\0c\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0\211" + "\0\0\0\211\0\0\0\211\0\0\0\211\0\0\0c\0\0\0\27\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0"}; + + diff --git a/src/libgdl/gdl-stock.c b/src/libgdl/gdl-stock.c new file mode 100644 index 000000000..7bcdd6be0 --- /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 +#endif + +#include +#include +#include "gdl-stock.h" +#include "gdl-stock-icons.h" + +static GtkIconFactory *gdl_stock_factory = NULL; + +static struct { + const gchar *stock_id; + const guint8 *icon_data; + const guint data_size; +} +gdl_icons[] = +{ + { GDL_STOCK_CLOSE, stock_close_icon, sizeof (stock_close_icon) }, + { GDL_STOCK_MENU_LEFT, stock_menu_left_icon, sizeof (stock_menu_left_icon) }, + { GDL_STOCK_MENU_RIGHT, stock_menu_right_icon, sizeof (stock_menu_right_icon) } +}; + +static void +icon_set_from_data (GtkIconSet *set, + const guint8 *icon_data, + const guint data_size, + GtkIconSize size, + gboolean fallback) +{ + GtkIconSource *source; + GdkPixbuf *pixbuf; + GError *err = NULL; + + source = gtk_icon_source_new (); + + gtk_icon_source_set_size (source, size); + gtk_icon_source_set_size_wildcarded (source, FALSE); + + pixbuf = gdk_pixbuf_new_from_inline (data_size, icon_data, FALSE, &err); + if (err) { + g_warning (err->message); + g_error_free (err); + err = NULL; + g_object_unref (source); + return; + } + + gtk_icon_source_set_pixbuf (source, pixbuf); + + g_object_unref (pixbuf); + + gtk_icon_set_add_source (set, source); + + if (fallback) { + gtk_icon_source_set_size_wildcarded (source, TRUE); + gtk_icon_set_add_source (set, source); + } + + gtk_icon_source_free (source); +} + +static void +add_icon (GtkIconFactory *factory, + const gchar *stock_id, + const guint8 *icon_data, + const guint data_size) +{ + GtkIconSet *set; + gboolean fallback = FALSE; + + set = gtk_icon_factory_lookup (factory, stock_id); + + if (!set) { + set = gtk_icon_set_new (); + gtk_icon_factory_add (factory, stock_id, set); + gtk_icon_set_unref (set); + + fallback = TRUE; + } + + icon_set_from_data (set, icon_data, data_size, GTK_ICON_SIZE_MENU, fallback); +} + +void +gdl_stock_init (void) +{ + static gboolean initialized = FALSE; + gint i; + + if (initialized) + return; + + gdl_stock_factory = gtk_icon_factory_new (); + + for (i = 0; i < G_N_ELEMENTS (gdl_icons); i++) { + add_icon (gdl_stock_factory, + gdl_icons[i].stock_id, + gdl_icons[i].icon_data, + gdl_icons[i].data_size); + } + + gtk_icon_factory_add_default (gdl_stock_factory); + + initialized = TRUE; +} diff --git a/src/libgdl/gdl-stock.h b/src/libgdl/gdl-stock.h new file mode 100644 index 000000000..3ada63f69 --- /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 new file mode 100644 index 000000000..23f05b924 --- /dev/null +++ b/src/libgdl/gdl-switcher.c @@ -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 + * Naba Kumar + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "gdl-i18n.h" +#include "gdl-switcher.h" +#include "gdl-tools.h" +#include "libgdlmarshal.h" +#include "libgdltypebuiltins.h" + +#include +#include +#include +#include +#include + +#if HAVE_GNOME +#include +#include +#endif + +static void gdl_switcher_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gdl_switcher_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static void gdl_switcher_add_button (GdlSwitcher *switcher, + const gchar *label, + const gchar *tooltips, + const gchar *stock_id, + const GdkPixbuf *pixbuf_icon, + gint switcher_id); +static void gdl_switcher_remove_button (GdlSwitcher *switcher, gint switcher_id); +static void gdl_switcher_select_page (GdlSwitcher *switcher, gint switcher_id); +static void gdl_switcher_select_button (GdlSwitcher *switcher, gint switcher_id); +static void gdl_switcher_set_show_buttons (GdlSwitcher *switcher, gboolean show); +static void gdl_switcher_set_style (GdlSwitcher *switcher, + GdlSwitcherStyle switcher_style); +static GdlSwitcherStyle gdl_switcher_get_style (GdlSwitcher *switcher); + +enum { + PROP_0, + PROP_SWITCHER_STYLE +}; + +typedef struct { + GtkWidget *button_widget; + GtkWidget *label; + GtkWidget *icon; + GtkWidget *arrow; + GtkWidget *hbox; + GtkTooltips *tooltips; + int id; +} Button; + +struct _GdlSwitcherPrivate { + GdlSwitcherStyle switcher_style; + GdlSwitcherStyle toolbar_style; + + gboolean show; + GSList *buttons; + + guint style_changed_id; + gint buttons_height_request; + gboolean in_toggle; +}; + +GDL_CLASS_BOILERPLATE (GdlSwitcher, gdl_switcher, GtkNotebook, GTK_TYPE_NOTEBOOK) + +#define INTERNAL_MODE(switcher) (switcher->priv->switcher_style == \ + GDL_SWITCHER_STYLE_TOOLBAR ? switcher->priv->toolbar_style : \ + switcher->priv->switcher_style) + +#define H_PADDING 2 +#define V_PADDING 2 + +/* Utility functions. */ + +static Button * +button_new (GtkWidget *button_widget, GtkWidget *label, GtkWidget *icon, + GtkTooltips *tooltips, GtkWidget *arrow, GtkWidget *hbox, int id) +{ + Button *button = g_new (Button, 1); + + button->button_widget = button_widget; + button->label = label; + button->icon = icon; + button->arrow = arrow; + button->hbox = hbox; + button->tooltips = tooltips; + button->id = id; + + g_object_ref (button_widget); + g_object_ref (label); + g_object_ref (icon); + g_object_ref (arrow); + g_object_ref (hbox); + g_object_ref (tooltips); + + return button; +} + +static void +button_free (Button *button) +{ + g_object_unref (button->button_widget); + g_object_unref (button->label); + g_object_unref (button->icon); + g_object_unref (button->hbox); + g_object_unref (button->tooltips); + g_free (button); +} + +static gint +gdl_switcher_get_page_id (GtkWidget *widget) +{ + static gint switcher_id_count = 0; + gint switcher_id; + switcher_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), + "__switcher_id")); + if (switcher_id <= 0) { + switcher_id = ++switcher_id_count; + g_object_set_data (G_OBJECT (widget), "__switcher_id", + GINT_TO_POINTER (switcher_id)); + } + return switcher_id; +} + +static void +update_buttons (GdlSwitcher *switcher, int new_selected_id) +{ + GSList *p; + + switcher->priv->in_toggle = TRUE; + + for (p = switcher->priv->buttons; p != NULL; p = p->next) { + Button *button = p->data; + + if (button->id == new_selected_id) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON + (button->button_widget), TRUE); + gtk_widget_set_sensitive (button->arrow, TRUE); + } else { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON + (button->button_widget), FALSE); + gtk_widget_set_sensitive (button->arrow, FALSE); + } + } + + switcher->priv->in_toggle = FALSE; +} + +/* Callbacks. */ + +static void +button_toggled_callback (GtkToggleButton *toggle_button, + GdlSwitcher *switcher) +{ + int id = 0; + gboolean is_active = FALSE; + GSList *p; + + if (switcher->priv->in_toggle) + return; + + switcher->priv->in_toggle = TRUE; + + if (gtk_toggle_button_get_active (toggle_button)) + is_active = TRUE; + + for (p = switcher->priv->buttons; p != NULL; p = p->next) { + Button *button = p->data; + + if (button->button_widget != GTK_WIDGET (toggle_button)) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON + (button->button_widget), FALSE); + gtk_widget_set_sensitive (button->arrow, FALSE); + } else { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON + (button->button_widget), TRUE); + gtk_widget_set_sensitive (button->arrow, TRUE); + id = button->id; + } + } + + switcher->priv->in_toggle = FALSE; + + if (is_active) + { + gdl_switcher_select_page (switcher, id); + } +} + +/* Returns -1 if layout didn't happen because a resize request was queued */ +static int +layout_buttons (GdlSwitcher *switcher) +{ + GtkRequisition client_requisition; + GtkAllocation *allocation = & GTK_WIDGET (switcher)->allocation; + GdlSwitcherStyle switcher_style; + gboolean icons_only; + int num_btns = g_slist_length (switcher->priv->buttons); + int btns_per_row; + GSList **rows, *p; + Button *button; + int row_number; + int max_btn_width = 0, max_btn_height = 0; + int optimal_layout_width = 0; + int row_last; + int x, y; + int i; + int rows_count; + int last_buttons_height; + + last_buttons_height = switcher->priv->buttons_height_request; + + GDL_CALL_PARENT (GTK_WIDGET_CLASS, size_request, + (GTK_WIDGET (switcher), &client_requisition)); + + y = allocation->y + allocation->height - V_PADDING - 1; + + if (num_btns == 0) + return y; + + switcher_style = INTERNAL_MODE (switcher); + icons_only = (switcher_style == GDL_SWITCHER_STYLE_ICON); + + /* Figure out the max width and height */ + optimal_layout_width = H_PADDING; + for (p = switcher->priv->buttons; p != NULL; p = p->next) { + GtkRequisition requisition; + + button = p->data; + gtk_widget_size_request (GTK_WIDGET (button->button_widget), + &requisition); + optimal_layout_width += requisition.width + H_PADDING; + max_btn_height = MAX (max_btn_height, requisition.height); + max_btn_width = MAX (max_btn_width, requisition.width); + } + + /* Figure out how many rows and columns we'll use. */ + btns_per_row = allocation->width / (max_btn_width + H_PADDING); + + /* If all the buttons could fit in the single row, have it so */ + if (allocation->width >= optimal_layout_width) + { + btns_per_row = num_btns; + } + if (!icons_only) { + /* If using text buttons, we want to try to have a + * completely filled-in grid, but if we can't, we want + * the odd row to have just a single button. + */ + while (num_btns % btns_per_row > 1) + btns_per_row--; + } + + rows_count = num_btns / btns_per_row; + if (num_btns % btns_per_row != 0) + rows_count++; + + /* Assign buttons to rows */ + rows = g_new0 (GSList *, rows_count); + + if (!icons_only && num_btns % btns_per_row != 0) { + button = switcher->priv->buttons->data; + rows [0] = g_slist_append (rows [0], button->button_widget); + + p = switcher->priv->buttons->next; + row_number = p ? 1 : 0; + } else { + p = switcher->priv->buttons; + row_number = 0; + } + + for (; p != NULL; p = p->next) { + button = p->data; + + if (g_slist_length (rows [row_number]) == btns_per_row) + row_number ++; + + rows [row_number] = g_slist_append (rows [row_number], + button->button_widget); + } + + row_last = row_number; + + /* If there are more than 1 row of buttons, save the current height + * requirement for subsequent size requests. + */ + if (row_last > 0) + { + switcher->priv->buttons_height_request = + (row_last + 1) * (max_btn_height + V_PADDING) + 1; + } else { /* Otherwize clear it */ + if (last_buttons_height >= 0) { + + switcher->priv->buttons_height_request = -1; + } + } + + /* If it turns out that we now require smaller height for the buttons + * than it was last time, make a resize request to ensure our + * size requisition is properly communicated to the parent (otherwise + * parent tend to keep assuming the older size). + */ + if (last_buttons_height > switcher->priv->buttons_height_request) + { + gtk_widget_queue_resize (GTK_WIDGET (switcher)); + return -1; + } + + /* Layout the buttons. */ + for (i = row_last; i >= 0; i --) { + int len, extra_width; + + y -= max_btn_height; + + /* Check for possible size over flow (taking into account client + * requisition + */ + if (y < (allocation->y + client_requisition.height)) { + /* We have an overflow: Insufficient allocation */ + if (last_buttons_height < switcher->priv->buttons_height_request) { + /* Request for a new resize */ + gtk_widget_queue_resize (GTK_WIDGET (switcher)); + return -1; + } + } + x = H_PADDING + allocation->x; + len = g_slist_length (rows[i]); + if (switcher_style == GDL_SWITCHER_STYLE_TEXT || + switcher_style == GDL_SWITCHER_STYLE_BOTH) + extra_width = (allocation->width - (len * max_btn_width ) + - (len * H_PADDING)) / len; + else + extra_width = 0; + for (p = rows [i]; p != NULL; p = p->next) { + GtkAllocation child_allocation; + + child_allocation.x = x; + child_allocation.y = y; + if (rows_count == 1 && row_number == 0) + { + GtkRequisition child_requisition; + gtk_widget_size_request (GTK_WIDGET (p->data), + &child_requisition); + child_allocation.width = child_requisition.width; + } + else + { + child_allocation.width = max_btn_width + extra_width; + } + child_allocation.height = max_btn_height; + + gtk_widget_size_allocate (GTK_WIDGET (p->data), &child_allocation); + + x += child_allocation.width + H_PADDING; + } + + y -= V_PADDING; + } + + for (i = 0; i <= row_last; i ++) + g_slist_free (rows [i]); + g_free (rows); + + return y; +} + +static void +do_layout (GdlSwitcher *switcher) +{ + GtkAllocation *allocation = & GTK_WIDGET (switcher)->allocation; + GtkAllocation child_allocation; + int y; + + if (switcher->priv->show) { + y = layout_buttons (switcher); + if (y < 0) /* Layout did not happen and a resize was requested */ + return; + } + else + y = allocation->y + allocation->height; + + /* Place the parent widget. */ + child_allocation.x = allocation->x; + child_allocation.y = allocation->y; + child_allocation.width = allocation->width; + child_allocation.height = y - allocation->y; + + GDL_CALL_PARENT (GTK_WIDGET_CLASS, size_allocate, + (GTK_WIDGET (switcher), &child_allocation)); +} + +/* GtkContainer methods. */ + +static void +gdl_switcher_forall (GtkContainer *container, gboolean include_internals, + GtkCallback callback, void *callback_data) +{ + GdlSwitcher *switcher = + GDL_SWITCHER (container); + GSList *p; + + GDL_CALL_PARENT (GTK_CONTAINER_CLASS, forall, + (GTK_CONTAINER (switcher), include_internals, + callback, callback_data)); + if (include_internals) { + for (p = switcher->priv->buttons; p != NULL; p = p->next) { + GtkWidget *widget = ((Button *) p->data)->button_widget; + (* callback) (widget, callback_data); + } + } +} + +static void +gdl_switcher_remove (GtkContainer *container, GtkWidget *widget) +{ + gint switcher_id; + GdlSwitcher *switcher = + GDL_SWITCHER (container); + GSList *p; + + switcher_id = gdl_switcher_get_page_id (widget); + for (p = switcher->priv->buttons; p != NULL; p = p->next) { + Button *b = (Button *) p->data; + + if (b->id == switcher_id) { + gtk_widget_unparent (b->button_widget); + switcher->priv->buttons = + g_slist_remove_link (switcher->priv->buttons, p); + button_free (b); + gtk_widget_queue_resize (GTK_WIDGET (switcher)); + break; + } + } + GDL_CALL_PARENT (GTK_CONTAINER_CLASS, remove, + (GTK_CONTAINER (switcher), widget)); +} + +/* GtkWidget methods. */ + +static void +gdl_switcher_size_request (GtkWidget *widget, GtkRequisition *requisition) +{ + GdlSwitcher *switcher = GDL_SWITCHER (widget); + GSList *p; + gint button_height = 0; + + GDL_CALL_PARENT (GTK_WIDGET_CLASS, size_request, + (GTK_WIDGET (switcher), requisition)); + + if (!switcher->priv->show) + return; + + for (p = switcher->priv->buttons; p != NULL; p = p->next) { + gint button_width; + Button *button = p->data; + GtkRequisition button_requisition; + + gtk_widget_size_request (button->button_widget, &button_requisition); + button_width = button_requisition.width + 2 * H_PADDING; + requisition->width = MAX (requisition->width, button_width); + button_height = MAX (button_height, + button_requisition.height + 2 * V_PADDING); + } + + if (switcher->priv->buttons_height_request > 0) { + requisition->height += switcher->priv->buttons_height_request; + } else { + requisition->height += button_height + V_PADDING; + } +} + +static void +gdl_switcher_size_allocate (GtkWidget *widget, GtkAllocation *allocation) +{ + widget->allocation = *allocation; + do_layout (GDL_SWITCHER (widget)); +} + +static gint +gdl_switcher_expose (GtkWidget *widget, GdkEventExpose *event) +{ + GSList *p; + GdlSwitcher *switcher = GDL_SWITCHER (widget); + if (switcher->priv->show) { + for (p = switcher->priv->buttons; p != NULL; p = p->next) { + GtkWidget *button = ((Button *) p->data)->button_widget; + gtk_container_propagate_expose (GTK_CONTAINER (widget), + button, event); + } + } + GDL_CALL_PARENT_WITH_DEFAULT (GTK_WIDGET_CLASS, expose_event, + (widget, event), FALSE); +} + +static void +gdl_switcher_map (GtkWidget *widget) +{ + GSList *p; + GdlSwitcher *switcher = GDL_SWITCHER (widget); + + if (switcher->priv->show) { + for (p = switcher->priv->buttons; p != NULL; p = p->next) { + GtkWidget *button = ((Button *) p->data)->button_widget; + gtk_widget_map (button); + } + } + GDL_CALL_PARENT (GTK_WIDGET_CLASS, map, (widget)); +} + +/* GObject methods. */ + +static void +gdl_switcher_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdlSwitcher *switcher = GDL_SWITCHER (object); + + switch (prop_id) { + case PROP_SWITCHER_STYLE: + gdl_switcher_set_style (switcher, g_value_get_enum (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdl_switcher_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdlSwitcher *switcher = GDL_SWITCHER (object); + + switch (prop_id) { + case PROP_SWITCHER_STYLE: + g_value_set_enum (value, gdl_switcher_get_style (switcher)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdl_switcher_dispose (GObject *object) +{ + GdlSwitcherPrivate *priv = GDL_SWITCHER (object)->priv; + +#if HAVE_GNOME + GConfClient *gconf_client = gconf_client_get_default (); + + if (priv->style_changed_id) { + gconf_client_notify_remove (gconf_client, priv->style_changed_id); + priv->style_changed_id = 0; + } + g_object_unref (gconf_client); +#endif + + g_slist_foreach (priv->buttons, (GFunc) button_free, NULL); + g_slist_free (priv->buttons); + priv->buttons = NULL; + + GDL_CALL_PARENT (G_OBJECT_CLASS, dispose, (object)); +} + +static void +gdl_switcher_finalize (GObject *object) +{ + GdlSwitcherPrivate *priv = GDL_SWITCHER (object)->priv; + + g_free (priv); + + GDL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); +} + +/* Signal handlers */ + +static void +gdl_switcher_notify_cb (GObject *g_object, GParamSpec *pspec, + GdlSwitcher *switcher) +{ + gboolean show_tabs; + g_return_if_fail (switcher != NULL && GDL_IS_SWITCHER (switcher)); + show_tabs = gtk_notebook_get_show_tabs (GTK_NOTEBOOK (switcher)); + gdl_switcher_set_show_buttons (switcher, !show_tabs); +} + +static void +gdl_switcher_switch_page_cb (GtkNotebook *nb, GtkNotebookPage *page, + gint page_num, GdlSwitcher *switcher) +{ + GtkWidget *page_widget; + GtkWidget *tablabel; + gint switcher_id; + + /* Change switcher button */ + page_widget = gtk_notebook_get_nth_page (nb, page_num); + switcher_id = gdl_switcher_get_page_id (page_widget); + gdl_switcher_select_button (GDL_SWITCHER (switcher), switcher_id); +} + +static void +gdl_switcher_page_added_cb (GtkNotebook *nb, GtkWidget *page, + gint page_num, GdlSwitcher *switcher) +{ + gint switcher_id; + + switcher_id = gdl_switcher_get_page_id (page); + + gdl_switcher_add_button (GDL_SWITCHER (switcher), NULL, NULL, NULL, NULL, + switcher_id); + gdl_switcher_select_button (GDL_SWITCHER (switcher), switcher_id); +} + +static void +gdl_switcher_select_page (GdlSwitcher *switcher, gint id) +{ + GList *children, *node; + children = gtk_container_get_children (GTK_CONTAINER (switcher)); + node = children; + while (node) + { + gint switcher_id; + switcher_id = gdl_switcher_get_page_id (GTK_WIDGET (node->data)); + if (switcher_id == id) + { + gint page_num; + page_num = gtk_notebook_page_num (GTK_NOTEBOOK (switcher), + GTK_WIDGET (node->data)); + g_signal_handlers_block_by_func (switcher, + gdl_switcher_switch_page_cb, + switcher); + gtk_notebook_set_current_page (GTK_NOTEBOOK (switcher), page_num); + g_signal_handlers_unblock_by_func (switcher, + gdl_switcher_switch_page_cb, + switcher); + break; + } + node = g_list_next (node); + } + g_list_free (children); +} + +/* Initialization. */ + +static void +gdl_switcher_class_init (GdlSwitcherClass *klass) +{ + GtkNotebookClass *notebook_class = GTK_NOTEBOOK_CLASS (klass); + GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + container_class->forall = gdl_switcher_forall; + container_class->remove = gdl_switcher_remove; + + widget_class->size_request = gdl_switcher_size_request; + widget_class->size_allocate = gdl_switcher_size_allocate; + widget_class->expose_event = gdl_switcher_expose; + widget_class->map = gdl_switcher_map; + + object_class->dispose = gdl_switcher_dispose; + object_class->finalize = gdl_switcher_finalize; + object_class->set_property = gdl_switcher_set_property; + object_class->get_property = gdl_switcher_get_property; + + g_object_class_install_property ( + object_class, PROP_SWITCHER_STYLE, + g_param_spec_enum ("switcher-style", _("Switcher Style"), + _("Switcher buttons style"), + GDL_TYPE_SWITCHER_STYLE, + GDL_SWITCHER_STYLE_BOTH, + G_PARAM_READWRITE)); +} + +static void +gdl_switcher_instance_init (GdlSwitcher *switcher) +{ + GdlSwitcherPrivate *priv; + + GTK_WIDGET_SET_FLAGS (switcher, GTK_NO_WINDOW); + + priv = g_new0 (GdlSwitcherPrivate, 1); + switcher->priv = priv; + + priv->show = TRUE; + priv->buttons_height_request = -1; + + gtk_notebook_set_tab_pos (GTK_NOTEBOOK (switcher), GTK_POS_BOTTOM); + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (switcher), FALSE); + gtk_notebook_set_show_border (GTK_NOTEBOOK (switcher), FALSE); + gdl_switcher_set_style (switcher, GDL_SWITCHER_STYLE_BOTH); + + /* notebook signals */ + g_signal_connect (switcher, "switch-page", + G_CALLBACK (gdl_switcher_switch_page_cb), switcher); + g_signal_connect (switcher, "page-added", + G_CALLBACK (gdl_switcher_page_added_cb), switcher); + g_signal_connect (switcher, "notify::show-tabs", + G_CALLBACK (gdl_switcher_notify_cb), switcher); +} + +GtkWidget * +gdl_switcher_new (void) +{ + GdlSwitcher *switcher = g_object_new (gdl_switcher_get_type (), NULL); + return GTK_WIDGET (switcher); +} + +void +gdl_switcher_add_button (GdlSwitcher *switcher, const gchar *label, + const gchar *tooltips, const gchar *stock_id, + const GdkPixbuf *pixbuf_icon, gint switcher_id) +{ + GtkWidget *button_widget; + GtkWidget *hbox; + GtkWidget *icon_widget; + GtkWidget *label_widget; + GtkWidget *arrow; + GtkTooltips *button_tooltips; + + button_widget = gtk_toggle_button_new (); + if (switcher->priv->show) + gtk_widget_show (button_widget); + g_signal_connect (button_widget, "toggled", + G_CALLBACK (button_toggled_callback), + switcher); + hbox = gtk_hbox_new (FALSE, 3); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 0); + gtk_container_add (GTK_CONTAINER (button_widget), hbox); + gtk_widget_show (hbox); + + if (stock_id) + icon_widget = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON); + else if (pixbuf_icon) + icon_widget = gtk_image_new_from_pixbuf (pixbuf_icon); + else + icon_widget = gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_BUTTON); + + gtk_widget_show (icon_widget); + + if (!label) { + gchar *text = g_strdup_printf ("Item %d", switcher_id); + label_widget = gtk_label_new (text); + g_free (text); + } else { + label_widget = gtk_label_new (label); + } + gtk_misc_set_alignment (GTK_MISC (label_widget), 0.0, 0.5); + gtk_widget_show (label_widget); + button_tooltips = gtk_tooltips_new(); + gtk_tooltips_set_tip (GTK_TOOLTIPS (button_tooltips), button_widget, + tooltips, NULL); + + switch (INTERNAL_MODE (switcher)) { + case GDL_SWITCHER_STYLE_TEXT: + gtk_box_pack_start (GTK_BOX (hbox), label_widget, TRUE, TRUE, 0); + gtk_tooltips_disable (button_tooltips); + break; + case GDL_SWITCHER_STYLE_ICON: + gtk_box_pack_start (GTK_BOX (hbox), icon_widget, TRUE, TRUE, 0); + gtk_tooltips_enable (button_tooltips); + break; + case GDL_SWITCHER_STYLE_BOTH: + default: + gtk_box_pack_start (GTK_BOX (hbox), icon_widget, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), label_widget, TRUE, TRUE, 0); + gtk_tooltips_disable (button_tooltips); + break; + } + arrow = gtk_arrow_new (GTK_ARROW_UP, GTK_SHADOW_NONE); + gtk_widget_show (arrow); + gtk_box_pack_start (GTK_BOX (hbox), arrow, FALSE, FALSE, 0); + + switcher->priv->buttons = + g_slist_append (switcher->priv->buttons, + button_new (button_widget, label_widget, + icon_widget, button_tooltips, + arrow, hbox, switcher_id)); + gtk_widget_set_parent (button_widget, GTK_WIDGET (switcher)); + + gtk_widget_queue_resize (GTK_WIDGET (switcher)); +} + +static void +gdl_switcher_remove_button (GdlSwitcher *switcher, gint switcher_id) +{ + GSList *p; + + for (p = switcher->priv->buttons; p != NULL; p = p->next) { + Button *button = p->data; + + if (button->id == switcher_id) + { + gtk_container_remove (GTK_CONTAINER (switcher), + button->button_widget); + break; + } + } + gtk_widget_queue_resize (GTK_WIDGET (switcher)); +} + +static void +gdl_switcher_select_button (GdlSwitcher *switcher, gint switcher_id) +{ + update_buttons (switcher, switcher_id); + + /* Select the notebook page associated with this button */ + gdl_switcher_select_page (switcher, switcher_id); +} + +gint +gdl_switcher_insert_page (GdlSwitcher *switcher, GtkWidget *page, + GtkWidget *tab_widget, const gchar *label, + const gchar *tooltips, const gchar *stock_id, + const GdkPixbuf *pixbuf_icon, gint position) +{ + gint ret_position; + gint switcher_id; + g_signal_handlers_block_by_func (switcher, + gdl_switcher_page_added_cb, + switcher); + + if (!tab_widget) { + tab_widget = gtk_label_new (label); + gtk_widget_show (tab_widget); + } + switcher_id = gdl_switcher_get_page_id (page); + gdl_switcher_add_button (switcher, label, tooltips, stock_id, pixbuf_icon, switcher_id); + ret_position = gtk_notebook_insert_page (GTK_NOTEBOOK (switcher), page, + tab_widget, position); + g_signal_handlers_unblock_by_func (switcher, + gdl_switcher_page_added_cb, + switcher); + return ret_position; +} + +static void +set_switcher_style_internal (GdlSwitcher *switcher, + GdlSwitcherStyle switcher_style ) +{ + GSList *p; + + if (switcher_style == GDL_SWITCHER_STYLE_TABS && + switcher->priv->show == FALSE) + return; + + if (switcher_style == GDL_SWITCHER_STYLE_TABS) + { + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (switcher), TRUE); + return; + } + + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (switcher), FALSE); + + if (switcher_style == INTERNAL_MODE (switcher)) + return; + + for (p = switcher->priv->buttons; p != NULL; p = p->next) { + Button *button = p->data; + + gtk_container_remove (GTK_CONTAINER (button->hbox), button->arrow); + switch (switcher_style) { + case GDL_SWITCHER_STYLE_TEXT: + gtk_container_remove (GTK_CONTAINER (button->hbox), button->icon); + if (INTERNAL_MODE (switcher) + == GDL_SWITCHER_STYLE_ICON) { + gtk_box_pack_start (GTK_BOX (button->hbox), button->label, + TRUE, TRUE, 0); + gtk_widget_show (button->label); + gtk_tooltips_disable (button->tooltips); + } + break; + case GDL_SWITCHER_STYLE_ICON: + gtk_container_remove(GTK_CONTAINER (button->hbox), button->label); + if (INTERNAL_MODE (switcher) + == GDL_SWITCHER_STYLE_TEXT) { + gtk_box_pack_start (GTK_BOX (button->hbox), button->icon, + TRUE, TRUE, 0); + gtk_widget_show (button->icon); + } else + gtk_container_child_set (GTK_CONTAINER (button->hbox), + button->icon, "expand", TRUE, NULL); + gtk_tooltips_enable (button->tooltips); + break; + case GDL_SWITCHER_STYLE_BOTH: + if (INTERNAL_MODE (switcher) + == GDL_SWITCHER_STYLE_TEXT) { + gtk_container_remove (GTK_CONTAINER (button->hbox), + button->label); + gtk_box_pack_start (GTK_BOX (button->hbox), button->icon, + FALSE, TRUE, 0); + gtk_widget_show (button->icon); + } else { + gtk_container_child_set (GTK_CONTAINER (button->hbox), + button->icon, "expand", FALSE, NULL); + } + + gtk_tooltips_disable (button->tooltips); + gtk_box_pack_start (GTK_BOX (button->hbox), button->label, TRUE, + TRUE, 0); + gtk_widget_show (button->label); + break; + default: + break; + } + gtk_box_pack_start (GTK_BOX (button->hbox), button->arrow, FALSE, + FALSE, 0); + } +} + +#if HAVE_GNOME +static GConfEnumStringPair toolbar_styles[] = { + { GDL_SWITCHER_STYLE_TEXT, "text" }, + { GDL_SWITCHER_STYLE_ICON, "icons" }, + { GDL_SWITCHER_STYLE_BOTH, "both" }, + { GDL_SWITCHER_STYLE_BOTH, "both-horiz" }, + { GDL_SWITCHER_STYLE_BOTH, "both_horiz" }, + { -1, NULL } +}; + +static void +style_changed_notify (GConfClient *gconf, guint id, GConfEntry *entry, + void *data) +{ + GdlSwitcher *switcher = data; + char *val; + int switcher_style; + + val = gconf_client_get_string (gconf, + "/desktop/gnome/interface/toolbar_style", + NULL); + if (val == NULL || !gconf_string_to_enum (toolbar_styles, val, + &switcher_style)) + switcher_style = GDL_SWITCHER_STYLE_BOTH; + g_free(val); + + set_switcher_style_internal (GDL_SWITCHER (switcher), switcher_style); + switcher->priv->toolbar_style = switcher_style; + + gtk_widget_queue_resize (GTK_WIDGET (switcher)); +} + +static void +gdl_switcher_set_style (GdlSwitcher *switcher, GdlSwitcherStyle switcher_style) +{ + GConfClient *gconf_client = gconf_client_get_default (); + + if (switcher_style == GDL_SWITCHER_STYLE_TABS && + switcher->priv->show == FALSE) + return; + + if (switcher->priv->switcher_style == switcher_style && + switcher->priv->show == TRUE) + return; + + if (switcher->priv->switcher_style == GDL_SWITCHER_STYLE_TOOLBAR) { + if (switcher->priv->style_changed_id) { + gconf_client_notify_remove (gconf_client, + switcher->priv->style_changed_id); + switcher->priv->style_changed_id = 0; + } + } + + if (switcher_style != GDL_SWITCHER_STYLE_TOOLBAR) { + set_switcher_style_internal (switcher, switcher_style); + + gtk_widget_queue_resize (GTK_WIDGET (switcher)); + } else { + /* This is a little bit tricky, toolbar style is more + * of a meta-style where the actual style is dictated by + * the gnome toolbar setting, so that is why we have + * the is_toolbar_style bool - it tracks the toolbar + * style while the switcher_style member is the actual look and + * feel */ + switcher->priv->style_changed_id = + gconf_client_notify_add (gconf_client, + "/desktop/gnome/interface/toolbar_style", + style_changed_notify, switcher, + NULL, NULL); + style_changed_notify (gconf_client, 0, NULL, switcher); + } + + g_object_unref (gconf_client); + + if (switcher_style != GDL_SWITCHER_STYLE_TABS) + switcher->priv->switcher_style = switcher_style; +} + +#else /* HAVE_GNOME */ + +static void +gdl_switcher_set_style (GdlSwitcher *switcher, GdlSwitcherStyle switcher_style) +{ + if (switcher_style == GDL_SWITCHER_STYLE_TABS && + switcher->priv->show == FALSE) + return; + + if (switcher->priv->switcher_style == switcher_style && + switcher->priv->show == TRUE) + return; + + set_switcher_style_internal (switcher, + ((switcher_style == + GDL_SWITCHER_STYLE_TOOLBAR)? + GDL_SWITCHER_STYLE_BOTH : switcher_style)); + gtk_widget_queue_resize (GTK_WIDGET (switcher)); + + if (switcher_style != GDL_SWITCHER_STYLE_TABS) + switcher->priv->switcher_style = switcher_style; +} + +#endif /* HAVE_GNOME */ + +static void +gdl_switcher_set_show_buttons (GdlSwitcher *switcher, gboolean show) +{ + GSList *p; + + if (switcher->priv->show == show) + return; + + for (p = switcher->priv->buttons; p != NULL; p = p->next) { + Button *button = p->data; + + if (show) + gtk_widget_show (button->button_widget); + else + gtk_widget_hide (button->button_widget); + } + + switcher->priv->show = show; + + gtk_widget_queue_resize (GTK_WIDGET (switcher)); +} + +static GdlSwitcherStyle +gdl_switcher_get_style (GdlSwitcher *switcher) +{ + if (!switcher->priv->show) + return GDL_SWITCHER_STYLE_TABS; + return switcher->priv->switcher_style; +} diff --git a/src/libgdl/gdl-switcher.h b/src/libgdl/gdl-switcher.h new file mode 100644 index 000000000..be4b179bf --- /dev/null +++ b/src/libgdl/gdl-switcher.h @@ -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 + * Naba Kumar + */ + +#ifndef _GDL_SWITCHER_H_ +#define _GDL_SWITCHER_H_ + +#include + +G_BEGIN_DECLS + +#define GDL_TYPE_SWITCHER (gdl_switcher_get_type ()) +#define GDL_SWITCHER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDL_TYPE_SWITCHER, GdlSwitcher)) +#define GDL_SWITCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDL_TYPE_SWITCHER, GdlSwitcherClass)) +#define GDL_IS_SWITCHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDL_TYPE_SWITCHER)) +#define GDL_IS_SWITCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), GDL_TYPE_SWITCHER)) + +typedef struct _GdlSwitcher GdlSwitcher; +typedef struct _GdlSwitcherPrivate GdlSwitcherPrivate; +typedef struct _GdlSwitcherClass GdlSwitcherClass; + +typedef enum { + GDL_SWITCHER_STYLE_TEXT, + GDL_SWITCHER_STYLE_ICON, + GDL_SWITCHER_STYLE_BOTH, + GDL_SWITCHER_STYLE_TOOLBAR, + GDL_SWITCHER_STYLE_TABS +} GdlSwitcherStyle; + +struct _GdlSwitcher { + GtkNotebook parent; + + GdlSwitcherPrivate *priv; +}; + +struct _GdlSwitcherClass { + GtkNotebookClass parent_class; +}; + +GType gdl_switcher_get_type (void); +GtkWidget *gdl_switcher_new (void); + +gint gdl_switcher_insert_page (GdlSwitcher *switcher, + GtkWidget *page, + GtkWidget *tab_widget, + const gchar *label, + const gchar *tooltips, + const gchar *stock_id, + const GdkPixbuf *pixbuf_icon, + gint position); +G_END_DECLS + +#endif /* _GDL_SWITCHER_H_ */ diff --git a/src/libgdl/gdl-tools.h b/src/libgdl/gdl-tools.h new file mode 100644 index 000000000..32c2e4a41 --- /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 + * + * 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 +#include + +/* 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 + * + * Copyright 2001 Ben Tea Spoons, Inc. + */ + +/* Macros for defining classes. Ideas taken from Nautilus and GOB. */ + +/* Define the boilerplate type stuff to reduce typos and code size. Defines + * the get_type method and the parent_class static variable. */ + +#define GDL_BOILERPLATE(type, type_as_function, corba_type, \ + parent_type, parent_type_macro, \ + register_type_macro) \ +static void type_as_function ## _class_init (type ## Class *klass); \ +static void type_as_function ## _instance_init (type *object); \ +static parent_type ## Class *parent_class = NULL; \ +static void \ +type_as_function ## _class_init_trampoline (gpointer klass, \ + gpointer data) \ +{ \ + parent_class = (parent_type ## Class *)g_type_class_ref ( \ + parent_type_macro); \ + type_as_function ## _class_init ((type ## Class *)klass); \ +} \ +GType \ +type_as_function ## _get_type (void) \ +{ \ + static GType object_type = 0; \ + if (object_type == 0) { \ + static const GTypeInfo object_info = { \ + sizeof (type ## Class), \ + NULL, /* base_init */ \ + NULL, /* base_finalize */ \ + type_as_function ## _class_init_trampoline, \ + NULL, /* class_finalize */ \ + NULL, /* class_data */ \ + sizeof (type), \ + 0, /* n_preallocs */ \ + (GInstanceInitFunc) type_as_function ## _instance_init \ + }; \ + object_type = register_type_macro \ + (type, type_as_function, corba_type, \ + parent_type, parent_type_macro); \ + } \ + return object_type; \ +} + +/* Just call the parent handler. This assumes that there is a variable + * named parent_class that points to the (duh!) parent class. Note that + * this macro is not to be used with things that return something, use + * the _WITH_DEFAULT version for that */ +#define GDL_CALL_PARENT(parent_class_cast, name, args) \ + ((parent_class_cast(parent_class)->name != NULL) ? \ + parent_class_cast(parent_class)->name args : (void)0) + +#define GDL_CALL_PARENT_GBOOLEAN(parent_class_cast, name, args) \ + ((parent_class_cast(parent_class)->name != NULL) ? \ + parent_class_cast(parent_class)->name args : (gboolean)0) + + +/* Same as above, but in case there is no implementation, it evaluates + * to def_return */ +#define GDL_CALL_PARENT_WITH_DEFAULT(parent_class_cast, \ + name, args, def_return) \ + ((parent_class_cast(parent_class)->name != NULL) ? \ + parent_class_cast(parent_class)->name args : def_return) + +/* Define the boilerplate type stuff to reduce typos and code size. Defines + * the get_type method and the parent_class static variable. */ +#define GDL_CLASS_BOILERPLATE(type, type_as_function, \ + parent_type, parent_type_macro) \ + GDL_BOILERPLATE(type, type_as_function, type, \ + parent_type, parent_type_macro, \ + GDL_REGISTER_TYPE) +#define GDL_REGISTER_TYPE(type, type_as_function, corba_type, \ + parent_type, parent_type_macro) \ + g_type_register_static (parent_type_macro, #type, &object_info, 0) + + +#define GDL_CALL_VIRTUAL(object, get_class_cast, method, args) \ + (get_class_cast (object)->method ? (* get_class_cast (object)->method) args : (void)0) +#define GDL_CALL_VIRTUAL_WITH_DEFAULT(object, get_class_cast, method, args, default) \ + (get_class_cast (object)->method ? (* get_class_cast (object)->method) args : default) + +/* GdlPixmap structure and method have been copied from Evolution. */ +typedef struct _GdlPixmap { + const char *path; + const char *fname; + char *pixbuf; +} GdlPixmap; + +#define GDL_PIXMAP(path,fname) { (path), (fname), NULL } +#define GDL_PIXMAP_END { NULL, NULL, NULL } + +G_END_DECLS + +#endif + diff --git a/src/libgdl/layout.glade b/src/libgdl/layout.glade new file mode 100644 index 000000000..87b7f9fb6 --- /dev/null +++ b/src/libgdl/layout.glade @@ -0,0 +1,283 @@ + + + + + + + + 4 + Layout Managment + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + True + True + False + True + + + + True + False + 8 + + + + True + GTK_BUTTONBOX_END + + + 0 + False + True + GTK_PACK_END + + + + + + True + True + True + True + GTK_POS_TOP + False + False + + + + True + False + 6 + + + + 100 + True + True + GTK_POLICY_NEVER + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + True + True + True + False + True + + + + + 0 + True + True + + + + + + True + True + _Lock dock items + True + GTK_RELIEF_NORMAL + False + False + True + + + 0 + False + False + + + + + False + True + + + + + + True + Dock items + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + tab + + + + + + True + False + 6 + + + + 100 + True + True + GTK_POLICY_NEVER + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + True + True + False + False + True + + + + + 0 + True + True + + + + + + True + GTK_BUTTONBOX_END + 6 + + + + True + True + True + gtk-delete + True + GTK_RELIEF_NORMAL + + + + + + + True + True + True + GTK_RELIEF_NORMAL + + + + + True + 0.5 + 0.5 + 0 + 0 + + + + True + False + 2 + + + + True + gtk-open + 4 + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + _Load + True + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + + + + + + 0 + False + True + + + + + False + True + + + + + + True + Saved layouts + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + tab + + + + + 0 + True + True + + + + + + + diff --git a/src/libgdl/libgdl.h b/src/libgdl/libgdl.h new file mode 100644 index 000000000..33f520724 --- /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 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GDL_H__ +#define __GDL_H__ + +#include "libgdl/gdl-tools.h" +#include "libgdl/gdl-dock-object.h" +#include "libgdl/gdl-dock-master.h" +#include "libgdl/gdl-dock.h" +#include "libgdl/gdl-dock-item.h" +#include "libgdl/gdl-dock-paned.h" +#include "libgdl/gdl-dock-notebook.h" +#include "libgdl/gdl-dock-tablabel.h" +#include "libgdl/gdl-dock-bar.h" +#include "libgdl/gdl-combo-button.h" +#include "libgdl/gdl-switcher.h" + +#endif diff --git a/src/libgdl/libgdlmarshal.c b/src/libgdl/libgdlmarshal.c new file mode 100644 index 000000000..06a4e7596 --- /dev/null +++ b/src/libgdl/libgdlmarshal.c @@ -0,0 +1,169 @@ +#include "libgdlmarshal.h" + +#include + + +#ifdef G_ENABLE_DEBUG +#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) +#define g_marshal_value_peek_char(v) g_value_get_char (v) +#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) +#define g_marshal_value_peek_int(v) g_value_get_int (v) +#define g_marshal_value_peek_uint(v) g_value_get_uint (v) +#define g_marshal_value_peek_long(v) g_value_get_long (v) +#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) +#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) +#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) +#define g_marshal_value_peek_enum(v) g_value_get_enum (v) +#define g_marshal_value_peek_flags(v) g_value_get_flags (v) +#define g_marshal_value_peek_float(v) g_value_get_float (v) +#define g_marshal_value_peek_double(v) g_value_get_double (v) +#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) +#define g_marshal_value_peek_param(v) g_value_get_param (v) +#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) +#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) +#define g_marshal_value_peek_object(v) g_value_get_object (v) +#else /* !G_ENABLE_DEBUG */ +/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. + * Do not access GValues directly in your code. Instead, use the + * g_value_get_*() functions + */ +#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int +#define g_marshal_value_peek_char(v) (v)->data[0].v_int +#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint +#define g_marshal_value_peek_int(v) (v)->data[0].v_int +#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint +#define g_marshal_value_peek_long(v) (v)->data[0].v_long +#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 +#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 +#define g_marshal_value_peek_enum(v) (v)->data[0].v_long +#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_float(v) (v)->data[0].v_float +#define g_marshal_value_peek_double(v) (v)->data[0].v_double +#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer +#endif /* !G_ENABLE_DEBUG */ + + +/* VOID:VOID (./libgdlmarshal.list:1) */ + +/* VOID:INT,INT (./libgdlmarshal.list:2) */ +void +gdl_marshal_VOID__INT_INT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__INT_INT) (gpointer data1, + gint arg_1, + gint arg_2, + gpointer data2); + register GMarshalFunc_VOID__INT_INT callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__INT_INT) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_int (param_values + 1), + g_marshal_value_peek_int (param_values + 2), + data2); +} + +/* VOID:UINT,UINT (./libgdlmarshal.list:3) */ +void +gdl_marshal_VOID__UINT_UINT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__UINT_UINT) (gpointer data1, + guint arg_1, + guint arg_2, + gpointer data2); + register GMarshalFunc_VOID__UINT_UINT callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__UINT_UINT) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_uint (param_values + 1), + g_marshal_value_peek_uint (param_values + 2), + data2); +} + +/* VOID:BOOLEAN (./libgdlmarshal.list:4) */ + +/* VOID:OBJECT,ENUM,BOXED (./libgdlmarshal.list:5) */ +void +gdl_marshal_VOID__OBJECT_ENUM_BOXED (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__OBJECT_ENUM_BOXED) (gpointer data1, + gpointer arg_1, + gint arg_2, + gpointer arg_3, + gpointer data2); + register GMarshalFunc_VOID__OBJECT_ENUM_BOXED callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 4); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__OBJECT_ENUM_BOXED) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_object (param_values + 1), + g_marshal_value_peek_enum (param_values + 2), + g_marshal_value_peek_boxed (param_values + 3), + data2); +} + +/* VOID:BOXED (./libgdlmarshal.list:6) */ + diff --git a/src/libgdl/libgdlmarshal.h b/src/libgdl/libgdlmarshal.h new file mode 100644 index 000000000..3e0116e1a --- /dev/null +++ b/src/libgdl/libgdlmarshal.h @@ -0,0 +1,45 @@ + +#ifndef __gdl_marshal_MARSHAL_H__ +#define __gdl_marshal_MARSHAL_H__ + +#include + +G_BEGIN_DECLS + +/* VOID:VOID (./libgdlmarshal.list:1) */ +#define gdl_marshal_VOID__VOID g_cclosure_marshal_VOID__VOID + +/* VOID:INT,INT (./libgdlmarshal.list:2) */ +extern void gdl_marshal_VOID__INT_INT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:UINT,UINT (./libgdlmarshal.list:3) */ +extern void gdl_marshal_VOID__UINT_UINT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:BOOLEAN (./libgdlmarshal.list:4) */ +#define gdl_marshal_VOID__BOOLEAN g_cclosure_marshal_VOID__BOOLEAN + +/* VOID:OBJECT,ENUM,BOXED (./libgdlmarshal.list:5) */ +extern void gdl_marshal_VOID__OBJECT_ENUM_BOXED (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:BOXED (./libgdlmarshal.list:6) */ +#define gdl_marshal_VOID__BOXED g_cclosure_marshal_VOID__BOXED + +G_END_DECLS + +#endif /* __gdl_marshal_MARSHAL_H__ */ + diff --git a/src/libgdl/libgdlmarshal.list b/src/libgdl/libgdlmarshal.list new file mode 100644 index 000000000..f3200d6d2 --- /dev/null +++ b/src/libgdl/libgdlmarshal.list @@ -0,0 +1,6 @@ +VOID:VOID +VOID:INT,INT +VOID:UINT,UINT +VOID:BOOLEAN +VOID:OBJECT,ENUM,BOXED +VOID:BOXED diff --git a/src/libgdl/libgdltypebuiltins.c b/src/libgdl/libgdltypebuiltins.c new file mode 100644 index 000000000..5ec9ec808 --- /dev/null +++ b/src/libgdl/libgdltypebuiltins.c @@ -0,0 +1,161 @@ + +/* Generated data (by glib-mkenums) */ + +#include +#include "libgdltypebuiltins.h" + + +/* enumerations from "gdl-dock-object.h" */ +static const GFlagsValue _gdl_dock_param_flags_values[] = { + { GDL_DOCK_PARAM_EXPORT, "GDL_DOCK_PARAM_EXPORT", "export" }, + { GDL_DOCK_PARAM_AFTER, "GDL_DOCK_PARAM_AFTER", "after" }, + { 0, NULL, NULL } +}; + +GType +gdl_dock_param_flags_get_type (void) +{ + static GType type = 0; + + if (!type) + type = g_flags_register_static ("GdlDockParamFlags", _gdl_dock_param_flags_values); + + return type; +} + +static const GFlagsValue _gdl_dock_object_flags_values[] = { + { GDL_DOCK_AUTOMATIC, "GDL_DOCK_AUTOMATIC", "automatic" }, + { GDL_DOCK_ATTACHED, "GDL_DOCK_ATTACHED", "attached" }, + { GDL_DOCK_IN_REFLOW, "GDL_DOCK_IN_REFLOW", "in-reflow" }, + { GDL_DOCK_IN_DETACH, "GDL_DOCK_IN_DETACH", "in-detach" }, + { 0, NULL, NULL } +}; + +GType +gdl_dock_object_flags_get_type (void) +{ + static GType type = 0; + + if (!type) + type = g_flags_register_static ("GdlDockObjectFlags", _gdl_dock_object_flags_values); + + return type; +} + +static const GEnumValue _gdl_dock_placement_values[] = { + { GDL_DOCK_NONE, "GDL_DOCK_NONE", "none" }, + { GDL_DOCK_TOP, "GDL_DOCK_TOP", "top" }, + { GDL_DOCK_BOTTOM, "GDL_DOCK_BOTTOM", "bottom" }, + { GDL_DOCK_RIGHT, "GDL_DOCK_RIGHT", "right" }, + { GDL_DOCK_LEFT, "GDL_DOCK_LEFT", "left" }, + { GDL_DOCK_CENTER, "GDL_DOCK_CENTER", "center" }, + { GDL_DOCK_FLOATING, "GDL_DOCK_FLOATING", "floating" }, + { 0, NULL, NULL } +}; + +GType +gdl_dock_placement_get_type (void) +{ + static GType type = 0; + + if (!type) + type = g_enum_register_static ("GdlDockPlacement", _gdl_dock_placement_values); + + return type; +} + + +/* enumerations from "gdl-dock-item.h" */ +static const GFlagsValue _gdl_dock_item_behavior_values[] = { + { GDL_DOCK_ITEM_BEH_NORMAL, "GDL_DOCK_ITEM_BEH_NORMAL", "normal" }, + { GDL_DOCK_ITEM_BEH_NEVER_FLOATING, "GDL_DOCK_ITEM_BEH_NEVER_FLOATING", "never-floating" }, + { GDL_DOCK_ITEM_BEH_NEVER_VERTICAL, "GDL_DOCK_ITEM_BEH_NEVER_VERTICAL", "never-vertical" }, + { GDL_DOCK_ITEM_BEH_NEVER_HORIZONTAL, "GDL_DOCK_ITEM_BEH_NEVER_HORIZONTAL", "never-horizontal" }, + { GDL_DOCK_ITEM_BEH_LOCKED, "GDL_DOCK_ITEM_BEH_LOCKED", "locked" }, + { GDL_DOCK_ITEM_BEH_CANT_DOCK_TOP, "GDL_DOCK_ITEM_BEH_CANT_DOCK_TOP", "cant-dock-top" }, + { GDL_DOCK_ITEM_BEH_CANT_DOCK_BOTTOM, "GDL_DOCK_ITEM_BEH_CANT_DOCK_BOTTOM", "cant-dock-bottom" }, + { GDL_DOCK_ITEM_BEH_CANT_DOCK_LEFT, "GDL_DOCK_ITEM_BEH_CANT_DOCK_LEFT", "cant-dock-left" }, + { GDL_DOCK_ITEM_BEH_CANT_DOCK_RIGHT, "GDL_DOCK_ITEM_BEH_CANT_DOCK_RIGHT", "cant-dock-right" }, + { GDL_DOCK_ITEM_BEH_CANT_DOCK_CENTER, "GDL_DOCK_ITEM_BEH_CANT_DOCK_CENTER", "cant-dock-center" }, + { GDL_DOCK_ITEM_BEH_CANT_CLOSE, "GDL_DOCK_ITEM_BEH_CANT_CLOSE", "cant-close" }, + { GDL_DOCK_ITEM_BEH_CANT_ICONIFY, "GDL_DOCK_ITEM_BEH_CANT_ICONIFY", "cant-iconify" }, + { GDL_DOCK_ITEM_BEH_NO_GRIP, "GDL_DOCK_ITEM_BEH_NO_GRIP", "no-grip" }, + { 0, NULL, NULL } +}; + +GType +gdl_dock_item_behavior_get_type (void) +{ + static GType type = 0; + + if (!type) + type = g_flags_register_static ("GdlDockItemBehavior", _gdl_dock_item_behavior_values); + + return type; +} + +static const GFlagsValue _gdl_dock_item_flags_values[] = { + { GDL_DOCK_IN_DRAG, "GDL_DOCK_IN_DRAG", "in-drag" }, + { GDL_DOCK_IN_PREDRAG, "GDL_DOCK_IN_PREDRAG", "in-predrag" }, + { GDL_DOCK_ICONIFIED, "GDL_DOCK_ICONIFIED", "iconified" }, + { GDL_DOCK_USER_ACTION, "GDL_DOCK_USER_ACTION", "user-action" }, + { 0, NULL, NULL } +}; + +GType +gdl_dock_item_flags_get_type (void) +{ + static GType type = 0; + + if (!type) + type = g_flags_register_static ("GdlDockItemFlags", _gdl_dock_item_flags_values); + + return type; +} + + +/* enumerations from "gdl-dock-bar.h" */ +static const GEnumValue _gdl_dock_bar_style_values[] = { + { GDL_DOCK_BAR_ICONS, "GDL_DOCK_BAR_ICONS", "icons" }, + { GDL_DOCK_BAR_TEXT, "GDL_DOCK_BAR_TEXT", "text" }, + { GDL_DOCK_BAR_BOTH, "GDL_DOCK_BAR_BOTH", "both" }, + { GDL_DOCK_BAR_AUTO, "GDL_DOCK_BAR_AUTO", "auto" }, + { 0, NULL, NULL } +}; + +GType +gdl_dock_bar_style_get_type (void) +{ + static GType type = 0; + + if (!type) + type = g_enum_register_static ("GdlDockBarStyle", _gdl_dock_bar_style_values); + + return type; +} + + +/* enumerations from "gdl-switcher.h" */ +static const GEnumValue _gdl_switcher_style_values[] = { + { GDL_SWITCHER_STYLE_TEXT, "GDL_SWITCHER_STYLE_TEXT", "text" }, + { GDL_SWITCHER_STYLE_ICON, "GDL_SWITCHER_STYLE_ICON", "icon" }, + { GDL_SWITCHER_STYLE_BOTH, "GDL_SWITCHER_STYLE_BOTH", "both" }, + { GDL_SWITCHER_STYLE_TOOLBAR, "GDL_SWITCHER_STYLE_TOOLBAR", "toolbar" }, + { GDL_SWITCHER_STYLE_TABS, "GDL_SWITCHER_STYLE_TABS", "tabs" }, + { 0, NULL, NULL } +}; + +GType +gdl_switcher_style_get_type (void) +{ + static GType type = 0; + + if (!type) + type = g_enum_register_static ("GdlSwitcherStyle", _gdl_switcher_style_values); + + return type; +} + + +/* Generated data ends here */ + diff --git a/src/libgdl/libgdltypebuiltins.h b/src/libgdl/libgdltypebuiltins.h new file mode 100644 index 000000000..22b28cf4b --- /dev/null +++ b/src/libgdl/libgdltypebuiltins.h @@ -0,0 +1,38 @@ + +/* Generated data (by glib-mkenums) */ + +#ifndef __LIBGDLTYPEBUILTINS_H__ +#define __LIBGDLTYPEBUILTINS_H__ 1 + +#include "libgdl/libgdl.h" + +G_BEGIN_DECLS + + +/* --- gdl-dock-object.h --- */ +#define GDL_TYPE_DOCK_PARAM_FLAGS gdl_dock_param_flags_get_type() +GType gdl_dock_param_flags_get_type (void); +#define GDL_TYPE_DOCK_OBJECT_FLAGS gdl_dock_object_flags_get_type() +GType gdl_dock_object_flags_get_type (void); +#define GDL_TYPE_DOCK_PLACEMENT gdl_dock_placement_get_type() +GType gdl_dock_placement_get_type (void); + +/* --- gdl-dock-item.h --- */ +#define GDL_TYPE_DOCK_ITEM_BEHAVIOR gdl_dock_item_behavior_get_type() +GType gdl_dock_item_behavior_get_type (void); +#define GDL_TYPE_DOCK_ITEM_FLAGS gdl_dock_item_flags_get_type() +GType gdl_dock_item_flags_get_type (void); + +/* --- gdl-dock-bar.h --- */ +#define GDL_TYPE_DOCK_BAR_STYLE gdl_dock_bar_style_get_type() +GType gdl_dock_bar_style_get_type (void); + +/* --- gdl-switcher.h --- */ +#define GDL_TYPE_SWITCHER_STYLE gdl_switcher_style_get_type() +GType gdl_switcher_style_get_type (void); +G_END_DECLS + +#endif /* __LIBGDLTYPEBUILTINS_H__ */ + +/* Generated data ends here */ + diff --git a/src/libgdl/makefile b/src/libgdl/makefile new file mode 100644 index 000000000..abe183f9d --- /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 new file mode 100644 index 000000000..ebaf3399a --- /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) diff --git a/src/preferences-skeleton.h b/src/preferences-skeleton.h index 457fa6926..208b822f2 100644 --- a/src/preferences-skeleton.h +++ b/src/preferences-skeleton.h @@ -134,14 +134,14 @@ static char const preferences_skeleton[] = " \n" " \n" " \n" +" \n" " \n" " \n" " \n" " \n" " \n" -" \n" -" \n" +" \n" +" \n" " \n" " \n" " \n" @@ -162,7 +162,7 @@ static char const preferences_skeleton[] = " \n" " \n" " \n" -" \n" +" \n" " \n" " \n" " \n" @@ -195,6 +195,7 @@ static char const preferences_skeleton[] = " \n" " \n" " \n" +" \n" " \n" #ifdef WIN32 // FIXME: Temporary Win32 special code to enable transient dialogs " \n" diff --git a/src/ui/dialog/Makefile_insert b/src/ui/dialog/Makefile_insert index d3bb8b62c..6679e99de 100644 --- a/src/ui/dialog/Makefile_insert +++ b/src/ui/dialog/Makefile_insert @@ -10,12 +10,17 @@ ui_dialog_libuidialog_a_SOURCES = \ ui/dialog/dialog-manager.h \ ui/dialog/dialog.cpp \ ui/dialog/dialog.h \ + ui/dialog/behavior.h \ + ui/dialog/dock-behavior.h \ + ui/dialog/dock-behavior.cpp \ + ui/dialog/floating-behavior.h \ + ui/dialog/floating-behavior.cpp \ ui/dialog/align-and-distribute.cpp \ ui/dialog/align-and-distribute.h \ ui/dialog/document-metadata.cpp \ ui/dialog/document-metadata.h \ ui/dialog/document-properties.cpp \ - ui/dialog/document-properties.h \ + ui/dialog/document-properties.h \ ui/dialog/export.cpp \ ui/dialog/export.h \ ui/dialog/extension-editor.cpp \ diff --git a/src/ui/dialog/align-and-distribute.cpp b/src/ui/dialog/align-and-distribute.cpp index 34fa910cc..924a98790 100644 --- a/src/ui/dialog/align-and-distribute.cpp +++ b/src/ui/dialog/align-and-distribute.cpp @@ -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")), diff --git a/src/ui/dialog/align-and-distribute.h b/src/ui/dialog/align-and-distribute.h index 6d22071f3..0559a11e9 100644 --- a/src/ui/dialog/align-and-distribute.h +++ b/src/ui/dialog/align-and-distribute.h @@ -46,10 +46,11 @@ class Action; class AlignAndDistribute : public Dialog { public: - AlignAndDistribute(); + AlignAndDistribute(Behavior::BehaviorFactory behavior_factory); virtual ~AlignAndDistribute(); - static AlignAndDistribute *create() { return new AlignAndDistribute(); } + static AlignAndDistribute *create(Behavior::BehaviorFactory behavior_factory) + { return new AlignAndDistribute(behavior_factory); } enum AlignTarget { LAST=0, FIRST, BIGGEST, SMALLEST, PAGE, DRAWING, SELECTION }; diff --git a/src/ui/dialog/behavior.h b/src/ui/dialog/behavior.h new file mode 100644 index 000000000..b7455bbe5 --- /dev/null +++ b/src/ui/dialog/behavior.h @@ -0,0 +1,108 @@ +/** + * \brief Dialog behavior interface + * + * Author: + * Gustav Broberg + * + * 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 +#include + +class SPDesktop; + +namespace Inkscape { +namespace UI { +namespace Dialog { + +class Dialog; + +namespace Behavior { + +class Behavior; + +typedef Behavior *(*BehaviorFactory)(Dialog& dialog); + +template +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 signal_show() =0; + virtual Glib::SignalProxy0 signal_hide() =0; + virtual Glib::SignalProxy1 signal_delete_event() =0; + virtual Glib::SignalProxy1 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 : diff --git a/src/ui/dialog/dialog-manager.cpp b/src/ui/dialog/dialog-manager.cpp index 23c64f1d5..e5d326efe 100644 --- a/src/ui/dialog/dialog-manager.cpp +++ b/src/ui/dialog/dialog-manager.cpp @@ -2,11 +2,12 @@ * \brief Object for managing a set of dialogs, including their signals and * construction/caching/destruction of them. * - * Author: + * Authors: * Bryce W. Harrington * Jon Phillips + * Gustav Broberg * - * Copyright (C) 2004, 2005 Authors + * Copyright (C) 2004--2007 Authors * * Released under GNU GPL. Read the file 'COPYING' for more information. */ @@ -37,7 +38,12 @@ #include "ui/dialog/undo-history.h" #include "ui/dialog/xml-editor.h" +#include "dialogs/layers-panel.h" #include "dialogs/tiledialog.h" +#include "dialogs/iconpreview.h" + +#include "ui/dialog/floating-behavior.h" +#include "ui/dialog/dock-behavior.h" namespace Inkscape { namespace UI { @@ -45,8 +51,8 @@ namespace Dialog { namespace { -template -Dialog *create() { return T::create(); } +template +inline Dialog *create() { return T::create(&B::create); } } @@ -71,26 +77,63 @@ Dialog *create() { return T::create(); } * */ DialogManager::DialogManager() { - registerFactory("AlignAndDistribute", &create); - registerFactory("DocumentMetadata", &create); - registerFactory("DocumentProperties", &create); - registerFactory("Export", &create); - registerFactory("ExtensionEditor", &create); - registerFactory("FillAndStroke", &create); - registerFactory("FilterEffectsDialog", &create); - registerFactory("Find", &create); - registerFactory("InkscapePreferences", &create); - registerFactory("LayerEditor", &create); - registerFactory("LivePathEffect", &create); - registerFactory("Memory", &create); - registerFactory("Messages", &create); - registerFactory("Script", &create); - registerFactory("TextProperties", &create); - registerFactory("TileDialog", &create); - registerFactory("Trace", &create); - registerFactory("Transformation", &create); - registerFactory("UndoHistory", &create); - registerFactory("XmlEditor", &create); + + 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); + registerFactory("DocumentMetadata", &create); + registerFactory("DocumentProperties", &create); + registerFactory("Export", &create); + registerFactory("ExtensionEditor", &create); + registerFactory("FillAndStroke", &create); + registerFactory("FilterEffectsDialog", &create); + registerFactory("Find", &create); + registerFactory("IconPreviewPanel", &create); + registerFactory("InkscapePreferences", &create); + registerFactory("LayerEditor", &create); + registerFactory("LayersPanel", &create); + registerFactory("LivePathEffect", &create); + registerFactory("Memory", &create); + registerFactory("Messages", &create); + registerFactory("Script", &create); + registerFactory("TextProperties", &create); + registerFactory("TileDialog", &create); + registerFactory("Trace", &create); + registerFactory("Transformation", &create); + registerFactory("UndoHistory", &create); + registerFactory("XmlEditor", &create); + + } else { + + registerFactory("AlignAndDistribute", &create); + registerFactory("DocumentMetadata", &create); + registerFactory("DocumentProperties", &create); + registerFactory("Export", &create); + registerFactory("ExtensionEditor", &create); + registerFactory("FillAndStroke", &create); + registerFactory("FilterEffectsDialog", &create); + registerFactory("Find", &create); + registerFactory("IconPreviewPanel", &create); + registerFactory("InkscapePreferences", &create); + registerFactory("LayerEditor", &create); + registerFactory("LayersPanel", &create); + registerFactory("LivePathEffect", &create); + registerFactory("Memory", &create); + registerFactory("Messages", &create); + registerFactory("Script", &create); + registerFactory("TextProperties", &create); + registerFactory("TileDialog", &create); + registerFactory("Trace", &create); + registerFactory("Transformation", &create); + registerFactory("UndoHistory", &create); + registerFactory("XmlEditor", &create); + + } } DialogManager::~DialogManager() { @@ -163,7 +206,6 @@ void DialogManager::showDialog(GQuark name) { Dialog *dialog=getDialog(name); if (dialog) { dialog->present(); - dialog->read_geometry(); } } diff --git a/src/ui/dialog/dialog.cpp b/src/ui/dialog/dialog.cpp index ac147d656..16124d9f5 100644 --- a/src/ui/dialog/dialog.cpp +++ b/src/ui/dialog/dialog.cpp @@ -2,12 +2,13 @@ * \brief Base class for dialogs in Inkscape. This class provides certain * common behaviors and styles wanted of all dialogs in the application. * - * Author: + * Authors: * Bryce W. Harrington * buliabyak@gmail.com * Johan Engelen + * Gustav Broberg * - * Copyright (C) 2004-2007 Authors + * Copyright (C) 2004--2007 Authors * * Released under GNU GPL. Read the file 'COPYING' for more information. */ @@ -32,23 +33,13 @@ #include "interface.h" #include "verbs.h" - #define MIN_ONSCREEN_DISTANCE 50 - namespace Inkscape { namespace UI { namespace Dialog { -static gboolean -sp_retransientize_again (gpointer dlgPtr) -{ - Dialog *dlg = (Dialog *)dlgPtr; - dlg->retransientize_suppress = false; - return FALSE; // so that it is only called once -} - static void sp_retransientize (Inkscape::Application *inkscape, SPDesktop *desktop, gpointer dlgPtr) { @@ -63,13 +54,6 @@ sp_dialog_shutdown (GtkObject *object, gpointer dlgPtr) dlg->onShutdown(); } -void -Dialog::present() -{ - _user_hidden = false; - Gtk::Dialog::present(); -} - void Dialog::save_geometry() { @@ -78,7 +62,7 @@ Dialog::save_geometry() get_position(x, y); get_size(w, h); -// g_print ("write %d %d %d %d\n", x, y, w, h); + // g_print ("write %d %d %d %d\n", x, y, w, h); if (x<0) x=0; if (y<0) y=0; @@ -87,8 +71,27 @@ Dialog::save_geometry() prefs_set_int_attribute (_prefs_path, "y", y); prefs_set_int_attribute (_prefs_path, "w", w); prefs_set_int_attribute (_prefs_path, "h", h); + } +void hideCallback(GtkObject *object, gpointer dlgPtr) +{ + g_return_if_fail( dlgPtr != NULL ); + + Dialog *dlg = (Dialog *)dlgPtr; + dlg->onHideF12(); +} + +void unhideCallback(GtkObject *object, gpointer dlgPtr) +{ + g_return_if_fail( dlgPtr != NULL ); + + Dialog *dlg = (Dialog *)dlgPtr; + dlg->onShowF12(); +} + + + void Dialog::read_geometry() { @@ -99,12 +102,12 @@ Dialog::read_geometry() int w = prefs_get_int_attribute (_prefs_path, "w", 0); int h = prefs_get_int_attribute (_prefs_path, "h", 0); -// g_print ("read %d %d %d %d\n", x, y, w, h); + // g_print ("read %d %d %d %d\n", x, y, w, h); // If there are stored height and width values for the dialog, // resize the window to match; otherwise we leave it at its default if (w != 0 && h != 0) { - resize (w, h); + resize(w, h); } // If there are stored values for where the dialog should be @@ -116,22 +119,47 @@ Dialog::read_geometry() // ...otherwise just put it in the middle of the screen set_position(Gtk::WIN_POS_CENTER); } -} - -void hideCallback(GtkObject *object, gpointer dlgPtr) -{ - g_return_if_fail( dlgPtr != NULL ); - Dialog *dlg = (Dialog *)dlgPtr; - dlg->onHideF12(); } -void unhideCallback(GtkObject *object, gpointer dlgPtr) +inline Dialog::operator Gtk::Widget&() { return *_behavior; } +inline GtkWidget *Dialog::gobj() { return _behavior->gobj(); } +inline void Dialog::present() { _behavior->present(); } +inline Gtk::VBox *Dialog::get_vbox() { return _behavior->get_vbox(); } +inline void Dialog::show_all_children() { _behavior->show_all_children(); } +inline void Dialog::hide() { _behavior->hide(); } +inline void Dialog::show() { _behavior->show(); } +inline void Dialog::set_size_request(int width, int height) { _behavior->set_size_request(width, height); } +inline void Dialog::size_request(Gtk::Requisition& requisition) { _behavior->size_request(requisition); } +inline void Dialog::get_position(int& x, int& y) { _behavior->get_position(x, y); } +inline void Dialog::get_size(int& width, int& height) { _behavior->get_size(width, height); } +inline void Dialog::resize(int width, int height) { _behavior->resize(width, height); } +inline void Dialog::move(int x, int y) { _behavior->move(x, y); } +inline void Dialog::set_position(Gtk::WindowPosition position) { _behavior->set_position(position); } +inline void Dialog::set_title(Glib::ustring title) { _behavior->set_title(title); } +inline void Dialog::set_sensitive(bool sensitive) { _behavior->set_sensitive(sensitive); } + +inline void Dialog::set_response_sensitive(int response_id, bool setting) +{ _behavior->set_response_sensitive(response_id, setting); } + +void Dialog::set_resizable(bool) { } +void Dialog::set_default(Gtk::Widget&) { } + +inline void Dialog::set_default_response(int response_id) { _behavior->set_default_response(response_id); } + +Glib::SignalProxy0 Dialog::signal_show () { return _behavior->signal_show(); } +Glib::SignalProxy0 Dialog::signal_hide () { return _behavior->signal_hide(); } +Glib::SignalProxy1 Dialog::signal_response () { return _behavior->signal_response(); } + +Gtk::Button* Dialog::add_button (const Glib::ustring& button_text, int response_id) +{ return _behavior->add_button(button_text, response_id); } + +Gtk::Button* Dialog::add_button (const Gtk::StockID& stock_id, int response_id) +{ return _behavior->add_button(stock_id, response_id); } + +Dialog::Dialog(const char *prefs_path, int verb_num, const char *apply_label) { - g_return_if_fail( dlgPtr != NULL ); - Dialog *dlg = (Dialog *)dlgPtr; - dlg->onShowF12(); } //===================================================================== @@ -146,34 +174,24 @@ void unhideCallback(GtkObject *object, gpointer dlgPtr) * It also provides some general purpose signal handlers for things like * showing and hiding all dialogs. */ -Dialog::Dialog(const char *prefs_path, int verb_num, const char *apply_label) +Dialog::Dialog(Behavior::BehaviorFactory behavior_factory, const char *prefs_path, int verb_num, + const char *apply_label) + : _hiddenF12 (false), + _prefs_path (prefs_path), + _verb_num(verb_num), + _apply_label (apply_label) { - hide(); - set_has_separator(false); - - _prefs_path = prefs_path; - - if (prefs_get_int_attribute ("dialogs", "showclose", 0) || apply_label) { - // TODO: make the order of buttons obey the global preference - if (apply_label) { - add_button(Glib::ustring(apply_label), Gtk::RESPONSE_APPLY); - set_default_response(Gtk::RESPONSE_APPLY); - } - add_button(Gtk::Stock::CLOSE, Gtk::RESPONSE_CLOSE); - } - - GtkWidget *dlg = GTK_WIDGET(gobj()); + gchar title[500]; if (verb_num) - { - gchar title[500]; sp_ui_dialog_title_string (Inkscape::Verb::get(verb_num), title); - set_title(title); - } - gtk_signal_connect( GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC(sp_dialog_event_handler), dlg ); + _title = title; + + _behavior = behavior_factory(*this); + + gtk_signal_connect( GTK_OBJECT (gobj()), "event", GTK_SIGNAL_FUNC(sp_dialog_event_handler), gobj() ); - _hiddenF12 = false; if (Inkscape::NSApplication::Application::getNewGui()) { _desktop_activated_connection = Inkscape::NSApplication::Editor::connectDesktopActivated (sigc::mem_fun (*this, &Dialog::onDesktopActivated)); @@ -183,23 +201,25 @@ Dialog::Dialog(const char *prefs_path, int verb_num, const char *apply_label) } else { - g_signal_connect (G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_retransientize), (void *)this); - g_signal_connect( G_OBJECT(INKSCAPE), "dialogs_hide", G_CALLBACK(hideCallback), (void *)this ); - g_signal_connect( G_OBJECT(INKSCAPE), "dialogs_unhide", G_CALLBACK(unhideCallback), (void *)this ); - g_signal_connect (G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_dialog_shutdown), (void *)this); + g_signal_connect (G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_retransientize), (void *)this); + g_signal_connect ( G_OBJECT(INKSCAPE), "dialogs_hide", G_CALLBACK(hideCallback), (void *)this ); + g_signal_connect ( G_OBJECT(INKSCAPE), "dialogs_unhide", G_CALLBACK(unhideCallback), (void *)this ); + g_signal_connect (G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_dialog_shutdown), (void *)this); } - g_signal_connect_after( gobj(), "key_press_event", (GCallback)windowKeyPress, NULL ); + Glib::wrap(gobj())->signal_key_press_event().connect(sigc::ptr_fun(&windowKeyPress)); + + if (prefs_get_int_attribute ("dialogs", "showclose", 0) || apply_label) { + // TODO: make the order of buttons obey the global preference + if (apply_label) { + add_button(Glib::ustring(apply_label), Gtk::RESPONSE_APPLY); + set_default_response(Gtk::RESPONSE_APPLY); + } + add_button(Gtk::Stock::CLOSE, Gtk::RESPONSE_CLOSE); + } read_geometry(); present(); - sp_transientize(dlg); - retransientize_suppress = false; -} - -Dialog::Dialog(BaseObjectType *gobj) - : Gtk::Dialog(gobj) -{ } Dialog::~Dialog() @@ -213,10 +233,11 @@ Dialog::~Dialog() } save_geometry(); + delete _behavior; } -bool Dialog::windowKeyPress( GtkWidget *widget, GdkEventKey *event ) +bool Dialog::windowKeyPress(GdkEventKey *event) { unsigned int shortcut = 0; shortcut = get_group0_keyval (event) | @@ -226,6 +247,7 @@ bool Dialog::windowKeyPress( GtkWidget *widget, GdkEventKey *event ) SP_SHORTCUT_CONTROL_MASK : 0 ) | ( event->state & GDK_MOD1_MASK ? SP_SHORTCUT_ALT_MASK : 0 ); + return sp_shortcut_invoke( shortcut, SP_ACTIVE_DESKTOP ); } @@ -259,8 +281,7 @@ void Dialog::onHideF12() { _hiddenF12 = true; - save_geometry(); - hide(); + _behavior->onHideF12(); } void @@ -270,8 +291,7 @@ Dialog::onShowF12() return; if (_hiddenF12) { - show(); - read_geometry(); + _behavior->onShowF12(); } _hiddenF12 = false; @@ -282,61 +302,15 @@ Dialog::onShutdown() { save_geometry(); _user_hidden = true; + _behavior->onShutdown(); } void -Dialog::onDesktopActivated (SPDesktop *desktop) +Dialog::onDesktopActivated(SPDesktop *desktop) { - gint transient_policy = prefs_get_int_attribute_limited ( "options.transientpolicy", "value", 1, 0, 2); - -#ifdef WIN32 // FIXME: Temporary Win32 special code to enable transient dialogs - if (prefs_get_int_attribute ( "options.dialogsontopwin32", "value", 0)) - transient_policy = 2; - else - return; -#endif - - if (!transient_policy) - return; - - - - GtkWindow *dialog_win = GTK_WINDOW(gobj()); - - if (retransientize_suppress) { - /* if retransientizing of this dialog is still forbidden after - * previous call warning turned off because it was confusingly fired - * when loading many files from command line - */ - - // g_warning("Retranzientize aborted! You're switching windows too fast!"); - return; - } - - if (dialog_win) - { - retransientize_suppress = true; // disallow other attempts to retranzientize this dialog - - desktop->setWindowTransient (dialog_win); - - /* - * This enables "aggressive" transientization, - * i.e. dialogs always emerging on top when you switch documents. Note - * however that this breaks "click to raise" policy of a window - * manager because the switched-to document will be raised at once - * (so that its transients also could raise) - */ - if (transient_policy == 2 && !_hiddenF12 && !_user_hidden) { - // without this, a transient window not always emerges on top - gtk_window_present (dialog_win); - } - } - - // we're done, allow next retransientizing not sooner than after 120 msec - gtk_timeout_add (120, (GtkFunction) sp_retransientize_again, (gpointer) this); + _behavior->onDesktopActivated(desktop); } - Inkscape::Selection* Dialog::_getSelection() { @@ -352,20 +326,26 @@ Dialog::_apply() void Dialog::_close() { - GtkWidget *dlg = GTK_WIDGET(gobj()); - - /* this code sends a delete_event to the dialog, - * instead of just destroying it, so that the - * dialog can do some housekeeping, such as remember - * its position. - */ - GdkEventAny event; - event.type = GDK_DELETE; - event.window = dlg->window; - event.send_event = TRUE; - g_object_ref (G_OBJECT (event.window)); - gtk_main_do_event ((GdkEvent*)&event); - g_object_unref (G_OBJECT (event.window)); + GtkWidget *dlg = GTK_WIDGET(_behavior->gobj()); + + /* this code sends a delete_event to the dialog, + * instead of just destroying it, so that the + * dialog can do some housekeeping, such as remember + * its position. + */ + + GdkEventAny event; + event.type = GDK_DELETE; + event.window = dlg->window; + event.send_event = TRUE; + + if (event.window) + g_object_ref (G_OBJECT (event.window)); + + gtk_main_do_event ((GdkEvent*)&event); + + if (event.window) + g_object_unref (G_OBJECT (event.window)); } } // namespace Dialog diff --git a/src/ui/dialog/dialog.h b/src/ui/dialog/dialog.h index dd3cdaafe..7530b9a35 100644 --- a/src/ui/dialog/dialog.h +++ b/src/ui/dialog/dialog.h @@ -1,11 +1,13 @@ /** - * \brief Base class for dialogs in Inkscape. This class provides certain - * common behaviors and styles wanted of all dialogs in the application. + * \brief Base class for dialogs in Inkscape. This class provides certain common behaviors and + * styles wanted of all dialogs in the application. Fundamental parts of the dialog's + * behavior is controlled by a Dialog::Behavior subclass instance connected to the dialog. * * Author: * Bryce W. Harrington + * Gustav Broberg * - * Copyright (C) 2004, 2005 Authors + * Copyright (C) 2004--2007 Authors * * Released under GNU GPL. Read the file 'COPYING' for more information. */ @@ -16,6 +18,9 @@ #include #include +#include "dock-behavior.h" +#include "floating-behavior.h" + namespace Inkscape { class Selection; } class SPDesktop; @@ -23,11 +28,15 @@ namespace Inkscape { namespace UI { namespace Dialog { -class Dialog : public Gtk::Dialog { +enum BehaviorType { FLOATING, DOCK }; + +class Dialog { public: - Dialog(BaseObjectType *gobj); // fixme: remove this - Dialog(const char *prefs_path, int verb_num = 0, const char *apply_label = NULL); + Dialog(const char *prefs_path = NULL, int verb_num = 0, const char *apply_label = NULL); + + Dialog(Behavior::BehaviorFactory behavior_factory, const char *prefs_path = NULL, + int verb_num = 0, const char *apply_label = NULL); virtual ~Dialog(); @@ -37,20 +46,50 @@ public: virtual void present(); /** Hide and show dialogs */ - virtual void onHideF12(); - virtual void onShowF12(); + virtual void onHideF12(); + virtual void onShowF12(); + + virtual operator Gtk::Widget&(); + virtual GtkWidget *gobj(); + virtual Gtk::VBox *get_vbox(); + virtual void show(); + virtual void hide(); + virtual void show_all_children(); + virtual void set_resizable(bool); + virtual void set_sensitive(bool sensitive=true); + virtual void set_default(Gtk::Widget&); + virtual void set_size_request(int, int); + virtual void size_request(Gtk::Requisition&); + virtual void get_position(int& x, int& y); + virtual void get_size(int& width, int& height); + virtual void resize(int width, int height); + virtual void move(int x, int y); + virtual void set_position(Gtk::WindowPosition position); + virtual void set_title(Glib::ustring title); + + virtual void set_response_sensitive(int response_id, bool setting); + virtual Glib::SignalProxy0 signal_show(); + virtual Glib::SignalProxy0 signal_hide(); + virtual Glib::SignalProxy1 signal_response(); + + virtual Gtk::Button* add_button (const Glib::ustring& button_text, int response_id); + virtual Gtk::Button* add_button (const Gtk::StockID& stock_id, int response_id); + + virtual void set_default_response(int response_id); - bool _hiddenF12; bool _user_hidden; // when it is closed by the user, to prevent repopping on f12 + bool _hiddenF12; void read_geometry(); void save_geometry(); - const char *_prefs_path; - bool retransientize_suppress; // when true, do not retransientize (prevents races when switching new windows too fast) protected: + const char *_prefs_path; + int _verb_num; + Glib::ustring _title; + const char *_apply_label; /** * Tooltips object for all descendants to use @@ -62,7 +101,7 @@ protected: virtual void _apply(); virtual void _close(); - static bool windowKeyPress( GtkWidget *widget, GdkEventKey *event ); + static bool windowKeyPress(GdkEventKey *event); Inkscape::Selection* _getSelection(); @@ -72,10 +111,15 @@ protected: sigc::connection _shutdown_connection; private: + Behavior::Behavior* _behavior; + Dialog(); // no constructor without params Dialog(Dialog const &d); // no copy Dialog& operator=(Dialog const &d); // no assign + + friend class Behavior::FloatingBehavior; + friend class Behavior::DockBehavior; }; } // namespace Dialog diff --git a/src/ui/dialog/dock-behavior.cpp b/src/ui/dialog/dock-behavior.cpp new file mode 100644 index 000000000..188fc6fb1 --- /dev/null +++ b/src/ui/dialog/dock-behavior.cpp @@ -0,0 +1,308 @@ +/** + * \brief A dockable dialog implementation. + * + * Author: + * Gustav Broberg + * + * Copyright (C) 2007 Authors + * + * Released under GNU GPL. Read the file 'COPYING' for more information. + */ + + +#ifdef HAVE_CONFIG_H +# include +#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 +#include +#include + +#include + +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( + 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(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 +DockBehavior::signal_show() { return _dock_item.signal_show(); } + +Glib::SignalProxy0 +DockBehavior::signal_hide() { return _dock_item.signal_hide(); } + +Glib::SignalProxy1 +DockBehavior::signal_response() { return _dock_item.signal_response(); } + +Glib::SignalProxy1 +DockBehavior::signal_delete_event() { return _dock_item.signal_delete_event(); } + +Glib::SignalProxy0 +DockBehavior::signal_drag_begin() { return _dock_item.signal_drag_begin(); } + +Glib::SignalProxy1 +DockBehavior::signal_drag_end() { return _dock_item.signal_drag_end(); } + + +} // namespace Behavior +} // namespace Dialog +} // namespace UI +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/dock-behavior.h b/src/ui/dialog/dock-behavior.h new file mode 100644 index 000000000..75bd14be4 --- /dev/null +++ b/src/ui/dialog/dock-behavior.h @@ -0,0 +1,119 @@ +/** + * \brief A dockable dialog implementation. + * + * Author: + * Gustav Broberg + * + * 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 + +#include +#include +#include + +#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 signal_show(); + Glib::SignalProxy0 signal_hide(); + Glib::SignalProxy1 signal_delete_event(); + Glib::SignalProxy0 signal_drag_begin(); + Glib::SignalProxy1 signal_drag_end(); + Glib::SignalProxy1 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 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 : diff --git a/src/ui/dialog/document-metadata.cpp b/src/ui/dialog/document-metadata.cpp index a3e87b31b..ca2da205e 100644 --- a/src/ui/dialog/document-metadata.cpp +++ b/src/ui/dialog/document-metadata.cpp @@ -63,10 +63,10 @@ static Inkscape::XML::NodeEventVector const _repr_events = { DocumentMetadata* -DocumentMetadata::create() +DocumentMetadata::create(Behavior::BehaviorFactory behavior_factory) { if (_instance) return _instance; - _instance = new DocumentMetadata; + _instance = new DocumentMetadata(behavior_factory); _instance->init(); return _instance; } @@ -81,8 +81,8 @@ DocumentMetadata::destroy() } } -DocumentMetadata::DocumentMetadata() - : Dialog ("dialogs.documentmetadata", SP_VERB_DIALOG_METADATA), +DocumentMetadata::DocumentMetadata(Behavior::BehaviorFactory behavior_factory) + : Dialog (behavior_factory, "dialogs.documentmetadata", SP_VERB_DIALOG_METADATA), _page_metadata1(1, 1), _page_metadata2(1, 1), _prefs_path("dialogs.documentmetadata") { diff --git a/src/ui/dialog/document-metadata.h b/src/ui/dialog/document-metadata.h index c3cba2d34..478f9334e 100644 --- a/src/ui/dialog/document-metadata.h +++ b/src/ui/dialog/document-metadata.h @@ -40,7 +40,7 @@ typedef std::list RDElist; class DocumentMetadata : public Inkscape::UI::Dialog::Dialog { public: void update(); - static DocumentMetadata *create(); + static DocumentMetadata *create(Behavior::BehaviorFactory behavior_factory); static void destroy(); sigc::connection _doc_replaced_connection; @@ -62,7 +62,7 @@ protected: Registry _wr; private: - DocumentMetadata(); + DocumentMetadata(Behavior::BehaviorFactory behavior_factory); virtual ~DocumentMetadata(); }; diff --git a/src/ui/dialog/document-properties.cpp b/src/ui/dialog/document-properties.cpp index 141a0c8cb..e2dc2c0b9 100644 --- a/src/ui/dialog/document-properties.cpp +++ b/src/ui/dialog/document-properties.cpp @@ -72,10 +72,10 @@ static Inkscape::XML::NodeEventVector const _repr_events = { DocumentProperties* -DocumentProperties::create() +DocumentProperties::create(Behavior::BehaviorFactory behavior_factory) { if (_instance) return _instance; - _instance = new DocumentProperties; + _instance = new DocumentProperties(behavior_factory); _instance->init(); return _instance; } @@ -90,8 +90,8 @@ DocumentProperties::destroy() } } -DocumentProperties::DocumentProperties() - : Dialog ("dialogs.documentoptions", SP_VERB_DIALOG_NAMEDVIEW), +DocumentProperties::DocumentProperties(Behavior::BehaviorFactory behavior_factory) + : Dialog (behavior_factory, "dialogs.documentoptions", SP_VERB_DIALOG_NAMEDVIEW), _page_page(1, 1), _page_guides(1, 1), _page_snap(1, 1), _page_grids(1, 1), _grids_button_new(_("_New"), _("Create new grid.")), diff --git a/src/ui/dialog/document-properties.h b/src/ui/dialog/document-properties.h index 846d95341..aa6e9c03b 100644 --- a/src/ui/dialog/document-properties.h +++ b/src/ui/dialog/document-properties.h @@ -38,7 +38,7 @@ namespace Inkscape { class DocumentProperties : public Inkscape::UI::Dialog::Dialog { public: void update(); - static DocumentProperties *create(); + static DocumentProperties *create(Behavior::BehaviorFactory behavior_factory); static void destroy(); sigc::connection _doc_replaced_connection; @@ -88,7 +88,7 @@ protected: Registry _wr; private: - DocumentProperties(); + DocumentProperties(Behavior::BehaviorFactory behavior_factory); virtual ~DocumentProperties(); // callback methods for buttons on grids page. diff --git a/src/ui/dialog/export.cpp b/src/ui/dialog/export.cpp index d9ea2d25d..4e4efafb5 100644 --- a/src/ui/dialog/export.cpp +++ b/src/ui/dialog/export.cpp @@ -20,8 +20,8 @@ namespace Inkscape { namespace UI { namespace Dialog { -Export::Export() - : Dialog ("dialogs.export", SP_VERB_FILE_EXPORT), +Export::Export(Behavior::BehaviorFactory behavior_factory) + : Dialog (behavior_factory, "dialogs.export", SP_VERB_FILE_EXPORT), _page_export(1, 1) { // Top level vbox diff --git a/src/ui/dialog/export.h b/src/ui/dialog/export.h index 567eae10d..c47c7c8cc 100644 --- a/src/ui/dialog/export.h +++ b/src/ui/dialog/export.h @@ -26,10 +26,11 @@ namespace Dialog { class Export : public Dialog { public: - Export(); + Export(Behavior::BehaviorFactory behavior_factory); virtual ~Export(); - static Export *create() { return new Export(); } + static Export *create(Behavior::BehaviorFactory behavior_factory) + { return new Export(behavior_factory); } protected: Gtk::Notebook _notebook; diff --git a/src/ui/dialog/extension-editor.cpp b/src/ui/dialog/extension-editor.cpp index 5f6386046..3a62cb144 100644 --- a/src/ui/dialog/extension-editor.cpp +++ b/src/ui/dialog/extension-editor.cpp @@ -42,8 +42,8 @@ namespace Dialog { about the selected extension. A handler is set up so that when a new extension is selected, the notebooks are changed appropriately. */ -ExtensionEditor::ExtensionEditor() - : Dialog ("dialogs.extensioneditor", SP_VERB_DIALOG_EXTENSIONEDITOR) +ExtensionEditor::ExtensionEditor(Behavior::BehaviorFactory behavior_factory) + : Dialog (behavior_factory, "dialogs.extensioneditor", SP_VERB_DIALOG_EXTENSIONEDITOR) { _notebook_info.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); _notebook_help.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); diff --git a/src/ui/dialog/extension-editor.h b/src/ui/dialog/extension-editor.h index b6c331eae..284a3651d 100644 --- a/src/ui/dialog/extension-editor.h +++ b/src/ui/dialog/extension-editor.h @@ -31,10 +31,11 @@ namespace Dialog { class ExtensionEditor : public Dialog { public: - ExtensionEditor(); + ExtensionEditor(Behavior::BehaviorFactory behavior_factory); virtual ~ExtensionEditor(); - static ExtensionEditor *create() { return new ExtensionEditor(); } + static ExtensionEditor *create(Behavior::BehaviorFactory behavior_factory) + { return new ExtensionEditor(behavior_factory); } static void show_help (gchar const * extension_id); diff --git a/src/ui/dialog/fill-and-stroke.cpp b/src/ui/dialog/fill-and-stroke.cpp index 8cf6bcfb4..ad9a9f031 100644 --- a/src/ui/dialog/fill-and-stroke.cpp +++ b/src/ui/dialog/fill-and-stroke.cpp @@ -55,8 +55,8 @@ void on_selection_modified(Inkscape::Application *inkscape, } -FillAndStroke::FillAndStroke() - : Dialog ("dialogs.fillstroke", SP_VERB_DIALOG_FILL_STROKE), +FillAndStroke::FillAndStroke(Behavior::BehaviorFactory behavior_factory) + : Dialog (behavior_factory, "dialogs.fillstroke", SP_VERB_DIALOG_FILL_STROKE), _page_fill(1, 1, true, true), _page_stroke_paint(1, 1, true, true), _page_stroke_style(1, 1, true, true), diff --git a/src/ui/dialog/fill-and-stroke.h b/src/ui/dialog/fill-and-stroke.h index b573161d9..87d5181db 100644 --- a/src/ui/dialog/fill-and-stroke.h +++ b/src/ui/dialog/fill-and-stroke.h @@ -33,10 +33,11 @@ namespace Dialog { class FillAndStroke : public Dialog { public: - FillAndStroke(); + FillAndStroke(Behavior::BehaviorFactory behavior_factory); virtual ~FillAndStroke(); - static FillAndStroke *create() { return new FillAndStroke(); } + static FillAndStroke *create(Behavior::BehaviorFactory behavior_factory) + { return new FillAndStroke(behavior_factory); } void selectionChanged(Inkscape::Application *inkscape, Inkscape::Selection *selection); diff --git a/src/ui/dialog/filter-effects-dialog.cpp b/src/ui/dialog/filter-effects-dialog.cpp index 3fa7d73d4..3bffb1285 100644 --- a/src/ui/dialog/filter-effects-dialog.cpp +++ b/src/ui/dialog/filter-effects-dialog.cpp @@ -1166,7 +1166,8 @@ void FilterEffectsDialog::FilterModifier::duplicate_filter() void FilterEffectsDialog::FilterModifier::rename_filter() { SPFilter* filter = get_selected_filter(); - Gtk::Dialog m("", _dialog, true); + Gtk::Window *window = dynamic_cast(_dialog.get_vbox()->get_ancestor(GTK_TYPE_WINDOW)); + Gtk::Dialog m("", *window, true); m.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); m.add_button(_("_Rename"), Gtk::RESPONSE_OK); m.set_default_response(Gtk::RESPONSE_OK); @@ -1843,8 +1844,8 @@ int FilterEffectsDialog::PrimitiveList::primitive_count() const /*** FilterEffectsDialog ***/ -FilterEffectsDialog::FilterEffectsDialog() - : Dialog ("dialogs.filtereffects", SP_VERB_DIALOG_FILTER_EFFECTS), +FilterEffectsDialog::FilterEffectsDialog(Behavior::BehaviorFactory behavior_factory) + : Dialog (behavior_factory, "dialogs.filtereffects", SP_VERB_DIALOG_FILTER_EFFECTS), _filter_modifier(*this), _primitive_list(*this), _add_primitive_type(FPConverter), diff --git a/src/ui/dialog/filter-effects-dialog.h b/src/ui/dialog/filter-effects-dialog.h index 1a4285dd9..743ce0239 100644 --- a/src/ui/dialog/filter-effects-dialog.h +++ b/src/ui/dialog/filter-effects-dialog.h @@ -40,11 +40,14 @@ class DualSpinButton; class MultiSpinButton; class FilterEffectsDialog : public Dialog { public: + + FilterEffectsDialog(Behavior::BehaviorFactory behavior_factory); ~FilterEffectsDialog(); - void set_attrs_locked(const bool); + static FilterEffectsDialog *create(Behavior::BehaviorFactory behavior_factory) + { return new FilterEffectsDialog(behavior_factory); } - static FilterEffectsDialog *create() { return new FilterEffectsDialog(); } + void set_attrs_locked(const bool); private: class SignalObserver; diff --git a/src/ui/dialog/find.cpp b/src/ui/dialog/find.cpp index 22fa608bf..0a0538c30 100644 --- a/src/ui/dialog/find.cpp +++ b/src/ui/dialog/find.cpp @@ -56,8 +56,8 @@ namespace Inkscape { namespace UI { namespace Dialog { -Find::Find() - : Dialog ("dialogs.find", SP_VERB_DIALOG_FIND), +Find::Find(Behavior::BehaviorFactory behavior_factory) + : Dialog (behavior_factory, "dialogs.find", SP_VERB_DIALOG_FIND), _entry_text(_("_Text: "), _("Find objects by their text content (exact or partial match)")), _entry_id(_("_ID: "), _("Find objects by the value of the id attribute (exact or partial match)")), _entry_style(_("_Style: "), _("Find objects by the value of the style attribute (exact or partial match)")), diff --git a/src/ui/dialog/find.h b/src/ui/dialog/find.h index 9594669fa..fb52b2c8e 100644 --- a/src/ui/dialog/find.h +++ b/src/ui/dialog/find.h @@ -59,10 +59,11 @@ namespace Dialog { class Find : public Dialog { public: - Find(); + Find(Behavior::BehaviorFactory behavior_factory); virtual ~Find(); - static Find *create() { return new Find(); } + static Find *create(Behavior::BehaviorFactory behavior_factory) + { return new Find(behavior_factory); } protected: // Widgets: diff --git a/src/ui/dialog/floating-behavior.cpp b/src/ui/dialog/floating-behavior.cpp new file mode 100644 index 000000000..b6b6949da --- /dev/null +++ b/src/ui/dialog/floating-behavior.cpp @@ -0,0 +1,182 @@ +/** + * \brief A floating dialog implementation. + * + * Author: + * Gustav Broberg + * + * Copyright (C) 2007 Authors + * + * Released under GNU GPL. Read the file 'COPYING' for more information. + */ + +#include +#include + +#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 FloatingBehavior::signal_show() { return _d->signal_show(); } +Glib::SignalProxy0 FloatingBehavior::signal_hide() { return _d->signal_hide(); } +Glib::SignalProxy1 FloatingBehavior::signal_delete_event () { return _d->signal_delete_event(); } +Glib::SignalProxy1 FloatingBehavior::signal_response () { return _d->signal_response(); } + + +void +FloatingBehavior::onHideF12() +{ + _dialog.save_geometry(); + hide(); +} + +void +FloatingBehavior::onShowF12() +{ + show(); + _dialog.read_geometry(); +} + +void +FloatingBehavior::onShutdown() {} + +void +FloatingBehavior::onDesktopActivated (SPDesktop *desktop) +{ + gint transient_policy = prefs_get_int_attribute_limited ( "options.transientpolicy", "value", 1, 0, 2); + +#ifdef WIN32 // FIXME: Temporary Win32 special code to enable transient dialogs + if (prefs_get_int_attribute ( "options.dialogsontopwin32", "value", 0)) + transient_policy = 2; + else + return; +#endif + + if (!transient_policy) + return; + + GtkWindow *dialog_win = GTK_WINDOW(_d->gobj()); + + if (_dialog.retransientize_suppress) { + /* if retransientizing of this dialog is still forbidden after + * previous call warning turned off because it was confusingly fired + * when loading many files from command line + */ + + // g_warning("Retranzientize aborted! You're switching windows too fast!"); + return; + } + + if (dialog_win) + { + _dialog.retransientize_suppress = true; // disallow other attempts to retranzientize this dialog + + desktop->setWindowTransient (dialog_win); + + /* + * This enables "aggressive" transientization, + * i.e. dialogs always emerging on top when you switch documents. Note + * however that this breaks "click to raise" policy of a window + * manager because the switched-to document will be raised at once + * (so that its transients also could raise) + */ + if (transient_policy == 2 && ! _dialog._hiddenF12 && !_dialog._user_hidden) { + // without this, a transient window not always emerges on top + gtk_window_present (dialog_win); + } + } + + // we're done, allow next retransientizing not sooner than after 120 msec + gtk_timeout_add (120, (GtkFunction) sp_retransientize_again, (gpointer) _d); +} + + +} // namespace Behavior +} // namespace Dialog +} // namespace UI +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/floating-behavior.h b/src/ui/dialog/floating-behavior.h new file mode 100644 index 000000000..354987dde --- /dev/null +++ b/src/ui/dialog/floating-behavior.h @@ -0,0 +1,88 @@ +/** + * \brief A floating dialog implementation. + * + * Author: + * Gustav Broberg + * + * 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 +#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 signal_show(); + Glib::SignalProxy0 signal_hide(); + Glib::SignalProxy1 signal_delete_event(); + Glib::SignalProxy1 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 : diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index 744d4f555..138f36c29 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -42,8 +42,8 @@ namespace Inkscape { namespace UI { namespace Dialog { -InkscapePreferences::InkscapePreferences() - : Dialog ("dialogs.preferences", SP_VERB_DIALOG_DISPLAY), +InkscapePreferences::InkscapePreferences(Behavior::BehaviorFactory behavior_factory) + : Dialog (behavior_factory, "dialogs.preferences", SP_VERB_DIALOG_DISPLAY), _max_dialog_width(0), _max_dialog_height(0), _current_page(0) @@ -429,6 +429,9 @@ void InkscapePreferences::initPageWindows() _win_save_geom_prefs.init ( _("Remember and use last window's geometry"), "options.savewindowgeometry", "value", 2, false, &_win_save_geom); _win_save_geom_off.init ( _("Don't save window geometry"), "options.savewindowgeometry", "value", 0, false, &_win_save_geom); + _win_dockable.init ( _("Dockable"), "options.dialogtype", "value", 1, true, 0); + _win_floating.init ( _("Floating"), "options.dialogtype", "value", 0, false, &_win_dockable); + _win_hide_task.init ( _("Dialogs are hidden in taskbar"), "options.dialogsskiptaskbar", "value", true); _win_zoom_resize.init ( _("Zoom when window is resized"), "options.stickyzoom", "value", false); _win_show_close.init ( _("Show close button on dialogs"), "dialogs", "showclose", false); @@ -449,6 +452,12 @@ void InkscapePreferences::initPageWindows() _page_windows.add_line( false, "", _win_save_geom, "", _("Save and restore window geometry for each document (saves geometry in the document)")); + _page_windows.add_group_header( _("Dialog behavior (requires restart):")); + _page_windows.add_line( true, "", _win_dockable, "", + _("Dockable")); + _page_windows.add_line( true, "", _win_floating, "", + _("Floating")); + _page_windows.add_group_header( _("Dialogs on top:")); #ifndef WIN32 // FIXME: Temporary Win32 special code to enable transient dialogs _page_windows.add_line( true, "", _win_ontop_none, "", diff --git a/src/ui/dialog/inkscape-preferences.h b/src/ui/dialog/inkscape-preferences.h index 48cec9341..6798de21d 100644 --- a/src/ui/dialog/inkscape-preferences.h +++ b/src/ui/dialog/inkscape-preferences.h @@ -76,7 +76,9 @@ class InkscapePreferences : public Dialog { public: virtual ~InkscapePreferences(); - static InkscapePreferences *create() {return new InkscapePreferences(); } + static InkscapePreferences *create(Behavior::BehaviorFactory behavior_factory) + { return new InkscapePreferences(behavior_factory); } + void present(); protected: @@ -123,6 +125,7 @@ protected: PrefSpinButton _t_pencil_tolerance; + PrefRadioButton _win_dockable, _win_floating; PrefRadioButton _win_ontop_none, _win_ontop_normal, _win_ontop_agressive; PrefRadioButton _win_save_geom_off, _win_save_geom, _win_save_geom_prefs; PrefCheckButton _win_hide_task, _win_zoom_resize , _win_show_close; @@ -191,7 +194,7 @@ protected: void initPageMisc(); private: - InkscapePreferences(); + InkscapePreferences(Behavior::BehaviorFactory behavior_factory); InkscapePreferences(InkscapePreferences const &d); InkscapePreferences operator=(InkscapePreferences const &d); }; diff --git a/src/ui/dialog/layer-editor.cpp b/src/ui/dialog/layer-editor.cpp index 79c4fbcc2..cb4b8a0b4 100644 --- a/src/ui/dialog/layer-editor.cpp +++ b/src/ui/dialog/layer-editor.cpp @@ -20,8 +20,8 @@ namespace Inkscape { namespace UI { namespace Dialog { -LayerEditor::LayerEditor() - : Dialog ("dialogs.layers", SP_VERB_NONE /*FIXME*/) +LayerEditor::LayerEditor(Behavior::BehaviorFactory behavior_factory) + : Dialog (behavior_factory, "dialogs.layers", SP_VERB_NONE /*FIXME*/) { // TODO: Insert widgets diff --git a/src/ui/dialog/layer-editor.h b/src/ui/dialog/layer-editor.h index 3d68a7fcf..3c25c7bc0 100644 --- a/src/ui/dialog/layer-editor.h +++ b/src/ui/dialog/layer-editor.h @@ -22,10 +22,11 @@ namespace Dialog { class LayerEditor : public Dialog { public: - LayerEditor(); + LayerEditor(Behavior::BehaviorFactory behavior_factory); virtual ~LayerEditor(); - static LayerEditor *create() { return new LayerEditor(); } + static LayerEditor *create(Behavior::BehaviorFactory behavior_factory) + { return new LayerEditor(behavior_factory); } protected: diff --git a/src/ui/dialog/livepatheffect-editor.cpp b/src/ui/dialog/livepatheffect-editor.cpp index 5f765b420..ee010eeca 100644 --- a/src/ui/dialog/livepatheffect-editor.cpp +++ b/src/ui/dialog/livepatheffect-editor.cpp @@ -66,8 +66,8 @@ static void lpeeditor_desktop_change(Inkscape::Application*, SPDesktop* desktop, /*####################### * LivePathEffectEditor */ -LivePathEffectEditor::LivePathEffectEditor() - : Dialog ("dialogs.livepatheffect", SP_VERB_DIALOG_LIVE_PATH_EFFECT), +LivePathEffectEditor::LivePathEffectEditor(Behavior::BehaviorFactory behavior_factory) + : Dialog (behavior_factory, "dialogs.livepatheffect", SP_VERB_DIALOG_LIVE_PATH_EFFECT), combo_effecttype(Inkscape::LivePathEffect::LPETypeConverter), button_apply(_("_Apply"), _("Apply chosen effect to selection")), button_remove(_("_Remove"), _("Remove effect from selection")), diff --git a/src/ui/dialog/livepatheffect-editor.h b/src/ui/dialog/livepatheffect-editor.h index 5476f8a1d..d99177fdd 100644 --- a/src/ui/dialog/livepatheffect-editor.h +++ b/src/ui/dialog/livepatheffect-editor.h @@ -30,10 +30,11 @@ namespace Dialog { class LivePathEffectEditor : public Dialog { public: - LivePathEffectEditor(); + LivePathEffectEditor(Behavior::BehaviorFactory behavior_factory); virtual ~LivePathEffectEditor(); - static LivePathEffectEditor *create() { return new LivePathEffectEditor(); } + static LivePathEffectEditor *create(Behavior::BehaviorFactory behavior_factory) + { return new LivePathEffectEditor(behavior_factory); } void onSelectionChanged(Inkscape::Selection *sel); void setDesktop(SPDesktop *desktop); diff --git a/src/ui/dialog/memory.cpp b/src/ui/dialog/memory.cpp index e81db17d8..eb61aaf9f 100644 --- a/src/ui/dialog/memory.cpp +++ b/src/ui/dialog/memory.cpp @@ -203,8 +203,8 @@ void Memory::Private::stop_update_task() { update_task.disconnect(); } -Memory::Memory() - : Dialog ("dialogs.memory", SP_VERB_HELP_MEMORY, _("Recalculate")), +Memory::Memory(Behavior::BehaviorFactory behavior_factory) + : Dialog (behavior_factory, "dialogs.memory", SP_VERB_HELP_MEMORY, _("Recalculate")), _private(*(new Memory::Private())) { get_vbox()->add(_private.view); diff --git a/src/ui/dialog/memory.h b/src/ui/dialog/memory.h index bec6c8fd0..0fe7f87c5 100644 --- a/src/ui/dialog/memory.h +++ b/src/ui/dialog/memory.h @@ -20,10 +20,11 @@ namespace Dialog { class Memory : public Dialog { public: - Memory(); + Memory(Behavior::BehaviorFactory behavior_factory); ~Memory(); - static Memory *create() { return new Memory(); } + static Memory *create(Behavior::BehaviorFactory behavior_factory) + { return new Memory(behavior_factory); } protected: void _apply(); diff --git a/src/ui/dialog/messages.cpp b/src/ui/dialog/messages.cpp index 9c7434edd..9e78903c9 100644 --- a/src/ui/dialog/messages.cpp +++ b/src/ui/dialog/messages.cpp @@ -45,8 +45,8 @@ void Messages::clear() /** * Constructor */ -Messages::Messages() - : Dialog ("dialogs.messages", SP_VERB_DIALOG_DEBUG) +Messages::Messages(Behavior::BehaviorFactory behavior_factory) + : Dialog (behavior_factory, "dialogs.messages", SP_VERB_DIALOG_DEBUG) { Gtk::VBox *mainVBox = get_vbox(); diff --git a/src/ui/dialog/messages.h b/src/ui/dialog/messages.h index 3ce7b5a87..85a7c4f0f 100644 --- a/src/ui/dialog/messages.h +++ b/src/ui/dialog/messages.h @@ -35,10 +35,11 @@ namespace Dialog { class Messages : public Dialog { public: - Messages(); + Messages(Behavior::BehaviorFactory behavior_factory); virtual ~Messages(); - static Messages *create() { return new Messages(); } + static Messages *create(Behavior::BehaviorFactory behavior_factory) + { return new Messages(behavior_factory); } /** * Clear all information from the dialog diff --git a/src/ui/dialog/scriptdialog.cpp b/src/ui/dialog/scriptdialog.cpp index 07f26b71f..304362f60 100644 --- a/src/ui/dialog/scriptdialog.cpp +++ b/src/ui/dialog/scriptdialog.cpp @@ -43,7 +43,7 @@ class ScriptDialogImpl : public ScriptDialog /** * Constructor */ - ScriptDialogImpl(); + ScriptDialogImpl(Behavior::BehaviorFactory behavior_factory); /** * Destructor @@ -192,7 +192,8 @@ void ScriptDialogImpl::executePerl() /** * Constructor */ -ScriptDialogImpl::ScriptDialogImpl() +ScriptDialogImpl::ScriptDialogImpl(Behavior::BehaviorFactory behavior_factory) : + ScriptDialog(behavior_factory) { Gtk::VBox *mainVBox = get_vbox(); @@ -249,9 +250,9 @@ ScriptDialogImpl::ScriptDialogImpl() /** * Factory method. Use this to create a new ScriptDialog */ -ScriptDialog *ScriptDialog::create() +ScriptDialog *ScriptDialog::create(Behavior::BehaviorFactory behavior_factory) { - ScriptDialog *dialog = new ScriptDialogImpl(); + ScriptDialog *dialog = new ScriptDialogImpl(behavior_factory); return dialog; } diff --git a/src/ui/dialog/scriptdialog.h b/src/ui/dialog/scriptdialog.h index b66aa93e7..00680d431 100644 --- a/src/ui/dialog/scriptdialog.h +++ b/src/ui/dialog/scriptdialog.h @@ -34,14 +34,15 @@ class ScriptDialog : public Dialog /** * Constructor */ - ScriptDialog() : Dialog ("dialogs.script", SP_VERB_DIALOG_SCRIPT) + ScriptDialog(Behavior::BehaviorFactory behavior_factory) : + Dialog (behavior_factory, "dialogs.script", SP_VERB_DIALOG_SCRIPT) {} /** * Factory method */ - static ScriptDialog *create(); + static ScriptDialog *create(Behavior::BehaviorFactory behavior_factory); /** * Destructor diff --git a/src/ui/dialog/text-properties.cpp b/src/ui/dialog/text-properties.cpp index a79992677..e6194ab56 100644 --- a/src/ui/dialog/text-properties.cpp +++ b/src/ui/dialog/text-properties.cpp @@ -20,8 +20,8 @@ namespace Inkscape { namespace UI { namespace Dialog { -TextProperties::TextProperties() - : Dialog ("dialogs.textandfont", SP_VERB_DIALOG_TEXT), +TextProperties::TextProperties(Behavior::BehaviorFactory behavior_factory) + : Dialog (behavior_factory, "dialogs.textandfont", SP_VERB_DIALOG_TEXT), _page_font(1, 1), _page_text(1, 1) { diff --git a/src/ui/dialog/text-properties.h b/src/ui/dialog/text-properties.h index 2d5a03b9e..393ca63b2 100644 --- a/src/ui/dialog/text-properties.h +++ b/src/ui/dialog/text-properties.h @@ -26,10 +26,11 @@ namespace Dialog { class TextProperties : public Dialog { public: - TextProperties(); + TextProperties(Behavior::BehaviorFactory behavior_factory); virtual ~TextProperties(); - static TextProperties *create() { return new TextProperties(); } + static TextProperties *create(Behavior::BehaviorFactory behavior_factory) + { return new TextProperties(behavior_factory); } protected: Gtk::Notebook _notebook; diff --git a/src/ui/dialog/tracedialog.cpp b/src/ui/dialog/tracedialog.cpp index 2c041864f..b7602b36a 100644 --- a/src/ui/dialog/tracedialog.cpp +++ b/src/ui/dialog/tracedialog.cpp @@ -50,7 +50,7 @@ class TraceDialogImpl : public TraceDialog /** * Constructor */ - TraceDialogImpl(); + TraceDialogImpl(Behavior::BehaviorFactory behavior_factory); /** * Destructor @@ -376,7 +376,8 @@ void TraceDialogImpl::responseCallback(int response_id) /** * Constructor */ -TraceDialogImpl::TraceDialogImpl() +TraceDialogImpl::TraceDialogImpl(Behavior::BehaviorFactory behavior_factory) : + TraceDialog(behavior_factory) { Gtk::VBox *mainVBox = get_vbox(); @@ -685,9 +686,9 @@ TraceDialogImpl::TraceDialogImpl() /** * Factory method. Use this to create a new TraceDialog */ -TraceDialog *TraceDialog::create() +TraceDialog *TraceDialog::create(Behavior::BehaviorFactory behavior_factory) { - TraceDialog *dialog = new TraceDialogImpl(); + TraceDialog *dialog = new TraceDialogImpl(behavior_factory); return dialog; } diff --git a/src/ui/dialog/tracedialog.h b/src/ui/dialog/tracedialog.h index 80311bb15..0e352ce10 100644 --- a/src/ui/dialog/tracedialog.h +++ b/src/ui/dialog/tracedialog.h @@ -34,14 +34,15 @@ public: /** * Constructor */ - TraceDialog() : Dialog ("dialogs.trace", SP_VERB_SELECTION_TRACE) + TraceDialog(Behavior::BehaviorFactory behavior_factory) : + Dialog (behavior_factory, "dialogs.trace", SP_VERB_SELECTION_TRACE) {} /** * Factory method */ - static TraceDialog *create(); + static TraceDialog *create(Behavior::BehaviorFactory behavior_factory); /** * Destructor diff --git a/src/ui/dialog/transformation.cpp b/src/ui/dialog/transformation.cpp index be5a8bb03..b2385a29c 100644 --- a/src/ui/dialog/transformation.cpp +++ b/src/ui/dialog/transformation.cpp @@ -70,8 +70,8 @@ void on_selection_modified ( Inkscape::Application *inkscape, * we use the ScalarUnit class for this. * */ -Transformation::Transformation() - : Dialog ("dialogs.transformation", SP_VERB_DIALOG_TRANSFORM), +Transformation::Transformation(Behavior::BehaviorFactory behavior_factory) + : Dialog (behavior_factory, "dialogs.transformation", SP_VERB_DIALOG_TRANSFORM), _page_move (4, 2), _page_scale (4, 2), _page_rotate (4, 2), @@ -181,8 +181,8 @@ void Transformation::presentPage(Transformation::PageType page) { _notebook.set_current_page(page); - Gtk::Dialog::show(); - Gtk::Dialog::present(); + show(); + present(); } diff --git a/src/ui/dialog/transformation.h b/src/ui/dialog/transformation.h index 0c7833c9e..361b30a04 100644 --- a/src/ui/dialog/transformation.h +++ b/src/ui/dialog/transformation.h @@ -46,7 +46,7 @@ public: /** * Create a new transform */ - Transformation(); + Transformation(Behavior::BehaviorFactory behavior_factory); /** * Cleanup @@ -57,8 +57,8 @@ public: /** * Factory method. Create an instance of this class/interface */ - static Transformation *create() - { return new Transformation(); } + static Transformation *create(Behavior::BehaviorFactory behavior_factory) + { return new Transformation(behavior_factory); } /** diff --git a/src/ui/dialog/undo-history.cpp b/src/ui/dialog/undo-history.cpp index 98f8cc0cc..344efc557 100644 --- a/src/ui/dialog/undo-history.cpp +++ b/src/ui/dialog/undo-history.cpp @@ -103,10 +103,10 @@ static void on_activate_desktop(Inkscape::Application*, SPDesktop* desktop, void static void on_deactivate_desktop(Inkscape::Application*, SPDesktop* desktop, void*); UndoHistory* -UndoHistory::create() +UndoHistory::create(Behavior::BehaviorFactory behavior_factory) { if (_instance) return _instance; - _instance = new UndoHistory; + _instance = new UndoHistory(behavior_factory); return _instance; } @@ -131,8 +131,8 @@ UndoHistory::setDesktop(SPDesktop* desktop) _callback_connections[EventLog::CALLB_SELECTION_CHANGE].block(false); } -UndoHistory::UndoHistory() - : Dialog ("dialogs.undo-history", SP_VERB_DIALOG_UNDO_HISTORY), +UndoHistory::UndoHistory(Behavior::BehaviorFactory behavior_factory) + : Dialog (behavior_factory, "dialogs.undo-history", SP_VERB_DIALOG_UNDO_HISTORY), _desktop (SP_ACTIVE_DESKTOP), _document (SP_ACTIVE_DOCUMENT), _event_log (_desktop ? _desktop->event_log : NULL), @@ -141,7 +141,7 @@ UndoHistory::UndoHistory() { if( !_document || !_event_log || !_columns ) return; - set_size_request(300, 400); + set_size_request(300, 200); get_vbox()->pack_start(_scrolled_window); _scrolled_window.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); diff --git a/src/ui/dialog/undo-history.h b/src/ui/dialog/undo-history.h index 7b900fde5..d7b00f343 100644 --- a/src/ui/dialog/undo-history.h +++ b/src/ui/dialog/undo-history.h @@ -120,7 +120,7 @@ class UndoHistory : public Dialog { public: virtual ~UndoHistory(); - static UndoHistory *create(); + static UndoHistory *create(Behavior::BehaviorFactory behavior_factory); void setDesktop(SPDesktop* desktop); sigc::connection _document_replaced_connection; @@ -146,9 +146,9 @@ protected: void _onCollapseEvent(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path); private: + UndoHistory(Behavior::BehaviorFactory behavior_factory); // no default constructor, noncopyable, nonassignable - UndoHistory(); UndoHistory(UndoHistory const &d); UndoHistory operator=(UndoHistory const &d); diff --git a/src/ui/dialog/xml-editor.cpp b/src/ui/dialog/xml-editor.cpp index b8abf8bfd..53f3b4c2c 100644 --- a/src/ui/dialog/xml-editor.cpp +++ b/src/ui/dialog/xml-editor.cpp @@ -20,8 +20,8 @@ namespace Inkscape { namespace UI { namespace Dialog { -XmlEditor::XmlEditor() - : Dialog ("dialogs.xml", SP_VERB_DIALOG_XML_EDITOR) +XmlEditor::XmlEditor(Behavior::BehaviorFactory behavior_factory) + : Dialog (behavior_factory, "dialogs.xml", SP_VERB_DIALOG_XML_EDITOR) { // TODO: Insert widgets diff --git a/src/ui/dialog/xml-editor.h b/src/ui/dialog/xml-editor.h index b494ade1f..65f25423f 100644 --- a/src/ui/dialog/xml-editor.h +++ b/src/ui/dialog/xml-editor.h @@ -22,10 +22,11 @@ namespace Dialog { class XmlEditor : public Dialog { public: - XmlEditor(); + XmlEditor(Behavior::BehaviorFactory behavior_factory); virtual ~XmlEditor(); - static XmlEditor *create() { return new XmlEditor(); } + static XmlEditor *create(Behavior::BehaviorFactory behavior_factory) + { return new XmlEditor(behavior_factory); } protected: diff --git a/src/ui/view/edit-widget-interface.h b/src/ui/view/edit-widget-interface.h index 8e388529e..c8219b7b2 100644 --- a/src/ui/view/edit-widget-interface.h +++ b/src/ui/view/edit-widget-interface.h @@ -18,9 +18,10 @@ #include "libnr/nr-point.h" #include "message.h" - #include +namespace Inkscape { namespace UI { namespace Widget { class Dock; } } } + namespace Inkscape { namespace UI { namespace View { @@ -135,6 +136,8 @@ struct EditWidgetInterface /// Open yes/no dialog with warning text and confirmation question. virtual bool warnDialog (gchar*) = 0; + + virtual Inkscape::UI::Widget::Dock* getDock () = 0; }; } // namespace View diff --git a/src/ui/view/edit-widget.cpp b/src/ui/view/edit-widget.cpp index b87250e47..066d4d0ba 100644 --- a/src/ui/view/edit-widget.cpp +++ b/src/ui/view/edit-widget.cpp @@ -65,6 +65,8 @@ #include "interface.h" #include "extension/db.h" +#include "ui/dialog/dialog-manager.h" + using namespace Inkscape::UI; using namespace Inkscape::UI::Widget; @@ -152,7 +154,6 @@ EditWidget::initLayout() _viewport_table.attach(_svg_canvas.widget(), 1, 2, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND); _svg_canvas.widget().show_all(); - // The statusbar comes last and appears at the bottom of _main_window_table initStatusbar(); @@ -1532,6 +1533,12 @@ EditWidget::warnDialog (gchar* msg) } +Inkscape::UI::Widget::Dock* +EditWidget::getDock () +{ + return &_dock; +} + void EditWidget::_namedview_modified (SPObject *obj, guint flags) { SPNamedView *nv = static_cast(obj); if (flags & SP_OBJECT_MODIFIED_FLAG) { diff --git a/src/ui/view/edit-widget.h b/src/ui/view/edit-widget.h index 443b10ac8..4341f667c 100644 --- a/src/ui/view/edit-widget.h +++ b/src/ui/view/edit-widget.h @@ -27,6 +27,7 @@ #include "ui/dialog/dialog-manager.h" #include "ui/view/edit-widget-interface.h" +#include "ui/widget/dock.h" #include "ui/widget/selected-style.h" #include "ui/widget/ruler.h" #include "ui/widget/toolbox.h" @@ -132,6 +133,8 @@ public: virtual void setMessage (Inkscape::MessageType type, gchar const* msg); virtual bool warnDialog (gchar*); + virtual Inkscape::UI::Widget::Dock* getDock (); + protected: void _namedview_modified(SPObject *namedview, guint); @@ -157,6 +160,7 @@ protected: Gtk::ToggleButton _sticky_zoom; UI::Widget::SVGCanvas _svg_canvas; Gtk::HBox _statusbar; + UI::Widget::Dock _dock; UI::Widget::SelectedStyle _selected_style_status; UI::Widget::ZoomStatus _zoom_status; Inkscape::Widgets::LayerSelector _layer_selector; diff --git a/src/ui/widget/Makefile_insert b/src/ui/widget/Makefile_insert index 178e954a4..7113857c8 100644 --- a/src/ui/widget/Makefile_insert +++ b/src/ui/widget/Makefile_insert @@ -15,6 +15,10 @@ ui_widget_libuiwidget_a_SOURCES = \ ui/widget/combo-enums.h \ ui/widget/combo-text.cpp \ ui/widget/combo-text.h \ + ui/widget/dock.h \ + ui/widget/dock.cpp \ + ui/widget/dock-item.h \ + ui/widget/dock-item.cpp \ ui/widget/entity-entry.cpp \ ui/widget/entity-entry.h \ ui/widget/entry.cpp \ diff --git a/src/ui/widget/dock-item.cpp b/src/ui/widget/dock-item.cpp new file mode 100644 index 000000000..1e232cb4e --- /dev/null +++ b/src/ui/widget/dock-item.cpp @@ -0,0 +1,539 @@ +/** + * \brief A custom Inkscape wrapper around gdl_dock_item + * + * Author: + * Gustav Broberg + * + * 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 + +#include +#include + +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 +DockItem::signal_show() +{ + return Glib::SignalProxy0(Glib::wrap(GTK_WIDGET(_gdl_dock_item)), + &_signal_show_proxy); +} + +Glib::SignalProxy0 +DockItem::signal_hide() +{ + return Glib::SignalProxy0(Glib::wrap(GTK_WIDGET(_gdl_dock_item)), + &_signal_hide_proxy); +} + +Glib::SignalProxy1 +DockItem::signal_delete_event() +{ + return Glib::SignalProxy1(Glib::wrap(GTK_WIDGET(_gdl_dock_item)), + &_signal_delete_event_proxy); +} + +Glib::SignalProxy1 +DockItem::signal_response() +{ + return Glib::SignalProxy1(Glib::wrap(GTK_WIDGET(_gdl_dock_item)), + &_signal_response_proxy); +} + +Glib::SignalProxy0 +DockItem::signal_drag_begin() +{ + return Glib::SignalProxy0(Glib::wrap(GTK_WIDGET(_gdl_dock_item)), + &_signal_drag_begin_proxy); +} + +Glib::SignalProxy1 +DockItem::signal_drag_end() +{ + return Glib::SignalProxy1(Glib::wrap(GTK_WIDGET(_gdl_dock_item)), + &_signal_drag_end_proxy); +} + +sigc::signal +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(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 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( (*static_cast(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 SlotType; + + if (Glib::ObjectBase::_get_current_wrapper((GObject *) self)) { + try { + if(sigc::slot_base *const slot = Glib::SignalProxyNormal::data_to_slot(data)) + (*static_cast(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 SlotType; + + if (Glib::ObjectBase::_get_current_wrapper((GObject *) self)) { + try { + if(sigc::slot_base *const slot = Glib::SignalProxyNormal::data_to_slot(data)) + (*static_cast(slot))(cancelled); + } catch(...) { + Glib::exception_handlers_invoke(); + } + } +} + + +} // namespace Widget +} // namespace UI +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/dock-item.h b/src/ui/widget/dock-item.h new file mode 100644 index 000000000..38d13a579 --- /dev/null +++ b/src/ui/widget/dock-item.h @@ -0,0 +1,165 @@ +/** + * \brief A custom wrapper around gdl-dock-item + * + * Author: + * Gustav Broberg + * + * 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 +#include +#include +#include +#include + +#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 signal_show(); + Glib::SignalProxy0 signal_hide(); + Glib::SignalProxy1 signal_delete_event(); + Glib::SignalProxy1 signal_response(); + Glib::SignalProxy0 signal_drag_begin(); + Glib::SignalProxy1 signal_drag_end(); + + sigc::signal 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 _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 _signal_state_changed; + + DockItem(); +}; + +} // namespace Widget +} // namespace UI +} // namespace Inkscape + +#endif // INKSCAPE_UI_WIGET_DOCK_ITEM_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/widget/dock.cpp b/src/ui/widget/dock.cpp new file mode 100644 index 000000000..828b70451 --- /dev/null +++ b/src/ui/widget/dock.cpp @@ -0,0 +1,235 @@ +/** + * \brief A desktop dock pane to dock dialogs. + * + * Author: + * Gustav Broberg + * + * 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(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(parent) : 0); +} + + +Gtk::Paned * +Dock::getPaned() +{ + return _paned; +} + + +bool +Dock::isEmpty() const +{ + std::list::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_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 +Dock::signal_layout_changed() +{ + return Glib::SignalProxy0(Glib::wrap(GTK_WIDGET(_gdl_dock)), + &_signal_layout_changed_proxy); +} + +void +Dock::_onLayoutChanged() +{ + if (isEmpty()) { + + if (hasIconifiedItems()) + _scrolled_window->set_size_request(_default_dock_bar_width); + else + _scrolled_window->set_size_request(_default_empty_width); + + getParentPaned()->set_position(INT_MAX); + } else { + _scrolled_window->set_size_request(-1); + } +} + + +const Glib::SignalProxyInfo +Dock::_signal_layout_changed_proxy = +{ + "layout-changed", + (GCallback) &Glib::SignalProxyNormal::slot0_void_callback, + (GCallback) &Glib::SignalProxyNormal::slot0_void_callback +}; + + +} // namespace Widget +} // namespace UI +} // namespace Inkscape + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 diff --git a/src/ui/widget/dock.h b/src/ui/widget/dock.h new file mode 100644 index 000000000..be0c4ef1d --- /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 + * + * 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 +#include +#include + +#include + +#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 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 _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 5f7b51a03..8d2d4e69f 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -107,7 +107,6 @@ sp_action_get_title(SPAction const *action) } // end of sp_action_get_title() - namespace Inkscape { /// \todo !!!FIXME:: kill this, use DialogManager instead!!! @@ -115,7 +114,12 @@ namespace Inkscape { class PanelDialog : public Inkscape::UI::Dialog::Dialog { public: - PanelDialog(char const *prefs_path, int const verb_num) : Dialog(prefs_path, verb_num) {} + PanelDialog(char const *prefs_path, int const verb_num) : + Dialog( + (prefs_get_int_attribute_limited ("options.dialogtype", "value", UI::Dialog::DOCK, 0, 1) == UI::Dialog::FLOATING ? + &UI::Dialog::Behavior::FloatingBehavior::create : + &UI::Dialog::Behavior::DockBehavior::create), + prefs_path, verb_num) {} /* virtual Glib::ustring getName() const {return "foo";} virtual Glib::ustring getDesc() const {return "bar";} @@ -146,6 +150,7 @@ static void show_panel( Inkscape::UI::Widget::Panel &panel, char const *prefs_pa } } + /** \brief A class to encompass all of the verbs which deal with file operations. */ class FileVerb : public Verb { @@ -1001,6 +1006,8 @@ SelectionVerb::perform(SPAction *action, void *data, void *pdata) if (!dt) return; + g_assert(dt->_dlg_mgr != NULL); + switch (reinterpret_cast(data)) { case SP_VERB_SELECTION_TO_FRONT: sp_selection_raise_to_top(); @@ -1088,6 +1095,7 @@ SelectionVerb::perform(SPAction *action, void *data, void *pdata) sp_selected_path_reverse(); break; case SP_VERB_SELECTION_TRACE: + inkscape_dialogs_unhide(); dt->_dlg_mgr->showDialog("Trace"); break; case SP_VERB_SELECTION_CREATE_BITMAP: @@ -1101,6 +1109,7 @@ SelectionVerb::perform(SPAction *action, void *data, void *pdata) sp_selected_path_break_apart(); break; case SP_VERB_SELECTION_GRIDTILE: + inkscape_dialogs_unhide(); dt->_dlg_mgr->showDialog("TileDialog"); break; default: @@ -1652,7 +1661,8 @@ ZoomVerb::perform(SPAction *action, void *data, void *pdata) dt->displayModeToggle(); break; case SP_VERB_VIEW_ICON_PREVIEW: - show_panel( Inkscape::UI::Dialogs::IconPreviewPanel::getInstance(), "dialogs.iconpreview", SP_VERB_VIEW_ICON_PREVIEW ); + inkscape_dialogs_unhide(); + dt->_dlg_mgr->showDialog("IconPreviewPanel"); break; default: break; @@ -1688,12 +1698,12 @@ DialogVerb::perform(SPAction *action, void *data, void *pdata) dt->_dlg_mgr->showDialog("DocumentProperties"); break; case SP_VERB_DIALOG_FILL_STROKE: - sp_object_properties_dialog(); - // dt->_dlg_mgr->showDialog("FillAndStroke"); + // sp_object_properties_dialog(); + dt->_dlg_mgr->showDialog("FillAndStroke"); break; case SP_VERB_DIALOG_SWATCHES: show_panel( Inkscape::UI::Dialogs::SwatchesPanel::getInstance(), "dialogs.swatches", SP_VERB_DIALOG_SWATCHES); - break; + break; case SP_VERB_DIALOG_TRANSFORM: dt->_dlg_mgr->showDialog("Transformation"); break; @@ -1743,7 +1753,7 @@ DialogVerb::perform(SPAction *action, void *data, void *pdata) dt->_dlg_mgr->showDialog("ExtensionEditor"); break; case SP_VERB_DIALOG_LAYERS: - show_panel( Inkscape::UI::Dialogs::LayersPanel::getInstance(), "dialogs.layers", SP_VERB_DIALOG_LAYERS ); + dt->_dlg_mgr->showDialog("LayersPanel"); break; case SP_VERB_DIALOG_LIVE_PATH_EFFECT: dt->_dlg_mgr->showDialog("LivePathEffect"); @@ -1788,6 +1798,7 @@ HelpVerb::perform(SPAction *action, void *data, void *pdata) */ case SP_VERB_HELP_MEMORY: + inkscape_dialogs_unhide(); dt->_dlg_mgr->showDialog("Memory"); break; default: diff --git a/src/widgets/desktop-widget.cpp b/src/widgets/desktop-widget.cpp index 4b744e3df..f33950f70 100644 --- a/src/widgets/desktop-widget.cpp +++ b/src/widgets/desktop-widget.cpp @@ -48,6 +48,8 @@ #include "widgets/spw-utilities.h" #include "widgets/spinbutton-events.h" #include "widgets/layer-selector.h" +#include "ui/dialog/dialog-manager.h" +#include "ui/widget/dock.h" #include "ui/widget/selected-style.h" #include "sp-item.h" #include "dialogs/swatches.h" @@ -174,6 +176,7 @@ sp_desktop_widget_init (SPDesktopWidget *dtw) { GtkWidget *widget; GtkWidget *tbl; + GtkWidget *canvas_tbl; GtkWidget *hbox; GtkWidget *eventbox; @@ -221,9 +224,11 @@ sp_desktop_widget_init (SPDesktopWidget *dtw) dtw->tool_toolbox = sp_tool_toolbox_new (); gtk_box_pack_start (GTK_BOX (hbox), dtw->tool_toolbox, FALSE, TRUE, 0); - tbl = gtk_table_new (4, 3, FALSE); + tbl = gtk_table_new (2, 3, FALSE); gtk_box_pack_start (GTK_BOX (hbox), tbl, TRUE, TRUE, 1); + canvas_tbl = gtk_table_new (3, 3, FALSE); + /* Horizontal ruler */ eventbox = gtk_event_box_new (); dtw->hruler = sp_hruler_new (); @@ -231,7 +236,7 @@ sp_desktop_widget_init (SPDesktopWidget *dtw) sp_ruler_set_metric (GTK_RULER (dtw->hruler), SP_PT); gtk_tooltips_set_tip (dtw->tt, dtw->hruler_box, gettext(sp_unit_get_plural (&sp_unit_get_by_id(SP_UNIT_PT))), NULL); gtk_container_add (GTK_CONTAINER (eventbox), dtw->hruler); - gtk_table_attach (GTK_TABLE (tbl), eventbox, 1, 2, 0, 1, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(GTK_FILL), widget->style->xthickness, 0); + gtk_table_attach (GTK_TABLE (canvas_tbl), eventbox, 1, 2, 0, 1, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(GTK_FILL), widget->style->xthickness, 0); g_signal_connect (G_OBJECT (eventbox), "button_press_event", G_CALLBACK (sp_dt_hruler_event), dtw); g_signal_connect (G_OBJECT (eventbox), "button_release_event", G_CALLBACK (sp_dt_hruler_event), dtw); g_signal_connect (G_OBJECT (eventbox), "motion_notify_event", G_CALLBACK (sp_dt_hruler_event), dtw); @@ -243,7 +248,7 @@ sp_desktop_widget_init (SPDesktopWidget *dtw) sp_ruler_set_metric (GTK_RULER (dtw->vruler), SP_PT); gtk_tooltips_set_tip (dtw->tt, dtw->vruler_box, gettext(sp_unit_get_plural (&sp_unit_get_by_id(SP_UNIT_PT))), NULL); gtk_container_add (GTK_CONTAINER (eventbox), GTK_WIDGET (dtw->vruler)); - gtk_table_attach (GTK_TABLE (tbl), eventbox, 0, 1, 1, 2, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(GTK_FILL), 0, widget->style->ythickness); + gtk_table_attach (GTK_TABLE (canvas_tbl), eventbox, 0, 1, 1, 2, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(GTK_FILL), 0, widget->style->ythickness); g_signal_connect (G_OBJECT (eventbox), "button_press_event", G_CALLBACK (sp_dt_vruler_event), dtw); g_signal_connect (G_OBJECT (eventbox), "button_release_event", G_CALLBACK (sp_dt_vruler_event), dtw); g_signal_connect (G_OBJECT (eventbox), "motion_notify_event", G_CALLBACK (sp_dt_vruler_event), dtw); @@ -251,7 +256,8 @@ sp_desktop_widget_init (SPDesktopWidget *dtw) /* Horizontal scrollbar */ dtw->hadj = (GtkAdjustment *) gtk_adjustment_new (0.0, -4000.0, 4000.0, 10.0, 100.0, 4.0); dtw->hscrollbar = gtk_hscrollbar_new (GTK_ADJUSTMENT (dtw->hadj)); - gtk_table_attach (GTK_TABLE (tbl), dtw->hscrollbar, 1, 2, 2, 3, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_FILL), 0, 0); + gtk_table_attach (GTK_TABLE (canvas_tbl), dtw->hscrollbar, 1, 2, 2, 3, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(GTK_SHRINK), 0, 0); + /* Vertical scrollbar and the sticky zoom button */ dtw->vscrollbar_box = gtk_vbox_new (FALSE, 0); dtw->sticky_zoom = sp_button_new_from_data ( Inkscape::ICON_SIZE_DECORATION, @@ -265,8 +271,8 @@ sp_desktop_widget_init (SPDesktopWidget *dtw) dtw->vadj = (GtkAdjustment *) gtk_adjustment_new (0.0, -4000.0, 4000.0, 10.0, 100.0, 4.0); dtw->vscrollbar = gtk_vscrollbar_new (GTK_ADJUSTMENT (dtw->vadj)); gtk_box_pack_start (GTK_BOX (dtw->vscrollbar_box), dtw->vscrollbar, TRUE, TRUE, 0); - gtk_table_attach (GTK_TABLE (tbl), dtw->vscrollbar_box, 2, 3, 0, 2, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0); - + gtk_table_attach (GTK_TABLE (canvas_tbl), dtw->vscrollbar_box, 2, 3, 0, 2, (GtkAttachOptions)(GTK_SHRINK), (GtkAttachOptions)(GTK_FILL), 0, 0); + /* Canvas */ dtw->canvas = SP_CANVAS (sp_canvas_new_aa ()); GTK_WIDGET_SET_FLAGS (GTK_WIDGET (dtw->canvas), GTK_CAN_FOCUS); @@ -275,7 +281,27 @@ sp_desktop_widget_init (SPDesktopWidget *dtw) gtk_widget_set_style (GTK_WIDGET (dtw->canvas), style); gtk_widget_set_extension_events(GTK_WIDGET (dtw->canvas) , GDK_EXTENSION_EVENTS_ALL); g_signal_connect (G_OBJECT (dtw->canvas), "event", G_CALLBACK (sp_desktop_widget_event), dtw); - gtk_table_attach (GTK_TABLE (tbl), GTK_WIDGET (dtw->canvas), 1, 2, 1, 2, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0); + gtk_table_attach (GTK_TABLE (canvas_tbl), GTK_WIDGET(dtw->canvas), 1, 2, 1, 2, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), 0, 0); + + /* Dock */ + bool create_dock = + prefs_get_int_attribute_limited ("options.dialogtype", "value", Inkscape::UI::Dialog::FLOATING, 0, 1) == + Inkscape::UI::Dialog::DOCK; + + if (create_dock) + { + dtw->dock = new Inkscape::UI::Widget::Dock(); + + Gtk::HPaned *paned = new Gtk::HPaned(); + paned->pack1(*Glib::wrap(canvas_tbl)); + paned->pack2(dtw->dock->getWidget(), Gtk::FILL); + gtk_table_attach (GTK_TABLE (tbl), GTK_WIDGET (paned->gobj()), 1, 2, 1, 2, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), + (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0); + + } else { + gtk_table_attach (GTK_TABLE (tbl), GTK_WIDGET (canvas_tbl), 1, 2, 1, 2, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), + (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0); + } dtw->selected_style = new Inkscape::UI::Widget::SelectedStyle(true); GtkHBox *ss_ = dtw->selected_style->gobj(); @@ -421,6 +447,12 @@ SPDesktopWidget::updateTitle(gchar const* uri) } } +Inkscape::UI::Widget::Dock* +SPDesktopWidget::getDock() +{ + return dock; +} + /** * Callback to allocate space for desktop widget. */ diff --git a/src/widgets/desktop-widget.h b/src/widgets/desktop-widget.h index bb9d7ef86..18348e594 100644 --- a/src/widgets/desktop-widget.h +++ b/src/widgets/desktop-widget.h @@ -90,6 +90,8 @@ struct SPDesktopWidget { GtkWidget *zoom_status; gulong zoom_update; + Inkscape::UI::Widget::Dock *dock; + Inkscape::UI::Widget::SelectedStyle *selected_style; gint coord_status_id, select_status_id; @@ -182,6 +184,8 @@ struct SPDesktopWidget { { _dtw->setMessage (type, msg); } virtual bool warnDialog (gchar* text) { return _dtw->warnDialog (text); } + virtual Inkscape::UI::Widget::Dock* getDock () + { return _dtw->getDock(); } }; WidgetStub *stub; @@ -207,8 +211,10 @@ struct SPDesktopWidget { void enableInteraction(); void disableInteraction(); void updateTitle(gchar const *uri); - - bool onFocusInEvent(GdkEventFocus*); + bool onFocusInEvent(GdkEventFocus*); + + Inkscape::UI::Widget::Dock* getDock(); + }; /// The SPDesktopWidget vtable -- 2.30.2