niepce r2 - in trunk: . art data data/icons data/themes doc doc/doxygen m4 po src src/db src/ext src/ext/libgdl src/framework src/framework/widgets src/library src/libraryclient src/main src/modules src/modules/darkroom src/ncr src/niepce src/ui src/ui/thumb-view src/utils src/utils/db src/utils/db/sqlite



Author: hub
Date: Wed Dec 10 19:46:36 2008
New Revision: 2
URL: http://svn.gnome.org/viewvc/niepce?rev=2&view=rev

Log:
Merge /home/hub/tmp/niepce.import

Added:
   trunk/.gitignore
   trunk/AUTHORS
   trunk/COPYING
   trunk/ChangeLog
   trunk/INSTALL
   trunk/Makefile.am
   trunk/NEWS
   trunk/README
   trunk/art/
   trunk/art/Makefile.am
   trunk/art/format-emblems.svg
   trunk/autogen.sh   (contents, props changed)
   trunk/configure.ac
   trunk/data/
   trunk/data/Makefile.am
   trunk/data/icons/
   trunk/data/icons/Makefile.am
   trunk/data/icons/niepce-img-fmt.png
   trunk/data/icons/niepce-jpg-fmt.png
   trunk/data/icons/niepce-png-fmt.png
   trunk/data/icons/niepce-raw-fmt.png
   trunk/data/icons/niepce-rawjpeg-fmt.png
   trunk/data/icons/niepce-rotate-left.png
   trunk/data/icons/niepce-rotate-right.png
   trunk/data/icons/niepce-set-star.png
   trunk/data/icons/niepce-tiff-fmt.png
   trunk/data/icons/niepce-unknown-fmt.png
   trunk/data/icons/niepce-unset-star.png
   trunk/data/icons/niepce-video-fmt.png
   trunk/data/themes/
   trunk/data/themes/Makefile.am
   trunk/data/themes/README
   trunk/data/themes/niepce-dark.gtkrc
   trunk/doc/
   trunk/doc/Doxyfile.in
   trunk/doc/Makefile.am
   trunk/doc/database.txt
   trunk/doc/doxygen/
   trunk/doc/doxygen/Makefile.am
   trunk/doc/gconf.txt
   trunk/doc/mainwindow-ui.svg
   trunk/doc/xmp.txt
   trunk/m4/
   trunk/m4/boost.m4
   trunk/po/
   trunk/po/ChangeLog
   trunk/po/POTFILES.in
   trunk/po/fr.po
   trunk/po/niepce.pot
   trunk/po/ru.po
   trunk/src/
   trunk/src/Makefile.am
   trunk/src/db/
   trunk/src/db/Makefile.am
   trunk/src/db/keyword.cpp
   trunk/src/db/keyword.h
   trunk/src/db/libfile.cpp
   trunk/src/db/libfile.h
   trunk/src/db/libfolder.h
   trunk/src/db/libmetadata.cpp
   trunk/src/db/libmetadata.h
   trunk/src/db/library.cpp
   trunk/src/db/library.h
   trunk/src/db/metadata.h
   trunk/src/db/storage.cpp
   trunk/src/db/storage.h
   trunk/src/db/test_library.cpp
   trunk/src/ext/
   trunk/src/ext/Makefile.am
   trunk/src/ext/README
   trunk/src/ext/libgdl/
   trunk/src/ext/libgdl/CMakeLists.txt
   trunk/src/ext/libgdl/Makefile.am
   trunk/src/ext/libgdl/Makefile_insert
   trunk/src/ext/libgdl/README.gdl-dock
   trunk/src/ext/libgdl/gdl-dock-bar.c
   trunk/src/ext/libgdl/gdl-dock-bar.h
   trunk/src/ext/libgdl/gdl-dock-item-grip.c
   trunk/src/ext/libgdl/gdl-dock-item-grip.h
   trunk/src/ext/libgdl/gdl-dock-item.c
   trunk/src/ext/libgdl/gdl-dock-item.h
   trunk/src/ext/libgdl/gdl-dock-master.c
   trunk/src/ext/libgdl/gdl-dock-master.h
   trunk/src/ext/libgdl/gdl-dock-notebook.c
   trunk/src/ext/libgdl/gdl-dock-notebook.h
   trunk/src/ext/libgdl/gdl-dock-object.c
   trunk/src/ext/libgdl/gdl-dock-object.h
   trunk/src/ext/libgdl/gdl-dock-paned.c
   trunk/src/ext/libgdl/gdl-dock-paned.h
   trunk/src/ext/libgdl/gdl-dock-placeholder.c
   trunk/src/ext/libgdl/gdl-dock-placeholder.h
   trunk/src/ext/libgdl/gdl-dock-tablabel.c
   trunk/src/ext/libgdl/gdl-dock-tablabel.h
   trunk/src/ext/libgdl/gdl-dock.c
   trunk/src/ext/libgdl/gdl-dock.h
   trunk/src/ext/libgdl/gdl-i18n.c
   trunk/src/ext/libgdl/gdl-i18n.h
   trunk/src/ext/libgdl/gdl-stock-icons.h
   trunk/src/ext/libgdl/gdl-stock.c
   trunk/src/ext/libgdl/gdl-stock.h
   trunk/src/ext/libgdl/gdl-switcher.c
   trunk/src/ext/libgdl/gdl-switcher.h
   trunk/src/ext/libgdl/gdl-tools.h
   trunk/src/ext/libgdl/gdl-win32.c
   trunk/src/ext/libgdl/gdl-win32.h
   trunk/src/ext/libgdl/libgdl.h
   trunk/src/ext/libgdl/libgdlmarshal.c
   trunk/src/ext/libgdl/libgdlmarshal.h
   trunk/src/ext/libgdl/libgdlmarshal.list
   trunk/src/ext/libgdl/libgdltypebuiltins.c
   trunk/src/ext/libgdl/libgdltypebuiltins.h
   trunk/src/ext/libgdl/makefile.in
   trunk/src/framework/
   trunk/src/framework/Makefile.am
   trunk/src/framework/application.cpp
   trunk/src/framework/application.h
   trunk/src/framework/command.h
   trunk/src/framework/configdatabinder.cpp
   trunk/src/framework/configdatabinder.h
   trunk/src/framework/configuration.cpp
   trunk/src/framework/configuration.h
   trunk/src/framework/controller.cpp
   trunk/src/framework/controller.h
   trunk/src/framework/dockable.cpp
   trunk/src/framework/dockable.h
   trunk/src/framework/frame.cpp
   trunk/src/framework/frame.h
   trunk/src/framework/gconf_proxy_header.h
   trunk/src/framework/gdkutils.cpp
   trunk/src/framework/gdkutils.h
   trunk/src/framework/goocanvas_proxy_header.h
   trunk/src/framework/imageloader.cpp
   trunk/src/framework/imageloader.h
   trunk/src/framework/metadatawidget.cpp
   trunk/src/framework/metadatawidget.h
   trunk/src/framework/mimetype.cpp
   trunk/src/framework/mimetype.h
   trunk/src/framework/notification.h
   trunk/src/framework/notificationcenter.cpp
   trunk/src/framework/notificationcenter.h
   trunk/src/framework/undo.cpp
   trunk/src/framework/undo.h
   trunk/src/framework/widgets/
   trunk/src/framework/widgets/dock-item.cpp
   trunk/src/framework/widgets/dock-item.h
   trunk/src/framework/widgets/dock.cpp
   trunk/src/framework/widgets/dock.h
   trunk/src/framework/widgets/editablehscale.cpp
   trunk/src/framework/widgets/editablehscale.h
   trunk/src/framework/widgets/toolboxitemwidget.cpp
   trunk/src/framework/widgets/toolboxitemwidget.h
   trunk/src/library/
   trunk/src/library/Makefile.am
   trunk/src/library/clienttypes.h
   trunk/src/library/commands.cpp
   trunk/src/library/commands.h
   trunk/src/library/op.cpp
   trunk/src/library/op.h
   trunk/src/library/opqueue.h
   trunk/src/library/test_opqueue.cpp
   trunk/src/library/thumbnailcache.cpp
   trunk/src/library/thumbnailcache.h
   trunk/src/library/thumbnailnotification.h
   trunk/src/libraryclient/
   trunk/src/libraryclient/Makefile.am
   trunk/src/libraryclient/clientimpl.cpp
   trunk/src/libraryclient/clientimpl.h
   trunk/src/libraryclient/libraryclient.cpp
   trunk/src/libraryclient/libraryclient.h
   trunk/src/libraryclient/locallibraryserver.cpp
   trunk/src/libraryclient/locallibraryserver.h
   trunk/src/libraryclient/test_worker.cpp
   trunk/src/main/
   trunk/src/main/Makefile.am
   trunk/src/main/main.cpp
   trunk/src/modules/
   trunk/src/modules/Makefile.am
   trunk/src/modules/darkroom/
   trunk/src/modules/darkroom/Makefile.am
   trunk/src/modules/darkroom/darkroommodule.cpp
   trunk/src/modules/darkroom/darkroommodule.h
   trunk/src/modules/darkroom/dritemwidget.cpp
   trunk/src/modules/darkroom/dritemwidget.h
   trunk/src/modules/darkroom/imagecanvas.cpp
   trunk/src/modules/darkroom/imagecanvas.h
   trunk/src/modules/darkroom/toolboxcontroller.cpp
   trunk/src/modules/darkroom/toolboxcontroller.h
   trunk/src/ncr/
   trunk/src/ncr/Makefile.am
   trunk/src/ncr/image.cpp
   trunk/src/ncr/image.h
   trunk/src/ncr/ncr.cpp
   trunk/src/ncr/ncr.h
   trunk/src/niepce/
   trunk/src/niepce/Makefile.am
   trunk/src/niepce/notifications.h
   trunk/src/niepce/stock.cpp
   trunk/src/niepce/stock.h
   trunk/src/niepce/xmp.cpp
   trunk/src/niepce/xmp.h
   trunk/src/ui/
   trunk/src/ui/Makefile.am
   trunk/src/ui/filmstripcontroller.cpp
   trunk/src/ui/filmstripcontroller.h
   trunk/src/ui/imageliststore.cpp
   trunk/src/ui/imageliststore.h
   trunk/src/ui/importdialog.cpp
   trunk/src/ui/importdialog.glade
   trunk/src/ui/importdialog.h
   trunk/src/ui/librarycellrenderer.cpp
   trunk/src/ui/librarycellrenderer.h
   trunk/src/ui/librarymainview.cpp
   trunk/src/ui/librarymainview.h
   trunk/src/ui/librarymainviewcontroller.cpp
   trunk/src/ui/librarymainviewcontroller.h
   trunk/src/ui/metadatapanecontroller.cpp
   trunk/src/ui/metadatapanecontroller.h
   trunk/src/ui/niepceapplication.cpp
   trunk/src/ui/niepceapplication.h
   trunk/src/ui/niepcewindow.cpp
   trunk/src/ui/niepcewindow.h
   trunk/src/ui/preferences.glade
   trunk/src/ui/selectioncontroller.cpp
   trunk/src/ui/selectioncontroller.h
   trunk/src/ui/thumb-view/
   trunk/src/ui/thumb-view/Makefile.am
   trunk/src/ui/thumb-view/eog-thumb-nav.cpp
   trunk/src/ui/thumb-view/eog-thumb-nav.h
   trunk/src/ui/thumb-view/eog-thumb-view.cpp
   trunk/src/ui/thumb-view/eog-thumb-view.h
   trunk/src/ui/workspacecontroller.cpp
   trunk/src/ui/workspacecontroller.h
   trunk/src/utils/
   trunk/src/utils/Makefile.am
   trunk/src/utils/autoflag.h
   trunk/src/utils/boost.h
   trunk/src/utils/buffer.h
   trunk/src/utils/databinder.cpp
   trunk/src/utils/databinder.h
   trunk/src/utils/db/
   trunk/src/utils/db/Makefile.am
   trunk/src/utils/db/iconnectiondriver.h
   trunk/src/utils/db/iconnectionmanagerdriver.h
   trunk/src/utils/db/insertstatement.cpp
   trunk/src/utils/db/insertstatement.h
   trunk/src/utils/db/sqlite/
   trunk/src/utils/db/sqlite/Makefile.am
   trunk/src/utils/db/sqlite/sqlitecnxdrv.cpp
   trunk/src/utils/db/sqlite/sqlitecnxdrv.h
   trunk/src/utils/db/sqlite/sqlitecnxmgrdrv.cpp
   trunk/src/utils/db/sqlite/sqlitecnxmgrdrv.h
   trunk/src/utils/db/sqlstatement.cpp
   trunk/src/utils/db/sqlstatement.h
   trunk/src/utils/db/test_db.cpp
   trunk/src/utils/db/test_db2.cpp
   trunk/src/utils/db/test_db3.cpp
   trunk/src/utils/db/test_db4.cpp
   trunk/src/utils/debug.cpp
   trunk/src/utils/debug.h
   trunk/src/utils/exception.cpp
   trunk/src/utils/exception.h
   trunk/src/utils/exempi.cpp
   trunk/src/utils/exempi.h
   trunk/src/utils/files.cpp
   trunk/src/utils/files.h
   trunk/src/utils/fsutils.cpp
   trunk/src/utils/fsutils.h
   trunk/src/utils/geometry.cpp
   trunk/src/utils/geometry.h
   trunk/src/utils/logstreamutils.h
   trunk/src/utils/moniker.cpp
   trunk/src/utils/moniker.h
   trunk/src/utils/mtqueue.h
   trunk/src/utils/stringutils.h
   trunk/src/utils/test.xmp
   trunk/src/utils/test2.ufraw
   trunk/src/utils/testfiles.cpp
   trunk/src/utils/testgeometry.cpp
   trunk/src/utils/testmoniker.cpp
   trunk/src/utils/teststringutils.cpp
   trunk/src/utils/testufrawmeta.cpp
   trunk/src/utils/testxmp.cpp
   trunk/src/utils/thread.cpp
   trunk/src/utils/thread.h
   trunk/src/utils/ufrawmeta.cpp
   trunk/src/utils/ufrawmeta.h
   trunk/src/utils/worker.h

Added: trunk/.gitignore
==============================================================================
--- (empty file)
+++ trunk/.gitignore	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,36 @@
+.libs
+.deps
+Makefile
+Makefile.in
+config.guess
+autogen.err
+config.log
+config.sub
+config.status
+config.h
+config.h.in
+configure
+aclocal.m4
+missing
+libtool
+depcomp
+install-sh
+ltmain.sh
+stamp-*
+autom4te.cache
+intltool-*.in
+*.o
+*.lo
+*.la
+*~
+*.bak
+m4/intltool.m4
+po/Makefile.in.in
+src/utils/testmoniker
+src/db/test_db
+src/db/test_library
+src/library/test_opqueue
+src/library/test_worker
+src/main/niepce
+doc/Doxyfile
+doc/doxygen/html

Added: trunk/AUTHORS
==============================================================================
--- (empty file)
+++ trunk/AUTHORS	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,9 @@
+Authors:
+--------
+
+Lead development:
+Hubert Figuiere <hub figuiere net>
+
+Contributions:
+Pau RuÅlan Ferragut <paurullan bulma net>
+Alexandre Prokoudine <alexandre prokoudine gmail com>
\ No newline at end of file

Added: trunk/COPYING
==============================================================================
--- (empty file)
+++ trunk/COPYING	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.

Added: trunk/INSTALL
==============================================================================

Added: trunk/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/Makefile.am	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,15 @@
+ACLOCAL_AMFLAGS = -I m4
+
+EXTRA_DIST = COPYING AUTHORS README
+
+INTLTOOL_FILES = intltool-extract.in \
+                 intltool-merge.in \
+                 intltool-update.in
+
+DISTCLEANFILES = intltool-extract \
+                 intltool-merge \
+                 intltool-update \
+                 po/.intltool-merge-cache
+
+SUBDIRS = po data src
+DIST_SUBDIRS = po art data doc src
\ No newline at end of file

Added: trunk/NEWS
==============================================================================

Added: trunk/README
==============================================================================
--- (empty file)
+++ trunk/README	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,84 @@
+Niepce Digital
+==============
+
+This software is licensed under GPL version 3. See file 
+COPYING for details.
+Some files maybe under GPL version 2 or later.
+
+(c) 2006-2008 Hubert FiguiÃre
+Some portions are written by other people.
+
+libgdl has been copied from Inkscape. Still wondering why
+Gtk+ does not provide this functionality by default.
+
+What is this?
+-------------
+
+Niepce Digital is a digital photography software.
+
+It is currently work in progress.
+
+To build is you need the following:
+-----------------------------------
+
+A C++ compiler
+gtk+ 2.8
+cairo 1.1
+gtkmm 2.10
+gconfmm 2.6 (this will be removed)
+cairomm
+gnome-vfs >= 2.14
+sqlite3
+libxml > 2.5.0
+boost 1.33
+ -boost thread
+ -boost filesystem
+ -boost test (for the unit test)
+exempi 2.0.0
+libopenraw 0.0.4
+geglmm 0.0.17
+babl
+goocanvasmm 0.10.0
+
+Niepce is being developed on Linux. It should build and work on other
+UNIX systems.
+
+
+Working on Debian
+~~~~~~~~~~~~~~~~~
+In order to get Niepce working on Debian you should have to install:
+aptitude install libglademm-2.4-dev libgconfmm-2.6-dev libgnomevfs2-dev \
+> libexempi-dev libopenraw-dev libopenrawgnome-dev libgtkmm-2.4-dev \
+> libgoocanvas-dev
+
+Debian (nor Ubuntu) do not still pack geglmm nor libgoocanvasmm-dev. In order
+to get them visit to get the latest version:
+http://ftp.gnome.org/pub/gnome/sources/geglmm/
+http://ftp.gnome.org/pub/gnome/sources/goocanvasmm/
+
+Working on SUSE
+~~~~~~~~~~~~~~~
+To install the up to date dependencies, add the following repostitory to
+zypper using "zypper ar"
+
+For openSUSE 10.3
+http://download.opensuse.org/repositories/home:/hfiguiere/openSUSE_10.3/
+For openSUSE 11.0
+http://download.opensuse.org/repositories/home:/hfiguiere/openSUSE_11.0/
+
+You'll find an up to date set of dependencies to build Niepce Digital.
+
+Getting the code
+----------------
+
+Obviously you have it.
+
+To get the most recent code, it is hosted on Gna.org Subversion servers.
+All you need is to check the following page:
+http://gna.org/svn/?group=niepce
+
+
+Website
+-------
+
+http://home.gna.org/niepce/

Added: trunk/art/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/art/Makefile.am	Wed Dec 10 19:46:36 2008
@@ -0,0 +1 @@
+EXTRA_DIST = format-emblems.svg

Added: trunk/art/format-emblems.svg
==============================================================================
--- (empty file)
+++ trunk/art/format-emblems.svg	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,350 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="744.09448819"
+   height="1052.3622047"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   sodipodi:docbase="/home/hub/cvslocal/niepce/art"
+   sodipodi:docname="format-emblems.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective2522" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="2"
+     inkscape:cx="259.84493"
+     inkscape:cy="780.30115"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer2"
+     inkscape:window-width="1272"
+     inkscape:window-height="716"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     showgrid="false"
+     showguides="true"
+     inkscape:guide-bbox="true" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     style="display:inline">
+    <rect
+       style="fill:#333333;fill-opacity:1"
+       id="rect4125"
+       width="65"
+       height="30"
+       x="325.5"
+       y="25.362183"
+       ry="9.2857141"
+       rx="5.3061228" />
+    <text
+       xml:space="preserve"
+       style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+       x="334.69043"
+       y="89.782104"
+       id="text4127"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan4129"
+         x="334.69043"
+         y="89.782104"
+         style="font-size:20px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:DejaVu Sans">JPEG</tspan></text>
+    <g
+       id="g3169"
+       inkscape:export-filename="/home/hub/cvslocal/niepce/data/icons/niepce-png-fmt.png"
+       inkscape:export-xdpi="36"
+       inkscape:export-ydpi="36">
+      <rect
+         rx="5.3061228"
+         ry="9.2857141"
+         y="283.36218"
+         x="108.5"
+         height="30"
+         width="65"
+         id="rect2180"
+         style="fill:#333333;fill-opacity:1;display:inline" />
+      <text
+         sodipodi:linespacing="125%"
+         id="text2182"
+         y="304.7821"
+         x="118.69043"
+         style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline;font-family:Bitstream Vera Sans"
+         xml:space="preserve"><tspan
+           style="font-size:20px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;font-family:DejaVu Sans"
+           y="304.7821"
+           x="118.69043"
+           id="tspan2184"
+           sodipodi:role="line">PNG</tspan></text>
+    </g>
+    <g
+       id="g3164"
+       transform="translate(-8,0)"
+       inkscape:export-filename="/home/hub/cvslocal/niepce/data/icons/niepce-tiff-fmt.png"
+       inkscape:export-xdpi="36"
+       inkscape:export-ydpi="36">
+      <rect
+         rx="5.3061228"
+         ry="9.2857141"
+         y="332.36218"
+         x="115.5"
+         height="30"
+         width="65"
+         id="rect3158"
+         style="fill:#333333;fill-opacity:1;display:inline" />
+      <text
+         sodipodi:linespacing="125%"
+         id="text3160"
+         y="353.7821"
+         x="125.69043"
+         style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline;font-family:Bitstream Vera Sans"
+         xml:space="preserve"><tspan
+           style="font-size:20px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;font-family:DejaVu Sans"
+           y="353.7821"
+           x="125.69043"
+           id="tspan3162"
+           sodipodi:role="line">TIFF</tspan></text>
+    </g>
+    <g
+       id="g2656"
+       inkscape:export-filename="/home/paurullan/experiment/niepce/niepce/data/icons/niepce-unknown-fmt.png"
+       inkscape:export-xdpi="36"
+       inkscape:export-ydpi="36">
+      <rect
+         rx="5.3061228"
+         ry="9.2857141"
+         y="377.86218"
+         x="108.5"
+         height="30"
+         width="65"
+         id="rect2534"
+         style="fill:#333333;fill-opacity:1;display:inline" />
+      <text
+         sodipodi:linespacing="125%"
+         id="text2536"
+         y="399.2821"
+         x="118.69043"
+         style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline;font-family:Bitstream Vera Sans"
+         xml:space="preserve"><tspan
+           style="font-size:20px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;font-family:DejaVu Sans"
+           y="399.2821"
+           x="118.69043"
+           id="tspan2538"
+           sodipodi:role="line">????</tspan></text>
+    </g>
+    <flowRoot
+       xml:space="preserve"
+       id="flowRoot2543"
+       style="fill:black;stroke:none;stroke-opacity:1;stroke-width:1px;stroke-linejoin:miter;stroke-linecap:butt;fill-opacity:1;font-family:Bitstream Vera Sans;font-style:normal;font-weight:normal;font-size:40px"><flowRegion
+         id="flowRegion2545"><rect
+           id="rect2547"
+           width="141.5"
+           height="55.5"
+           x="68"
+           y="368.36218" /></flowRegion><flowPara
+         id="flowPara2549" /></flowRoot>  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer4"
+     inkscape:label="R+J"
+     style="display:inline">
+    <g
+       id="g3174">
+      <rect
+         inkscape:export-ydpi="36"
+         inkscape:export-xdpi="36"
+         inkscape:export-filename="/home/hub/cvslocal/niepce/art/niepce-rawjpeg-fmt.png"
+         rx="5.3061228"
+         ry="9.2857141"
+         y="229.36218"
+         x="109"
+         height="30"
+         width="65"
+         id="rect2178"
+         style="fill:#333333;fill-opacity:1;display:inline" />
+      <text
+         inkscape:export-ydpi="36"
+         inkscape:export-xdpi="36"
+         inkscape:export-filename="/home/hub/cvslocal/niepce/art/niepce-rawjpeg-fmt.png"
+         sodipodi:linespacing="100%"
+         id="text2168"
+         y="251.36218"
+         x="123"
+         style="font-size:20px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline;font-family:DejaVu Sans"
+         xml:space="preserve"><tspan
+           y="251.36218"
+           x="123"
+           id="tspan2176"
+           sodipodi:role="line">R+J</tspan></text>
+    </g>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="JPG"
+     style="display:inline">
+    <rect
+       style="fill:#333333;fill-opacity:1"
+       id="rect4123"
+       width="65"
+       height="30"
+       x="106.5"
+       y="126.36218"
+       ry="9.2857141"
+       rx="5.3061228"
+       inkscape:export-filename="/home/hub/cvslocal/niepce/art/niepce-jpg-fmt.png"
+       inkscape:export-xdpi="36"
+       inkscape:export-ydpi="36" />
+    <text
+       xml:space="preserve"
+       style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+       x="121"
+       y="149.36218"
+       id="text2160"
+       sodipodi:linespacing="125%"
+       inkscape:export-filename="/home/hub/cvslocal/niepce/art/niepce-jpg-fmt.png"
+       inkscape:export-xdpi="36"
+       inkscape:export-ydpi="36"><tspan
+         sodipodi:role="line"
+         id="tspan2162"
+         x="121"
+         y="149.36218"
+         style="font-size:20px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;font-family:DejaVu Sans">JPG</tspan></text>
+    <rect
+       style="fill:#333333;fill-opacity:1;display:inline"
+       id="rect2438"
+       width="65"
+       height="30"
+       x="107"
+       y="85.862183"
+       ry="9.2857141"
+       rx="5.3061228"
+       inkscape:export-filename="/home/paurullan/experiment/niepce/niepce/data/icons/niepce-img-fmt.png"
+       inkscape:export-xdpi="36"
+       inkscape:export-ydpi="36" />
+    <text
+       xml:space="preserve"
+       style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline;font-family:Bitstream Vera Sans"
+       x="121.5"
+       y="106.86218"
+       id="text2440"
+       sodipodi:linespacing="125%"
+       inkscape:export-filename="/home/paurullan/experiment/niepce/niepce/data/icons/niepce-img-fmt.png"
+       inkscape:export-xdpi="36"
+       inkscape:export-ydpi="36"><tspan
+         sodipodi:role="line"
+         id="tspan2442"
+         x="121.5"
+         y="106.86218"
+         style="font-size:20px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;font-family:DejaVu Sans">IMG</tspan></text>
+    <rect
+       style="fill:#333333;fill-opacity:1;display:inline"
+       id="rect2446"
+       width="65"
+       height="30"
+       x="190.5"
+       y="86.362183"
+       ry="9.2857141"
+       rx="5.3061228"
+       inkscape:export-filename="/home/paurullan/experiment/niepce/niepce/data/icons/niepce-video-fmt.png"
+       inkscape:export-xdpi="36"
+       inkscape:export-ydpi="36" />
+    <text
+       xml:space="preserve"
+       style="font-size:11.41997242px;font-style:normal;font-weight:normal;line-height:125%;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline;font-family:Bitstream Vera Sans"
+       x="198.13702"
+       y="105.6746"
+       id="text2448"
+       sodipodi:linespacing="125%"
+       inkscape:export-filename="/home/paurullan/experiment/niepce/niepce/data/icons/niepce-video-fmt.png"
+       inkscape:export-xdpi="36"
+       inkscape:export-ydpi="36"
+       transform="scale(0.9841956,1.0160582)"><tspan
+         sodipodi:role="line"
+         id="tspan2450"
+         x="198.13702"
+         y="105.6746"
+         style="font-size:19.03328705px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;font-family:DejaVu Sans">VIDEO </tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:40px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+       x="232.5"
+       y="155.36218"
+       id="text2452"><tspan
+         sodipodi:role="line"
+         id="tspan2454"
+         x="232.5"
+         y="155.36218"> </tspan></text>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer3"
+     inkscape:label="RAW"
+     style="display:inline">
+    <g
+       id="g3179">
+      <rect
+         inkscape:export-ydpi="36"
+         inkscape:export-xdpi="36"
+         inkscape:export-filename="/home/hub/cvslocal/niepce/art/niepce-raw-fmt.png"
+         rx="5.3061228"
+         ry="9.2857141"
+         y="178.36218"
+         x="109.5"
+         height="30"
+         width="65"
+         id="rect4121"
+         style="fill:#333333;fill-opacity:1;display:inline" />
+      <text
+         inkscape:export-ydpi="36"
+         inkscape:export-xdpi="36"
+         inkscape:export-filename="/home/hub/cvslocal/niepce/art/niepce-raw-fmt.png"
+         sodipodi:linespacing="125%"
+         id="text2164"
+         y="201.36218"
+         x="119"
+         style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline;font-family:Bitstream Vera Sans"
+         xml:space="preserve"><tspan
+           style="font-size:20px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;font-family:DejaVu Sans"
+           y="201.36218"
+           x="119"
+           id="tspan2166"
+           sodipodi:role="line">RAW</tspan></text>
+    </g>
+  </g>
+</svg>

Added: trunk/autogen.sh
==============================================================================
--- (empty file)
+++ trunk/autogen.sh	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+#
+# part of niepce
+#
+
+
+topsrcdir=`dirname $0`
+if test x$topsrcdir = x ; then
+        topsrcdir=.
+fi
+
+builddir=`pwd`
+
+AUTOCONF=autoconf
+if test -x /usr/bin/glibtool ; then
+    LIBTOOL=glibtool
+else
+    LIBTOOL=libtool
+fi
+if test -x /usr/bin/glibtoolize ; then
+    LIBTOOLIZE=glibtoolize
+else
+    LIBTOOLIZE=libtoolize
+fi
+AUTOMAKE=automake
+ACLOCAL=aclocal
+
+cd $topsrcdir
+
+rm -f autogen.err
+$ACLOCAL -I m4 >> autogen.err 2>&1
+
+intltoolize
+
+$AUTOMAKE --add-missing --copy --foreign 
+$LIBTOOLIZE --force
+autoheader --force
+$AUTOCONF
+
+cd $builddir
+
+if test -z "$NOCONFIGURE" ; then 
+	if test -z "$*"; then
+		echo "I am going to run ./configure with --enable-maintainer-mode"
+		echo "If you wish to pass any to it, please specify them on "
+		echo "the $0 command line."
+	fi
+	echo "Running configure..."
+	$topsrcdir/configure --enable-maintainer-mode "$@"
+fi
+

Added: trunk/configure.ac
==============================================================================
--- (empty file)
+++ trunk/configure.ac	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,179 @@
+AC_INIT(niepce, 0.0.1)
+AC_PREREQ(2.59)
+AC_CONFIG_SRCDIR(README)
+AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
+AM_MAINTAINER_MODE
+AC_CONFIG_HEADER(config.h)
+AC_CONFIG_MACRO_DIR(m4)
+
+m4_pattern_allow([^BOOST_])
+
+MAJOR_VERSION=0
+MINOR_VERSION=0
+MICRO_VERSION=1
+
+NIEPCE_VERSION="$MAJOR_VERSION.$MINOR_VERSION.$MICRO_VERSION"
+AC_SUBST(NIEPCE_VERSION)
+
+
+dnl all the library version.
+dnl if one is harcoded elsewhere, it is a bug
+LIBGTKMM_VERSION=2.10.0
+LIBGLADEMM_VERSION=2.6.0
+LIBGCONFMM_VERSION=2.6.0
+LIBGNOMEUI_VERSION=2.0.0
+LIBGOOCANVASMM_VERSION=0.6.0
+EXEMPI_VERSION=2.0.0
+SQLITE_VERSION=3.0
+GEGLMM_VERSION=0.0.17.1
+LIBOPENRAW_VERSION=0.0.5
+dnl need at least 2.5.0 because of xmlTextReader
+LIBXML2_VERSION=2.5.0
+BOOST_VERSION=1.33.1
+
+AC_PROG_CXX
+AC_GNU_SOURCE
+
+AC_ARG_ENABLE(debug,[  --enable-debug    Turn on debugging],[
+        case "${enableval}" in
+           yes) debug=true ;
+                DEBUG_CFLAGS="-DDEBUG -g" ;
+                OPTIMIZE_CFLAGS="" ;;
+            no) debug=false ;
+                DEBUG_CFLAGS="-DNDEBUG" ;;
+             *) AC_MSG_ERROR(bad value ${enableval} for --enable-debug) ;;
+        esac
+],[     debug=false
+        DEBUG_CFLAGS="-DNDEBUG"
+])
+AM_CONDITIONAL(DEBUG, test x$debug = xtrue)
+
+
+CPPFLAGS="$CPPFLAGS $DEBUG_CFLAGS -pedantic -Wall -Wcast-align -Wcast-qual -Wpointer-arith -Wreturn-type"
+CFLAGS="$CFLAGS $DEBUG_CFLAGS $OPTIMIZE_CFLAGS"
+CXXFLAGS="$CXXFLAGS $DEBUG_CFLAGS $OPTIMIZE_CFLAGS"
+dnl CFLAGS=""
+LDFLAGS="$LDFLAGS"
+
+
+dnl AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+
+AC_LANG_CPLUSPLUS
+AC_LANG_COMPILER_REQUIRE
+
+PKG_CHECK_MODULES(LIBGTKMM, [gtkmm-2.4 >= $LIBGTKMM_VERSION])
+PKG_CHECK_MODULES(LIBGLADEMM, [libglademm-2.4 >= $LIBGLADEMM_VERSION])
+dnl PKG_CHECK_MODULES(LIBGNOMEUI, [libgnomeui-2.0 >= $LIBGNOMEUI_VERSION])
+PKG_CHECK_MODULES(LIBGCONFMM, [gconfmm-2.6 >= $LIBGCONFMM_VERSION])
+PKG_CHECK_MODULES(GNOMEVFS, [gnome-vfs-2.0 >= 2.12])
+PKG_CHECK_EXISTS([gnome-vfs-2.0 >= 2.14], [HAVE_GNOME_VFS_2_14=1], [HAVE_GNOME_VFS_2_14=0])
+dnl AC_SUBST(GNOMEVFS_VER)
+PKG_CHECK_MODULES(SQLITE3, [sqlite3 >= $SQLITE_VERSION])
+PKG_CHECK_MODULES(EXEMPI, [exempi-2.0 >= $EXEMPI_VERSION])
+PKG_CHECK_MODULES(LIBXML2, [libxml-2.0 >= $LIBXML2_VERSION])
+PKG_CHECK_MODULES(BABL, babl)
+
+PKG_CHECK_MODULES(OPENRAW, libopenraw-gnome-1.0 >= $LIBOPENRAW_VERSION)
+AC_SUBST(OPENRAW_CFLAGS)
+AC_SUBST(OPENRAW_LIBS)
+
+dnl we want to use geglmm.
+dnl 0.0.17.1 because of bugs in 0.0.17
+PKG_CHECK_MODULES(GEGLMM, geglmm >= $GEGLMM_VERSION)
+AC_SUBST(GEGLMM_CFLAGS)
+AC_SUBST(GEGLMM_LIBS)
+
+PKG_CHECK_MODULES(GOOCANVASMM, [goocanvasmm-1.0 >= $LIBGOOCANVASMM_VERSION])
+
+
+if test $HAVE_GNOME_VFS_2_14 = 1
+then
+	NIEPCE_BUILD_CONFIG="$NIEPCE_BUILD_CONFIG Gnome-VFS>=2.14"
+else
+	NIEPCE_BUILD_CONFIG="$NIEPCE_BUILD_CONFIG Gnome-VFS=2.12"	
+fi
+
+AC_DEFINE_UNQUOTED([HAVE_GNOME_VFS_2_14], [$HAVE_GNOME_VFS_2_14], [If gnome-vfs is at least 2.14])
+
+BOOST_REQUIRE([$BOOST_VERSION])
+BOOST_BIND
+BOOST_CONVERSION
+BOOST_FILESYSTEM
+BOOST_FORMAT
+BOOST_FUNCTION
+BOOST_SMART_PTR
+BOOST_TEST([s])
+BOOST_THREADS
+BOOST_SIGNALS
+
+AC_LANG_PUSH(C++)
+if test "$GCC" = "yes"; then
+        NIEPCE_BUILD_CONFIG="$NIEPCE_BUILD_CONFIG gcc-options="
+        for option in -Wall -Wextra -Wsign-compare -Wpointer-arith \
+                      -Wchar-subscripts -Wwrite-strings -Wmissing-noreturn \
+                      -Wunused -Wpointer-arith -Wshadow ; do
+                SAVE_CXXFLAGS="$CXXFLAGS"
+                CXXFLAGS="$CXXFLAGS $option"
+		NIEPCE_BUILD_CONFIG="$NIEPCE_BUILD_CONFIG$option "
+                AC_MSG_CHECKING([whether gcc understands $option])
+                AC_TRY_COMPILE([], [],
+                        has_option=yes,
+                        has_option=no,)
+                if test $has_option = no; then
+                  CXXFLAGS="$SAVE_CXXFLAGS"
+                fi
+                AC_MSG_RESULT($has_option)
+                unset has_option
+                unset SAVE_CXXFLAGS
+        done
+        unset option
+fi
+AC_LANG_POP
+
+
+IT_PROG_INTLTOOL([0.35.0])
+
+GETTEXT_PACKAGE=niepce
+AC_SUBST(GETTEXT_PACKAGE)
+AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], ["$GETTEXT_PACKAGE"],
+                   [The domain to use with gettext])
+ALL_LINGUAS="fr ru"
+AM_GLIB_GNU_GETTEXT
+
+NIEPCE_LOCALEDIR=[${datadir}/locale]
+AC_SUBST(NIEPCE_LOCALEDIR)
+
+AC_DEFINE_UNQUOTED([NIEPCE_BUILD_CONFIG], ["$NIEPCE_BUILD_CONFIG"], [The string used to hardcode the build config.])
+
+AC_CONFIG_FILES([
+Makefile
+doc/Doxyfile
+doc/Makefile
+doc/doxygen/Makefile
+data/Makefile
+data/icons/Makefile
+data/themes/Makefile
+src/Makefile
+src/ext/Makefile
+src/ext/libgdl/Makefile
+src/niepce/Makefile
+src/utils/Makefile
+src/utils/db/Makefile
+src/utils/db/sqlite/Makefile
+src/db/Makefile
+src/library/Makefile
+src/libraryclient/Makefile
+src/framework/Makefile
+src/ncr/Makefile
+src/ui/Makefile
+src/ui/thumb-view/Makefile
+src/modules/Makefile
+src/modules/darkroom/Makefile
+src/main/Makefile
+po/Makefile.in
+po/Makefile
+])
+
+
+AC_OUTPUT

Added: trunk/data/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/data/Makefile.am	Wed Dec 10 19:46:36 2008
@@ -0,0 +1 @@
+SUBDIRS = icons themes

Added: trunk/data/icons/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/data/icons/Makefile.am	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,8 @@
+
+
+iconsdir = @datarootdir@/niepce/pixmaps/
+icons_DATA = niepce-jpg-fmt.png niepce-raw-fmt.png niepce-rawjpeg-fmt.png\
+	niepce-tiff-fmt.png niepce-png-fmt.png\
+	niepce-img-fmt.png niepce-unknown-fmt.png niepce-video-fmt.png\
+	niepce-set-star.png niepce-unset-star.png\
+	niepce-rotate-left.png niepce-rotate-right.png

Added: trunk/data/icons/niepce-img-fmt.png
==============================================================================
Binary files (empty file) and trunk/data/icons/niepce-img-fmt.png	Wed Dec 10 19:46:36 2008 differ

Added: trunk/data/icons/niepce-jpg-fmt.png
==============================================================================
Binary files (empty file) and trunk/data/icons/niepce-jpg-fmt.png	Wed Dec 10 19:46:36 2008 differ

Added: trunk/data/icons/niepce-png-fmt.png
==============================================================================
Binary files (empty file) and trunk/data/icons/niepce-png-fmt.png	Wed Dec 10 19:46:36 2008 differ

Added: trunk/data/icons/niepce-raw-fmt.png
==============================================================================
Binary files (empty file) and trunk/data/icons/niepce-raw-fmt.png	Wed Dec 10 19:46:36 2008 differ

Added: trunk/data/icons/niepce-rawjpeg-fmt.png
==============================================================================
Binary files (empty file) and trunk/data/icons/niepce-rawjpeg-fmt.png	Wed Dec 10 19:46:36 2008 differ

Added: trunk/data/icons/niepce-rotate-left.png
==============================================================================
Binary files (empty file) and trunk/data/icons/niepce-rotate-left.png	Wed Dec 10 19:46:36 2008 differ

Added: trunk/data/icons/niepce-rotate-right.png
==============================================================================
Binary files (empty file) and trunk/data/icons/niepce-rotate-right.png	Wed Dec 10 19:46:36 2008 differ

Added: trunk/data/icons/niepce-set-star.png
==============================================================================
Binary files (empty file) and trunk/data/icons/niepce-set-star.png	Wed Dec 10 19:46:36 2008 differ

Added: trunk/data/icons/niepce-tiff-fmt.png
==============================================================================
Binary files (empty file) and trunk/data/icons/niepce-tiff-fmt.png	Wed Dec 10 19:46:36 2008 differ

Added: trunk/data/icons/niepce-unknown-fmt.png
==============================================================================
Binary files (empty file) and trunk/data/icons/niepce-unknown-fmt.png	Wed Dec 10 19:46:36 2008 differ

Added: trunk/data/icons/niepce-unset-star.png
==============================================================================
Binary files (empty file) and trunk/data/icons/niepce-unset-star.png	Wed Dec 10 19:46:36 2008 differ

Added: trunk/data/icons/niepce-video-fmt.png
==============================================================================
Binary files (empty file) and trunk/data/icons/niepce-video-fmt.png	Wed Dec 10 19:46:36 2008 differ

Added: trunk/data/themes/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/data/themes/Makefile.am	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,7 @@
+
+
+EXTRA_DIST = README
+
+themesdir = @datadir@/niepce/themes/
+
+themes_DATA = niepce-dark.gtkrc
\ No newline at end of file

Added: trunk/data/themes/README
==============================================================================
--- (empty file)
+++ trunk/data/themes/README	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,2 @@
+niepce-dark.gtkrc comes from:
+http://rawstudio.org/svn/rawstudio/trunk/src/rawstudio.gtkrc

Added: trunk/data/themes/niepce-dark.gtkrc
==============================================================================
--- (empty file)
+++ trunk/data/themes/niepce-dark.gtkrc	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,38 @@
+style "rawstudio-style"
+{
+	bg[NORMAL] = { 0.4, 0.4, 0.4 } #done
+	bg[ACTIVE] = { 0.37, 0.37, 0.37 } #done
+	bg[PRELIGHT] = { 0.45, 0.45, 0.45 } #done
+	bg[SELECTED] = { 0.5, 0.5, 0.5 } #done
+	bg[INSENSITIVE] = { 0.4, 0.4, 0.4 } #done
+
+	fg[NORMAL] = { 0.7, 0.7, 0.7 } #done
+	fg[ACTIVE] = { 0.67, 0.67, 0.67 } #done
+	fg[PRELIGHT] = { 0.95, 0.95, 0.95 } #done
+	fg[SELECTED] = { 0.6, 0.6, 0.9 } #???
+	fg[INSENSITIVE] = { 0.4, 0.4, 0.4 } #crap
+
+	text[NORMAL] = { 0.7, 0.7, 0.7 } #done
+	text[ACTIVE] = { 0.67, 0.67, 0.67 } #done
+	text[PRELIGHT] = { 0.95, 0.95, 0.95 }
+	text[SELECTED] = { 0.9, 0.9, 0.9 } #done
+	text[INSENSITIVE] = { 0.4, 0.4, 0.4 } #crap
+
+	base[NORMAL] = { 0.5, 0.5, 0.5 } #done
+	base[ACTIVE] = { 0.55, 0.55, 0.55 } #done
+	base[PRELIGHT] = { 0.95, 0.5, 0.5 } #???
+	base[SELECTED] = { 0.6, 0.6, 0.6 } #done
+	base[INSENSITIVE] = { 0.4, 0.94, 0.4 } #???
+
+	GtkTreeView::even-row-color = "#666"
+	GtkTreeView::odd-row-color = "#555"
+
+	bg_pixmap[NORMAL] = "<none>"
+
+	engine "clearlooks" {
+	}
+}
+
+widget "*" style "rawstudio-style"
+class "*" style "rawstudio-style"
+widget_class "*" style "rawstudio-style"

Added: trunk/doc/Doxyfile.in
==============================================================================
--- (empty file)
+++ trunk/doc/Doxyfile.in	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,1239 @@
+# Doxyfile 1.4.5
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = niepce
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER         = 
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = ./doxygen
+
+# $(build_dir)/doc/doxygen
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
+# 4096 sub-directories (in 2 levels) under the output directory of each output 
+# format and will distribute the generated files over these directories. 
+# Enabling this option can be useful when feeding doxygen a huge amount of 
+# source files, where putting all generated files in the same directory would 
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, 
+# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, 
+# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, 
+# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, 
+# Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# This tag can be used to specify the encoding used in the generated output. 
+# The encoding is not always determined by the language that is chosen, 
+# but also whether or not the output is meant for Windows or non-Windows users. 
+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES 
+# forces the Windows encoding (this is the default for the Windows binary), 
+# whereas setting the tag to NO uses a Unix-style encoding (the default for 
+# all platforms other than Windows).
+
+USE_WINDOWS_ENCODING   = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc). 
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator 
+# that is used to form the text in various listings. Each string 
+# in this list, if found as the leading text of the brief description, will be 
+# stripped from the text and the result after processing the whole list, is 
+# used as the annotated text. Otherwise, the brief description is used as-is. 
+# If left blank, the following values are used ("$name" is automatically 
+# replaced with the name of the entity): "The $name class" "The $name widget" 
+# "The $name file" "is" "provides" "specifies" "contains" 
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       = 
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 
+# inherited members of a class in the documentation of that class as if those 
+# members were ordinary class members. Constructors, destructors and assignment 
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user-defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. The tag can be used to show relative paths in the file list. 
+# If left blank the directory from which doxygen is run is used as the 
+# path to strip.
+
+STRIP_FROM_PATH        = @top_srcdir@
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
+# the path mentioned in the documentation of a class, which tells 
+# the reader which header file to include in order to use a class. 
+# If left blank only the name of the header file containing the class 
+# definition is used. Otherwise one should specify the include paths that 
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    = 
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful is your file systems 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments will behave just like the Qt-style comments (thus requiring an 
+# explicit @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF      = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
+# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
+# comments) as a brief description. This used to be the default behaviour. 
+# The new default is to treat a multi-line C++ comment block as a detailed 
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen 
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member 
+# documentation.
+
+DETAILS_AT_TOP         = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 
+# a new page for each member. If set to NO, the documentation of a member will 
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user-defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                = 
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 
+# sources only. Doxygen will then generate output that is more tailored for C. 
+# For instance, some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java 
+# sources only. Doxygen will then generate output that is more tailored for Java. 
+# For instance, namespaces will be presented as packages, qualified scopes 
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to 
+# include (a tag file for) the STL sources as input, then you should 
+# set this tag to YES in order to let doxygen match functions declarations and 
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. 
+# func(std::string) {}). This also make the inheritance and collaboration 
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
+# the same type (for instance a group of public functions) to be put as a 
+# subgroup of that type (e.g. under the Public Functions section). Set it to 
+# NO to prevent subgrouping. Alternatively, this can be done per class using 
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local 
+# methods, which are defined in the implementation section but not in 
+# the interface are included in the documentation. 
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these classes will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
+# friend (class|struct|union) declarations. 
+# If set to NO (the default) these declarations will be included in the 
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
+# documentation blocks found inside the body of a function. 
+# If set to NO (the default) these blocks will be appended to the 
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower-case letters. If set to YES upper-case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put a list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
+# brief documentation of file, namespace and class members alphabetically 
+# by member name. If set to NO (the default) the members will appear in 
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
+# sorted by fully-qualified names, including namespaces. If set to 
+# NO (the default), the class list will be sorted only by class name, 
+# not including the namespace part. 
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the 
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting 
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       = 
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consists of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories 
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
+# in the documentation. The default is YES.
+
+SHOW_DIRECTORIES       = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that 
+# doxygen should invoke to get the current version for each file (typically from the 
+# version control system). Doxygen will invoke the program by executing (via 
+# popen()) the command <command> <input-file>, where <command> is the value of 
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file 
+# provided by doxygen. Whatever the program writes to standard output 
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
+# potential errors in the documentation, such as not documenting some 
+# parameters in a documented function, or documenting parameters that 
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for 
+# functions that are documented, but have no documentation for their parameters 
+# or return value. If set to NO (the default) doxygen will only warn about 
+# wrong or incomplete parameter documentation, but not about the absence of 
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text. Optionally the format may contain 
+# $version, which will be replaced by the version of the file (if it could 
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  = @top_srcdir@/src
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx 
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py
+
+FILE_PATTERNS          =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                = 
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or 
+# directories that are symbolic links (a Unix filesystem feature) are excluded 
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories. Note that the wildcards are matched 
+# against the file with absolute path, so to exclude all test directories 
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       = 
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           = 
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = 
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
+# ignored.
+
+INPUT_FILTER           = 
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
+# basis.  Doxygen will compare the file name with each pattern and apply the 
+# filter if there is a match.  The filters are a list of the form: 
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 
+# is applied to all files.
+
+FILTER_PATTERNS        = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources. 
+# Note: To get rid of all source code in the generated output, make sure also 
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default) 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code 
+# will point to the HTML generated by the htags(1) tool instead of doxygen 
+# built-in source browser. The htags tool is part of GNU's global source 
+# tagging system (see http://www.gnu.org/software/global/global.html). You 
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER            = 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            = 
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet. Note that doxygen will try to copy 
+# the style sheet file to the HTML output directory, so don't put your own 
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        = 
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output directory.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
+# include the index chapters (such as File Index, Compound Index, etc.) 
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimized for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assignments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
+# dump the program listings (including syntax highlighting 
+# and cross-referencing information) to the XML output. Note that 
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
+# generate a Perl module file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
+# nicely formatted so it can be parsed by a human reader.  This is useful 
+# if you want to understand what is going on.  On the other hand, if this 
+# tag is set to NO the size of the Perl module output will be much smaller 
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file 
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
+# This is useful so different doxyrules.make files included by the same 
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed. To prevent a macro definition from being 
+# undefined via #undef or recursively expanded use the := operator 
+# instead of the = operator.
+
+PREDEFINED             = 
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse 
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. 
+# Optionally an initial location of the external documentation 
+# can be added for each tagfile. The format of a tag file without 
+# this location is as follows: 
+#   TAGFILES = file1 file2 ... 
+# Adding location for the tag files is done as follows: 
+#   TAGFILES = file1=loc1 "file2 = loc2" ... 
+# where "loc1" and "loc2" can be relative or absolute paths or 
+# URLs. If a location is present for each tag, the installdox tool 
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen 
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = 
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 
+# or super classes. Setting the tag to NO turns the diagrams off. Note that 
+# this option is superseded by the HAVE_DOT option below. This is only a 
+# fallback. It is recommended to install and use dot, since it yields more 
+# powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
+# collaboration diagrams in a style similar to the OMG's Unified Modeling 
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will 
+# generate a call dependency graph for every global function or class method. 
+# Note that enabling this option will significantly increase the time of a run. 
+# So in most cases it will be better to enable call graphs for selected 
+# functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 
+# then doxygen will show the dependencies a directory has on other directories 
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_WIDTH    = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT   = 1024
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
+# graphs generated by dot. A depth value of 3 means that only nodes reachable 
+# from the root by following a path via at most 3 edges will be shown. Nodes 
+# that lay further from the root node will be omitted. Note that setting this 
+# option to 1 or 2 may greatly reduce the computation time needed for large 
+# code bases. Also note that a graph may be further truncated if the graph's 
+# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH 
+# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), 
+# the graph is not depth-constrained.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
+# background. This is disabled by default, which results in a white background. 
+# Warning: Depending on the platform used, enabling this option may lead to 
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to 
+# read).
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 
+# files in one run (i.e. multiple -o and -T options on the command line). This 
+# makes dot run faster, but since only newer versions of dot (>1.8.10) 
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermediate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO

Added: trunk/doc/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/doc/Makefile.am	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,7 @@
+
+SUBDIRS = doxygen
+
+EXTRA_DIST = xmp.txt database.txt
+
+dox: Doxyfile
+	doxygen Doxyfile

Added: trunk/doc/database.txt
==============================================================================
--- (empty file)
+++ trunk/doc/database.txt	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,61 @@
+Database description
+Hubert Figuiere <hub figuiere net>
+
+
+Adminstration table. Contain various parameters.
+
+admin	        key            Key
+                value	       Value
+
+The known keys are:
+version :       The version of the database. Current = 1.
+
+
+Files in the library
+
+files           id             Unique ID in the database
+		path           The absolute path to the file
+		parent_id      The ID on the containing folder (= folders.id)
+		orientation    The Exif orientation of the file
+		file_type      The file type. See db::LibFile::FileType for
+                               possible values.
+		file_date      The file date, likely shooting date from 
+                               Exif (time_t)
+		rating         The file rating (0-5)
+		label          The label (labels.id)
+		import_date    The date of import in the database (time_t)
+		mod_date       The date modified (time_t)
+		xmp            The XMP blob
+		xmp_data       The date the XMP is rewritten on disk (time_t)
+
+
+Folders for the library "storage"
+
+folders         id             Unique ID in the database
+		path           The absolute path of the folder
+		name 	       The display name
+		vault_id       The vault ID (unused) 0 = no vault (= vaults.id)
+		parent_id      The ID of the parent (= folders.id). 0 = top level
+
+Keywords are defined in on table, and linked to files in the other
+
+keywords        id             Unique ID in the database
+		keyword	       The text of the keyword
+		parent_id      The parent keyword. 0 = top level (= keywords.id)
+
+keywording	file_id	       The file ID it is linked to (= files.id)
+		keyword_id     The keyword ID associated (= keywords.id)
+
+There shouldn't be more than one pair of identical (file_id,keyword_id)
+
+The update queue for XMP. When an XMP is changed in the DB it is
+queued in the table.
+
+xmp_update_queue   id          File ID to update.
+
+Vaults are storage location for files. Currently unimplemented
+
+vaults		id	       Unique ID in the database
+		path	       Absolute path of the vault
+
+

Added: trunk/doc/doxygen/Makefile.am
==============================================================================

Added: trunk/doc/gconf.txt
==============================================================================
--- (empty file)
+++ trunk/doc/gconf.txt	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,14 @@
+gconf keys
+
+
+/app/niepce
+      ui_theme_file = the UI rc file to load. Empty means use the system one.
+      ui_theme_set = 0, use system theme. 1, use the dark theme.
+      reopen_last_library = 1 or 0 to reopen the last library
+      lastOpenLibrary = the last open library to reopen (a directory)
+
+Things to move to the library conf file.
+      last_import_location
+      meta_pane_splitter
+      workspace_splitter
+      mainWindow-frame

Added: trunk/doc/mainwindow-ui.svg
==============================================================================
--- (empty file)
+++ trunk/doc/mainwindow-ui.svg	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,282 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://web.resource.org/cc/";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="744.09448819"
+   height="1052.3622047"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.45.1"
+   sodipodi:docbase="/home/hub/cvslocal/niepce/doc"
+   sodipodi:docname="mainwindow-ui.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs4">
+    <marker
+       inkscape:stockid="Arrow1Lstart"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow1Lstart"
+       style="overflow:visible">
+      <path
+         id="path4160"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+         transform="scale(0.8) translate(12.5,0)" />
+    </marker>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.98994949"
+     inkscape:cx="339.23371"
+     inkscape:cy="609.53074"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     inkscape:window-width="1018"
+     inkscape:window-height="691"
+     inkscape:window-x="0"
+     inkscape:window-y="26" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Main"
+     inkscape:groupmode="layer"
+     id="layer1"
+     style="display:inline">
+    <rect
+       style="fill:#0000ff;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="rect2160"
+       width="565.71429"
+       height="334.28571"
+       x="91.428574"
+       y="263.79074" />
+    <rect
+       style="fill:#008080"
+       id="rect2162"
+       width="172.81509"
+       height="254.83539"
+       x="107.21465"
+       y="275.70953" />
+    <rect
+       style="fill:#008000"
+       id="rect2164"
+       width="537.86584"
+       height="39.163162"
+       x="105.51124"
+       y="543.79077" />
+    <rect
+       style="fill:#000080"
+       id="rect2166"
+       width="348.77451"
+       height="255.00871"
+       x="294.28571"
+       y="276.19974" />
+    <rect
+       style="fill:#800080"
+       id="rect2189"
+       width="76.771591"
+       height="19.192898"
+       x="305.06607"
+       y="286.66656" />
+    <rect
+       style="fill:#ff2a2a"
+       id="rect2195"
+       width="81.822357"
+       height="19.192898"
+       x="388.90872"
+       y="285.6564" />
+    <g
+       id="g4365">
+      <path
+         sodipodi:nodetypes="ccc"
+         id="path3186"
+         d="M 219.27979,592.22451 L 148.41574,652.41846 L 148.41574,652.41846"
+         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.84663004px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);stroke-opacity:1" />
+      <text
+         id="text4357"
+         y="668.50421"
+         x="94.954338"
+         style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+         xml:space="preserve"><tspan
+           y="668.50421"
+           x="94.954338"
+           id="tspan4359"
+           sodipodi:role="line">Window (niepcewindow)</tspan></text>
+    </g>
+    <g
+       id="g4372">
+      <path
+         sodipodi:nodetypes="cc"
+         id="path4355"
+         d="M 430.23572,571.84642 L 495.06402,654.04524"
+         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94627947px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);stroke-opacity:1;display:inline" />
+      <text
+         id="text4361"
+         y="669.51434"
+         x="437.39606"
+         style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+         xml:space="preserve"><tspan
+           y="669.51434"
+           x="437.39606"
+           id="tspan4363"
+           sodipodi:role="line">Film strip (filmstripcontroller)</tspan></text>
+    </g>
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.86790729px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);stroke-opacity:1;display:inline"
+       d="M 781.22454,559.68539 L 845.12106,629.84076"
+       id="path4370"
+       sodipodi:nodetypes="cc" />
+    <g
+       id="g4409">
+      <path
+         sodipodi:nodetypes="cc"
+         id="path4377"
+         d="M 195.33093,297.01845 L 109.73512,225.80702"
+         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.0120579px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);stroke-opacity:1;display:inline" />
+      <text
+         id="text4379"
+         y="197.77312"
+         x="28.284269"
+         style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+         xml:space="preserve"><tspan
+           y="197.77312"
+           x="28.284269"
+           id="tspan4381"
+           sodipodi:role="line">Workspace</tspan><tspan
+           id="tspan4383"
+           y="212.77312"
+           x="28.284269"
+           sodipodi:role="line">(workspacecontroller)</tspan></text>
+    </g>
+    <g
+       id="g4444">
+      <path
+         sodipodi:nodetypes="cc"
+         id="path4391"
+         d="M 609.42014,288.01919 L 657.31116,197.43063"
+         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.85382313px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);stroke-opacity:1;display:inline" />
+      <text
+         id="text4393"
+         y="177.57008"
+         x="550.53314"
+         style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+         xml:space="preserve"><tspan
+           y="177.57008"
+           x="550.53314"
+           id="tspan4395"
+           sodipodi:role="line">Main View</tspan><tspan
+           id="tspan4397"
+           y="192.57008"
+           x="550.53314"
+           sodipodi:role="line">(librarymainviewcontroller)</tspan></text>
+    </g>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="Library"
+     style="display:inline">
+    <rect
+       style="fill:#800080"
+       id="rect2187"
+       width="328.29956"
+       height="206.07112"
+       x="305.06607"
+       y="313.94067" />
+    <rect
+       style="fill:#ffaaaa"
+       id="rect2199"
+       width="94.954338"
+       height="188.89853"
+       x="532.3504"
+       y="324.04221" />
+    <rect
+       style="fill:#ffaaaa"
+       id="rect2201"
+       width="213.14218"
+       height="188.89853"
+       x="311.12698"
+       y="324.04221" />
+    <g
+       id="g4434">
+      <path
+         sodipodi:nodetypes="cc"
+         id="path4415"
+         d="M 348.47339,333.14129 L 282.87197,176.55219"
+         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.31383741px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);stroke-opacity:1;display:inline" />
+      <text
+         id="text4418"
+         y="154.33656"
+         x="217.1828"
+         style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+         xml:space="preserve"><tspan
+           y="154.33656"
+           x="217.1828"
+           id="tspan4420"
+           sodipodi:role="line">Main List View</tspan><tspan
+           id="tspan4422"
+           y="169.33656"
+           x="217.1828"
+           sodipodi:role="line">(librarymainviewcontroller)</tspan></text>
+    </g>
+    <g
+       id="g4459">
+      <path
+         sodipodi:nodetypes="cc"
+         id="path4450"
+         d="M 564.01589,354.59857 L 468.36003,226.81577"
+         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.43316531px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);stroke-opacity:1;display:inline" />
+      <text
+         id="text4453"
+         y="203.83405"
+         x="364.66507"
+         style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+         xml:space="preserve"><tspan
+           y="203.83405"
+           x="364.66507"
+           id="tspan4455"
+           sodipodi:role="line">Meta Pane</tspan><tspan
+           id="tspan4457"
+           y="218.83405"
+           x="364.66507"
+           sodipodi:role="line">(librarymainviewcontroller)</tspan></text>
+    </g>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer3"
+     inkscape:label="Darkroom"
+     style="display:none">
+    <rect
+       style="fill:#ff2a2a"
+       id="rect2193"
+       width="328.29959"
+       height="205.06097"
+       x="305.06607"
+       y="313.94067" />
+  </g>
+</svg>

Added: trunk/doc/xmp.txt
==============================================================================
--- (empty file)
+++ trunk/doc/xmp.txt	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,12 @@
+XMP Documentation for Niepce Digital
+Hubert Figuiere <hub figuiere net>
+
+
+Namespace 
+niepce::NIEPCE_XMP_NAMESPACE = "http://ns.figuiere.net/ns/niepce/1.0";
+Suggested prefix
+niepce::NIEPCE_XMP_NS_PREFIX = "niepce";
+
+This namespace is to store metadata spefic to Niepce Digital.
+
+

Added: trunk/m4/boost.m4
==============================================================================
--- (empty file)
+++ trunk/m4/boost.m4	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,767 @@
+# boost.m4: Locate Boost headers and libraries for autoconf-based projects.
+# Copyright (C) 2007  Benoit Sigoure <tsuna lrde epita fr>
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+
+# serial 4
+# Original sources can be found at http://repo.or.cz/w/boost.m4.git
+# You can fetch the latest version of the script by doing:
+#   wget 'http://repo.or.cz/w/boost.m4.git?a=blob_plain;f=build-aux/boost.m4;hb=HEAD' -O boost.m4
+
+# ------ #
+# README #
+# ------ #
+
+# This file provides several macros to use the various Boost libraries.
+# The first macro is BOOST_REQUIRE.  It will simply check if it's possible to
+# find the Boost headers of a given (optional) minimum version and it will
+# define BOOST_CPPFLAGS accordingly.  It will add an option --with-boost to
+# your configure so that users can specify non standard locations.
+# For more README and documentation, go to http://repo.or.cz/w/boost.m4.git
+# Note: THESE MACRO ASSUME THAT YOU USE LIBTOOL.  If you don't, don't worry,
+# simply read the README, it will show you what to do step by step.
+
+m4_pattern_forbid([^_?BOOST_])
+
+# BOOST_REQUIRE([VERSION])
+# ------------------------
+# Look for Boost.  If version is given, it must either be a literal of the form
+# "X.Y.Z" where X, Y and Z are integers (the ".Z" part being optional) or a
+# variable "$var".
+# Defines the value BOOST_CPPFLAGS.  This macro only checks for headers with
+# the required version, it does not check for any of the Boost libraries.
+# FIXME: Add a 2nd optional argument so that it's not fatal if Boost isn't found
+# and add an AC_DEFINE to tell whether HAVE_BOOST.
+AC_DEFUN([BOOST_REQUIRE],
+[dnl First find out what kind of argument we have.
+dnl If we have an empty argument, there is no constraint on the version of
+dnl Boost to use.  If it's a literal version number, we can split it in M4 (so
+dnl the resulting configure script will be smaller/faster).  Otherwise we do
+dnl the splitting at runtime.
+m4_bmatch([$1],
+  [^ *$], [m4_pushdef([BOOST_VERSION_REQ], [])dnl
+           boost_version_major=0
+           boost_version_minor=0
+           boost_version_subminor=0
+],
+  [^[0-9]+\([-._][0-9]+\)*$],
+    [m4_pushdef([BOOST_VERSION_REQ], [ version >= $1])dnl
+     boost_version_major=m4_bregexp([$1], [^\([0-9]+\)], [\1])
+     boost_version_minor=m4_bregexp([$1], [^[0-9]+[-._]\([0-9]+\)], [\1])
+     boost_version_subminor=m4_bregexp([$1], [^[0-9]+[-._][0-9]+[-._]\([0-9]+\)], [\1])
+],
+  [^\$[a-zA-Z_]+$],
+    [m4_pushdef([BOOST_VERSION_REQ], [])dnl
+     boost_version_major=`expr "X$1" : 'X\([[^-._]]*\)'`
+     boost_version_minor=`expr "X$1" : 'X[[0-9]]*[[-._]]\([[^-._]]*\)'`
+     boost_version_subminor=`expr "X$1" : 'X[[0-9]]*[[-._]][[0-9]]*[[-._]]\([[0-9]]*\)'`
+     case $boost_version_major:$boost_version_minor in #(
+       *: | :* | *[[^0-9]]*:* | *:*[[^0-9]]*)
+         AC_MSG_ERROR([[Invalid argument for REQUIRE_BOOST: `$1']])
+         ;;
+     esac
+],
+  [m4_fatal(Invalid argument: `$1')]
+)dnl
+AC_ARG_WITH([boost],
+   [AS_HELP_STRING([--with-boost=DIR],
+                   [prefix of Boost]BOOST_VERSION_REQ[ @<:@guess@:>@])])dnl
+AC_SUBST([DISTCHECK_CONFIGURE_FLAGS],
+         ["$DISTCHECK_CONFIGURE_FLAGS '--with-boost=$with_boost'"])
+  AC_CACHE_CHECK([for Boost headers[]BOOST_VERSION_REQ],
+    [boost_cv_inc_path],
+    [boost_cv_inc_path=no
+AC_LANG_PUSH([C++])dnl
+    boost_subminor_chk=
+    test x"$boost_version_subminor" != x \
+      && boost_subminor_chk="|| (B_V_MAJ == $boost_version_major \
+&& B_V_MIN == $boost_version_minor \
+&& B_V_SUB < $boost_version_subminor)"
+    for boost_inc in "$with_boost/include" '' \
+             /opt/local/include /usr/local/include /opt/include /usr/include \
+             "$with_boost" C:/Boost/include
+    do
+      test -e "$boost_inc" || continue
+      # Ensure that version.hpp exists: we're going to read it.  Moreover,
+      # Boost could be reachable thanks to the default include path so we can
+      # mistakenly accept a wrong include path without this check.
+      test -e "$boost_inc/boost/version.hpp" || continue
+      boost_save_CPPFLAGS=$CPPFLAGS
+      test x"$boost_inc" != x && CPPFLAGS="$CPPFLAGS -I$boost_inc"
+m4_pattern_allow([^BOOST_VERSION$])dnl
+      AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <boost/version.hpp>
+#ifndef BOOST_VERSION
+# error BOOST_VERSION is not defined
+#endif
+#define B_V_MAJ (BOOST_VERSION / 100000)
+#define B_V_MIN (BOOST_VERSION / 100 % 1000)
+#define B_V_SUB (BOOST_VERSION % 100)
+#if (B_V_MAJ < $boost_version_major) \
+   || (B_V_MAJ == $boost_version_major \
+       && B_V_MIN < $boost_version_minor) $boost_subminor_chk
+# error Boost headers version < $1
+#endif
+]])], [boost_cv_inc_path=yes], [boost_cv_version=no])
+      CPPFLAGS=$boost_save_CPPFLAGS
+      if test x"$boost_cv_inc_path" = xyes; then
+        if test x"$boost_inc" != x; then
+          boost_cv_inc_path=$boost_inc
+        fi
+        break
+      fi
+    done
+AC_LANG_POP([C++])dnl
+    ])
+    case $boost_cv_inc_path in #(
+      no)
+        AC_MSG_ERROR([Could not find Boost headers[]BOOST_VERSION_REQ])
+        ;;#(
+      yes)
+        BOOST_CPPFLAGS=
+        ;;#(
+      *)
+        BOOST_CPPFLAGS="-I$boost_cv_inc_path"
+        ;;
+    esac
+AC_SUBST([BOOST_CPPFLAGS])dnl
+  AC_CACHE_CHECK([for Boost's header version],
+    [boost_cv_lib_version],
+    [m4_pattern_allow([^BOOST_LIB_VERSION$])dnl
+    boost_cv_lib_version=unknown
+    boost_sed_version='/^.*BOOST_LIB_VERSION.*"\([[^"]]*\)".*$/!d;s//\1/'
+    boost_version_hpp="$boost_inc/boost/version.hpp"
+    test -e "$boost_version_hpp" \
+      && boost_cv_lib_version=`sed "$boost_sed_version" "$boost_version_hpp"`
+    ])
+m4_popdef([BOOST_VERSION_REQ])dnl
+])# BOOST_REQUIRE
+
+
+# BOOST_FIND_HEADER([HEADER-NAME], [ACTION-IF-NOT-FOUND], [ACTION-IF-FOUND])
+# --------------------------------------------------------------------------
+# Wrapper around AC_CHECK_HEADER for Boost headers.  Useful to check for
+# some parts of the Boost library which are only made of headers and don't
+# require linking (such as Boost.Foreach).
+#
+# Default ACTION-IF-NOT-FOUND: Fail with a fatal error.
+#
+# Default ACTION-IF-FOUND: define the preprocessor symbol HAVE_<HEADER-NAME> in
+# case of success # (where HEADER-NAME is written LIKE_THIS, e.g.,
+# HAVE_BOOST_FOREACH_HPP).
+AC_DEFUN([BOOST_FIND_HEADER],
+[AC_REQUIRE([BOOST_REQUIRE])dnl
+AC_LANG_PUSH([C++])dnl
+boost_save_CPPFLAGS=$CPPFLAGS
+CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
+AC_CHECK_HEADER([$1],
+  [m4_default([$3], [AC_DEFINE(AS_TR_CPP([HAVE_$1]), [1],
+                               [Define to 1 if you have <$1>])])],
+  [m4_default([$2], [AC_MSG_ERROR([cannot find $1])])])
+CPPFLAGS=$boost_save_CPPFLAGS
+AC_LANG_POP([C++])dnl
+])# BOOST_FIND_HEADER
+
+
+# BOOST_FIND_LIB([LIB-NAME], [PREFERRED-RT-OPT], [HEADER-NAME], [CXX-TEST], [CXX-PROLOGUE])
+# -------------------------------------------------------------------------
+# Look for the Boost library LIB-NAME (e.g., LIB-NAME = `thread', for
+# libboost_thread).  Check that HEADER-NAME works and check that
+# libboost_LIB-NAME can link with the code CXX-TEST.
+#
+# Invokes BOOST_FIND_HEADER([HEADER-NAME]) (see above).
+#
+# Boost libraries typically come compiled with several flavors (with different
+# runtime options) so PREFERRED-RT-OPT is the preferred suffix.  A suffix is one
+# or more of the following letters: sgdpn (in that order).  s = static
+# runtime, d = debug build, g = debug/diagnostic runtime, p = STLPort build,
+# n = (unsure) STLPort build without iostreams from STLPort (it looks like `n'
+# must always be used along with `p').  Additionally, PREFERRED-RT-OPT can
+# start with `mt-' to indicate that there is a preference for multi-thread
+# builds.  Some sample values for PREFERRED-RT-OPT: (nothing), mt, d, mt-d, gdp
+# ...  If you want to make sure you have a specific version of Boost
+# (eg, >= 1.33) you *must* invoke BOOST_REQUIRE before this macro.
+AC_DEFUN([BOOST_FIND_LIB],
+[AC_REQUIRE([_BOOST_FIND_COMPILER_TAG])dnl
+AC_REQUIRE([BOOST_REQUIRE])dnl
+AC_REQUIRE([_BOOST_GUESS_WHETHER_TO_USE_MT])dnl
+AC_LANG_PUSH([C++])dnl
+AS_VAR_PUSHDEF([Boost_lib], [boost_cv_lib_$1])dnl
+AS_VAR_PUSHDEF([Boost_lib_LDFLAGS], [boost_cv_lib_$1_LDFLAGS])dnl
+AS_VAR_PUSHDEF([Boost_lib_LIBS], [boost_cv_lib_$1_LIBS])dnl
+BOOST_FIND_HEADER([$3])
+boost_save_CPPFLAGS=$CPPFLAGS
+CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
+# Now let's try to find the library.  The algorithm is as follows: first look
+# for a given library name according to the user's PREFERRED-RT-OPT.  For each
+# library name, we prefer to use the ones that carry the tag (toolset name).
+# Each library is searched through the various standard paths were Boost is
+# usually installed.  If we can't find the standard variants, we try to
+# enforce -mt (for instance on MacOSX, libboost_threads.dylib doesn't exist
+# but there's -obviously- libboost_threads-mt.dylib).
+AC_CACHE_CHECK([for the Boost $1 library], [Boost_lib],
+  [Boost_lib=no
+  case "$2" in #(
+    mt | mt-) boost_mt=-mt; boost_rtopt=;; #(
+    mt* | mt-*) boost_mt=-mt; boost_rtopt=`expr "X$2" : 'Xmt-*\(.*\)'`;; #(
+    *) boost_mt=; boost_rtopt=$2;;
+  esac
+  # If the PREFERRED-RT-OPT are not empty, prepend a `-'.
+  case $boost_rtopt in #(
+    *[[a-z0-9A-Z]]*) boost_rtopt="-$boost_rtopt";;
+  esac
+  $boost_guess_use_mt && boost_mt=-mt
+  # Look for the abs path the static archive.
+  # $libext is computed by Libtool but let's make sure it's non empty.
+  test -z "$libext" &&
+    AC_MSG_ERROR([the libext variable is empty, did you invoke Libtool?])
+  boost_save_ac_objext=$ac_objext
+  # Generate the test file.
+  AC_LANG_CONFTEST([AC_LANG_PROGRAM([#include <$3> 
+  $5], [$4])])
+dnl Optimization hacks: compiling C++ is slow, especially with Boost.  What
+dnl we're trying to do here is guess the right combination of link flags
+dnl (LIBS / LDFLAGS) to use a given library.  This can take several
+dnl iterations before it succeeds and is thus *very* slow.  So what we do
+dnl instead is that we compile the code first (and thus get an object file,
+dnl typically conftest.o).  Then we try various combinations of link flags
+dnl until we succeed to link conftest.o in an executable.  The problem is
+dnl that the various TRY_LINK / COMPILE_IFELSE macros of Autoconf always
+dnl remove all the temporary files including conftest.o.  So the trick here
+dnl is to temporarily change the value of ac_objext so that conftest.o is
+dnl preserved accross tests.  This is obviously fragile and I will burn in
+dnl hell for not respecting Autoconf's documented interfaces, but in the
+dnl mean time, it optimizes the macro by a factor of 5 to 30.
+dnl Another small optimization: the first argument of AC_COMPILE_IFELSE left
+dnl empty because the test file is generated only once above (before we
+dnl start the for loops).
+  AC_COMPILE_IFELSE([],
+    [ac_objext=do_not_rm_me_plz],
+    [AC_MSG_ERROR([Cannot compile a test that uses Boost $1])])
+  ac_objext=$boost_save_ac_objext
+  boost_failed_libs=
+# Don't bother to ident the 6 nested for loops, only the 2 innermost ones
+# matter.
+for boost_tag_ in -$boost_cv_lib_tag ''; do
+for boost_ver_ in -$boost_cv_lib_version ''; do
+for boost_mt_ in $boost_mt -mt ''; do
+for boost_rtopt_ in $boost_rtopt '' -d; do
+  for boost_lib in \
+    boost_$1$boost_tag_$boost_mt_$boost_rtopt_$boost_ver_ \
+    boost_$1$boost_tag_$boost_mt_$boost_ver_ \
+    boost_$1$boost_tag_$boost_rtopt_$boost_ver_ \
+    boost_$1$boost_tag_$boost_mt_ \
+    boost_$1$boost_tag_$boost_ver_
+  do
+    # Avoid testing twice the same lib
+    case $boost_failed_libs in #(
+      * $boost_lib@*) continue;;
+    esac
+    # If with_boost is empty, we'll search in /lib first, which is not quite
+    # right so instead we'll try to a location based on where the headers are.
+    boost_tmp_lib=$with_boost
+    test x"$with_boost" = x && boost_tmp_lib=${boost_cv_inc_path%/include}
+    for boost_ldpath in "$boost_tmp_lib/lib" '' \
+             /opt/local/lib /usr/local/lib /opt/lib /usr/lib \
+             "$with_boost" C:/Boost/lib /lib /usr/lib64 /lib64
+    do
+      test -e "$boost_ldpath" || continue
+      boost_save_LDFLAGS=$LDFLAGS
+      # Are we looking for a static library?
+      case $boost_ldpath:$boost_rtopt_ in #(
+        *?*:*s*) # Yes (Non empty boost_ldpath + s in rt opt)
+          Boost_lib_LIBS="$boost_ldpath/lib$boost_lib.$libext"
+          test -e "$Boost_lib_LIBS" || continue;; #(
+        *) # No: use -lboost_foo to find the shared library.
+          Boost_lib_LIBS="-l$boost_lib";;
+      esac
+      boost_save_LIBS=$LIBS
+      LIBS="$Boost_lib_LIBS $LIBS"
+      test x"$boost_ldpath" != x && LDFLAGS="$LDFLAGS -L$boost_ldpath"
+dnl First argument of AC_LINK_IFELSE left empty because the test file is
+dnl generated only once above (before we start the for loops).
+      _BOOST_AC_LINK_IFELSE([],
+                            [Boost_lib=yes], [Boost_lib=no])
+      ac_objext=$boost_save_ac_objext
+      LDFLAGS=$boost_save_LDFLAGS
+      LIBS=$boost_save_LIBS
+      if test x"$Boost_lib" = xyes; then
+        Boost_lib_LDFLAGS="-L$boost_ldpath -R$boost_ldpath"
+        break 6
+      else
+        boost_failed_libs="$boost_failed_libs $boost_lib@"
+      fi
+    done
+  done
+done
+done
+done
+done
+rm -f conftest.$ac_objext
+])
+case $Boost_lib in #(
+  no) AC_MSG_ERROR([Could not find the flags to link with Boost $1])
+    ;;
+esac
+AC_SUBST(AS_TR_CPP([BOOST_$1_LDFLAGS]), [$Boost_lib_LDFLAGS])
+AC_SUBST(AS_TR_CPP([BOOST_$1_LIBS]), [$Boost_lib_LIBS])
+CPPFLAGS=$boost_save_CPPFLAGS
+AS_VAR_POPDEF([Boost_lib])dnl
+AS_VAR_POPDEF([Boost_lib_LDFLAGS])dnl
+AS_VAR_POPDEF([Boost_lib_LIBS])dnl
+AC_LANG_POP([C++])dnl
+])# BOOST_FIND_LIB
+
+
+# --------------------------------------- #
+# Checks for the various Boost libraries. #
+# --------------------------------------- #
+
+# List of boost libraries: http://www.boost.org/libs/libraries.htm
+# The page http://beta.boost.org/doc/libs is useful: it gives the first release
+# version of each library (among other things).
+
+
+# BOOST_BIND()
+# ------------
+# Look for Boost.Bind
+AC_DEFUN([BOOST_BIND],
+[BOOST_FIND_HEADER([boost/bind.hpp])])
+
+
+# BOOST_CONVERSION()
+# ------------------
+# Look for Boost.Conversion (cast / lexical_cast)
+AC_DEFUN([BOOST_CONVERSION],
+[BOOST_FIND_HEADER([boost/cast.hpp])
+BOOST_FIND_HEADER([boost/lexical_cast.hpp])
+])# BOOST_CONVERSION
+
+
+# BOOST_DATE_TIME([PREFERRED-RT-OPT])
+# -----------------------------------
+# Look for Boost.Date_Time.  For the documentation of PREFERRED-RT-OPT, see the
+# documentation of BOOST_FIND_LIB above.
+AC_DEFUN([BOOST_DATE_TIME],
+[BOOST_FIND_LIB([date_time], [$1],
+                [boost/date_time/posix_time/posix_time.hpp],
+                [boost::posix_time::ptime t;])
+])# BOOST_DATE_TIME
+
+
+# BOOST_FILESYSTEM([PREFERRED-RT-OPT])
+# ------------------------------------
+# Look for Boost.Filesystem.  For the documentation of PREFERRED-RT-OPT, see the
+# documentation of BOOST_FIND_LIB above.
+# Do not check for boost/filesystem.hpp because this file was introduced in 1.34.
+AC_DEFUN([BOOST_FILESYSTEM],
+[BOOST_FIND_LIB([filesystem], [$1],
+                [boost/filesystem/path.hpp], [boost::filesystem::path p;])
+])# BOOST_FILESYSTEM
+
+
+# BOOST_FOREACH()
+# ---------------
+# Look for Boost.Foreach
+AC_DEFUN([BOOST_FOREACH],
+[BOOST_FIND_HEADER([boost/foreach.hpp])])
+
+
+# BOOST_FORMAT()
+# --------------
+# Look for Boost.Format
+# Note: we can't check for boost/format/format_fwd.hpp because the header isn't
+# standalone.  It can't be compiled because it triggers the following error:
+# boost/format/detail/config_macros.hpp:88: error: 'locale' in namespace 'std'
+#                                                  does not name a type
+AC_DEFUN([BOOST_FORMAT],
+[BOOST_FIND_HEADER([boost/format.hpp])])
+
+
+# BOOST_FUNCTION()
+# ----------------
+# Look for Boost.Function
+AC_DEFUN([BOOST_FUNCTION],
+[BOOST_FIND_HEADER([boost/function.hpp])])
+
+
+# BOOST_GRAPH([PREFERRED-RT-OPT])
+# -------------------------------
+# Look for Boost.Graphs.  For the documentation of PREFERRED-RT-OPT, see the
+# documentation of BOOST_FIND_LIB above.
+AC_DEFUN([BOOST_GRAPH],
+[BOOST_FIND_LIB([graph], [$1],
+                [boost/graph/adjacency_list.hpp], [boost::adjacency_list<> g;])
+])# BOOST_GRAPH
+
+
+# BOOST_IOSTREAMS([PREFERRED-RT-OPT])
+# -------------------------------
+# Look for Boost.IOStreams.  For the documentation of PREFERRED-RT-OPT, see the
+# documentation of BOOST_FIND_LIB above.
+AC_DEFUN([BOOST_IOSTREAMS],
+[BOOST_FIND_LIB([iostreams], [$1],
+                [boost/iostreams/device/file_descriptor.hpp],
+                [boost::iostreams::file_descriptor fd(0); fd.close();])
+])# BOOST_IOSTREAMS
+
+
+# BOOST_HASH()
+# ------------
+# Look for Boost.Functional/Hash
+AC_DEFUN([BOOST_HASH],
+[BOOST_FIND_HEADER([boost/functional/hash.hpp])])
+
+
+# BOOST_PROGRAM_OPTIONS([PREFERRED-RT-OPT])
+# -----------------------------------------
+# Look for Boost.Program_options.  For the documentation of PREFERRED-RT-OPT, see
+# the documentation of BOOST_FIND_LIB above.
+AC_DEFUN([BOOST_PROGRAM_OPTIONS],
+[BOOST_FIND_LIB([program_options], [$1],
+                [boost/program_options.hpp],
+                [boost::program_options::options_description d("test");])
+])# BOOST_PROGRAM_OPTIONS
+
+
+# BOOST_REF()
+# -----------
+# Look for Boost.Ref
+AC_DEFUN([BOOST_REF],
+[BOOST_FIND_HEADER([boost/ref.hpp])])
+
+
+# BOOST_REGEX([PREFERRED-RT-OPT])
+# -------------------------------
+# Look for Boost.Regex.  For the documentation of PREFERRED-RT-OPT, see the
+# documentation of BOOST_FIND_LIB above.
+AC_DEFUN([BOOST_REGEX],
+[BOOST_FIND_LIB([regex], [$1],
+                [boost/regex.hpp],
+                [boost::regex exp("*"); boost::regex_match("foo", exp);])
+])# BOOST_REGEX
+
+
+# BOOST_SIGNALS([PREFERRED-RT-OPT])
+# ---------------------------------
+# Look for Boost.Signals.  For the documentation of PREFERRED-RT-OPT, see the
+# documentation of BOOST_FIND_LIB above.
+AC_DEFUN([BOOST_SIGNALS],
+[BOOST_FIND_LIB([signals], [$1],
+                [boost/signal.hpp],
+                [boost::signal<void ()> s;])
+])# BOOST_SIGNALS
+
+
+# BOOST_SMART_PTR()
+# -----------------
+# Look for Boost.SmartPtr
+AC_DEFUN([BOOST_SMART_PTR],
+[BOOST_FIND_HEADER([boost/scoped_ptr.hpp])
+BOOST_FIND_HEADER([boost/shared_ptr.hpp])
+])
+
+
+# BOOST_STRING_ALGO()
+# -------------------
+# Look for Boost.StringAlgo
+AC_DEFUN([BOOST_STRING_ALGO],
+[BOOST_FIND_HEADER([boost/algorithm/string.hpp])
+])
+
+
+# BOOST_TEST([PREFERRED-RT-OPT])
+# ------------------------------
+# Look for Boost.Test.  For the documentation of PREFERRED-RT-OPT, see the
+# documentation of BOOST_FIND_LIB above.
+AC_DEFUN([BOOST_TEST],
+[m4_pattern_allow([^BOOST_CHECK$])dnl
+BOOST_FIND_LIB([unit_test_framework], [$1],
+               [boost/test/unit_test.hpp], [BOOST_CHECK(2==2);],
+               [using boost::unit_test::test_suite; test_suite* init_unit_test_suite( int argc, char ** argv ) { return NULL;}])
+])# BOOST_TEST
+
+
+# BOOST_THREADS([PREFERRED-RT-OPT])
+# ---------------------------------
+# Look for Boost.Thread.  For the documentation of PREFERRED-RT-OPT, see the
+# documentation of BOOST_FIND_LIB above.
+# FIXME: Provide an alias "BOOST_THREAD".
+AC_DEFUN([BOOST_THREADS],
+[dnl Having the pthread flag is required at least on GCC3 where
+dnl boost/thread.hpp would complain if we try to compile without
+dnl -pthread on GNU/Linux.
+AC_REQUIRE([_BOOST_PTHREAD_FLAG])dnl
+boost_threads_save_LIBS=$LIBS
+boost_threads_save_CPPFLAGS=$CPPFLAGS
+LIBS="$LIBS $boost_cv_pthread_flag"
+# Yes, we *need* to put the -pthread thing in CPPFLAGS because with GCC3,
+# boost/thread.hpp will trigger a #error if -pthread isn't used:
+#   boost/config/requires_threads.hpp:47:5: #error "Compiler threading support
+#   is not turned on. Please set the correct command line options for
+#   threading: -pthread (Linux), -pthreads (Solaris) or -mthreads (Mingw32)"
+CPPFLAGS="$CPPFLAGS $boost_cv_pthread_flag"
+BOOST_FIND_LIB([thread], [$1],
+                [boost/thread.hpp], [boost::thread t; boost::mutex m;])
+BOOST_THREAD_LIBS="$BOOST_THREAD_LIBS $boost_cv_pthread_flag"
+BOOST_CPPFLAGS="$BOOST_CPPFLAGS $boost_cv_pthread_flag"
+LIBS=$boost_threads_save_LIBS
+CPPFLAGS=$boost_threads_save_CPPFLAGS
+])# BOOST_THREADS
+
+
+# BOOST_TOKENIZER()
+# -----------------
+# Look for Boost.Tokenizer
+AC_DEFUN([BOOST_TOKENIZER],
+[BOOST_FIND_HEADER([boost/tokenizer.hpp])])
+
+
+# BOOST_TRIBOOL()
+# ---------------
+# Look for Boost.Tribool
+AC_DEFUN([BOOST_TRIBOOL],
+[BOOST_FIND_HEADER([boost/logic/tribool_fwd.hpp])
+BOOST_FIND_HEADER([boost/logic/tribool.hpp])
+])
+
+
+# BOOST_TUPLE()
+# -------------
+# Look for Boost.Tuple
+AC_DEFUN([BOOST_TUPLE],
+[BOOST_FIND_HEADER([boost/tuple/tuple.hpp])])
+
+
+# BOOST_UTILITY()
+# ---------------
+# Look for Boost.Utility (noncopyable, result_of, base-from-member idiom,
+# etc.)
+AC_DEFUN([BOOST_UTILITY],
+[BOOST_FIND_HEADER([boost/utility.hpp])])
+
+
+# BOOST_VARIANT()
+# ---------------
+# Look for Boost.Variant.
+AC_DEFUN([BOOST_VARIANT],
+[BOOST_FIND_HEADER([boost/variant/variant_fwd.hpp])
+BOOST_FIND_HEADER([boost/variant.hpp])])
+
+
+# BOOST_WAVE([PREFERRED-RT-OPT])
+# ------------------------------
+# Look for Boost.Wave.  For the documentation of PREFERRED-RT-OPT, see the
+# documentation of BOOST_FIND_LIB above.
+AC_DEFUN([BOOST_WAVE],
+[BOOST_FIND_LIB([wave], [$1],
+                [boost/wave.hpp],
+                [boost::wave::token_id id; get_token_name(id);])])
+
+
+# ----------------- #
+# Internal helpers. #
+# ----------------- #
+
+
+# _BOOST_PTHREAD_FLAG()
+# ---------------------
+# Internal helper for BOOST_THREADS.  Based on ACX_PTHREAD:
+# http://autoconf-archive.cryp.to/acx_pthread.html
+AC_DEFUN([_BOOST_PTHREAD_FLAG],
+[AC_REQUIRE([AC_PROG_CXX])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_LANG_PUSH([C++])dnl
+AC_CACHE_CHECK([for the flags needed to use pthreads], [boost_cv_pthread_flag],
+[ boost_cv_pthread_flag=
+  # The ordering *is* (sometimes) important.  Some notes on the
+  # individual items follow:
+  # (none): in case threads are in libc; should be tried before -Kthread and
+  #       other compiler flags to prevent continual compiler warnings
+  # -lpthreads: AIX (must check this before -lpthread)
+  # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+  # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+  # -llthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+  # -pthread: GNU Linux/GCC (kernel threads), BSD/GCC (userland threads)
+  # -pthreads: Solaris/GCC
+  # -mthreads: MinGW32/GCC, Lynx/GCC
+  # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+  #      doesn't hurt to check since this sometimes defines pthreads too;
+  #      also defines -D_REENTRANT)
+  #      ... -mt is also the pthreads flag for HP/aCC
+  # -lpthread: GNU Linux, etc.
+  # --thread-safe: KAI C++
+  case $host_os in #(
+    *solaris*)
+      # On Solaris (at least, for some versions), libc contains stubbed
+      # (non-functional) versions of the pthreads routines, so link-based
+      # tests will erroneously succeed.  (We need to link with -pthreads/-mt/
+      # -lpthread.)  (The stubs are missing pthread_cleanup_push, or rather
+      # a function called by this macro, so we could check for that, but
+      # who knows whether they'll stub that too in a future libc.)  So,
+      # we'll just look for -pthreads and -lpthread first:
+      boost_pthread_flags="-pthreads -lpthread -mt -pthread";; #(
+    *)
+      boost_pthread_flags="-lpthreads -Kthread -kthread -llthread -pthread \
+                           -pthreads -mthreads -lpthread --thread-safe -mt";;
+  esac
+  # Generate the test file.
+  AC_LANG_CONFTEST([AC_LANG_PROGRAM([#include <pthread.h>],
+    [pthread_t th; pthread_join(th, 0);
+    pthread_attr_init(0); pthread_cleanup_push(0, 0);
+    pthread_create(0,0,0,0); pthread_cleanup_pop(0);])])
+  for boost_pthread_flag in '' $boost_pthread_flags; do
+    boost_pthread_ok=false
+dnl Re-use the test file already generated.
+    boost_pthreads__save_LIBS=$LIBS
+    LIBS="$LIBS $boost_pthread_flag"
+    AC_LINK_IFELSE([],
+      [if grep ".*$boost_pthread_flag" conftest.err; then
+         echo "This flag seems to have triggered warnings" >&AS_MESSAGE_LOG_FD
+       else
+         boost_pthread_ok=:; boost_cv_pthread_flag=$boost_pthread_flag
+       fi])
+    LIBS=$boost_pthreads__save_LIBS
+    $boost_pthread_ok && break
+  done
+])
+AC_LANG_POP([C++])dnl
+])# _BOOST_PTHREAD_FLAG
+
+
+# _BOOST_gcc_test(MAJOR, MINOR)
+# -----------------------------
+# Internal helper for _BOOST_FIND_COMPILER_TAG.
+m4_define([_BOOST_gcc_test],
+["defined __GNUC__ && __GNUC__ == $1 && __GNUC_MINOR__ == $2 && !defined __ICC @ gcc$1$2"])dnl
+
+
+# _BOOST_FIND_COMPILER_TAG()
+# --------------------------
+# Internal.  When Boost is installed without --layout=system, each library
+# filename will hold a suffix that encodes the compiler used during the
+# build.  The Boost build system seems to call this a `tag'.
+AC_DEFUN([_BOOST_FIND_COMPILER_TAG],
+[AC_REQUIRE([AC_PROG_CXX])dnl
+AC_CACHE_CHECK([for the toolset name used by Boost for $CXX], [boost_cv_lib_tag],
+[AC_LANG_PUSH([C++])dnl
+  boost_cv_lib_tag=unknown
+  # The following tests are mostly inspired by boost/config/auto_link.hpp
+  # The list is sorted to most recent/common to oldest compiler (in order
+  # to increase the likelihood of finding the right compiler with the
+  # least number of compilation attempt).
+  # Beware that some tests are sensible to the order (for instance, we must
+  # look for MinGW before looking for GCC3).
+  # I used one compilation test per compiler with a #error to recognize
+  # each compiler so that it works even when cross-compiling (let me know
+  # if you know a better approach).
+  # Known missing tags (known from Boost's tools/build/v2/tools/common.jam):
+  #   como, edg, kcc, bck, mp, sw, tru, xlc
+  # I'm not sure about my test for `il' (be careful: Intel's ICC pre-defines
+  # the same defines as GCC's).
+  # TODO: Move the test on GCC 4.3 up once it's released.
+  for i in \
+    _BOOST_gcc_test(4, 2) \
+    _BOOST_gcc_test(4, 1) \
+    _BOOST_gcc_test(4, 0) \
+    "defined __GNUC__ && __GNUC__ == 3 && !defined __ICC \
+     && (defined WIN32 || defined WINNT || defined _WIN32 || defined __WIN32 \
+         || defined __WIN32__ || defined __WINNT || defined __WINNT__) @ mgw" \
+    _BOOST_gcc_test(3, 4) \
+    _BOOST_gcc_test(3, 3) \
+    "defined _MSC_VER && _MSC_VER >= 1400 @ vc80" \
+    _BOOST_gcc_test(3, 2) \
+    "defined _MSC_VER && _MSC_VER == 1310 @ vc71" \
+    _BOOST_gcc_test(3, 1) \
+    _BOOST_gcc_test(3, 0) \
+    "defined __BORLANDC__ @ bcb" \
+    "defined __ICC && (defined __unix || defined __unix__) @ il" \
+    "defined __ICL @ iw" \
+    "defined _MSC_VER && _MSC_VER == 1300 @ vc7" \
+    _BOOST_gcc_test(4, 3) \
+    _BOOST_gcc_test(2, 95) \
+    "defined __MWERKS__ && __MWERKS__ <= 0x32FF @ cw9" \
+    "defined _MSC_VER && _MSC_VER < 1300 && !defined UNDER_CE @ vc6" \
+    "defined _MSC_VER && _MSC_VER < 1300 && defined UNDER_CE @ evc4" \
+    "defined __MWERKS__ && __MWERKS__ <= 0x31FF @ cw8"
+  do
+    boost_tag_test=`expr "X$i" : 'X\([[^ ]]*\) @ '`
+    boost_tag=`expr "X$i" : 'X[[^ ]]* @ \(.*\)'`
+    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#if $boost_tag_test
+/* OK */
+#else
+# error $boost_tag_test
+#endif
+]])], [boost_cv_lib_tag=$boost_tag; break], [])
+  done
+AC_LANG_POP([C++])dnl
+])
+  if test x"$boost_cv_lib_tag" = xunknown; then
+    AC_MSG_WARN([[could not figure out which toolset name to use for $CXX]])
+    boost_cv_lib_tag=
+  fi
+])# _BOOST_FIND_COMPILER_TAG
+
+
+# _BOOST_GUESS_WHETHER_TO_USE_MT()
+# --------------------------------
+# Compile a small test to try to guess whether we should favor MT (Multi
+# Thread) flavors of Boost.  Sets boost_guess_use_mt accordingly.
+AC_DEFUN([_BOOST_GUESS_WHETHER_TO_USE_MT],
+[# Check whether we do better use `mt' even though we weren't ask to.
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#if defined _REENTRANT || defined _MT || defined __MT__
+/* use -mt */
+#else
+# error MT not needed
+#endif
+]])], [boost_guess_use_mt=:], [boost_guess_use_mt=false])
+])
+
+# _BOOST_AC_LINK_IFELSE(PROGRAM, [ACTION-IF-TRUE], [ACTION-IF-FALSE])
+# -------------------------------------------------------------------
+# Fork of _AC_LINK_IFELSE that preserves conftest.o across calls.  Fragile,
+# will break when Autoconf changes its internals.  Requires that you manually
+# rm -f conftest.$ac_objext in between to really different tests, otherwise
+# you will try to link a conftest.o left behind by a previous test.
+# Used to aggressively optimize BOOST_FIND_LIB (see the big comment in this
+# macro)
+m4_define([_BOOST_AC_LINK_IFELSE],
+[m4_ifvaln([$1], [AC_LANG_CONFTEST([$1])])dnl
+rm -f conftest$ac_exeext
+boost_ac_ext_save=$ac_ext
+boost_use_source=:
+# If we already have a .o, re-use it.  We change $ac_ext so that $ac_link
+# tries to link the existing object file instead of compiling from source.
+test -f conftest.$ac_objext && ac_ext=$ac_objext && boost_use_source=false &&
+  _AS_ECHO_LOG([re-using the existing conftest.$ac_objext])
+AS_IF([_AC_DO_STDERR($ac_link) && {
+	 test -z "$ac_[]_AC_LANG_ABBREV[]_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+	 test "$cross_compiling" = yes ||
+	 $as_executable_p conftest$ac_exeext
+dnl FIXME: use AS_TEST_X instead when 2.61 is widespread enough.
+       }],
+      [$2],
+      [if $boost_use_source; then
+         _AC_MSG_LOG_CONFTEST
+       fi
+       $3])
+dnl Delete also the IPA/IPO (Inter Procedural Analysis/Optimization)
+dnl information created by the PGI compiler (conftest_ipa8_conftest.oo),
+dnl as it would interfere with the next link command.
+rm -f core conftest.err conftest_ipa8_conftest.oo \
+      conftest$ac_exeext m4_ifval([$1], [conftest.$ac_ext])[]dnl
+])# _BOOST_AC_LINK_IFELSE

Added: trunk/po/POTFILES.in
==============================================================================
--- (empty file)
+++ trunk/po/POTFILES.in	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,24 @@
+src/framework/metadatawidget.cpp
+src/ui/importdialog.cpp
+src/ui/importdialog.glade
+src/ui/librarymainviewcontroller.cpp
+src/ui/metadatapanecontroller.cpp
+src/ui/niepceapplication.cpp
+src/ui/niepcewindow.cpp
+src/ui/preferences.glade
+src/ui/selectioncontroller.cpp
+src/ui/workspacecontroller.cpp
+src/ui/thumb-view/eog-thumb-view.cpp
+src/niepce/stock.cpp
+src/modules/darkroom/toolboxcontroller.cpp
+src/ext/libgdl/gdl-dock-bar.c
+src/ext/libgdl/gdl-dock-item-grip.c
+src/ext/libgdl/gdl-dock-item.c
+src/ext/libgdl/gdl-dock-master.c
+src/ext/libgdl/gdl-dock-notebook.c
+src/ext/libgdl/gdl-dock-object.c
+src/ext/libgdl/gdl-dock-paned.c
+src/ext/libgdl/gdl-dock-placeholder.c
+src/ext/libgdl/gdl-dock-tablabel.c
+src/ext/libgdl/gdl-dock.c
+src/ext/libgdl/gdl-switcher.c

Added: trunk/po/niepce.pot
==============================================================================
--- (empty file)
+++ trunk/po/niepce.pot	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,299 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2008-06-07 16:45-0400\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL ADDRESS>\n"
+"Language-Team: LANGUAGE <LL li org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+
+#: ../src/ui/importdialog.cpp:76
+msgid "Import picture folder"
+msgstr ""
+
+#: ../src/ui/importdialog.cpp:80 ../src/ui/importdialog.glade.h:5
+msgid "Import"
+msgstr ""
+
+#: ../src/ui/importdialog.glade.h:1
+msgid "..."
+msgstr ""
+
+#: ../src/ui/importdialog.glade.h:2
+msgid "<b>Date:</b>"
+msgstr ""
+
+#: ../src/ui/importdialog.glade.h:3
+msgid ""
+"Date is local\n"
+"Date is UTC"
+msgstr ""
+
+#: ../src/ui/importdialog.glade.h:6
+msgid "Import Raw_Studio"
+msgstr ""
+
+#: ../src/ui/importdialog.glade.h:7
+msgid "Import _UFRaw"
+msgstr ""
+
+#: ../src/ui/importdialog.glade.h:8
+msgid "You can still change this after importing the pictures."
+msgstr ""
+
+#: ../src/ui/importdialog.glade.h:9
+msgid "_Directory:"
+msgstr ""
+
+#: ../src/ui/importdialog.glade.h:10
+msgid "_Folders"
+msgstr ""
+
+#: ../src/ui/importdialog.glade.h:11
+msgid "_Import"
+msgstr ""
+
+#: ../src/ui/importdialog.glade.h:12
+msgid "_Options"
+msgstr ""
+
+#: ../src/ui/importdialog.glade.h:13
+msgid "gtk-cancel"
+msgstr ""
+
+#: ../src/ui/librarymainviewcontroller.cpp:146
+msgid "Library"
+msgstr ""
+
+#: ../src/ui/librarymainviewcontroller.cpp:150
+msgid "Darkroom"
+msgstr ""
+
+#: ../src/ui/metadatapanecontroller.cpp:39
+msgid "Make:"
+msgstr ""
+
+#: ../src/ui/metadatapanecontroller.cpp:40
+msgid "Model:"
+msgstr ""
+
+#: ../src/ui/metadatapanecontroller.cpp:41
+msgid "Lens:"
+msgstr ""
+
+#: ../src/ui/metadatapanecontroller.cpp:45
+msgid "Exposure Program:"
+msgstr ""
+
+#: ../src/ui/metadatapanecontroller.cpp:46
+msgid "Speed:"
+msgstr ""
+
+#: ../src/ui/metadatapanecontroller.cpp:47
+msgid "Aperture:"
+msgstr ""
+
+#: ../src/ui/metadatapanecontroller.cpp:48
+msgid "ISO:"
+msgstr ""
+
+#: ../src/ui/metadatapanecontroller.cpp:49
+msgid "Exposure Bias:"
+msgstr ""
+
+#. this one is fishy as it hardcode the prefix.
+#: ../src/ui/metadatapanecontroller.cpp:51
+msgid "Flash:"
+msgstr ""
+
+#: ../src/ui/metadatapanecontroller.cpp:52
+msgid "Flash compensation:"
+msgstr ""
+
+#: ../src/ui/metadatapanecontroller.cpp:53
+msgid "Focal length:"
+msgstr ""
+
+#: ../src/ui/metadatapanecontroller.cpp:54
+msgid "White balance:"
+msgstr ""
+
+#: ../src/ui/metadatapanecontroller.cpp:55
+msgid "Date:"
+msgstr ""
+
+#: ../src/ui/metadatapanecontroller.cpp:59
+msgid "Rating:"
+msgstr ""
+
+#: ../src/ui/metadatapanecontroller.cpp:60
+msgid "Keywords:"
+msgstr ""
+
+#: ../src/ui/metadatapanecontroller.cpp:64
+msgid "Camera Information"
+msgstr ""
+
+#: ../src/ui/metadatapanecontroller.cpp:67
+msgid "Shooting Information"
+msgstr ""
+
+#: ../src/ui/metadatapanecontroller.cpp:70
+msgid "IPTC"
+msgstr ""
+
+#: ../src/ui/metadatapanecontroller.cpp:73
+msgid "Rights"
+msgstr ""
+
+#: ../src/ui/niepcewindow.cpp:134
+msgid "Ready"
+msgstr ""
+
+#: ../src/ui/niepcewindow.cpp:220
+msgid "_Library"
+msgstr ""
+
+#: ../src/ui/niepcewindow.cpp:226
+msgid "New _Folder..."
+msgstr ""
+
+#: ../src/ui/niepcewindow.cpp:227
+msgid "New _Project..."
+msgstr ""
+
+#: ../src/ui/niepcewindow.cpp:229
+msgid "_Import..."
+msgstr ""
+
+#: ../src/ui/niepcewindow.cpp:239
+msgid "_Edit"
+msgstr ""
+
+#: ../src/ui/niepcewindow.cpp:268
+msgid "_Image"
+msgstr ""
+
+#: ../src/ui/niepcewindow.cpp:279
+msgid "Set _Label"
+msgstr ""
+
+#: ../src/ui/niepcewindow.cpp:282
+msgid "Set _Rating"
+msgstr ""
+
+#: ../src/ui/niepcewindow.cpp:283
+msgid "_No Rating"
+msgstr ""
+
+#: ../src/ui/niepcewindow.cpp:285
+msgid "_1 Star"
+msgstr ""
+
+#: ../src/ui/niepcewindow.cpp:287
+msgid "_2 Stars"
+msgstr ""
+
+#: ../src/ui/niepcewindow.cpp:289
+msgid "_3 Stars"
+msgstr ""
+
+#: ../src/ui/niepcewindow.cpp:291
+msgid "_4 Stars"
+msgstr ""
+
+#: ../src/ui/niepcewindow.cpp:293
+msgid "_5 Stars"
+msgstr ""
+
+#: ../src/ui/niepcewindow.cpp:298
+msgid "_Help"
+msgstr ""
+
+#: ../src/ui/niepcewindow.cpp:315
+msgid "Undo "
+msgstr ""
+
+#: ../src/ui/niepcewindow.cpp:324
+msgid "Redo "
+msgstr ""
+
+#: ../src/ui/niepcewindow.cpp:385
+msgid "Create library"
+msgstr ""
+
+#: ../src/ui/niepcewindow.cpp:389
+msgid "Create"
+msgstr ""
+
+#: ../src/ui/niepcewindow.cpp:463
+msgid "Niepce Digital - "
+msgstr ""
+
+#.
+#. Local Variables:
+#. mode:c++
+#. c-file-style:"stroustrup"
+#. c-file-offsets:((innamespace . 0))
+#. indent-tabs-mode:nil
+#. fill-column:99
+#. End:
+#.
+#: ../src/ui/preferences.glade.h:1
+msgid ""
+"System\n"
+"Dark"
+msgstr ""
+
+#: ../src/ui/preferences.glade.h:3
+msgid "_General"
+msgstr ""
+
+#: ../src/ui/preferences.glade.h:4
+msgid "_Reopen Library"
+msgstr ""
+
+#: ../src/ui/preferences.glade.h:5
+msgid "_Theme:\t"
+msgstr ""
+
+#: ../src/ui/preferences.glade.h:6
+msgid "_User Interface"
+msgstr ""
+
+#: ../src/ui/workspacecontroller.cpp:197
+msgid "Pictures"
+msgstr ""
+
+#: ../src/ui/workspacecontroller.cpp:201
+msgid "Projects"
+msgstr ""
+
+#: ../src/ui/workspacecontroller.cpp:205
+msgid "Keywords"
+msgstr ""
+
+#. TODO make it a mnemonic
+#: ../src/ui/workspacecontroller.cpp:218
+msgid "_Workspace"
+msgstr ""
+
+#: ../src/ui/thumb-view/eog-thumb-view.cpp:423
+msgid "pixel"
+msgid_plural "pixels"
+msgstr[0] ""
+msgstr[1] ""
+
+#: ../src/ui/thumb-view/eog-thumb-view.cpp:434
+msgid "Taken on"
+msgstr ""

Added: trunk/src/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/Makefile.am	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,5 @@
+
+AM_CPPFLAGS = -DNIEPCE_LOCALEDIR=\"${NIEPCE_LOCALEDIR}\"
+
+SUBDIRS = ext niepce utils framework \
+	db library libraryclient ncr ui modules main

Added: trunk/src/db/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/db/Makefile.am	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,33 @@
+
+
+SUBDIRS = 
+
+INCLUDES = -I$(top_srcdir)/src/  @EXEMPI_CFLAGS@
+
+TESTS = test_library
+
+TEST_LIBS =  \
+	libniepcedb.a \
+	../utils/libniepceutils.a \
+	../framework/libniepceframework.a \
+	@LIBGTKMM_LIBS@ @GNOMEVFS_LIBS@ \
+	@BOOST_UNIT_TEST_FRAMEWORK_LIBS@ \
+	@BOOST_FILESYSTEM_LIBS@ \
+	@BOOST_THREAD_LIBS@ \
+	@SQLITE3_LIBS@ @EXEMPI_LIBS@
+
+check_PROGRAMS = test_library
+
+test_library_SOURCES = test_library.cpp
+test_library_LDADD = $(TEST_LIBS)  
+
+noinst_LIBRARIES = libniepcedb.a
+
+noinst_HEADERS = library.h 
+
+libniepcedb_a_SOURCES = library.cpp \
+	libfile.h libfile.cpp \
+	libmetadata.h libmetadata.cpp \
+	keyword.h keyword.cpp \
+	storage.h storage.cpp \
+	metadata.h

Added: trunk/src/db/keyword.cpp
==============================================================================
--- (empty file)
+++ trunk/src/db/keyword.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,33 @@
+/*
+ * niepce - db/keyword.cpp
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+#include "keyword.h"
+
+
+namespace db {
+
+	Keyword::Keyword(int _id, const std::string & _keyword)
+		: m_id(_id), m_keyword(_keyword)
+	{
+	}
+
+
+}

Added: trunk/src/db/keyword.h
==============================================================================
--- (empty file)
+++ trunk/src/db/keyword.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,51 @@
+/*
+ * niepce - library/keyword.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __NIEPCE_DB_KEYWORD_H__
+#define __NIEPCE_DB_KEYWORD_H__
+
+#include <string>
+#include <vector>
+#include <boost/shared_ptr.hpp>
+
+namespace db {
+
+	class Keyword
+	{
+	public:
+		typedef boost::shared_ptr<Keyword> Ptr;
+		typedef std::vector<Ptr> List;
+		typedef boost::shared_ptr<List> ListPtr;
+		typedef std::vector<int> IdList;
+
+		Keyword(int id, const std::string & keyword);
+		
+		int id() const
+			{ return m_id; }
+		const std::string & keyword() 
+			{ return m_keyword; }
+	private:
+		int m_id;
+		std::string m_keyword;
+	};
+
+}
+
+#endif

Added: trunk/src/db/libfile.cpp
==============================================================================
--- (empty file)
+++ trunk/src/db/libfile.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,122 @@
+/*
+ * niepce - db/libfile.cpp
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "utils/debug.h"
+#include "libfile.h"
+#include "metadata.h"
+
+namespace bfs = boost::filesystem;
+
+namespace db {
+	
+LibFile::LibFile(int _id, int _folderId, const bfs::path & p,
+                 const std::string & _name )
+	: m_id(_id), m_folderId(_folderId),
+	  m_name(_name), m_path(p),
+	  m_orientation(0), m_rating(0), m_label(0),
+      m_file_type(FILE_TYPE_UNKNOWN)
+{
+    
+}
+
+LibFile::~LibFile()
+{
+}
+
+
+const Keyword::IdList & LibFile::keywords() const
+{
+    if(!m_hasKeywordList) {
+//			storage()->fetchKeywordsForFile(m_id, m_keywordList);
+    }
+    return m_keywordList;
+}
+
+void LibFile::setOrientation(int32_t v)
+{
+    m_orientation = v;
+}
+
+
+void LibFile::setRating(int32_t v)
+{
+    m_rating = v;
+}
+
+
+void LibFile::setLabel(int32_t v)
+{
+    m_label = v;
+}
+
+void LibFile::setFileType(FileType v)
+{
+    m_file_type = v;
+}
+
+void LibFile::setMetaData(int meta, int32_t v)
+{
+    switch(meta) {
+    case MAKE_METADATA_IDX(db::META_NS_XMPCORE, db::META_XMPCORE_RATING):
+        setRating(v);
+        break;
+    case MAKE_METADATA_IDX(db::META_NS_TIFF, db::META_TIFF_ORIENTATION):
+        setOrientation(v);
+        break;
+    default:
+        // TODO deal with label as well
+        ERR_OUT("unknown meta %d", meta);
+        break;
+    }
+}
+
+/**
+ * Converts a mimetype, which is expensive to calculate, into a FileType.
+ * @param mime The mimetype we want to know as a filetype
+ * @return the filetype
+ * @todo: add the Video, JPEG+RAW file types.
+ */
+LibFile::FileType LibFile::mimetype_to_filetype(framework::MimeType mime)
+{
+    if(mime.isDigicamRaw())
+    {
+        return FILE_TYPE_RAW;
+    }
+    else if(mime.isImage())
+    {
+        return FILE_TYPE_IMAGE;
+    }
+    else
+    {
+        return FILE_TYPE_UNKNOWN;
+    }
+}
+
+
+}
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/

Added: trunk/src/db/libfile.h
==============================================================================
--- (empty file)
+++ trunk/src/db/libfile.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,132 @@
+/*
+ * niepce - db/libfile.h
+ *
+ * Copyright (C) 2007, 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __NIEPCE_DB_LIBFILE_H__
+#define __NIEPCE_DB_LIBFILE_H__
+
+#include <string>
+#include <list>
+#include <boost/shared_ptr.hpp>
+#include <boost/filesystem/path.hpp>
+
+#include "framework/mimetype.h"
+#include "db/keyword.h"
+#include "db/storage.h"
+
+namespace db {
+
+
+class LibFile
+{
+public:
+    typedef boost::shared_ptr< LibFile > Ptr;
+    typedef std::list< Ptr > List;
+    typedef boost::shared_ptr< List > ListPtr;
+
+    enum FileType {
+        FILE_TYPE_UNKNOWN = 0,
+        FILE_TYPE_RAW = 1,
+        FILE_TYPE_RAW_JPEG = 2,
+        FILE_TYPE_IMAGE = 3,
+        FILE_TYPE_VIDEO = 4
+    };
+
+    static FileType mimetype_to_filetype(framework::MimeType mime);
+
+    LibFile(int id, int folderId, const boost::filesystem::path & p,
+            const std::string & name );
+    virtual ~LibFile();
+
+    int id() const
+        { return m_id; }
+    int folderId() const
+        { return m_folderId; }
+    const std::string & name() const
+        { return m_name; }
+    const boost::filesystem::path & path() const
+        { return m_path; }
+
+//		Storage::Ptr storage() const;
+
+    void setOrientation(int32_t v);
+    int32_t orientation() const
+        { return m_orientation; }
+    void setRating(int32_t v);
+    int32_t rating() const
+        { return m_rating; }
+    void setLabel(int32_t v);
+    int32_t label() const
+        { return m_label; }
+
+    /** Setter for the filetype.
+     * @param v the FILETYPE of the file
+     */
+    void setFileType(FileType v);
+    /** Getter for the filetype enumeration. */
+    FileType fileType() const
+        { return m_file_type; }
+    
+    /** set an arbitrary meta data value */
+    void setMetaData(int meta, int32_t v); 
+
+    /** retrieve the keywords id list 
+     * @return the list
+     */
+    const Keyword::IdList & keywords() const;
+		
+    /** return an URI of the real path
+     * because the Gtk stuff want that.
+     */
+    const std::string uri() const
+        { return std::string("file://") + m_path.string(); }
+    /** check is the library file is at uri
+     * @return true of the uri match
+     * @todo
+     */
+//		bool isUri(const char * _uri) const
+//			{ return uri() == _uri; }
+private:
+    int         m_id;           /**< file ID */
+    int         m_folderId;     /**< parent folder */
+    std::string m_name;         /**< name */
+    boost::filesystem::path  m_path;/**< path name relative to the folder */
+//		std::string m_type;
+    int32_t     m_orientation;  /**< Exif orientatoin */
+    int32_t     m_rating;       /**< rating */
+    int32_t     m_label;        /**< Label ID */
+    FileType    m_file_type;    /**< File type */
+    mutable bool m_hasKeywordList;
+    mutable Keyword::IdList m_keywordList;
+};
+
+}
+
+
+#endif
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/db/libfolder.h
==============================================================================
--- (empty file)
+++ trunk/src/db/libfolder.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,52 @@
+/*
+ * niepce - db/libfolder.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+#ifndef __NIEPCE_DB_LIBFOLDER_H__
+#define __NIEPCE_DB_LIBFOLDER_H__
+
+#include <string>
+#include <list>
+#include <boost/shared_ptr.hpp>
+
+namespace db {
+
+	class LibFolder
+	{
+	public:
+		typedef boost::shared_ptr< LibFolder > Ptr;
+		typedef std::list< Ptr > List;
+		typedef boost::shared_ptr< List > ListPtr;
+
+		LibFolder(int _id, std::string _name)
+			: m_id(_id), m_name(_name)
+			{
+			}
+		int id() const
+			{ return m_id; }
+		const std::string & name() const
+			{ return m_name; }
+	private:
+		int         m_id;
+		std::string m_name;
+	};
+}
+
+#endif

Added: trunk/src/db/libmetadata.cpp
==============================================================================
--- (empty file)
+++ trunk/src/db/libmetadata.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,120 @@
+/*
+ * niepce - db/libmetadata.cpp
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <time.h>
+#include <exempi/xmpconsts.h>
+
+#include "utils/debug.h"
+#include "libmetadata.h"
+
+namespace db {
+
+
+LibMetadata::LibMetadata(int _id)
+	: XmpMeta(),
+      m_id(_id)
+{
+}
+
+namespace {
+
+bool xmpPropertyNameFromIndex(int meta, std::string & ns, std::string & property)
+{
+    bool found = true;
+    switch(meta) {
+    case MAKE_METADATA_IDX(db::META_NS_XMPCORE, db::META_XMPCORE_RATING):
+        ns = NS_XAP;
+        property = "Rating";
+        break;
+    case MAKE_METADATA_IDX(db::META_NS_XMPCORE, db::META_XMPCORE_LABEL):
+        ns = NS_XAP;
+        property = "Label";
+        break;
+    case MAKE_METADATA_IDX(db::META_NS_TIFF, db::META_TIFF_ORIENTATION):
+        ns = NS_TIFF;
+        property = "Orientation";
+        break;
+    default:
+        found = false;
+        break;
+    }
+   
+    return found;
+}
+
+}
+
+bool LibMetadata::setMetaData(int meta, const boost::any & value)
+{
+    std::string ns;
+    std::string property;
+    bool result = false;
+
+    result = xmpPropertyNameFromIndex(meta, ns, property);
+    if(result) {
+        if(value.type() == typeid(int32_t)) {
+            result = xmp_set_property_int32(xmp(), ns.c_str(), property.c_str(), 
+                                            boost::any_cast<int32_t>(value), 0);
+        }
+        else if(value.type() == typeid(std::string)) {
+            result = xmp_set_property(xmp(), ns.c_str(), property.c_str(), 
+                                      boost::any_cast<std::string>(value).c_str(), 0);
+        }
+    }
+    else {
+        ERR_OUT("unknown property");
+    }
+    return result;
+}
+
+bool LibMetadata::touch()
+{
+    bool result = false;
+    XmpDateTime date;
+    struct tm dt, *dt2;
+    time_t currenttime = time(NULL);
+    dt2 = gmtime_r(&currenttime, &dt);
+    if(dt2 == &dt) {
+        memset(&date, 0, sizeof date);
+        date.second = dt.tm_sec;
+        date.minute = dt.tm_min;
+        date.hour = dt.tm_hour;
+        date.day = dt.tm_mday;
+        date.month = dt.tm_mon;
+        date.year = dt.tm_year + 1900;
+
+        result = xmp_set_property_date(xmp(), NS_XAP, "MetadataDate",
+                                       &date, 0);
+    }
+    return result;
+}
+
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/db/libmetadata.h
==============================================================================
--- (empty file)
+++ trunk/src/db/libmetadata.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,65 @@
+/*
+ * niepce - db/libmetadata.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __DB_LIBMETADATA_H_
+#define __DB_LIBMETADATA_H_
+
+#include <vector>
+#include <string>
+#include <boost/shared_ptr.hpp>
+#include <boost/any.hpp>
+
+#include "utils/exempi.h"
+#include "db/metadata.h"
+
+namespace db {
+
+	class LibMetadata
+		: public utils::XmpMeta
+	{
+	public:
+		typedef boost::shared_ptr<LibMetadata> Ptr;
+
+		LibMetadata(int _id);
+
+        int id() const
+            { return m_id; }
+        bool setMetaData(int meta, const boost::any & value);
+        /** do like the unix "touch". Update the MetadataDate 
+         * to the current time, in UTC.
+         */
+        bool touch();
+    private:
+        LibMetadata(const LibMetadata &);
+        int m_id;
+	};
+
+}
+
+#endif
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/db/library.cpp
==============================================================================
--- (empty file)
+++ trunk/src/db/library.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,724 @@
+/*
+ * niepce - db/library.cpp
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <time.h>
+#include <stdio.h>
+#include <iostream>
+
+#include <boost/lexical_cast.hpp>
+#include <boost/format.hpp>
+#include <boost/bind.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/filesystem/convenience.hpp>
+
+#include "niepce/notifications.h"
+#include "library.h"
+#include "metadata.h"
+#include "utils/exception.h"
+#include "utils/exempi.h"
+#include "utils/db/sqlite/sqlitecnxmgrdrv.h"
+#include "utils/db/sqlite/sqlitecnxdrv.h"
+#include "utils/db/sqlstatement.h"
+#include "framework/notificationcenter.h"
+#include "framework/mimetype.h"
+
+using framework::NotificationCenter;
+
+namespace bfs = boost::filesystem;
+
+namespace db {
+
+const char * s_databaseName = "niepcelibrary.db";
+
+
+
+Library::Library(const std::string & dir, const NotificationCenter::Ptr & nc)
+    : m_maindir(dir),
+      m_dbname(m_maindir / s_databaseName),
+      m_dbmgr(new db::sqlite::SqliteCnxMgrDrv()),
+      m_notif_center(nc),
+      m_inited(false)
+{
+    DBG_OUT("dir = %s", dir.c_str());
+    db::DBDesc desc("", 0, m_dbname.string());
+    m_dbdrv = m_dbmgr->connect_to_db(desc, "", "");
+    m_inited = init();
+
+    m_dbdrv->create_function0("rewrite_xmp", 
+                              boost::bind(&Library::triggerRewriteXmp,
+                                          this));
+}
+
+Library::~Library()
+{
+}
+
+void Library::triggerRewriteXmp(void)
+{
+    DBG_OUT("rewrite_xmp");
+    notify(NOTIFY_XMP_NEEDS_UPDATE, boost::any());
+}
+
+void Library::notify(NotifyType t, const boost::any & param)
+{
+    framework::NotificationCenter::Ptr nc(m_notif_center.lock());
+    if(nc) {
+        DBG_OUT("notif");
+        // pass the notification
+        framework::Notification::Ptr n(new framework::Notification(niepce::NOTIFICATION_LIB));
+        framework::Notification::mutex_t::scoped_lock lock(n->mutex());
+        LibNotification ln;
+        ln.type = t;
+        ln.param = param;
+        n->setData(boost::any(ln));
+        nc->post(n);
+    }
+    else {
+        DBG_OUT("try to send a notification without notification center");
+    }
+}
+
+/** init the database
+ * @return true is the DB is inited. false if it fail.
+ */
+bool Library::init()
+{
+    int version = checkDatabaseVersion();
+    if(version == -1) {
+        // error
+        DBG_OUT("version check -1");
+    }
+    else if(version == 0) {
+        // let's create our DB
+        DBG_OUT("version == 0");
+        return _initDb();
+    }
+    return true;
+}
+
+bool Library::_initDb()
+{
+    SQLStatement adminTable("CREATE TABLE admin (key TEXT NOT NULL,"
+                            " value TEXT)");
+    SQLStatement adminVersion("INSERT INTO admin (key, value) "
+                              " VALUES ('version', '1')");
+    SQLStatement vaultTable("CREATE TABLE vaults (id INTEGER PRIMARY KEY,"
+                            " path TEXT)");
+    SQLStatement folderTable("CREATE TABLE folders (id INTEGER PRIMARY KEY,"
+                             " path TEXT, name TEXT, vault_id INTEGER, "
+                             " parent_id INTEGER)");
+    SQLStatement fileTable("CREATE TABLE files (id INTEGER PRIMARY KEY,"
+                           " path TEXT, name TEXT, parent_id INTEGER,"
+                           " orientation INTEGER, file_type INTEGER, "
+                           " file_date INTEGER, rating INTEGER, label INTEGER,"
+                           " import_date INTEGER, mod_date INTEGER, "
+                           " xmp TEXT, xmp_date INTEGER)");
+    SQLStatement keywordTable("CREATE TABLE keywords (id INTEGER PRIMARY KEY,"
+                              " keyword TEXT, parent_id INTEGER)");
+    SQLStatement keywordingTable("CREATE TABLE keywording (file_id INTEGER,"
+                                 " keyword_id INTEGER)");
+    SQLStatement xmpUpdateQueueTable("CREATE TABLE xmp_update_queue "
+                                     " (id INTEGER UNIQUE)");
+//		SQLStatement collsTable("CREATE TABLE collections (id INTEGER PRIMARY KEY,"
+//								" name TEXT)");
+//		SQLStatement collectingTable("CREATE TABLE collecting (file_id INTEGER,"
+//									 " collection_id INTEGER)");
+
+    SQLStatement fileUpdateTrigger(
+        "CREATE TRIGGER file_update_trigger UPDATE ON files "
+        " BEGIN"
+        "  UPDATE files SET mod_date = strftime('%s','now');"
+        " END");
+    SQLStatement xmpUpdateTrigger(
+        "CREATE TRIGGER xmp_update_trigger UPDATE OF xmp ON files "
+        " BEGIN"
+        "  INSERT OR IGNORE INTO xmp_update_queue (id) VALUES(new.id);"
+        "  SELECT rewrite_xmp(); "
+        " END");
+
+    m_dbdrv->execute_statement(adminTable);
+    m_dbdrv->execute_statement(adminVersion);
+    m_dbdrv->execute_statement(vaultTable);
+    m_dbdrv->execute_statement(folderTable);
+    m_dbdrv->execute_statement(fileTable);
+    m_dbdrv->execute_statement(keywordTable);
+    m_dbdrv->execute_statement(keywordingTable);
+    m_dbdrv->execute_statement(xmpUpdateQueueTable);
+//		m_dbdrv->execute_statement(collsTable);
+//		m_dbdrv->execute_statement(collectingTable);
+
+    m_dbdrv->execute_statement(fileUpdateTrigger);
+    m_dbdrv->execute_statement(xmpUpdateTrigger);
+    return true;
+}
+
+/** check that database verion
+ * @return the DB version. -1 in case of error. 0 is can't read it.
+ */
+int Library::checkDatabaseVersion()
+{
+    int v = 0;
+    std::string version;
+    try {
+        SQLStatement sql("SELECT value FROM admin WHERE key='version'");
+			
+        if(m_dbdrv->execute_statement(sql)) {
+            if(m_dbdrv->read_next_row() 
+               && m_dbdrv->get_column_content(0, version)) {
+                v = boost::lexical_cast<int>(version);
+            }
+        }
+    }
+    catch(const utils::Exception & e)
+    {
+        DBG_OUT("db exception %s", e.what());
+        v = -1;
+    }
+    catch(const boost::bad_lexical_cast &)
+    {
+        DBG_OUT("version is %s, can't convert to int", version.c_str());
+        v = 0;
+    }
+    catch(...)
+    {
+        v = -1;
+    }
+    return v;
+}
+
+
+int Library::addFile(int folder_id, const bfs::path & file, bool manage)
+{
+    int ret = -1;
+    DBG_ASSERT(!manage, "manage not supported");
+    DBG_ASSERT(folder_id != -1, "invalid folder ID");
+    try {
+        int32_t rating, label_id, orientation;
+        std::string label;  
+        framework::MimeType mime = framework::MimeType(file);
+        db::LibFile::FileType file_type = db::LibFile::mimetype_to_filetype(mime);
+        utils::XmpMeta meta(file, file_type == db::LibFile::FILE_TYPE_RAW);
+        label_id = 0;
+        orientation = meta.orientation();
+        rating = meta.rating();
+        label = meta.label();
+        time_t creation_date = meta.creation_date();
+        if(creation_date == -1) {
+            creation_date = 0;
+        }
+
+        SQLStatement sql(boost::format("INSERT INTO files ("
+                                       " path, name, parent_id, "
+                                       " import_date, mod_date, "
+                                       " orientation, file_date, rating, label, file_type,"
+                                       " xmp) "
+                                       " VALUES ("
+                                       " '%1%', '%2%', '%3%', "
+                                       " '%4%', '%4%',"
+                                       " '%5%', '%6%', '%7%', '%8%', '%9%',"
+                                       " ?1);") 
+                         % file.string() % file.leaf() % folder_id
+                         % time(NULL)
+                         % orientation % creation_date % rating
+                         % folder_id % file_type);
+        std::string buf = meta.serialize_inline();
+        sql.bind(1, buf);
+        if(m_dbdrv->execute_statement(sql)) {
+            int64_t id = m_dbdrv->last_row_id();
+            DBG_OUT("last row inserted %d", (int)id);
+            ret = id;
+            const std::vector< std::string > &keywords(meta.keywords());
+            std::vector< std::string >::const_iterator iter;
+            for(iter = keywords.begin();
+                iter != keywords.end(); iter++) 
+            {
+                int kwid = makeKeyword(*iter);
+                if(kwid != -1) {
+                    assignKeyword(kwid, id);
+                }
+            }
+        }
+    }
+    catch(const utils::Exception & e)
+    {
+        DBG_OUT("db exception %s", e.what());
+    }
+    catch(const std::exception & e)
+    {
+        DBG_OUT("unknown exception %s", e.what());
+    }
+    return ret;
+}
+
+
+int Library::addFileAndFolder(const bfs::path & folder, const bfs::path & file, 
+                              bool manage)
+{
+    LibFolder::Ptr f;
+    f = getFolder(folder);
+    if(f == NULL) {
+        ERR_OUT("Folder %s not found", folder.string().c_str());
+    }
+    return addFile(f ? f->id() : -1, file, manage);
+}
+	
+
+LibFolder::Ptr Library::getFolder(const bfs::path & folder)
+{
+    LibFolder::Ptr f;
+    SQLStatement sql(boost::format("SELECT id,name "
+                                   "FROM folders WHERE path='%1%'") 
+                     % folder.string());
+		
+    try {
+        if(m_dbdrv->execute_statement(sql)) {
+            if(m_dbdrv->read_next_row()) {
+                int32_t id;
+                std::string name;
+                m_dbdrv->get_column_content(0, id);
+                m_dbdrv->get_column_content(1, name);
+                f = LibFolder::Ptr(new LibFolder(id, name));
+            }
+        }
+    }
+    catch(utils::Exception & e)
+    {
+        DBG_OUT("db exception %s", e.what());
+    }
+    return f;
+}
+
+
+LibFolder::Ptr Library::addFolder(const bfs::path & folder)
+{
+    LibFolder::Ptr f;
+    SQLStatement sql(boost::format("INSERT INTO folders "
+                                   "(path,name,vault_id,parent_id) "
+                                   "VALUES('%1%', '%2%', '0', '0')") 
+                     % folder.string() % folder.leaf());
+    try {
+        if(m_dbdrv->execute_statement(sql)) {
+            int64_t id = m_dbdrv->last_row_id();
+            DBG_OUT("last row inserted %d", (int)id);
+            f = LibFolder::Ptr(new LibFolder((int)id, folder.leaf()));
+        }
+    }
+    catch(utils::Exception & e)
+    {
+        DBG_OUT("db exception %s", e.what());
+    }
+    return f;
+}
+
+
+void Library::getAllFolders(const LibFolder::ListPtr & l)
+{
+    SQLStatement sql("SELECT id,name FROM folders");
+    try {
+        if(m_dbdrv->execute_statement(sql)) {
+            while(m_dbdrv->read_next_row()) {
+                int32_t id;
+                std::string name;
+                m_dbdrv->get_column_content(0, id);
+                m_dbdrv->get_column_content(1, name);
+                l->push_back(LibFolder::Ptr(new LibFolder(id, name)));
+            }
+        }
+    }
+    catch(utils::Exception & e)
+    {
+        DBG_OUT("db exception %s", e.what());
+    }
+}
+
+static LibFile::Ptr getFileFromDbRow(const db::IConnectionDriver::Ptr & dbdrv)
+{
+    int32_t id;
+    int32_t fid;
+    std::string pathname;
+    std::string name;
+    dbdrv->get_column_content(0, id);
+    dbdrv->get_column_content(1, fid);
+    dbdrv->get_column_content(2, pathname);
+    dbdrv->get_column_content(3, name);
+    DBG_OUT("found %s", pathname.c_str());
+    LibFile::Ptr f(new LibFile(id, fid,
+                               bfs::path(pathname), 
+                               name));
+    int32_t val;
+    dbdrv->get_column_content(4, val);
+    f->setOrientation(val);
+    dbdrv->get_column_content(5, val);
+    f->setRating(val);
+    dbdrv->get_column_content(6, val);
+    f->setLabel(val);
+
+    /* Casting needed. Remember that a simple enum like this is just a couple
+     * of #define for integers.
+     */
+    dbdrv->get_column_content(7, val);
+    f->setFileType((db::LibFile::FileType)val);
+    return f;
+}
+
+void Library::getFolderContent(int folder_id, const LibFile::ListPtr & fl)
+{
+    SQLStatement sql(boost::format("SELECT id,parent_id,path,name,"
+                                   "orientation,rating,label,file_type FROM files "
+                                   " WHERE parent_id='%1%'")
+                     % folder_id);
+    try {
+        if(m_dbdrv->execute_statement(sql)) {
+            while(m_dbdrv->read_next_row()) {
+                LibFile::Ptr f(getFileFromDbRow(m_dbdrv));
+                fl->push_back(f);
+            }
+        }
+    }
+    catch(utils::Exception & e)
+    {
+        DBG_OUT("db exception %s", e.what());
+    }
+}
+
+int Library::countFolder(int folder_id)
+{
+    int count = -1;
+    SQLStatement sql(boost::format("SELECT COUNT(id) FROM files WHERE parent_id='%1%';")
+                     % folder_id);
+    try {
+        if(m_dbdrv->execute_statement(sql)) {
+            if(m_dbdrv->read_next_row()) {
+                m_dbdrv->get_column_content(0, count);
+            }
+        }			
+    }
+    catch(utils::Exception & e)
+    {
+        DBG_OUT("db exception %s", e.what());
+    }
+    return count;
+}
+
+void Library::getAllKeywords(const Keyword::ListPtr & l)
+{
+    SQLStatement sql("SELECT id,keyword FROM keywords ORDER BY keyword");
+    try {
+        if(m_dbdrv->execute_statement(sql)) {
+            while(m_dbdrv->read_next_row()) {
+                int32_t id;
+                std::string name;
+                m_dbdrv->get_column_content(0, id);
+                m_dbdrv->get_column_content(1, name);
+                l->push_back(Keyword::Ptr(new Keyword(id, name)));
+            }
+        }
+    }
+    catch(utils::Exception & e)
+    {
+        DBG_OUT("db exception %s", e.what());
+    }
+}
+	
+int Library::makeKeyword(const std::string & keyword)
+{
+    int keyword_id = -1;
+    SQLStatement sql("SELECT id FROM keywords WHERE "
+                     "keyword=?1;");
+    sql.bind(1, keyword);
+    try {
+        if(m_dbdrv->execute_statement(sql)) {
+            if(m_dbdrv->read_next_row()) {
+                m_dbdrv->get_column_content(0, keyword_id);
+            }
+        }
+    }
+    catch(utils::Exception & e)
+    {
+        DBG_OUT("db exception %s", e.what());
+    }
+    if(keyword_id == -1) {
+        SQLStatement sql2("INSERT INTO keywords (keyword, parent_id) "
+                          " VALUES(?1, 0);");
+        sql2.bind(1, keyword);
+        try {
+            if(m_dbdrv->execute_statement(sql2)) {
+                keyword_id = m_dbdrv->last_row_id();
+                Keyword::Ptr kw(new Keyword(keyword_id, keyword));
+                notify(NOTIFY_ADDED_KEYWORD, boost::any(kw));
+            }
+        }
+        catch(utils::Exception & e)
+        {
+            DBG_OUT("db exception %s", e.what());
+        }
+    }
+
+    return keyword_id;
+}
+
+
+bool Library::assignKeyword(int kw_id, int file_id)
+{
+    bool ret = false;
+    SQLStatement sql(boost::format("INSERT INTO keywording (file_id, keyword_id) "
+                                   " VALUES('%1%', '%2%');") 
+                     % file_id % kw_id );
+    try {
+        ret = m_dbdrv->execute_statement(sql);
+    }
+    catch(utils::Exception & e)
+    {
+        DBG_OUT("db exception %s", e.what());
+    }
+    return ret;
+}
+
+
+void Library::getKeywordContent(int keyword_id, const LibFile::ListPtr & fl)
+{
+    SQLStatement sql(boost::format("SELECT id,parent_id,path,name,"
+                                   "orientation,rating,label FROM files "
+                                   " WHERE id IN "
+                                   " (SELECT file_id FROM keywording "
+                                   " WHERE keyword_id='%1%');")
+                     % keyword_id);
+    try {
+        if(m_dbdrv->execute_statement(sql)) {
+            while(m_dbdrv->read_next_row()) {
+                LibFile::Ptr f(getFileFromDbRow(m_dbdrv));
+                fl->push_back(f);
+            }
+        }
+    }
+    catch(utils::Exception & e)
+    {
+        DBG_OUT("db exception %s", e.what());
+    }
+}
+
+
+void Library::getMetaData(int file_id, const LibMetadata::Ptr & meta)
+{
+    SQLStatement sql(boost::format("SELECT xmp FROM files "
+                                   " WHERE id='%1%';")
+                     % file_id);
+    try {
+        if(m_dbdrv->execute_statement(sql)) {
+            while(m_dbdrv->read_next_row()) {
+                std::string xml;
+                m_dbdrv->get_column_content(0, xml);
+                meta->unserialize(xml.c_str());
+            }
+        }
+    }
+    catch(utils::Exception & e)
+    {
+        DBG_OUT("db exception %s", e.what());
+    }
+}
+
+
+
+
+bool Library::setInternalMetaDataInt(int file_id, const char* col,
+                                     int32_t value)
+{
+    bool ret = false;
+    DBG_OUT("setting metadata in column %s", col);
+    SQLStatement sql(boost::format("UPDATE files SET %1%='%2%' "
+                                   " WHERE id='%3%';")
+                     % col % value % file_id);
+    try {
+        ret = m_dbdrv->execute_statement(sql);
+    }
+    catch(utils::Exception & e)
+    {
+        DBG_OUT("db exception %s", e.what());
+        ret = false;
+    }
+    return ret;
+}
+
+/** set metadata block
+ * @param file_id the file ID to set the metadata block
+ * @param meta the metadata block
+ * @return false on error
+ */
+bool Library::setMetaData(int file_id, const LibMetadata::Ptr & meta)
+{
+    bool ret = false;
+    SQLStatement sql(boost::format("UPDATE files SET xmp=?1 "
+                                   " WHERE id='%1%';")
+                     % file_id);
+    sql.bind(1, meta->serialize_inline());
+    try {
+        ret = m_dbdrv->execute_statement(sql);
+    }
+    catch(utils::Exception & e)
+    {
+        DBG_OUT("db exception %s", e.what());
+        ret = false;
+    }
+    return ret;    
+}
+
+
+/** set metadata 
+ * @param file_id the file ID to set the metadata block
+ * @param meta the metadata index
+ * @param value the value to set
+ * @return false on error
+ */
+bool Library::setMetaData(int file_id, int meta, 
+                          const boost::any & value)
+{
+    bool retval = false;
+    DBG_OUT("setting metadata in column %x", meta);
+    switch(meta) {
+    case MAKE_METADATA_IDX(db::META_NS_XMPCORE, db::META_XMPCORE_RATING):
+    case MAKE_METADATA_IDX(db::META_NS_XMPCORE, db::META_XMPCORE_LABEL):
+    case MAKE_METADATA_IDX(db::META_NS_TIFF, db::META_TIFF_ORIENTATION):
+        if(value.type() == typeid(int32_t)) {
+            // internal.
+            int32_t nvalue = boost::any_cast<int32_t>(value);
+            // make the column mapping more generic.
+            const char * col = NULL;
+            switch(meta) {
+            case MAKE_METADATA_IDX(db::META_NS_XMPCORE, db::META_XMPCORE_RATING):
+                col = "rating";
+                break;
+            case MAKE_METADATA_IDX(db::META_NS_TIFF, db::META_TIFF_ORIENTATION):
+                col = "orientation";
+                break;
+            case MAKE_METADATA_IDX(db::META_NS_XMPCORE, db::META_XMPCORE_LABEL):
+                col = "label";
+                break;
+            }
+            if(col) {
+                retval = setInternalMetaDataInt(file_id, col, nvalue);
+            }
+        }
+        break;
+    default:
+        // external.
+        ERR_OUT("unknown metadata to set");
+        return false;
+    }
+    LibMetadata::Ptr metablock(new LibMetadata(file_id));
+    getMetaData(file_id, metablock);
+    retval = metablock->setMetaData(meta, value);
+    retval = metablock->touch();
+    retval = setMetaData(file_id, metablock);
+    return retval;
+}
+
+
+bool Library::getXmpIdsInQueue(std::vector<int> & ids)
+{
+    SQLStatement sql("SELECT id  FROM xmp_update_queue;");
+    try {
+        if(m_dbdrv->execute_statement(sql)) {
+            while(m_dbdrv->read_next_row()) {
+                int32_t id;
+                m_dbdrv->get_column_content(0, id);
+                ids.push_back(id);
+            }
+        }
+    }
+    catch(utils::Exception & e)
+    {
+        DBG_OUT("db exception %s", e.what());
+        return false;
+    }
+    return true;
+}
+
+
+bool Library::rewriteXmpForId(int id)
+{
+    SQLStatement del(boost::format("DELETE FROM xmp_update_queue "
+                                   " WHERE id='%1%';") % id);
+    SQLStatement getxmp(boost::format("SELECT xmp, path FROM files "
+                                   " WHERE id='%1%';") % id);
+    try {
+        
+        if(m_dbdrv->execute_statement(del) 
+           && m_dbdrv->execute_statement(getxmp)) {
+            while(m_dbdrv->read_next_row()) {
+                std::string xmp_buffer;
+                std::string spath;
+                m_dbdrv->get_column_content(0, xmp_buffer);
+                m_dbdrv->get_column_content(1, spath);
+                boost::filesystem::path p;
+                p = boost::filesystem::change_extension(spath, ".xmp");
+                DBG_ASSERT(p.string() != spath, "path must have been changed");
+                if(exists(p)) {
+                    DBG_OUT("%s already exist", p.string().c_str());
+                    // TODO backup
+                }
+                // TODO probably a faster way to do that
+                utils::XmpMeta xmppacket;
+                xmppacket.unserialize(xmp_buffer.c_str());
+                // TODO use different API
+                FILE * f = fopen(p.string().c_str(), "w");
+                if(f) {
+                    std::string sidecar = xmppacket.serialize();
+                    fwrite(sidecar.c_str(), sizeof(std::string::value_type),
+                           sidecar.size(), f);
+                    fclose(f);
+                }
+                // TODO rewrite the modified date in the files table
+                // caveat: this will trigger this rewrite recursively.
+            }
+        }
+    }
+    catch(utils::Exception & e)
+    {
+        DBG_OUT("db exception %s", e.what());
+        return false;
+    }    
+    return true;
+}
+
+
+bool Library::processXmpUpdateQueue()
+{
+    bool retval = false;
+    std::vector<int> ids;
+    retval = getXmpIdsInQueue(ids);
+    if(retval) {
+        std::for_each(ids.begin(), ids.end(),
+                     boost::bind(&Library::rewriteXmpForId,
+                                 this, _1));
+    }
+    return retval;
+}
+
+
+}
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/db/library.h
==============================================================================
--- (empty file)
+++ trunk/src/db/library.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,185 @@
+/*
+ * niepce - db/library.h
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+#ifndef _DB_LIBRARY_H_
+#define _DB_LIBRARY_H_
+
+#include <string>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include <boost/any.hpp>
+#include <boost/filesystem/path.hpp>
+
+#include "framework/notificationcenter.h"
+#include "utils/db/iconnectiondriver.h"
+#include "utils/db/iconnectionmanagerdriver.h"
+#include "db/libfolder.h"
+#include "db/libfile.h"
+#include "db/libmetadata.h"
+#include "db/keyword.h"
+
+
+namespace db {
+
+	class Library
+	{
+	public:
+		typedef boost::shared_ptr<Library> Ptr;
+
+		typedef enum {
+			NOTIFY_NONE = 0,
+			NOTIFY_ADDED_FOLDERS,
+			NOTIFY_ADDED_FILES,
+			NOTIFY_ADDED_KEYWORDS,
+			NOTIFY_ADDED_KEYWORD,
+			NOTIFY_FOLDER_CONTENT_QUERIED,
+			NOTIFY_KEYWORD_CONTENT_QUERIED,
+			NOTIFY_METADATA_QUERIED,
+            NOTIFY_METADATA_CHANGED,
+            NOTIFY_XMP_NEEDS_UPDATE,
+			NOTIFY_FOLDER_COUNTED
+		} NotifyType;
+
+		Library(const std::string & dir, const framework::NotificationCenter::Ptr & nc);
+		virtual ~Library();
+
+		bool ok()
+			{ return m_inited; }
+		/** set the main library directory */
+//		void setMainDir(const std::string & dir)
+//			{ m_maindir = dir; }
+		/** return the main directory */
+		const boost::filesystem::path & mainDir() const
+			{ return m_maindir; }
+		/** get the path to the DB file */
+		const boost::filesystem::path & dbName() const
+			{ return m_dbname; }
+
+		void notify(NotifyType t, const boost::any & param);
+
+		/** add a file to the library
+		 * @param folder the path of the containing folder
+		 * @param file the file path
+		 * @param manage pass true it the library *manage* the file. Currently unsupported.
+		 */
+		int addFileAndFolder(const boost::filesystem::path & folder, 
+							 const boost::filesystem::path & file, bool manage);
+		/** add a file to the library
+		 * @param folder_id the id of the containing folder
+		 * @param file the file path
+		 * @param manage pass true it the library *manage* the file. Currently unsupported.
+		 */
+		int addFile(int folder_id, const boost::filesystem::path & file, 
+					bool manage);
+		
+		/** Get a specific folder id from the library
+		 * @param folder the folder path to check
+		 * @return the folder, NULL if not found
+		 */
+		LibFolder::Ptr getFolder(const boost::filesystem::path & folder);
+		/** Add a folder
+		 * @param folder the folder path
+		 */
+		LibFolder::Ptr addFolder(const boost::filesystem::path & folder);
+		/** List all the folders.
+		 * @param l the list of LibFolder
+		 */
+		void getAllFolders(const LibFolder::ListPtr & l);
+
+		/** List the folder content
+		 * @param folder_id id of the folder
+		 * @param fl the resulting file list
+		 */
+		void getFolderContent(int folder_id, const LibFile::ListPtr & fl);
+		int countFolder(int folder_id);
+		void getAllKeywords(const Keyword::ListPtr & l);
+		void getKeywordContent(int keyword_id, const LibFile::ListPtr & fl);
+        /** get the metadata block (XMP) */
+		void getMetaData(int file_id, const LibMetadata::Ptr & );
+        /** set the metadata block (XMP) */
+        bool setMetaData(int file_id, const LibMetadata::Ptr & );
+        bool setMetaData(int file_id, int meta, const boost::any & value);
+
+        /** Trigger the processing of the XMP update queue */
+        bool processXmpUpdateQueue();
+
+		/** Locate the keyword, creating it if needed
+		 * @param keyword the keyword to locate
+		 * @return -1 if not found (shouldn't happen) or the id of the
+		 * keyword, either found or just created.
+		 */
+		int makeKeyword(const std::string & keyword);
+		/** Assign a keyword to a file.
+		 * @param kw_id the keyword id
+		 * @param file_id the file id
+		 * @return true if success, false if error
+		 */
+		bool assignKeyword(int kw_id, int file_id);
+
+		int checkDatabaseVersion();
+		
+		db::IConnectionDriver::Ptr dbDriver()
+			{ return m_dbdrv; }
+	private:
+		bool init();
+		bool _initDb();
+
+        /** external sqlite fucntion to trigger the rewrite of the XMP */
+        void triggerRewriteXmp(void);
+        bool getXmpIdsInQueue(std::vector<int> & ids);
+        /** rewrite the XMP sidecar for the file whose id is %id
+         * and remove it from the queue.
+         */
+        bool rewriteXmpForId(int id);
+
+        /** set an "internal" metadata of type int */
+        bool setInternalMetaDataInt(int file_id, const char* col,
+                                    int32_t value);
+
+		boost::filesystem::path           m_maindir;
+		boost::filesystem::path           m_dbname;
+		db::IConnectionManagerDriver::Ptr m_dbmgr;
+		db::IConnectionDriver::Ptr        m_dbdrv;
+		boost::weak_ptr<framework::NotificationCenter>  m_notif_center;
+		bool                              m_inited;
+	};
+
+	
+	struct LibNotification
+	{
+		Library::NotifyType type;
+		boost::any          param;
+	};
+
+}
+
+#endif
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+

Added: trunk/src/db/metadata.h
==============================================================================
--- (empty file)
+++ trunk/src/db/metadata.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,67 @@
+/*
+ * niepce - db/metadata.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+#ifndef _DB_METADATA_H_
+#define _DB_METADATA_H_
+
+namespace db {
+
+enum {
+    META_NS_XMPCORE = 0,
+    META_NS_TIFF,
+    META_NS_EXIF
+};
+
+/** Metadata for xmpcore. Combine with %META_NS_XMPCORE */
+enum {
+    META_XMPCORE_RATING = 0,
+    META_XMPCORE_LABEL
+};
+
+/** Metadata for tiff. Combine with %META_NS_TIFF */
+enum {
+    META_TIFF_ORIENTATION = 0
+};
+
+enum {
+
+};
+
+/** make a metadata index with a namespace and a value */
+#define MAKE_METADATA_IDX(x,y) (x<<16|y)
+/** get the NS form the metadata index */
+#define METADATA_NS(x) (x >>16)
+/** get the data from the metadata index */
+#define METADATA_DATA(x) (x & 0xffff)
+
+}
+
+
+#endif
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/db/storage.cpp
==============================================================================
--- (empty file)
+++ trunk/src/db/storage.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,29 @@
+/*
+ * niepce - db/storage.cpp
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "storage.h"
+
+namespace db {
+
+	Storage::~Storage()
+	{
+
+	}
+
+}

Added: trunk/src/db/storage.h
==============================================================================
--- (empty file)
+++ trunk/src/db/storage.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,43 @@
+/*
+ * niepce - db/storage.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __NIEPCE_LIBRARY_STORAGE_H__
+#define __NIEPCE_LIBRARY_STORAGE_H__
+
+#include <boost/shared_ptr.hpp>
+
+#include "db/keyword.h"
+
+namespace db {
+
+	/** @brief the interface for a storage */
+	class Storage
+	{
+	public:
+		typedef boost::shared_ptr<Storage> Ptr;
+
+		virtual ~Storage();
+
+		virtual bool fetchKeywordsForFile(int file, Keyword::IdList &keywords) = 0;
+	};
+
+}
+
+#endif

Added: trunk/src/db/test_library.cpp
==============================================================================
--- (empty file)
+++ trunk/src/db/test_library.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,53 @@
+/*
+ * niepce - db/test_library.cpp
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  
+ * 02110-1301, USA
+ */
+
+
+#include "utils/db/sqlstatement.h"
+#include "utils/db/iconnectiondriver.h"
+#include "library.h"
+#include "libfile.h"
+#include "libfolder.h"
+
+#define BOOST_AUTO_TEST_MAIN
+#include <boost/test/auto_unit_test.hpp>
+
+
+BOOST_AUTO_TEST_CASE(library_test)
+{
+	db::Library lib("./", framework::NotificationCenter::Ptr());
+
+	BOOST_CHECK_EQUAL(lib.checkDatabaseVersion(), 1);
+
+	db::IConnectionDriver::Ptr db(lib.dbDriver());
+	
+	lib.addFolder("foo");
+	db::LibFolder::Ptr f(lib.getFolder("foo"));
+	BOOST_CHECK(f);
+	lib.addFolder("bar");
+	BOOST_CHECK(lib.getFolder("bar"));
+
+	db::LibFolder::ListPtr l( new db::LibFolder::List );
+	lib.getAllFolders( l );
+	BOOST_CHECK( l->size() == 2 );
+
+
+	BOOST_CHECK(unlink(lib.dbName().string().c_str()) != -1);
+}

Added: trunk/src/ext/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/ext/Makefile.am	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,5 @@
+
+EXTRA_DIST = README
+
+SUBDIRS = libgdl
+DIST_SUBDIRS =

Added: trunk/src/ext/README
==============================================================================
--- (empty file)
+++ trunk/src/ext/README	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,7 @@
+This directory contains external code:
+
+libgdl/
+  - copied from Inskscape has it has more feature then upstream gdl.
+    last resync r19169
+    https://inkscape.svn.sourceforge.net/svnroot/inkscape/inkscape/trunk/src/libgdl/gdl-stock.h
+

Added: trunk/src/ext/libgdl/CMakeLists.txt
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/CMakeLists.txt	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,42 @@
+IF(WIN32)
+SET(GDL_WIN
+gdl-win32.c
+gdl-win32.h)
+ENDIF(WIN32)
+
+SET(libgdl_SRC
+gdl-dock.c
+gdl-dock.h
+gdl-dock-bar.c
+gdl-dock-bar.h
+gdl-dock-item.c
+gdl-dock-item.h
+gdl-dock-item-grip.c
+gdl-dock-item-grip.h
+gdl-dock-master.c
+gdl-dock-master.h
+gdl-dock-notebook.c
+gdl-dock-notebook.h
+gdl-dock-object.c
+gdl-dock-object.h
+gdl-dock-paned.c
+gdl-dock-paned.h
+gdl-dock-placeholder.c
+gdl-dock-placeholder.h
+gdl-dock-tablabel.c
+gdl-dock-tablabel.h
+gdl-i18n.c
+gdl-i18n.h
+gdl-stock.c
+gdl-stock.h
+gdl-stock-icons.h
+gdl-switcher.c
+gdl-switcher.h
+gdl-tools.h
libgdl.h
+libgdlmarshal.c
+libgdlmarshal.h
+libgdltypebuiltins.c
+libgdltypebuiltins.h
+${GDL_WIN}
+)
+ADD_LIBRARY(gdl STATIC ${libgdl_SRC})

Added: trunk/src/ext/libgdl/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/Makefile.am	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,41 @@
+
+
+noinst_LIBRARIES = libgdl.a
+
+AM_CPPFLAGS = @LIBGTKMM_CFLAGS@ -I$(srcdir)/..
+
+libgdl_a_SOURCES =	\
+	gdl-tools.h		\
+	gdl-dock-object.h	\
+	gdl-dock-master.h	\
+	gdl-dock.h		\
+	gdl-dock-item.h		\
+	gdl-dock-notebook.h	\
+	gdl-dock-paned.h	        \
+	gdl-dock-tablabel.h	\
+	gdl-dock-placeholder.h	\
+	gdl-dock-bar.h		\
+	gdl-stock.h		\
+	gdl-stock-icons.h	\
+	gdl-i18n.h		\
+	gdl-i18n.c		\
+	gdl-dock-object.c	\
+	gdl-dock-master.c	\
+	gdl-dock.c		\
+	gdl-dock-item.c		\
+	gdl-dock-item-grip.h	\
+	gdl-dock-item-grip.c	\
+	gdl-dock-notebook.c	\
+	gdl-dock-paned.c		\
+	gdl-dock-tablabel.c	\
+	gdl-dock-placeholder.c	\
+	gdl-dock-bar.c		\
+	gdl-stock.c		\
+	gdl-switcher.h		\
+	gdl-switcher.c		\
+	libgdltypebuiltins.h	\
+	libgdltypebuiltins.c	\
+	libgdlmarshal.h		\
+	libgdlmarshal.c		\
+	libgdl.h		
+

Added: trunk/src/ext/libgdl/Makefile_insert
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/Makefile_insert	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,41 @@
+## 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-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-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		

Added: trunk/src/ext/libgdl/README.gdl-dock
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/README.gdl-dock	Wed Dec 10 19:46:36 2008
@@ -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)
+
+

Added: trunk/src/ext/libgdl/gdl-dock-bar.c
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-dock-bar.c	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,976 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 
+ *
+ * This file is part of the GNOME Devtools Libraries.
+ *
+ * Copyright (C) 2003 Jeroen Zwartepoorte <jeroen xs4all nl>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gdl-i18n.h"
+#include <stdlib.h>
+#include <string.h>
+
+#include "gdl-tools.h"
+#include "gdl-dock.h"
+#include "gdl-dock-master.h"
+#include "gdl-dock-bar.h"
+#include "libgdltypebuiltins.h"
+
+enum {
+    PROP_0,
+    PROP_MASTER,
+    PROP_DOCKBAR_STYLE
+};
+
+/* ----- Private prototypes ----- */
+
+static void  gdl_dock_bar_class_init      (GdlDockBarClass *klass);
+static void  gdl_dock_bar_instance_init   (GdlDockBar      *dockbar);
+
+static void  gdl_dock_bar_get_property    (GObject         *object,
+                                           guint            prop_id,
+                                           GValue          *value,
+                                           GParamSpec      *pspec);
+static void  gdl_dock_bar_set_property    (GObject         *object,
+                                           guint            prop_id,
+                                           const GValue    *value,
+                                           GParamSpec      *pspec);
+
+static void  gdl_dock_bar_destroy         (GtkObject       *object);
+
+static void  gdl_dock_bar_attach          (GdlDockBar      *dockbar,
+                                           GdlDockMaster   *master);
+static void gdl_dock_bar_remove_item      (GdlDockBar      *dockbar,
+                                           GdlDockItem     *item);
+
+/* ----- Class variables and definitions ----- */
+
+struct _GdlDockBarPrivate {
+    GdlDockMaster   *master;
+    GSList          *items;
+    GtkTooltips     *tooltips;
+    GtkOrientation   orientation;
+    GdlDockBarStyle  dockbar_style;
+};
+
+/* ----- Private functions ----- */
+
+GDL_CLASS_BOILERPLATE (GdlDockBar, gdl_dock_bar, GtkBox, GTK_TYPE_BOX)
+
+static void gdl_dock_bar_size_request (GtkWidget *widget,
+		                       GtkRequisition *requisition );
+static void gdl_dock_bar_size_allocate (GtkWidget *widget,
+		                       GtkAllocation *allocation );
+static void gdl_dock_bar_size_vrequest (GtkWidget *widget,
+		                       GtkRequisition *requisition );
+static void gdl_dock_bar_size_vallocate (GtkWidget *widget,
+		                       GtkAllocation *allocation );
+static void gdl_dock_bar_size_hrequest (GtkWidget *widget,
+		                       GtkRequisition *requisition );
+static void gdl_dock_bar_size_hallocate (GtkWidget *widget,
+		                       GtkAllocation *allocation );
+static void update_dock_items (GdlDockBar *dockbar, gboolean full_update);
+
+void
+gdl_dock_bar_class_init (GdlDockBarClass *klass)
+{
+    GObjectClass       *g_object_class;
+    GtkObjectClass     *gtk_object_class;
+    GtkWidgetClass     *widget_class;
+    
+    g_object_class = G_OBJECT_CLASS (klass);
+    gtk_object_class = GTK_OBJECT_CLASS (klass);
+
+    g_object_class->get_property = gdl_dock_bar_get_property;
+    g_object_class->set_property = gdl_dock_bar_set_property;
+
+    gtk_object_class->destroy = gdl_dock_bar_destroy;
+
+    g_object_class_install_property (
+        g_object_class, PROP_MASTER,
+        g_param_spec_object ("master", _("Master"),
+                             _("GdlDockMaster object which the dockbar widget "
+                               "is attached to"),
+                             GDL_TYPE_DOCK_MASTER, 
+                             G_PARAM_READWRITE));
+
+    g_object_class_install_property (
+        g_object_class, PROP_DOCKBAR_STYLE,
+        g_param_spec_enum ("dockbar-style", _("Dockbar style"),
+                           _("Dockbar style to show items on it"),
+                           GDL_TYPE_DOCK_BAR_STYLE,
+                           GDL_DOCK_BAR_BOTH,
+                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+    widget_class = GTK_WIDGET_CLASS (klass);
+    widget_class->size_request = gdl_dock_bar_size_request;
+    widget_class->size_allocate = gdl_dock_bar_size_allocate;
+}
+
+static void
+gdl_dock_bar_instance_init (GdlDockBar *dockbar)
+{
+    dockbar->_priv = g_new0 (GdlDockBarPrivate, 1);
+    dockbar->_priv->master = NULL;
+    dockbar->_priv->items = NULL;
+    dockbar->_priv->tooltips = gtk_tooltips_new ();
+    dockbar->_priv->orientation = GTK_ORIENTATION_VERTICAL;
+    dockbar->_priv->dockbar_style = GDL_DOCK_BAR_BOTH;
+    g_object_ref (dockbar->_priv->tooltips);
+    gtk_object_sink (GTK_OBJECT (dockbar->_priv->tooltips));
+}
+
+static void
+gdl_dock_bar_get_property (GObject         *object,
+                           guint            prop_id,
+                           GValue          *value,
+                           GParamSpec      *pspec)
+{
+    GdlDockBar *dockbar = GDL_DOCK_BAR (object);
+
+    switch (prop_id) {
+        case PROP_MASTER:
+            g_value_set_object (value, dockbar->_priv->master);
+            break;
+        case PROP_DOCKBAR_STYLE:
+            g_value_set_enum (value, dockbar->_priv->dockbar_style);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    };
+}
+
+static void
+gdl_dock_bar_set_property (GObject         *object,
+                           guint            prop_id,
+                           const GValue    *value,
+                           GParamSpec      *pspec)
+{
+    GdlDockBar *dockbar = GDL_DOCK_BAR (object);
+
+    switch (prop_id) {
+        case PROP_MASTER:
+            gdl_dock_bar_attach (dockbar, g_value_get_object (value));
+            break;
+        case PROP_DOCKBAR_STYLE:
+            dockbar->_priv->dockbar_style = g_value_get_enum (value);
+            update_dock_items (dockbar, TRUE);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    };
+}
+
+static void
+on_dock_item_foreach_disconnect (GdlDockItem *item, GdlDockBar *dock_bar)
+{
+    g_signal_handlers_disconnect_by_func (item, gdl_dock_bar_remove_item,
+                                          dock_bar);
+}
+
+static void
+gdl_dock_bar_destroy (GtkObject *object)
+{
+    GdlDockBar *dockbar = GDL_DOCK_BAR (object);
+
+    if (dockbar->_priv) {
+        GdlDockBarPrivate *priv = dockbar->_priv;
+
+        if (priv->items) {
+            g_slist_foreach (priv->items,
+                             (GFunc) on_dock_item_foreach_disconnect,
+                             object);
+            g_slist_free (priv->items);
+        }
+        
+        if (priv->master) {
+            g_signal_handlers_disconnect_matched (priv->master,
+                                                  G_SIGNAL_MATCH_DATA,
+                                                  0, 0, NULL, NULL, dockbar);
+            g_object_unref (priv->master);
+            priv->master = NULL;
+        }
+
+        if (priv->tooltips) {
+            g_object_unref (priv->tooltips);
+            priv->tooltips = NULL;
+        }
+        
+        dockbar->_priv = NULL;
+
+        g_free (priv);
+    }
+    
+    GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
+}
+
+static void
+gdl_dock_bar_remove_item (GdlDockBar  *dockbar,
+                          GdlDockItem *item)
+{
+    GdlDockBarPrivate *priv;
+    GtkWidget *button;
+
+    g_return_if_fail (GDL_IS_DOCK_BAR (dockbar));
+    g_return_if_fail (GDL_IS_DOCK_ITEM (item));
+
+    priv = dockbar->_priv;
+
+    if (g_slist_index (priv->items, item) == -1) {
+        g_warning ("Item has not been added to the dockbar");
+        return;
+    }
+    
+    priv->items = g_slist_remove (priv->items, item);
+    
+    button = g_object_get_data (G_OBJECT (item), "GdlDockBarButton");
+    g_assert (button != NULL);
+    gtk_container_remove (GTK_CONTAINER (dockbar), button);
+    g_object_set_data (G_OBJECT (item), "GdlDockBarButton", NULL);
+    g_signal_handlers_disconnect_by_func (item,
+                                          G_CALLBACK (gdl_dock_bar_remove_item),
+                                          dockbar);
+}
+
+static void
+gdl_dock_bar_item_clicked (GtkWidget   *button,
+                           GdlDockItem *item)
+{
+    GdlDockBar *dockbar;
+    GdlDockObject *controller;
+
+    g_return_if_fail (item != NULL);
+    
+    dockbar = g_object_get_data (G_OBJECT (item), "GdlDockBar");
+    g_assert (dockbar != NULL);
+    g_object_set_data (G_OBJECT (item), "GdlDockBar", NULL);
+
+    controller = gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (item));
+
+    GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_ICONIFIED);
+    gdl_dock_item_show_item (item);
+    gdl_dock_bar_remove_item (dockbar, item);
+    gtk_widget_queue_resize (GTK_WIDGET (controller));
+}
+
+static void
+gdl_dock_bar_add_item (GdlDockBar  *dockbar,
+                       GdlDockItem *item)
+{
+    GdlDockBarPrivate *priv;
+    GtkWidget *button;
+    gchar *stock_id;
+    gchar *name;
+    GdkPixbuf *pixbuf_icon;
+    GtkWidget *image, *box, *label;
+
+    g_return_if_fail (GDL_IS_DOCK_BAR (dockbar));
+    g_return_if_fail (GDL_IS_DOCK_ITEM (item));
+
+    priv = dockbar->_priv;
+
+    if (g_slist_index (priv->items, item) != -1) {
+        g_warning ("Item has already been added to the dockbar");
+        return;
+    }
+
+    priv->items = g_slist_append (priv->items, item);
+    
+    /* Create a button for the item. */
+    button = gtk_button_new ();
+    gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+    
+    if (dockbar->_priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+        box = gtk_hbox_new (FALSE, 0);
+    else
+        box = gtk_vbox_new (FALSE, 0);
+    
+    g_object_get (item, "stock-id", &stock_id, "pixbuf-icon", &pixbuf_icon,
+                  "long-name", &name, NULL);
+
+    if (dockbar->_priv->dockbar_style == GDL_DOCK_BAR_TEXT ||
+        dockbar->_priv->dockbar_style == GDL_DOCK_BAR_BOTH) {
+        label = gtk_label_new (name);
+        if (dockbar->_priv->orientation == GTK_ORIENTATION_VERTICAL)
+            gtk_label_set_angle (GTK_LABEL (label), 90);
+        gtk_box_pack_start_defaults (GTK_BOX (box), label);
+    }
+    
+    /* FIXME: For now AUTO behaves same as BOTH */
+    
+    if (dockbar->_priv->dockbar_style == GDL_DOCK_BAR_ICONS ||
+        dockbar->_priv->dockbar_style == GDL_DOCK_BAR_BOTH ||
+        dockbar->_priv->dockbar_style == GDL_DOCK_BAR_AUTO) {
+        if (stock_id) {
+            image = gtk_image_new_from_stock (stock_id,
+                                              GTK_ICON_SIZE_SMALL_TOOLBAR);
+            g_free (stock_id);
+        } else if (pixbuf_icon) {
+            image = gtk_image_new_from_pixbuf (pixbuf_icon);
+        } else {
+            image = gtk_image_new_from_stock (GTK_STOCK_NEW,
+                                              GTK_ICON_SIZE_SMALL_TOOLBAR);
+        }
+        gtk_box_pack_start_defaults (GTK_BOX (box), image);
+    }
+    
+    gtk_container_add (GTK_CONTAINER (button), box);
+    gtk_box_pack_start (GTK_BOX (dockbar), button, FALSE, FALSE, 0);
+
+    gtk_tooltips_set_tip (priv->tooltips, button, name, name);
+    g_free (name);
+
+    g_object_set_data (G_OBJECT (item), "GdlDockBar", dockbar);
+    g_object_set_data (G_OBJECT (item), "GdlDockBarButton", button);
+    g_signal_connect (G_OBJECT (button), "clicked",
+                      G_CALLBACK (gdl_dock_bar_item_clicked), item);
+
+    gtk_widget_show_all (button);
+    
+    /* Set up destroy notify */
+    g_signal_connect_swapped (item, "destroy",
+                              G_CALLBACK (gdl_dock_bar_remove_item),
+                              dockbar);
+}
+
+static void
+build_list (GdlDockObject *object, GList **list)
+{
+    /* add only items, not toplevels */
+    if (GDL_IS_DOCK_ITEM (object))
+        *list = g_list_prepend (*list, object);
+}
+
+static void
+update_dock_items (GdlDockBar *dockbar, gboolean full_update)
+{
+    GdlDockMaster *master;
+    GList *items, *l;
+
+    g_return_if_fail (dockbar != NULL);
+    
+    if (!dockbar->_priv->master)
+        return;
+
+    master = dockbar->_priv->master;
+    
+    /* build items list */
+    items = NULL;
+    gdl_dock_master_foreach (master, (GFunc) build_list, &items);
+    
+    if (!full_update) {
+        for (l = items; l != NULL; l = l->next) {
+            GdlDockItem *item = GDL_DOCK_ITEM (l->data);
+            
+            if (g_slist_index (dockbar->_priv->items, item) != -1 &&
+                !GDL_DOCK_ITEM_ICONIFIED (item))
+                gdl_dock_bar_remove_item (dockbar, item);
+            else if (g_slist_index (dockbar->_priv->items, item) == -1 &&
+                GDL_DOCK_ITEM_ICONIFIED (item))
+                gdl_dock_bar_add_item (dockbar, item);
+        }
+    } else {
+        for (l = items; l != NULL; l = l->next) {
+            GdlDockItem *item = GDL_DOCK_ITEM (l->data);
+            
+            if (g_slist_index (dockbar->_priv->items, item) != -1)
+                gdl_dock_bar_remove_item (dockbar, item);
+            if (GDL_DOCK_ITEM_ICONIFIED (item))
+                gdl_dock_bar_add_item (dockbar, item);
+        }
+    }
+    g_list_free (items);
+}
+
+static void
+gdl_dock_bar_layout_changed_cb (GdlDockMaster *master,
+                                GdlDockBar    *dockbar)
+{
+    update_dock_items (dockbar, FALSE);
+}
+
+static void
+gdl_dock_bar_attach (GdlDockBar    *dockbar,
+                     GdlDockMaster *master)
+{
+    g_return_if_fail (dockbar != NULL);
+    g_return_if_fail (master == NULL || GDL_IS_DOCK_MASTER (master));
+    
+    if (dockbar->_priv->master) {
+        g_signal_handlers_disconnect_matched (dockbar->_priv->master,
+                                              G_SIGNAL_MATCH_DATA,
+                                              0, 0, NULL, NULL, dockbar);
+        g_object_unref (dockbar->_priv->master);
+    }
+    
+    dockbar->_priv->master = master;
+    if (dockbar->_priv->master) {
+        g_object_ref (dockbar->_priv->master);
+        g_signal_connect (dockbar->_priv->master, "layout-changed",
+                          G_CALLBACK (gdl_dock_bar_layout_changed_cb),
+                          dockbar);
+    }
+
+    update_dock_items (dockbar, FALSE);
+}
+
+static void gdl_dock_bar_size_request (GtkWidget *widget,
+		                       GtkRequisition *requisition )
+{
+    GdlDockBar *dockbar;
+
+    dockbar = GDL_DOCK_BAR (widget);
+    
+    /* default to vertical for unknown values */
+    switch (dockbar->_priv->orientation) {
+	case GTK_ORIENTATION_HORIZONTAL:
+		gdl_dock_bar_size_hrequest (widget, requisition);
+		break;
+	case GTK_ORIENTATION_VERTICAL:
+	default:
+		gdl_dock_bar_size_vrequest (widget, requisition);
+		break;
+    }
+}
+
+static void gdl_dock_bar_size_allocate (GtkWidget *widget,
+		                       GtkAllocation *allocation )
+{
+    GdlDockBar *dockbar;
+
+    dockbar = GDL_DOCK_BAR (widget);
+    
+    /* default to vertical for unknown values */
+    switch (dockbar->_priv->orientation) {
+	case GTK_ORIENTATION_HORIZONTAL:
+		gdl_dock_bar_size_hallocate (widget, allocation);
+		break;
+	case GTK_ORIENTATION_VERTICAL:
+	default:
+		gdl_dock_bar_size_vallocate (widget, allocation);
+		break;
+    }
+}
+
+static void gdl_dock_bar_size_vrequest (GtkWidget *widget,
+		                       GtkRequisition *requisition )
+{
+  GtkBox *box;
+  GtkBoxChild *child;
+  GtkRequisition child_requisition;
+  GList *children;
+  gint nvis_children;
+  gint height;
+
+  box = GTK_BOX (widget);
+  requisition->width = 0;
+  requisition->height = 0;
+  nvis_children = 0;
+
+  children = box->children;
+  while (children)
+    {
+      child = children->data;
+      children = children->next;
+
+      if (GTK_WIDGET_VISIBLE (child->widget))
+	{
+	  gtk_widget_size_request (child->widget, &child_requisition);
+
+	  if (box->homogeneous)
+	    {
+	      height = child_requisition.height + child->padding * 2;
+	      requisition->height = MAX (requisition->height, height);
+	    }
+	  else
+	    {
+	      requisition->height += child_requisition.height + child->padding * 2;
+	    }
+
+	  requisition->width = MAX (requisition->width, child_requisition.width);
+
+	  nvis_children += 1;
+	}
+    }
+
+  if (nvis_children > 0)
+    {
+      if (box->homogeneous)
+	requisition->height *= nvis_children;
+      requisition->height += (nvis_children - 1) * box->spacing;
+    }
+
+  requisition->width += GTK_CONTAINER (box)->border_width * 2;
+  requisition->height += GTK_CONTAINER (box)->border_width * 2;
+
+}
+
+static void gdl_dock_bar_size_vallocate (GtkWidget     *widget,
+			GtkAllocation *allocation)
+{
+  GtkBox *box;
+  GtkBoxChild *child;
+  GList *children;
+  GtkAllocation child_allocation;
+  gint nvis_children;
+  gint nexpand_children;
+  gint child_height;
+  gint height;
+  gint extra;
+  gint y;
+
+  box = GTK_BOX (widget);
+  widget->allocation = *allocation;
+
+  nvis_children = 0;
+  nexpand_children = 0;
+  children = box->children;
+
+  while (children)
+    {
+      child = children->data;
+      children = children->next;
+
+      if (GTK_WIDGET_VISIBLE (child->widget))
+	{
+	  nvis_children += 1;
+	  if (child->expand)
+	    nexpand_children += 1;
+	}
+    }
+
+  if (nvis_children > 0)
+    {
+      if (box->homogeneous)
+	{
+	  height = (allocation->height -
+		   GTK_CONTAINER (box)->border_width * 2 -
+		   (nvis_children - 1) * box->spacing);
+	  extra = height / nvis_children;
+	}
+      else if (nexpand_children > 0)
+	{
+	  height = (gint) allocation->height - (gint) widget->requisition.height;
+	  extra = height / nexpand_children;
+	}
+      else
+	{
+	  height = 0;
+	  extra = 0;
+	}
+
+      y = allocation->y + GTK_CONTAINER (box)->border_width;
+      child_allocation.x = allocation->x + GTK_CONTAINER (box)->border_width;
+      child_allocation.width = MAX (1, (gint) allocation->width - (gint) GTK_CONTAINER (box)->border_width * 2);
+
+      children = box->children;
+      while (children)
+	{
+	  child = children->data;
+	  children = children->next;
+
+	  if ((child->pack == GTK_PACK_START) && GTK_WIDGET_VISIBLE (child->widget))
+	    {
+	      if (box->homogeneous)
+		{
+		  if (nvis_children == 1)
+		    child_height = height;
+		  else
+		    child_height = extra;
+
+		  nvis_children -= 1;
+		  height -= extra;
+		}
+	      else
+		{
+		  GtkRequisition child_requisition;
+
+		  gtk_widget_get_child_requisition (child->widget, &child_requisition);
+		  child_height = child_requisition.height + child->padding * 2;
+
+		  if (child->expand)
+		    {
+		      if (nexpand_children == 1)
+			child_height += height;
+		      else
+			child_height += extra;
+
+		      nexpand_children -= 1;
+		      height -= extra;
+		    }
+		}
+
+	      if (child->fill)
+		{
+		  child_allocation.height = MAX (1, child_height - (gint)child->padding * 2);
+		  child_allocation.y = y + child->padding;
+		}
+	      else
+		{
+		  GtkRequisition child_requisition;
+
+		  gtk_widget_get_child_requisition (child->widget, &child_requisition);
+		  child_allocation.height = child_requisition.height;
+		  child_allocation.y = y + (child_height - child_allocation.height) / 2;
+		}
+
+	      gtk_widget_size_allocate (child->widget, &child_allocation);
+
+	      y += child_height + box->spacing;
+	    }
+	}
+
+      y = allocation->y + allocation->height - GTK_CONTAINER (box)->border_width;
+
+      children = box->children;
+      while (children)
+	{
+	  child = children->data;
+	  children = children->next;
+
+	  if ((child->pack == GTK_PACK_END) && GTK_WIDGET_VISIBLE (child->widget))
+	    {
+	      GtkRequisition child_requisition;
+	      gtk_widget_get_child_requisition (child->widget, &child_requisition);
+
+              if (box->homogeneous)
+                {
+                  if (nvis_children == 1)
+                    child_height = height;
+                  else
+                    child_height = extra;
+
+                  nvis_children -= 1;
+                  height -= extra;
+                }
+              else
+                {
+		  child_height = child_requisition.height + child->padding * 2;
+
+                  if (child->expand)
+                    {
+                      if (nexpand_children == 1)
+                        child_height += height;
+                      else
+                        child_height += extra;
+
+                      nexpand_children -= 1;
+                      height -= extra;
+                    }
+                }
+
+              if (child->fill)
+                {
+                  child_allocation.height = MAX (1, child_height - (gint)child->padding * 2);
+                  child_allocation.y = y + child->padding - child_height;
+                }
+              else
+                {
+		  child_allocation.height = child_requisition.height;
+                  child_allocation.y = y + (child_height - child_allocation.height) / 2 - child_height;
+                }
+
+              gtk_widget_size_allocate (child->widget, &child_allocation);
+
+              y -= (child_height + box->spacing);
+	    }
+	}
+    }
+}
+
+static void gdl_dock_bar_size_hrequest (GtkWidget *widget,
+		                       GtkRequisition *requisition )
+{
+  GtkBox *box;
+  GtkBoxChild *child;
+  GList *children;
+  gint nvis_children;
+  gint width;
+
+  box = GTK_BOX (widget);
+  requisition->width = 0;
+  requisition->height = 0;
+  nvis_children = 0;
+
+  children = box->children;
+  while (children)
+    {
+      child = children->data;
+      children = children->next;
+
+      if (GTK_WIDGET_VISIBLE (child->widget))
+	{
+	  GtkRequisition child_requisition;
+
+	  gtk_widget_size_request (child->widget, &child_requisition);
+
+	  if (box->homogeneous)
+	    {
+	      width = child_requisition.width + child->padding * 2;
+	      requisition->width = MAX (requisition->width, width);
+	    }
+	  else
+	    {
+	      requisition->width += child_requisition.width + child->padding * 2;
+	    }
+
+	  requisition->height = MAX (requisition->height, child_requisition.height);
+
+	  nvis_children += 1;
+	}
+    }
+
+  if (nvis_children > 0)
+    {
+      if (box->homogeneous)
+	requisition->width *= nvis_children;
+      requisition->width += (nvis_children - 1) * box->spacing;
+    }
+
+  requisition->width += GTK_CONTAINER (box)->border_width * 2;
+  requisition->height += GTK_CONTAINER (box)->border_width * 2;
+}
+
+static void gdl_dock_bar_size_hallocate (GtkWidget     *widget,
+			GtkAllocation *allocation)
+{
+ GtkBox *box;
+  GtkBoxChild *child;
+  GList *children;
+  GtkAllocation child_allocation;
+  gint nvis_children;
+  gint nexpand_children;
+  gint child_width;
+  gint width;
+  gint extra;
+  gint x;
+  GtkTextDirection direction;
+
+  box = GTK_BOX (widget);
+  widget->allocation = *allocation;
+
+  direction = gtk_widget_get_direction (widget);
+  
+  nvis_children = 0;
+  nexpand_children = 0;
+  children = box->children;
+
+  while (children)
+    {
+      child = children->data;
+      children = children->next;
+
+      if (GTK_WIDGET_VISIBLE (child->widget))
+	{
+	  nvis_children += 1;
+	  if (child->expand)
+	    nexpand_children += 1;
+	}
+    }
+
+  if (nvis_children > 0)
+    {
+      if (box->homogeneous)
+	{
+	  width = (allocation->width -
+		   GTK_CONTAINER (box)->border_width * 2 -
+		   (nvis_children - 1) * box->spacing);
+	  extra = width / nvis_children;
+	}
+      else if (nexpand_children > 0)
+	{
+	  width = (gint) allocation->width - (gint) widget->requisition.width;
+	  extra = width / nexpand_children;
+	}
+      else
+	{
+	  width = 0;
+	  extra = 0;
+	}
+
+      x = allocation->x + GTK_CONTAINER (box)->border_width;
+      child_allocation.y = allocation->y + GTK_CONTAINER (box)->border_width;
+      child_allocation.height = MAX (1, (gint) allocation->height - (gint) GTK_CONTAINER (box)->border_width * 2);
+
+      children = box->children;
+      while (children)
+	{
+	  child = children->data;
+	  children = children->next;
+
+	  if ((child->pack == GTK_PACK_START) && GTK_WIDGET_VISIBLE (child->widget))
+	    {
+	      if (box->homogeneous)
+		{
+		  if (nvis_children == 1)
+		    child_width = width;
+		  else
+		    child_width = extra;
+
+		  nvis_children -= 1;
+		  width -= extra;
+		}
+	      else
+		{
+		  GtkRequisition child_requisition;
+
+		  gtk_widget_get_child_requisition (child->widget, &child_requisition);
+
+		  child_width = child_requisition.width + child->padding * 2;
+
+		  if (child->expand)
+		    {
+		      if (nexpand_children == 1)
+			child_width += width;
+		      else
+			child_width += extra;
+
+		      nexpand_children -= 1;
+		      width -= extra;
+		    }
+		}
+
+	      if (child->fill)
+		{
+		  child_allocation.width = MAX (1, (gint) child_width - (gint) child->padding * 2);
+		  child_allocation.x = x + child->padding;
+		}
+	      else
+		{
+		  GtkRequisition child_requisition;
+
+		  gtk_widget_get_child_requisition (child->widget, &child_requisition);
+		  child_allocation.width = child_requisition.width;
+		  child_allocation.x = x + (child_width - child_allocation.width) / 2;
+		}
+
+	      if (direction == GTK_TEXT_DIR_RTL)
+		child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
+
+	      gtk_widget_size_allocate (child->widget, &child_allocation);
+
+	      x += child_width + box->spacing;
+	    }
+	}
+
+      x = allocation->x + allocation->width - GTK_CONTAINER (box)->border_width;
+
+      children = box->children;
+      while (children)
+	{
+	  child = children->data;
+	  children = children->next;
+
+	  if ((child->pack == GTK_PACK_END) && GTK_WIDGET_VISIBLE (child->widget))
+	    {
+	      GtkRequisition child_requisition;
+	      gtk_widget_get_child_requisition (child->widget, &child_requisition);
+
+              if (box->homogeneous)
+                {
+                  if (nvis_children == 1)
+                    child_width = width;
+                  else
+                    child_width = extra;
+
+                  nvis_children -= 1;
+                  width -= extra;
+                }
+              else
+                {
+		  child_width = child_requisition.width + child->padding * 2;
+
+                  if (child->expand)
+                    {
+                      if (nexpand_children == 1)
+                        child_width += width;
+                      else
+                        child_width += extra;
+
+                      nexpand_children -= 1;
+                      width -= extra;
+                    }
+                }
+
+              if (child->fill)
+                {
+                  child_allocation.width = MAX (1, (gint)child_width - (gint)child->padding * 2);
+                  child_allocation.x = x + child->padding - child_width;
+                }
+              else
+                {
+		  child_allocation.width = child_requisition.width;
+                  child_allocation.x = x + (child_width - child_allocation.width) / 2 - child_width;
+                }
+
+	      if (direction == GTK_TEXT_DIR_RTL)
+		child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
+
+              gtk_widget_size_allocate (child->widget, &child_allocation);
+
+              x -= (child_width + box->spacing);
+	    }
+	}
+    }
+}
+
+GtkWidget *
+gdl_dock_bar_new (GdlDock *dock)
+{
+    GdlDockMaster *master = NULL;
+    
+    /* get the master of the given dock */
+    if (dock)
+        master = GDL_DOCK_OBJECT_GET_MASTER (dock);
+
+    return g_object_new (GDL_TYPE_DOCK_BAR,
+                         "master", master, NULL);
+}
+
+GtkOrientation gdl_dock_bar_get_orientation (GdlDockBar *dockbar)
+{
+    g_return_val_if_fail (GDL_IS_DOCK_BAR (dockbar),
+                          GTK_ORIENTATION_VERTICAL);
+
+    return dockbar->_priv->orientation;
+}
+
+void gdl_dock_bar_set_orientation (GdlDockBar *dockbar,
+	                             GtkOrientation orientation)
+{
+    g_return_if_fail (GDL_IS_DOCK_BAR (dockbar));
+
+    dockbar->_priv->orientation = orientation;
+
+    gtk_widget_queue_resize (GTK_WIDGET (dockbar));
+}
+
+void gdl_dock_bar_set_style(GdlDockBar* dockbar,
+			    GdlDockBarStyle style)
+{
+    g_object_set(G_OBJECT(dockbar), "dockbar-style", style, NULL);
+}
+
+GdlDockBarStyle gdl_dock_bar_get_style(GdlDockBar* dockbar)
+{
+    GdlDockBarStyle style;
+    g_object_get(G_OBJECT(dockbar), "dockbar-style", &style, NULL);
+    return style;
+}

Added: trunk/src/ext/libgdl/gdl-dock-bar.h
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-dock-bar.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,75 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This file is part of the GNOME Devtools Libraries.
+ *
+ * Copyright (C) 2003 Jeroen Zwartepoorte <jeroen xs4all nl>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GDL_DOCK_BAR_H__
+#define __GDL_DOCK_BAR_H__
+
+#include <gtk/gtkvbox.h>
+#include "libgdl/gdl-dock.h"
+
+G_BEGIN_DECLS
+
+/* standard macros */
+#define GDL_TYPE_DOCK_BAR            (gdl_dock_bar_get_type ())
+#define GDL_DOCK_BAR(obj)            (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_BAR, GdlDockBar))
+#define GDL_DOCK_BAR_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_BAR, GdlDockBarClass))
+#define GDL_IS_DOCK_BAR(obj)         (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_BAR))
+#define GDL_IS_DOCK_BAR_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_BAR))
+#define GDL_DOCK_BAR_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK_BAR, GdlDockBarClass))
+
+/* data types & structures */
+typedef struct _GdlDockBar        GdlDockBar;
+typedef struct _GdlDockBarClass   GdlDockBarClass;
+typedef struct _GdlDockBarPrivate GdlDockBarPrivate;
+
+typedef enum {
+    GDL_DOCK_BAR_ICONS,
+    GDL_DOCK_BAR_TEXT,
+    GDL_DOCK_BAR_BOTH,
+    GDL_DOCK_BAR_AUTO
+} GdlDockBarStyle;
+
+struct _GdlDockBar {
+    GtkBox parent;
+
+    GdlDock *dock;
+
+    GdlDockBarPrivate *_priv;
+};
+
+struct _GdlDockBarClass {
+    GtkVBoxClass parent_class;
+};
+
+GType      gdl_dock_bar_get_type            (void); 
+
+GtkWidget *gdl_dock_bar_new                 (GdlDock     *dock);
+
+GtkOrientation gdl_dock_bar_get_orientation (GdlDockBar *dockbar);
+void           gdl_dock_bar_set_orientation (GdlDockBar *dockbar,
+                                             GtkOrientation orientation);
+void           gdl_dock_bar_set_style       (GdlDockBar *dockbar,
+                                             GdlDockBarStyle style);
+GdlDockBarStyle gdl_dock_bar_get_style      (GdlDockBar *dockbar);
+
+G_END_DECLS
+
+#endif /* __GDL_DOCK_BAR_H__ */

Added: trunk/src/ext/libgdl/gdl-dock-item-grip.c
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-dock-item-grip.c	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,722 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 8 -*- */
+/**
+ * gdl-dock-item-grip.c
+ *
+ * Based on bonobo-dock-item-grip.  Original copyright notice follows.
+ *
+ * Author:
+ *    Michael Meeks
+ *
+ * Copyright (C) 2002 Sun Microsystems, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gdl-i18n.h"
+#include <string.h>
+#include <glib-object.h>
+#include <gtk/gtkbutton.h>
+#include <gtk/gtktooltips.h>
+#include <gtk/gtkimage.h>
+#include "gdl-dock-item.h"
+#include "gdl-dock-item-grip.h"
+#include "gdl-stock.h"
+#include "gdl-tools.h"
+
+#define ALIGN_BORDER 5
+
+enum {
+    PROP_0,
+    PROP_ITEM
+};
+ 
+struct _GdlDockItemGripPrivate {
+    GtkWidget   *close_button;
+    GtkWidget   *iconify_button;
+    GtkTooltips *tooltips;
+
+    gboolean     icon_pixbuf_valid;
+    GdkPixbuf   *icon_pixbuf;
+
+    gchar       *title;
+    PangoLayout *title_layout;
+};
+ 
+GDL_CLASS_BOILERPLATE (GdlDockItemGrip, gdl_dock_item_grip,
+		       GtkContainer, GTK_TYPE_CONTAINER);
+
+/* must be called after size_allocate */
+static void
+gdl_dock_item_grip_get_title_area (GdlDockItemGrip *grip,
+                                   GdkRectangle    *area)
+{
+    GtkWidget *widget = GTK_WIDGET (grip);
+    gint       border = GTK_CONTAINER (grip)->border_width;
+    gint       alloc_height;
+
+    area->width = (widget->allocation.width - 2 * border - ALIGN_BORDER);
+    
+    pango_layout_get_pixel_size (grip->_priv->title_layout, NULL, &alloc_height);
+    
+    alloc_height = MAX (grip->_priv->close_button->allocation.height, alloc_height);
+    alloc_height = MAX (grip->_priv->iconify_button->allocation.height, alloc_height);
+    if (GTK_WIDGET_VISIBLE (grip->_priv->close_button)) {
+        area->width -= grip->_priv->close_button->allocation.width;
+    }
+    if (GTK_WIDGET_VISIBLE (grip->_priv->iconify_button)) {
+        area->width -= grip->_priv->iconify_button->allocation.width;
+    }
+
+    area->x      = widget->allocation.x + border + ALIGN_BORDER;
+    area->y      = widget->allocation.y + border;
+    area->height = alloc_height;
+
+    if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+        area->x += (widget->allocation.width - 2 * border) - area->width;
+}
+
+static void
+ensure_title_and_icon_pixbuf (GdlDockItemGrip *grip)
+{
+    gchar *stock_id;
+    GdkPixbuf *pixbuf;
+    
+    g_return_if_fail (GDL_IS_DOCK_ITEM_GRIP (grip));
+    
+    /* get long name property from the dock object */
+    if (!grip->_priv->title) {
+        g_object_get (G_OBJECT (grip->item), "long-name", &grip->_priv->title, NULL);
+        if (!grip->_priv->title)
+            grip->_priv->title = g_strdup ("");
+    }
+
+    /* retrieve stock pixbuf, if any */
+    if (!grip->_priv->icon_pixbuf_valid) {
+        g_object_get (G_OBJECT (grip->item), "stock-id", &stock_id, NULL);
+        
+        if (stock_id) {
+            grip->_priv->icon_pixbuf = gtk_widget_render_icon (GTK_WIDGET (grip),
+                                                               stock_id,
+                                                               GTK_ICON_SIZE_MENU, "");
+            g_free (stock_id);
+            grip->_priv->icon_pixbuf_valid = TRUE;
+        }
+    }
+
+    /* retrieve pixbuf icon, if any */
+    if (!grip->_priv->icon_pixbuf_valid) {
+        g_object_get (G_OBJECT (grip->item), "pixbuf-icon", &pixbuf, NULL);
+        
+        if (pixbuf) {
+            grip->_priv->icon_pixbuf = pixbuf;
+            grip->_priv->icon_pixbuf_valid = TRUE;
+        }
+    }
+
+    /* create layout: the actual text is reset at size_allocate */
+    if (!grip->_priv->title_layout) {
+        grip->_priv->title_layout = gtk_widget_create_pango_layout (GTK_WIDGET (grip),
+                                                                    grip->_priv->title);
+        pango_layout_set_single_paragraph_mode (grip->_priv->title_layout, TRUE);
+    }
+}
+
+static gint
+gdl_dock_item_grip_expose (GtkWidget      *widget,
+			   GdkEventExpose *event)
+{
+    GdlDockItemGrip *grip;
+    GdkRectangle     title_area;
+    GdkRectangle     expose_area;
+    GdkGC           *bg_style;
+    gint             layout_width;
+    gint             layout_height;
+    gint             text_x;
+    gint             text_y;
+    gboolean         item_or_child_has_focus;
+
+    grip = GDL_DOCK_ITEM_GRIP (widget);
+    gdl_dock_item_grip_get_title_area (grip, &title_area);
+
+    /* draw background, highlight it if the dock item or any of its
+     * descendants have focus */
+    bg_style = (gdl_dock_item_or_child_has_focus (grip->item) ?
+                gtk_widget_get_style (widget)->dark_gc[widget->state] :
+                gtk_widget_get_style (widget)->mid_gc[widget->state]);
+
+    gdk_draw_rectangle (GDK_DRAWABLE (widget->window), bg_style, TRUE,
+                        1, 0, widget->allocation.width - 1, widget->allocation.height);
+
+    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_RIGHT, 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);
+}

Added: trunk/src/ext/libgdl/gdl-dock-item-grip.h
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-dock-item-grip.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,56 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 8 -*- */
+/**
+ * gdl-dock-item-grip.h
+ * 
+ * Based on bonobo-dock-item-grip.  Original copyright notice follows.
+ *
+ * Author:
+ *    Michael Meeks
+ *
+ * Copyright (C) 2002 Sun Microsystems, Inc.
+ */
+
+#ifndef _GDL_DOCK_ITEM_GRIP_H_
+#define _GDL_DOCK_ITEM_GRIP_H_
+
+#include <gtk/gtkwidget.h>
+#include "libgdl/gdl-dock-item.h"
+
+G_BEGIN_DECLS
+
+#define GDL_TYPE_DOCK_ITEM_GRIP            (gdl_dock_item_grip_get_type())
+#define GDL_DOCK_ITEM_GRIP(obj)            \
+    (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_ITEM_GRIP, GdlDockItemGrip))
+#define GDL_DOCK_ITEM_GRIP_CLASS(klass)    \
+    (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_ITEM_GRIP, GdlDockItemGripClass))
+#define GDL_IS_DOCK_ITEM_GRIP(obj)         \
+    (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_ITEM_GRIP))
+#define GDL_IS_DOCK_ITEM_GRIP_CLASS(klass) \
+    (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_ITEM_GRIP))
+#define GDL_DOCK_ITEM_GRIP_GET_CLASS(obj)  \
+    (GTK_CHECK_GET_CLASS ((obj), GDL_TYPE_DOCK_ITEM_GRIP, GdlDockItemGripClass))
+
+typedef struct _GdlDockItemGrip        GdlDockItemGrip;
+typedef struct _GdlDockItemGripClass   GdlDockItemGripClass;
+typedef struct _GdlDockItemGripPrivate GdlDockItemGripPrivate;
+
+struct _GdlDockItemGrip {
+    GtkContainer parent;
+	
+    GdlDockItem *item;
+    
+    GdkWindow *title_window;
+    
+    GdlDockItemGripPrivate *_priv;
+};
+
+struct _GdlDockItemGripClass {
+    GtkContainerClass parent_class;
+};
+
+GType      gdl_dock_item_grip_get_type (void);
+GtkWidget *gdl_dock_item_grip_new      (GdlDockItem *item);
+
+G_END_DECLS
+
+#endif /* _GDL_DOCK_ITEM_GRIP_H_ */

Added: trunk/src/ext/libgdl/gdl-dock-item.c
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-dock-item.c	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,2060 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 
+ *
+ * gdl-dock-item.c
+ *
+ * Author: Gustavo Giráez <gustavo giraldez gmx net>
+ *         Naba Kumar  <naba gnome org>
+ *
+ * Based on GnomeDockItem/BonoboDockItem.  Original copyright notice follows.
+ *
+ * Copyright (C) 1998 Ettore Perazzoli
+ * Copyright (C) 1998 Elliot Lee
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald 
+ * All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gdl-i18n.h"
+#include <string.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "gdl-tools.h"
+#include "gdl-dock.h"
+#include "gdl-dock-item.h"
+#include "gdl-dock-item-grip.h"
+#include "gdl-dock-notebook.h"
+#include "gdl-dock-paned.h"
+#include "gdl-dock-tablabel.h"
+#include "gdl-dock-placeholder.h"
+#include "gdl-dock-master.h"
+#include "libgdltypebuiltins.h"
+#include "libgdlmarshal.h"
+
+#define NEW_DOCK_ITEM_RATIO 0.3
+
+/* ----- Private prototypes ----- */
+
+static void  gdl_dock_item_class_init    (GdlDockItemClass *class);
+static void  gdl_dock_item_instance_init (GdlDockItem *item);
+
+static GObject *gdl_dock_item_constructor (GType                  type,
+                                           guint                  n_construct_properties,
+                                           GObjectConstructParam *construct_param);
+
+static void  gdl_dock_item_set_property  (GObject      *object,
+                                          guint         prop_id,
+                                          const GValue *value,
+                                          GParamSpec   *pspec);
+static void  gdl_dock_item_get_property  (GObject      *object,
+                                          guint         prop_id,
+                                          GValue       *value,
+                                          GParamSpec   *pspec);
+
+static void  gdl_dock_item_destroy       (GtkObject *object);
+
+static void  gdl_dock_item_add           (GtkContainer *container,
+                                          GtkWidget    *widget);
+static void  gdl_dock_item_remove        (GtkContainer *container,
+                                          GtkWidget    *widget);
+static void  gdl_dock_item_forall        (GtkContainer *container,
+                                          gboolean      include_internals,
+                                          GtkCallback   callback,
+                                          gpointer      callback_data);
+static GtkType gdl_dock_item_child_type  (GtkContainer *container);
+
+static void  gdl_dock_item_set_focus_child (GtkContainer *container,
+                                            GtkWidget    *widget,
+                                            gpointer      callback_data);
+
+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 void  gdl_dock_item_move_focus_child (GdlDockItem      *item,
+                                             GtkDirectionType  dir);
+
+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,
+    MOVE_FOCUS_CHILD,
+    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
+add_tab_bindings (GtkBindingSet    *binding_set,
+		  GdkModifierType   modifiers,
+		  GtkDirectionType  direction)
+{
+    gtk_binding_entry_add_signal (binding_set, GDK_Tab, modifiers,
+                                  "move_focus_child", 1,
+                                  GTK_TYPE_DIRECTION_TYPE, direction);
+    gtk_binding_entry_add_signal (binding_set, GDK_KP_Tab, modifiers,
+                                  "move_focus_child", 1,
+                                  GTK_TYPE_DIRECTION_TYPE, direction);
+}
+
+static void
+add_arrow_bindings (GtkBindingSet    *binding_set,
+		    guint             keysym,
+		    GtkDirectionType  direction)
+{
+    guint keypad_keysym = keysym - GDK_Left + GDK_KP_Left;
+
+    gtk_binding_entry_add_signal (binding_set, keysym, 0,
+                                  "move_focus_child", 1,
+                                  GTK_TYPE_DIRECTION_TYPE, direction);
+    gtk_binding_entry_add_signal (binding_set, keysym, GDK_CONTROL_MASK,
+                                  "move_focus_child", 1,
+                                  GTK_TYPE_DIRECTION_TYPE, direction);
+    gtk_binding_entry_add_signal (binding_set, keysym, GDK_CONTROL_MASK,
+                                  "move_focus_child", 1,
+                                  GTK_TYPE_DIRECTION_TYPE, direction);
+    gtk_binding_entry_add_signal (binding_set, keypad_keysym, GDK_CONTROL_MASK,
+                                  "move_focus_child", 1,
+                                  GTK_TYPE_DIRECTION_TYPE, direction);
+}
+
+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;
+    GtkBindingSet      *binding_set;
+    
+    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;
+    container_class->set_focus_child = gdl_dock_item_set_focus_child;
+    
+    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 panel"),
+                              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);
+
+    gdl_dock_item_signals [MOVE_FOCUS_CHILD] =
+        g_signal_new ("move_focus_child",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (GdlDockItemClass, move_focus_child),
+                      NULL, /* accumulator */
+                      NULL, /* accu_data */
+                      gdl_marshal_VOID__ENUM,
+                      G_TYPE_NONE,
+                      1,
+                      GTK_TYPE_DIRECTION_TYPE);
+
+
+    /* key bindings */
+
+    binding_set = gtk_binding_set_by_class (klass);
+
+    add_arrow_bindings (binding_set, GDK_Up, GTK_DIR_UP);
+    add_arrow_bindings (binding_set, GDK_Down, GTK_DIR_DOWN);
+    add_arrow_bindings (binding_set, GDK_Left, GTK_DIR_LEFT);
+    add_arrow_bindings (binding_set, GDK_Right, GTK_DIR_RIGHT);
+
+    add_tab_bindings (binding_set, 0, GTK_DIR_TAB_FORWARD);
+    add_tab_bindings (binding_set, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD);
+    add_tab_bindings (binding_set, GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
+    add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
+
+    klass->has_grip = TRUE;
+    klass->dock_drag_begin = NULL;
+    klass->dock_drag_motion = NULL;
+    klass->dock_drag_end = NULL;
+    klass->move_focus_child = gdl_dock_item_move_focus_child;
+    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);
+    GTK_WIDGET_SET_FLAGS (GTK_WIDGET (item), GTK_CAN_FOCUS);
+
+    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_set_focus_child (GtkContainer *container,
+                               GtkWidget    *child,
+                               gpointer      callback_data)
+{
+    g_return_if_fail (GDL_IS_DOCK_ITEM (container));
+    
+    if (GTK_CONTAINER_CLASS (parent_class)->set_focus_child)
+        (* GTK_CONTAINER_CLASS (parent_class)->set_focus_child) (container, child);
+
+    gdl_dock_item_showhide_grip (GDL_DOCK_ITEM (container));
+}
+
+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;
+}
+
+static void
+gdl_dock_item_move_focus_child (GdlDockItem      *item,
+                                GtkDirectionType  dir)
+{
+    g_return_if_fail (GDL_IS_DOCK_ITEM (item));
+    gtk_widget_child_focus (GTK_WIDGET (item->child), dir);
+}
+
+#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) {
+
+        if (!gdl_dock_item_or_child_has_focus (item))
+            gtk_widget_grab_focus (GTK_WIDGET (item));
+            
+        /* 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);
+            gtk_widget_grab_focus (GTK_WIDGET (item));
+            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 && object_req.height > req.height)
+                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);
+}
+
+
+gboolean
+gdl_dock_item_or_child_has_focus (GdlDockItem *item)
+{
+    GtkWidget *item_child;
+    gboolean item_or_child_has_focus;
+
+    g_return_val_if_fail (GDL_IS_DOCK_ITEM (item), FALSE);
+
+    for (item_child = GTK_CONTAINER (item)->focus_child;
+         item_child && GTK_IS_CONTAINER (item_child) && GTK_CONTAINER (item_child)->focus_child;
+         item_child = GTK_CONTAINER (item_child)->focus_child) ;
+    
+    item_or_child_has_focus =
+        (GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (item)) || 
+         (GTK_IS_WIDGET (item_child) && GTK_WIDGET_HAS_FOCUS (item_child)));
+    
+    return item_or_child_has_focus;
+}
+
+
+/* ----- 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;
+}
+

Added: trunk/src/ext/libgdl/gdl-dock-item.h
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-dock-item.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,195 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * gdl-dock-item.h
+ *
+ * Author: Gustavo Giráez <gustavo giraldez gmx net>
+ *
+ * Based on GnomeDockItem/BonoboDockItem.  Original copyright notice follows.
+ *
+ * Copyright (C) 1998 Ettore Perazzoli
+ * Copyright (C) 1998 Elliot Lee
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald 
+ * All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GDL_DOCK_ITEM_H__
+#define __GDL_DOCK_ITEM_H__
+
+#include "libgdl/gdl-dock-object.h"
+
+G_BEGIN_DECLS
+
+/* standard macros */
+#define GDL_TYPE_DOCK_ITEM            (gdl_dock_item_get_type ())
+#define GDL_DOCK_ITEM(obj)            (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_ITEM, GdlDockItem))
+#define GDL_DOCK_ITEM_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_ITEM, GdlDockItemClass))
+#define GDL_IS_DOCK_ITEM(obj)         (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_ITEM))
+#define GDL_IS_DOCK_ITEM_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_ITEM))
+#define GDL_DOCK_ITEM_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK_ITEM, GdlDockItemClass))
+
+/* data types & structures */
+typedef enum {
+    GDL_DOCK_ITEM_BEH_NORMAL           = 0,
+    GDL_DOCK_ITEM_BEH_NEVER_FLOATING   = 1 << 0,
+    GDL_DOCK_ITEM_BEH_NEVER_VERTICAL   = 1 << 1,
+    GDL_DOCK_ITEM_BEH_NEVER_HORIZONTAL = 1 << 2,
+    GDL_DOCK_ITEM_BEH_LOCKED           = 1 << 3,
+    GDL_DOCK_ITEM_BEH_CANT_DOCK_TOP    = 1 << 4,
+    GDL_DOCK_ITEM_BEH_CANT_DOCK_BOTTOM = 1 << 5,
+    GDL_DOCK_ITEM_BEH_CANT_DOCK_LEFT   = 1 << 6,
+    GDL_DOCK_ITEM_BEH_CANT_DOCK_RIGHT  = 1 << 7,
+    GDL_DOCK_ITEM_BEH_CANT_DOCK_CENTER = 1 << 8,
+    GDL_DOCK_ITEM_BEH_CANT_CLOSE       = 1 << 9,
+    GDL_DOCK_ITEM_BEH_CANT_ICONIFY     = 1 << 10,
+    GDL_DOCK_ITEM_BEH_NO_GRIP          = 1 << 11
+} GdlDockItemBehavior;
+
+typedef enum {
+    GDL_DOCK_IN_DRAG             = 1 << GDL_DOCK_OBJECT_FLAGS_SHIFT,
+    GDL_DOCK_IN_PREDRAG          = 1 << (GDL_DOCK_OBJECT_FLAGS_SHIFT + 1),
+    GDL_DOCK_ICONIFIED           = 1 << (GDL_DOCK_OBJECT_FLAGS_SHIFT + 2),
+    /* for general use: indicates the user has started an action on
+       the dock item */
+    GDL_DOCK_USER_ACTION         = 1 << (GDL_DOCK_OBJECT_FLAGS_SHIFT + 3)
+} GdlDockItemFlags;
+
+typedef struct _GdlDockItem        GdlDockItem;
+typedef struct _GdlDockItemClass   GdlDockItemClass;
+typedef struct _GdlDockItemPrivate GdlDockItemPrivate;
+
+struct _GdlDockItem {
+    GdlDockObject        object;
+
+    GtkWidget           *child;
+    GdlDockItemBehavior  behavior;
+    GtkOrientation       orientation;
+
+    guint                resize : 1;
+
+    gint                 dragoff_x, dragoff_y;    /* these need to be
+                                                     accesible from
+                                                     outside */
+    GdlDockItemPrivate  *_priv;
+};
+
+struct _GdlDockItemClass {
+    GdlDockObjectClass  parent_class;
+
+    gboolean            has_grip;
+    
+    /* virtuals */
+    void     (* dock_drag_begin)  (GdlDockItem      *item);
+    void     (* dock_drag_motion) (GdlDockItem      *item,
+                                   gint              x,
+                                   gint              y);
+    void     (* dock_drag_end)    (GdlDockItem      *item,
+                                   gboolean          cancelled);
+    void     (* move_focus_child) (GdlDockItem      *item,
+                                   GtkDirectionType  direction);
+                                   
+    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);
+
+gboolean    gdl_dock_item_or_child_has_focus  (GdlDockItem      *item);
+
+G_END_DECLS
+
+#endif

Added: trunk/src/ext/libgdl/gdl-dock-master.c
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-dock-master.c	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,1025 @@
+/* -*- 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áez <gustavo giraldez gmx net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gdl-i18n.h"
+
+#include "gdl-tools.h"
+#include "gdl-dock-master.h"
+#include "gdl-dock.h"
+#include "gdl-dock-item.h"
+#include "libgdlmarshal.h"
+#include "libgdltypebuiltins.h"
+#ifdef WIN32
+#include "gdl-win32.h"
+#endif
+
+/* ----- 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,
+    PROP_EXPANSION_DIRECTION
+};
+
+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;
+
+    GdlDockExpansionDirection expansion_direction;
+};
+
+#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));
+
+    g_object_class_install_property (
+        g_object_class, PROP_EXPANSION_DIRECTION,
+        g_param_spec_enum ("expand-direction", _("Expand direction"),
+                           _("Allow the master's dock items to expand their container "
+                             "dock objects in the given direction"),
+                           GDL_TYPE_EXPANSION_DIRECTION,
+                           GDL_DOCK_EXPANSION_DIRECTION_NONE,
+                           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->expansion_direction = GDL_DOCK_EXPANSION_DIRECTION_NONE;
+    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;
+        case PROP_EXPANSION_DIRECTION:
+            master->_priv->expansion_direction = 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;
+        case PROP_EXPANSION_DIRECTION:
+            g_value_set_enum (value, master->_priv->expansion_direction);
+            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);
+    };
+
+#ifdef WIN32    
+    GdkLineStyle lineStyle = GDK_LINE_ON_OFF_DASH;
+    if (is_os_vista())
+    {
+        // On Vista the dash-line is increadibly slow to draw, it takes several minutes to draw the tracking lines
+        // With GDK_LINE_SOLID it is parts of a second
+        // No performance issue on WinXP
+        lineStyle = GDK_LINE_SOLID;
+    }
+#else
+    GdkLineStyle lineStyle = GDK_LINE_ON_OFF_DASH;
+#endif
+    gdk_gc_set_line_attributes (master->_priv->root_xor_gc, 1,
+                                lineStyle,
+                                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));
+}

Added: trunk/src/ext/libgdl/gdl-dock-master.h
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-dock-master.h	Wed Dec 10 19:46:36 2008
@@ -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áez <gustavo giraldez gmx net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GDL_DOCK_MASTER_H__
+#define __GDL_DOCK_MASTER_H__
+
+#include <glib-object.h>
+#include <gtk/gtktypeutils.h>
+#include "libgdl/gdl-dock-object.h"
+
+
+G_BEGIN_DECLS
+
+/* standard macros */
+#define GDL_TYPE_DOCK_MASTER             (gdl_dock_master_get_type ())
+#define GDL_DOCK_MASTER(obj)             (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_MASTER, GdlDockMaster))
+#define GDL_DOCK_MASTER_CLASS(klass)     (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_MASTER, GdlDockMasterClass))
+#define GDL_IS_DOCK_MASTER(obj)          (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_MASTER))
+#define GDL_IS_DOCK_MASTER_CLASS(klass)  (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_MASTER))
+#define GDL_DOCK_MASTER_GET_CLASS(obj)   (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK_MASTER, GdlDockMasterClass))
+
+/* data types & structures */
+typedef struct _GdlDockMaster        GdlDockMaster;
+typedef struct _GdlDockMasterClass   GdlDockMasterClass;
+typedef struct _GdlDockMasterPrivate GdlDockMasterPrivate;
+
+struct _GdlDockMaster {
+    GObject               object;
+
+    GHashTable           *dock_objects;
+    GList                *toplevel_docks;
+    GdlDockObject        *controller;      /* GUI root object */
+    
+    gint                  dock_number;     /* for toplevel dock numbering */
+    
+    GdlDockMasterPrivate *_priv;
+};
+
+struct _GdlDockMasterClass {
+    GObjectClass parent_class;
+
+    void (* layout_changed) (GdlDockMaster *master);
+};
+
+/* additional macros */
+
+#define GDL_DOCK_OBJECT_GET_MASTER(object) \
+    (GDL_DOCK_OBJECT (object)->master ? \
+        GDL_DOCK_MASTER (GDL_DOCK_OBJECT (object)->master) : NULL)
+
+/* public interface */
+ 
+GType          gdl_dock_master_get_type         (void);
+
+void           gdl_dock_master_add              (GdlDockMaster *master,
+                                                 GdlDockObject *object);
+void           gdl_dock_master_remove           (GdlDockMaster *master,
+                                                 GdlDockObject *object);
+void           gdl_dock_master_foreach          (GdlDockMaster *master,
+                                                 GFunc          function,
+                                                 gpointer       user_data);
+
+void           gdl_dock_master_foreach_toplevel (GdlDockMaster *master,
+                                                 gboolean       include_controller,
+                                                 GFunc          function,
+                                                 gpointer       user_data);
+
+GdlDockObject *gdl_dock_master_get_object       (GdlDockMaster *master,
+                                                 const gchar   *nick_name);
+
+GdlDockObject *gdl_dock_master_get_controller   (GdlDockMaster *master);
+void           gdl_dock_master_set_controller   (GdlDockMaster *master,
+                                                 GdlDockObject *new_controller);
+
+G_END_DECLS
+
+#endif /* __GDL_DOCK_MASTER_H__ */

Added: trunk/src/ext/libgdl/gdl-dock-notebook.c
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-dock-notebook.c	Wed Dec 10 19:46:36 2008
@@ -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áez <gustavo giraldez gmx net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gdl-i18n.h"
+#include "gdl-switcher.h"
+
+#include "gdl-tools.h"
+#include "gdl-dock-notebook.h"
+#include "gdl-dock-tablabel.h"
+
+
+/* Private prototypes */
+
+static void  gdl_dock_notebook_class_init    (GdlDockNotebookClass *klass);
+static void  gdl_dock_notebook_instance_init (GdlDockNotebook      *notebook);
+static void  gdl_dock_notebook_set_property  (GObject              *object,
+                                              guint                 prop_id,
+                                              const GValue         *value,
+                                              GParamSpec           *pspec);
+static void  gdl_dock_notebook_get_property  (GObject              *object,
+                                              guint                 prop_id,
+                                              GValue               *value,
+                                              GParamSpec           *pspec);
+
+static void  gdl_dock_notebook_destroy       (GtkObject    *object);
+
+static void  gdl_dock_notebook_add           (GtkContainer *container,
+					      GtkWidget    *widget);
+static void  gdl_dock_notebook_forall        (GtkContainer *container,
+					      gboolean      include_internals,
+					      GtkCallback   callback,
+					      gpointer      callback_data);
+static GType gdl_dock_notebook_child_type    (GtkContainer *container);
+
+static void  gdl_dock_notebook_dock          (GdlDockObject    *object,
+                                              GdlDockObject    *requestor,
+                                              GdlDockPlacement  position,
+                                              GValue           *other_data);
+
+static void  gdl_dock_notebook_switch_page_cb  (GtkNotebook     *nb,
+                                                GtkNotebookPage *page,
+                                                gint             page_num,
+                                                gpointer         data);
+
+static void  gdl_dock_notebook_set_orientation (GdlDockItem     *item,
+                                                GtkOrientation   orientation);
+					       
+static gboolean gdl_dock_notebook_child_placement (GdlDockObject    *object,
+                                                   GdlDockObject    *child,
+                                                   GdlDockPlacement *placement);
+
+static void     gdl_dock_notebook_present         (GdlDockObject    *object,
+                                                   GdlDockObject    *child);
+
+static gboolean gdl_dock_notebook_reorder         (GdlDockObject    *object,
+                                                   GdlDockObject    *requestor,
+                                                   GdlDockPlacement  new_position,
+                                                   GValue           *other_data);
+
+
+/* Class variables and definitions */
+
+enum {
+    PROP_0,
+    PROP_PAGE
+};
+
+
+/* ----- Private functions ----- */
+
+GDL_CLASS_BOILERPLATE (GdlDockNotebook, gdl_dock_notebook, GdlDockItem, GDL_TYPE_DOCK_ITEM) ;
+
+static void
+gdl_dock_notebook_class_init (GdlDockNotebookClass *klass)
+{
+    static gboolean style_initialized = FALSE;
+    
+    GObjectClass       *g_object_class;
+    GtkObjectClass     *gtk_object_class;
+    GtkWidgetClass     *widget_class;
+    GtkContainerClass  *container_class;
+    GdlDockObjectClass *object_class;
+    GdlDockItemClass   *item_class;
+
+    g_object_class = G_OBJECT_CLASS (klass);
+    gtk_object_class = GTK_OBJECT_CLASS (klass);
+    widget_class = GTK_WIDGET_CLASS (klass);
+    container_class = GTK_CONTAINER_CLASS (klass);
+    object_class = GDL_DOCK_OBJECT_CLASS (klass);
+    item_class = GDL_DOCK_ITEM_CLASS (klass);
+
+    g_object_class->set_property = gdl_dock_notebook_set_property;
+    g_object_class->get_property = gdl_dock_notebook_get_property;
+    
+    gtk_object_class->destroy = gdl_dock_notebook_destroy;
+
+    container_class->add = gdl_dock_notebook_add;
+    container_class->forall = gdl_dock_notebook_forall;
+    container_class->child_type = gdl_dock_notebook_child_type;
+    
+    object_class->is_compound = TRUE;
+    object_class->dock = gdl_dock_notebook_dock;
+    object_class->child_placement = gdl_dock_notebook_child_placement;
+    object_class->present = gdl_dock_notebook_present;
+    object_class->reorder = gdl_dock_notebook_reorder;
+    
+    item_class->has_grip = FALSE;
+    item_class->set_orientation = gdl_dock_notebook_set_orientation;    
+    
+    g_object_class_install_property (
+        g_object_class, PROP_PAGE,
+        g_param_spec_int ("page", _("Page"),
+                          _("The index of the current page"),
+                          0, G_MAXINT,
+                          0,
+                          G_PARAM_READWRITE |
+                          GDL_DOCK_PARAM_EXPORT | GDL_DOCK_PARAM_AFTER));
+
+    if (!style_initialized) {
+        style_initialized = TRUE;
+        
+        gtk_rc_parse_string (
+            "style \"gdl-dock-notebook-default\" {\n"
+            "xthickness = 2\n"
+            "ythickness = 2\n"
+            "}\n"
+            "widget_class \"*.GtkNotebook.GdlDockItem\" "
+            "style : gtk \"gdl-dock-notebook-default\"\n");
+    }
+}
+
+static void 
+gdl_dock_notebook_notify_cb (GObject    *g_object,
+                             GParamSpec *pspec,
+                             gpointer    user_data) 
+{
+    g_return_if_fail (user_data != NULL && GDL_IS_DOCK_NOTEBOOK (user_data));
+
+    /* chain the notify signal */
+    g_object_notify (G_OBJECT (user_data), pspec->name);
+}
+
+static gboolean 
+gdl_dock_notebook_button_cb (GtkWidget      *widget,
+                             GdkEventButton *event,
+                             gpointer        user_data)
+{
+    if (event->type == GDK_BUTTON_PRESS)
+        GDL_DOCK_ITEM_SET_FLAGS (user_data, GDL_DOCK_USER_ACTION);
+    else
+        GDL_DOCK_ITEM_UNSET_FLAGS (user_data, GDL_DOCK_USER_ACTION);
+
+    return FALSE;
+}
+    
+static void
+gdl_dock_notebook_instance_init (GdlDockNotebook *notebook)
+{
+    GdlDockItem *item;
+
+    item = GDL_DOCK_ITEM (notebook);
+
+    /* create the container notebook */
+    item->child = gdl_switcher_new ();
+    gtk_widget_set_parent (item->child, GTK_WIDGET (notebook));
+    gtk_notebook_set_tab_pos (GTK_NOTEBOOK (item->child), GTK_POS_BOTTOM);
+    g_signal_connect (item->child, "switch-page",
+                      (GCallback) gdl_dock_notebook_switch_page_cb, (gpointer) item);
+    g_signal_connect (item->child, "notify::page",
+                      (GCallback) gdl_dock_notebook_notify_cb, (gpointer) item);
+    g_signal_connect (item->child, "button-press-event",
+                      (GCallback) gdl_dock_notebook_button_cb, (gpointer) item);
+    g_signal_connect (item->child, "button-release-event",
+                      (GCallback) gdl_dock_notebook_button_cb, (gpointer) item);
+    gtk_notebook_set_scrollable (GTK_NOTEBOOK (item->child), TRUE);
+    gtk_widget_show (item->child);
+}
+
+static void 
+gdl_dock_notebook_set_property (GObject      *object,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+    GdlDockItem *item = GDL_DOCK_ITEM (object);
+
+    switch (prop_id) {
+        case PROP_PAGE:
+            if (item->child && GTK_IS_NOTEBOOK (item->child)) {
+                gtk_notebook_set_current_page (GTK_NOTEBOOK (item->child),
+                                               g_value_get_int (value));
+            }
+            
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+static void 
+gdl_dock_notebook_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+    GdlDockItem *item = GDL_DOCK_ITEM (object);
+
+    switch (prop_id) {
+        case PROP_PAGE:
+            if (item->child && GTK_IS_NOTEBOOK (item->child)) {
+                g_value_set_int (value, gtk_notebook_get_current_page
+                                 (GTK_NOTEBOOK (item->child)));
+            }
+            
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+
+static void
+gdl_dock_notebook_destroy (GtkObject *object)
+{
+    GdlDockItem *item = GDL_DOCK_ITEM (object);
+
+    /* we need to call the virtual first, since in GdlDockDestroy our
+       children dock objects are detached */
+    GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
+
+    /* after that we can remove the GtkNotebook */
+    if (item->child) {
+        gtk_widget_unparent (item->child);
+        item->child = NULL;
+    };
+}
+
+static void
+gdl_dock_notebook_switch_page_cb (GtkNotebook     *nb,
+                                  GtkNotebookPage *page,
+                                  gint             page_num,
+                                  gpointer         data)
+{
+    GdlDockNotebook *notebook;
+    GtkWidget       *tablabel;
+    
+    notebook = GDL_DOCK_NOTEBOOK (data);
+
+    /* deactivate old tablabel */
+    if (nb->cur_page) {
+        tablabel = gtk_notebook_get_tab_label (
+            nb, gtk_notebook_get_nth_page (
+                nb, gtk_notebook_get_current_page (nb)));
+        if (tablabel && GDL_IS_DOCK_TABLABEL (tablabel))
+            gdl_dock_tablabel_deactivate (GDL_DOCK_TABLABEL (tablabel));
+    };
+
+    /* activate new label */
+    tablabel = gtk_notebook_get_tab_label (
+        nb, gtk_notebook_get_nth_page (nb, page_num));
+    if (tablabel && GDL_IS_DOCK_TABLABEL (tablabel))
+        gdl_dock_tablabel_activate (GDL_DOCK_TABLABEL (tablabel));
+
+    if (GDL_DOCK_ITEM_USER_ACTION (notebook) &&
+        GDL_DOCK_OBJECT (notebook)->master)
+        g_signal_emit_by_name (GDL_DOCK_OBJECT (notebook)->master,
+                               "layout-changed");
+}
+
+static void
+gdl_dock_notebook_add (GtkContainer *container,
+		       GtkWidget    *widget)
+{
+    g_return_if_fail (container != NULL && widget != NULL);
+    g_return_if_fail (GDL_IS_DOCK_NOTEBOOK (container));
+    g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
+
+    gdl_dock_object_dock (GDL_DOCK_OBJECT (container),
+                          GDL_DOCK_OBJECT (widget),
+                          GDL_DOCK_CENTER,
+                          NULL);
+}
+
+static void
+gdl_dock_notebook_forall (GtkContainer *container,
+			  gboolean      include_internals,
+			  GtkCallback   callback,
+			  gpointer      callback_data)
+{
+    GdlDockItem *item;
+
+    g_return_if_fail (container != NULL);
+    g_return_if_fail (GDL_IS_DOCK_NOTEBOOK (container));
+    g_return_if_fail (callback != NULL);
+
+    if (include_internals) {
+        /* use GdlDockItem's forall */
+        GDL_CALL_PARENT (GTK_CONTAINER_CLASS, forall, 
+                           (container, include_internals, callback, callback_data));
+    }
+    else {
+        item = GDL_DOCK_ITEM (container);
+        if (item->child)
+            gtk_container_foreach (GTK_CONTAINER (item->child), callback, callback_data);
+    }
+}
+
+static GType
+gdl_dock_notebook_child_type (GtkContainer *container)
+{
+    return GDL_TYPE_DOCK_ITEM;
+}
+    
+static void
+gdl_dock_notebook_dock_child (GdlDockObject *requestor,
+                              gpointer       user_data)
+{
+    struct {
+        GdlDockObject    *object;
+        GdlDockPlacement  position;
+        GValue           *other_data;
+    } *data = user_data;
+
+    gdl_dock_object_dock (data->object, requestor, data->position, data->other_data);
+}
+
+static void
+gdl_dock_notebook_dock (GdlDockObject    *object,
+                        GdlDockObject    *requestor,
+                        GdlDockPlacement  position,
+                        GValue           *other_data)
+{
+    g_return_if_fail (GDL_IS_DOCK_NOTEBOOK (object));
+    g_return_if_fail (GDL_IS_DOCK_ITEM (requestor));
+
+    /* we only add support for GDL_DOCK_CENTER docking strategy here... for the rest
+       use our parent class' method */
+    if (position == GDL_DOCK_CENTER) {
+        /* we can only dock simple (not compound) items */
+        if (gdl_dock_object_is_compound (requestor)) {
+            struct {
+                GdlDockObject    *object;
+                GdlDockPlacement  position;
+                GValue           *other_data;
+            } data;
+
+            gdl_dock_object_freeze (requestor);
+            
+            data.object = object;
+            data.position = position;
+            data.other_data = other_data;
+             
+            gtk_container_foreach (GTK_CONTAINER (requestor),
+                                   (GtkCallback) gdl_dock_notebook_dock_child, &data);
+
+            gdl_dock_object_thaw (requestor);
+        }
+        else {
+            GdlDockItem *item = GDL_DOCK_ITEM (object);
+            GdlDockItem *requestor_item = GDL_DOCK_ITEM (requestor);
+            gchar       *long_name, *stock_id;
+            GdkPixbuf   *pixbuf_icon;
+            GtkWidget   *label;
+            gint         position = -1;
+            
+            g_object_get (requestor_item, "long-name", &long_name,
+                          "stock-id", &stock_id, "pixbuf-icon", &pixbuf_icon, NULL);
+            label = gdl_dock_item_get_tablabel (requestor_item);
+            if (!label) {
+                label = gtk_label_new (long_name);
+                gdl_dock_item_set_tablabel (requestor_item, label);
+            }
+#if 0
+            if (GDL_IS_DOCK_TABLABEL (label)) {
+                gdl_dock_tablabel_deactivate (GDL_DOCK_TABLABEL (label));
+                /* hide the item grip, as we will use the tablabel's */
+                gdl_dock_item_hide_grip (requestor_item);
+            }
+#endif
+
+            if (other_data && G_VALUE_HOLDS (other_data, G_TYPE_INT))
+                position = g_value_get_int (other_data);
+            
+            position = gdl_switcher_insert_page (GDL_SWITCHER (item->child), 
+                                                 GTK_WIDGET (requestor), label,
+                                                 long_name, long_name,
+                                                 stock_id, pixbuf_icon, position);
+            
+            GDL_DOCK_OBJECT_SET_FLAGS (requestor, GDL_DOCK_ATTACHED);
+            
+            /* Set current page to the newly docked widget. set current page
+             * really doesn't work if the page widget is not shown
+             */
+            gtk_widget_show (GTK_WIDGET (requestor));
+            gtk_notebook_set_current_page (GTK_NOTEBOOK (item->child),
+                                           position);
+            g_free (long_name);
+            g_free (stock_id);
+        }
+    }
+    else
+        GDL_CALL_PARENT (GDL_DOCK_OBJECT_CLASS, dock,
+                           (object, requestor, position, other_data));
+}
+
+static void
+gdl_dock_notebook_set_orientation (GdlDockItem    *item,
+                                   GtkOrientation  orientation)
+{
+    if (item->child && GTK_IS_NOTEBOOK (item->child)) {
+        if (orientation == GTK_ORIENTATION_HORIZONTAL)
+            gtk_notebook_set_tab_pos (GTK_NOTEBOOK (item->child), GTK_POS_TOP);
+        else
+            gtk_notebook_set_tab_pos (GTK_NOTEBOOK (item->child), GTK_POS_LEFT);
+    }
+
+    GDL_CALL_PARENT (GDL_DOCK_ITEM_CLASS, set_orientation, (item, orientation));
+}
+
+static gboolean 
+gdl_dock_notebook_child_placement (GdlDockObject    *object,
+                                   GdlDockObject    *child,
+                                   GdlDockPlacement *placement)
+{
+    GdlDockItem      *item = GDL_DOCK_ITEM (object);
+    GdlDockPlacement  pos = GDL_DOCK_NONE;
+    
+    if (item->child) {
+        GList *children, *l;
+
+        children = gtk_container_get_children (GTK_CONTAINER (item->child));
+        for (l = children; l; l = l->next) {
+            if (l->data == (gpointer) child) {
+                pos = GDL_DOCK_CENTER;
+                break;
+            }
+        }
+        g_list_free (children);
+    }
+
+    if (pos != GDL_DOCK_NONE) {
+        if (placement)
+            *placement = pos;
+        return TRUE;
+    }
+    else
+        return FALSE;
+}
+
+static void
+gdl_dock_notebook_present (GdlDockObject *object,
+                           GdlDockObject *child)
+{
+    GdlDockItem *item = GDL_DOCK_ITEM (object);
+    int i;
+    
+    i = gtk_notebook_page_num (GTK_NOTEBOOK (item->child),
+                               GTK_WIDGET (child));
+    if (i >= 0)
+        gtk_notebook_set_current_page (GTK_NOTEBOOK (item->child), i);
+
+    GDL_CALL_PARENT (GDL_DOCK_OBJECT_CLASS, present, (object, child));
+}
+
+static gboolean 
+gdl_dock_notebook_reorder (GdlDockObject    *object,
+                           GdlDockObject    *requestor,
+                           GdlDockPlacement  new_position,
+                           GValue           *other_data)
+{
+    GdlDockItem *item = GDL_DOCK_ITEM (object);
+    gint         current_position, new_pos = -1;
+    gboolean     handled = FALSE;
+    
+    if (item->child && new_position == GDL_DOCK_CENTER) {
+        current_position = gtk_notebook_page_num (GTK_NOTEBOOK (item->child),
+                                                  GTK_WIDGET (requestor));
+        if (current_position >= 0) {
+            handled = TRUE;
+    
+            if (other_data && G_VALUE_HOLDS (other_data, G_TYPE_INT))
+                new_pos = g_value_get_int (other_data);
+            
+            gtk_notebook_reorder_child (GTK_NOTEBOOK (item->child), 
+                                        GTK_WIDGET (requestor),
+                                        new_pos);
+        }
+    }
+    return handled;
+}
+
+/* ----- Public interface ----- */
+
+GtkWidget *
+gdl_dock_notebook_new (void)
+{
+    GdlDockNotebook *notebook;
+
+    notebook = GDL_DOCK_NOTEBOOK (g_object_new (GDL_TYPE_DOCK_NOTEBOOK, NULL));
+    GDL_DOCK_OBJECT_UNSET_FLAGS (notebook, GDL_DOCK_AUTOMATIC);
+    
+    return GTK_WIDGET (notebook);
+}
+

Added: trunk/src/ext/libgdl/gdl-dock-notebook.h
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-dock-notebook.h	Wed Dec 10 19:46:36 2008
@@ -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áez <gustavo giraldez gmx net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GDL_DOCK_NOTEBOOK_H__
+#define __GDL_DOCK_NOTEBOOK_H__
+
+#include "libgdl/gdl-dock-item.h"
+
+G_BEGIN_DECLS
+
+/* standard macros */
+#define GDL_TYPE_DOCK_NOTEBOOK            (gdl_dock_notebook_get_type ())
+#define GDL_DOCK_NOTEBOOK(obj)            (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_NOTEBOOK, GdlDockNotebook))
+#define GDL_DOCK_NOTEBOOK_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_NOTEBOOK, GdlDockNotebookClass))
+#define GDL_IS_DOCK_NOTEBOOK(obj)         (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_NOTEBOOK))
+#define GDL_IS_DOCK_NOTEBOOK_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_NOTEBOOK))
+#define GDL_DOCK_NOTEBOOK_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK_NOTEBOOK, GdlDockNotebookClass))
+
+/* data types & structures */
+typedef struct _GdlDockNotebook        GdlDockNotebook;
+typedef struct _GdlDockNotebookClass   GdlDockNotebookClass;
+
+struct _GdlDockNotebook {
+    GdlDockItem  item;
+};
+
+struct _GdlDockNotebookClass {
+    GdlDockItemClass  parent_class;
+};
+
+
+/* public interface */
+ 
+GtkWidget     *gdl_dock_notebook_new               (void);
+
+GType          gdl_dock_notebook_get_type          (void);
+
+G_END_DECLS
+
+#endif
+

Added: trunk/src/ext/libgdl/gdl-dock-object.c
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-dock-object.c	Wed Dec 10 19:46:36 2008
@@ -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áez <gustavo giraldez gmx net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gdl-i18n.h"
+#include <stdlib.h>
+#include <string.h>
+
+#include "gdl-tools.h"
+#include "gdl-dock-object.h"
+#include "gdl-dock-master.h"
+#include "libgdltypebuiltins.h"
+#include "libgdlmarshal.h"
+
+/* for later use by the registry */
+#include "gdl-dock.h"
+#include "gdl-dock-item.h"
+#include "gdl-dock-paned.h"
+#include "gdl-dock-notebook.h"
+#include "gdl-dock-placeholder.h"
+
+
+/* ----- Private prototypes ----- */
+
+static void     gdl_dock_object_class_init         (GdlDockObjectClass *klass);
+static void     gdl_dock_object_instance_init      (GdlDockObject      *object);
+
+static void     gdl_dock_object_set_property       (GObject            *g_object,
+                                                    guint               prop_id,
+                                                    const GValue       *value,
+                                                    GParamSpec         *pspec);
+static void     gdl_dock_object_get_property       (GObject            *g_object,
+                                                    guint               prop_id,
+                                                    GValue             *value,
+                                                    GParamSpec         *pspec);
+static void     gdl_dock_object_finalize           (GObject            *g_object);
+
+static void     gdl_dock_object_destroy            (GtkObject          *gtk_object);
+
+static void     gdl_dock_object_show               (GtkWidget          *widget);
+static void     gdl_dock_object_hide               (GtkWidget          *widget);
+
+static void     gdl_dock_object_real_detach        (GdlDockObject      *object,
+                                                    gboolean            recursive);
+static void     gdl_dock_object_real_reduce        (GdlDockObject      *object);
+static void     gdl_dock_object_dock_unimplemented (GdlDockObject     *object,
+                                                    GdlDockObject     *requestor,
+                                                    GdlDockPlacement   position,
+                                                    GValue            *other_data);
+static void     gdl_dock_object_real_present       (GdlDockObject     *object,
+                                                    GdlDockObject     *child);
+
+
+/* ----- Private data types and variables ----- */
+
+enum {
+    PROP_0,
+    PROP_NAME,
+    PROP_LONG_NAME,
+    PROP_STOCK_ID,
+    PROP_PIXBUF_ICON,
+    PROP_MASTER,
+    PROP_EXPORT_PROPERTIES
+};
+
+enum {
+    DETACH,
+    DOCK,
+    LAST_SIGNAL
+};
+
+static guint gdl_dock_object_signals [LAST_SIGNAL] = { 0 };
+
+/* ----- Private interface ----- */
+
+GDL_CLASS_BOILERPLATE (GdlDockObject, gdl_dock_object, GtkContainer, GTK_TYPE_CONTAINER);
+
+static void
+gdl_dock_object_class_init (GdlDockObjectClass *klass)
+{
+    GObjectClass      *g_object_class;
+    GtkObjectClass    *object_class;
+    GtkWidgetClass    *widget_class;
+    GtkContainerClass *container_class;
+
+    g_object_class = G_OBJECT_CLASS (klass);
+    object_class = GTK_OBJECT_CLASS (klass);
+    widget_class = GTK_WIDGET_CLASS (klass);
+    container_class = GTK_CONTAINER_CLASS (klass);
+
+    g_object_class->set_property = gdl_dock_object_set_property;
+    g_object_class->get_property = gdl_dock_object_get_property;
+    g_object_class->finalize = gdl_dock_object_finalize;
+
+    g_object_class_install_property (
+        g_object_class, PROP_NAME,
+        g_param_spec_string (GDL_DOCK_NAME_PROPERTY, _("Name"),
+                             _("Unique name for identifying the dock object"),
+                             NULL,
+                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                             GDL_DOCK_PARAM_EXPORT));
+
+    g_object_class_install_property (
+        g_object_class, PROP_LONG_NAME,
+        g_param_spec_string ("long-name", _("Long name"),
+                             _("Human readable name for the dock object"),
+                             NULL,
+                             G_PARAM_READWRITE));
+
+    g_object_class_install_property (
+        g_object_class, PROP_STOCK_ID,
+        g_param_spec_string ("stock-id", _("Stock Icon"),
+                             _("Stock icon for the dock object"),
+                             NULL,
+                             G_PARAM_READWRITE));
+
+    g_object_class_install_property (
+        g_object_class, PROP_PIXBUF_ICON,
+        g_param_spec_pointer ("pixbuf-icon", _("Pixbuf Icon"),
+                              _("Pixbuf icon for the dock object"),
+                              G_PARAM_READWRITE));
+
+    g_object_class_install_property (
+        g_object_class, PROP_MASTER,
+        g_param_spec_object ("master", _("Dock master"),
+                             _("Dock master this dock object is bound to"),
+                             GDL_TYPE_DOCK_MASTER,
+                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+    
+    object_class->destroy = gdl_dock_object_destroy;
+    
+    widget_class->show = gdl_dock_object_show;
+    widget_class->hide = gdl_dock_object_hide;
+    
+    klass->is_compound = TRUE;
+    
+    klass->detach = gdl_dock_object_real_detach;
+    klass->reduce = gdl_dock_object_real_reduce;
+    klass->dock_request = NULL;
+    klass->dock = gdl_dock_object_dock_unimplemented;
+    klass->reorder = NULL;
+    klass->present = gdl_dock_object_real_present;
+    klass->child_placement = NULL;
+    
+    gdl_dock_object_signals [DETACH] =
+        g_signal_new ("detach",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (GdlDockObjectClass, detach),
+                      NULL,
+                      NULL,
+                      gdl_marshal_VOID__BOOLEAN,
+                      G_TYPE_NONE,
+                      1,
+                      G_TYPE_BOOLEAN);
+
+    gdl_dock_object_signals [DOCK] =
+        g_signal_new ("dock",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_FIRST,
+                      G_STRUCT_OFFSET (GdlDockObjectClass, dock),
+                      NULL,
+                      NULL,
+                      gdl_marshal_VOID__OBJECT_ENUM_BOXED,
+                      G_TYPE_NONE,
+                      3,
+                      GDL_TYPE_DOCK_OBJECT,
+                      GDL_TYPE_DOCK_PLACEMENT,
+                      G_TYPE_VALUE);
+}
+
+static void
+gdl_dock_object_instance_init (GdlDockObject *object)
+{
+    object->flags = GDL_DOCK_AUTOMATIC;
+    object->freeze_count = 0;
+}
+
+static void
+gdl_dock_object_set_property  (GObject      *g_object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+    GdlDockObject *object = GDL_DOCK_OBJECT (g_object);
+
+    switch (prop_id) {
+    case PROP_NAME:
+        g_free (object->name);
+        object->name = g_value_dup_string (value);
+        break;
+    case PROP_LONG_NAME:
+        g_free (object->long_name);
+        object->long_name = g_value_dup_string (value);
+        break;
+    case PROP_STOCK_ID:
+        g_free (object->stock_id);
+        object->stock_id = g_value_dup_string (value);
+        break;
+    case PROP_PIXBUF_ICON:
+        object->pixbuf_icon = g_value_get_pointer (value);
+        break;
+    case PROP_MASTER:
+        if (g_value_get_object (value)) 
+            gdl_dock_object_bind (object, g_value_get_object (value));
+        else
+            gdl_dock_object_unbind (object);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+gdl_dock_object_get_property  (GObject      *g_object,
+                               guint         prop_id,
+                               GValue       *value,
+                               GParamSpec   *pspec)
+{
+    GdlDockObject *object = GDL_DOCK_OBJECT (g_object);
+
+    switch (prop_id) {
+    case PROP_NAME:
+        g_value_set_string (value, object->name);
+        break;
+    case PROP_LONG_NAME:
+        g_value_set_string (value, object->long_name);
+        break;
+    case PROP_STOCK_ID:
+        g_value_set_string (value, object->stock_id);
+        break;
+    case PROP_PIXBUF_ICON:
+        g_value_set_pointer (value, object->pixbuf_icon);
+        break;
+    case PROP_MASTER:
+        g_value_set_object (value, object->master);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+gdl_dock_object_finalize (GObject *g_object)
+{
+    GdlDockObject *object;
+    
+    g_return_if_fail (g_object != NULL && GDL_IS_DOCK_OBJECT (g_object));
+
+    object = GDL_DOCK_OBJECT (g_object);
+
+    g_free (object->name);
+    object->name = NULL;
+    g_free (object->long_name);
+    object->long_name = NULL;
+    g_free (object->stock_id);
+    object->stock_id = NULL;
+    object->pixbuf_icon = NULL;
+
+    GDL_CALL_PARENT (G_OBJECT_CLASS, finalize, (g_object));
+}
+
+static void
+gdl_dock_object_foreach_detach (GdlDockObject *object,
+                                gpointer       user_data)
+{
+    gdl_dock_object_detach (object, TRUE);
+}
+
+static void
+gdl_dock_object_destroy (GtkObject *gtk_object)
+{
+    GdlDockObject *object;
+
+    g_return_if_fail (GDL_IS_DOCK_OBJECT (gtk_object));
+
+    object = GDL_DOCK_OBJECT (gtk_object);
+    if (gdl_dock_object_is_compound (object)) {
+        /* detach our dock object children if we have some, and even
+           if we are not attached, so they can get notification */
+        gdl_dock_object_freeze (object);
+        gtk_container_foreach (GTK_CONTAINER (object),
+                               (GtkCallback) gdl_dock_object_foreach_detach,
+                               NULL);
+        object->reduce_pending = FALSE;
+        gdl_dock_object_thaw (object);
+    }
+    if (GDL_DOCK_OBJECT_ATTACHED (object)) {
+        /* detach ourselves */
+        gdl_dock_object_detach (object, FALSE);
+    }
+    
+    /* finally unbind us */
+    if (object->master)
+        gdl_dock_object_unbind (object);
+        
+    GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (gtk_object));
+}
+
+static void
+gdl_dock_object_foreach_automatic (GdlDockObject *object,
+                                   gpointer       user_data)
+{
+    void (* function) (GtkWidget *) = user_data;
+
+    if (GDL_DOCK_OBJECT_AUTOMATIC (object))
+        (* function) (GTK_WIDGET (object));
+}
+
+static void
+gdl_dock_object_show (GtkWidget *widget)
+{
+    if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT (widget))) {
+        gtk_container_foreach (GTK_CONTAINER (widget),
+                               (GtkCallback) gdl_dock_object_foreach_automatic,
+                               gtk_widget_show);
+    }
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, show, (widget));
+}
+
+static void
+gdl_dock_object_hide (GtkWidget *widget)
+{
+    if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT (widget))) {
+        gtk_container_foreach (GTK_CONTAINER (widget),
+                               (GtkCallback) gdl_dock_object_foreach_automatic,
+                               gtk_widget_hide);
+    }
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, hide, (widget));
+}
+
+static void
+gdl_dock_object_real_detach (GdlDockObject *object,
+                             gboolean       recursive)
+{
+    GdlDockObject *parent;
+    GtkWidget     *widget;
+    
+    g_return_if_fail (object != NULL);
+
+    /* detach children */
+    if (recursive && gdl_dock_object_is_compound (object)) {
+        gtk_container_foreach (GTK_CONTAINER (object),
+                               (GtkCallback) gdl_dock_object_detach,
+                               GINT_TO_POINTER (recursive));
+    }
+    
+    /* detach the object itself */
+    GDL_DOCK_OBJECT_UNSET_FLAGS (object, GDL_DOCK_ATTACHED);
+    parent = gdl_dock_object_get_parent_object (object);
+    widget = GTK_WIDGET (object);
+    if (widget->parent)
+        gtk_container_remove (GTK_CONTAINER (widget->parent), widget);
+    if (parent)
+        gdl_dock_object_reduce (parent);
+}
+
+static void
+gdl_dock_object_real_reduce (GdlDockObject *object)
+{
+    GdlDockObject *parent;
+    GList         *children;
+    
+    g_return_if_fail (object != NULL);
+
+    if (!gdl_dock_object_is_compound (object))
+        return;
+
+    parent = gdl_dock_object_get_parent_object (object);
+    children = gtk_container_get_children (GTK_CONTAINER (object));
+    if (g_list_length (children) <= 1) {
+        GList *l;
+        
+        /* detach ourselves and then re-attach our children to our
+           current parent.  if we are not currently attached, the
+           children are detached */
+        if (parent)
+            gdl_dock_object_freeze (parent);
+        gdl_dock_object_freeze (object);
+        gdl_dock_object_detach (object, FALSE);
+        for (l = children; l; l = l->next) {
+            GdlDockObject *child = GDL_DOCK_OBJECT (l->data);
+
+            g_object_ref (child);
+            GDL_DOCK_OBJECT_SET_FLAGS (child, GDL_DOCK_IN_REFLOW);
+            gdl_dock_object_detach (child, FALSE);
+            if (parent)
+                gtk_container_add (GTK_CONTAINER (parent), GTK_WIDGET (child));
+            GDL_DOCK_OBJECT_UNSET_FLAGS (child, GDL_DOCK_IN_REFLOW);
+            g_object_unref (child);
+        }
+        /* sink the widget, so any automatic floating widget is destroyed */
+        gtk_object_sink (GTK_OBJECT (object));
+        /* don't reenter */
+        object->reduce_pending = FALSE;
+        gdl_dock_object_thaw (object);
+        if (parent)
+            gdl_dock_object_thaw (parent);
+    }
+    g_list_free (children);
+}
+
+static void
+gdl_dock_object_dock_unimplemented (GdlDockObject    *object,
+                                    GdlDockObject    *requestor,
+                                    GdlDockPlacement  position,
+                                    GValue           *other_data)
+{
+    g_warning (_("Call to gdl_dock_object_dock in a dock object %p "
+                 "(object type is %s) which hasn't implemented this method"),
+               object, G_OBJECT_TYPE_NAME (object));
+}
+
+static void 
+gdl_dock_object_real_present (GdlDockObject *object,
+                              GdlDockObject *child)
+{
+    gtk_widget_show (GTK_WIDGET (object));
+}
+
+
+/* ----- Public interface ----- */
+
+gboolean
+gdl_dock_object_is_compound (GdlDockObject *object)
+{
+    GdlDockObjectClass *klass;
+
+    g_return_val_if_fail (object != NULL, FALSE);
+    g_return_val_if_fail (GDL_IS_DOCK_OBJECT (object), FALSE);
+
+    klass = GDL_DOCK_OBJECT_GET_CLASS (object);
+    return klass->is_compound;
+}
+
+void
+gdl_dock_object_detach (GdlDockObject *object,
+                        gboolean       recursive)
+{
+    g_return_if_fail (object != NULL);
+
+    if (!GDL_DOCK_OBJECT_ATTACHED (object))
+        return;
+    
+    /* freeze the object to avoid reducing while detaching children */
+    gdl_dock_object_freeze (object);
+    GDL_DOCK_OBJECT_SET_FLAGS (object, GDL_DOCK_IN_DETACH);
+    g_signal_emit (object, gdl_dock_object_signals [DETACH], 0, recursive);
+    GDL_DOCK_OBJECT_UNSET_FLAGS (object, GDL_DOCK_IN_DETACH);
+    gdl_dock_object_thaw (object);
+}
+
+GdlDockObject *
+gdl_dock_object_get_parent_object (GdlDockObject *object)
+{
+    GtkWidget *parent;
+    
+    g_return_val_if_fail (object != NULL, NULL);
+
+    parent = GTK_WIDGET (object)->parent;
+    while (parent && !GDL_IS_DOCK_OBJECT (parent)) {
+        parent = parent->parent;
+    }
+    
+    return parent ? GDL_DOCK_OBJECT (parent) : NULL;
+}
+
+void
+gdl_dock_object_freeze (GdlDockObject *object)
+{
+    g_return_if_fail (object != NULL);
+    
+    if (object->freeze_count == 0) {
+        g_object_ref (object);   /* dock objects shouldn't be
+                                    destroyed if they are frozen */
+    }
+    object->freeze_count++;
+}
+
+void
+gdl_dock_object_thaw (GdlDockObject *object)
+{
+    g_return_if_fail (object != NULL);
+    g_return_if_fail (object->freeze_count > 0);
+    
+    object->freeze_count--;
+    if (object->freeze_count == 0) {
+        if (object->reduce_pending) {
+            object->reduce_pending = FALSE;
+            gdl_dock_object_reduce (object);
+        }
+        g_object_unref (object);
+    }
+}
+
+void
+gdl_dock_object_reduce (GdlDockObject *object)
+{
+    g_return_if_fail (object != NULL);
+
+    if (GDL_DOCK_OBJECT_FROZEN (object)) {
+        object->reduce_pending = TRUE;
+        return;
+    }
+
+    GDL_CALL_VIRTUAL (object, GDL_DOCK_OBJECT_GET_CLASS, reduce, (object));
+}
+
+gboolean
+gdl_dock_object_dock_request (GdlDockObject  *object,
+                              gint            x,
+                              gint            y,
+                              GdlDockRequest *request)
+{
+    g_return_val_if_fail (object != NULL && request != NULL, FALSE);
+
+    return GDL_CALL_VIRTUAL_WITH_DEFAULT (object,
+                                          GDL_DOCK_OBJECT_GET_CLASS,
+                                          dock_request,
+                                          (object, x, y, request),
+                                          FALSE);
+}
+
+void
+gdl_dock_object_dock (GdlDockObject    *object,
+                      GdlDockObject    *requestor,
+                      GdlDockPlacement  position,
+                      GValue           *other_data)
+{
+    GdlDockObject *parent;
+    
+    g_return_if_fail (object != NULL && requestor != NULL);
+        
+    if (object == requestor)
+        return;
+    
+    if (!object->master)
+        g_warning (_("Dock operation requested in a non-bound object %p. "
+                     "The application might crash"), object);
+        
+    if (!gdl_dock_object_is_bound (requestor))
+        gdl_dock_object_bind (requestor, object->master);
+
+    if (requestor->master != object->master) {
+        g_warning (_("Cannot dock %p to %p because they belong to different masters"),
+                   requestor, object);
+        return;
+    }
+
+    /* first, see if we can optimize things by reordering */
+    if (position != GDL_DOCK_NONE) {
+        parent = gdl_dock_object_get_parent_object (object);
+        if (gdl_dock_object_reorder (object, requestor, position, other_data) ||
+            (parent && gdl_dock_object_reorder (parent, requestor, position, other_data)))
+            return;
+    }
+    
+    /* freeze the object, since under some conditions it might be destroyed when
+       detaching the requestor */
+    gdl_dock_object_freeze (object);
+
+    /* detach the requestor before docking */
+    g_object_ref (requestor);
+    if (GDL_DOCK_OBJECT_ATTACHED (requestor))
+        gdl_dock_object_detach (requestor, FALSE);
+    
+    if (position != GDL_DOCK_NONE)
+        g_signal_emit (object, gdl_dock_object_signals [DOCK], 0,
+                       requestor, position, other_data);
+
+    g_object_unref (requestor);
+    gdl_dock_object_thaw (object);
+}
+
+void
+gdl_dock_object_bind (GdlDockObject *object,
+                      GObject       *master)
+{
+    g_return_if_fail (object != NULL && master != NULL);
+    g_return_if_fail (GDL_IS_DOCK_MASTER (master));
+    
+    if (object->master == master)
+        /* nothing to do here */
+        return;
+    
+    if (object->master) {
+        g_warning (_("Attempt to bind to %p an already bound dock object %p "
+                     "(current master: %p)"), master, object, object->master);
+        return;
+    }
+
+    gdl_dock_master_add (GDL_DOCK_MASTER (master), object);
+    object->master = master;
+    g_object_add_weak_pointer (master, (gpointer *) &object->master);
+
+    g_object_notify (G_OBJECT (object), "master");
+}
+
+void
+gdl_dock_object_unbind (GdlDockObject *object)
+{
+    g_return_if_fail (object != NULL);
+
+    g_object_ref (object);
+
+    /* detach the object first */
+    if (GDL_DOCK_OBJECT_ATTACHED (object))
+        gdl_dock_object_detach (object, TRUE);
+    
+    if (object->master) {
+        GObject *master = object->master;
+        g_object_remove_weak_pointer (master, (gpointer *) &object->master);
+        object->master = NULL;
+        gdl_dock_master_remove (GDL_DOCK_MASTER (master), object);
+        g_object_notify (G_OBJECT (object), "master");
+    }
+    g_object_unref (object);
+}
+
+gboolean
+gdl_dock_object_is_bound (GdlDockObject *object)
+{
+    g_return_val_if_fail (object != NULL, FALSE);
+    return (object->master != NULL);
+}
+
+gboolean
+gdl_dock_object_reorder (GdlDockObject    *object,
+                         GdlDockObject    *child,
+                         GdlDockPlacement  new_position,
+                         GValue           *other_data)
+{
+    g_return_val_if_fail (object != NULL && child != NULL, FALSE);
+
+    return GDL_CALL_VIRTUAL_WITH_DEFAULT (object,
+                                          GDL_DOCK_OBJECT_GET_CLASS,
+                                          reorder,
+                                          (object, child, new_position, other_data),
+                                          FALSE);
+}
+
+void 
+gdl_dock_object_present (GdlDockObject *object,
+                         GdlDockObject *child)
+{
+    GdlDockObject *parent;
+    
+    g_return_if_fail (object != NULL && GDL_IS_DOCK_OBJECT (object));
+
+    parent = gdl_dock_object_get_parent_object (object);
+    if (parent)
+        /* chain the call to our parent */
+        gdl_dock_object_present (parent, object);
+
+    GDL_CALL_VIRTUAL (object, GDL_DOCK_OBJECT_GET_CLASS, present, (object, child));
+}
+
+/**
+ * gdl_dock_object_child_placement:
+ * @object: the dock object we are asking for child placement
+ * @child: the child of the @object we want the placement for
+ * @placement: where to return the placement information
+ *
+ * This function returns information about placement of a child dock
+ * object inside another dock object.  The function returns %TRUE if
+ * @child is effectively a child of @object.  @placement should
+ * normally be initially setup to %GDL_DOCK_NONE.  If it's set to some
+ * other value, this function will not touch the stored value if the
+ * specified placement is "compatible" with the actual placement of
+ * the child.
+ *
+ * @placement can be %NULL, in which case the function simply tells if
+ * @child is attached to @object.
+ *
+ * Returns: %TRUE if @child is a child of @object.
+ */
+gboolean 
+gdl_dock_object_child_placement (GdlDockObject    *object,
+                                 GdlDockObject    *child,
+                                 GdlDockPlacement *placement)
+{
+    g_return_val_if_fail (object != NULL && child != NULL, FALSE);
+
+    /* simple case */
+    if (!gdl_dock_object_is_compound (object))
+        return FALSE;
+    
+    return GDL_CALL_VIRTUAL_WITH_DEFAULT (object, GDL_DOCK_OBJECT_GET_CLASS,
+                                          child_placement,
+                                          (object, child, placement),
+                                          FALSE);
+}
+
+
+/* ----- dock param type functions start here ------ */
+
+static void 
+gdl_dock_param_export_int (const GValue *src,
+                           GValue       *dst)
+{
+    dst->data [0].v_pointer = g_strdup_printf ("%d", src->data [0].v_int);
+}
+
+static void 
+gdl_dock_param_export_uint (const GValue *src,
+                            GValue       *dst)
+{
+    dst->data [0].v_pointer = g_strdup_printf ("%u", src->data [0].v_uint);
+}
+
+static void 
+gdl_dock_param_export_string (const GValue *src,
+                              GValue       *dst)
+{
+    dst->data [0].v_pointer = g_strdup (src->data [0].v_pointer);
+}
+
+static void 
+gdl_dock_param_export_bool (const GValue *src,
+                            GValue       *dst)
+{
+    dst->data [0].v_pointer = g_strdup_printf ("%s", src->data [0].v_int ? "yes" : "no");
+}
+
+static void 
+gdl_dock_param_export_placement (const GValue *src,
+                                 GValue       *dst)
+{
+    switch (src->data [0].v_int) {
+        case GDL_DOCK_NONE:
+            dst->data [0].v_pointer = g_strdup ("");
+            break;
+        case GDL_DOCK_TOP:
+            dst->data [0].v_pointer = g_strdup ("top");
+            break;
+        case GDL_DOCK_BOTTOM:
+            dst->data [0].v_pointer = g_strdup ("bottom");
+            break;
+        case GDL_DOCK_LEFT:
+            dst->data [0].v_pointer = g_strdup ("left");
+            break;
+        case GDL_DOCK_RIGHT:
+            dst->data [0].v_pointer = g_strdup ("right");
+            break;
+        case GDL_DOCK_CENTER:
+            dst->data [0].v_pointer = g_strdup ("center");
+            break;
+        case GDL_DOCK_FLOATING:
+            dst->data [0].v_pointer = g_strdup ("floating");
+            break;
+    }
+}
+
+static void 
+gdl_dock_param_import_int (const GValue *src,
+                           GValue       *dst)
+{
+    dst->data [0].v_int = atoi (src->data [0].v_pointer);
+}
+
+static void 
+gdl_dock_param_import_uint (const GValue *src,
+                            GValue       *dst)
+{
+    dst->data [0].v_uint = (guint) atoi (src->data [0].v_pointer);
+}
+
+static void 
+gdl_dock_param_import_string (const GValue *src,
+                              GValue       *dst)
+{
+    dst->data [0].v_pointer = g_strdup (src->data [0].v_pointer);
+}
+
+static void 
+gdl_dock_param_import_bool (const GValue *src,
+                            GValue       *dst)
+{
+    dst->data [0].v_int = !strcmp (src->data [0].v_pointer, "yes");
+}
+
+static void 
+gdl_dock_param_import_placement (const GValue *src,
+                                 GValue       *dst)
+{
+    if (!strcmp (src->data [0].v_pointer, "top"))
+        dst->data [0].v_int = GDL_DOCK_TOP;
+    else if (!strcmp (src->data [0].v_pointer, "bottom"))
+        dst->data [0].v_int = GDL_DOCK_BOTTOM;
+    else if (!strcmp (src->data [0].v_pointer, "center"))
+        dst->data [0].v_int = GDL_DOCK_CENTER;
+    else if (!strcmp (src->data [0].v_pointer, "left"))
+        dst->data [0].v_int = GDL_DOCK_LEFT;
+    else if (!strcmp (src->data [0].v_pointer, "right"))
+        dst->data [0].v_int = GDL_DOCK_RIGHT;
+    else if (!strcmp (src->data [0].v_pointer, "floating"))
+        dst->data [0].v_int = GDL_DOCK_FLOATING;
+    else
+        dst->data [0].v_int = GDL_DOCK_NONE;
+}
+
+GType
+gdl_dock_param_get_type (void)
+{
+    static GType our_type = 0;
+
+    if (our_type == 0) {
+        GTypeInfo tinfo = { 0, };
+        our_type = g_type_register_static (G_TYPE_STRING, "GdlDockParam", &tinfo, 0);
+
+        /* register known transform functions */
+        /* exporters */
+        g_value_register_transform_func (G_TYPE_INT, our_type, gdl_dock_param_export_int);
+        g_value_register_transform_func (G_TYPE_UINT, our_type, gdl_dock_param_export_uint);
+        g_value_register_transform_func (G_TYPE_STRING, our_type, gdl_dock_param_export_string);
+        g_value_register_transform_func (G_TYPE_BOOLEAN, our_type, gdl_dock_param_export_bool);
+        g_value_register_transform_func (GDL_TYPE_DOCK_PLACEMENT, our_type, gdl_dock_param_export_placement);
+        /* importers */
+        g_value_register_transform_func (our_type, G_TYPE_INT, gdl_dock_param_import_int);
+        g_value_register_transform_func (our_type, G_TYPE_UINT, gdl_dock_param_import_uint);
+        g_value_register_transform_func (our_type, G_TYPE_STRING, gdl_dock_param_import_string);
+        g_value_register_transform_func (our_type, G_TYPE_BOOLEAN, gdl_dock_param_import_bool);
+        g_value_register_transform_func (our_type, GDL_TYPE_DOCK_PLACEMENT, gdl_dock_param_import_placement);
+    }
+
+    return our_type;
+}
+
+/* -------------- nick <-> type conversion functions --------------- */
+
+static GRelation *dock_register = NULL;
+
+enum {
+    INDEX_NICK = 0,
+    INDEX_TYPE
+};
+
+static void
+gdl_dock_object_register_init (void)
+{
+    if (dock_register)
+        return;
+    
+    /* FIXME: i don't know if GRelation is efficient */
+    dock_register = g_relation_new (2);
+    g_relation_index (dock_register, INDEX_NICK, g_str_hash, g_str_equal);
+    g_relation_index (dock_register, INDEX_TYPE, g_direct_hash, g_direct_equal);
+
+    /* add known types */
+    g_relation_insert (dock_register, "dock", (gpointer) GDL_TYPE_DOCK);
+    g_relation_insert (dock_register, "item", (gpointer) GDL_TYPE_DOCK_ITEM);
+    g_relation_insert (dock_register, "paned", (gpointer) GDL_TYPE_DOCK_PANED);
+    g_relation_insert (dock_register, "notebook", (gpointer) GDL_TYPE_DOCK_NOTEBOOK);
+    g_relation_insert (dock_register, "placeholder", (gpointer) GDL_TYPE_DOCK_PLACEHOLDER);
+}
+
+G_CONST_RETURN gchar *
+gdl_dock_object_nick_from_type (GType type)
+{
+    GTuples *tuples;
+    gchar *nick = NULL;
+    
+    if (!dock_register)
+        gdl_dock_object_register_init ();
+
+    if (g_relation_count (dock_register, (gpointer) type, INDEX_TYPE) > 0) {
+        tuples = g_relation_select (dock_register, (gpointer) type, INDEX_TYPE);
+        nick = (gchar *) g_tuples_index (tuples, 0, INDEX_NICK);
+        g_tuples_destroy (tuples);
+    }
+    
+    return nick ? nick : g_type_name (type);
+}
+
+GType
+gdl_dock_object_type_from_nick (const gchar *nick)
+{
+    GTuples *tuples;
+    GType type = G_TYPE_NONE;
+    
+    if (!dock_register)
+        gdl_dock_object_register_init ();
+
+    if (g_relation_count (dock_register, (gpointer) nick, INDEX_NICK) > 0) {
+        tuples = g_relation_select (dock_register, (gpointer) nick, INDEX_NICK);
+        type = (GType) g_tuples_index (tuples, 0, INDEX_TYPE);
+        g_tuples_destroy (tuples);
+    }
+    else {
+        /* try searching in the glib type system */
+        type = g_type_from_name (nick);
+    }
+    
+    return type;
+}
+
+GType
+gdl_dock_object_set_type_for_nick (const gchar *nick,
+                                   GType        type)
+{
+    GType old_type = G_TYPE_NONE;
+    
+    if (!dock_register)
+        gdl_dock_object_register_init ();
+
+    g_return_val_if_fail (g_type_is_a (type, GDL_TYPE_DOCK_OBJECT), G_TYPE_NONE);
+    
+    if (g_relation_count (dock_register, (gpointer) nick, INDEX_NICK) > 0) {
+        old_type = gdl_dock_object_type_from_nick (nick);
+        g_relation_delete (dock_register, (gpointer) nick, INDEX_NICK);
+    }
+    
+    g_relation_insert (dock_register, nick, type);
+
+    return old_type;
+}
+

Added: trunk/src/ext/libgdl/gdl-dock-object.h
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-dock-object.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,239 @@
+/* -*- 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áez <gustavo giraldez gmx net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GDL_DOCK_OBJECT_H__
+#define __GDL_DOCK_OBJECT_H__
+
+#ifdef __GNUC__
+// we have too mark this as a system header because otherwise GCC barfs 
+// on variadic macros.
+#pragma GCC system_header
+#endif
+
+#include <gtk/gtkcontainer.h>
+
+G_BEGIN_DECLS
+
+/* standard macros */
+#define GDL_TYPE_DOCK_OBJECT             (gdl_dock_object_get_type ())
+#define GDL_DOCK_OBJECT(obj)             (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_OBJECT, GdlDockObject))
+#define GDL_DOCK_OBJECT_CLASS(klass)     (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_OBJECT, GdlDockObjectClass))
+#define GDL_IS_DOCK_OBJECT(obj)          (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_OBJECT))
+#define GDL_IS_DOCK_OBJECT_CLASS(klass)  (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_OBJECT))
+#define GDL_DOCK_OBJECT_GET_CLASS(obj)   (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK_OBJECT, GdlDockObjectClass))
+
+/* data types & structures */
+typedef enum {
+    /* the parameter is to be exported for later layout rebuilding */
+    GDL_DOCK_PARAM_EXPORT = 1 << G_PARAM_USER_SHIFT,
+    /* the parameter must be set after adding the children objects */
+    GDL_DOCK_PARAM_AFTER  = 1 << (G_PARAM_USER_SHIFT + 1)
+} GdlDockParamFlags;
+
+#define GDL_DOCK_NAME_PROPERTY    "name"
+#define GDL_DOCK_MASTER_PROPERTY  "master"
+
+typedef enum {
+    GDL_DOCK_AUTOMATIC  = 1 << 0,
+    GDL_DOCK_ATTACHED   = 1 << 1,
+    GDL_DOCK_IN_REFLOW  = 1 << 2,
+    GDL_DOCK_IN_DETACH  = 1 << 3
+} GdlDockObjectFlags;
+
+#define GDL_DOCK_OBJECT_FLAGS_SHIFT 8
+
+typedef enum {
+    GDL_DOCK_NONE = 0,
+    GDL_DOCK_TOP,
+    GDL_DOCK_BOTTOM,
+    GDL_DOCK_RIGHT,
+    GDL_DOCK_LEFT,
+    GDL_DOCK_CENTER,
+    GDL_DOCK_FLOATING
+} GdlDockPlacement;
+
+typedef enum {
+    GDL_DOCK_EXPANSION_DIRECTION_NONE = 0,
+    GDL_DOCK_EXPANSION_DIRECTION_UP,
+    GDL_DOCK_EXPANSION_DIRECTION_DOWN,
+    GDL_DOCK_EXPANSION_DIRECTION_LEFT,
+    GDL_DOCK_EXPANSION_DIRECTION_RIGHT
+} GdlDockExpansionDirection;
+
+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__ */
+

Added: trunk/src/ext/libgdl/gdl-dock-paned.c
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-dock-paned.c	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,797 @@
+/* -*- 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áez <gustavo giraldez gmx net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gdl-i18n.h"
+#include <string.h>
+#include <gtk/gtkhpaned.h>
+#include <gtk/gtkvpaned.h>
+
+#include "gdl-tools.h"
+#include "gdl-dock-paned.h"
+
+
+/* Private prototypes */
+
+static void     gdl_dock_paned_class_init     (GdlDockPanedClass *klass);
+static void     gdl_dock_paned_instance_init  (GdlDockPaned      *paned);
+static GObject *gdl_dock_paned_constructor    (GType              type,
+                                               guint              n_construct_properties,
+                                               GObjectConstructParam *construct_param);
+static void     gdl_dock_paned_set_property   (GObject           *object,
+                                               guint              prop_id,
+                                               const GValue      *value,
+                                               GParamSpec        *pspec);
+static void     gdl_dock_paned_get_property   (GObject           *object,
+                                               guint              prop_id,
+                                               GValue            *value,
+                                               GParamSpec        *pspec);
+
+static void     gdl_dock_paned_destroy        (GtkObject         *object);
+
+static void     gdl_dock_paned_add            (GtkContainer      *container,
+                                               GtkWidget         *widget);
+static void     gdl_dock_paned_forall         (GtkContainer      *container,
+                                               gboolean           include_internals,
+                                               GtkCallback        callback,
+                                               gpointer           callback_data);
+static GType    gdl_dock_paned_child_type     (GtkContainer      *container);
+
+static gboolean gdl_dock_paned_dock_request   (GdlDockObject     *object, 
+                                               gint               x,
+                                               gint               y, 
+                                               GdlDockRequest    *request);
+static void     gdl_dock_paned_dock           (GdlDockObject    *object,
+                                               GdlDockObject    *requestor,
+                                               GdlDockPlacement  position,
+                                               GValue           *other_data);
+
+static void     gdl_dock_paned_set_orientation (GdlDockItem    *item,
+                                                GtkOrientation  orientation);
+
+static gboolean gdl_dock_paned_child_placement (GdlDockObject    *object,
+                                                GdlDockObject    *child,
+                                                GdlDockPlacement *placement);
+
+
+/* ----- Class variables and definitions ----- */
+
+#define SPLIT_RATIO  0.3
+
+enum {
+    PROP_0,
+    PROP_POSITION
+};
+
+
+/* ----- Private functions ----- */
+
+GDL_CLASS_BOILERPLATE (GdlDockPaned, gdl_dock_paned, GdlDockItem, GDL_TYPE_DOCK_ITEM);
+
+static void
+gdl_dock_paned_class_init (GdlDockPanedClass *klass)
+{
+    GObjectClass       *g_object_class;
+    GtkObjectClass     *gtk_object_class;
+    GtkWidgetClass     *widget_class;
+    GtkContainerClass  *container_class;
+    GdlDockObjectClass *object_class;
+    GdlDockItemClass   *item_class;
+
+    g_object_class = G_OBJECT_CLASS (klass);
+    gtk_object_class = GTK_OBJECT_CLASS (klass);
+    widget_class = GTK_WIDGET_CLASS (klass);
+    container_class = GTK_CONTAINER_CLASS (klass);
+    object_class = GDL_DOCK_OBJECT_CLASS (klass);
+    item_class = GDL_DOCK_ITEM_CLASS (klass);
+
+    g_object_class->set_property = gdl_dock_paned_set_property;
+    g_object_class->get_property = gdl_dock_paned_get_property;
+    g_object_class->constructor = gdl_dock_paned_constructor;
+    
+    gtk_object_class->destroy = gdl_dock_paned_destroy;
+
+    container_class->add = gdl_dock_paned_add;
+    container_class->forall = gdl_dock_paned_forall;
+    container_class->child_type = gdl_dock_paned_child_type;
+    
+    object_class->is_compound = TRUE;
+    
+    object_class->dock_request = gdl_dock_paned_dock_request;
+    object_class->dock = gdl_dock_paned_dock;
+    object_class->child_placement = gdl_dock_paned_child_placement;
+    
+    item_class->has_grip = FALSE;
+    item_class->set_orientation = gdl_dock_paned_set_orientation;    
+
+    g_object_class_install_property (
+        g_object_class, PROP_POSITION,
+        g_param_spec_uint ("position", _("Position"),
+                           _("Position of the divider in pixels"),
+                           0, G_MAXINT, 0,
+                           G_PARAM_READWRITE |
+                           GDL_DOCK_PARAM_EXPORT | GDL_DOCK_PARAM_AFTER));
+}
+
+static void
+gdl_dock_paned_instance_init (GdlDockPaned *paned)
+{
+    paned->position_changed = FALSE;
+    paned->in_drag = FALSE;
+    paned->last_drag_position = -1;
+}
+
+static void
+gdl_dock_paned_resize_paned_ancestors (GdlDockPaned       *paned, 
+                                       GdlDockExpansionDirection  expansion_direction,
+                                       gint                diff)
+{
+     GtkWidget *widget, *last_widget;
+
+     g_return_if_fail (GDL_IS_DOCK_PANED (paned));
+
+     last_widget = GTK_WIDGET (paned);
+
+     /* make sure resizing only can be done in the drag direction */
+     if (expansion_direction == GDL_DOCK_EXPANSION_DIRECTION_NONE ||
+         ((expansion_direction == GDL_DOCK_EXPANSION_DIRECTION_DOWN || 
+           expansion_direction == GDL_DOCK_EXPANSION_DIRECTION_UP) && 
+          !GTK_IS_VPANED (GDL_DOCK_ITEM (paned)->child)) ||
+         ((expansion_direction == GDL_DOCK_EXPANSION_DIRECTION_LEFT || 
+           expansion_direction == GDL_DOCK_EXPANSION_DIRECTION_RIGHT) && 
+          !GTK_IS_HPANED (GDL_DOCK_ITEM (paned)->child)))
+     {         
+         return;
+     }
+
+     for (widget = GTK_WIDGET (paned)->parent; widget != NULL; 
+          widget = widget->parent) {
+
+         if (GTK_IS_PANED (widget)) {
+
+             GtkPaned *paned = GTK_PANED (widget);
+
+                 if (last_widget == paned->child1) {
+
+                     if (!GDL_IS_DOCK_OBJECT(widget->parent)) {
+                         GtkRequisition requisition;
+                         GtkAllocation allocation = paned->child1->allocation;
+
+                         gtk_widget_size_request (paned->child1, &requisition);
+
+                         gint new_height =
+                             (allocation.height > requisition.height && diff > 0 ?
+                              allocation.height : requisition.height) + diff;
+
+                         gtk_widget_set_size_request (paned->child1, -1, new_height);
+                     }
+                     
+                     gtk_paned_set_position (paned, gtk_paned_get_position (paned) + diff);
+                 }
+             
+         } else if (!GDL_IS_DOCK_OBJECT (widget)) {
+             break;
+         }
+
+         last_widget = widget;
+     }
+}
+
+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")) {
+
+        GdlDockExpansionDirection expansion_direction;
+
+        paned->position_changed = TRUE;
+
+        g_object_get (GDL_DOCK_OBJECT(paned)->master,
+                      "expand-direction", &expansion_direction,
+                      NULL);
+
+        switch (expansion_direction) {
+        case GDL_DOCK_EXPANSION_DIRECTION_DOWN:
+            if (paned->in_drag) {
+                gint max_position, position, diff;
+                g_object_get (GDL_DOCK_ITEM (paned)->child,
+                              "max-position", &max_position,
+                              "position", &position,
+                              NULL);
+ 
+                diff = position - paned->last_drag_position;
+                paned->last_drag_position = position;
+
+                if (diff > 0 && position == max_position) {
+                    gdl_dock_paned_resize_paned_ancestors (paned, expansion_direction, diff + 1);
+                } else {
+                    gdl_dock_paned_resize_paned_ancestors (paned, expansion_direction, diff);
+                }
+                 
+            }
+            break;
+        default:
+            ;
+        }
+    }
+}
+
+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) {
+
+            GdlDockExpansionDirection expansion_direction;
+
+            GDL_DOCK_ITEM_SET_FLAGS (user_data, GDL_DOCK_USER_ACTION);
+
+            paned->in_drag = TRUE;
+
+            g_object_get (GDL_DOCK_OBJECT (paned)->master,
+                          "expand-direction", &expansion_direction,
+                          NULL);
+
+            switch (expansion_direction) {
+            case GDL_DOCK_EXPANSION_DIRECTION_DOWN:
+            {
+                gint max_position, position;
+                g_object_get (GDL_DOCK_ITEM (paned)->child,
+                              "max-position", &max_position,
+                              "position", &position,
+                              NULL);
+
+                paned->last_drag_position = position;
+                /* expand the paned's ancestors a bit if the separator is in max position
+                 * to allow dragging in all directions */
+                if (position == max_position)
+                    gdl_dock_paned_resize_paned_ancestors (paned, expansion_direction, 1);
+                break;
+            }
+            default:
+                ;
+            }
+        }
+        else if (event->type == GDK_BUTTON_RELEASE) {
+	     paned->last_drag_position = -1;
+	     paned->in_drag = FALSE;
+        }
+        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), TRUE, 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);
+}

Added: trunk/src/ext/libgdl/gdl-dock-paned.h
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-dock-paned.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,66 @@
+/* -*- 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áez <gustavo giraldez gmx net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GDL_DOCK_PANED_H__
+#define __GDL_DOCK_PANED_H__
+
+#include "libgdl/gdl-dock-item.h"
+
+G_BEGIN_DECLS
+
+/* standard macros */
+#define GDL_TYPE_DOCK_PANED                  (gdl_dock_paned_get_type ())
+#define GDL_DOCK_PANED(obj)                  (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_PANED, GdlDockPaned))
+#define GDL_DOCK_PANED_CLASS(klass)          (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_PANED, GdlDockPanedClass))
+#define GDL_IS_DOCK_PANED(obj)               (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_PANED))
+#define GDL_IS_DOCK_PANED_CLASS(klass)       (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_PANED))
+#define GDL_DOCK_PANED_GET_CLASS(obj)        (GTK_CHECK_GET_CLASS ((obj), GDL_TYE_DOCK_PANED, GdlDockPanedClass))
+
+/* data types & structures */
+typedef struct _GdlDockPaned      GdlDockPaned;
+typedef struct _GdlDockPanedClass GdlDockPanedClass;
+
+struct _GdlDockPaned {
+    GdlDockItem  dock_item;
+
+    gboolean     position_changed;
+    gboolean     in_drag;
+    gint         last_drag_position;
+};
+
+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__ */
+

Added: trunk/src/ext/libgdl/gdl-dock-placeholder.c
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-dock-placeholder.c	Wed Dec 10 19:46:36 2008
@@ -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áez <gustavo giraldez gmx net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gdl-i18n.h"
+
+#include "gdl-tools.h"
+#include "gdl-dock-placeholder.h"
+#include "gdl-dock-item.h"
+#include "gdl-dock-master.h"
+#include "libgdltypebuiltins.h"
+
+
+#undef PLACEHOLDER_DEBUG
+
+/* ----- Private prototypes ----- */
+
+static void     gdl_dock_placeholder_class_init     (GdlDockPlaceholderClass *klass);
+static void     gdl_dock_placeholder_instance_init  (GdlDockPlaceholder      *ph);
+
+static void     gdl_dock_placeholder_set_property   (GObject                 *g_object,
+                                                     guint                    prop_id,
+                                                     const GValue            *value,
+                                                     GParamSpec              *pspec);
+static void     gdl_dock_placeholder_get_property   (GObject                 *g_object,
+                                                     guint                    prop_id,
+                                                     GValue                  *value,
+                                                     GParamSpec              *pspec);
+
+static void     gdl_dock_placeholder_destroy        (GtkObject               *object);
+
+static void     gdl_dock_placeholder_add            (GtkContainer            *container,
+                                                     GtkWidget               *widget);
+
+static void     gdl_dock_placeholder_detach         (GdlDockObject           *object,
+                                                     gboolean                 recursive);
+static void     gdl_dock_placeholder_reduce         (GdlDockObject           *object);
+static void     gdl_dock_placeholder_dock           (GdlDockObject           *object,
+                                                     GdlDockObject           *requestor,
+                                                     GdlDockPlacement         position,
+                                                     GValue                  *other_data);
+
+static void     gdl_dock_placeholder_weak_notify    (gpointer                 data,
+                                                     GObject                 *old_object);
+
+static void     disconnect_host                     (GdlDockPlaceholder      *ph);
+static void     connect_host                        (GdlDockPlaceholder      *ph,
+                                                     GdlDockObject           *new_host);
+static void     do_excursion                        (GdlDockPlaceholder      *ph);
+
+static void     gdl_dock_placeholder_present        (GdlDockObject           *object,
+                                                     GdlDockObject           *child);
+
+static void     detach_cb                           (GdlDockObject           *object,
+                                                     gboolean                 recursive,
+                                                     gpointer                 user_data);
+
+/* ----- Private variables and data structures ----- */
+
+enum {
+    PROP_0,
+    PROP_STICKY,
+    PROP_HOST,
+    PROP_NEXT_PLACEMENT,
+    PROP_WIDTH,
+    PROP_HEIGHT,
+	PROP_FLOATING,
+	PROP_FLOAT_X,
+	PROP_FLOAT_Y
+};
+
+struct _GdlDockPlaceholderPrivate {
+    /* current object this placeholder is pinned to */
+    GdlDockObject    *host;
+    gboolean          sticky;
+    
+    /* when the placeholder is moved up the hierarchy, this stack
+       keeps track of the necessary dock positions needed to get the
+       placeholder to the original position */
+    GSList           *placement_stack;
+
+    /* Width and height of the attachments */
+    gint              width;
+    gint              height;
+    
+    /* connected signal handlers */
+    guint             host_detach_handler;
+    guint             host_dock_handler;
+	
+	/* Window Coordinates if Dock was floating */
+	gboolean    	floating;
+	gint              floatx;
+	gint              floaty;
+};
+
+
+/* ----- Private interface ----- */
+
+GDL_CLASS_BOILERPLATE (GdlDockPlaceholder, gdl_dock_placeholder,
+                       GdlDockObject, GDL_TYPE_DOCK_OBJECT);
+
+static void 
+gdl_dock_placeholder_class_init (GdlDockPlaceholderClass *klass)
+{
+    GObjectClass       *g_object_class;
+    GtkObjectClass     *gtk_object_class;
+    GtkContainerClass  *container_class;
+    GdlDockObjectClass *object_class;
+    
+    g_object_class = G_OBJECT_CLASS (klass);
+    gtk_object_class = GTK_OBJECT_CLASS (klass);
+    container_class = GTK_CONTAINER_CLASS (klass);
+    object_class = GDL_DOCK_OBJECT_CLASS (klass);
+
+    g_object_class->get_property = gdl_dock_placeholder_get_property;
+    g_object_class->set_property = gdl_dock_placeholder_set_property;
+    
+    g_object_class_install_property (
+        g_object_class, PROP_STICKY,
+        g_param_spec_boolean ("sticky", _("Sticky"),
+        					_("Whether the placeholder will stick to its host or "
+                			"move up the hierarchy when the host is redocked"),
+							FALSE,
+       						G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+    
+    g_object_class_install_property (
+    	g_object_class, PROP_HOST,
+        g_param_spec_object ("host", _("Host"),
+                            _("The dock object this placeholder is attached to"),
+                            GDL_TYPE_DOCK_OBJECT,
+                            G_PARAM_READWRITE));
+    
+    /* this will return the top of the placement stack */
+    g_object_class_install_property (
+        g_object_class, PROP_NEXT_PLACEMENT,
+        g_param_spec_enum ("next-placement", _("Next placement"),
+         					_("The position an item will be docked to our host if a "
+           					"request is made to dock to us"),
+                           	GDL_TYPE_DOCK_PLACEMENT,
+                           	GDL_DOCK_CENTER,
+                           	G_PARAM_READWRITE |
+                           	GDL_DOCK_PARAM_EXPORT | GDL_DOCK_PARAM_AFTER));
+    
+    g_object_class_install_property (
+        g_object_class, PROP_WIDTH,
+        g_param_spec_int ("width", _("Width"),
+                          	_("Width for the widget when it's attached to the placeholder"),
+                          	-1, G_MAXINT, -1,
+                          	G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+                          	GDL_DOCK_PARAM_EXPORT));
+    
+    g_object_class_install_property (
+        g_object_class, PROP_HEIGHT,
+        g_param_spec_int ("height", _("Height"),
+                     		_("Height for the widget when it's attached to the placeholder"),
+                          	-1, G_MAXINT, -1,
+                          	G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+                          	GDL_DOCK_PARAM_EXPORT));
+	g_object_class_install_property (
+        g_object_class, PROP_FLOATING,
+		g_param_spec_boolean ("floating", _("Floating Toplevel"),
+                            _("Whether the placeholder is standing in for a "
+                            "floating toplevel dock"),
+                            FALSE,
+                            G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+	g_object_class_install_property (
+        g_object_class, PROP_FLOAT_X,
+        g_param_spec_int ("floatx", _("X-Coordinate"),
+                          	_("X coordinate for 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 for 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_TOP;   /* 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 ("%s",_("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));
+}

Added: trunk/src/ext/libgdl/gdl-dock-placeholder.h
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-dock-placeholder.h	Wed Dec 10 19:46:36 2008
@@ -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áez <gustavo giraldez gmx net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GDL_DOCK_PLACEHOLDER_H__
+#define __GDL_DOCK_PLACEHOLDER_H__
+
+#include "libgdl/gdl-dock-object.h"
+
+G_BEGIN_DECLS
+
+/* standard macros */
+#define GDL_TYPE_DOCK_PLACEHOLDER             (gdl_dock_placeholder_get_type ())
+#define GDL_DOCK_PLACEHOLDER(obj)             (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_PLACEHOLDER, GdlDockPlaceholder))
+#define GDL_DOCK_PLACEHOLDER_CLASS(klass)     (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_PLACEHOLDER, GdlDockPlaceholderClass))
+#define GDL_IS_DOCK_PLACEHOLDER(obj)          (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_PLACEHOLDER))
+#define GDL_IS_DOCK_PLACEHOLDER_CLASS(klass)  (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_PLACEHOLDER))
+#define GDL_DOCK_PLACEHOLDER_GET_CLASS(obj)   (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK_PLACEHOLDER, GdlDockPlaceholderClass))
+
+/* data types & structures */
+typedef struct _GdlDockPlaceholder        GdlDockPlaceholder;
+typedef struct _GdlDockPlaceholderClass   GdlDockPlaceholderClass;
+typedef struct _GdlDockPlaceholderPrivate GdlDockPlaceholderPrivate;
+
+struct _GdlDockPlaceholder {
+    GdlDockObject              object;
+
+    GdlDockPlaceholderPrivate *_priv;
+};
+
+struct _GdlDockPlaceholderClass {
+    GdlDockObjectClass parent_class;
+};
+
+/* public interface */
+
+GType       gdl_dock_placeholder_get_type (void);
+
+GtkWidget  *gdl_dock_placeholder_new      (gchar              *name,
+                                           GdlDockObject      *object,
+                                           GdlDockPlacement    position,
+                                           gboolean            sticky);
+
+void        gdl_dock_placeholder_attach   (GdlDockPlaceholder *ph,
+                                           GdlDockObject      *object);
+
+
+G_END_DECLS
+
+#endif /* __GDL_DOCK_PLACEHOLDER_H__ */

Added: trunk/src/ext/libgdl/gdl-dock-tablabel.c
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-dock-tablabel.c	Wed Dec 10 19:46:36 2008
@@ -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áez <gustavo giraldez gmx net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gdl-i18n.h"
+#include <gtk/gtk.h>
+
+#include "gdl-dock-tablabel.h"
+#include "gdl-tools.h"
+#include "gdl-dock-item.h"
+#include "libgdlmarshal.h"
+
+
+/* ----- Private prototypes ----- */
+
+static void  gdl_dock_tablabel_class_init    (GdlDockTablabelClass *klass);
+static void  gdl_dock_tablabel_instance_init (GdlDockTablabel      *tablabel);
+
+static void  gdl_dock_tablabel_set_property  (GObject              *object,
+                                              guint                 prop_id,
+                                              const GValue         *value,
+                                              GParamSpec           *pspec);
+static void  gdl_dock_tablabel_get_property  (GObject              *object,
+                                              guint                 prop_id,
+                                              GValue               *value,
+                                              GParamSpec           *pspec);
+
+static void  gdl_dock_tablabel_item_notify   (GObject            *master,
+                                              GParamSpec         *pspec,
+                                              gpointer            data);
+
+static void  gdl_dock_tablabel_size_request  (GtkWidget          *widget,
+                                              GtkRequisition     *requisition);
+static void  gdl_dock_tablabel_size_allocate (GtkWidget          *widget,
+                                              GtkAllocation      *allocation);
+                                              
+static void  gdl_dock_tablabel_paint         (GtkWidget      *widget,
+                                              GdkEventExpose *event);
+static gint  gdl_dock_tablabel_expose        (GtkWidget      *widget,
+                                              GdkEventExpose *event);
+
+static gboolean gdl_dock_tablabel_button_event  (GtkWidget      *widget,
+                                                 GdkEventButton *event);
+static gboolean gdl_dock_tablabel_motion_event  (GtkWidget      *widget,
+                                                 GdkEventMotion *event);
+
+static void  gdl_dock_tablabel_realize (GtkWidget *widget);
+static void  gdl_dock_tablabel_unrealize (GtkWidget *widget);
+static void  gdl_dock_tablabel_map (GtkWidget *widget);
+static void  gdl_dock_tablabel_unmap (GtkWidget *widget);
+
+/* ----- Private data types and variables ----- */
+
+#define DEFAULT_DRAG_HANDLE_SIZE 10
+#define HANDLE_RATIO 1.0
+
+enum {
+    BUTTON_PRESSED_HANDLE,
+    LAST_SIGNAL
+};
+
+enum {
+    PROP_0,
+    PROP_ITEM
+};
+
+
+static guint dock_tablabel_signals [LAST_SIGNAL] = { 0 };
+
+
+/* ----- Private interface ----- */
+
+GDL_CLASS_BOILERPLATE (GdlDockTablabel, gdl_dock_tablabel,
+                       GtkBin, GTK_TYPE_BIN);
+
+static void
+gdl_dock_tablabel_class_init (GdlDockTablabelClass *klass)
+{
+    GObjectClass      *g_object_class;
+    GtkObjectClass    *object_class;
+    GtkWidgetClass    *widget_class;
+    GtkContainerClass *container_class;
+
+    g_object_class = G_OBJECT_CLASS (klass);
+    object_class = GTK_OBJECT_CLASS (klass);
+    widget_class = GTK_WIDGET_CLASS (klass);
+    container_class = GTK_CONTAINER_CLASS (klass);
+    
+    g_object_class->set_property = gdl_dock_tablabel_set_property;
+    g_object_class->get_property = gdl_dock_tablabel_get_property;
+
+    widget_class->size_request = gdl_dock_tablabel_size_request;
+    widget_class->size_allocate = gdl_dock_tablabel_size_allocate;
+    widget_class->expose_event = gdl_dock_tablabel_expose;
+    widget_class->button_press_event = gdl_dock_tablabel_button_event;
+    widget_class->button_release_event = gdl_dock_tablabel_button_event;
+    widget_class->motion_notify_event = gdl_dock_tablabel_motion_event;
+    widget_class->realize = gdl_dock_tablabel_realize;
+    widget_class->unrealize = gdl_dock_tablabel_unrealize;
+    widget_class->map = gdl_dock_tablabel_map;
+    widget_class->unmap = gdl_dock_tablabel_unmap;
+
+    g_object_class_install_property (
+        g_object_class, PROP_ITEM,
+        g_param_spec_object ("item", _("Controlling dock item"),
+                             _("Dockitem which 'owns' this tablabel"),
+                             GDL_TYPE_DOCK_ITEM,
+                             G_PARAM_READWRITE));
+
+    dock_tablabel_signals [BUTTON_PRESSED_HANDLE] =
+        g_signal_new ("button_pressed_handle",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (GdlDockTablabelClass, 
+                                       button_pressed_handle),
+                      NULL, NULL,
+                      gdl_marshal_VOID__BOXED,
+                      G_TYPE_NONE,
+                      1,
+                      GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+    klass->button_pressed_handle = NULL;
+}
+
+static void
+gdl_dock_tablabel_instance_init (GdlDockTablabel *tablabel)
+{
+    GtkWidget *widget;
+    GtkWidget *label_widget;
+
+    widget = GTK_WIDGET (tablabel);
+
+    tablabel->drag_handle_size = DEFAULT_DRAG_HANDLE_SIZE;
+    tablabel->item = NULL;
+
+    label_widget = gtk_label_new ("Dock item");
+    gtk_container_add (GTK_CONTAINER (tablabel), label_widget);
+    gtk_widget_show (label_widget);
+
+    tablabel->active = FALSE;
+    gtk_widget_set_state (GTK_WIDGET (tablabel), GTK_STATE_ACTIVE);
+}
+
+static void
+gdl_dock_tablabel_set_property (GObject      *object,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+    GdlDockTablabel *tablabel;
+    GtkBin          *bin;
+
+    tablabel = GDL_DOCK_TABLABEL (object);
+
+    switch (prop_id) {
+        case PROP_ITEM:
+            if (tablabel->item) {
+                g_object_remove_weak_pointer (G_OBJECT (tablabel->item), 
+                                              (gpointer *) &tablabel->item);
+                g_signal_handlers_disconnect_by_func (
+                    tablabel->item, gdl_dock_tablabel_item_notify, tablabel);
+            };
+
+            tablabel->item = g_value_get_object (value);
+            if (tablabel->item) {
+                gboolean locked;
+                gchar   *long_name;
+                
+                g_object_add_weak_pointer (G_OBJECT (tablabel->item), 
+                                           (gpointer *) &tablabel->item);
+
+                g_signal_connect (tablabel->item, "notify::locked",
+                                  G_CALLBACK (gdl_dock_tablabel_item_notify),
+                                  tablabel);
+                g_signal_connect (tablabel->item, "notify::long_name",
+                                  G_CALLBACK (gdl_dock_tablabel_item_notify),
+                                  tablabel);
+                g_signal_connect (tablabel->item, "notify::grip_size",
+                                  G_CALLBACK (gdl_dock_tablabel_item_notify),
+                                  tablabel);
+
+                g_object_get (tablabel->item,
+                              "locked", &locked,
+                              "long-name", &long_name,
+                              "grip-size", &tablabel->drag_handle_size,
+                              NULL);
+
+                if (locked)
+                    tablabel->drag_handle_size = 0;
+                
+                bin = GTK_BIN (tablabel);
+                if (bin->child && g_object_class_find_property (
+                    G_OBJECT_GET_CLASS (bin->child), "label"))
+                    g_object_set (bin->child, "label", long_name, NULL);
+                g_free (long_name);
+            };
+            break;
+            
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+gdl_dock_tablabel_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+    GdlDockTablabel *tablabel;
+
+    tablabel = GDL_DOCK_TABLABEL (object);
+
+    switch (prop_id) {
+        case PROP_ITEM:
+            g_value_set_object (value, tablabel->item);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+gdl_dock_tablabel_item_notify (GObject    *master,
+                               GParamSpec *pspec,
+                               gpointer    data)
+{
+    GdlDockTablabel *tablabel = GDL_DOCK_TABLABEL (data);
+    gboolean         locked;
+    gchar           *label;
+    GtkBin          *bin;
+    
+    g_object_get (master,
+                  "locked", &locked,
+                  "grip-size", &tablabel->drag_handle_size,
+                  "long-name", &label,
+                  NULL);
+
+    if (locked)
+        tablabel->drag_handle_size = 0;
+
+    bin = GTK_BIN (tablabel);
+    if (bin->child && g_object_class_find_property (
+        G_OBJECT_GET_CLASS (bin->child), "label"))
+        g_object_set (bin->child, "label", label, NULL);
+    g_free (label);
+
+    gtk_widget_queue_resize (GTK_WIDGET (tablabel));
+}
+
+static void
+gdl_dock_tablabel_size_request (GtkWidget      *widget,
+                                GtkRequisition *requisition)
+{
+    GtkBin          *bin;
+    GtkRequisition   child_req;
+    GdlDockTablabel *tablabel;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (GDL_IS_DOCK_TABLABEL (widget));
+    g_return_if_fail (requisition != NULL);
+
+    tablabel = GDL_DOCK_TABLABEL (widget);
+    bin = GTK_BIN (widget);
+
+    requisition->width = tablabel->drag_handle_size;
+    requisition->height = 0;
+
+    if (bin->child)
+        gtk_widget_size_request (bin->child, &child_req);
+    else
+        child_req.width = child_req.height = 0;
+        
+    requisition->width += child_req.width;
+    requisition->height += child_req.height;
+
+    requisition->width += GTK_CONTAINER (widget)->border_width * 2;
+    requisition->height += GTK_CONTAINER (widget)->border_width * 2;
+
+    widget->requisition = *requisition;
+}
+
+static void
+gdl_dock_tablabel_size_allocate (GtkWidget     *widget,
+                                 GtkAllocation *allocation)
+{
+    GtkBin          *bin;
+    GdlDockTablabel *tablabel;
+    gint             border_width;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (GDL_IS_DOCK_TABLABEL (widget));
+    g_return_if_fail (allocation != NULL);
+  
+    bin = GTK_BIN (widget);
+    tablabel = GDL_DOCK_TABLABEL (widget);
+
+    border_width = GTK_CONTAINER (widget)->border_width;
+  
+    widget->allocation = *allocation;
+
+    if (GTK_WIDGET_REALIZED (widget))
+        gdk_window_move_resize (tablabel->event_window, 
+                                allocation->x, 
+                                allocation->y,
+                                allocation->width, 
+                                allocation->height);
+
+    if (bin->child && GTK_WIDGET_VISIBLE (bin->child)) {
+        GtkAllocation  child_allocation;
+
+        child_allocation.x = widget->allocation.x + border_width;
+        child_allocation.y = widget->allocation.y + border_width;
+
+        allocation->width = MAX (1, (int) allocation->width - 
+                                 (int) tablabel->drag_handle_size);
+        child_allocation.x += tablabel->drag_handle_size;
+
+        child_allocation.width = 
+            MAX (1, (int) allocation->width - 2 * border_width);
+        child_allocation.height = 
+            MAX (1, (int) allocation->height - 2 * border_width);
+
+        gtk_widget_size_allocate (bin->child, &child_allocation);
+    }
+}
+
+static void
+gdl_dock_tablabel_paint (GtkWidget      *widget,
+                         GdkEventExpose *event)
+{
+    GdkRectangle     dest, rect;
+    GtkBin          *bin;
+    GdlDockTablabel *tablabel;
+    gint             border_width;
+
+    bin = GTK_BIN (widget);
+    tablabel = GDL_DOCK_TABLABEL (widget);
+    border_width = GTK_CONTAINER (widget)->border_width;
+
+    rect.x = widget->allocation.x + border_width;
+    rect.y = widget->allocation.y + border_width;
+    rect.width = tablabel->drag_handle_size * HANDLE_RATIO;
+    rect.height = widget->allocation.height - 2*border_width;
+
+    if (gdk_rectangle_intersect (&event->area, &rect, &dest)) {
+        gtk_paint_handle (widget->style, widget->window, 
+                          tablabel->active ? GTK_STATE_NORMAL : GTK_STATE_ACTIVE, 
+                          GTK_SHADOW_NONE,
+                          &dest, widget, "dock-tablabel",
+                          rect.x, rect.y, rect.width, rect.height,
+                          GTK_ORIENTATION_VERTICAL);
+    };
+}
+
+static gint
+gdl_dock_tablabel_expose (GtkWidget      *widget,
+                          GdkEventExpose *event)
+{
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (GDL_IS_DOCK_TABLABEL (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget)) {
+        GDL_CALL_PARENT_GBOOLEAN(GTK_WIDGET_CLASS, expose_event, (widget,event));
+        gdl_dock_tablabel_paint (widget, event);
+    };
+  
+    return FALSE;
+}
+
+static gboolean 
+gdl_dock_tablabel_button_event (GtkWidget      *widget,
+                                GdkEventButton *event)
+{
+    GdlDockTablabel *tablabel;
+    gboolean         event_handled;
+  
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (GDL_IS_DOCK_TABLABEL (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+    
+    tablabel = GDL_DOCK_TABLABEL (widget);
+    
+    event_handled = FALSE;
+
+    if (event->window != tablabel->event_window)
+        return FALSE;
+    
+    switch (event->type) {
+        case GDK_BUTTON_PRESS:
+            if (tablabel->active) {
+                gboolean in_handle;
+                gint     rel_x, rel_y;
+                guint    border_width;
+                GtkBin  *bin;
+
+                bin = GTK_BIN (widget);
+                border_width = GTK_CONTAINER (widget)->border_width;
+
+                rel_x = event->x - border_width;
+                rel_y = event->y - border_width;
+
+                /* Check if user clicked on the drag handle. */      
+                in_handle = (rel_x < tablabel->drag_handle_size * HANDLE_RATIO) &&
+                    (rel_x > 0);
+
+                if (event->button == 1) {
+                    tablabel->pre_drag = TRUE;
+                    tablabel->drag_start_event = *event;
+                }
+                else {
+                    g_signal_emit (widget, 
+                                   dock_tablabel_signals [BUTTON_PRESSED_HANDLE],
+                                   0,
+                                   event);
+                }
+                
+                event_handled = TRUE;
+            }
+            break;
+
+        case GDK_BUTTON_RELEASE:
+            tablabel->pre_drag = FALSE;
+            break;
+
+        default:
+            break;
+    }
+    
+    if (!event_handled) {
+        /* propagate the event to the parent's gdkwindow */
+        GdkEventButton e;
+
+        e = *event;
+        e.window = gtk_widget_get_parent_window (widget);
+        e.x += widget->allocation.x;
+        e.y += widget->allocation.y;
+        
+        gdk_event_put ((GdkEvent *) &e);
+    };
+
+    return event_handled;
+}
+
+static gboolean 
+gdl_dock_tablabel_motion_event (GtkWidget      *widget,
+                                GdkEventMotion *event)
+{
+    GdlDockTablabel *tablabel;
+    gboolean         event_handled;
+  
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (GDL_IS_DOCK_TABLABEL (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+    
+    tablabel = GDL_DOCK_TABLABEL (widget);
+    
+    event_handled = FALSE;
+
+    if (event->window != tablabel->event_window)
+        return FALSE;
+    
+    if (tablabel->pre_drag) {
+        if (gtk_drag_check_threshold (widget,
+                                      tablabel->drag_start_event.x,
+                                      tablabel->drag_start_event.y,
+                                      event->x,
+                                      event->y)) {
+            tablabel->pre_drag = FALSE;
+            g_signal_emit (widget, 
+                           dock_tablabel_signals [BUTTON_PRESSED_HANDLE],
+                           0,
+                           &tablabel->drag_start_event);
+            event_handled = TRUE;
+        }
+    }
+    
+    if (!event_handled) {
+        /* propagate the event to the parent's gdkwindow */
+        GdkEventMotion e;
+
+        e = *event;
+        e.window = gtk_widget_get_parent_window (widget);
+        e.x += widget->allocation.x;
+        e.y += widget->allocation.y;
+        
+        gdk_event_put ((GdkEvent *) &e);
+    };
+
+    return event_handled;
+}
+
+static void   
+gdl_dock_tablabel_realize (GtkWidget *widget)
+{
+    GdlDockTablabel *tablabel;
+    GdkWindowAttr attributes;
+    int attributes_mask;
+    
+    tablabel = GDL_DOCK_TABLABEL (widget);
+    
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.wclass = GDK_INPUT_ONLY;
+    attributes.event_mask = gtk_widget_get_events (widget);
+    attributes.event_mask |= (GDK_EXPOSURE_MASK | 
+                              GDK_BUTTON_PRESS_MASK |
+                              GDK_BUTTON_RELEASE_MASK | 
+                              GDK_ENTER_NOTIFY_MASK | 
+                              GDK_POINTER_MOTION_MASK | 
+                              GDK_LEAVE_NOTIFY_MASK);
+    attributes_mask = GDK_WA_X | GDK_WA_Y;
+    
+    widget->window = gtk_widget_get_parent_window (widget);
+    g_object_ref (widget->window);
+    
+    tablabel->event_window = 
+        gdk_window_new (gtk_widget_get_parent_window (widget),
+                        &attributes, attributes_mask);
+    gdk_window_set_user_data (tablabel->event_window, widget);
+    
+    widget->style = gtk_style_attach (widget->style, widget->window);
+    
+    GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+}
+
+static void   
+gdl_dock_tablabel_unrealize (GtkWidget *widget)
+{
+    GdlDockTablabel *tablabel = GDL_DOCK_TABLABEL (widget);
+    
+    if (tablabel->event_window) {
+        gdk_window_set_user_data (tablabel->event_window, NULL);
+        gdk_window_destroy (tablabel->event_window);
+        tablabel->event_window = NULL;
+    }
+    
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, unrealize, (widget));
+}
+
+static void  
+gdl_dock_tablabel_map (GtkWidget *widget)
+{
+    GdlDockTablabel *tablabel = GDL_DOCK_TABLABEL (widget);
+    
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, map, (widget));
+    
+    gdk_window_show (tablabel->event_window);
+}
+
+static void   
+gdl_dock_tablabel_unmap (GtkWidget *widget)
+{
+    GdlDockTablabel *tablabel = GDL_DOCK_TABLABEL (widget);
+
+    gdk_window_hide (tablabel->event_window);
+
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, unmap, (widget));
+}
+
+/* ----- Public interface ----- */
+
+GtkWidget *
+gdl_dock_tablabel_new (GdlDockItem *item)
+{
+    GdlDockTablabel *tablabel;
+
+    tablabel = GDL_DOCK_TABLABEL (g_object_new (GDL_TYPE_DOCK_TABLABEL,
+                                                "item", item,
+                                                NULL));
+    
+    return GTK_WIDGET (tablabel);
+}
+
+void
+gdl_dock_tablabel_activate (GdlDockTablabel *tablabel)
+{
+    g_return_if_fail (tablabel != NULL);
+
+    tablabel->active = TRUE;
+    gtk_widget_set_state (GTK_WIDGET (tablabel), GTK_STATE_NORMAL);
+}
+
+void
+gdl_dock_tablabel_deactivate (GdlDockTablabel *tablabel)
+{
+    g_return_if_fail (tablabel != NULL);
+
+    tablabel->active = FALSE;
+    /* yeah, i know it contradictive */
+    gtk_widget_set_state (GTK_WIDGET (tablabel), GTK_STATE_ACTIVE);
+}

Added: trunk/src/ext/libgdl/gdl-dock-tablabel.h
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-dock-tablabel.h	Wed Dec 10 19:46:36 2008
@@ -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áez <gustavo giraldez gmx net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GDL_DOCK_TABLABEL_H__
+#define __GDL_DOCK_TABLABEL_H__
+
+#include <gtk/gtk.h>
+#include "libgdl/gdl-dock-item.h"
+
+
+G_BEGIN_DECLS
+
+/* standard macros */
+#define GDL_TYPE_DOCK_TABLABEL            (gdl_dock_tablabel_get_type ())
+#define GDL_DOCK_TABLABEL(obj)            (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK_TABLABEL, GdlDockTablabel))
+#define GDL_DOCK_TABLABEL_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_TABLABEL, GdlDockTablabelClass))
+#define GDL_IS_DOCK_TABLABEL(obj)         (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK_TABLABEL))
+#define GDL_IS_DOCK_TABLABEL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_TABLABEL))
+#define GDL_DOCK_TABLABEL_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK_TABLABEL, GdlDockTablabelClass))
+
+/* data types & structures */
+typedef struct _GdlDockTablabel      GdlDockTablabel;
+typedef struct _GdlDockTablabelClass GdlDockTablabelClass;
+
+struct _GdlDockTablabel {
+    GtkBin          parent;
+
+    guint           drag_handle_size;
+    GtkWidget      *item;
+    GdkWindow      *event_window;
+    gboolean        active;
+
+    GdkEventButton  drag_start_event;
+    gboolean        pre_drag;
+};
+
+struct _GdlDockTablabelClass {
+    GtkBinClass      parent_class;
+
+    void            (*button_pressed_handle)  (GdlDockTablabel *tablabel,
+                                               GdkEventButton  *event);
+};
+
+/* public interface */
+ 
+GtkWidget     *gdl_dock_tablabel_new           (GdlDockItem *item);
+GType          gdl_dock_tablabel_get_type      (void);
+
+void           gdl_dock_tablabel_activate      (GdlDockTablabel *tablabel);
+void           gdl_dock_tablabel_deactivate    (GdlDockTablabel *tablabel);
+
+G_END_DECLS
+
+#endif

Added: trunk/src/ext/libgdl/gdl-dock.c
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-dock.c	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,1368 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 
+ *
+ * This file is part of the GNOME Devtools Libraries.
+ *
+ * Copyright (C) 2002 Gustavo GirÃldez <gustavo giraldez gmx net>
+ *               2007 Naba Kumar  <naba gnome org>
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gdl-i18n.h"
+#include <stdlib.h>
+#include <string.h>
+
+#include "gdl-tools.h"
+#include "gdl-dock.h"
+#include "gdl-dock-master.h"
+#include "gdl-dock-paned.h"
+#include "gdl-dock-notebook.h"
+#include "gdl-dock-placeholder.h"
+
+#include "libgdlmarshal.h"
+
+
+/* ----- Private prototypes ----- */
+
+static void  gdl_dock_class_init      (GdlDockClass *class);
+static void  gdl_dock_instance_init   (GdlDock *dock);
+
+static GObject *gdl_dock_constructor  (GType                  type,
+                                       guint                  n_construct_properties,
+                                       GObjectConstructParam *construct_param);
+static void  gdl_dock_set_property    (GObject      *object,
+                                       guint         prop_id,
+                                       const GValue *value,
+                                       GParamSpec   *pspec);
+static void  gdl_dock_get_property    (GObject      *object,
+                                       guint         prop_id,
+                                       GValue       *value,
+                                       GParamSpec   *pspec);
+static void  gdl_dock_notify_cb       (GObject      *object,
+                                       GParamSpec   *pspec,
+                                       gpointer      user_data);
+
+static void  gdl_dock_set_title       (GdlDock      *dock);
+
+static void  gdl_dock_destroy         (GtkObject    *object);
+
+static void  gdl_dock_size_request    (GtkWidget      *widget,
+                                       GtkRequisition *requisition);
+static void  gdl_dock_size_allocate   (GtkWidget      *widget,
+                                       GtkAllocation  *allocation);
+static void  gdl_dock_map             (GtkWidget      *widget);
+static void  gdl_dock_unmap           (GtkWidget      *widget);
+static void  gdl_dock_show            (GtkWidget      *widget);
+static void  gdl_dock_hide            (GtkWidget      *widget);
+
+static void  gdl_dock_add             (GtkContainer *container,
+                                       GtkWidget    *widget);
+static void  gdl_dock_remove          (GtkContainer *container,
+                                       GtkWidget    *widget);
+static void  gdl_dock_forall          (GtkContainer *container,
+                                       gboolean      include_internals,
+                                       GtkCallback   callback,
+                                       gpointer      callback_data);
+static GtkType  gdl_dock_child_type   (GtkContainer *container);
+
+static void     gdl_dock_detach       (GdlDockObject    *object,
+                                       gboolean          recursive);
+static void     gdl_dock_reduce       (GdlDockObject    *object);
+static gboolean gdl_dock_dock_request (GdlDockObject    *object,
+                                       gint              x,
+                                       gint              y,
+                                       GdlDockRequest   *request);
+static void     gdl_dock_dock         (GdlDockObject    *object,
+                                       GdlDockObject    *requestor,
+                                       GdlDockPlacement  position,
+                                       GValue           *other_data);
+static gboolean gdl_dock_reorder      (GdlDockObject    *object,
+                                       GdlDockObject    *requestor,
+                                       GdlDockPlacement  new_position,
+                                       GValue           *other_data);
+
+static gboolean gdl_dock_floating_window_delete_event_cb (GtkWidget *widget);
+
+static gboolean gdl_dock_child_placement  (GdlDockObject    *object,
+                                           GdlDockObject    *child,
+                                           GdlDockPlacement *placement);
+
+static void     gdl_dock_present          (GdlDockObject    *object,
+                                           GdlDockObject    *child);
+
+
+/* ----- Class variables and definitions ----- */
+
+struct _GdlDockPrivate
+{
+    /* for floating docks */
+    gboolean            floating;
+    GtkWidget          *window;
+    gboolean            auto_title;
+    
+    gint                float_x;
+    gint                float_y;
+    gint                width;
+    gint                height;
+
+    /* auxiliary fields */
+    GdkGC              *xor_gc;
+};
+
+enum {
+    LAYOUT_CHANGED,
+    LAST_SIGNAL
+};
+
+enum {
+    PROP_0,
+    PROP_FLOATING,
+    PROP_DEFAULT_TITLE,
+    PROP_WIDTH,
+    PROP_HEIGHT,
+    PROP_FLOAT_X,
+    PROP_FLOAT_Y
+};
+
+static guint dock_signals [LAST_SIGNAL] = { 0 };
+
+#define SPLIT_RATIO  0.3
+
+
+/* ----- Private functions ----- */
+
+GDL_CLASS_BOILERPLATE (GdlDock, gdl_dock, GdlDockObject, GDL_TYPE_DOCK_OBJECT);
+
+static void
+gdl_dock_class_init (GdlDockClass *klass)
+{
+    GObjectClass       *g_object_class;
+    GtkObjectClass     *gtk_object_class;
+    GtkWidgetClass     *widget_class;
+    GtkContainerClass  *container_class;
+    GdlDockObjectClass *object_class;
+    
+    g_object_class = G_OBJECT_CLASS (klass);
+    gtk_object_class = GTK_OBJECT_CLASS (klass);
+    widget_class = GTK_WIDGET_CLASS (klass);
+    container_class = GTK_CONTAINER_CLASS (klass);
+    object_class = GDL_DOCK_OBJECT_CLASS (klass);
+    
+    g_object_class->constructor = gdl_dock_constructor;
+    g_object_class->set_property = gdl_dock_set_property;
+    g_object_class->get_property = gdl_dock_get_property;
+    
+    /* properties */
+
+    g_object_class_install_property (
+        g_object_class, PROP_FLOATING,
+        g_param_spec_boolean ("floating", _("Floating"),
+                              _("Whether the dock is floating in its own window"),
+                              FALSE,
+                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                              GDL_DOCK_PARAM_EXPORT));
+    
+    g_object_class_install_property (
+        g_object_class, PROP_DEFAULT_TITLE,
+        g_param_spec_string ("default-title", _("Default title"),
+                             _("Default title for the newly created floating docks"),
+                             NULL,
+                             G_PARAM_READWRITE));
+    
+    g_object_class_install_property (
+        g_object_class, PROP_WIDTH,
+        g_param_spec_int ("width", _("Width"),
+                          _("Width for the dock when it's of floating type"),
+                          -1, G_MAXINT, -1,
+                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+                          GDL_DOCK_PARAM_EXPORT));
+    
+    g_object_class_install_property (
+        g_object_class, PROP_HEIGHT,
+        g_param_spec_int ("height", _("Height"),
+                          _("Height for the dock when it's of floating type"),
+                          -1, G_MAXINT, -1,
+                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+                          GDL_DOCK_PARAM_EXPORT));
+    
+    g_object_class_install_property (
+        g_object_class, PROP_FLOAT_X,
+        g_param_spec_int ("floatx", _("Float X"),
+                          _("X coordinate for a floating dock"),
+                          G_MININT, G_MAXINT, 0,
+                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+                          GDL_DOCK_PARAM_EXPORT));
+    
+    g_object_class_install_property (
+        g_object_class, PROP_FLOAT_Y,
+        g_param_spec_int ("floaty", _("Float Y"),
+                          _("Y coordinate for a floating dock"),
+                          G_MININT, G_MAXINT, 0,
+                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+                          GDL_DOCK_PARAM_EXPORT));
+    
+    gtk_object_class->destroy = gdl_dock_destroy;
+
+    widget_class->size_request = gdl_dock_size_request;
+    widget_class->size_allocate = gdl_dock_size_allocate;
+    widget_class->map = gdl_dock_map;
+    widget_class->unmap = gdl_dock_unmap;
+    widget_class->show = gdl_dock_show;
+    widget_class->hide = gdl_dock_hide;
+    
+    container_class->add = gdl_dock_add;
+    container_class->remove = gdl_dock_remove;
+    container_class->forall = gdl_dock_forall;
+    container_class->child_type = gdl_dock_child_type;
+    
+    object_class->is_compound = TRUE;
+    
+    object_class->detach = gdl_dock_detach;
+    object_class->reduce = gdl_dock_reduce;
+    object_class->dock_request = gdl_dock_dock_request;
+    object_class->dock = gdl_dock_dock;
+    object_class->reorder = gdl_dock_reorder;    
+    object_class->child_placement = gdl_dock_child_placement;
+    object_class->present = gdl_dock_present;
+    
+    /* signals */
+
+    dock_signals [LAYOUT_CHANGED] = 
+        g_signal_new ("layout-changed", 
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (GdlDockClass, layout_changed),
+                      NULL, /* accumulator */
+                      NULL, /* accu_data */
+                      gdl_marshal_VOID__VOID,
+                      G_TYPE_NONE, /* return type */
+                      0);
+
+    klass->layout_changed = NULL;
+}
+
+static void
+gdl_dock_instance_init (GdlDock *dock)
+{
+    GTK_WIDGET_SET_FLAGS (GTK_WIDGET (dock), GTK_NO_WINDOW);
+
+    dock->root = NULL;
+    dock->_priv = g_new0 (GdlDockPrivate, 1);
+    dock->_priv->width = -1;
+    dock->_priv->height = -1;
+}
+
+static gboolean 
+gdl_dock_floating_configure_event_cb (GtkWidget         *widget,
+                                      GdkEventConfigure *event,
+                                      gpointer           user_data)
+{
+    GdlDock *dock;
+
+    g_return_val_if_fail (user_data != NULL && GDL_IS_DOCK (user_data), TRUE);
+
+    dock = GDL_DOCK (user_data);
+    dock->_priv->float_x = event->x;
+    dock->_priv->float_y = event->y;
+    dock->_priv->width = event->width;
+    dock->_priv->height = event->height;
+
+    return FALSE;
+}
+
+static GObject *
+gdl_dock_constructor (GType                  type,
+                      guint                  n_construct_properties,
+                      GObjectConstructParam *construct_param)
+{
+    GObject *g_object;
+    
+    g_object = GDL_CALL_PARENT_WITH_DEFAULT (G_OBJECT_CLASS, 
+                                               constructor, 
+                                               (type,
+                                                n_construct_properties,
+                                                construct_param),
+                                               NULL);
+    if (g_object) {
+        GdlDock *dock = GDL_DOCK (g_object);
+        GdlDockMaster *master;
+        
+        /* create a master for the dock if none was provided in the construction */
+        master = GDL_DOCK_OBJECT_GET_MASTER (GDL_DOCK_OBJECT (dock));
+        if (!master) {
+            GDL_DOCK_OBJECT_UNSET_FLAGS (dock, GDL_DOCK_AUTOMATIC);
+            master = g_object_new (GDL_TYPE_DOCK_MASTER, NULL);
+            /* the controller owns the master ref */
+            gdl_dock_object_bind (GDL_DOCK_OBJECT (dock), G_OBJECT (master));
+        }
+
+        if (dock->_priv->floating) {
+            GdlDockObject *controller;
+            
+            /* create floating window for this dock */
+            dock->_priv->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+            g_object_set_data (G_OBJECT (dock->_priv->window), "dock", dock);
+            
+            /* set position and default size */
+            gtk_window_set_position (GTK_WINDOW (dock->_priv->window),
+                                     GTK_WIN_POS_MOUSE);
+            gtk_window_set_default_size (GTK_WINDOW (dock->_priv->window),
+                                         dock->_priv->width,
+                                         dock->_priv->height);
+            gtk_window_set_type_hint (GTK_WINDOW (dock->_priv->window),
+                                      GDK_WINDOW_TYPE_HINT_NORMAL);
+            
+            gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dock->_priv->window), 
+                                              TRUE);
+
+            /* metacity ignores this */
+            gtk_window_move (GTK_WINDOW (dock->_priv->window),
+                             dock->_priv->float_x,
+                             dock->_priv->float_y);
+            
+            /* connect to the configure event so we can track down window geometry */
+            g_signal_connect (dock->_priv->window, "configure_event",
+                              (GCallback) gdl_dock_floating_configure_event_cb,
+                              dock);
+            
+            /* set the title and connect to the long_name notify queue
+               so we can reset the title when this prop changes */
+            gdl_dock_set_title (dock);
+            g_signal_connect (dock, "notify::long-name",
+                              (GCallback) gdl_dock_notify_cb, NULL);
+            
+            /* set transient for the first dock if that is a non-floating dock */
+            controller = gdl_dock_master_get_controller (master);
+            if (controller && GDL_IS_DOCK (controller)) {
+                gboolean first_is_floating;
+                g_object_get (controller, "floating", &first_is_floating, NULL);
+                if (!first_is_floating) {
+                    GtkWidget *toplevel =
+                        gtk_widget_get_toplevel (GTK_WIDGET (controller));
+
+                    if (GTK_IS_WINDOW (toplevel))
+                        gtk_window_set_transient_for (GTK_WINDOW (dock->_priv->window),
+                                                      GTK_WINDOW (toplevel));
+                }
+            }
+
+            gtk_container_add (GTK_CONTAINER (dock->_priv->window), GTK_WIDGET (dock));
+    
+            g_signal_connect (dock->_priv->window, "delete_event",
+                              G_CALLBACK (gdl_dock_floating_window_delete_event_cb), 
+                              NULL);
+        }
+        GDL_DOCK_OBJECT_SET_FLAGS (dock, GDL_DOCK_ATTACHED);
+    }
+    
+    return g_object;
+}
+
+static void
+gdl_dock_set_property  (GObject      *object,
+                        guint         prop_id,
+                        const GValue *value,
+                        GParamSpec   *pspec)
+{
+    GdlDock *dock = GDL_DOCK (object);
+    
+    switch (prop_id) {
+        case PROP_FLOATING:
+            dock->_priv->floating = g_value_get_boolean (value);
+            break;
+        case PROP_DEFAULT_TITLE:
+            if (GDL_DOCK_OBJECT (object)->master)
+                g_object_set (GDL_DOCK_OBJECT (object)->master,
+                              "default-title", g_value_get_string (value),
+                              NULL);
+            break;
+        case PROP_WIDTH:
+            dock->_priv->width = g_value_get_int (value);
+            break;
+        case PROP_HEIGHT:
+            dock->_priv->height = g_value_get_int (value);
+            break;
+        case PROP_FLOAT_X:
+            dock->_priv->float_x = g_value_get_int (value);
+            break;
+        case PROP_FLOAT_Y:
+            dock->_priv->float_y = g_value_get_int (value);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+
+    switch (prop_id) {
+        case PROP_WIDTH:
+        case PROP_HEIGHT:
+        case PROP_FLOAT_X:
+        case PROP_FLOAT_Y:
+            if (dock->_priv->floating && dock->_priv->window) {
+                gtk_window_resize (GTK_WINDOW (dock->_priv->window),
+                                   dock->_priv->width,
+                                   dock->_priv->height);
+            }
+            break;
+    }
+}
+
+static void
+gdl_dock_get_property  (GObject      *object,
+                        guint         prop_id,
+                        GValue       *value,
+                        GParamSpec   *pspec)
+{
+    GdlDock *dock = GDL_DOCK (object);
+
+    switch (prop_id) {
+        case PROP_FLOATING:
+            g_value_set_boolean (value, dock->_priv->floating);
+            break;
+        case PROP_DEFAULT_TITLE:
+            if (GDL_DOCK_OBJECT (object)->master) {
+                gchar *default_title;
+                g_object_get (GDL_DOCK_OBJECT (object)->master,
+                              "default-title", &default_title,
+                              NULL);
+#if GLIB_CHECK_VERSION(2,3,0)
+                g_value_take_string (value, default_title);
+#else
+                g_value_set_string_take_ownership (value, default_title);
+#endif
+            }
+            else
+                g_value_set_string (value, NULL);
+            break;
+        case PROP_WIDTH:
+            g_value_set_int (value, dock->_priv->width);
+            break;
+        case PROP_HEIGHT:
+            g_value_set_int (value, dock->_priv->height);
+            break;
+        case PROP_FLOAT_X:
+            g_value_set_int (value, dock->_priv->float_x);
+            break;
+        case PROP_FLOAT_Y:
+            g_value_set_int (value, dock->_priv->float_y);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+gdl_dock_set_title (GdlDock *dock)
+{
+    GdlDockObject *object = GDL_DOCK_OBJECT (dock);
+    gchar         *title = NULL;
+    gboolean       free_title = FALSE;
+    
+    if (!dock->_priv->window)
+        return;
+    
+    if (!dock->_priv->auto_title && object->long_name) {
+        title = object->long_name;
+    }
+    else if (object->master) {
+        g_object_get (object->master, "default-title", &title, NULL);
+        free_title = TRUE;
+    }
+
+    if (!title && dock->root) {
+        g_object_get (dock->root, "long-name", &title, NULL);
+        free_title = TRUE;
+    }
+    
+    if (!title) {
+        /* set a default title in the long_name */
+        dock->_priv->auto_title = TRUE;
+        free_title = FALSE;
+        title = object->long_name = g_strdup_printf (
+            _("Dock #%d"), GDL_DOCK_MASTER (object->master)->dock_number++);
+    }
+
+    gtk_window_set_title (GTK_WINDOW (dock->_priv->window), title);
+    if (free_title)
+        g_free (title);
+}
+
+static void
+gdl_dock_notify_cb (GObject    *object,
+                    GParamSpec *pspec,
+                    gpointer    user_data)
+{
+    GdlDock *dock;
+    
+    g_return_if_fail (object != NULL || GDL_IS_DOCK (object));
+    
+    dock = GDL_DOCK (object);
+    dock->_priv->auto_title = FALSE;
+    gdl_dock_set_title (dock);
+}
+
+static void
+gdl_dock_destroy (GtkObject *object)
+{
+    GdlDock *dock = GDL_DOCK (object);
+
+    if (dock->_priv) {
+        GdlDockPrivate *priv = dock->_priv;
+        dock->_priv = NULL;
+
+        if (priv->window) {
+            gtk_widget_destroy (priv->window);
+            priv->floating = FALSE;
+            priv->window = NULL;
+        }
+        
+        /* destroy the xor gc */
+        if (priv->xor_gc) {
+            g_object_unref (priv->xor_gc);
+            priv->xor_gc = NULL;
+        }
+
+        g_free (priv);
+    }
+    
+    GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
+}
+
+static void
+gdl_dock_size_request (GtkWidget      *widget,
+                       GtkRequisition *requisition)
+{
+    GdlDock       *dock;
+    GtkContainer  *container;
+    guint          border_width;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (GDL_IS_DOCK (widget));
+
+    dock = GDL_DOCK (widget);
+    container = GTK_CONTAINER (widget);
+    border_width = container->border_width;
+
+    /* make request to root */
+    if (dock->root && GTK_WIDGET_VISIBLE (dock->root))
+        gtk_widget_size_request (GTK_WIDGET (dock->root), requisition);
+    else {
+        requisition->width = 0;
+        requisition->height = 0;
+    };
+
+    requisition->width += 2 * border_width;
+    requisition->height += 2 * border_width;
+
+    widget->requisition = *requisition;
+}
+
+static void
+gdl_dock_size_allocate (GtkWidget     *widget,
+                        GtkAllocation *allocation)
+{
+    GdlDock       *dock;
+    GtkContainer  *container;
+    guint          border_width;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (GDL_IS_DOCK (widget));
+    
+    dock = GDL_DOCK (widget);
+    container = GTK_CONTAINER (widget);
+    border_width = container->border_width;
+
+    widget->allocation = *allocation;
+
+    /* reduce allocation by border width */
+    allocation->x += border_width;
+    allocation->y += border_width;
+    allocation->width = MAX (1, allocation->width - 2 * border_width);
+    allocation->height = MAX (1, allocation->height - 2 * border_width);
+
+    if (dock->root && GTK_WIDGET_VISIBLE (dock->root))
+        gtk_widget_size_allocate (GTK_WIDGET (dock->root), allocation);
+}
+
+static void
+gdl_dock_map (GtkWidget *widget)
+{
+    GtkWidget *child;
+    GdlDock   *dock;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (GDL_IS_DOCK (widget));
+
+    dock = GDL_DOCK (widget);
+
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, map, (widget));
+
+    if (dock->root) {
+        child = GTK_WIDGET (dock->root);
+        if (GTK_WIDGET_VISIBLE (child) && !GTK_WIDGET_MAPPED (child))
+            gtk_widget_map (child);
+    }
+}
+
+static void
+gdl_dock_unmap (GtkWidget *widget)
+{
+    GtkWidget *child;
+    GdlDock   *dock;
+    
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (GDL_IS_DOCK (widget));
+
+    dock = GDL_DOCK (widget);
+
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, unmap, (widget));
+
+    if (dock->root) {
+        child = GTK_WIDGET (dock->root);
+        if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_MAPPED (child))
+            gtk_widget_unmap (child);
+    }
+    
+    if (dock->_priv->window)
+        gtk_widget_unmap (dock->_priv->window);
+}
+
+static void
+gdl_dock_foreach_automatic (GdlDockObject *object,
+                            gpointer       user_data)
+{
+    void (* function) (GtkWidget *) = user_data;
+
+    if (GDL_DOCK_OBJECT_AUTOMATIC (object))
+        (* function) (GTK_WIDGET (object));
+}
+
+static void
+gdl_dock_show (GtkWidget *widget)
+{
+    GdlDock *dock;
+    
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (GDL_IS_DOCK (widget));
+    
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, show, (widget));
+    
+    dock = GDL_DOCK (widget);
+    if (dock->_priv->floating && dock->_priv->window)
+        gtk_widget_show (dock->_priv->window);
+
+    if (GDL_DOCK_IS_CONTROLLER (dock)) {
+        gdl_dock_master_foreach_toplevel (GDL_DOCK_OBJECT_GET_MASTER (dock),
+                                          FALSE, (GFunc) gdl_dock_foreach_automatic,
+                                          gtk_widget_show);
+    }
+}
+
+static void
+gdl_dock_hide (GtkWidget *widget)
+{
+    GdlDock *dock;
+    
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (GDL_IS_DOCK (widget));
+    
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, hide, (widget));
+    
+    dock = GDL_DOCK (widget);
+    if (dock->_priv->floating && dock->_priv->window)
+        gtk_widget_hide (dock->_priv->window);
+
+    if (GDL_DOCK_IS_CONTROLLER (dock)) {
+        gdl_dock_master_foreach_toplevel (GDL_DOCK_OBJECT_GET_MASTER (dock),
+                                          FALSE, (GFunc) gdl_dock_foreach_automatic,
+                                          gtk_widget_hide);
+    }
+}
+
+static void
+gdl_dock_add (GtkContainer *container,
+              GtkWidget    *widget)
+{
+    g_return_if_fail (container != NULL);
+    g_return_if_fail (GDL_IS_DOCK (container));
+    g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
+
+    gdl_dock_add_item (GDL_DOCK (container), 
+                       GDL_DOCK_ITEM (widget), 
+                       GDL_DOCK_TOP);  /* default position */
+}
+
+static void
+gdl_dock_remove (GtkContainer *container,
+                 GtkWidget    *widget)
+{
+    GdlDock  *dock;
+    gboolean  was_visible;
+
+    g_return_if_fail (container != NULL);
+    g_return_if_fail (widget != NULL);
+
+    dock = GDL_DOCK (container);
+    was_visible = GTK_WIDGET_VISIBLE (widget);
+
+    if (GTK_WIDGET (dock->root) == widget) {
+        dock->root = NULL;
+        GDL_DOCK_OBJECT_UNSET_FLAGS (widget, GDL_DOCK_ATTACHED);
+        gtk_widget_unparent (widget);
+
+        if (was_visible && GTK_WIDGET_VISIBLE (GTK_WIDGET (container)))
+            gtk_widget_queue_resize (GTK_WIDGET (dock));
+    }
+}
+
+static void
+gdl_dock_forall (GtkContainer *container,
+                 gboolean      include_internals,
+                 GtkCallback   callback,
+                 gpointer      callback_data)
+{
+    GdlDock *dock;
+
+    g_return_if_fail (container != NULL);
+    g_return_if_fail (GDL_IS_DOCK (container));
+    g_return_if_fail (callback != NULL);
+
+    dock = GDL_DOCK (container);
+
+    if (dock->root)
+        (*callback) (GTK_WIDGET (dock->root), callback_data);
+}
+
+static GtkType
+gdl_dock_child_type (GtkContainer *container)
+{
+    return GDL_TYPE_DOCK_ITEM;
+}
+
+static void
+gdl_dock_detach (GdlDockObject *object,
+                 gboolean       recursive)
+{
+    GdlDock *dock = GDL_DOCK (object);
+    
+    /* detach children */
+    if (recursive && dock->root) {
+        gdl_dock_object_detach (dock->root, recursive);
+    }
+    GDL_DOCK_OBJECT_UNSET_FLAGS (object, GDL_DOCK_ATTACHED);
+}
+
+static void
+gdl_dock_reduce (GdlDockObject *object)
+{
+    GdlDock *dock = GDL_DOCK (object);
+    
+    if (dock->root)
+        return;
+    
+    if (GDL_DOCK_OBJECT_AUTOMATIC (dock)) {
+        gtk_widget_destroy (GTK_WIDGET (dock));
+
+    } else if (!GDL_DOCK_OBJECT_ATTACHED (dock)) {
+        /* if the user explicitly detached the object */
+        if (dock->_priv->floating)
+            gtk_widget_hide (GTK_WIDGET (dock));
+        else {
+            GtkWidget *widget = GTK_WIDGET (object);
+            if (widget->parent) 
+                gtk_container_remove (GTK_CONTAINER (widget->parent), widget);
+        }
+    }
+}
+
+static gboolean
+gdl_dock_dock_request (GdlDockObject  *object,
+                       gint            x,
+                       gint            y,
+                       GdlDockRequest *request)
+{
+    GdlDock            *dock;
+    guint               bw;
+    gint                rel_x, rel_y;
+    GtkAllocation      *alloc;
+    gboolean            may_dock = FALSE;
+    GdlDockRequest      my_request;
+
+    g_return_val_if_fail (GDL_IS_DOCK (object), FALSE);
+
+    /* we get (x,y) in our allocation coordinates system */
+    
+    dock = GDL_DOCK (object);
+    
+    /* Get dock size. */
+    alloc = &(GTK_WIDGET (dock)->allocation);
+    bw = GTK_CONTAINER (dock)->border_width;
+
+    /* Get coordinates relative to our allocation area. */
+    rel_x = x - alloc->x;
+    rel_y = y - alloc->y;
+
+    if (request)
+        my_request = *request;
+        
+    /* Check if coordinates are in GdlDock widget. */
+    if (rel_x > 0 && rel_x < alloc->width &&
+        rel_y > 0 && rel_y < alloc->height) {
+
+        /* It's inside our area. */
+        may_dock = TRUE;
+
+	/* Set docking indicator rectangle to the GdlDock size. */
+        my_request.rect.x = alloc->x + bw;
+        my_request.rect.y = alloc->y + bw;
+        my_request.rect.width = alloc->width - 2*bw;
+        my_request.rect.height = alloc->height - 2*bw;
+
+	/* If GdlDock has no root item yet, set the dock itself as 
+	   possible target. */
+        if (!dock->root) {
+            my_request.position = GDL_DOCK_TOP;
+            my_request.target = object;
+        } else {
+            my_request.target = dock->root;
+
+            /* See if it's in the border_width band. */
+            if (rel_x < bw) {
+                my_request.position = GDL_DOCK_LEFT;
+                my_request.rect.width *= SPLIT_RATIO;
+            } else if (rel_x > alloc->width - bw) {
+                my_request.position = GDL_DOCK_RIGHT;
+                my_request.rect.x += my_request.rect.width * (1 - SPLIT_RATIO);
+                my_request.rect.width *= SPLIT_RATIO;
+            } else if (rel_y < bw) {
+                my_request.position = GDL_DOCK_TOP;
+                my_request.rect.height *= SPLIT_RATIO;
+            } else if (rel_y > alloc->height - bw) {
+                my_request.position = GDL_DOCK_BOTTOM;
+                my_request.rect.y += my_request.rect.height * (1 - SPLIT_RATIO);
+                my_request.rect.height *= SPLIT_RATIO;
+            } else {
+                /* Otherwise try our children. */
+                /* give them allocation coordinates (we are a
+                   GTK_NO_WINDOW) widget */
+                may_dock = gdl_dock_object_dock_request (GDL_DOCK_OBJECT (dock->root), 
+                                                         x, y, &my_request);
+            }
+        }
+    }
+
+    if (may_dock && request)
+        *request = my_request;
+    
+    return may_dock;
+}
+
+static void
+gdl_dock_dock (GdlDockObject    *object,
+               GdlDockObject    *requestor,
+               GdlDockPlacement  position,
+               GValue           *user_data)
+{
+    GdlDock *dock;
+    
+    g_return_if_fail (GDL_IS_DOCK (object));
+    /* only dock items allowed at this time */
+    g_return_if_fail (GDL_IS_DOCK_ITEM (requestor));
+
+    dock = GDL_DOCK (object);
+    
+    if (position == GDL_DOCK_FLOATING) {
+        GdlDockItem *item = GDL_DOCK_ITEM (requestor);
+        gint x, y, width, height;
+
+        if (user_data && G_VALUE_HOLDS (user_data, GDK_TYPE_RECTANGLE)) {
+            GdkRectangle *rect;
+
+            rect = g_value_get_boxed (user_data);
+            x = rect->x;
+            y = rect->y;
+            width = rect->width;
+            height = rect->height;
+        }
+        else {
+            x = y = 0;
+            width = height = -1;
+        }
+        
+        gdl_dock_add_floating_item (dock, item,
+                                    x, y, width, height);
+    }
+    else if (dock->root) {
+        /* This is somewhat a special case since we know which item to
+           pass the request on because we only have on child */
+        gdl_dock_object_dock (dock->root, requestor, position, NULL);
+        gdl_dock_set_title (dock);
+        
+    }
+    else { /* Item about to be added is root item. */
+        GtkWidget *widget = GTK_WIDGET (requestor);
+        
+        dock->root = requestor;
+        GDL_DOCK_OBJECT_SET_FLAGS (requestor, GDL_DOCK_ATTACHED);
+        gtk_widget_set_parent (widget, GTK_WIDGET (dock));
+        
+        gdl_dock_item_show_grip (GDL_DOCK_ITEM (requestor));
+
+        /* Realize the item (create its corresponding GdkWindow) when 
+           GdlDock has been realized. */
+        if (GTK_WIDGET_REALIZED (dock))
+            gtk_widget_realize (widget);
+        
+        /* Map the widget if it's visible and the parent is visible and has 
+           been mapped. This is done to make sure that the GdkWindow is 
+           visible. */
+        if (GTK_WIDGET_VISIBLE (dock) && 
+            GTK_WIDGET_VISIBLE (widget)) {
+            if (GTK_WIDGET_MAPPED (dock))
+                gtk_widget_map (widget);
+            
+            /* Make the widget resize. */
+            gtk_widget_queue_resize (widget);
+        }
+        gdl_dock_set_title (dock);
+    }
+}
+    
+static gboolean
+gdl_dock_floating_window_delete_event_cb (GtkWidget *widget)
+{
+    GdlDock *dock;
+    
+    g_return_val_if_fail (GTK_IS_WINDOW (widget), FALSE);
+    
+    dock = GDL_DOCK (g_object_get_data (G_OBJECT (widget), "dock"));
+    if (dock->root) {
+        /* this will call reduce on ourselves, hiding the window if appropiate */
+        gdl_dock_item_hide_item (GDL_DOCK_ITEM (dock->root));
+    }
+
+    return TRUE;
+}
+
+static void
+_gdl_dock_foreach_build_list (GdlDockObject *object,
+                              gpointer       user_data)
+{
+    GList **l = (GList **) user_data;
+
+    if (GDL_IS_DOCK_ITEM (object))
+        *l = g_list_prepend (*l, object);
+}
+
+static gboolean
+gdl_dock_reorder (GdlDockObject    *object,
+                  GdlDockObject    *requestor,
+                  GdlDockPlacement  new_position,
+                  GValue           *other_data)
+{
+    GdlDock *dock = GDL_DOCK (object);
+    gboolean handled = FALSE;
+    
+    if (dock->_priv->floating &&
+        new_position == GDL_DOCK_FLOATING &&
+        dock->root == requestor) {
+        
+        if (other_data && G_VALUE_HOLDS (other_data, GDK_TYPE_RECTANGLE)) {
+            GdkRectangle *rect;
+
+            rect = g_value_get_boxed (other_data);
+            gtk_window_move (GTK_WINDOW (dock->_priv->window),
+                             rect->x,
+                             rect->y);
+            handled = TRUE;
+        }
+    }
+    
+    return handled;
+}
+
+static gboolean 
+gdl_dock_child_placement (GdlDockObject    *object,
+                          GdlDockObject    *child,
+                          GdlDockPlacement *placement)
+{
+    GdlDock *dock = GDL_DOCK (object);
+    gboolean retval = TRUE;
+    
+    if (dock->root == child) {
+        if (placement) {
+            if (*placement == GDL_DOCK_NONE || *placement == GDL_DOCK_FLOATING)
+                *placement = GDL_DOCK_TOP;
+        }
+    } else 
+        retval = FALSE;
+
+    return retval;
+}
+
+static void 
+gdl_dock_present (GdlDockObject *object,
+                  GdlDockObject *child)
+{
+    GdlDock *dock = GDL_DOCK (object);
+
+    if (dock->_priv->floating)
+        gtk_window_present (GTK_WINDOW (dock->_priv->window));
+}
+
+
+/* ----- Public interface ----- */
+
+GtkWidget *
+gdl_dock_new (void)
+{
+    GObject *dock;
+
+    dock = g_object_new (GDL_TYPE_DOCK, NULL);
+    GDL_DOCK_OBJECT_UNSET_FLAGS (dock, GDL_DOCK_AUTOMATIC);
+    
+    return GTK_WIDGET (dock);
+}
+
+GtkWidget *
+gdl_dock_new_from (GdlDock  *original,
+                   gboolean  floating)
+{
+    GObject *new_dock;
+    
+    g_return_val_if_fail (original != NULL, NULL);
+    
+    new_dock = g_object_new (GDL_TYPE_DOCK, 
+                             "master", GDL_DOCK_OBJECT_GET_MASTER (original), 
+                             "floating", floating,
+                             NULL);
+    GDL_DOCK_OBJECT_UNSET_FLAGS (new_dock, GDL_DOCK_AUTOMATIC);
+    
+    return GTK_WIDGET (new_dock);
+}
+
+/* Depending on where the dock item (where new item will be docked) locates
+ * in the dock, we might need to change the docking placement. If the
+ * item is does not touches the center of dock, the new-item-to-dock would
+ * require a center dock on this item.
+ */
+static GdlDockPlacement
+gdl_dock_refine_placement (GdlDock *dock, GdlDockItem *dock_item,
+                           GdlDockPlacement placement)
+{
+    GtkRequisition object_size;
+    
+    gdl_dock_item_preferred_size (dock_item, &object_size);
+    g_return_val_if_fail (GTK_WIDGET (dock)->allocation.width > 0, placement);
+    g_return_val_if_fail (GTK_WIDGET (dock)->allocation.height > 0, placement);
+    g_return_val_if_fail (object_size.width > 0, placement);
+    g_return_val_if_fail (object_size.height > 0, placement);
+
+    if (placement == GDL_DOCK_LEFT || placement == GDL_DOCK_RIGHT) {
+        /* Check if dock_object touches center in terms of width */
+        if (GTK_WIDGET (dock)->allocation.width/2 > object_size.width) {
+            return GDL_DOCK_TOP;
+        }
+    } 
+
+    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);
+}

Added: trunk/src/ext/libgdl/gdl-dock.h
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-dock.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,99 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This file is part of the GNOME Devtools Libraries.
+ *
+ * Copyright (C) 2002 Gustavo GirÃldez <gustavo giraldez gmx net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GDL_DOCK_H__
+#define __GDL_DOCK_H__
+
+#include <gtk/gtk.h>
+#include "libgdl/gdl-dock-object.h"
+#include "libgdl/gdl-dock-item.h"
+#include "libgdl/gdl-dock-placeholder.h"
+
+G_BEGIN_DECLS
+
+/* standard macros */
+#define GDL_TYPE_DOCK            (gdl_dock_get_type ())
+#define GDL_DOCK(obj)            (GTK_CHECK_CAST ((obj), GDL_TYPE_DOCK, GdlDock))
+#define GDL_DOCK_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK, GdlDockClass))
+#define GDL_IS_DOCK(obj)         (GTK_CHECK_TYPE ((obj), GDL_TYPE_DOCK))
+#define GDL_IS_DOCK_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK))
+#define GDL_DOCK_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DOCK, GdlDockClass))
+
+/* data types & structures */
+typedef struct _GdlDock        GdlDock;
+typedef struct _GdlDockClass   GdlDockClass;
+typedef struct _GdlDockPrivate GdlDockPrivate;
+
+struct _GdlDock {
+    GdlDockObject    object;
+
+    GdlDockObject   *root;
+
+    GdlDockPrivate  *_priv;
+};
+
+struct _GdlDockClass {
+    GdlDockObjectClass parent_class;
+
+    void  (* layout_changed)  (GdlDock *dock);    /* proxy signal for the master */
+};
+
+/* additional macros */
+#define GDL_DOCK_IS_CONTROLLER(dock)  \
+    (gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (dock)) == \
+     GDL_DOCK_OBJECT (dock))
+
+/* public interface */
+ 
+GtkWidget     *gdl_dock_new               (void);
+
+GtkWidget     *gdl_dock_new_from          (GdlDock          *original,
+                                           gboolean          floating);
+
+GType          gdl_dock_get_type          (void);
+
+void           gdl_dock_add_item          (GdlDock          *dock,
+                                           GdlDockItem      *item,
+                                           GdlDockPlacement  place);
+
+void           gdl_dock_add_floating_item (GdlDock        *dock,
+                                           GdlDockItem    *item,
+                                           gint            x,
+                                           gint            y,
+                                           gint            width,
+                                           gint            height);
+
+GdlDockItem   *gdl_dock_get_item_by_name  (GdlDock     *dock,
+                                           const gchar *name);
+
+GdlDockPlaceholder *gdl_dock_get_placeholder_by_name (GdlDock     *dock,
+                                                      const gchar *name);
+
+GList         *gdl_dock_get_named_items   (GdlDock    *dock);
+
+GdlDock       *gdl_dock_object_get_toplevel (GdlDockObject *object);
+
+void           gdl_dock_xor_rect            (GdlDock       *dock,
+                                             GdkRectangle  *rect);
+
+G_END_DECLS
+
+#endif

Added: trunk/src/ext/libgdl/gdl-i18n.c
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-i18n.c	Wed Dec 10 19:46:36 2008
@@ -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);
+}
+
+

Added: trunk/src/ext/libgdl/gdl-i18n.h
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-i18n.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Devtools Library.
+ *
+ * The Gnome Devtools Library is free software; you can redistribute
+ * it and/or modify it under the terms of the GNU Library General
+ * Public License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * The Gnome Devtools Library is distributed in the hope that it will
+ * be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+
+/*
+ * Handles all of the internationalization configuration options.
+ * Author: Tom Tromey <tromey creche cygnus com>
+ */
+
+#ifndef __GDL_18N_H__
+#define __GDL_18N_H__ 1
+
+#include <glib.h>
+
+
+G_BEGIN_DECLS
+
+#ifdef ENABLE_NLS
+#    include <libintl.h>
+#    undef _
+#    define _(String) gdl_gettext (String)
+#    ifdef gettext_noop
+#        define N_(String) gettext_noop (String)
+#    else
+#        define N_(String) (String)
+#    endif
+#else
+/* Stubs that do something close enough.  */
+#    undef textdomain
+#    define textdomain(String) (String)
+#    undef gettext
+#    define gettext(String) (String)
+#    undef dgettext
+#    define dgettext(Domain,Message) (Message)
+#    undef dcgettext
+#    define dcgettext(Domain,Message,Type) (Message)
+#    undef bindtextdomain
+#    define bindtextdomain(Domain,Directory) (Domain)
+#    undef bind_textdomain_codeset
+#    define bind_textdomain_codeset(Domain,CodeSet) (Domain)
+#    undef _
+#    define _(String) (String)
+#    undef N_
+#    define N_(String) (String)
+#endif
+
+char *gdl_gettext (const char *msgid);
+
+G_END_DECLS
+
+#endif /* __GDL_I18N_H__ */

Added: trunk/src/ext/libgdl/gdl-stock-icons.h
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-stock-icons.h	Wed Dec 10 19:46:36 2008
@@ -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"};
+
+

Added: trunk/src/ext/libgdl/gdl-stock.c
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-stock.c	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,127 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- 
+ * gdl-stock.c
+ * 
+ * Copyright (C) 2003 Jeroen Zwartepoorte
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <gtk/gtkiconfactory.h>
+#include "gdl-stock.h"
+#include "gdl-stock-icons.h"
+
+static GtkIconFactory *gdl_stock_factory = NULL;
+
+static struct {
+	const gchar *stock_id;
+	const guint8 *icon_data;
+	const guint data_size;
+}
+gdl_icons[] = 
+{
+	{ GDL_STOCK_CLOSE, stock_close_icon, sizeof (stock_close_icon) },
+	{ GDL_STOCK_MENU_LEFT, stock_menu_left_icon, sizeof (stock_menu_left_icon) },
+	{ GDL_STOCK_MENU_RIGHT, stock_menu_right_icon, sizeof (stock_menu_right_icon) }
+};
+
+static void
+icon_set_from_data (GtkIconSet       *set,
+		    const guint8     *icon_data,
+		    const guint       data_size,
+		    GtkIconSize       size,
+		    gboolean          fallback)
+{
+	GtkIconSource *source;
+	GdkPixbuf     *pixbuf;
+	GError        *err = NULL;
+
+	source = gtk_icon_source_new ();
+
+	gtk_icon_source_set_size (source, size);
+	gtk_icon_source_set_size_wildcarded (source, FALSE);
+	
+	pixbuf = gdk_pixbuf_new_from_inline (data_size, icon_data, FALSE, &err);
+	if (err) {
+	    g_warning ("%s",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;
+}

Added: trunk/src/ext/libgdl/gdl-stock.h
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-stock.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,37 @@
+/* -*- 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__
+
+#include <glib/gmacros.h>   // G_BEGIN_DECLS
+
+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__ */

Added: trunk/src/ext/libgdl/gdl-switcher.c
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-switcher.c	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,1081 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 8 -*- */
+/* gdl-switcher.c
+ *
+ * Copyright (C) 2003  Ettore Perazzoli,
+ *               2007  Naba Kumar
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Copied and adapted from ESidebar.[ch] from evolution
+ * 
+ * Authors: Ettore Perazzoli <ettore ximian com>
+ *          Naba Kumar  <naba gnome org>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gdl-i18n.h"
+#include "gdl-switcher.h"
+#include "gdl-tools.h"
+#include "libgdlmarshal.h"
+#include "libgdltypebuiltins.h"
+
+#include <gtk/gtk.h>
+#include <gtk/gtkhbox.h>
+#include <gtk/gtkimage.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtktogglebutton.h>
+
+#if HAVE_GNOME
+#include <gconf/gconf-client.h>
+#include <libgnome/gnome-gconf.h>
+#endif
+
+static void gdl_switcher_set_property  (GObject            *object,
+                                        guint               prop_id,
+                                        const GValue       *value,
+                                        GParamSpec         *pspec);
+static void gdl_switcher_get_property  (GObject            *object,
+                                        guint               prop_id,
+                                        GValue             *value,
+                                        GParamSpec         *pspec);
+
+static void gdl_switcher_add_button  (GdlSwitcher *switcher,
+                                      const gchar *label,
+                                      const gchar *tooltips,
+                                      const gchar *stock_id,
+                                      const GdkPixbuf *pixbuf_icon,
+                                      gint switcher_id);
+static void gdl_switcher_remove_button (GdlSwitcher *switcher, gint switcher_id);
+static void gdl_switcher_select_page (GdlSwitcher *switcher, gint switcher_id);
+static void gdl_switcher_select_button (GdlSwitcher *switcher, gint switcher_id);
+static void gdl_switcher_set_show_buttons (GdlSwitcher *switcher, gboolean show);
+static void gdl_switcher_set_style (GdlSwitcher *switcher,
+                                    GdlSwitcherStyle switcher_style);
+static GdlSwitcherStyle gdl_switcher_get_style (GdlSwitcher *switcher);
+
+enum {
+    PROP_0,
+    PROP_SWITCHER_STYLE
+};
+
+typedef struct {
+    GtkWidget *button_widget;
+    GtkWidget *label;
+    GtkWidget *icon;
+    GtkWidget *arrow;
+    GtkWidget *hbox;
+    GtkTooltips *tooltips;
+    int id;
+} Button;
+
+struct _GdlSwitcherPrivate {
+    GdlSwitcherStyle switcher_style;
+    GdlSwitcherStyle toolbar_style;
+    
+    gboolean show;
+    GSList *buttons;
+
+    guint style_changed_id;
+    gint buttons_height_request;
+    gboolean in_toggle;
+};
+
+GDL_CLASS_BOILERPLATE (GdlSwitcher, gdl_switcher, GtkNotebook, GTK_TYPE_NOTEBOOK)
+
+#define INTERNAL_MODE(switcher)  (switcher->priv->switcher_style == \
+            GDL_SWITCHER_STYLE_TOOLBAR ? switcher->priv->toolbar_style : \
+            switcher->priv->switcher_style)
+
+#define H_PADDING 2
+#define V_PADDING 2
+
+/* Utility functions.  */
+
+static Button *
+button_new (GtkWidget *button_widget, GtkWidget *label, GtkWidget *icon,
+            GtkTooltips *tooltips, GtkWidget *arrow, GtkWidget *hbox, int id)
+{
+    Button *button = g_new (Button, 1);
+
+    button->button_widget = button_widget;
+    button->label = label;
+    button->icon = icon;
+    button->arrow = arrow;
+    button->hbox = hbox;
+    button->tooltips = tooltips;
+    button->id = id;
+
+    g_object_ref (button_widget);
+    g_object_ref (label);
+    g_object_ref (icon);
+    g_object_ref (arrow);
+    g_object_ref (hbox);
+    g_object_ref (tooltips);
+
+    return button;
+}
+
+static void
+button_free (Button *button)
+{
+    g_object_unref (button->button_widget);
+    g_object_unref (button->label);
+    g_object_unref (button->icon);
+    g_object_unref (button->hbox);
+    g_object_unref (button->tooltips);
+    g_free (button);
+}
+
+static gint
+gdl_switcher_get_page_id (GtkWidget *widget)
+{
+    static gint switcher_id_count = 0;
+    gint switcher_id;
+    switcher_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
+                                                      "__switcher_id"));
+    if (switcher_id <= 0) {
+        switcher_id = ++switcher_id_count;
+        g_object_set_data (G_OBJECT (widget), "__switcher_id",
+                           GINT_TO_POINTER (switcher_id));
+    }
+    return switcher_id;
+}
+
+static void
+update_buttons (GdlSwitcher *switcher, int new_selected_id)
+{
+    GSList *p;
+
+    switcher->priv->in_toggle = TRUE;
+
+    for (p = switcher->priv->buttons; p != NULL; p = p->next) {
+        Button *button = p->data;
+
+        if (button->id == new_selected_id) {
+            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
+                                          (button->button_widget), TRUE);
+            gtk_widget_set_sensitive (button->arrow, TRUE);
+        } else {
+            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
+                                          (button->button_widget), FALSE);
+            gtk_widget_set_sensitive (button->arrow, FALSE);
+        }
+    }
+
+    switcher->priv->in_toggle = FALSE;
+}
+
+/* Callbacks.  */
+
+static void
+button_toggled_callback (GtkToggleButton *toggle_button,
+                         GdlSwitcher *switcher)
+{
+    int id = 0;
+    gboolean is_active = FALSE;
+    GSList *p;
+
+    if (switcher->priv->in_toggle)
+        return;
+
+    switcher->priv->in_toggle = TRUE;
+
+    if (gtk_toggle_button_get_active (toggle_button))
+        is_active = TRUE;
+
+    for (p = switcher->priv->buttons; p != NULL; p = p->next) {
+        Button *button = p->data;
+
+        if (button->button_widget != GTK_WIDGET (toggle_button)) {
+            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
+                                          (button->button_widget), FALSE);
+            gtk_widget_set_sensitive (button->arrow, FALSE);
+        } else {
+            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
+                                          (button->button_widget), TRUE);
+            gtk_widget_set_sensitive (button->arrow, TRUE);
+            id = button->id;
+        }
+    }
+    
+    switcher->priv->in_toggle = FALSE;
+
+    if (is_active)
+    {
+        gdl_switcher_select_page (switcher, id);
+    }
+}
+
+/* Returns -1 if layout didn't happen because a resize request was queued */
+static int
+layout_buttons (GdlSwitcher *switcher)
+{
+    GtkRequisition client_requisition;
+    GtkAllocation *allocation = & GTK_WIDGET (switcher)->allocation;
+    GdlSwitcherStyle switcher_style;
+    gboolean icons_only;
+    int num_btns = g_slist_length (switcher->priv->buttons);
+    int btns_per_row;
+    GSList **rows, *p;
+    Button *button;
+    int row_number;
+    int max_btn_width = 0, max_btn_height = 0;
+    int optimal_layout_width = 0;
+    int row_last;
+    int x, y;
+    int i;
+    int rows_count;
+    int last_buttons_height;
+    
+    last_buttons_height = switcher->priv->buttons_height_request;
+    
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, size_request,
+                     (GTK_WIDGET (switcher), &client_requisition));
+
+    y = allocation->y + allocation->height - V_PADDING - 1;
+
+    if (num_btns == 0)
+        return y;
+
+    switcher_style = INTERNAL_MODE (switcher);
+    icons_only = (switcher_style == GDL_SWITCHER_STYLE_ICON);
+    
+    /* Figure out the max width and height */
+    optimal_layout_width = H_PADDING;
+    for (p = switcher->priv->buttons; p != NULL; p = p->next) {
+        GtkRequisition requisition;
+
+        button = p->data;
+        gtk_widget_size_request (GTK_WIDGET (button->button_widget),
+                                 &requisition);
+        optimal_layout_width += requisition.width + H_PADDING;
+        max_btn_height = MAX (max_btn_height, requisition.height);
+        max_btn_width = MAX (max_btn_width, requisition.width);    
+    }
+
+    /* Figure out how many rows and columns we'll use. */
+    btns_per_row = allocation->width / (max_btn_width + H_PADDING);
+    
+    /* If all the buttons could fit in the single row, have it so */
+    if (allocation->width >= optimal_layout_width)
+    {
+        btns_per_row = num_btns;
+    }
+    if (!icons_only) {
+        /* If using text buttons, we want to try to have a
+         * completely filled-in grid, but if we can't, we want
+         * the odd row to have just a single button.
+         */
+        while (num_btns % btns_per_row > 1)
+            btns_per_row--;
+    }
+
+    rows_count = num_btns / btns_per_row;
+    if (num_btns % btns_per_row != 0)
+        rows_count++;
+     
+    /* Assign buttons to rows */
+    rows = g_new0 (GSList *, rows_count);
+
+    if (!icons_only && num_btns % btns_per_row != 0) {
+        button = switcher->priv->buttons->data;
+        rows [0] = g_slist_append (rows [0], button->button_widget);
+
+        p = switcher->priv->buttons->next;
+        row_number = p ? 1 : 0;
+    } else {
+        p = switcher->priv->buttons;
+        row_number = 0;
+    }
+
+    for (; p != NULL; p = p->next) {
+        button = p->data;
+
+        if (g_slist_length (rows [row_number]) == btns_per_row)
+            row_number ++;
+
+        rows [row_number] = g_slist_append (rows [row_number],
+                                            button->button_widget);
+    }
+
+    row_last = row_number;
+
+    /* If there are more than 1 row of buttons, save the current height
+     * requirement for subsequent size requests.
+     */
+    if (row_last > 0)
+    {
+        switcher->priv->buttons_height_request =
+            (row_last + 1) * (max_btn_height + V_PADDING) + 1;
+    } else { /* Otherwize clear it */
+        if (last_buttons_height >= 0) {
+
+            switcher->priv->buttons_height_request = -1;
+        }
+    }
+    
+    /* If it turns out that we now require smaller height for the buttons
+     * than it was last time, make a resize request to ensure our
+     * size requisition is properly communicated to the parent (otherwise
+     * parent tend to keep assuming the older size).
+     */
+    if (last_buttons_height > switcher->priv->buttons_height_request)
+    {
+        gtk_widget_queue_resize (GTK_WIDGET (switcher));
+        return -1;
+    }
+    
+    /* Layout the buttons. */
+    for (i = row_last; i >= 0; i --) {
+        int len, extra_width;
+        
+        y -= max_btn_height;
+
+        /* Check for possible size over flow (taking into account client
+         * requisition
+         */
+        if (y < (allocation->y + client_requisition.height)) {
+            /* We have an overflow: Insufficient allocation */
+            if (last_buttons_height < switcher->priv->buttons_height_request) {
+                /* Request for a new resize */
+                gtk_widget_queue_resize (GTK_WIDGET (switcher));
+                return -1;
+            }
+        }
+        x = H_PADDING + allocation->x;
+        len = g_slist_length (rows[i]);
+        if (switcher_style == GDL_SWITCHER_STYLE_TEXT ||
+            switcher_style == GDL_SWITCHER_STYLE_BOTH)
+            extra_width = (allocation->width - (len * max_btn_width )
+                           - (len * H_PADDING)) / len;
+        else
+            extra_width = 0;
+        for (p = rows [i]; p != NULL; p = p->next) {
+            GtkAllocation child_allocation;
+            
+            child_allocation.x = x;
+            child_allocation.y = y;
+            if (rows_count == 1 && row_number == 0)
+            {
+                GtkRequisition child_requisition;
+                gtk_widget_size_request (GTK_WIDGET (p->data),
+                                         &child_requisition);
+                child_allocation.width = child_requisition.width;
+            }
+            else
+            {
+                child_allocation.width = max_btn_width + extra_width;
+            }
+            child_allocation.height = max_btn_height;
+
+            gtk_widget_size_allocate (GTK_WIDGET (p->data), &child_allocation);
+
+            x += child_allocation.width + H_PADDING;
+        }
+
+        y -= V_PADDING;
+    }
+    
+    for (i = 0; i <= row_last; i ++)
+        g_slist_free (rows [i]);
+    g_free (rows);
+
+    return y;
+}
+
+static void
+do_layout (GdlSwitcher *switcher)
+{
+    GtkAllocation *allocation = & GTK_WIDGET (switcher)->allocation;
+    GtkAllocation child_allocation;
+    int y;
+
+    if (switcher->priv->show) {
+        y = layout_buttons (switcher);
+        if (y < 0) /* Layout did not happen and a resize was requested */
+            return;
+    }
+    else
+        y = allocation->y + allocation->height;
+    
+    /* Place the parent widget.  */
+    child_allocation.x = allocation->x;
+    child_allocation.y = allocation->y;
+    child_allocation.width = allocation->width;
+    child_allocation.height = y - allocation->y;
+    
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, size_allocate,
+                     (GTK_WIDGET (switcher), &child_allocation));
+}
+
+/* GtkContainer methods.  */
+
+static void
+gdl_switcher_forall (GtkContainer *container, gboolean include_internals,
+                     GtkCallback callback, void *callback_data)
+{
+    GdlSwitcher *switcher =
+        GDL_SWITCHER (container);
+    GSList *p;
+    
+    GDL_CALL_PARENT (GTK_CONTAINER_CLASS, forall,
+                     (GTK_CONTAINER (switcher), include_internals,
+                      callback, callback_data));
+    if (include_internals) {
+        for (p = switcher->priv->buttons; p != NULL; p = p->next) {
+            GtkWidget *widget = ((Button *) p->data)->button_widget;
+            (* callback) (widget, callback_data);
+        }
+    }
+}
+
+static void
+gdl_switcher_remove (GtkContainer *container, GtkWidget *widget)
+{
+    gint switcher_id;
+    GdlSwitcher *switcher =
+        GDL_SWITCHER (container);
+    GSList *p;
+    
+    switcher_id = gdl_switcher_get_page_id (widget);
+    for (p = switcher->priv->buttons; p != NULL; p = p->next) {
+        Button *b = (Button *) p->data;
+
+        if (b->id == switcher_id) {
+            gtk_widget_unparent (b->button_widget);
+            switcher->priv->buttons =
+                g_slist_remove_link (switcher->priv->buttons, p);
+            button_free (b);
+            gtk_widget_queue_resize (GTK_WIDGET (switcher));
+            break;
+        }
+    }
+    GDL_CALL_PARENT (GTK_CONTAINER_CLASS, remove,
+                     (GTK_CONTAINER (switcher), widget));
+}
+
+/* GtkWidget methods.  */
+
+static void
+gdl_switcher_size_request (GtkWidget *widget, GtkRequisition *requisition)
+{
+    GdlSwitcher *switcher = GDL_SWITCHER (widget);
+    GSList *p;
+    gint button_height = 0;
+    
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, size_request,
+                     (GTK_WIDGET (switcher), requisition));
+
+    if (!switcher->priv->show)
+        return;
+    
+    for (p = switcher->priv->buttons; p != NULL; p = p->next) {
+        gint button_width;
+        Button *button = p->data;
+        GtkRequisition button_requisition;
+
+        gtk_widget_size_request (button->button_widget, &button_requisition);
+        button_width = button_requisition.width + 2 * H_PADDING;
+        requisition->width = MAX (requisition->width, button_width);
+        button_height = MAX (button_height,
+                             button_requisition.height + 2 * V_PADDING);
+    }
+    
+    if (switcher->priv->buttons_height_request > 0) {
+        requisition->height += switcher->priv->buttons_height_request;
+    } else {
+        requisition->height += button_height + V_PADDING;
+    }
+}
+
+static void
+gdl_switcher_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+    widget->allocation = *allocation;
+    do_layout (GDL_SWITCHER (widget));
+}
+
+static gint
+gdl_switcher_expose (GtkWidget *widget, GdkEventExpose *event)
+{
+    GSList *p;
+    GdlSwitcher *switcher = GDL_SWITCHER (widget);
+    if (switcher->priv->show) {
+        for (p = switcher->priv->buttons; p != NULL; p = p->next) {
+            GtkWidget *button = ((Button *) p->data)->button_widget;
+            gtk_container_propagate_expose (GTK_CONTAINER (widget),
+                                            button, event);
+        }
+    }
+    GDL_CALL_PARENT_WITH_DEFAULT (GTK_WIDGET_CLASS, expose_event,
+                                  (widget, event), FALSE);
+}
+
+static void
+gdl_switcher_map (GtkWidget *widget)
+{
+    GSList *p;
+    GdlSwitcher *switcher = GDL_SWITCHER (widget);
+    
+    if (switcher->priv->show) {
+        for (p = switcher->priv->buttons; p != NULL; p = p->next) {
+            GtkWidget *button = ((Button *) p->data)->button_widget;
+            gtk_widget_map (button);
+        }
+    }
+    GDL_CALL_PARENT (GTK_WIDGET_CLASS, map, (widget));
+}
+
+/* GObject methods.  */
+
+static void
+gdl_switcher_set_property  (GObject      *object,
+                            guint         prop_id,
+                            const GValue *value,
+                            GParamSpec   *pspec)
+{
+    GdlSwitcher *switcher = GDL_SWITCHER (object);
+
+    switch (prop_id) {
+        case PROP_SWITCHER_STYLE:
+            gdl_switcher_set_style (switcher, g_value_get_enum (value));
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+gdl_switcher_get_property  (GObject      *object,
+                            guint         prop_id,
+                            GValue       *value,
+                            GParamSpec   *pspec)
+{
+    GdlSwitcher *switcher = GDL_SWITCHER (object);
+
+    switch (prop_id) {
+        case PROP_SWITCHER_STYLE:
+            g_value_set_enum (value, gdl_switcher_get_style (switcher));
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+gdl_switcher_dispose (GObject *object)
+{
+    GdlSwitcherPrivate *priv = GDL_SWITCHER (object)->priv;
+    
+#if HAVE_GNOME
+    GConfClient *gconf_client = gconf_client_get_default ();
+    
+    if (priv->style_changed_id) {
+        gconf_client_notify_remove (gconf_client, priv->style_changed_id);
+        priv->style_changed_id = 0;
+    }
+    g_object_unref (gconf_client);
+#endif
+    
+    g_slist_foreach (priv->buttons, (GFunc) button_free, NULL);
+    g_slist_free (priv->buttons);
+    priv->buttons = NULL;
+
+    GDL_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
+}
+
+static void
+gdl_switcher_finalize (GObject *object)
+{
+    GdlSwitcherPrivate *priv = GDL_SWITCHER (object)->priv;
+
+    g_free (priv);
+
+    GDL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
+}
+
+/* Signal handlers */
+
+static void 
+gdl_switcher_notify_cb (GObject *g_object, GParamSpec *pspec,
+                        GdlSwitcher *switcher) 
+{
+    gboolean show_tabs;
+    g_return_if_fail (switcher != NULL && GDL_IS_SWITCHER (switcher));
+    show_tabs = gtk_notebook_get_show_tabs (GTK_NOTEBOOK (switcher));
+    gdl_switcher_set_show_buttons (switcher, !show_tabs);
+}
+
+static void
+gdl_switcher_switch_page_cb (GtkNotebook *nb, GtkNotebookPage *page,
+                             gint page_num, GdlSwitcher *switcher)
+{
+    GtkWidget       *page_widget;
+    GtkWidget       *tablabel;
+    gint             switcher_id;
+    
+    /* Change switcher button */
+    page_widget = gtk_notebook_get_nth_page (nb, page_num);
+    switcher_id = gdl_switcher_get_page_id (page_widget);
+    gdl_switcher_select_button (GDL_SWITCHER (switcher), switcher_id);
+}
+
+static void
+gdl_switcher_page_added_cb (GtkNotebook *nb, GtkWidget *page,
+                            gint page_num, GdlSwitcher *switcher)
+{
+    gint         switcher_id;
+ 
+    switcher_id = gdl_switcher_get_page_id (page);
+    
+    gdl_switcher_add_button (GDL_SWITCHER (switcher), NULL, NULL, NULL, NULL,
+                             switcher_id);
+    gdl_switcher_select_button (GDL_SWITCHER (switcher), switcher_id);
+}
+
+static void
+gdl_switcher_select_page (GdlSwitcher *switcher, gint id)
+{
+    GList *children, *node;
+    children = gtk_container_get_children (GTK_CONTAINER (switcher));
+    node = children;
+    while (node)
+    {
+        gint switcher_id;
+        switcher_id = gdl_switcher_get_page_id (GTK_WIDGET (node->data));
+        if (switcher_id == id)
+        {
+            gint page_num;
+            page_num = gtk_notebook_page_num (GTK_NOTEBOOK (switcher),
+                                              GTK_WIDGET (node->data));
+            g_signal_handlers_block_by_func (switcher,
+                                             gdl_switcher_switch_page_cb,
+                                             switcher);
+            gtk_notebook_set_current_page (GTK_NOTEBOOK (switcher), page_num);
+            g_signal_handlers_unblock_by_func (switcher,
+                                               gdl_switcher_switch_page_cb,
+                                               switcher);
+            break;
+        }
+        node = g_list_next (node);
+    }
+    g_list_free (children);
+}
+
+/* Initialization.  */
+
+static void
+gdl_switcher_class_init (GdlSwitcherClass *klass)
+{
+    GtkNotebookClass *notebook_class = GTK_NOTEBOOK_CLASS (klass);
+    GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    container_class->forall = gdl_switcher_forall;
+    container_class->remove = gdl_switcher_remove;
+
+    widget_class->size_request = gdl_switcher_size_request;
+    widget_class->size_allocate = gdl_switcher_size_allocate;
+    widget_class->expose_event = gdl_switcher_expose;
+    widget_class->map = gdl_switcher_map;
+    
+    object_class->dispose  = gdl_switcher_dispose;
+    object_class->finalize = gdl_switcher_finalize;
+    object_class->set_property = gdl_switcher_set_property;
+    object_class->get_property = gdl_switcher_get_property;
+    
+    g_object_class_install_property (
+        object_class, PROP_SWITCHER_STYLE,
+        g_param_spec_enum ("switcher-style", _("Switcher Style"),
+                           _("Switcher buttons style"),
+                           GDL_TYPE_SWITCHER_STYLE,
+                           GDL_SWITCHER_STYLE_BOTH,
+                           G_PARAM_READWRITE));
+}
+
+static void
+gdl_switcher_instance_init (GdlSwitcher *switcher)
+{
+    GdlSwitcherPrivate *priv;
+
+    GTK_WIDGET_SET_FLAGS (switcher, GTK_NO_WINDOW);
+  
+    priv = g_new0 (GdlSwitcherPrivate, 1);
+    switcher->priv = priv;
+
+    priv->show = TRUE;
+    priv->buttons_height_request = -1;
+
+    gtk_notebook_set_tab_pos (GTK_NOTEBOOK (switcher), GTK_POS_BOTTOM);
+    gtk_notebook_set_show_tabs (GTK_NOTEBOOK (switcher), FALSE);
+    gtk_notebook_set_show_border (GTK_NOTEBOOK (switcher), FALSE);
+    gdl_switcher_set_style (switcher, GDL_SWITCHER_STYLE_BOTH);
+    
+    /* notebook signals */
+    g_signal_connect (switcher, "switch-page",
+                      G_CALLBACK (gdl_switcher_switch_page_cb), switcher);
+    g_signal_connect (switcher, "page-added",
+                      G_CALLBACK (gdl_switcher_page_added_cb), switcher);
+    g_signal_connect (switcher, "notify::show-tabs",
+                      G_CALLBACK (gdl_switcher_notify_cb), switcher);
+}
+
+GtkWidget *
+gdl_switcher_new (void)
+{
+    GdlSwitcher *switcher = g_object_new (gdl_switcher_get_type (), NULL);
+    return GTK_WIDGET (switcher);
+}
+
+void
+gdl_switcher_add_button (GdlSwitcher *switcher, const gchar *label,
+                         const gchar *tooltips, const gchar *stock_id,
+                         const GdkPixbuf *pixbuf_icon, gint switcher_id)
+{
+    GtkWidget *button_widget;
+    GtkWidget *hbox;
+    GtkWidget *icon_widget;
+    GtkWidget *label_widget;
+    GtkWidget *arrow;
+    GtkTooltips *button_tooltips;
+    
+    button_widget = gtk_toggle_button_new ();
+    if (switcher->priv->show)
+        gtk_widget_show (button_widget);
+    g_signal_connect (button_widget, "toggled",
+                      G_CALLBACK (button_toggled_callback),
+                      switcher);
+    hbox = gtk_hbox_new (FALSE, 3);
+    gtk_container_set_border_width (GTK_CONTAINER (hbox), 0);
+    gtk_container_add (GTK_CONTAINER (button_widget), hbox);
+    gtk_widget_show (hbox);
+
+    if (stock_id)
+        icon_widget = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
+    else if (pixbuf_icon)
+        icon_widget = gtk_image_new_from_pixbuf ((GdkPixbuf *)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;
+}

Added: trunk/src/ext/libgdl/gdl-switcher.h
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-switcher.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,73 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 8 -*- */
+/* gdl-switcher.h
+ *
+ * Copyright (C) 2003  Ettore Perazzoli
+ *               2007  Naba Kumar
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Ettore Perazzoli <ettore ximian com>
+ *          Naba Kumar  <naba gnome org>
+ */
+
+#ifndef _GDL_SWITCHER_H_
+#define _GDL_SWITCHER_H_
+
+#include <gtk/gtknotebook.h>
+
+G_BEGIN_DECLS
+
+#define GDL_TYPE_SWITCHER            (gdl_switcher_get_type ())
+#define GDL_SWITCHER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDL_TYPE_SWITCHER, GdlSwitcher))
+#define GDL_SWITCHER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GDL_TYPE_SWITCHER, GdlSwitcherClass))
+#define GDL_IS_SWITCHER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDL_TYPE_SWITCHER))
+#define GDL_IS_SWITCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), GDL_TYPE_SWITCHER))
+
+typedef struct _GdlSwitcher        GdlSwitcher;
+typedef struct _GdlSwitcherPrivate GdlSwitcherPrivate;
+typedef struct _GdlSwitcherClass   GdlSwitcherClass;
+
+typedef enum {
+    GDL_SWITCHER_STYLE_TEXT,
+    GDL_SWITCHER_STYLE_ICON,
+    GDL_SWITCHER_STYLE_BOTH,
+    GDL_SWITCHER_STYLE_TOOLBAR,
+    GDL_SWITCHER_STYLE_TABS
+} GdlSwitcherStyle;
+
+struct _GdlSwitcher {
+    GtkNotebook parent;
+
+    GdlSwitcherPrivate *priv;
+};
+
+struct _GdlSwitcherClass {
+    GtkNotebookClass parent_class;
+};
+
+GType      gdl_switcher_get_type     (void);
+GtkWidget *gdl_switcher_new          (void);
+
+gint       gdl_switcher_insert_page  (GdlSwitcher *switcher,
+                                      GtkWidget   *page,
+                                      GtkWidget   *tab_widget,
+                                      const gchar *label,
+                                      const gchar *tooltips,
+                                      const gchar *stock_id,
+                                      const GdkPixbuf *pixbuf_icon,
+                                      gint position);
+G_END_DECLS
+
+#endif /* _GDL_SWITCHER_H_ */

Added: trunk/src/ext/libgdl/gdl-tools.h
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-tools.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,190 @@
+/*  -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 
+ * This file is part of the GNOME Devtool Libraries
+ * 
+ * Copyright (C) 1999-2000 Dave Camp <dave helixcode com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/* Miscellaneous GDL tools/macros */
+
+#ifndef __GDL_TOOLS_H__
+#define __GDL_TOOLS_H__
+
+#ifdef __GNUC__
+// we have too mark this as a system header because otherwise GCC barfs 
+// on variadic macros.
+#pragma GCC system_header
+#endif
+
+#include <glib.h>
+#include <gtk/gtkwidget.h>
+
+/* FIXME: Toggle this */
+
+G_BEGIN_DECLS
+
+#define DO_GDL_TRACE
+
+#ifdef DO_GDL_TRACE
+
+#ifdef __GNUC__
+
+#define GDL_TRACE()  G_STMT_START {             \
+    g_log (G_LOG_DOMAIN,                      \
+	   G_LOG_LEVEL_DEBUG,                 \
+	   "file %s: line %d (%s)",           \
+	   __FILE__,                          \
+	   __LINE__,                          \
+	   __PRETTY_FUNCTION__); } G_STMT_END 
+
+#define GDL_TRACE_EXTRA(format, args...) G_STMT_START {     \
+    g_log (G_LOG_DOMAIN,                      \
+	   G_LOG_LEVEL_DEBUG,                 \
+	   "file %s: line %d (%s): "format,   \
+	   __FILE__,                          \
+	   __LINE__,                          \
+	   __PRETTY_FUNCTION__,               \
+	   ##args); } G_STMT_END                   
+    
+#else /* __GNUC__ */
+
+#define GDL_TRACE()  G_STMT_START {             \
+    g_log (G_LOG_DOMAIN,                      \
+	   G_LOG_LEVEL_DEBUG,                 \
+	   "file %s: line %d",                \
+	   __FILE__,                          \
+	   __LINE__); } G_STMT_END 
+
+#define GDL_TRACE_EXTRA(format, args...) G_STMT_START {     \
+    g_log (G_LOG_DOMAIN,                      \
+	   G_LOG_LEVEL_DEBUG,                 \
+	   "file %s: line %d: "format,        \
+	   __FILE__,                          \
+	   __LINE__,                          \
+	   ##args); } G_STMT_END                       
+#endif /* __GNUC__ */
+
+#else /* DO_GDL_TRACE */
+
+#define GDL_TRACE()
+#define GDL_TRACE_EXTRA()
+
+#endif /* DO_GDL_TRACE */
+
+/**
+ * Class boilerplate and base class call macros copied from
+ * bonobo/bonobo-macros.h.  Original copyright follows.
+ *
+ * Author:
+ *   Darin Adler <darin bentspoon com>
+ *
+ * Copyright 2001 Ben Tea Spoons, Inc.
+ */
+
+/* Macros for defining classes.  Ideas taken from Nautilus and GOB. */
+
+/* Define the boilerplate type stuff to reduce typos and code size.  Defines
+ * the get_type method and the parent_class static variable. */
+
+#define GDL_BOILERPLATE(type, type_as_function, corba_type,                     \
+			parent_type, parent_type_macro,                         \
+			register_type_macro)                                    \
+static void type_as_function ## _class_init    (type ## Class *klass);          \
+static void type_as_function ## _instance_init (type          *object);         \
+static parent_type ## Class *parent_class = NULL;                               \
+static void                                                                     \
+type_as_function ## _class_init_trampoline (gpointer klass,                     \
+					    gpointer data)                      \
+{                                                                               \
+	parent_class = (parent_type ## Class *)g_type_class_ref (               \
+		parent_type_macro);                                             \
+	type_as_function ## _class_init ((type ## Class *)klass);               \
+}                                                                               \
+GType                                                                           \
+type_as_function ## _get_type (void)                                            \
+{                                                                               \
+	static GType object_type = 0;                                           \
+	if (object_type == 0) {                                                 \
+		static const GTypeInfo object_info = {                          \
+		    sizeof (type ## Class),                                     \
+		    NULL,		/* base_init */                         \
+		    NULL,		/* base_finalize */                     \
+		    type_as_function ## _class_init_trampoline,                 \
+		    NULL,		/* class_finalize */                    \
+		    NULL,               /* class_data */                        \
+		    sizeof (type),                                              \
+		    0,                  /* n_preallocs */                       \
+		    (GInstanceInitFunc) type_as_function ## _instance_init      \
+		};                                                              \
+		object_type = register_type_macro                               \
+			(type, type_as_function, corba_type,                    \
+			 parent_type, parent_type_macro);                       \
+	}                                                                       \
+	return object_type;                                                     \
+}
+
+/* Just call the parent handler.  This assumes that there is a variable
+ * named parent_class that points to the (duh!) parent class.  Note that
+ * this macro is not to be used with things that return something, use
+ * the _WITH_DEFAULT version for that */
+#define GDL_CALL_PARENT(parent_class_cast, name, args)          \
+	((parent_class_cast(parent_class)->name != NULL) ?      \
+	 parent_class_cast(parent_class)->name args : (void)0)
+
+#define GDL_CALL_PARENT_GBOOLEAN(parent_class_cast, name, args)          \
+       ((parent_class_cast(parent_class)->name != NULL) ?      \
+        parent_class_cast(parent_class)->name args : (gboolean)0)
+
+
+/* Same as above, but in case there is no implementation, it evaluates
+ * to def_return */
+#define GDL_CALL_PARENT_WITH_DEFAULT(parent_class_cast,                 \
+				     name, args, def_return)            \
+	((parent_class_cast(parent_class)->name != NULL) ?              \
+	 parent_class_cast(parent_class)->name args : def_return)
+
+/* Define the boilerplate type stuff to reduce typos and code size.  Defines
+ * the get_type method and the parent_class static variable. */
+#define GDL_CLASS_BOILERPLATE(type, type_as_function,           \
+				parent_type, parent_type_macro) \
+	GDL_BOILERPLATE(type, type_as_function, type,           \
+			  parent_type, parent_type_macro,       \
+			  GDL_REGISTER_TYPE)
+#define GDL_REGISTER_TYPE(type, type_as_function, corba_type,                   \
+			  parent_type, parent_type_macro)                       \
+	g_type_register_static (parent_type_macro, #type, &object_info, 0)
+
+
+#define GDL_CALL_VIRTUAL(object, get_class_cast, method, args) \
+    (get_class_cast (object)->method ? (* get_class_cast (object)->method) args : (void)0)
+#define GDL_CALL_VIRTUAL_WITH_DEFAULT(object, get_class_cast, method, args, default) \
+    (get_class_cast (object)->method ? (* get_class_cast (object)->method) args : default)
+
+/* GdlPixmap structure and method have been copied from Evolution. */
+typedef struct _GdlPixmap {
+	const char *path;
+	const char *fname;
+	char       *pixbuf;
+} GdlPixmap;
+
+#define GDL_PIXMAP(path,fname)	{ (path), (fname), NULL }
+#define GDL_PIXMAP_END		{ NULL, NULL, NULL }
+
+G_END_DECLS
+
+#endif
+

Added: trunk/src/ext/libgdl/gdl-win32.c
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-win32.c	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,42 @@
+/*
+ * Windows stuff
+ *
+ * Author:
+ *   Albin Sunnanbo
+ *   Based on code by Lauris Kaplinski <lauris kaplinski com> (/src/extension/internal/win32.cpp)
+ *
+ * This code is in public domain
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "gdl-win32.h"
+
+/* Platform detection */
+gboolean
+is_os_vista()
+{
+	static gboolean initialized = FALSE;
+	static gboolean is_vista = FALSE;
+	static OSVERSIONINFOA osver;
+
+	if ( !initialized )
+	{
+		BOOL result;
+
+		initialized = TRUE;
+
+		memset (&osver, 0, sizeof(OSVERSIONINFOA));
+		osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
+		result = GetVersionExA (&osver);
+		if (result)
+		{
+			if (osver.dwMajorVersion == WIN32_MAJORVERSION_VISTA)
+				is_vista = TRUE;
+		}
+	}
+
+	return is_vista;
+}

Added: trunk/src/ext/libgdl/gdl-win32.h
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/gdl-win32.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,30 @@
+#ifndef __INKSCAPE_GDL_WIN32_H__
+#define __INKSCAPE_GDL_WIN32_H__
+
+/*
+ * Windows stuff
+ *
+ * Author:
+ *   Albin Sunnanbo
+ *
+ * This code is in public domain
+ */
+
+
+
+#define WIN32_MAJORVERSION_VISTA               0x0006
+
+
+
+#include <config.h>
+#include <windows.h>
+#include <gdk/gdk.h>
+
+#ifndef WIN32
+#error "This file is only usable for Windows"
+#endif
+
+/* Platform detection */
+gboolean is_os_vista();
+
+#endif /* __INKSCAPE_GDL_WIN32_H__ */

Added: trunk/src/ext/libgdl/libgdl.h
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/libgdl.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,37 @@
+/*  -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 
+ * This file is part of the GNOME Devtools Libraries.
+ * 
+ * Copyright (C) 1999-2000 Dave Camp <dave helixcode com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#ifndef __GDL_H__
+#define __GDL_H__
+
+#include "libgdl/gdl-tools.h"
+#include "libgdl/gdl-dock-object.h"
+#include "libgdl/gdl-dock-master.h"
+#include "libgdl/gdl-dock.h"
+#include "libgdl/gdl-dock-item.h"
+#include "libgdl/gdl-dock-paned.h"
+#include "libgdl/gdl-dock-notebook.h"
+#include "libgdl/gdl-dock-tablabel.h"
+#include "libgdl/gdl-dock-bar.h"
+#include "libgdl/gdl-switcher.h"
+
+#endif

Added: trunk/src/ext/libgdl/libgdlmarshal.c
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/libgdlmarshal.c	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,170 @@
+
+#include	<glib-object.h>
+
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v)     g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v)      g_value_get_int (v)
+#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
+#define g_marshal_value_peek_long(v)     g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
+#define g_marshal_value_peek_float(v)    g_value_get_float (v)
+#define g_marshal_value_peek_double(v)   g_value_get_double (v)
+#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v)    g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v)   g_value_get_object (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ *          Do not access GValues directly in your code. Instead, use the
+ *          g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
+#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
+#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
+#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* VOID:VOID (./libgdlmarshal.list:1) */
+
+/* VOID:ENUM (./libgdlmarshal.list:2) */
+
+/* VOID:INT,INT (./libgdlmarshal.list:3) */
+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:4) */
+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:5) */
+
+/* VOID:OBJECT,ENUM,BOXED (./libgdlmarshal.list:6) */
+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:7) */
+

Added: trunk/src/ext/libgdl/libgdlmarshal.h
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/libgdlmarshal.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,48 @@
+
+#ifndef __gdl_marshal_MARSHAL_H__
+#define __gdl_marshal_MARSHAL_H__
+
+#include	<glib-object.h>
+
+G_BEGIN_DECLS
+
+/* VOID:VOID (./libgdlmarshal.list:1) */
+#define gdl_marshal_VOID__VOID	g_cclosure_marshal_VOID__VOID
+
+/* VOID:ENUM (./libgdlmarshal.list:2) */
+#define gdl_marshal_VOID__ENUM	g_cclosure_marshal_VOID__ENUM
+
+/* VOID:INT,INT (./libgdlmarshal.list:3) */
+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:4) */
+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:5) */
+#define gdl_marshal_VOID__BOOLEAN	g_cclosure_marshal_VOID__BOOLEAN
+
+/* VOID:OBJECT,ENUM,BOXED (./libgdlmarshal.list:6) */
+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:7) */
+#define gdl_marshal_VOID__BOXED	g_cclosure_marshal_VOID__BOXED
+
+G_END_DECLS
+
+#endif /* __gdl_marshal_MARSHAL_H__ */
+

Added: trunk/src/ext/libgdl/libgdlmarshal.list
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/libgdlmarshal.list	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,7 @@
+VOID:VOID
+VOID:ENUM
+VOID:INT,INT
+VOID:UINT,UINT
+VOID:BOOLEAN
+VOID:OBJECT,ENUM,BOXED
+VOID:BOXED

Added: trunk/src/ext/libgdl/libgdltypebuiltins.c
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/libgdltypebuiltins.c	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,183 @@
+
+/* Generated data (by glib-mkenums) */
+
+#include <glib-object.h>
+#include "libgdltypebuiltins.h"
+
+
+/* enumerations from "gdl-dock-object.h" */
+static const GFlagsValue _gdl_dock_param_flags_values[] = {
+  { GDL_DOCK_PARAM_EXPORT, "GDL_DOCK_PARAM_EXPORT", "export" },
+  { GDL_DOCK_PARAM_AFTER, "GDL_DOCK_PARAM_AFTER", "after" },
+  { 0, NULL, NULL }
+};
+
+GType
+gdl_dock_param_flags_get_type (void)
+{
+  static GType type = 0;
+
+  if (!type)
+    type = g_flags_register_static ("GdlDockParamFlags", _gdl_dock_param_flags_values);
+
+  return type;
+}
+
+static const GFlagsValue _gdl_dock_object_flags_values[] = {
+  { GDL_DOCK_AUTOMATIC, "GDL_DOCK_AUTOMATIC", "automatic" },
+  { GDL_DOCK_ATTACHED, "GDL_DOCK_ATTACHED", "attached" },
+  { GDL_DOCK_IN_REFLOW, "GDL_DOCK_IN_REFLOW", "in-reflow" },
+  { GDL_DOCK_IN_DETACH, "GDL_DOCK_IN_DETACH", "in-detach" },
+  { 0, NULL, NULL }
+};
+
+GType
+gdl_dock_object_flags_get_type (void)
+{
+  static GType type = 0;
+
+  if (!type)
+    type = g_flags_register_static ("GdlDockObjectFlags", _gdl_dock_object_flags_values);
+
+  return type;
+}
+
+static const GEnumValue _gdl_dock_placement_values[] = {
+  { GDL_DOCK_NONE, "GDL_DOCK_NONE", "none" },
+  { GDL_DOCK_TOP, "GDL_DOCK_TOP", "top" },
+  { GDL_DOCK_BOTTOM, "GDL_DOCK_BOTTOM", "bottom" },
+  { GDL_DOCK_RIGHT, "GDL_DOCK_RIGHT", "right" },
+  { GDL_DOCK_LEFT, "GDL_DOCK_LEFT", "left" },
+  { GDL_DOCK_CENTER, "GDL_DOCK_CENTER", "center" },
+  { GDL_DOCK_FLOATING, "GDL_DOCK_FLOATING", "floating" },
+  { 0, NULL, NULL }
+};
+
+GType
+gdl_dock_placement_get_type (void)
+{
+  static GType type = 0;
+
+  if (!type)
+    type = g_enum_register_static ("GdlDockPlacement", _gdl_dock_placement_values);
+
+  return type;
+}
+
+
+
+static const GEnumValue _gdl_dock_expansion_direction_values[] = {
+  { GDL_DOCK_EXPANSION_DIRECTION_NONE, "GDL_DOCK_EXPANSION_DIRECTION_NONE", "none" },
+  { GDL_DOCK_EXPANSION_DIRECTION_UP, "GDL_DOCK_EXPANSION_DIRECTION_UP", "up" },
+  { GDL_DOCK_EXPANSION_DIRECTION_DOWN, "GDL_DOCK_EXPANSION_DIRECTION_DOWN", "down" },
+  { GDL_DOCK_EXPANSION_DIRECTION_LEFT, "GDL_DOCK_EXPANSION_DIRECTION_LEFT", "left" },
+  { GDL_DOCK_EXPANSION_DIRECTION_RIGHT, "GDL_DOCK_EXPANSION_DIRECTION_RIGHT", "right" },
+  { 0, NULL, NULL }
+};
+
+GType
+gdl_dock_expansion_direction_get_type (void)
+{
+  static GType type = 0;
+
+  if (!type)
+    type = g_enum_register_static ("GdlDockExpansionDirection", _gdl_dock_expansion_direction_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 */
+

Added: trunk/src/ext/libgdl/libgdltypebuiltins.h
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/libgdltypebuiltins.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,40 @@
+
+/* 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);
+#define GDL_TYPE_EXPANSION_DIRECTION gdl_dock_expansion_direction_get_type()
+GType gdl_dock_expansion_direction_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 */
+

Added: trunk/src/ext/libgdl/makefile.in
==============================================================================
--- (empty file)
+++ trunk/src/ext/libgdl/makefile.in	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,17 @@
+# Convenience stub makefile to call the real Makefile.
+
+ SET_MAKE@
+
+OBJEXT = @OBJEXT@
+
+# Explicit so that it's the default rule.
+all:
+	cd .. && $(MAKE) libgdl/all
+
+clean %.a %.$(OBJEXT):
+	cd .. && $(MAKE) libgdl/$@
+
+.PHONY: all clean
+
+.SUFFIXES:
+.SUFFIXES: .a .$(OBJEXT)

Added: trunk/src/framework/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/framework/Makefile.am	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,31 @@
+
+
+
+INCLUDES = -I$(top_srcdir)/src -I$(top_srcdir)/src/ext \
+	@LIBGTKMM_CFLAGS@ @LIBGLADEMM_CFLAGS@ \
+	@LIBGCONFMM_CFLAGS@  @GNOMEVFS_CFLAGS@ @EXEMPI_CFLAGS@ \
+	$(NULL)
+
+noinst_LIBRARIES = libniepceframework.a
+
+noinst_HEADERS = configuration.h application.h \
+	frame.h controller.h goocanvas_proxy_header.h \
+	gconf_proxy_header.h
+
+libniepceframework_a_SOURCES = configuration.cpp \
+	application.cpp frame.cpp controller.cpp \
+	notification.h \
+	mimetype.h mimetype.cpp \
+	imageloader.h imageloader.cpp \
+	notificationcenter.h notificationcenter.cpp \
+	configdatabinder.h configdatabinder.cpp \
+	gdkutils.h gdkutils.cpp \
+	widgets/toolboxitemwidget.h widgets/toolboxitemwidget.cpp \
+	widgets/editablehscale.h widgets/editablehscale.cpp \
+	widgets/dock-item.cpp widgets/dock-item.h \
+	widgets/dock.cpp widgets/dock.h \
+	dockable.h dockable.cpp \
+	metadatawidget.h metadatawidget.cpp \
+	undo.h undo.cpp \
+	command.h \
+	$(NULL)

Added: trunk/src/framework/application.cpp
==============================================================================
--- (empty file)
+++ trunk/src/framework/application.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,186 @@
+/*
+ * niepce - framework/application.cpp
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <glibmm/i18n.h>
+#include <gtkmm/main.h>
+#include <gtkmm/aboutdialog.h>
+#include <gtkmm/rc.h>
+
+#include "utils/debug.h"
+#include "application.h"
+#include "frame.h"
+
+
+namespace framework {
+
+Application::Ptr Application::m_application;
+
+Application::Application(const char * name)
+    : m_config(Glib::ustring("/apps/") + name),
+      m_refUIManager()
+{
+    register_theme(_("System"), "");
+}
+
+
+Application::~Application()
+{
+}
+
+/** no widget for applications */
+Gtk::Widget * Application::buildWidget()
+{
+    return NULL;
+}
+
+Application::Ptr Application::app()
+{
+    return m_application;
+}
+
+
+Glib::ustring Application::get_rc_path()
+{
+    return m_config.getValue("ui_theme_file", "");
+}
+
+Glib::RefPtr<Gtk::IconTheme> Application::getIconTheme() const
+{
+    return Gtk::IconTheme::get_default();
+}
+
+int Application::get_use_custom_theme() const
+{
+    int v;
+    try {
+        v = boost::lexical_cast<int>(m_config.getValue("ui_theme_set", "0"));
+    } 
+    catch(...) {
+        v = 0;
+    }
+    return v != 0;
+}
+
+void Application::set_use_custom_theme(int theme_idx)
+{
+    m_config.setValue("ui_theme_set",
+                      boost::lexical_cast<Glib::ustring>(theme_idx));
+    if((theme_idx > -1) && ((size_t)theme_idx < m_themes.size())) {
+        m_config.setValue("ui_theme_file", m_themes[theme_idx].second);
+    }
+}
+
+
+void Application::register_theme(const Glib::ustring & label,
+                                 const std::string & path)
+{
+    m_themes.push_back(std::make_pair(label, path));
+}
+
+
+/** Main loop. 
+ * @param constructor the Application object constructor
+ * @param argc
+ * @param argv
+ * @return main return code
+ */
+int Application::main(boost::function<Application::Ptr (void)> constructor, 
+                      int argc, char **argv)
+{
+    Gnome::Conf::init();
+    if(!Glib::thread_supported()) {
+        DBG_OUT("thread init");
+        Glib::thread_init();
+    }
+
+    Gtk::Main kit(argc, argv);
+    Application::Ptr app = constructor();
+
+    DBG_OUT("use_custon_theme %d", app->get_use_custom_theme());
+    if(app->get_use_custom_theme() != -1) {
+        std::string rcpath = app->get_rc_path();
+        if(!rcpath.empty()) {
+            Gtk::RC rc(rcpath);
+        }
+    }
+
+    kit.signal_run().connect(sigc::mem_fun(get_pointer(app), 
+                                           &Application::_ready));
+    Frame::Ptr window(app->makeMainFrame());
+    app->add(window);
+		
+    Gtk::Main::run(window->gtkWindow());
+	
+    return 0;
+}
+
+
+void Application::terminate()
+{
+    std::for_each(m_subs.begin(), m_subs.end(),
+                  boost::bind(&Controller::terminate, _1));
+    std::for_each(m_subs.begin(), m_subs.end(),
+                  boost::bind(&Controller::clearParent, _1));		
+    m_subs.clear();
+}
+
+
+void Application::quit()
+{
+    terminate();
+    Gtk::Main::quit();
+}
+
+void Application::about()
+{
+    on_about();
+}
+
+/** adding a controller to an application build said controller
+ * widget 
+ */
+void Application::add(const Controller::Ptr & sub)
+{
+    Controller::add(sub);
+    sub->buildWidget();
+}
+
+void Application::on_about()
+{
+    Gtk::AboutDialog dlg;
+    dlg.run();
+}
+
+}
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/

Added: trunk/src/framework/application.h
==============================================================================
--- (empty file)
+++ trunk/src/framework/application.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,104 @@
+/*
+ * niepce - framework/application.h
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _FRAMEWORK_APPLICATION_H_
+#define _FRAMEWORK_APPLICATION_H_
+
+#include <boost/function.hpp>
+
+#include <glibmm/refptr.h>
+#include <gtkmm/uimanager.h>
+#include <gtkmm/icontheme.h>
+
+#include "framework/configuration.h"
+#include "framework/frame.h"
+#include "framework/undo.h"
+
+
+namespace framework {
+
+class Application 
+    : public Controller
+{
+public:
+    typedef boost::shared_ptr<Application> Ptr;
+    typedef std::pair<Glib::ustring, std::string> ThemeDesc;
+
+    virtual ~Application();
+
+    virtual Glib::ustring get_rc_path();
+    virtual int get_use_custom_theme() const;
+    virtual void set_use_custom_theme(int theme_idx);
+    const std::vector<ThemeDesc> & get_available_themes() const
+        {
+            return m_themes;
+        }
+
+    virtual Frame::Ptr makeMainFrame() = 0;
+
+    Configuration & config()
+        { return m_config; }
+    Glib::RefPtr<Gtk::UIManager> uiManager()
+        { 
+            if(!m_refUIManager) {
+                m_refUIManager = Gtk::UIManager::create();
+            }
+            return m_refUIManager; 
+        }
+
+    virtual Gtk::Widget * buildWidget();
+    virtual void quit();
+    void about();
+    virtual void add(const Controller::Ptr & sub);
+    virtual void terminate();
+
+    Glib::RefPtr<Gtk::IconTheme> getIconTheme() const;
+
+    static Application::Ptr app();
+    static int main(boost::function<Application::Ptr (void)> constructor, 
+                    int argc, char **argv);
+
+    UndoHistory & undo_history()
+        { return m_undo; }
+protected:
+    Application(const char *);
+    static Application::Ptr m_application; 
+    virtual void on_about();
+    void register_theme(const Glib::ustring & label,
+                        const std::string &path);
+private:
+    Configuration                m_config;
+    Glib::RefPtr<Gtk::UIManager> m_refUIManager;
+    UndoHistory                  m_undo;
+    std::vector<ThemeDesc>       m_themes;
+};
+
+}
+
+#endif
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/

Added: trunk/src/framework/command.h
==============================================================================
--- (empty file)
+++ trunk/src/framework/command.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,48 @@
+/*
+ * niepce - framework/command.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __FRAMEWORK_COMMAND_H_
+#define __FRAMEWORK_COMMAND_H_
+
+#include <boost/function.hpp>
+
+namespace framework {
+
+class Command
+{
+public:
+    boost::function<void (void)> undo;
+    boost::function<void (void)> redo;
+};
+
+
+#endif
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/framework/configdatabinder.cpp
==============================================================================
--- (empty file)
+++ trunk/src/framework/configdatabinder.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,39 @@
+/*
+ * niepce - framework/configdatabinder.cpp
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "configdatabinder.h"
+
+namespace framework {
+
+
+ConfigDataBinderBase::ConfigDataBinderBase(const property_t & property,
+										   Configuration & config, 
+										   const std::string & key)
+	: utils::DataBinderBase(),
+	  m_property(property),
+	  m_config_key(key),
+	  m_config(config)
+{
+	m_conn = m_property.signal_changed().connect(
+		sigc::mem_fun(*this, &ConfigDataBinderBase::on_changed));
+}
+
+
+}
+

Added: trunk/src/framework/configdatabinder.h
==============================================================================
--- (empty file)
+++ trunk/src/framework/configdatabinder.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,110 @@
+/*
+ * niepce - framework/configdatabinder.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _FRAMEWORK_CONFIGDATABINDER_H_
+#define _FRAMEWORK_CONFIGDATABINDER_H_
+
+#include <exception>
+#include <string>
+
+#include <boost/lexical_cast.hpp>
+
+#include <glibmm/propertyproxy.h>
+
+#include "utils/debug.h"
+#include "utils/databinder.h"
+#include "framework/configuration.h"
+
+namespace framework {
+
+class ConfigDataBinderBase
+	: public utils::DataBinderBase
+{
+public:
+	typedef Glib::PropertyProxy_Base property_t;
+
+	ConfigDataBinderBase(const property_t & property,
+						 Configuration & config, const std::string & key);
+	
+	virtual void on_changed(void) = 0;
+protected:
+	property_t        m_property;
+	std::string       m_config_key;
+	Configuration   & m_config;
+	sigc::connection  m_conn;
+};
+
+template <class T>
+class ConfigDataBinder
+	: public ConfigDataBinderBase
+{
+public:
+	typedef Glib::PropertyProxy<T> property_t;
+
+	ConfigDataBinder(const property_t & property,
+					 Configuration & config, const std::string & key)
+		: ConfigDataBinderBase(property, config, key)
+		{
+			Glib::ustring value;
+			value = m_config.getValue(m_config_key, "");
+			if(!value.empty()) {
+				try {
+					static_cast<property_t&>(m_property).set_value(
+						boost::lexical_cast<T>(value));
+				}
+				catch(const boost::bad_lexical_cast &)
+				{
+					ERR_OUT("exception converting %s", value.c_str());
+				}
+			}
+		}
+
+	virtual ~ConfigDataBinder()
+		{
+			try {
+				m_config.setValue(m_config_key, 
+								  boost::lexical_cast<std::string>(m_value));
+			}
+			catch(const boost::bad_lexical_cast &)
+			{
+				ERR_OUT("exception");
+			}
+		}
+
+
+	virtual void on_changed(void)
+		{
+			try {
+				m_value = static_cast<property_t&>(m_property).get_value();
+			}
+			catch(const std::exception &)
+			{
+				ERR_OUT("exception");
+			}
+		}
+private:
+	T m_value;
+};
+
+
+}
+
+
+#endif

Added: trunk/src/framework/configuration.cpp
==============================================================================
--- (empty file)
+++ trunk/src/framework/configuration.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,87 @@
+/*
+ * niepce - framework/configuration.cpp
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <memory>
+
+#include "gconf_proxy_header.h"
+
+#include "utils/debug.h"
+#include "configuration.h"
+
+
+namespace framework {
+
+	Configuration::Configuration(const Glib::ustring & root)
+		: m_gconf(Gnome::Conf::Client::get_default_client()),
+		  m_root(root)
+	{
+	}
+
+
+	Configuration::~Configuration()
+	{
+	}
+
+
+	bool Configuration::hasKey(const Glib::ustring & key) const
+	{
+		//
+		bool found = true;
+
+		try {
+			m_gconf->get(m_root + "/" + key);
+		}
+		catch(Gnome::Conf::Error & err) {
+			DBG_OUT("key %s not found", key.c_str());
+			DBG_OUT("exception is %s", err.what().c_str());
+			found = false;
+		}
+
+		return found;
+	}
+
+
+	const Glib::ustring Configuration::getValue(const Glib::ustring & key,
+												const Glib::ustring & def) const
+	{
+		Glib::ustring value;
+		try {
+			value = m_gconf->get_string(m_root + "/" + key);
+		}
+		catch(Gnome::Conf::Error &err) {
+			value = def;
+			DBG_OUT("Exception raised: %s", err.what().c_str());
+		}
+
+		return value;
+	}
+
+	void Configuration::setValue(const Glib::ustring & key, 
+								 const Glib::ustring & value)
+	{
+		try {
+			m_gconf->set(m_root + "/" + key, value);
+		}
+		catch(Gnome::Conf::Error & err) {
+			DBG_OUT("Exception raised: %s", err.what().c_str());
+		}
+	}
+
+}

Added: trunk/src/framework/configuration.h
==============================================================================
--- (empty file)
+++ trunk/src/framework/configuration.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,50 @@
+/*
+ * niepce - framework/configuration.h
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _FRAMEWORK_CONFIGURATION_H_
+#define _FRAMEWORK_CONFIGURATION_H_
+
+#include <glibmm/ustring.h>
+#include <glibmm/refptr.h>
+
+#include "framework/gconf_proxy_header.h"
+
+
+namespace framework {
+
+	class Configuration
+	{
+	public:
+		Configuration(const Glib::ustring & root);
+		~Configuration();
+
+		bool hasKey(const Glib::ustring & key) const;
+		const Glib::ustring getValue(const Glib::ustring & key,
+									 const Glib::ustring & def) const;
+
+		void setValue(const Glib::ustring & key, const Glib::ustring & value);
+	private:
+		Glib::RefPtr< Gnome::Conf::Client > m_gconf;
+		Glib::ustring                       m_root;
+	};
+
+}
+
+#endif

Added: trunk/src/framework/controller.cpp
==============================================================================
--- (empty file)
+++ trunk/src/framework/controller.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,103 @@
+/*
+ * niepce - framework/controller.cpp
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <boost/bind.hpp>
+
+#include <gtkmm/widget.h>
+
+#include "utils/debug.h"
+#include "controller.h"
+
+
+namespace framework {
+
+Controller::Controller()
+    : m_widget(NULL)
+{
+}
+
+
+Controller::~Controller()
+{
+//    DBG_OUT("destroy Controllers");
+}
+
+Gtk::Widget * Controller::widget()
+{
+    DBG_ASSERT(!m_parent.expired(), "must be attached");
+    if(m_widget == NULL) 
+    {
+        m_widget = buildWidget();
+    }
+    return m_widget;
+}
+
+void
+Controller::add(const Controller::Ptr & sub)
+{
+    m_subs.push_back(sub);
+    sub->m_parent = shared_from_this();
+    sub->_added();
+}
+
+void Controller::remove(const Ptr & sub)
+{
+    std::list<Ptr>::iterator iter = std::find(m_subs.begin(), 
+                                              m_subs.end(), sub);
+    if(iter != m_subs.end()) {
+        (*iter)->clearParent();
+        m_subs.erase(iter);
+    }
+}
+
+bool Controller::canTerminate()
+{
+    return true;
+}
+
+void Controller::terminate()
+{
+}
+
+void Controller::_added()
+{
+}
+
+void Controller::_ready()
+{
+    std::for_each(m_subs.begin(), m_subs.end(),
+                  boost::bind(&Controller::_ready, _1));
+    on_ready();
+}
+
+void Controller::on_ready()
+{
+}
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/framework/controller.h
==============================================================================
--- (empty file)
+++ trunk/src/framework/controller.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,95 @@
+/*
+ * niepce - framework/controller.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+#ifndef __FRAMEWORK_CONTROLLER_H__
+#define __FRAMEWORK_CONTROLLER_H__
+
+
+#include <list>
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include "utils/databinder.h"
+
+namespace Gtk {
+	class Widget;
+}
+
+namespace framework {
+
+	/** Generic controller class
+	 */
+	class Controller
+		: public boost::enable_shared_from_this<Controller>
+	{
+	public:
+		typedef boost::shared_ptr<Controller> Ptr;
+		typedef boost::weak_ptr<Controller> WeakPtr;
+
+		Controller();
+		virtual ~Controller();
+
+		/** add a subcontroller to this one */
+		void add(const Ptr & sub);
+		/** clear the parent. Usually called by the parent when unparenting */
+		void clearParent()
+			{ m_parent.reset(); }
+		void remove(const Ptr & sub);
+		
+		virtual bool canTerminate();
+		/** signal that the controller needs to terminate */
+		virtual void terminate();
+
+		/** return the widget controlled (construct it if needed) */
+		virtual Gtk::Widget * buildWidget() = 0;
+		Gtk::Widget * widget();
+
+		/** called when everything is ready 
+		 * subclasses should reimplement if needed
+		 */
+		virtual void on_ready();
+	protected:
+		/** called when the controller has been added to a parent. */
+		virtual void _added();
+
+		void _ready();
+		Gtk::Widget* m_widget;
+
+		WeakPtr          m_parent;
+		std::list<Ptr> m_subs; /**< sub controllers */
+
+		utils::DataBinderPool m_databinders;
+	};
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+
+#endif

Added: trunk/src/framework/dockable.cpp
==============================================================================
--- (empty file)
+++ trunk/src/framework/dockable.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,43 @@
+/*
+ * niepce - ui/dockable.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "dockable.h"
+
+namespace framework {
+
+Dockable::Dockable(framework::Dock & dock, const Glib::ustring& name, 
+             const Glib::ustring& long_name, 
+             const Glib::ustring& icon_name, DockItem::State state)
+    : DockItem(dock, name, long_name, icon_name, state)
+{
+}
+
+
+}
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/
+

Added: trunk/src/framework/dockable.h
==============================================================================
--- (empty file)
+++ trunk/src/framework/dockable.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,56 @@
+/*
+ * niepce - ui/dockable.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __FRAMEWORK_DOCKABLE_H__
+#define __FRAMEWORK_DOCKABLE_H__
+
+#include "framework/controller.h"
+#include "framework/widgets/dock-item.h"
+
+namespace framework {
+
+
+/** A dockable item controller
+ */
+class Dockable
+    : public Controller,
+      protected DockItem
+{
+public:
+    Dockable(framework::Dock & dock, const Glib::ustring& name, 
+             const Glib::ustring& long_name, 
+             const Glib::ustring& icon_name, DockItem::State state);
+
+};
+
+
+}
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/
+
+#endif
+

Added: trunk/src/framework/frame.cpp
==============================================================================
--- (empty file)
+++ trunk/src/framework/frame.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,195 @@
+/*
+ * niepce - framework/application.cpp
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <list>
+#include <vector>
+#include <boost/bind.hpp>
+
+#include <gtkmm/dialog.h>
+
+#include "utils/debug.h"
+#include "utils/boost.h"
+#include "utils/geometry.h"
+#include "frame.h"
+#include "application.h"
+
+
+
+namespace framework {
+
+	Frame::Frame(const std::string & layout_cfg_key)
+		: m_window(new Gtk::Window()),
+		  m_glade(NULL),
+		  m_layout_cfg_key(layout_cfg_key)
+	{
+		connectSignals();
+		frameRectFromConfig();
+	}
+
+
+	Frame::Frame(const std::string & gladeFile, 
+				 const Glib::ustring & widgetName,
+				 const std::string & layout_cfg_key)
+		: m_window(NULL),
+		  m_glade(Gnome::Glade::Xml::create(gladeFile)),
+		  m_layout_cfg_key(layout_cfg_key)
+	{
+		if (m_glade) {
+			m_window = static_cast<Gtk::Window*>(m_glade->get_widget(widgetName));
+			connectSignals();
+			frameRectFromConfig();
+		}
+	}
+
+
+	void Frame::connectSignals()
+	{
+		m_window->signal_delete_event().connect(
+			sigc::hide(sigc::mem_fun(this, &Frame::_close)));
+		m_window->signal_hide().connect(
+			sigc::retype_return<void>(sigc::mem_fun(this, &Frame::_close)));
+	}
+
+	Frame::~Frame()
+	{
+		frameRectToConfig();
+        DBG_OUT("destroy Frame");
+        delete m_window;
+	}
+
+
+	void Frame::set_icon_from_theme(const Glib::ustring & name)
+	{
+		using Glib::RefPtr;
+		using Gtk::IconTheme;
+		using std::vector;
+		using std::list;
+		
+		RefPtr< IconTheme > icon_theme(Application::app()->getIconTheme());
+		vector<int> icon_sizes(icon_theme->get_icon_sizes(name));
+		
+		list< RefPtr <Gdk::Pixbuf> > icons;
+
+		for_each(icon_sizes.begin(), icon_sizes.end(),
+				 // store the icon
+				 bind(&std::list< RefPtr<Gdk::Pixbuf> >::push_back, 
+					  boost::ref(icons), 
+					  // load the icon
+					  bind( &IconTheme::load_icon, 
+							boost::ref(icon_theme), 
+							boost::ref(name), _1, 
+							Gtk::ICON_LOOKUP_USE_BUILTIN)));
+		gtkWindow().set_icon_list(icons);
+	}
+
+	void Frame::set_title(const std::string & title)
+	{
+		gtkWindow().set_title(Glib::ustring(title));
+	}
+
+	int Frame::show_modal_dialog(Gtk::Dialog & dlg)
+	{
+		int result;
+		dlg.set_transient_for(*m_window);
+		dlg.set_default_response(Gtk::RESPONSE_CLOSE);
+		result = dlg.run();
+		dlg.hide();
+		return result;
+	}
+
+	int Frame::show_modal_dialog(const char *gladefile,
+								 const char *widgetname,
+								 boost::function<void (const Glib::RefPtr<Gnome::Glade::Xml> &, Gtk::Dialog *)> setup)
+	{
+		Glib::RefPtr<Gnome::Glade::Xml> refXml 
+            = Gnome::Glade::Xml::create(gladefile);
+		Gtk::Dialog *dlg = NULL;
+
+		dlg = refXml->get_widget(widgetname, dlg);
+		if(setup) {
+			setup(refXml, dlg);
+		}
+		return show_modal_dialog(*dlg);
+	}
+
+    void Frame::toggle_tools_visible()
+    {
+        if(m_hide_tools_action->get_active()) {
+            signal_hide_tools.emit();   
+        }
+        else {
+            signal_show_tools.emit();
+        }
+    }
+
+
+	bool Frame::_close()
+	{
+		if(Controller::Ptr parent = m_parent.lock()) {
+			parent->remove(shared_from_this());
+		}
+		return false;
+	}
+
+	void Frame::frameRectFromConfig()
+	{
+		DBG_OUT("loading frame rect (%s)", m_layout_cfg_key.c_str());
+		if(!m_layout_cfg_key.empty()) {
+			Configuration & cfg = Application::app()->config();
+			std::string val;
+			val = cfg.getValue(m_layout_cfg_key, "");
+			if(!val.empty()) {
+				try {
+					utils::Rect r(val);
+					m_window->move(r.x(), r.y());
+					m_window->resize(r.w(), r.h());				
+				}
+				catch(std::bad_cast)
+				{
+					ERR_OUT("wrong value in configuration: %s", val.c_str());
+				}
+			}
+		}
+	}
+
+
+	void Frame::frameRectToConfig()
+	{
+		DBG_OUT("saving frame rect (%s)", m_layout_cfg_key.c_str());
+		if(!m_layout_cfg_key.empty()) {
+			Configuration & cfg = Application::app()->config();
+			int x, y, w, h;
+			x = y = w = h = 0;
+			m_window->get_position(x, y);
+			m_window->get_size(w, h);
+			utils::Rect r(x, y, w, h);
+			cfg.setValue(m_layout_cfg_key, r.to_string());
+		}
+	}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/
+}

Added: trunk/src/framework/frame.h
==============================================================================
--- (empty file)
+++ trunk/src/framework/frame.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,109 @@
+/*
+ * niepce - framework/frame.h
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _FRAMEWORK_FRAME_H_
+#define _FRAMEWORK_FRAME_H_
+
+#include <string>
+#include <boost/function.hpp>
+
+#include <sigc++/signal.h>
+#include <libglademm/xml.h>
+#include <gtkmm/toggleaction.h>
+
+#include "framework/controller.h"
+
+namespace Gtk {
+	class Dialog;
+}
+
+namespace framework {
+
+	class Frame 
+		: public Controller
+	{
+	public:
+		typedef boost::shared_ptr<Frame> Ptr;
+
+		Frame(const std::string & gladeFile, const Glib::ustring & widgetName,
+			  const std::string & layout_cfg_key = "");
+		Frame(const std::string & layout_cfg_key = "");
+		~Frame();
+
+		Gtk::Window & gtkWindow()
+			{
+				return *m_window; 
+			}
+		Glib::RefPtr<Gnome::Glade::Xml> & glade()
+			{ return m_glade; }
+
+		/** set the title of the window.
+		 * @param title the title of the window.
+		 * 
+		 * override to provide you own hooks - behaviour.
+		 */
+		virtual void set_title(const std::string & title);
+		/** set the window icon from the theme 
+		 * @param name the icon name in the theme
+		 */
+		void set_icon_from_theme(const Glib::ustring & name);
+
+		/** show a model dialog
+		 * @param dlg the dialog to show.
+		 * @return the result from Dialog::run()
+		 */
+		int show_modal_dialog(Gtk::Dialog & dlg);
+		int show_modal_dialog(const char *gladefile,
+							  const char *widgetname,
+							  boost::function<void (const Glib::RefPtr<Gnome::Glade::Xml> &, Gtk::Dialog *)> setup = NULL);
+
+        
+        void toggle_tools_visible();
+
+        sigc::signal<void> signal_hide_tools;
+        sigc::signal<void> signal_show_tools;
+	protected:
+		/** close signal handler */
+		virtual bool _close();
+        Glib::RefPtr<Gtk::ToggleAction> m_hide_tools_action;
+
+	private:
+		void connectSignals();
+		void frameRectFromConfig();
+		void frameRectToConfig();
+
+		Gtk::Window *m_window;
+		Glib::RefPtr<Gnome::Glade::Xml> m_glade;
+		std::string m_layout_cfg_key;
+	};
+
+}
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/
+#endif

Added: trunk/src/framework/gconf_proxy_header.h
==============================================================================
--- (empty file)
+++ trunk/src/framework/gconf_proxy_header.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,34 @@
+/*
+ * niepce - framework/gconf_proxy_header.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @brief Wrap Gconfmm to be warning free. */
+
+#ifndef __GCONF_PROXY_HEADER_H__
+#define __GCONF_PROXY_HEADER_H__
+
+/*
+ * Insert here the work around for the warning disabling to your taste.
+ */
+#if __GNUC__
+#pragma GCC system_header
+#endif
+#include <gconfmm.h>
+
+
+#endif

Added: trunk/src/framework/gdkutils.cpp
==============================================================================
--- (empty file)
+++ trunk/src/framework/gdkutils.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,77 @@
+/*
+ * niepce - frawework/gdkutils.cpp
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gdkutils.h"
+
+
+
+namespace framework {
+
+	Glib::RefPtr<Gdk::Pixbuf> gdkpixbuf_scale_to_fit(const Glib::RefPtr<Gdk::Pixbuf> & pix,
+													 int dim)
+	{
+		int height, width;
+		int orig_h, orig_w;
+		orig_h = pix->get_height();
+		orig_w = pix->get_width();
+		int orig_dim = std::max(orig_h, orig_w);
+		double ratio = (double)dim / (double)orig_dim;
+		width = (int)(ratio * orig_w);
+		height = (int)(ratio * orig_h);
+		return pix->scale_simple(width, height, 
+								 Gdk::INTERP_BILINEAR);
+	}
+
+	Glib::RefPtr<Gdk::Pixbuf> gdkpixbuf_exif_rotate(const Glib::RefPtr<Gdk::Pixbuf> & tmp,
+													int exif_orientation)
+	{
+		Glib::RefPtr<Gdk::Pixbuf> pixbuf;
+		switch(exif_orientation) {
+		case 0:
+		case 1:
+			pixbuf = tmp;
+			break;
+		case 2:
+			pixbuf = tmp->flip(TRUE);
+			break;
+		case 3:
+			pixbuf = tmp->rotate_simple(Gdk::PIXBUF_ROTATE_UPSIDEDOWN);
+			break;
+		case 4:
+			pixbuf = tmp->rotate_simple(Gdk::PIXBUF_ROTATE_UPSIDEDOWN)->flip(TRUE);
+			break;
+		case 5:
+			pixbuf = tmp->rotate_simple(Gdk::PIXBUF_ROTATE_CLOCKWISE)->flip(FALSE);
+			break;
+		case 6:
+			pixbuf =  tmp->rotate_simple(Gdk::PIXBUF_ROTATE_CLOCKWISE);
+			break;
+		case 7:
+			pixbuf =  tmp->rotate_simple(Gdk::PIXBUF_ROTATE_COUNTERCLOCKWISE)->flip(FALSE);
+			break;		
+		case 8:
+			pixbuf =  tmp->rotate_simple(Gdk::PIXBUF_ROTATE_COUNTERCLOCKWISE);
+			break;		
+		default:
+			break;
+		}
+		return pixbuf;
+	}
+
+}

Added: trunk/src/framework/gdkutils.h
==============================================================================
--- (empty file)
+++ trunk/src/framework/gdkutils.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,39 @@
+/*
+ * niepce - frawework/gdkutils.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __FRAMEWORK_GDKUTILS_H__
+#define __FRAMEWORK_GDKUTILS_H__
+
+#include <gdkmm/pixbuf.h>
+
+
+namespace framework {
+	
+	/** scale the pixbuf to fit in the square 
+	 * @param dim the dimension of the square
+	 */
+	Glib::RefPtr<Gdk::Pixbuf> gdkpixbuf_scale_to_fit(const Glib::RefPtr<Gdk::Pixbuf> & pix,
+													 int dim);
+	/** Rotate a pixbuf following the Exif rotation (may mirror too) */
+	Glib::RefPtr<Gdk::Pixbuf> gdkpixbuf_exif_rotate(const Glib::RefPtr<Gdk::Pixbuf> & pixbuf,
+													int exif_orientation);
+}
+
+#endif

Added: trunk/src/framework/goocanvas_proxy_header.h
==============================================================================
--- (empty file)
+++ trunk/src/framework/goocanvas_proxy_header.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,38 @@
+/*
+ * niepce - framework/goocanvas_proxy_header.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @brief Wrap goocanvas to be warning free. */
+
+#ifndef __GOOCANVAS_PROXY_HEADER_H__
+#define __GOOCANVAS_PROXY_HEADER_H__
+
+/*
+ * Insert here the work around for the warning disabling to your taste.
+ */
+#if __GNUC__
+#pragma GCC system_header
+#endif
+#include <goocanvasmm/canvas.h>
+#include <goocanvasmm/image.h>
+#include <goocanvasmm/rect.h>
+
+
+
+
+#endif

Added: trunk/src/framework/imageloader.cpp
==============================================================================
--- (empty file)
+++ trunk/src/framework/imageloader.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,61 @@
+/*
+ * niepce - framework/imageloader.cpp
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+
+#include <gdkmm/pixbufloader.h>
+
+#include "imageloader.h"
+#include "framework/mimetype.h"
+
+namespace framework {
+
+ImageLoader::ImageLoader(const boost::filesystem::path & file)
+	: m_file(file)
+{
+}
+
+Glib::RefPtr<Gdk::Pixbuf> ImageLoader::get_pixbuf()
+{
+	// TODO split get_pixbuf and the real load.
+	framework::MimeType mime_type(m_file);
+	
+	Glib::RefPtr<Gdk::PixbufLoader> loader =  
+		Gdk::PixbufLoader::create(mime_type.string(), true);
+
+	// TODO this code is ugly.
+	FILE * f = fopen(m_file.string().c_str(), "rb");
+    if(f) {
+        size_t byte_read;
+        guint8 buffer[128*1024];
+        do {
+            byte_read = fread((void*)buffer, 1, 128*1024, f);
+            if(byte_read) {
+                loader->write(buffer, byte_read);
+            }
+        } while(byte_read);
+	}
+    loader->close();
+
+	return loader->get_pixbuf();
+}
+
+
+}
+

Added: trunk/src/framework/imageloader.h
==============================================================================
--- (empty file)
+++ trunk/src/framework/imageloader.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,41 @@
+/*
+ * niepce - framework/imageloader.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __FRAMEWORK_IMAGELOADER_H__
+#define __FRAMEWORK_IMAGELOADER_H__
+
+#include <boost/filesystem/path.hpp>
+
+#include <gdkmm/pixbuf.h>
+
+namespace framework {
+
+class ImageLoader
+{
+public:
+	ImageLoader(const boost::filesystem::path & file);
+	Glib::RefPtr<Gdk::Pixbuf> get_pixbuf();
+private:
+	boost::filesystem::path m_file;
+};
+
+}
+
+#endif

Added: trunk/src/framework/metadatawidget.cpp
==============================================================================
--- (empty file)
+++ trunk/src/framework/metadatawidget.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,163 @@
+/*
+ * niepce - framework/metadatawidget.cpp
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <utility>
+
+#include <boost/lexical_cast.hpp>
+#include <boost/bind.hpp>
+#include <glibmm/i18n.h>
+#include <gtkmm/label.h>
+
+#include "utils/exempi.h"
+#include "utils/stringutils.h"
+#include "utils/debug.h"
+
+#include "metadatawidget.h"
+
+
+
+namespace framework {
+
+MetaDataWidget::MetaDataWidget(const Glib::ustring & title)
+    : ToolboxItemWidget(title),
+      m_table(1, 2, false),
+      m_fmt(NULL)
+{
+    add(m_table);
+}
+
+void MetaDataWidget::set_data_format(const xmp::MetaDataSectionFormat * fmt)
+{
+    m_fmt = fmt;
+}
+
+namespace {
+static 
+void clear_widget(std::pair<const std::string, Gtk::Widget *> & p)
+{
+    Gtk::Label * l = dynamic_cast<Gtk::Label*>(p.second);
+    if(l) {
+        l->set_text("");
+    }
+}
+}
+
+void MetaDataWidget::set_data_source(const utils::XmpMeta * xmp)
+{
+    DBG_OUT("set data source");
+    if(!m_data_map.empty()) {
+        std::for_each(m_data_map.begin(), m_data_map.end(),
+                      boost::bind(&clear_widget, _1));
+    }
+    if(!xmp) {
+        return;
+    }
+    if(!m_fmt) {
+        DBG_OUT("empty format");
+        return;
+    }
+
+    const xmp::MetaDataFormat * current = m_fmt->formats;
+    xmp::ScopedPtr<XmpStringPtr> value(xmp_string_new());
+    while(current && current->label) {
+        std::string id(current->property);
+        id += "-";
+        id += current->ns;
+        if(current->type == xmp::META_DT_STRING_ARRAY) {
+            xmp::ScopedPtr<XmpIteratorPtr> 
+                iter(xmp_iterator_new(xmp->xmp(), current->ns,
+                                      current->property, XMP_ITER_JUSTLEAFNODES));
+            std::vector<std::string> vec;
+            while(xmp_iterator_next(iter, NULL, NULL, value, NULL)) {
+                vec.push_back(xmp_string_cstr(value));
+            }
+            std::string v = utils::join(vec, ", ");
+            add_data(id, current->label, v.c_str(), current->type);
+        }
+        else {
+            const char * v = "";
+            if(xmp_get_property(xmp->xmp(), current->ns,
+                                current->property, value, NULL)) {
+                v = xmp_string_cstr(value);
+            }
+            else {
+                DBG_OUT("get_property failed id = %s, ns = %s, prop = %s,"
+                        "label = %s",
+                        id.c_str(), current->ns, current->property,
+                        current->label);
+            }
+            add_data(id, current->label, v, current->type);
+        }
+        current++;
+    }
+}
+
+
+void MetaDataWidget::add_data(const std::string & id, 
+                              const std::string & label,
+                              const char * value,
+                              xmp::MetaDataType /*type*/)
+{
+    Gtk::Label *w = NULL;
+    int n_row;
+    std::map<std::string, Gtk::Widget *>::iterator iter 
+        = m_data_map.end();
+    if(m_data_map.empty()) {
+        n_row = 0;
+    }
+    else {
+        iter = m_data_map.find(id);
+        n_row = m_table.property_n_rows();
+    }
+    if(iter == m_data_map.end()) {
+        Gtk::Label *labelw = Gtk::manage(new Gtk::Label(
+                                             Glib::ustring("<b>") 
+                                             + label + "</b>"));
+        labelw->set_alignment(0, 0.5);
+        labelw->set_use_markup(true);
+
+        w = Gtk::manage(new Gtk::Label());
+        w->set_alignment(0, 0.5);
+
+        m_table.resize(n_row + 1, 2);
+        m_table.attach(*labelw, 0, 1, n_row, n_row+1, 
+                       Gtk::FILL, Gtk::SHRINK, 4, 0);
+        m_table.attach(*w, 1, 2, n_row, n_row+1, 
+                       Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK, 4, 0);
+        m_data_map.insert(std::make_pair(id, w));
+    }
+    else {
+        w = static_cast<Gtk::Label*>(iter->second);
+    }
+    w->set_text(value);
+    m_table.show_all();
+}
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/

Added: trunk/src/framework/metadatawidget.h
==============================================================================
--- (empty file)
+++ trunk/src/framework/metadatawidget.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,70 @@
+/*
+ * niepce - framework/metadatawidget.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __NIEPCE_FRAMEWORK_META_DATA_WIDGET_H__
+#define __NIEPCE_FRAMEWORK_META_DATA_WIDGET_H__
+
+
+#include <map>
+#include <string>
+
+#include <gtkmm/table.h>
+
+#include "framework/widgets/toolboxitemwidget.h"
+
+namespace utils {
+	class XmpMeta;
+}
+namespace xmp {
+	struct MetaDataSectionFormat;
+}
+
+namespace framework {
+
+
+class MetaDataWidget 
+	: public framework::ToolboxItemWidget
+{
+public:
+	MetaDataWidget(const Glib::ustring & title);
+	
+	void add_data(const std::string & id, const std::string & label,
+				  const char * value, xmp::MetaDataType type);
+	void set_data_format(const xmp::MetaDataSectionFormat * fmt);
+	void set_data_source(const utils::XmpMeta * xmp);
+protected:
+private:
+	Gtk::Table    m_table;
+	std::map<std::string, Gtk::Widget *> m_data_map;
+	const xmp::MetaDataSectionFormat * m_fmt;
+};
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/
+
+#endif

Added: trunk/src/framework/mimetype.cpp
==============================================================================
--- (empty file)
+++ trunk/src/framework/mimetype.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,75 @@
+/*
+ * niepce - framework/mimetype.cpp
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string>
+
+#include <libgnomevfs/gnome-vfs-mime-utils.h>
+
+#include "config.h"
+
+#include "mimetype.h"
+
+namespace bfs = boost::filesystem;
+
+namespace framework {
+
+	MimeType::MimeType(const char * filename)
+	{
+#if HAVE_GNOME_VFS_2_14
+		m_type = gnome_vfs_get_mime_type_for_name(filename);
+#else
+		std::string f("file:///");
+		f += filename;
+		m_type = gnome_vfs_get_mime_type(f.c_str());
+#endif
+	}
+
+	MimeType::MimeType(const boost::filesystem::path & filename)
+	{
+#if HAVE_GNOME_VFS_2_14
+		m_type = gnome_vfs_get_mime_type_for_name(filename.string().c_str());
+#else
+		std::string f("file:///");
+		f += filename.string();
+		m_type = gnome_vfs_get_mime_type(f.c_str());
+#endif
+	}
+
+	bool MimeType::isDigicamRaw() const
+	{
+		return (gnome_vfs_mime_type_get_equivalence(m_type.c_str(), 
+													"image/x-dcraw") 
+				!= GNOME_VFS_MIME_UNRELATED);
+	}
+
+
+	bool MimeType::isImage() const
+	{
+		return (gnome_vfs_mime_type_get_equivalence(m_type.c_str(), "image/*") 
+				!= GNOME_VFS_MIME_UNRELATED);
+	}
+	
+
+	bool MimeType::isUnknown() const
+	{
+		return (m_type == GNOME_VFS_MIME_TYPE_UNKNOWN);
+	}
+
+}
+

Added: trunk/src/framework/mimetype.h
==============================================================================
--- (empty file)
+++ trunk/src/framework/mimetype.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,48 @@
+/*
+ * niepce - framework/mimetype.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _FRAMEWORK_MIMETYPE_H_
+#define _FRAMEWORK_MIMETYPE_H_
+
+#include <string>
+#include <boost/filesystem/path.hpp>
+
+namespace framework {
+
+	class MimeType
+	{
+	public:
+		MimeType(const char *filename);
+		MimeType(const boost::filesystem::path & filename);
+
+		bool isDigicamRaw() const;
+		bool isImage() const;
+		bool isUnknown() const;
+
+		const std::string & string() const
+			{ return m_type; }
+	private:
+		std::string m_type;
+	};
+
+}
+
+
+#endif

Added: trunk/src/framework/notification.h
==============================================================================
--- (empty file)
+++ trunk/src/framework/notification.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,62 @@
+/*
+ * niepce - framework/notification.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+
+
+#ifndef __FRAMEWORK_NOTIFICATION_H__
+#define __FRAMEWORK_NOTIFICATION_H__
+
+#include <boost/shared_ptr.hpp>
+#include <boost/any.hpp>
+#include <boost/thread/recursive_mutex.hpp>
+
+namespace framework {
+
+	/** A notification to post to the notification center */
+	class Notification
+	{
+	public:
+		typedef boost::shared_ptr<Notification> Ptr;
+		typedef boost::recursive_mutex mutex_t;
+
+		Notification(int _type)
+			: m_type(_type)
+			{}
+		~Notification()
+			{ mutex_t::scoped_lock lock(m_mutex); }
+		mutex_t & mutex() const
+			{ return m_mutex; }
+		int type() const
+			{ return m_type; }
+		const boost::any & data() const
+			{ return m_data; }
+		void setData(const boost::any & d)
+			{ m_data = d; }
+	private:
+		mutable mutex_t    m_mutex;
+		int        m_type;
+		boost::any m_data;
+	};
+
+}
+
+
+#endif

Added: trunk/src/framework/notificationcenter.cpp
==============================================================================
--- (empty file)
+++ trunk/src/framework/notificationcenter.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,88 @@
+/*
+ * niepce - framework/notification.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <list>
+#include <map>
+#include <functional>
+
+#include <boost/bind.hpp>
+#include <boost/bind/apply.hpp>
+#include <boost/function_equal.hpp>
+
+#include <glibmm/dispatcher.h>
+
+#include "utils/mtqueue.h"
+#include "notificationcenter.h"
+
+namespace framework {
+
+	class NotificationCenter::Priv
+	{
+	public:
+		typedef std::list< subscriber_t > SubscriberList;
+		Glib::Dispatcher                     m_dispatcher;
+		sigc::connection                     m_dispatchConn;
+		utils::MtQueue< Notification::Ptr >  m_notificationQueue;
+		std::map< int, SubscriberList >      m_subscribers;
+	};
+
+
+	NotificationCenter::NotificationCenter()
+		: p( new Priv )
+	{
+		p->m_dispatchConn = p->m_dispatcher.connect(
+			sigc::mem_fun(this, &NotificationCenter::_dispatch));
+	}
+
+	NotificationCenter::~NotificationCenter()
+	{
+		p->m_dispatchConn.disconnect();
+		delete p;
+	}
+
+
+	void NotificationCenter::subscribe(int type, const subscriber_t & s)
+	{
+		
+		// TODO make sure it is not yet subscribed
+		p->m_subscribers[type].push_back(s);
+	}
+
+	void NotificationCenter::unsubscribe(int /*type*/, const subscriber_t & /*s*/)
+	{
+//		m_subscribers.remove_if(boost::bind(&boost::function_equal, _1, s));
+	}
+
+	void NotificationCenter::post(const Notification::Ptr & n)
+	{ 
+		p->m_notificationQueue.add(n);
+		p->m_dispatcher.emit();
+	}
+	
+	void NotificationCenter::_dispatch(void)
+	{
+		Notification::Ptr notif( p->m_notificationQueue.pop() );
+
+		Notification::mutex_t::scoped_lock lock(notif->mutex());
+		const Priv::SubscriberList & subscriber_list(p->m_subscribers[notif->type()]);
+		std::for_each(subscriber_list.begin(), subscriber_list.end(), 
+					  bind(boost::apply<void>(), _1, boost::ref(notif)));
+	}
+}
+

Added: trunk/src/framework/notificationcenter.h
==============================================================================
--- (empty file)
+++ trunk/src/framework/notificationcenter.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,59 @@
+/*
+ * niepce - framework/notification.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+
+#ifndef __FRAMEWORK_NOTIFICATIONCENTER_H__
+#define __FRAMEWORK_NOTIFICATIONCENTER_H__
+
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "framework/notification.h"
+
+namespace framework {
+
+	class NotificationCenter
+	{
+	public:
+		typedef boost::shared_ptr< NotificationCenter > Ptr;
+		typedef boost::function< void (Notification::Ptr) > subscriber_t;
+
+		NotificationCenter();
+		~NotificationCenter();
+
+		
+		// called from out of thread
+		void post(const Notification::Ptr & n);
+
+		void subscribe(int type, const subscriber_t & );
+		void unsubscribe(int type, const subscriber_t & );
+		
+	private:
+		void _dispatch(void);
+
+		class Priv;
+		Priv *p;
+	};
+
+
+}
+
+#endif

Added: trunk/src/framework/undo.cpp
==============================================================================
--- (empty file)
+++ trunk/src/framework/undo.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,162 @@
+/*
+ * niepce - framework/undo.cpp
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <boost/bind.hpp>
+#include <boost/checked_delete.hpp>
+
+#include "utils/debug.h"
+#include "command.h"
+#include "undo.h"
+
+namespace framework {
+
+UndoTransaction::UndoTransaction(const std::string & n)
+    : m_name(n)
+{
+}
+
+UndoTransaction::~UndoTransaction()
+{
+    std::for_each(m_operations.begin(), m_operations.end(),
+                  boost::bind(&boost::checked_delete<Command>, _1));
+}
+
+void UndoTransaction::add(Command * cmd)
+{
+    m_operations.push_back(cmd);
+}
+
+void UndoTransaction::undo()
+{
+    DBG_OUT("undo transaction %d cmd", m_operations.size());
+// I have no idea why this do not work
+//    std::for_each(m_operations.rbegin(), m_operations.rend(),
+//                  boost::bind(&Command::undo, _1));
+    for(std::list<Command *>::reverse_iterator iter = m_operations.rbegin();
+        iter != m_operations.rend(); iter++)
+    {
+        (*iter)->undo();
+    }
+}
+
+void UndoTransaction::redo()
+{
+    DBG_OUT("redo transaction %d cmd", m_operations.size());
+// I have no idea why this do not work
+//    std::for_each(m_operations.begin(), m_operations.end(),
+//                  boost::bind(&Command::redo, _1));
+    for(std::list<Command *>::iterator iter = m_operations.begin();
+        iter != m_operations.end(); iter++)
+    {
+        (*iter)->redo();
+    }
+}
+
+UndoHistory::~UndoHistory()
+{
+    clear();
+}
+
+void UndoHistory::add(UndoTransaction* t)
+{
+    m_undos.push_front(t);
+    clear(m_redos);
+
+    changed();
+}
+
+void UndoHistory::undo()
+{
+    DBG_OUT("run undo history");
+    if(!m_undos.empty()) {
+        UndoTransaction * t = m_undos.front();
+        if(t) {
+            t->undo();
+            m_undos.pop_front();
+            m_redos.push_front(t);
+            changed();
+        }
+    }
+}
+
+void UndoHistory::redo()
+{
+    DBG_OUT("run redo history");
+    if(!m_redos.empty()) {
+        UndoTransaction * t = m_redos.front();
+        if(t) {
+            t->redo();
+            m_redos.pop_front();
+            m_undos.push_front(t);
+            changed();
+        }
+    }
+}
+
+
+void UndoHistory::clear()
+{
+    clear(m_undos);
+    clear(m_redos);
+
+    changed();
+}
+
+std::string UndoHistory::next_undo() const
+{
+    if(!m_undos.empty()) {
+        UndoTransaction * t = m_undos.front();
+        if(t) {
+            return t->name();
+        }
+    }
+    return "";
+}
+
+std::string UndoHistory::next_redo() const
+{
+    if(!m_redos.empty()) {
+        UndoTransaction * t = m_redos.front();
+        if(t) {
+            return t->name();
+        }
+    }
+    return "";
+}
+
+	
+void UndoHistory::clear(std::list<UndoTransaction*> & l)
+{
+    std::for_each(l.begin(), l.end(), 
+                  boost::bind(&boost::checked_delete<UndoTransaction>, _1));
+    l.clear();
+}
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/framework/undo.h
==============================================================================
--- (empty file)
+++ trunk/src/framework/undo.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,89 @@
+/*
+ * niepce - framework/undo.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _FRAMEWORK_UNDO_H_
+#define _FRAMEWORK_UNDO_H_
+
+#include <list>
+#include <stack>
+#include <string>
+#include <boost/signal.hpp>
+#include <boost/noncopyable.hpp>
+
+namespace framework {
+
+class Command;
+
+class UndoTransaction
+{
+public:
+    UndoTransaction(const std::string & n);
+    ~UndoTransaction();
+    void add(Command *);
+    void undo();
+    void redo();
+    const std::string & name() const
+        { return m_name; }
+private:
+    std::list<Command *> m_operations;
+    std::string m_name;
+};
+
+class UndoHistory
+    : public boost::noncopyable
+{
+public:
+    ~UndoHistory();
+		
+    /** the history becomes owner */
+    void add(UndoTransaction*);
+    void undo();
+    void redo();
+    void clear();
+    bool has_undo() const
+        { return !m_undos.empty(); }
+    bool has_redo() const
+        { return !m_redos.empty(); }
+    std::string next_undo() const;
+    std::string next_redo() const;
+
+    // called when the undo history change.
+    boost::signal<void (void)> changed;
+private:
+    void clear(std::list<UndoTransaction*> & l);
+
+    std::list<UndoTransaction*> m_undos;
+    std::list<UndoTransaction*> m_redos;
+};
+
+	
+
+}
+
+#endif
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/framework/widgets/dock-item.cpp
==============================================================================
--- (empty file)
+++ trunk/src/framework/widgets/dock-item.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,518 @@
+/**
+ * \brief A custom Inkscape wrapper around gdl_dock_item
+ *
+ * Author:
+ *   Gustav Broberg <broberg kth se>
+ *
+ * Copyright (C) 2007 Authors
+ *
+ * Released under GNU GPL.  Read the file 'COPYING' for more information.
+ */
+
+#include <gtk/gtk.h>
+
+#include <gtkmm/invisible.h>
+#include <gtkmm/stock.h>
+#include <gtkmm/icontheme.h>
+
+#include "dock-item.h"
+//#include "desktop.h"
+//#include "inkscape.h"
+//#include "prefs-utils.h"
+#include "framework/widgets/dock.h"
+#include "framework/application.h"
+//#include "widgets/icon.h"
+
+
+namespace framework {
+
+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),
+    _prev_position(0),
+    _window (NULL),
+    _x(0),
+    _y(0),
+    _grab_focus_on_realize(false),
+    _gdl_dock_item(0),
+    _dock_item_action_area (NULL)
+{
+
+    GdlDockItemBehavior gdl_dock_behavior = GDL_DOCK_ITEM_BEH_CANT_DOCK_CENTER;
+// HUB
+//        (prefs_get_int_attribute_limited ("options.dock", "cancenterdock", 1, 0, 1) == 0 ?
+//         GDL_DOCK_ITEM_BEH_CANT_DOCK_CENTER
+//         : GDL_DOCK_ITEM_BEH_NORMAL);
+
+    if (!icon_name.empty()) {
+        Glib::RefPtr<Gtk::IconTheme> icon_theme(Application::app()->getIconTheme());
+        Glib::RefPtr<Gdk::Pixbuf> icon(icon_theme->load_icon(icon_name, 16, Gtk::ICON_LOOKUP_USE_BUILTIN));
+
+        if (icon) {
+            // icon needs to have a ref when 
+            // passed to gdl_dock_item_new_with_pixbuf_icon()
+            _gdl_dock_item =
+                gdl_dock_item_new_with_pixbuf_icon(name.c_str(), 
+                                                   long_name.c_str(),
+                                                   GDK_PIXBUF(g_object_ref(icon->gobj())), 
+                                                   gdl_dock_behavior);
+        }
+    } else {
+        _gdl_dock_item =
+            gdl_dock_item_new(name.c_str(), long_name.c_str(), gdl_dock_behavior);
+    }
+
+    _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, &framework::DockItem::_onDragBegin));
+    signal_drag_end().connect(sigc::mem_fun(*this, &framework::DockItem::_onDragEnd));
+    signal_hide().connect(sigc::mem_fun(*this, &framework::DockItem::_onHide), false);
+    signal_show().connect(sigc::mem_fun(*this, &framework::DockItem::_onShow), false);
+    signal_state_changed().connect(sigc::mem_fun(*this, &framework::DockItem::_onStateChanged));
+    signal_delete_event().connect(sigc::mem_fun(*this, &framework::DockItem::_onDeleteEvent));
+    signal_realize().connect(sigc::mem_fun(*this, &framework::DockItem::_onRealize));
+
+    _dock.addItem(*this, (_prev_state == FLOATING_STATE ? FLOATING : TOP));
+
+    show_all();
+}
+
+DockItem::~DockItem()
+{
+}
+
+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;
+}
+
+
+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()));
+}
+
+bool
+DockItem::isAttached() const
+{
+    return GDL_DOCK_OBJECT_ATTACHED (_gdl_dock_item);
+}
+
+
+bool
+DockItem::isFloating() const
+{
+    return (GTK_WIDGET(gdl_dock_object_get_toplevel(GDL_DOCK_OBJECT (_gdl_dock_item))) !=
+            _dock.getGdlWidget());
+}
+
+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::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()
+{
+
+    if (isIconified() || !isAttached()) {
+        show();
+    }
+
+    // tabbed
+    else 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);
+    }
+
+    // always grab focus, even if we're already present
+    grab_focus();
+
+    if (!isFloating() && getWidget().is_realized())
+        _dock.scrollToItem(*this);
+}
+
+
+void
+DockItem::grab_focus()
+{
+    if (GTK_WIDGET_REALIZED (_gdl_dock_item)) {
+
+        // make sure the window we're in is present
+        Gtk::Widget *toplevel = getWidget().get_toplevel();
+        if (Gtk::Window *window = dynamic_cast<Gtk::Window *>(toplevel)) {
+            window->present();
+        }
+
+        gtk_widget_grab_focus (_gdl_dock_item);
+
+    } else {
+        _grab_focus_on_realize = true;
+    }
+}
+
+
+/* Signal wrappers */
+
+Glib::SignalProxy0<void>
+DockItem::signal_show()
+{
+    return Glib::SignalProxy0<void>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)),
+                                    &_signal_show_proxy);
+}
+
+Glib::SignalProxy0<void>
+DockItem::signal_hide()
+{
+    return Glib::SignalProxy0<void>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)),
+                                    &_signal_hide_proxy);
+}
+
+Glib::SignalProxy1<bool, GdkEventAny *>
+DockItem::signal_delete_event()
+{
+    return Glib::SignalProxy1<bool, GdkEventAny *>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)),
+                                                  &_signal_delete_event_proxy);
+}
+
+Glib::SignalProxy0<void>
+DockItem::signal_drag_begin()
+{
+    return Glib::SignalProxy0<void>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)),
+                                    &_signal_drag_begin_proxy);
+}
+
+Glib::SignalProxy1<void, bool>
+DockItem::signal_drag_end()
+{
+    return Glib::SignalProxy1<void, bool>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)),
+                                          &_signal_drag_end_proxy);
+}
+
+Glib::SignalProxy0<void>
+DockItem::signal_realize()
+{
+    return Glib::SignalProxy0<void>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)),
+                                    &_signal_realize_proxy);
+}
+
+sigc::signal<void, DockItem::State, DockItem::State>
+DockItem::signal_state_changed()
+{
+    return _signal_state_changed;
+}
+
+void
+DockItem::_onHideWindow()
+{
+    if (_window)
+        _window->get_position(_x, _y);
+}
+
+void
+DockItem::_onHide()
+{
+    _signal_state_changed.emit(UNATTACHED, getState());
+}
+
+void
+DockItem::_onShow()
+{
+    _signal_state_changed.emit(UNATTACHED, getState());
+}
+
+void
+DockItem::_onDragBegin()
+{
+    _prev_state = getState();
+    if (_prev_state == FLOATING_STATE)
+        _dock.toggleDockable(getWidget().get_width(), getWidget().get_height());
+}
+
+void
+DockItem::_onDragEnd(bool)
+{
+    State state = getState();
+
+    if (state != _prev_state)
+        _signal_state_changed.emit(_prev_state, state);
+
+    if (state == FLOATING_STATE) {
+        if (_prev_state == FLOATING_STATE)
+            _dock.toggleDockable();
+    }
+
+    _prev_state = state;
+}
+
+void
+DockItem::_onRealize()
+{
+    if (_grab_focus_on_realize) {
+        _grab_focus_on_realize = false;
+        grab_focus();
+    }
+}
+
+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) {
+        _window->signal_hide().connect(sigc::mem_fun(*this, &framework::DockItem::_onHideWindow));
+        _signal_key_press_event_connection =
+            _window->signal_key_press_event().connect(sigc::mem_fun(*this, &framework::DockItem::_onKeyPress));
+    }
+}
+
+
+bool
+DockItem::_onDeleteEvent(GdkEventAny */*event*/)
+{
+    hide();
+    return false;
+}
+
+
+Gtk::Window *
+DockItem::getWindow()
+{
+    g_return_val_if_fail(_gdl_dock_item, 0);
+    Gtk::Container *parent = getWidget().get_parent();
+    parent = (parent ? parent->get_parent() : 0);
+    return (parent ? dynamic_cast<Gtk::Window *>(parent) : 0);
+}
+
+const Glib::SignalProxyInfo
+DockItem::_signal_show_proxy =
+{
+    "show",
+    (GCallback) &Glib::SignalProxyNormal::slot0_void_callback,
+    (GCallback) &Glib::SignalProxyNormal::slot0_void_callback
+};
+
+const Glib::SignalProxyInfo
+DockItem::_signal_hide_proxy =
+{
+    "hide",
+    (GCallback) &Glib::SignalProxyNormal::slot0_void_callback,
+    (GCallback) &Glib::SignalProxyNormal::slot0_void_callback
+};
+
+
+const Glib::SignalProxyInfo
+DockItem::_signal_delete_event_proxy =
+{
+    "delete_event",
+    (GCallback) &_signal_delete_event_callback,
+    (GCallback) &_signal_delete_event_callback
+};
+
+
+const Glib::SignalProxyInfo
+DockItem::_signal_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
+};
+
+
+const Glib::SignalProxyInfo
+DockItem::_signal_realize_proxy =
+{
+    "realize",
+    (GCallback) &Glib::SignalProxyNormal::slot0_void_callback,
+    (GCallback) &Glib::SignalProxyNormal::slot0_void_callback
+};
+
+
+gboolean
+DockItem::_signal_delete_event_callback(GtkWidget *self, GdkEventAny *event, void *data)
+{
+    using namespace Gtk;
+    typedef sigc::slot<bool, GdkEventAny *> SlotType;
+
+    if (Glib::ObjectBase::_get_current_wrapper((GObject *) self)) {
+        try {
+            if(sigc::slot_base *const slot = Glib::SignalProxyNormal::data_to_slot(data))
+                return static_cast<int>( (*static_cast<SlotType*>(slot))(event) );
+        } catch(...) {
+            Glib::exception_handlers_invoke();
+        }
+    }
+
+    typedef gboolean RType;
+    return RType();
+}
+
+void
+DockItem::_signal_drag_end_callback(GtkWidget *self, gboolean cancelled, void *data)
+{
+    using namespace Gtk;
+    typedef sigc::slot<void, bool> SlotType;
+
+    if (Glib::ObjectBase::_get_current_wrapper((GObject *) self)) {
+        try {
+            if(sigc::slot_base *const slot = Glib::SignalProxyNormal::data_to_slot(data))
+                (*static_cast<SlotType *>(slot))(cancelled);
+        } catch(...) {
+            Glib::exception_handlers_invoke();
+        }
+    }
+}
+
+
+} // namespace framework
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80 :

Added: trunk/src/framework/widgets/dock-item.h
==============================================================================
--- (empty file)
+++ trunk/src/framework/widgets/dock-item.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,158 @@
+/**
+ * \brief A custom wrapper around gdl-dock-item
+ *
+ * Author:
+ *   Gustav Broberg <broberg kth se>
+ *
+ * Copyright (C) 2007 Authors
+ *
+ * Released under GNU GPL.  Read the file 'COPYING' for more information.
+ */
+
+
+#ifndef INKSCAPE_UI_WIGET_DOCK_ITEM_H
+#define INKSCAPE_UI_WIGET_DOCK_ITEM_H
+
+#include <gtkmm/button.h>
+#include <gtkmm/buttonbox.h>
+#include <gtkmm/frame.h>
+#include <gtkmm/paned.h>
+#include <gtkmm/window.h>
+
+#include "libgdl/libgdl.h"
+
+namespace framework {
+
+class Dock;
+
+class DockItem {
+
+public:
+
+    enum State { UNATTACHED,     // item not bound to the dock (a temporary state)
+                 FLOATING_STATE, // item not in its dock (but can be docked in other,
+                                 // e.g. floating, docks)
+                 DOCKED_STATE }; // item in its assigned dock
+
+    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);
+
+    virtual ~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;
+
+    Gtk::Window *getWindow();   //< gives the parent window, if the dock item has one (i.e. it's floating)
+
+    void hide();
+    void show();
+    void show_all();
+
+    void present();
+
+    void grab_focus();
+
+    Glib::SignalProxy0<void> signal_show();
+    Glib::SignalProxy0<void> signal_hide();
+    Glib::SignalProxy1<bool, GdkEventAny *> signal_delete_event();
+    Glib::SignalProxy0<void> signal_drag_begin();
+    Glib::SignalProxy1<void, bool> signal_drag_end();
+    Glib::SignalProxy0<void> signal_realize();
+
+    sigc::signal<void, State, State> signal_state_changed();
+
+private:
+    Dock &_dock;              //< parent dock
+
+    State _prev_state;        //< last known state
+
+    int _prev_position;
+
+    Gtk::Window *_window;     //< reference to floating window, if any 
+    int _x, _y;               //< last known position of window, if floating
+
+    bool _grab_focus_on_realize;   //< if the dock item should grab focus on the next realize
+
+    GtkWidget *_gdl_dock_item;
+
+    /** 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 _onDragBegin();
+    void _onDragEnd(bool cancelled);
+    void _onRealize();
+
+    bool _onKeyPress(GdkEventKey *event);
+    void _onStateChanged(State prev_state, State new_state);
+    bool _onDeleteEvent(GdkEventAny *event);
+
+    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_drag_begin_proxy;
+    static const Glib::SignalProxyInfo _signal_drag_end_proxy;
+    static const Glib::SignalProxyInfo _signal_realize_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);
+
+    sigc::signal<void, State, State> _signal_state_changed;
+
+    DockItem();
+};
+
+} // namespace framework
+
+#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:80
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80 :

Added: trunk/src/framework/widgets/dock.cpp
==============================================================================
--- (empty file)
+++ trunk/src/framework/widgets/dock.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,296 @@
+/**
+ * \brief A desktop dock pane to dock dialogs.
+ *
+ * Author:
+ *   Gustav Broberg <broberg kth se>
+ *
+ * Copyright (C) 2007 Authors
+ *
+ * Released under GNU GPL.  Read the file 'COPYING' for more information.
+ */
+
+//#include "inkscape.h"
+//#include "prefs-utils.h"
+//#include "desktop.h"
+
+#include "dock.h"
+
+#include <gtkmm/adjustment.h>
+
+namespace framework {
+
+namespace {
+
+void hideCallback(GtkObject */*object*/, gpointer dock_ptr)
+{
+    g_return_if_fail( dock_ptr != NULL );
+
+    Dock *dock = (Dock *)dock_ptr;
+    dock->hide();
+}
+
+void unhideCallback(GtkObject */*object*/, gpointer dock_ptr)
+{
+    g_return_if_fail( dock_ptr != NULL );
+
+    Dock *dock = (Dock *)dock_ptr;
+    dock->show();
+}
+
+}
+
+const int Dock::_default_empty_width = 0;
+const int Dock::_default_dock_bar_width = 36;
+
+
+Dock::Dock(Gtk::Orientation orientation)
+    : _gdl_dock (GDL_DOCK (gdl_dock_new())),
+      _gdl_dock_bar (GDL_DOCK_BAR (gdl_dock_bar_new(GDL_DOCK(_gdl_dock)))),
+      _scrolled_window (Gtk::manage(new Gtk::ScrolledWindow))
+{
+    gdl_dock_bar_set_orientation(_gdl_dock_bar, static_cast<GtkOrientation>(orientation));
+
+    switch (orientation) {
+        case Gtk::ORIENTATION_VERTICAL:
+            _dock_box = Gtk::manage(new Gtk::HBox());
+            _paned = Gtk::manage(new Gtk::VPaned());
+            break;
+        case Gtk::ORIENTATION_HORIZONTAL:
+            _dock_box = Gtk::manage(new Gtk::VBox());
+            _paned = Gtk::manage(new Gtk::HPaned());
+    }
+
+    _scrolled_window->add(*_dock_box);
+    _scrolled_window->set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
+
+    _paned->pack1(*Glib::wrap(GTK_WIDGET(_gdl_dock)), false, false);
+    _paned->pack2(_filler, true, false);
+
+    _dock_box->pack_start(*_paned, Gtk::PACK_EXPAND_WIDGET);
+    _dock_box->pack_end(*Gtk::manage(Glib::wrap(GTK_WIDGET(_gdl_dock_bar))), Gtk::PACK_SHRINK);
+    _dock_box->get_parent()->set_resize_mode(Gtk::RESIZE_PARENT);
+
+    _scrolled_window->set_size_request(0);
+
+    GdlSwitcherStyle gdl_switcher_style = GDL_SWITCHER_STYLE_BOTH;
+// HUB
+//        static_cast<GdlSwitcherStyle>(prefs_get_int_attribute_limited("options.dock", "switcherstyle",
+//                                                                      GDL_SWITCHER_STYLE_BOTH, 0, 4));
+
+// HUB
+    g_object_set (GDL_DOCK_OBJECT(_gdl_dock)->master,
+                  "switcher-style", gdl_switcher_style,
+                  "expand-direction", GDL_DOCK_EXPANSION_DIRECTION_DOWN,
+                  NULL);
+
+    GdlDockBarStyle gdl_dock_bar_style = GDL_DOCK_BAR_BOTH;
+// HUB
+//        static_cast<GdlDockBarStyle>(prefs_get_int_attribute_limited("options.dock", "dockbarstyle",
+//                                                                     GDL_DOCK_BAR_BOTH, 0, 3));
+
+    gdl_dock_bar_set_style(_gdl_dock_bar, gdl_dock_bar_style);
+// HUB
+//    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(_paned->gobj(), "button-press-event", G_CALLBACK(_on_paned_button_event), (void *)this);
+    g_signal_connect(_paned->gobj(), "button-release-event", G_CALLBACK(_on_paned_button_event), (void *)this);
+
+    signal_layout_changed().connect(sigc::mem_fun(*this, &framework::Dock::_onLayoutChanged));
+}
+
+Dock::~Dock()
+{
+//WTF is that?
+//    g_free(_gdl_dock);
+//    g_free(_gdl_dock_bar);
+}
+
+void
+Dock::addItem(DockItem& item, DockItem::Placement placement)
+{
+    _dock_items.push_back(&item);
+    gdl_dock_add_item(_gdl_dock, GDL_DOCK_ITEM(item.gobj()), (GdlDockPlacement)placement);
+
+    // FIXME: This is a hack to prevent the dock from expanding the main window, this can't be done
+    // initially as the paned doesn't exist.
+    if (Gtk::Paned *paned = getParentPaned()) {
+        paned->set_resize_mode(Gtk::RESIZE_QUEUE);
+    }
+}
+
+Gtk::Widget&
+Dock::getWidget()
+{
+     return *_scrolled_window;
+}
+
+Gtk::Paned *
+Dock::getParentPaned()
+{
+    g_return_val_if_fail(_dock_box, 0);
+    Gtk::Container *parent = getWidget().get_parent();
+    return (parent != 0 ? dynamic_cast<Gtk::Paned *>(parent) : 0);
+}
+
+
+Gtk::Paned *
+Dock::getPaned()
+{
+    return _paned;
+}
+
+GtkWidget *
+Dock::getGdlWidget()
+{
+    return GTK_WIDGET(_gdl_dock);
+}
+
+bool
+Dock::isEmpty() const
+{
+    std::list<const DockItem *>::const_iterator
+        i = _dock_items.begin(),
+        e = _dock_items.end();
+
+    for (; i != e; ++i) {
+        if ((*i)->getState() == DockItem::DOCKED_STATE) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool
+Dock::hasIconifiedItems() const
+{
+    std::list<const DockItem *>::const_iterator
+        i = _dock_items.begin(),
+        e = _dock_items.end();
+
+    for (; i != e; ++i) {
+        if ((*i)->isIconified()) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+void
+Dock::hide()
+{
+    getWidget().hide();
+}
+
+void
+Dock::show()
+{
+    getWidget().show();
+}
+
+void
+Dock::toggleDockable(int width, int height)
+{
+    static int prev_horizontal_position, prev_vertical_position;
+
+    Gtk::Paned *parent_paned = getParentPaned();
+
+    if (width > 0 && height > 0) {
+        prev_horizontal_position = parent_paned->get_position();
+        prev_vertical_position = _paned->get_position();
+
+        if (getWidget().get_width() < width)
+            parent_paned->set_position(parent_paned->get_width() - width);
+
+        if (_paned->get_position() < height)
+            _paned->set_position(height);
+
+    } else {
+        parent_paned->set_position(prev_horizontal_position);
+        _paned->set_position(prev_vertical_position);
+    }
+
+}
+
+void
+Dock::scrollToItem(DockItem& item)
+{
+    int item_x, item_y;
+    item.getWidget().translate_coordinates(getWidget(), 0, 0, item_x, item_y);
+
+    int dock_height = getWidget().get_height(), item_height = item.getWidget().get_height();
+    double vadjustment = _scrolled_window->get_vadjustment()->get_value();
+
+    if (item_y < 0)
+        _scrolled_window->get_vadjustment()->set_value(vadjustment + item_y);
+    else if (item_y + item_height > dock_height)
+        _scrolled_window->get_vadjustment()->set_value(
+            vadjustment + ((item_y + item_height) - dock_height));
+}
+
+Glib::SignalProxy0<void>
+Dock::signal_layout_changed()
+{
+    return Glib::SignalProxy0<void>(Glib::wrap(GTK_WIDGET(_gdl_dock)),
+                                    &_signal_layout_changed_proxy);
+}
+
+void
+Dock::_onLayoutChanged()
+{
+    if (isEmpty()) {
+        if (hasIconifiedItems()) {
+            _scrolled_window->set_size_request(_default_dock_bar_width);
+        } else {
+            _scrolled_window->set_size_request(_default_empty_width);
+        }
+
+        getParentPaned()->set_position(INT_MAX);
+    } else {
+        // unset any forced size requests
+        _paned->get_child1()->set_size_request(-1, -1);
+        _scrolled_window->set_size_request(-1);
+    }
+}
+
+void
+Dock::_onPanedButtonEvent(GdkEventButton *event)
+{
+    if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
+        /* unset size request when starting a drag */
+        _paned->get_child1()->set_size_request(-1, -1);
+}
+
+gboolean
+Dock::_on_paned_button_event(GtkWidget */*widget*/, GdkEventButton *event, gpointer user_data)
+{
+    if (Dock *dock = static_cast<Dock *>(user_data))
+        dock->_onPanedButtonEvent(event);
+
+    return FALSE;
+}
+
+const Glib::SignalProxyInfo
+Dock::_signal_layout_changed_proxy =
+{
+    "layout-changed",
+    (GCallback) &Glib::SignalProxyNormal::slot0_void_callback,
+    (GCallback) &Glib::SignalProxyNormal::slot0_void_callback
+};
+
+
+} // namespace framework
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

Added: trunk/src/framework/widgets/dock.h
==============================================================================
--- (empty file)
+++ trunk/src/framework/widgets/dock.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,99 @@
+/**
+ * \brief A desktop dock pane to dock dialogs, a custom wrapper around gdl-dock.
+ *
+ * Author:
+ *   Gustav Broberg <broberg kth se>
+ *
+ * Copyright (C) 2007 Authors
+ *
+ * Released under GNU GPL.  Read the file 'COPYING' for more information.
+ */
+
+#ifndef INKSCAPE_UI_WIDGET_DOCK_H
+#define INKSCAPE_UI_WIDGET_DOCK_H
+
+#include <gtkmm/scrolledwindow.h>
+#include <gtkmm/box.h>
+#include <gtkmm/paned.h>
+
+#include <list>
+
+#include "framework/widgets/dock-item.h"
+
+#include "libgdl/libgdl.h"
+
+namespace framework {
+
+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();
+
+    GtkWidget* getGdlWidget();    //< return the top gdl widget
+
+    bool isEmpty() const;         //< true iff none of the dock's items are in a docked state
+    bool hasIconifiedItems() const;
+
+    Glib::SignalProxy0<void> signal_layout_changed();
+
+    void hide();
+    void show();
+
+    /** Toggle size of dock between the previous dimensions and the ones sent as parameters */
+    void toggleDockable(int width=0, int height=0);
+
+    /** Scrolls the scrolled window container to make the provided dock item visible, if needed */
+    void scrollToItem(DockItem& item);
+
+protected:
+
+    std::list<const DockItem *> _dock_items;   //< added dock items
+
+    /** Interface widgets, will be packed like 
+     * _scrolled_window -> (_dock_box -> (_paned -> (_dock -> _filler) | _dock_bar))
+     */
+    Gtk::Box *_dock_box;
+    Gtk::Paned* _paned;
+    GdlDock *_gdl_dock;
+    GdlDockBar *_gdl_dock_bar;
+    Gtk::VBox _filler;
+    Gtk::ScrolledWindow *_scrolled_window;
+
+    /** Internal signal handlers */
+    void _onLayoutChanged();
+    void _onPanedButtonEvent(GdkEventButton *event);
+
+    static gboolean _on_paned_button_event(GtkWidget *widget, GdkEventButton *event, 
+                                           gpointer user_data);
+
+    /** 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 framework
+
+#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:80
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 
+

Added: trunk/src/framework/widgets/editablehscale.cpp
==============================================================================
--- (empty file)
+++ trunk/src/framework/widgets/editablehscale.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,102 @@
+/*
+ * niepce - framework/widgets/editablehscale.cpp
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <boost/lexical_cast.hpp>
+
+#include <glibmm/property.h>
+
+#include "utils/debug.h"
+#include "editablehscale.h"
+
+
+namespace framework {
+
+EditableHScale::EditableHScale(double min, double max, double step)
+    : m_adj(0, min, max, step),
+      m_scale(m_adj), m_entry(m_adj),
+      m_dirty(false)
+{
+    m_scale.property_draw_value() = false;
+    m_scale.add_events(Gdk::BUTTON_RELEASE_MASK);
+    m_scale.signal_button_release_event()
+        .connect(sigc::mem_fun(*this, &EditableHScale::on_button_press_event));
+    pack_start(m_scale, Gtk::PACK_EXPAND_WIDGET);
+    m_entry.set_width_chars(4);
+    m_entry.set_digits(2);
+    m_entry.set_editable(true);
+    m_entry.add_events(Gdk::BUTTON_RELEASE_MASK);
+    m_entry.signal_button_release_event()
+        .connect(sigc::mem_fun(*this, &EditableHScale::on_button_press_event));
+    pack_start(m_entry, Gtk::PACK_SHRINK);
+
+    m_adj.signal_value_changed()
+        .connect(sigc::mem_fun(*this, &EditableHScale::on_adj_value_changed));
+    add_events(Gdk::BUTTON_RELEASE_MASK);
+}
+
+
+bool EditableHScale::on_button_press_event(GdkEventButton *_event)
+{
+    DBG_OUT("button %d released", _event->button);
+    if (_event->type == GDK_BUTTON_RELEASE && _event->button != 1) {
+        return false;
+    } 
+    else {
+        Gtk::Widget::on_button_release_event(_event);
+        if(m_dirty) {
+            m_dirty = false;
+            DBG_OUT("value_change.emit(%f)", m_adj.get_value());
+            m_sig_value_changed.emit(m_adj.get_value());
+        }
+        return false;
+    }
+}
+
+
+void EditableHScale::on_adj_value_changed()
+{
+    m_dirty = true;
+    m_sig_value_changing.emit(m_adj.get_value());
+}
+
+
+sigc::signal<void,double> & EditableHScale::signal_value_changed()
+{
+    return m_sig_value_changed;
+}
+
+
+sigc::signal<void,double> & EditableHScale::signal_value_changing()
+{
+    return m_sig_value_changing;
+}
+
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/
+

Added: trunk/src/framework/widgets/editablehscale.h
==============================================================================
--- (empty file)
+++ trunk/src/framework/widgets/editablehscale.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,75 @@
+/*
+ * niepce - framework/widgets/editablehscale.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _FRAMEWORK_EDITABLEHSCALE_H_
+#define _FRAMEWORK_EDITABLEHSCALE_H_
+
+
+#include <gtkmm/box.h>
+#include <gtkmm/spinbutton.h>
+#include <gtkmm/scale.h>
+
+
+namespace framework {
+
+/** A widget similar to a Gtk::HScale with a edit box */
+class EditableHScale
+    : public Gtk::HBox
+{
+public:
+    EditableHScale(double min, double max, double step);
+
+    Gtk::Adjustment  &       get_adjustment()
+        { return m_adj; }
+    const Gtk::Adjustment  & get_adjustment() const
+        { return m_adj; }
+
+    sigc::signal<void,double> & signal_value_changed();
+    sigc::signal<void,double> & signal_value_changing();
+
+    bool on_button_press_event(GdkEventButton *event);
+
+private:
+
+    void on_adj_value_changed();
+
+    Gtk::Adjustment  m_adj;
+    Gtk::HScale      m_scale;
+    Gtk::SpinButton  m_entry;
+    bool             m_dirty;
+    /** emitted once the value changed */
+    sigc::signal<void,double> m_sig_value_changed;
+    /** emitted when the value is changing (think live update) */
+    sigc::signal<void,double> m_sig_value_changing;
+};
+
+
+}
+
+#endif
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/

Added: trunk/src/framework/widgets/toolboxitemwidget.cpp
==============================================================================
--- (empty file)
+++ trunk/src/framework/widgets/toolboxitemwidget.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,44 @@
+/*
+ * niepce - framework/toolboxitemwidget.cpp
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "toolboxitemwidget.h"
+
+namespace framework {
+
+ToolboxItemWidget::ToolboxItemWidget(const Glib::ustring & title)
+    : Gtk::Expander(Glib::ustring("<b>") + title + "</b>")
+{
+    set_expanded(true);
+    set_use_markup(true);
+}
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/
+
+

Added: trunk/src/framework/widgets/toolboxitemwidget.h
==============================================================================
--- (empty file)
+++ trunk/src/framework/widgets/toolboxitemwidget.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,48 @@
+/*
+ * niepce - framework/toolboxitemwidget.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+#ifndef __FRAMEWORK_TOOLBOXITEMWIDGET_H_
+#define __FRAMEWORK_TOOLBOXITEMWIDGET_H_
+
+#include <gtkmm/expander.h>
+
+namespace framework {
+
+class ToolboxItemWidget
+	: public Gtk::Expander
+{
+public:
+    ToolboxItemWidget(const Glib::ustring & title);
+};
+
+}
+
+#endif
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/
+

Added: trunk/src/library/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/library/Makefile.am	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,27 @@
+
+
+INCLUDES = -I$(top_srcdir)/src/ \
+	@LIBGTKMM_CFLAGS@ @OPENRAW_CFLAGS@ @EXEMPI_CFLAGS@
+
+check_PROGRAMS = test_opqueue
+TESTS = test_opqueue
+TEST_LIBS =  \
+        libniepcelibrary.a \
+	../db/libniepcedb.a \
+        ../utils/libniepceutils.a \
+	../framework/libniepceframework.a \
+        @BOOST_UNIT_TEST_FRAMEWORK_LIBS@ \
+	@BOOST_THREAD_LIBS@ @BOOST_FILESYSTEM_LIBS@ \
+	@LIBGTKMM_LIBS@ @SQLITE3_LIBS@ @OPENRAW_LIBS@
+
+noinst_LIBRARIES = libniepcelibrary.a
+
+test_opqueue_SOURCES = test_opqueue.cpp
+test_opqueue_LDADD = $(TEST_LIBS)
+
+
+libniepcelibrary_a_SOURCES = clienttypes.h \
+	op.h op.cpp  \
+	commands.h commands.cpp \
+	thumbnailcache.h thumbnailcache.cpp \
+	thumbnailnotification.h

Added: trunk/src/library/clienttypes.h
==============================================================================
--- (empty file)
+++ trunk/src/library/clienttypes.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,30 @@
+/*
+ * niepce - library/clienttypes.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _LIBRARY_CLIENTTYPES_H_
+#define _LIBRARY_CLIENTTYPES_H_
+
+namespace library {
+
+	typedef int tid_t; /**< transaction ID */
+
+}
+
+
+#endif

Added: trunk/src/library/commands.cpp
==============================================================================
--- (empty file)
+++ trunk/src/library/commands.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,147 @@
+/*
+ * niepce - library/commands.cpp
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <boost/any.hpp>
+#include <boost/bind.hpp>
+#include <boost/array.hpp>
+#include <boost/filesystem/path.hpp>
+
+
+#include "utils/debug.h"
+#include "db/library.h"
+#include "db/libfolder.h"
+#include "db/libfile.h"
+#include "db/libmetadata.h"
+#include "db/keyword.h"
+#include "commands.h"
+
+namespace bfs = boost::filesystem;
+
+using boost::any_cast;
+
+using db::Library;
+using db::LibFolder;
+using db::LibFile;
+using db::LibMetadata;
+using db::Keyword;
+using utils::FileList;
+
+namespace library {
+
+void Commands::cmdListAllKeywords(const Library::Ptr & lib)
+{
+    Keyword::ListPtr l( new Keyword::List );
+    lib->getAllKeywords( l );
+    /////
+    // notify folder added l
+    lib->notify(Library::NOTIFY_ADDED_KEYWORDS, boost::any(l));
+}
+
+void Commands::cmdListAllFolders(const Library::Ptr & lib)
+{
+    LibFolder::ListPtr l( new LibFolder::List );
+    lib->getAllFolders( l );
+    /////
+    // notify folder added l
+    lib->notify(Library::NOTIFY_ADDED_FOLDERS, boost::any(l));
+}
+	
+void Commands::cmdImportFiles(const Library::Ptr & lib, 
+                              const bfs::path & folder, 
+                              const FileList::Ptr & files, bool manage)
+{
+    DBG_ASSERT(!manage, "managing file is currently unsupported");
+    LibFolder::Ptr pf;
+    pf = lib->getFolder(folder);
+    if(pf == NULL)
+    {
+        pf = lib->addFolder(folder);
+        LibFolder::ListPtr l( new LibFolder::List );
+        l->push_back(pf);
+        lib->notify(Library::NOTIFY_ADDED_FOLDERS,
+                    boost::any(l));
+    }
+    std::for_each( files->begin(), files->end(),
+                   bind(&Library::addFile, boost::ref(lib),
+                        pf->id(), _1, manage) );
+    lib->notify(Library::NOTIFY_ADDED_FILES,
+                boost::any()); 
+}
+
+
+void Commands::cmdQueryFolderContent(const Library::Ptr & lib, 
+                                     int folder_id)
+{
+    LibFile::ListPtr fl(new LibFile::List());
+    lib->getFolderContent(folder_id, fl);
+    lib->notify(Library::NOTIFY_FOLDER_CONTENT_QUERIED, boost::any(fl));		
+}
+
+void Commands::cmdCountFolder(const db::Library::Ptr & lib, 
+                              int folder_id)
+{
+    int count = lib->countFolder(folder_id);
+    lib->notify(Library::NOTIFY_FOLDER_COUNTED, boost::any(std::make_pair(folder_id, count)));
+}
+
+void Commands::cmdQueryKeywordContent(const Library::Ptr & lib, 
+                                      int keyword_id)
+{
+    LibFile::ListPtr fl(new LibFile::List());
+    lib->getKeywordContent(keyword_id, fl);
+    lib->notify(Library::NOTIFY_KEYWORD_CONTENT_QUERIED, boost::any(fl));		
+}
+
+void Commands::cmdRequestMetadata(const db::Library::Ptr & lib,
+                                  int file_id)
+{
+    LibMetadata::Ptr lm(new LibMetadata(file_id));
+    lib->getMetaData(file_id, lm);
+    lib->notify(Library::NOTIFY_METADATA_QUERIED, boost::any(lm));
+}
+
+void Commands::cmdSetMetadata(const db::Library::Ptr & lib,
+                              int file_id, int meta, int value)
+{
+    boost::array<int, 3> m;
+    m[0] = file_id;
+    m[1] = meta;
+    m[2] = value;
+    lib->setMetaData(file_id, meta, value);
+    lib->notify(Library::NOTIFY_METADATA_CHANGED, boost::any(m));
+}
+
+void Commands::cmdProcessXmpUpdateQueue(const db::Library::Ptr & lib)
+{
+    lib->processXmpUpdateQueue();
+}
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+

Added: trunk/src/library/commands.h
==============================================================================
--- (empty file)
+++ trunk/src/library/commands.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,72 @@
+/*
+ * niepce - library/commands.h
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+#ifndef __LIBRARY_COMMANDS_H__
+#define __LIBRARY_COMMANDS_H__
+
+#include "op.h"
+#include "utils/files.h"
+#include "db/library.h"
+
+namespace library {
+
+	/** Marshalling and demarshalling of commands ops */ 
+	class Commands 
+	{
+	public:
+
+		// commands: execute an op
+//		static void cmdQueryFiles(const db::Library::Ptr & lib);
+//		static void cmdUpdateFiles(const db::Library::Ptr & lib);
+
+		static void cmdListAllFolders(const db::Library::Ptr & lib);
+		static void cmdListAllKeywords(const db::Library::Ptr & lib);
+		static void cmdImportFiles(const db::Library::Ptr & lib, 
+								   const boost::filesystem::path & folder, 
+								   const utils::FileList::Ptr & files, 
+								   bool manage);
+		static void cmdQueryFolderContent(const db::Library::Ptr & lib, 
+										  int folder_id);
+		static void cmdCountFolder(const db::Library::Ptr & lib, 
+								   int folder_id);
+		static void cmdQueryKeywordContent(const db::Library::Ptr & lib, 
+										   int keyword_id);
+		static void cmdRequestMetadata(const db::Library::Ptr & lib,
+									   int file_id);
+        static void cmdSetMetadata(const db::Library::Ptr & lib,
+                                   int file_id, int meta, int value);
+		static void cmdProcessXmpUpdateQueue(const db::Library::Ptr & lib);
+
+	};
+
+}
+
+
+#endif
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/library/op.cpp
==============================================================================
--- (empty file)
+++ trunk/src/library/op.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,49 @@
+/*
+ * niepce - library/op.cpp
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "op.h"
+
+
+namespace library {
+
+Op::Op(tid_t _id, const function_t & func)
+    : m_id(_id),
+      m_function(func)
+{
+}
+
+void Op::operator() (const db::Library::Ptr &l)
+{
+    if(m_function) {
+        m_function(l);
+    }
+}
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/library/op.h
==============================================================================
--- (empty file)
+++ trunk/src/library/op.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,65 @@
+/*
+ * niepce - library/op.h
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __NIEPCE_LIBRARY_OP_H__
+#define __NIEPCE_LIBRARY_OP_H__
+
+#include <boost/shared_ptr.hpp>
+#include <boost/function.hpp>
+
+#include "library/clienttypes.h"
+#include "db/library.h"
+
+namespace library {
+
+	/** a library operation */
+	class Op
+	{
+	public:
+		typedef boost::shared_ptr< Op > Ptr;
+		typedef boost::function<void (const db::Library::Ptr &)> function_t;
+
+		Op(tid_t id, const function_t & func);
+
+		tid_t id() const 
+			{ return m_id; }
+
+        void operator() (const db::Library::Ptr &);
+		const function_t & fn() const
+			{ return m_function; }
+	protected:
+	private:
+		tid_t   m_id;
+		function_t m_function;
+	};
+
+}
+
+
+#endif
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/library/opqueue.h
==============================================================================
--- (empty file)
+++ trunk/src/library/opqueue.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,35 @@
+/*
+ * niepce - library/opqueue.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+#ifndef __NIEPCE_LIBRARY_OPQUEUE_H__
+#define __NIEPCE_LIBRARY_OPQUEUE_H__
+
+
+#include "utils/mtqueue.h"
+#include "op.h"
+
+namespace library {
+
+	typedef utils::MtQueue< Op::Ptr > OpQueue;
+
+}
+
+#endif

Added: trunk/src/library/test_opqueue.cpp
==============================================================================
--- (empty file)
+++ trunk/src/library/test_opqueue.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,51 @@
+/*
+ * niepce - library/test_opqueue.cpp
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "op.h"
+#include "opqueue.h"
+
+#include <boost/bind.hpp>
+#define BOOST_AUTO_TEST_MAIN
+#include <boost/test/auto_unit_test.hpp>
+
+using namespace library;
+
+void foo(const db::Library::Ptr &)
+{
+}
+
+
+BOOST_AUTO_TEST_CASE(opqueue_test)
+{
+	OpQueue q;
+
+	Op::Ptr p(new Op(1, boost::bind(&foo, db::Library::Ptr())));
+
+	BOOST_CHECK(q.empty());
+
+	q.add(p);
+	BOOST_CHECK(!q.empty());
+
+	Op::Ptr p2(q.pop());
+	BOOST_CHECK(p2 == p);
+	BOOST_CHECK(p2->id() == p->id());
+	BOOST_CHECK(q.empty());	
+}
+

Added: trunk/src/library/thumbnailcache.cpp
==============================================================================
--- (empty file)
+++ trunk/src/library/thumbnailcache.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,134 @@
+/*
+ * niepce - library/thumbnailcache.cpp
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+
+#include <functional>
+#include <boost/bind.hpp>
+#include <boost/any.hpp>
+
+#include <gdkmm/pixbuf.h>
+#include <libopenraw-gnome/gdkpixbuf.h>
+
+#include "niepce/notifications.h"
+#include "utils/debug.h"
+#include "framework/mimetype.h"
+#include "framework/gdkutils.h"
+#include "thumbnailcache.h"
+#include "thumbnailnotification.h"
+
+using db::LibFile;
+
+namespace library {
+
+	ThumbnailCache::ThumbnailCache(const boost::filesystem::path & dir,
+								   const framework::NotificationCenter::Ptr & nc)
+		: m_cacheDir(dir),
+		  m_notif_center(nc)
+	{
+	}
+
+	ThumbnailCache::~ThumbnailCache()
+	{
+	}
+
+	void ThumbnailCache::request(const LibFile::ListPtr & fl)
+	{
+		clear();
+		std::for_each(fl->begin(), fl->end(),
+					 boost::bind(&ThumbnailCache::requestForFile, this, 
+								 _1));
+	}
+
+	void ThumbnailCache::requestForFile(const LibFile::Ptr & f)
+	{
+		ThumbnailTask::Ptr task(new ThumbnailTask(f, 160, 160));
+		schedule( task );
+	}
+
+
+	void ThumbnailCache::execute(const  ThumbnailTask::Ptr & task)
+	{
+		const char *filename = task->file()->path().string().c_str();
+		DBG_OUT("creating thumbnail for %s",filename);
+        int w, h;
+        w = task->width();
+        h = task->height();
+
+		framework::MimeType mime_type(filename);
+
+
+		DBG_OUT("MIME type %s", mime_type.string().c_str());
+
+		if(mime_type.isUnknown()) {
+			DBG_OUT("unknown file type", filename);
+			return;
+		}
+		if(!mime_type.isImage()) {
+			DBG_OUT("not an image type");
+			return;
+		}
+		
+		Glib::RefPtr<Gdk::Pixbuf> pix;
+		if(!mime_type.isDigicamRaw()) {
+			DBG_OUT("not a raw type, trying GdkPixbuf loaders");
+            try {
+                pix = Gdk::Pixbuf::create_from_file(filename, w, h, true);
+                if(pix) {
+                    pix = framework::gdkpixbuf_exif_rotate(pix, task->file()->orientation());
+                }
+            }
+            catch(const Glib::Error & e) 
+            {
+                ERR_OUT("exception %s", e.what().c_str());
+            }
+		}	
+		else {	
+			GdkPixbuf *pixbuf = or_gdkpixbuf_extract_rotated_thumbnail(filename, 
+																	   std::min(w, h));
+			if(pixbuf) {
+				 pix = Glib::wrap(pixbuf, true); // take ownership
+			}
+		}
+		if(pix)
+		{
+            if((w < pix->get_width()) || (h < pix->get_height())) {
+                pix = framework::gdkpixbuf_scale_to_fit(pix, std::min(w,h));
+            }
+			framework::NotificationCenter::Ptr nc(m_notif_center);
+			if(nc) {
+				// pass the notification
+				framework::Notification::Ptr n(new framework::Notification(niepce::NOTIFICATION_THUMBNAIL));
+				ThumbnailNotification tn;
+				tn.id = task->file()->id();
+				tn.width = pix->get_width();
+				tn.height = pix->get_height();
+				tn.pixmap = pix;
+				n->setData(boost::any(tn));
+				DBG_OUT("notify thumbnail for id=%d", tn.id);
+				nc->post(n);
+			}
+		}
+		else 
+		{
+			DBG_OUT("couldn't get the thumbnail for %s", filename);
+		}
+	}
+
+}

Added: trunk/src/library/thumbnailcache.h
==============================================================================
--- (empty file)
+++ trunk/src/library/thumbnailcache.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,76 @@
+/*
+ * niepce - library/thumbnailcache.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _LIBRARY_THUMBNAILCACHE_H__
+#define _LIBRARY_THUMBNAILCACHE_H__
+
+#include <boost/filesystem/path.hpp>
+#include <boost/weak_ptr.hpp>
+
+#include "utils/worker.h"
+#include "framework/notificationcenter.h"
+#include "db/libfile.h"
+
+namespace library {
+
+
+	class ThumbnailTask
+	{
+	public:
+		typedef boost::shared_ptr< ThumbnailTask > Ptr;
+		
+		ThumbnailTask(const db::LibFile::Ptr & f, int w, int h)
+			: m_file(f), m_width(w), m_height(h)
+			{ }
+		
+		const db::LibFile::Ptr & file()
+			{ return m_file; }
+		int width() const
+			{ return m_width; }
+		int height() const
+			{ return m_height; }
+	private:
+		const db::LibFile::Ptr m_file;
+		int m_width;
+		int m_height;
+	};
+
+
+	class ThumbnailCache
+		: private utils::Worker< ThumbnailTask::Ptr >
+	{
+	public:
+		ThumbnailCache(const boost::filesystem::path & dir,
+					   const framework::NotificationCenter::Ptr & nc);
+		~ThumbnailCache();
+
+		void request(const db::LibFile::ListPtr & fl);
+		void requestForFile(const db::LibFile::Ptr & f);
+
+	protected:
+		virtual void execute(const  ThumbnailTask::Ptr & task);
+	private:
+		boost::filesystem::path         m_cacheDir;
+		boost::weak_ptr<framework::NotificationCenter> m_notif_center;
+	};
+
+}
+
+#endif

Added: trunk/src/library/thumbnailnotification.h
==============================================================================
--- (empty file)
+++ trunk/src/library/thumbnailnotification.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,38 @@
+/*
+ * niepce - library/thumbnailnotification.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _LIBRARY_THUMBNAILNOTIFICATION_H__
+#define _LIBRARY_THUMBNAILNOTIFICATION_H__
+
+#include <gdkmm/pixbuf.h>
+
+namespace library {
+
+	struct ThumbnailNotification
+	{
+		int  id;
+		int  width;
+		int  height;
+		Glib::RefPtr<Gdk::Pixbuf> pixmap; 
+	};
+
+}
+
+#endif

Added: trunk/src/libraryclient/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/libraryclient/Makefile.am	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,25 @@
+
+
+INCLUDES = -I$(top_srcdir)/src @EXEMPI_CFLAGS@
+
+noinst_LIBRARIES = liblibraryclient.a
+
+check_PROGRAMS = test_worker
+TESTS = test_worker
+TEST_LIBS =  \
+	liblibraryclient.a \
+        ../library/libniepcelibrary.a \
+	../db/libniepcedb.a \
+        ../utils/libniepceutils.a \
+	../framework/libniepceframework.a \
+        @BOOST_UNIT_TEST_FRAMEWORK_LIBS@ \
+	@BOOST_THREAD_LIBS@ @BOOST_FILESYSTEM_LIBS@ \
+	@LIBGTKMM_LIBS@ @SQLITE3_LIBS@ @OPENRAW_LIBS@ \
+	@EXEMPI_LIBS@ @GNOMEVFS_LIBS@
+
+test_worker_SOURCES = test_worker.cpp
+test_worker_LDADD = $(TEST_LIBS)
+
+liblibraryclient_a_SOURCES = libraryclient.h  libraryclient.cpp \
+	clientimpl.h clientimpl.cpp \
+	locallibraryserver.h locallibraryserver.cpp

Added: trunk/src/libraryclient/clientimpl.cpp
==============================================================================
--- (empty file)
+++ trunk/src/libraryclient/clientimpl.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,159 @@
+/*
+ * niepce - libraryclient/libraryclient.cpp
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "utils/debug.h"
+#include "utils/files.h"
+#include "library/op.h"
+#include "library/commands.h"
+#include "libraryclient.h"
+#include "clientimpl.h"
+#include "locallibraryserver.h"
+
+using utils::FileList;
+using library::Op;
+using library::Commands;
+using library::tid_t;
+
+namespace libraryclient {
+	
+ClientImpl *ClientImpl::makeClientImpl(const utils::Moniker & moniker, 
+                                       const framework::NotificationCenter::Ptr & nc)
+{
+    return new ClientImpl(moniker, nc);
+}
+
+ClientImpl::ClientImpl(const utils::Moniker & moniker, const framework::NotificationCenter::Ptr & nc)
+    : m_moniker(moniker),
+      m_localLibrary(NULL)
+{
+    DBG_OUT("creating implementation with moniker %s", 
+            moniker.c_str());
+    m_localLibrary = new LocalLibraryServer(moniker.path(), nc);
+}
+
+ClientImpl::~ClientImpl()
+{
+    delete m_localLibrary;
+}
+
+tid_t ClientImpl::getAllKeywords()
+{
+    tid_t id = LibraryClient::newTid();
+    Op::Ptr op(new Op(id, boost::bind(&Commands::cmdListAllKeywords, _1)));
+    m_localLibrary->schedule(op);
+    return id;
+}
+
+
+tid_t ClientImpl::getAllFolders()
+{
+    tid_t id = LibraryClient::newTid();
+    Op::Ptr op(new Op(id, boost::bind(&Commands::cmdListAllFolders, _1)));
+    m_localLibrary->schedule(op);
+    return id;
+}
+
+tid_t ClientImpl::queryFolderContent(int folder_id)
+{
+    tid_t id = LibraryClient::newTid();
+    Op::Ptr op(new Op(id, boost::bind(&Commands::cmdQueryFolderContent,
+                                      _1, folder_id)));
+    m_localLibrary->schedule(op);
+    return id;
+}
+
+
+tid_t ClientImpl::countFolder(int folder_id)
+{
+    tid_t id = LibraryClient::newTid();
+    Op::Ptr op(new Op(id, boost::bind(&Commands::cmdCountFolder, 
+                                      _1, folder_id)));
+    m_localLibrary->schedule(op);
+    return id;
+}
+
+
+tid_t ClientImpl::queryKeywordContent(int keyword_id)
+{
+    tid_t id = LibraryClient::newTid();
+    Op::Ptr op(new Op(id, boost::bind(&Commands::cmdQueryKeywordContent,
+                                      _1, keyword_id)));
+    m_localLibrary->schedule(op);
+    return id;
+}
+
+
+tid_t ClientImpl::requestMetadata(int file_id)
+{
+    tid_t id = LibraryClient::newTid();
+    Op::Ptr op(new Op(id, boost::bind(&Commands::cmdRequestMetadata,
+                                      _1, file_id)));
+    m_localLibrary->schedule(op);
+    return id;
+}
+
+
+tid_t ClientImpl::setMetadata(int file_id, int meta, int value)
+{
+    tid_t id = LibraryClient::newTid();
+    Op::Ptr op(new Op(id, boost::bind(&Commands::cmdSetMetadata, _1,
+                                      file_id, meta, value)));
+    m_localLibrary->schedule(op);
+    return id;
+}
+
+
+tid_t ClientImpl::processXmpUpdateQueue()
+{
+    tid_t id = LibraryClient::newTid();
+    Op::Ptr op(new Op(id, boost::bind(&Commands::cmdProcessXmpUpdateQueue,
+                                      _1)));
+    m_localLibrary->schedule(op);
+    return id;
+}
+
+
+tid_t ClientImpl::importFromDirectory(const std::string & dir, bool manage)
+{
+    FileList::Ptr files;
+	
+    files = FileList::getFilesFromDirectory(dir, 
+                                            boost::bind(&utils::filter_xmp_out, _1));
+    
+    tid_t id = LibraryClient::newTid();
+    Op::Ptr op(new Op(id, boost::bind(&Commands::cmdImportFiles,
+                                      _1, dir, files, manage)));
+    m_localLibrary->schedule(op);
+    return id;
+}
+
+}
+
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/libraryclient/clientimpl.h
==============================================================================
--- (empty file)
+++ trunk/src/libraryclient/clientimpl.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,72 @@
+/*
+ * niepce - libraryclient/clientimpl.h
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _LIBRARYCLIENT_CLIENTIMPL_H_
+#define _LIBRARYCLIENT_CLIENTIMPL_H_
+
+#include <string>
+
+#include "utils/moniker.h"
+#include "library/clienttypes.h"
+
+
+namespace libraryclient {
+
+	class LocalLibraryServer;
+
+	class ClientImpl
+	{
+	public:
+		static ClientImpl *makeClientImpl(const utils::Moniker & moniker, 
+										  const framework::NotificationCenter::Ptr & nc);
+		
+		ClientImpl(const utils::Moniker & moniker, const framework::NotificationCenter::Ptr & nc);
+		virtual ~ClientImpl();
+
+		library::tid_t getAllKeywords();
+		library::tid_t queryKeywordContent(int id);
+		library::tid_t getAllFolders();
+		library::tid_t queryFolderContent(int id);
+		library::tid_t countFolder(int id);
+		library::tid_t requestMetadata(int id);
+        library::tid_t setMetadata(int id, int meta, int value);
+
+        library::tid_t processXmpUpdateQueue();
+
+		library::tid_t importFromDirectory(const std::string & dir, bool manage);
+
+	protected:
+		const utils::Moniker m_moniker;
+		LocalLibraryServer *m_localLibrary;
+	};
+
+}
+
+
+#endif
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/libraryclient/libraryclient.cpp
==============================================================================
--- (empty file)
+++ trunk/src/libraryclient/libraryclient.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,119 @@
+/*
+ * niepce - libraryclient/libraryclient.cpp
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <boost/filesystem/path.hpp>
+
+#include "utils/moniker.h"
+
+#include "libraryclient.h"
+#include "clientimpl.h"
+
+using library::tid_t;
+
+namespace bfs = boost::filesystem;
+
+namespace libraryclient {
+
+const char * s_thumbcacheDirname = "thumbcache";
+
+LibraryClient::LibraryClient(const utils::Moniker & moniker, 
+                             const framework::NotificationCenter::Ptr & nc)
+    : m_pImpl(ClientImpl::makeClientImpl(moniker, nc)),
+      m_thumbnailCache(bfs::path(moniker.path()) / s_thumbcacheDirname, nc)
+{
+
+}
+
+LibraryClient::~LibraryClient()
+{
+    delete m_pImpl;
+}
+
+tid_t LibraryClient::newTid()
+{
+    static tid_t id = 0;
+    id++;
+    return id;
+}
+
+
+tid_t LibraryClient::getAllKeywords()
+{
+    return m_pImpl->getAllKeywords();
+}
+
+
+tid_t LibraryClient::getAllFolders()
+{
+    return m_pImpl->getAllFolders();
+}
+
+tid_t LibraryClient::queryFolderContent(int id)
+{
+    return m_pImpl->queryFolderContent(id);
+}
+
+tid_t LibraryClient::queryKeywordContent(int id)
+{
+    return m_pImpl->queryKeywordContent(id);
+}
+
+library::tid_t LibraryClient::countFolder(int id)
+{
+    return m_pImpl->countFolder(id);
+}
+
+library::tid_t LibraryClient::requestMetadata(int id)
+{
+    return m_pImpl->requestMetadata(id);
+}
+
+/** set the metadata */
+library::tid_t LibraryClient::setMetadata(int id, int meta, int value)
+{
+    return m_pImpl->setMetadata(id, meta, value);
+}
+
+library::tid_t LibraryClient::processXmpUpdateQueue()
+{
+    return m_pImpl->processXmpUpdateQueue();
+}
+
+void LibraryClient::importFromDirectory(const std::string & dir, bool manage)
+{
+    m_pImpl->importFromDirectory(dir, manage);
+}
+
+bool LibraryClient::fetchKeywordsForFile(int /*file*/, 
+                                         db::Keyword::IdList & /*keywords*/)
+{
+    // TODO
+    return false;
+}
+
+}
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/libraryclient/libraryclient.h
==============================================================================
--- (empty file)
+++ trunk/src/libraryclient/libraryclient.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,95 @@
+/*
+ * niepce - libraryclient/libraryclient.h
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _LIBRARYCLIENT_H_
+#define _LIBRARYCLIENT_H_
+
+#include <string>
+#include <boost/shared_ptr.hpp>
+
+#include "library/clienttypes.h"
+#include "library/thumbnailcache.h"
+#include "db/storage.h"
+
+namespace utils {
+	class Moniker;
+}
+
+namespace framework {
+	class NotificationCenter;
+}
+
+namespace libraryclient {
+
+	class ClientImpl;
+
+	class LibraryClient
+		: public db::Storage
+	{
+	public:
+		typedef boost::shared_ptr< LibraryClient > Ptr;
+
+		LibraryClient(const utils::Moniker & moniker, const framework::NotificationCenter::Ptr & nc);
+		virtual ~LibraryClient();
+
+		static library::tid_t newTid();
+		/** get all the keywords 
+		 * @return transaction ID
+		 */
+		library::tid_t getAllKeywords();
+		/** get all the folder
+		 * @return transaction ID
+		 */
+		library::tid_t getAllFolders();
+
+		library::tid_t queryFolderContent(int id);
+		library::tid_t queryKeywordContent(int id);
+		library::tid_t countFolder(int id);
+		library::tid_t requestMetadata(int id);
+
+        /** set the metadata */
+        library::tid_t setMetadata(int id, int meta, int value);
+
+        /** tell to process the Xmp update Queue */
+        library::tid_t processXmpUpdateQueue();
+
+		/** Import files from a directory
+		 * @param dir the directory
+		 * @param manage true if imports have to be managed
+		 */
+		void importFromDirectory(const std::string & dir, bool manage);
+		
+		library::ThumbnailCache & thumbnailCache()
+			{ return m_thumbnailCache; }
+
+		/* sync call */
+		virtual bool fetchKeywordsForFile(int file, db::Keyword::IdList &keywords);
+
+	private:
+		ClientImpl* m_pImpl;
+
+		library::ThumbnailCache                    m_thumbnailCache;
+
+		LibraryClient(const LibraryClient &);
+		LibraryClient & operator=(const LibraryClient &);
+	};
+
+}
+
+#endif

Added: trunk/src/libraryclient/locallibraryserver.cpp
==============================================================================
--- (empty file)
+++ trunk/src/libraryclient/locallibraryserver.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,36 @@
+/*
+ * niepce - libraryclient/locallibraryserver.cpp
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  
+ * 02110-1301, USA
+ */
+
+#include "locallibraryserver.h"
+
+using library::Op;
+
+namespace libraryclient {
+
+
+	void LocalLibraryServer::execute(const Op::Ptr & _op)
+	{
+		(*_op)(m_library);
+	}
+
+
+}
+

Added: trunk/src/libraryclient/locallibraryserver.h
==============================================================================
--- (empty file)
+++ trunk/src/libraryclient/locallibraryserver.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,55 @@
+/*
+ * niepce - libraryclient/locallibraryserver.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  
+ * 02110-1301, USA
+ */
+
+
+
+#ifndef _LIBRARYCLIENT_LOCALLIBRARYSERVER_H_
+#define _LIBRARYCLIENT_LOCALLIBRARYSERVER_H_
+
+#include "utils/worker.h"
+#include "library/op.h"
+#include "db/library.h"
+
+namespace libraryclient {
+
+	class LocalLibraryServer
+		: public utils::Worker< library::Op::Ptr >
+	{
+	public:
+		/** create the local server for the library whose dir is specified */
+		LocalLibraryServer(const std::string & dir, 
+						   const framework::NotificationCenter::Ptr & nc)
+			: utils::Worker< library::Op::Ptr >()
+			, m_library(db::Library::Ptr(new db::Library(dir, nc)))
+			{
+			}
+
+	protected:
+		virtual void execute(const library::Op::Ptr & _op);
+
+	private:
+		db::Library::Ptr m_library;
+	};
+
+}
+
+
+#endif

Added: trunk/src/libraryclient/test_worker.cpp
==============================================================================
--- (empty file)
+++ trunk/src/libraryclient/test_worker.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,53 @@
+/*
+ * niepce - library/test_worker.cpp
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "utils/fsutils.h"
+
+#define BOOST_AUTO_TEST_MAIN
+#include "locallibraryserver.h"
+
+#include <boost/bind.hpp>
+#include <boost/test/auto_unit_test.hpp>
+
+
+using namespace library;
+using namespace libraryclient;
+
+void foo(const db::Library::Ptr &)
+{
+}
+
+
+BOOST_AUTO_TEST_CASE(worker_test)
+{
+	char templ[] = "/tmp/niepce-tmpXXXXXX";
+	char *ptempl =  mkdtemp(templ);
+	BOOST_CHECK(ptempl);
+	{
+		utils::DirectoryDisposer d(ptempl);
+		LocalLibraryServer w(std::string("") + ptempl, framework::NotificationCenter::Ptr());
+		
+		BOOST_CHECK(w._tasks().empty());
+		
+		Op::Ptr p(new Op(0, boost::bind(&foo, db::Library::Ptr())));
+		w.schedule(p);
+	}
+}
+

Added: trunk/src/main/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/main/Makefile.am	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,31 @@
+
+
+
+INCLUDES = -I$(srcdir)/.. @EXEMPI_CFLAGS@
+
+bin_PROGRAMS = niepce
+
+
+niepce_SOURCES = main.cpp
+
+niepce_CPPFLAGS = @LIBGTKMM_CFLAGS@ @LIBGLADEMM_CFLAGS@	@LIBGCONFMM_CFLAGS@
+niepce_LDFLAGS = -Wl,--as-needed @BOOST_FILESYSTEM_LDFLAGS@ \
+	@BOOST_THREAD_LDFLAGS@ @BOOST_SIGNALS_LDFLAGS@
+niepce_LDADD = \
+	$(top_builddir)/src/db/libniepcedb.a \
+	$(top_builddir)/src/ui/libniepceui.a \
+	$(top_builddir)/src/library/libniepcelibrary.a \
+	$(top_builddir)/src/libraryclient/liblibraryclient.a \
+	$(top_builddir)/src/library/libniepcelibrary.a \
+	$(top_builddir)/src/db/libniepcedb.a \
+	$(top_builddir)/src/modules/darkroom/libmoduledarkroom.a \
+	$(top_builddir)/src/framework/libniepceframework.a \
+	$(top_builddir)/src/utils/libniepceutils.a \
+	$(top_builddir)/src/niepce/libniepceglobals.a \
+	$(top_builddir)/src/ncr/libncr.a \
+	$(top_builddir)/src/ext/libgdl/libgdl.a \
+	@LIBGTKMM_LIBS@ @LIBGLADEMM_LIBS@ @SQLITE3_LIBS@ \
+	@GNOMEVFS_LIBS@ @LIBGCONFMM_LIBS@ @BOOST_THREAD_LIBS@ \
+	@BOOST_FILESYSTEM_LIBS@  @BOOST_SIGNALS_LIBS@ \
+	@GOOCANVASMM_LIBS@ @BABL_LIBS@ \
+	@GEGLMM_LIBS@ @OPENRAW_LIBS@ @EXEMPI_LIBS@

Added: trunk/src/main/main.cpp
==============================================================================
--- (empty file)
+++ trunk/src/main/main.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,47 @@
+/*
+ * niepce - main/main.cpp
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  
+ * 02110-1301, USA
+ */
+
+
+#include <boost/bind.hpp>
+#include <boost/filesystem/path.hpp>
+
+#include "utils/exempi.h"
+#include "niepce/xmp.h"
+#include "ui/niepceapplication.h"
+
+namespace bfs = boost::filesystem;
+using utils::ExempiManager;
+
+
+int main(int argc, char ** argv)
+{
+	// we need to init boost::filesystem to use native path checks.
+	// note: this is not the case in the tests.
+	bfs::path::default_name_check(&bfs::native);
+
+	ExempiManager ex_manager(niepce::xmp_namespaces);
+	
+	return framework::Application::main(
+		boost::bind(&ui::NiepceApplication::create),
+		argc, argv);
+}
+
+

Added: trunk/src/modules/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/modules/Makefile.am	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,3 @@
+
+
+SUBDIRS = darkroom
\ No newline at end of file

Added: trunk/src/modules/darkroom/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/modules/darkroom/Makefile.am	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,19 @@
+
+
+INCLUDES = -I$(top_srcdir)/src  -I$(top_srcdir)/src/ext \
+	-DGLADEDIR=\"$(gladedir)\" \
+	-DDATADIR=\"$(datadir)\" \
+	@LIBGTKMM_CFLAGS@ @LIBGLADEMM_CFLAGS@ \
+	@GNOMEVFS_CFLAGS@ @LIBGCONFMM_CFLAGS@ \
+	@GOOCANVASMM_CFLAGS@  \
+	@GEGLMM_CFLAGS@ @EXEMPI_CFLAGS@ @OPENRAW_CFLAGS@
+
+
+noinst_LIBRARIES=libmoduledarkroom.a
+
+
+libmoduledarkroom_a_SOURCES = darkroommodule.cpp darkroommodule.h \
+	imagecanvas.h imagecanvas.cpp \
+	toolboxcontroller.h toolboxcontroller.cpp \
+	dritemwidget.h dritemwidget.cpp \
+	$(NULL)

Added: trunk/src/modules/darkroom/darkroommodule.cpp
==============================================================================
--- (empty file)
+++ trunk/src/modules/darkroom/darkroommodule.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,105 @@
+/*
+ * niepce - ui/darkroommodule.cpp
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gdkmm/pixbuf.h>
+#include <gtkmm/toolbar.h>
+#include <gtkmm/stock.h>
+
+#include "utils/debug.h"
+#include "framework/application.h"
+#include "framework/configdatabinder.h"
+#include "framework/widgets/dock.h"
+#include "ncr/ncr.h"
+#include "darkroommodule.h"
+
+namespace darkroom {
+
+
+void DarkroomModule::set_image(const db::LibFile::Ptr & file)
+{
+    m_image->reload(file->path(), 
+                    file->fileType() == db::LibFile::FILE_TYPE_RAW,
+                    file->orientation());
+/*    int w, h;
+    w = m_imagecanvas->get_width();
+    h = m_imagecanvas->get_height();
+    m_image->set_scale_to_dim(w, h);
+*/
+    m_imagecanvas->set_image(m_image->pixbuf_for_display());
+}
+
+
+Gtk::Widget * DarkroomModule::buildWidget()
+{
+    ncr::init();
+    m_imagecanvas = Gtk::manage(new ImageCanvas());
+// TODO set a proper canvas size
+//    m_canvas_scroll.add(*m_imagecanvas);
+	m_vbox.pack_start(*m_imagecanvas, Gtk::PACK_EXPAND_WIDGET);
+
+//    int w,h;
+//    w = h = 0;
+//    m_canvas_scroll.get_size_request(w, h);
+//    DBG_OUT("scroll size %d %d", w, h);
+//    m_imagecanvas->set_size_request(w, h);
+    m_imagecanvas->set_bounds(0, 0, 1000, 1000);
+
+	// build the toolbar.
+	Gtk::Toolbar * toolbar = Gtk::manage(new Gtk::Toolbar);
+
+	Glib::RefPtr<Gtk::Action> an_action;
+	an_action = m_actionGroup->get_action("PrevImage");
+	toolbar->append(*(an_action->create_tool_item()));
+	an_action = m_actionGroup->get_action("NextImage");
+	toolbar->append(*(an_action->create_tool_item()));
+	an_action = m_actionGroup->get_action("RotateLeft");
+	toolbar->append(*(an_action->create_tool_item()));
+	an_action = m_actionGroup->get_action("RotateRight");
+	toolbar->append(*(an_action->create_tool_item()));
+
+	m_vbox.pack_start(*toolbar, Gtk::PACK_SHRINK);
+	m_dr_splitview.pack1(m_vbox, Gtk::EXPAND);
+    m_dock = new framework::Dock();
+    m_dr_splitview.pack2(m_dock->getWidget(), Gtk::SHRINK);
+
+    m_databinders.add_binder(new framework::ConfigDataBinder<int>(
+                                 m_dr_splitview.property_position(),
+                                 framework::Application::app()->config(),
+                                 "dr_toolbox_pane_splitter"));
+
+    m_toolbox_ctrl = ToolboxController::Ptr(new ToolboxController(*m_dock));
+    add(m_toolbox_ctrl);
+    (void)m_toolbox_ctrl->buildWidget();
+
+	m_widget = &m_dr_splitview;
+	return m_widget;
+}
+
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/

Added: trunk/src/modules/darkroom/darkroommodule.h
==============================================================================
--- (empty file)
+++ trunk/src/modules/darkroom/darkroommodule.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,89 @@
+/*
+ * niepce - modules/darkroom/darkroommodule.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+
+#ifndef _DARKROOM_MODULE_H__
+#define _DARKROOM_MODULE_H__
+
+#include <gtkmm/widget.h>
+#include <gtkmm/paned.h>
+#include <gtkmm/box.h>
+#include <gtkmm/actiongroup.h>
+#include <gtkmm/scrolledwindow.h>
+
+#include "framework/controller.h"
+#include "db/libfile.h"
+#include "libraryclient/libraryclient.h"
+#include "ncr/image.h"
+#include "modules/darkroom/imagecanvas.h"
+#include "modules/darkroom/toolboxcontroller.h"
+	
+namespace framework {
+class Dock;
+}
+
+namespace darkroom {
+
+class DarkroomModule
+	: public framework::Controller
+{
+public:
+	typedef boost::shared_ptr<DarkroomModule> Ptr;
+
+	DarkroomModule(const Glib::RefPtr<Gtk::ActionGroup> & action_group,
+                   const libraryclient::LibraryClient::Ptr & client)
+        : m_actionGroup(action_group),
+          m_image(new ncr::Image),
+          m_libClient(client)
+		{
+		}
+
+	void set_image(const db::LibFile::Ptr & file);
+
+protected:
+	virtual Gtk::Widget * buildWidget();
+
+private:
+    // darkroom split view
+    Gtk::HPaned                  m_dr_splitview;
+    Gtk::VBox                    m_vbox;
+    ImageCanvas*                 m_imagecanvas;
+    Gtk::ScrolledWindow          m_canvas_scroll;
+    ToolboxController::Ptr       m_toolbox_ctrl;
+    Glib::RefPtr<Gtk::ActionGroup> m_actionGroup;
+    ncr::Image::Ptr              m_image;
+    libraryclient::LibraryClient::Ptr m_libClient;
+    framework::Dock              *m_dock;
+};
+
+
+}
+
+#endif
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/

Added: trunk/src/modules/darkroom/dritemwidget.cpp
==============================================================================
--- (empty file)
+++ trunk/src/modules/darkroom/dritemwidget.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,52 @@
+/*
+ * niepce - darkroom/dritem.cpp
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtkmm/label.h>
+
+#include "dritemwidget.h"
+
+namespace darkroom {
+
+DrItemWidget::DrItemWidget(const Glib::ustring & title)
+    : framework::ToolboxItemWidget(title)
+{
+    add(m_box);
+    m_box.set_border_width(6);
+}
+
+void DrItemWidget::add_widget(const Glib::ustring & label, Gtk::Widget & w)
+{
+    Gtk::Label *l = manage(new Gtk::Label(label, 0.0, 0.5));
+    m_box.pack_start(*l, Gtk::PACK_SHRINK);
+    m_box.pack_start(w, Gtk::PACK_SHRINK);
+}
+
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/
+

Added: trunk/src/modules/darkroom/dritemwidget.h
==============================================================================
--- (empty file)
+++ trunk/src/modules/darkroom/dritemwidget.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,57 @@
+/*
+ * niepce - darkroom/dritem.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+#ifndef _DARKROOM_DRITEMWIDGET_H_
+#define _DARKROOM_DRITEMWIDGET_H_
+
+#include <gtkmm/box.h>
+
+#include "framework/widgets/toolboxitemwidget.h"
+
+namespace darkroom {
+
+/** Generic Darkroom item for the toolbox. */
+class DrItemWidget
+    : public framework::ToolboxItemWidget
+{
+public:
+    DrItemWidget(const Glib::ustring & title);
+
+    void add_widget(const Glib::ustring & label, Gtk::Widget &);
+private:
+    Gtk::VBox   m_box;
+};
+
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/
+
+
+#endif

Added: trunk/src/modules/darkroom/imagecanvas.cpp
==============================================================================
--- (empty file)
+++ trunk/src/modules/darkroom/imagecanvas.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,127 @@
+/*
+ * niepce - darkroom/imagecanvas.cpp
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/* remove this when we require a version that does not barf warnings */
+#include "framework/goocanvas_proxy_header.h"
+//#include <goocanvasmm/canvas.h>
+#include "utils/debug.h"
+#include "utils/geometry.h"
+
+#include "imagecanvas.h"
+
+namespace darkroom {
+
+ImageCanvas::ImageCanvas()
+    : Goocanvas::Canvas(),
+      m_need_redisplay(true),
+      m_zoom_mode(ZOOM_MODE_FIT)
+{
+    m_imagecanvas = this;
+    Glib::RefPtr<Goocanvas::Item> root;
+    root = m_imagecanvas->get_root_item ();
+    
+    m_frameitem = Goocanvas::Rect::create(0,0,0,0);
+    m_imageitem = Goocanvas::Image::create(0,0);
+    
+    root->add_child(m_frameitem);
+    root->add_child(m_imageitem);
+}
+
+
+void ImageCanvas::set_image(const Glib::RefPtr<Gdk::Pixbuf> & img)
+{
+    m_need_redisplay = true;
+    m_image = img;
+    _redisplay();
+}
+
+
+void ImageCanvas::_calc_image_frame(int img_w, int img_h,
+                                   double & x, double & y,
+                                   double & width, double & height)
+{
+    double b_w, b_h;
+    b_w = m_imagecanvas->get_width();
+    b_h = m_imagecanvas->get_height();
+//    DBG_OUT("bounds %f %f", b_w, b_h);
+    x = (b_w - img_w) / 2;
+    y = (b_h - img_h) / 2;
+    width = img_w;
+    height = img_h;
+//    DBG_OUT("image frame %f %f %f %f", x, y, width, height);  
+}
+
+
+void ImageCanvas::_redisplay(bool force)
+{
+    if(m_need_redisplay || force) {
+        Glib::RefPtr<Gdk::Pixbuf> img = m_image;
+        DBG_OUT("set image w %d h %d", img->get_width(), img->get_height());
+        
+        // the position and dimension of the image frame
+        double x, y, w, h;
+
+        utils::Rect dest(0,0, m_imagecanvas->get_width() - 8,
+            m_imagecanvas->get_height() - 8);
+        utils::Rect source(0,0, img->get_width(), img->get_height());
+        utils::Rect frame;
+        switch(m_zoom_mode)
+        {
+        case ZOOM_MODE_FIT:
+            frame = source.fit_into(dest);
+            break;
+        case ZOOM_MODE_FILL:
+            frame = source.fill_into(dest);
+            break;
+        default:
+            frame = source;
+            break;
+        }
+        
+        _calc_image_frame(frame.w(), frame.h(),
+                          x,y,w,h);
+
+        m_imageitem->property_x() = x;
+        m_imageitem->property_y() = y;
+        m_imageitem->property_width() = w;
+        m_imageitem->property_height() = h;
+        m_imageitem->property_pixbuf() = img->scale_simple(w, h, 
+                                                           Gdk::INTERP_BILINEAR);
+
+        m_frameitem->property_x() = x;
+        m_frameitem->property_y() = y;
+        m_frameitem->property_width() = w;
+        m_frameitem->property_height() = h;        
+
+        m_need_redisplay = false;
+    }
+}
+
+}
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/modules/darkroom/imagecanvas.h
==============================================================================
--- (empty file)
+++ trunk/src/modules/darkroom/imagecanvas.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,79 @@
+/*
+ * niepce - darkroom/imagecanvas.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <gdkmm/pixbuf.h>
+#include <gtkmm/bin.h>
+
+#include "framework/goocanvas_proxy_header.h"
+
+namespace darkroom {
+
+class ImageCanvas
+    : public Goocanvas::Canvas
+{
+public:
+    typedef enum {
+        ZOOM_MODE_NONE = 0,
+        ZOOM_MODE_FIT,
+        ZOOM_MODE_FILL,
+        ZOOM_MODE_100P,
+        ZOOM_MODE_CUSTOM
+    } ZoomMode;
+    ImageCanvas();
+
+    void set_image(const Glib::RefPtr<Gdk::Pixbuf> & img);
+    void set_zoom_mode(ZoomMode mode)
+        {
+            if(m_zoom_mode != mode) {
+                m_need_redisplay = true;
+                m_zoom_mode = mode;
+            }
+        }
+    ZoomMode get_zoom_mode() const
+        {
+            return m_zoom_mode;
+        }
+private:
+    void _calc_image_frame(int img_w, int img_h,
+                          double & x, double & y,
+                          double & width, double & height);
+    /** cause to "recalulate" the content. 
+        Only if m_need_display of force */
+    void _redisplay(bool force = false);
+
+    bool                           m_need_redisplay;
+    ZoomMode                       m_zoom_mode;
+    Glib::RefPtr<Gdk::Pixbuf>      m_image;
+    Glib::RefPtr<Goocanvas::Image> m_imageitem;
+    Glib::RefPtr<Goocanvas::Rect>  m_frameitem;
+    Goocanvas::Canvas*             m_imagecanvas;
+};
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/

Added: trunk/src/modules/darkroom/toolboxcontroller.cpp
==============================================================================
--- (empty file)
+++ trunk/src/modules/darkroom/toolboxcontroller.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,93 @@
+/*
+ * niepce - darkroom/toolboxcontroller.cpp
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glibmm/i18n.h>
+
+#include <gtkmm/box.h>
+#include <gtkmm/scale.h>
+#include <gtkmm/adjustment.h>
+#include <gtkmm/stock.h>
+
+#include "toolboxcontroller.h"
+#include "framework/widgets/editablehscale.h"
+#include "framework/widgets/dock-item.h"
+#include "dritemwidget.h"
+
+namespace darkroom {
+
+ToolboxController::ToolboxController(framework::Dock &dock)
+    : Dockable(dock,"tools", _("Develop"), Gtk::Stock::APPLY.id, 
+               DockItem::DOCKED_STATE)
+{
+
+}
+
+Gtk::Widget * ToolboxController::buildWidget()
+{
+    DrItemWidget *item = NULL;
+    framework::EditableHScale *s = NULL;
+    Gtk::VBox *toolbox = DockItem::get_vbox();
+    m_widget = &DockItem::getWidget();
+
+    item = manage(new DrItemWidget(_("Crop")));
+    toolbox->pack_start(*item, Gtk::PACK_SHRINK);
+    s = Gtk::manage(new framework::EditableHScale(-45.0, 45.0, 0.5));    
+    item->add_widget(_("Tilt"), *s);
+
+    item = manage(new DrItemWidget(_("White balance")));
+    toolbox->pack_start(*item, Gtk::PACK_SHRINK);
+    s = Gtk::manage(new framework::EditableHScale(0.0, 100.0, 1.0));
+    item->add_widget(_("Color temperature"), *s);
+
+    item = manage(new DrItemWidget(_("Tone and colour")));
+    toolbox->pack_start(*item, Gtk::PACK_SHRINK);
+    s = Gtk::manage(new framework::EditableHScale(-5.0, 5.0, 0.1));
+//    s->signal_value_changed().connect();
+    item->add_widget(_("Exposure"), *s);
+    s = Gtk::manage(new framework::EditableHScale(0, 100, 1));
+    item->add_widget(_("Recovery"), *s);
+    s = Gtk::manage(new framework::EditableHScale(0, 100, 1));
+    item->add_widget(_("Fill Light"), *s);
+    s = Gtk::manage(new framework::EditableHScale(0, 100, 1));
+    item->add_widget(_("Blacks"), *s);
+    s = Gtk::manage(new framework::EditableHScale(-100, 100, 1));
+    item->add_widget(_("Brightness"), *s);
+    s = Gtk::manage(new framework::EditableHScale(-100, 100, 1));
+    item->add_widget(_("Contrast"), *s);
+    s = Gtk::manage(new framework::EditableHScale(-100, 100, 1));
+    item->add_widget(_("Saturation"), *s);
+    s = Gtk::manage(new framework::EditableHScale(-100, 100, 1));
+    item->add_widget(_("Vibrance"), *s);
+
+    return m_widget;
+}
+
+
+}
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/

Added: trunk/src/modules/darkroom/toolboxcontroller.h
==============================================================================
--- (empty file)
+++ trunk/src/modules/darkroom/toolboxcontroller.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,53 @@
+/*
+ * niepce - darkroom/toolboxcontroller.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _DARKROOM_TOOLBOXCONTROLLER_H__
+#define _DARKROOM_TOOLBOXCONTROLLER_H__
+
+#include "framework/dockable.h"
+
+namespace framework {
+class Dock;
+}
+
+namespace darkroom {
+
+class ToolboxController
+    : public framework::Dockable
+{
+public:
+    typedef boost::shared_ptr<ToolboxController> Ptr;
+    ToolboxController(framework::Dock &);
+    virtual Gtk::Widget * buildWidget();
+};
+
+}
+
+
+#endif
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/

Added: trunk/src/ncr/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/ncr/Makefile.am	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,18 @@
+
+
+
+INCLUDES = -I$(top_srcdir)/src  \
+	@LIBGTKMM_CFLAGS@ \
+	@GEGLMM_CFLAGS@ @OPENRAW_CFLAGS@
+
+niepcelibdir = @libdir@/niepce/
+#niepcelib_LTLIBRARIES = libncr.la
+noinst_LIBRARIES = libncr.a
+
+
+libncr_a_SOURCES = ncr.h ncr.cpp \
+	image.h image.cpp \
+	$(NULL)
+
+#libncr_la_SOURCES = ncr.h ncr.cpp
+#libncr_la_LIBADD = @OPENRAW_LIBS@ @GEGLMM_LIBS@

Added: trunk/src/ncr/image.cpp
==============================================================================
--- (empty file)
+++ trunk/src/ncr/image.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,223 @@
+/*
+ * niepce - ncr/image.cpp
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * This program 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 3 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see 
+ * <http://www.gnu.org/licenses/>.
+ */
+
+
+extern "C" {
+#include <babl/babl.h>
+}
+
+#include <boost/bind.hpp>
+
+#include <libopenraw/libopenraw.h>
+
+#include <geglmm/node.h>
+#include <geglmm/operation.h>
+
+#include "utils/debug.h"
+#include "ncr.h"
+#include "image.h"
+
+namespace ncr {
+
+struct Image::Private {
+    Private()
+        : m_width(0),
+          m_height(0)
+        {
+        }
+
+    int m_width, m_height; /**< the native dimension */
+    Glib::RefPtr<Gegl::Node> m_node;
+    Glib::RefPtr<Gegl::Node> m_rgb;    /**< RGB pixmap */
+    Glib::RefPtr<Gegl::Node> m_scale;
+    Glib::RefPtr<Gegl::Node> m_output;
+};
+
+Image::Image()
+    : priv(new Private)
+{
+}
+
+Image::~Image()
+{
+    delete priv;
+}
+
+void Image::reload(const boost::filesystem::path & p, bool is_raw,
+    int orientation)
+{
+    Glib::RefPtr<Gegl::Node> load_file;
+
+    priv->m_node = Gegl::Node::create();
+    priv->m_node->set("format", babl_format("RGB u16"));
+
+    if(!is_raw) {
+        load_file = priv->m_node->new_child("operation", "load");
+        load_file->set("path", p.string());
+        priv->m_rgb = load_file;
+    }
+    else {
+        ORRawDataRef rawdata;
+        or_get_extract_rawdata(p.string().c_str(), 0, &rawdata);
+        Glib::RefPtr<Gegl::Buffer> buffer = ncr::load_rawdata(rawdata);
+        // @todo can return a NULL buffer if load failed. Deal with that.
+        load_file = priv->m_node->new_child("operation", "load-buffer");
+        load_file->set("buffer", buffer);
+        or_cfa_pattern pattern = or_rawdata_get_cfa_pattern(rawdata);
+        or_rawdata_release(rawdata);
+
+        Glib::RefPtr<Gegl::Node> stretch = priv->m_node->new_child(
+            "operation", "stretch-contrast");
+        
+        Glib::RefPtr<Gegl::Node> demosaic = priv->m_node->new_child(
+            "operation", "demosaic-bimedian");
+        // @todo refactor somewhere.
+        int npattern = 0;
+        switch(pattern) {
+        case OR_CFA_PATTERN_GRBG:
+            npattern = 0;
+            break;
+        case OR_CFA_PATTERN_BGGR:
+            npattern = 1;
+            break;
+        case OR_CFA_PATTERN_GBRG:
+            npattern = 2;
+            break;
+        case OR_CFA_PATTERN_RGGB:
+            npattern = 3;
+            break;
+        default:
+            break;
+        }
+
+        demosaic->set("pattern", npattern);
+
+        priv->m_rgb = load_file->link(stretch)->link(demosaic);
+    }
+    
+    Glib::RefPtr<Gegl::Node> current;
+
+    DBG_OUT("rotation is %d", orientation);
+    int degrees = 0;
+    bool flip = false;
+    switch(orientation) {
+    case 0:
+    case 1:
+        break;
+    case 2:
+        flip = true;
+        break;
+    case 4:
+        flip = true;
+        // fall through
+    case 3:
+        degrees = 180;
+        break;
+    case 5:
+        flip = true;
+        // fall through
+    case 6:
+        degrees = 90;
+        break;
+    case 7:
+        flip = true;
+        // fall through
+    case 8:
+        degrees = 270;
+        break;
+    }
+    // @todo ideally we would have a plain GEGL op for that.
+    Glib::RefPtr<Gegl::Node> rotate;
+    if(flip) {
+        // @todo find a test case.
+        rotate =  priv->m_node->new_child("operation", "reflect");
+        rotate->set("x", -1.0);
+        current = priv->m_rgb->link(rotate);
+    }
+    else {
+        current = priv->m_rgb;
+    }
+    rotate = priv->m_node->new_child("operation", "rotate");
+    rotate->set("degrees", degrees);
+    current = current->link(rotate);
+
+    priv->m_scale = priv->m_node->new_child("operation", "scale");
+    set_scale(0.25);
+    current->link(priv->m_scale);
+    priv->m_output = priv->m_scale;
+    
+    int width, height;
+    Gegl::Rectangle rect;
+    rect = load_file->get_bounding_box();
+    width = rect.gobj()->width;
+    height = rect.gobj()->height;
+    DBG_OUT("width %d height %d", width, height);
+    priv->m_width = width;
+    priv->m_height = height;
+}
+
+void Image::set_scale(double scale)
+{
+    DBG_OUT("scale %f", scale);
+    priv->m_scale->set("x", scale);
+    priv->m_scale->set("y", scale);    
+}
+
+
+namespace {
+
+/** callback to free the buffer */
+void _free_buf(const guint8* p) 
+{
+    g_free(const_cast<guint8*>(p));
+}
+
+}
+
+Glib::RefPtr<Gdk::Pixbuf> Image::pixbuf_for_display()
+{
+    priv->m_output->process();
+    Gegl::Rectangle roi = priv->m_output->get_bounding_box();
+    int w, h;
+    w = roi.gobj()->width;
+    h = roi.gobj()->height;
+    guint8* buf = (guint8*)g_malloc (w * h * 3);
+    priv->m_output->blit(1.0, roi, babl_format ("R'G'B' u8"),
+                       (void*)buf, GEGL_AUTO_ROWSTRIDE,
+                       (Gegl::BlitFlags)(GEGL_BLIT_CACHE | GEGL_BLIT_DIRTY));
+    Glib::RefPtr<Gdk::Pixbuf> pix 
+        = Gdk::Pixbuf::create_from_data(buf,
+                                        Gdk::COLORSPACE_RGB,
+                                        false, 8, w, h,
+                                        w * 3, boost::bind(&_free_buf, _1));
+    return pix;
+}
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/

Added: trunk/src/ncr/image.h
==============================================================================
--- (empty file)
+++ trunk/src/ncr/image.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,74 @@
+/*
+ * niepce - ncr/image.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * This program 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 3 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see 
+ * <http://www.gnu.org/licenses/>.
+ */
+
+
+
+#ifndef _NCR_IMAGE_H_
+#define _NCR_IMAGE_H_
+
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/filesystem/path.hpp>
+
+#include <gdkmm/pixbuf.h>
+
+namespace ncr {
+
+class Image 
+    : public boost::enable_shared_from_this<Image>
+{
+public:
+    typedef boost::shared_ptr<Image> Ptr;
+
+    Image();
+    virtual ~Image();
+
+    Glib::RefPtr<Gdk::Pixbuf> pixbuf_for_display();
+    void reload(const boost::filesystem::path & p, bool is_raw,
+        int orientation);
+    void set_scale(double scale);
+
+    void set_tilt(double angle);
+    void set_color_temp(int temp);
+    void set_exposure(double exposure);
+    void set_brightness(int brightness);
+    void set_contrast(int contrast);
+    void set_saturation(int saturation);
+    void set_vibrance(int vibrance);
+
+    sigc::signal<void, Image::Ptr> signal_update;
+private:
+    class Private;
+    Private *priv;
+};
+
+}
+
+#endif
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/

Added: trunk/src/ncr/ncr.cpp
==============================================================================
--- (empty file)
+++ trunk/src/ncr/ncr.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,68 @@
+/*
+ * niepce - ncr/ncr.cpp
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * This program 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 3 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see 
+ * <http://www.gnu.org/licenses/>.
+ */
+
+extern "C" {
+#include <babl/babl.h>
+}
+
+#include <geglmm/init.h>
+
+#include "ncr.h"
+
+namespace ncr {
+
+void init()
+{
+    Gegl::init(0, NULL);
+}
+
+Glib::RefPtr<Gegl::Buffer> load_rawdata(ORRawDataRef rawdata)
+{
+    uint32_t x, y;
+    void *data;
+    if(or_rawdata_format(rawdata) == OR_DATA_TYPE_CFA) {
+        /* TODO take the dest_x and dest_y into account */
+        GeglRectangle rect = {0, 0, 0, 0};
+        or_rawdata_dimensions(rawdata, &x, &y);
+        rect.width = x;
+        rect.height = y;
+        
+        Glib::RefPtr<Gegl::Buffer> buffer 
+            = Gegl::Buffer::create(Gegl::Rectangle(rect), babl_format ("Y u16"));
+        
+        data = or_rawdata_data(rawdata);
+        buffer->set(Gegl::Rectangle(rect), babl_format ("Y u16"),
+                    data, GEGL_AUTO_ROWSTRIDE);
+        
+        return buffer;
+    }
+    return Glib::RefPtr<Gegl::Buffer>();
+}
+
+}
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/

Added: trunk/src/ncr/ncr.h
==============================================================================
--- (empty file)
+++ trunk/src/ncr/ncr.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,46 @@
+/*
+ * niepce - ncr/ncr.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * This program 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 3 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see 
+ * <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _NIEPCE_NCR_H
+#define _NIEPCE_NCR_H
+
+#include <geglmm/buffer.h>
+#include <libopenraw/libopenraw.h>
+
+namespace ncr {
+
+void init();
+
+/** load RAW data into a buffer. */
+Glib::RefPtr<Gegl::Buffer> load_rawdata(ORRawDataRef rawdata); 
+
+}
+
+#endif
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/

Added: trunk/src/niepce/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/niepce/Makefile.am	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,11 @@
+
+
+INCLUDES = -DDATADIR=\"$(datadir)\" \
+	-I$(srcdir)/.. @EXEMPI_CFLAGS@ @LIBGTKMM_CFLAGS@
+
+noinst_HEADERS = notifications.h xmp.h
+
+noinst_LIBRARIES = libniepceglobals.a
+
+libniepceglobals_a_SOURCES = xmp.cpp \
+	stock.h stock.cpp

Added: trunk/src/niepce/notifications.h
==============================================================================
--- (empty file)
+++ trunk/src/niepce/notifications.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,35 @@
+/*
+ * niepce - niepce/notifications.h
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _NIEPCE_NOTIFICATION_H_
+#define _NIEPCE_NOTIFICATION_H_
+
+namespace niepce {
+
+	enum {
+		NOTIFICATION_LIB = 0,
+		NOTIFICATION_THUMBNAIL,
+		NOTIFICATION_COUNT                    /**< Notification a counted items */
+	};
+
+}
+
+
+#endif

Added: trunk/src/niepce/stock.cpp
==============================================================================
--- (empty file)
+++ trunk/src/niepce/stock.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,71 @@
+/*
+ * niepce - niepce/stock.cpp
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glibmm/i18n.h>
+#include <gdkmm/pixbuf.h>
+#include <gtkmm/iconfactory.h>
+#include <gtkmm/stock.h>
+#include <gtkmm/stockitem.h>
+
+#include "utils/debug.h"
+#include "stock.h"
+
+#ifndef DATADIR
+#error DATADIR is not defined
+#endif
+
+namespace niepce {
+namespace Stock {
+
+const Gtk::StockID ROTATE_LEFT = Gtk::StockID("rotate-left");
+const Gtk::StockID ROTATE_RIGHT = Gtk::StockID("rotate-right");
+
+
+void registerStockItems()
+{
+    Glib::RefPtr<Gtk::IconFactory> icon_factory = Gtk::IconFactory::create();
+    icon_factory->add_default();
+
+    try {
+        Gtk::Stock::add(Gtk::StockItem(ROTATE_LEFT, _("Rotate L_eft"), Gdk::ModifierType(0), '['));
+        icon_factory->add(ROTATE_LEFT, Gtk::IconSet(
+                              Gdk::Pixbuf::create_from_file(DATADIR"/niepce/pixmaps/niepce-rotate-left.png")));
+        Gtk::Stock::add(Gtk::StockItem(ROTATE_RIGHT, _("Rotate R_ight"), Gdk::ModifierType(0), ']'));
+        icon_factory->add(ROTATE_RIGHT, Gtk::IconSet(
+                              Gdk::Pixbuf::create_from_file(DATADIR"/niepce/pixmaps/niepce-rotate-right.png")));
+    }
+    catch(const Glib::Exception &e)
+    {
+        ERR_OUT("Exception %s", e.what().c_str());
+    }
+}
+
+
+}
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/niepce/stock.h
==============================================================================
--- (empty file)
+++ trunk/src/niepce/stock.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,45 @@
+/*
+ * niepce - niepce/stock.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @brief define the stock items */
+
+
+
+namespace niepce {
+namespace Stock {
+
+extern const Gtk::StockID ROTATE_LEFT;
+extern const Gtk::StockID ROTATE_RIGHT;
+
+/** register the stock items. */
+void registerStockItems();
+
+}
+}
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/niepce/xmp.cpp
==============================================================================
--- (empty file)
+++ trunk/src/niepce/xmp.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,35 @@
+/*
+ * niepce - niepce/xmp.cpp
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "xmp.h"
+
+using utils::ExempiManager;
+
+namespace niepce {
+
+	const char NIEPCE_XMP_NAMESPACE[] = "http://xmlns.figuiere.net/ns/niepce/1.0";;
+	const char NIEPCE_XMP_NS_PREFIX[] = "niepce";
+
+	const ExempiManager::ns_defs_t xmp_namespaces[] = 
+	{
+		{ NIEPCE_XMP_NAMESPACE, NIEPCE_XMP_NS_PREFIX },
+		{ 0, 0 }
+	};
+
+}

Added: trunk/src/niepce/xmp.h
==============================================================================
--- (empty file)
+++ trunk/src/niepce/xmp.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,34 @@
+/*
+ * niepce - niepce/xmp.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _NIEPCE_XMP_H_
+#define _NIEPCE_XMP_H_
+
+#include "utils/exempi.h"
+
+namespace niepce {
+
+	extern const char NIEPCE_XMP_NAMESPACE[];
+	extern const char NIEPCE_XMP_NS_PREFIX[];
+	extern const utils::ExempiManager::ns_defs_t xmp_namespaces[];
+
+}
+
+#endif

Added: trunk/src/ui/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/ui/Makefile.am	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,37 @@
+
+
+DIST_SUBDIRS = thumb-view
+
+gladefiles = preferences.glade \
+	importdialog.glade	
+
+gladedir = @datarootdir@/niepce/glade/
+glade_DATA = $(gladefiles)
+
+INCLUDES = -I$(srcdir)/.. -I$(srcdir)/thumb-view \
+	-I$(top_srcdir)/src/ext \
+	-DGLADEDIR=\"$(gladedir)\" \
+	-DDATADIR=\"$(datadir)\" \
+	@LIBGTKMM_CFLAGS@ @LIBGLADEMM_CFLAGS@ \
+	@GNOMEVFS_CFLAGS@ @LIBGCONFMM_CFLAGS@ \
+	@GOOCANVASMM_CFLAGS@ \
+	@EXEMPI_CFLAGS@
+
+EXTRA_DIST = $(gladefiles)
+
+noinst_LIBRARIES = libniepceui.a
+
+libniepceui_a_CPPFLAGS = 
+libniepceui_a_SOURCES = niepcewindow.h niepcewindow.cpp \
+	niepceapplication.h niepceapplication.cpp \
+	librarymainview.h librarymainview.cpp \
+	librarycellrenderer.h librarycellrenderer.cpp \
+	librarymainviewcontroller.h librarymainviewcontroller.cpp \
+	imageliststore.h imageliststore.cpp\
+	workspacecontroller.h workspacecontroller.cpp \
+	metadatapanecontroller.h metadatapanecontroller.cpp \
+	importdialog.h importdialog.cpp \
+	selectioncontroller.h selectioncontroller.cpp \
+	filmstripcontroller.h filmstripcontroller.cpp \
+	thumb-view/eog-thumb-nav.cpp thumb-view/eog-thumb-nav.h \
+	thumb-view/eog-thumb-view.cpp thumb-view/eog-thumb-view.h

Added: trunk/src/ui/filmstripcontroller.cpp
==============================================================================
--- (empty file)
+++ trunk/src/ui/filmstripcontroller.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,140 @@
+/*
+ * niepce - ui/filmstripcontroller.cpp
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <gtkmm/iconview.h>
+
+#include "niepce/notifications.h"
+#include "db/library.h"
+#include "library/thumbnailnotification.h"
+#include "utils/debug.h"
+
+#include "eog-thumb-nav.h"
+#include "eog-thumb-view.h"
+#include "filmstripcontroller.h"
+
+namespace ui {
+
+FilmStripController::FilmStripController(const Glib::RefPtr<ImageListStore> & store)
+    : m_store(store)
+{
+}
+
+Gtk::Widget * FilmStripController::buildWidget()
+{
+    DBG_ASSERT(m_store, "m_store NULL");
+	m_thumbview = Glib::wrap(GTK_ICON_VIEW(eog_thumb_view_new(m_store)));
+	GtkWidget *thn = eog_thumb_nav_new(GTK_WIDGET(m_thumbview->gobj()), 
+									   EOG_THUMB_NAV_MODE_ONE_ROW, true);
+	gtk_icon_view_set_selection_mode(GTK_ICON_VIEW(m_thumbview->gobj()),
+									 GTK_SELECTION_SINGLE);
+	m_widget = Glib::wrap(thn);
+	return m_widget;
+}
+
+Gtk::IconView * FilmStripController::image_list() 
+{ 
+	return m_thumbview; 
+}
+
+int FilmStripController::get_selected()
+{
+	int id = 0;
+	Gtk::IconView::ArrayHandle_TreePaths paths = m_thumbview->get_selected_items();
+	if(!paths.empty()) {
+		Gtk::TreePath path(*(paths.begin()));
+		DBG_OUT("found path %s", path.to_string().c_str());
+		Gtk::TreeRow row = *(m_store->get_iter(path));
+		if(row) {
+			DBG_OUT("found row");
+            db::LibFile::Ptr libfile = row[m_store->columns().m_libfile];
+            if(libfile) {
+                id = libfile->id();
+            }
+		}
+	}
+	return id;
+}
+
+void FilmStripController::select_image(int id)
+{
+	DBG_OUT("filmstrip select %d", id);
+    Gtk::TreePath path = m_store->get_path_from_id(id);
+    m_thumbview->select_path(path);
+}
+
+
+#if 0
+void FilmStripController::on_lib_notification(const framework::Notification::Ptr &n)
+{
+	DBG_ASSERT(n->type() == niepce::NOTIFICATION_LIB, "wrong notification type");
+	if(n->type() == niepce::NOTIFICATION_LIB) {
+		db::LibNotification ln = boost::any_cast<db::LibNotification>(n->data());
+		switch(ln.type) {
+		case db::Library::NOTIFY_FOLDER_CONTENT_QUERIED:
+		case db::Library::NOTIFY_KEYWORD_CONTENT_QUERIED:
+		{
+			db::LibFile::ListPtr l 
+				= boost::any_cast<db::LibFile::ListPtr>(ln.param);
+			DBG_OUT("received folder content file # %d", l->size());
+			
+			Glib::RefPtr<EogListStore> store(new EogListStore( *l ));
+			eog_thumb_view_set_model((EogThumbView*)(m_thumbview->gobj()), 
+									 store);
+			break;
+		}
+		default:
+			break;
+		}
+	}
+}
+
+
+void FilmStripController::on_tnail_notification(const framework::Notification::Ptr &n)
+{
+	DBG_ASSERT(n->type() == niepce::NOTIFICATION_THUMBNAIL, "wrong notification type");
+	if(n->type() == niepce::NOTIFICATION_THUMBNAIL)	{
+		Glib::RefPtr<EogListStore> store 
+			= eog_thumb_view_get_model((EogThumbView*)(m_thumbview->gobj()));
+		library::ThumbnailNotification tn 
+			= boost::any_cast<library::ThumbnailNotification>(n->data());
+		Gtk::TreeRow row;
+		bool found = store->find_by_id(tn.id, row);
+		if(found) {
+			// FIXME parametrize
+			row[store->m_columns.m_thumbnail] = framework::gdkpixbuf_scale_to_fit(tn.pixmap, 100);
+		}
+		else {
+			DBG_OUT("row %d not found", tn.id);
+		}
+	}
+}
+#endif
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/ui/filmstripcontroller.h
==============================================================================
--- (empty file)
+++ trunk/src/ui/filmstripcontroller.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,63 @@
+/*
+ * niepce - ui/filmstripcontroller.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+#ifndef __UI_FILMSTRIPCONTROLLER_H_
+#define __UI_FILMSTRIPCONTROLLER_H_
+
+#include "framework/controller.h"
+#include "framework/notificationcenter.h"
+#include "ui/selectioncontroller.h"
+
+namespace Gtk {
+	class IconView;
+}
+
+namespace ui {
+
+
+class FilmStripController
+	: public framework::Controller,
+	  public IImageSelectable
+{
+public:
+	typedef boost::shared_ptr<FilmStripController> Ptr;
+	typedef boost::weak_ptr<FilmStripController> WeakPtr;
+
+	FilmStripController(const Glib::RefPtr<ImageListStore> & store);
+
+	virtual Gtk::IconView * image_list();
+	virtual int get_selected();
+	virtual void select_image(int id);
+
+//	void on_tnail_notification(const framework::Notification::Ptr &);
+//	void on_lib_notification(const framework::Notification::Ptr &);
+	
+protected:
+	virtual Gtk::Widget * buildWidget();
+private:
+	Gtk::IconView * m_thumbview;
+	Glib::RefPtr<ImageListStore> m_store;
+};
+
+
+}
+
+#endif

Added: trunk/src/ui/imageliststore.cpp
==============================================================================
--- (empty file)
+++ trunk/src/ui/imageliststore.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,165 @@
+/*
+ * niepce - ui/imageliststore.cpp
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtkmm/icontheme.h>
+
+#include "imageliststore.h"
+#include "utils/debug.h"
+#include "framework/application.h"
+#include "framework/gdkutils.h"
+#include "niepce/notifications.h"
+#include "db/library.h"
+#include "library/thumbnailnotification.h"
+#include "niepcewindow.h"
+
+namespace ui {
+
+Glib::RefPtr<ImageListStore> ImageListStore::create()
+{
+    static const Columns columns;
+    Glib::RefPtr<ImageListStore> p(new ImageListStore(columns));
+    return p;
+}
+
+ImageListStore::ImageListStore(const Columns& _columns)
+    : Gtk::ListStore(_columns),
+      m_columns(_columns)
+{
+}
+
+
+Gtk::TreeIter ImageListStore::get_iter_from_id(int id)
+{
+    std::map<int, Gtk::TreeIter>::iterator iter
+        = m_idmap.find( id );
+    if(iter != m_idmap.end()) {
+        return iter->second;
+    }
+    return Gtk::TreeIter();
+}
+
+Gtk::TreePath ImageListStore::get_path_from_id(int id)
+{
+    Gtk::TreeIter iter = get_iter_from_id(id);
+    if(iter) {
+        return get_path(iter);
+    }
+    return Gtk::TreePath();
+}
+
+
+void ImageListStore::on_lib_notification(const framework::Notification::Ptr &n)
+{
+    DBG_ASSERT(n->type() == niepce::NOTIFICATION_LIB, 
+               "wrong notification type");
+    if(n->type() == niepce::NOTIFICATION_LIB) {
+        db::LibNotification ln = boost::any_cast<db::LibNotification>(n->data());
+        switch(ln.type) {
+        case db::Library::NOTIFY_FOLDER_CONTENT_QUERIED:
+        case db::Library::NOTIFY_KEYWORD_CONTENT_QUERIED:
+        {
+            db::LibFile::ListPtr l 
+                = boost::any_cast<db::LibFile::ListPtr>(ln.param);
+            DBG_OUT("received folder content file # %d", l->size());
+            Glib::RefPtr< Gtk::IconTheme > icon_theme(framework::Application::app()->getIconTheme());
+            clear();
+            m_idmap.clear();
+            db::LibFile::List::const_iterator iter = l->begin();
+            for( ; iter != l->end(); iter++ )
+            {
+                Gtk::TreeModel::iterator riter = append();
+                Gtk::TreeRow row = *riter;
+                // locate it in local cache...
+                row[m_columns.m_pix] = icon_theme->load_icon(
+                    Glib::ustring("image-loading"), 32,
+                    Gtk::ICON_LOOKUP_USE_BUILTIN);
+                row[m_columns.m_libfile] = *iter;
+                row[m_columns.m_strip_thumb] = framework::gdkpixbuf_scale_to_fit(row[m_columns.m_pix], 100);
+                m_idmap[(*iter)->id()] = riter;
+            }
+            // at that point clear the cache because the icon view is populated.
+            getLibraryClient()->thumbnailCache().request(l);
+            break;
+        }
+        case db::Library::NOTIFY_METADATA_CHANGED:
+        {
+            boost::array<int, 3> m = boost::any_cast<boost::array<int, 3> >(ln.param);
+            DBG_OUT("metadata changed");
+            Gtk::TreeRow row;
+            std::map<int, Gtk::TreeIter>::const_iterator iter = m_idmap.find(m[0]);
+            if(iter != m_idmap.end()) {
+                row = *(iter->second);
+                //
+                db::LibFile::Ptr file = row[m_columns.m_libfile];
+                file->setMetaData(m[1], m[2]);
+                row[m_columns.m_libfile] = file;
+            }
+            break;
+        }
+        case db::Library::NOTIFY_XMP_NEEDS_UPDATE:
+        {
+            getLibraryClient()->processXmpUpdateQueue();
+            break;
+        }
+        default:
+            break;
+        }
+    }
+}
+
+void ImageListStore::on_tnail_notification(const framework::Notification::Ptr &n)
+{
+    DBG_ASSERT(n->type() == niepce::NOTIFICATION_THUMBNAIL, 
+               "wrong notification type");
+    if(n->type() == niepce::NOTIFICATION_THUMBNAIL) {
+        library::ThumbnailNotification tn 
+            = boost::any_cast<library::ThumbnailNotification>(n->data());
+        std::map<int, Gtk::TreeIter>::iterator iter
+            = m_idmap.find( tn.id );
+        if(iter != m_idmap.end()) {
+            // found the icon view item
+            Gtk::TreeRow row = *(iter->second);
+            row[m_columns.m_pix] = tn.pixmap;
+            row[m_columns.m_strip_thumb] = framework::gdkpixbuf_scale_to_fit(tn.pixmap, 100);
+        }
+        else {
+            DBG_OUT("row %d not found", tn.id);
+        }
+    }
+}
+
+libraryclient::LibraryClient::Ptr ImageListStore::getLibraryClient()
+{
+    return	boost::dynamic_pointer_cast<NiepceWindow>(m_controller.lock())->getLibraryClient();
+}
+
+}
+
+
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/ui/imageliststore.h
==============================================================================
--- (empty file)
+++ trunk/src/ui/imageliststore.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,95 @@
+/*
+ * niepce - ui/imageliststore.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+#ifndef __UI_IMAGELISTSTORE__
+#define __UI_IMAGELISTSTORE__
+
+
+#include <gdkmm/pixbuf.h>
+#include <gtkmm/liststore.h>
+
+#include "framework/notification.h"
+#include "framework/controller.h"
+#include "db/libfile.h"
+#include "libraryclient/libraryclient.h"
+
+namespace ui {
+
+/** @brief the general list store */
+class ImageListStore 
+    : public Gtk::ListStore
+{
+public:
+    class Columns 
+        : public Gtk::TreeModelColumnRecord
+    {
+    public:
+        enum {
+            THUMB_INDEX = 0,
+            FILE_INDEX = 1,
+            STRIP_THUMB_INDEX = 2
+        };
+        Columns()
+            { 
+                add(m_pix);
+                add(m_libfile);
+                add(m_strip_thumb);
+            }
+        Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf> > m_pix;
+        Gtk::TreeModelColumn<db::LibFile::Ptr> m_libfile;
+        Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf> > m_strip_thumb;
+    };
+
+    const Columns & columns() const
+        { return m_columns; }
+    Gtk::TreePath get_path_from_id(int id);
+    Gtk::TreeIter get_iter_from_id(int id);
+
+    static Glib::RefPtr<ImageListStore> create();
+
+    void set_parent_controller(const framework::Controller::WeakPtr & ctrl)
+        { m_controller = ctrl; }
+
+    void on_lib_notification(const framework::Notification::Ptr &n);
+    void on_tnail_notification(const framework::Notification::Ptr &n);
+protected:
+    ImageListStore(const Columns& columns);
+private:
+    libraryclient::LibraryClient::Ptr getLibraryClient();
+
+    const Columns           & m_columns;
+    std::map<int, Gtk::TreeIter> m_idmap;
+    framework::Controller::WeakPtr m_controller;
+};
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+
+#endif

Added: trunk/src/ui/importdialog.cpp
==============================================================================
--- (empty file)
+++ trunk/src/ui/importdialog.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,102 @@
+/*
+ * niepce - ui/importdialog.cpp
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <boost/bind.hpp>
+
+#include <glibmm/i18n.h>
+#include <gtkmm/filechooserdialog.h>
+#include <gtkmm/button.h>
+#include <gtkmm/stock.h>
+#include <gtkmm/checkbutton.h>
+#include <gtkmm/combobox.h>
+#include <gtkmm/label.h>
+
+#include "framework/configuration.h"
+#include "framework/application.h"
+#include "importdialog.h"
+
+using framework::Configuration;
+using framework::Application;
+
+namespace ui {
+
+ImportDialog::ImportDialog()
+	: m_date_tz_combo(NULL),
+	  m_ufraw_import_check(NULL),
+	  m_rawstudio_import_check(NULL),
+	  m_directory_name(NULL)
+{
+}
+
+
+Gtk::Widget * ImportDialog::buildWidget()
+{
+	Glib::RefPtr<Gnome::Glade::Xml> xml 
+		= Gnome::Glade::Xml::create(GLADEDIR"importdialog.glade");
+	Gtk::Dialog *dlg = NULL;
+	dlg = xml->get_widget("importDialog", dlg);
+
+	Gtk::Button *select_directories = xml->get_widget("select_directories",
+													  select_directories);
+	select_directories->signal_clicked().connect(
+			boost::bind(&ImportDialog::do_select_directories, this));
+	m_date_tz_combo = xml->get_widget("date_tz_combo", m_date_tz_combo);
+	m_ufraw_import_check = xml->get_widget("ufraw_import_check", 
+										   m_ufraw_import_check);
+	m_rawstudio_import_check = xml->get_widget("rawstudio_import_check",
+											   m_rawstudio_import_check);
+	m_directory_name = xml->get_widget("directory_name",
+									   m_directory_name);
+	
+	return dlg;
+}
+
+
+void ImportDialog::do_select_directories()
+{
+	Configuration & cfg = Application::app()->config();
+	
+	Gtk::FileChooserDialog dialog(gtkWindow(), _("Import picture folder"),
+								  Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
+	
+	dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+	dialog.add_button(_("Import"), Gtk::RESPONSE_OK);
+	
+	std::string last_import_location;
+	last_import_location = cfg.getValue("last_import_location", "");
+	if(!last_import_location.empty()) {
+		dialog.set_filename(last_import_location);
+	}
+	
+	int result = dialog.run();
+	switch(result)
+	{
+	case Gtk::RESPONSE_OK:
+		m_to_import = dialog.get_filename();
+		m_directory_name->set_label(m_to_import);
+		break;
+	default:
+		break;
+	}
+}
+
+
+}
+

Added: trunk/src/ui/importdialog.glade
==============================================================================
--- (empty file)
+++ trunk/src/ui/importdialog.glade	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,244 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--*- mode: xml -*-->
+<glade-interface>
+  <widget class="GtkDialog" id="importDialog">
+    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Import</property>
+    <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox2">
+        <property name="visible">True</property>
+        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+        <property name="spacing">2</property>
+        <child>
+          <widget class="GtkVBox" id="vbox2">
+            <property name="visible">True</property>
+            <property name="spacing">8</property>
+            <child>
+              <widget class="GtkHBox" id="hbox1">
+                <property name="visible">True</property>
+                <property name="spacing">8</property>
+                <child>
+                  <widget class="GtkLabel" id="label2">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">_Directory:</property>
+                    <property name="use_underline">True</property>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="directory_name">
+                    <property name="visible">True</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <property name="xalign">0</property>
+                  </widget>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkButton" id="select_directories">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <property name="label" translatable="yes">...</property>
+                    <property name="response_id">0</property>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label1">
+                <property name="visible">True</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">_Folders</property>
+                <property name="use_underline">True</property>
+                <property name="mnemonic_widget">folderList</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkScrolledWindow" id="scroller1">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+                <property name="shadow_type">GTK_SHADOW_IN</property>
+                <child>
+                  <widget class="GtkTreeView" id="folderList">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                  </widget>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkExpander" id="expander1">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="expanded">True</property>
+                <child>
+                  <widget class="GtkVBox" id="vbox3">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkCheckButton" id="ufraw_import_check">
+                        <property name="visible">True</property>
+                        <property name="sensitive">False</property>
+                        <property name="can_focus">True</property>
+                        <property name="label" translatable="yes">Import _UFRaw</property>
+                        <property name="use_underline">True</property>
+                        <property name="response_id">0</property>
+                        <property name="draw_indicator">True</property>
+                      </widget>
+                      <packing>
+                        <property name="fill">False</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkCheckButton" id="rawstudio_import_check">
+                        <property name="visible">True</property>
+                        <property name="sensitive">False</property>
+                        <property name="can_focus">True</property>
+                        <property name="label" translatable="yes">Import Raw_Studio</property>
+                        <property name="use_underline">True</property>
+                        <property name="response_id">0</property>
+                        <property name="draw_indicator">True</property>
+                      </widget>
+                      <packing>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkFrame" id="date_frame">
+                        <property name="visible">True</property>
+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                        <property name="label_xalign">0</property>
+                        <property name="shadow_type">GTK_SHADOW_IN</property>
+                        <child>
+                          <widget class="GtkVBox" id="vbox1">
+                            <property name="visible">True</property>
+                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                            <property name="spacing">4</property>
+                            <child>
+                              <widget class="GtkComboBox" id="date_tz_combo">
+                                <property name="visible">True</property>
+                                <property name="sensitive">False</property>
+                                <property name="active">0</property>
+                                <property name="items" translatable="yes">Date is local
+Date is UTC</property>
+                              </widget>
+                            </child>
+                            <child>
+                              <widget class="GtkLabel" id="label5">
+                                <property name="visible">True</property>
+                                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">You can still change this after importing the pictures.</property>
+                                <property name="wrap">True</property>
+                              </widget>
+                              <packing>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkLabel" id="label4">
+                            <property name="visible">True</property>
+                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                            <property name="label" translatable="yes">&lt;b&gt;Date:&lt;/b&gt;</property>
+                            <property name="use_markup">True</property>
+                          </widget>
+                          <packing>
+                            <property name="type">label_item</property>
+                          </packing>
+                        </child>
+                      </widget>
+                      <packing>
+                        <property name="padding">4</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="label3">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">_Options</property>
+                    <property name="use_underline">True</property>
+                  </widget>
+                  <packing>
+                    <property name="type">label_item</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">3</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area2">
+            <property name="visible">True</property>
+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+            <property name="layout_style">GTK_BUTTONBOX_END</property>
+            <child>
+              <widget class="GtkButton" id="button3">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="label" translatable="yes">gtk-cancel</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">1</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkButton" id="button4">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="label" translatable="yes">_Import</property>
+                <property name="use_underline">True</property>
+                <property name="response_id">0</property>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+</glade-interface>

Added: trunk/src/ui/importdialog.h
==============================================================================
--- (empty file)
+++ trunk/src/ui/importdialog.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,67 @@
+/*
+ * niepce - ui/importdialog.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+
+#ifndef __UI_IMPORTDIALOG_H__
+#define __UI_IMPORTDIALOG_H__
+
+#include <glibmm/refptr.h>
+#include <libglademm/xml.h>
+
+#include "framework/frame.h"
+
+namespace Gtk {
+	class Dialog;
+	class ComboBox;
+	class CheckButton;
+}
+
+namespace ui {
+
+class ImportDialog 
+	: public framework::Frame
+{
+public:
+	typedef boost::shared_ptr<ImportDialog> Ptr;
+
+	ImportDialog();
+
+ 	Gtk::Widget * buildWidget();
+
+	const Glib::ustring & to_import() const
+		{ return m_to_import; }
+
+private:
+	class ImportParam;
+
+	void do_select_directories();
+	
+	Glib::ustring m_to_import;
+	Gtk::ComboBox *m_date_tz_combo;
+	Gtk::CheckButton *m_ufraw_import_check;
+	Gtk::CheckButton *m_rawstudio_import_check;
+	Gtk::Label *m_directory_name;
+};
+
+}
+
+#endif
+

Added: trunk/src/ui/librarycellrenderer.cpp
==============================================================================
--- (empty file)
+++ trunk/src/ui/librarycellrenderer.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,270 @@
+/*
+ * niepce - ui/librarycellrenderer.cpp
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <gdkmm/general.h>
+
+#include "utils/debug.h"
+#include "librarycellrenderer.h"
+
+#ifndef DATADIR
+#error DATADIR is not defined
+#endif
+
+namespace ui {
+
+#define PAD 16
+
+LibraryCellRenderer::LibraryCellRenderer()
+	: Glib::ObjectBase(typeid(LibraryCellRenderer)),
+	  Gtk::CellRendererPixbuf(),
+      m_size(160),
+	  m_libfileproperty(*this, "libfile")
+{
+	try {
+        m_raw_format_emblem 
+            = Cairo::ImageSurface::create_from_png(
+                std::string(DATADIR"/niepce/pixmaps/niepce-raw-fmt.png"));
+        m_rawjpeg_format_emblem 
+            = Cairo::ImageSurface::create_from_png(
+                std::string(DATADIR"/niepce/pixmaps/niepce-rawjpeg-fmt.png"));
+        m_img_format_emblem 
+            = Cairo::ImageSurface::create_from_png(
+                std::string(DATADIR"/niepce/pixmaps/niepce-img-fmt.png"));     
+        m_video_format_emblem 
+            = Cairo::ImageSurface::create_from_png(
+                std::string(DATADIR"/niepce/pixmaps/niepce-video-fmt.png"));
+        m_unknown_format_emblem 
+            = Cairo::ImageSurface::create_from_png(
+                std::string(DATADIR"/niepce/pixmaps/niepce-unknown-fmt.png"));
+        
+        m_star = Cairo::ImageSurface::create_from_png(
+            std::string(DATADIR"/niepce/pixmaps/niepce-set-star.png"));
+        m_unstar = Cairo::ImageSurface::create_from_png(
+            std::string(DATADIR"/niepce/pixmaps/niepce-unset-star.png"));
+	}
+	catch(const std::exception & e)
+	{
+            ERR_OUT("exception while creating emblems: %s", e.what());
+            ERR_OUT("a - check if all the needed pixmaps are present in the filesystem");
+	}
+    catch(...)
+    {
+        ERR_OUT("uncatched exception");
+    }
+        
+}
+
+namespace {
+
+void drawThumbnail(const Cairo::RefPtr<Cairo::Context> & cr, 
+                   Glib::RefPtr<Gdk::Pixbuf> & pixbuf,
+                   const GdkRectangle & r)
+{
+    double x, y;
+    x = r.x + PAD;
+    y = r.y + PAD;
+    int w = pixbuf->get_width();
+    int h = pixbuf->get_height();
+    int min = std::min(w,h);
+    int max = std::max(w,h);
+    int offset = (max - min) / 2;
+    if(w > h) {
+        y += offset;
+    }
+    else {
+        x += offset;
+    }
+	
+// draw the shadow...
+//		cr->set_source_rgb(0.0, 0.0, 0.0);
+//		cr->rectangle(x + 3, y + 3, w, h);
+//		cr->fill();
+
+// draw the white border
+    cr->set_source_rgb(1.0, 1.0, 1.0);
+    cr->rectangle(x, y, w, h);
+    cr->stroke();
+		
+    Gdk::Cairo::set_source_pixbuf(cr, pixbuf, x, y);
+    cr->paint();
+}
+
+void drawFormatEmblem(const Cairo::RefPtr<Cairo::Context> & cr, 
+                      const Cairo::RefPtr<Cairo::ImageSurface> & emblem,
+                      const GdkRectangle & r)
+		
+{	
+    if(emblem) {
+        int w, h;
+        w = emblem->get_width();
+        h = emblem->get_height();
+        double x, y;
+        x = r.x + r.width - 4 - w;
+        y = r.y + r.height - 4 - h;
+        cr->set_source(emblem, x, y);
+        cr->paint();
+    }
+}
+
+void drawRating(const Cairo::RefPtr<Cairo::Context> & cr, 
+                int32_t rating,
+                const Cairo::RefPtr<Cairo::ImageSurface> & star,
+                const Cairo::RefPtr<Cairo::ImageSurface> & unstar,
+                const GdkRectangle & r)
+{
+    if(rating == -1 || !star || !unstar) {
+        return;
+    }
+    int w = star->get_width();
+    int h = star->get_height();
+    double x, y;
+    x = r.x + 4;
+    y = r.y + r.height - 4 - h;
+    for(int32_t i = 1; i <= 5; i++) {
+        if(i <= rating) {
+            cr->set_source(star, x, y);
+        }
+        else {
+            cr->set_source(unstar, x, y);
+        }
+        cr->paint();
+        x += w;
+    }
+}
+
+}
+
+
+void 
+LibraryCellRenderer::get_size_vfunc (Gtk::Widget& /*widget*/, 
+									 const Gdk::Rectangle* /*cell_area*/, 
+									 int* x_offset, int* y_offset, 
+									 int* width, int* height) const
+{
+	if(x_offset)
+		*x_offset = 0;
+	if(y_offset)
+		*y_offset = 0;
+
+	if(width || height) {
+		// TODO this should just be a property
+		//
+		Glib::RefPtr<Gdk::Pixbuf> pixbuf = property_pixbuf();
+		int maxdim = m_size + PAD * 2;
+		
+		if(width) 
+			*width = maxdim;
+		if(height) 
+			*height = maxdim;
+	}
+}
+
+
+void 
+LibraryCellRenderer::render_vfunc (const Glib::RefPtr<Gdk::Drawable>& window, 
+								   Gtk::Widget& widget, 
+								   const Gdk::Rectangle& /*background_area*/, 
+								   const Gdk::Rectangle& cell_area, 
+								   const Gdk::Rectangle& /*expose_area*/, 
+								   Gtk::CellRendererState flags)
+{
+	unsigned int xpad = Gtk::CellRenderer::property_xpad();
+	unsigned int ypad = Gtk::CellRenderer::property_ypad();
+	
+	Cairo::RefPtr<Cairo::Context> cr = window->create_cairo_context();
+	GdkRectangle r = *(cell_area.gobj());
+	r.x += xpad;
+	r.y += ypad;
+
+	db::LibFile::Ptr file = m_libfileproperty.get_value();
+
+	Gdk::Color color;
+	if(flags & Gtk::CELL_RENDERER_SELECTED) {
+		color = widget.get_style()->get_bg(Gtk::STATE_SELECTED);
+	}
+	else {
+		color = widget.get_style()->get_bg(Gtk::STATE_NORMAL);
+	}
+
+	Gdk::Cairo::set_source_color(cr, color);
+	cr->rectangle(r.x, r.y, r.width, r.height);
+	cr->fill();
+
+	color = widget.get_style()->get_dark(Gtk::STATE_SELECTED);
+	Gdk::Cairo::set_source_color(cr, color);
+	cr->set_line_width(1.0);
+	cr->rectangle(r.x, r.y, r.width, r.height);
+	cr->stroke();
+        
+
+    Cairo::RefPtr<Cairo::ImageSurface> emblem;
+    
+//    DBG_OUT("the filetype: %i", file->fileType());
+    
+    switch(file->fileType()) {
+    case db::LibFile::FILE_TYPE_RAW:
+        emblem = m_raw_format_emblem;
+        break;
+    case db::LibFile::FILE_TYPE_RAW_JPEG:
+        emblem = m_rawjpeg_format_emblem;
+        break;
+    case db::LibFile::FILE_TYPE_IMAGE:
+        emblem = m_img_format_emblem;
+        break;
+    case db::LibFile::FILE_TYPE_VIDEO:
+        emblem = m_video_format_emblem;
+        break;
+    default:
+        emblem = m_unknown_format_emblem;
+        break;
+    }
+
+	Glib::RefPtr<Gdk::Pixbuf> pixbuf = property_pixbuf();
+	drawThumbnail(cr, pixbuf, r);
+	drawRating(cr, file->rating(), m_star, m_unstar, r);
+	drawFormatEmblem(cr, emblem, r);
+}
+
+
+
+Glib::PropertyProxy_ReadOnly<db::LibFile::Ptr> 	
+LibraryCellRenderer::property_libfile() const
+{
+	return Glib::PropertyProxy_ReadOnly<db::LibFile::Ptr>(this, "libfile");
+}
+
+
+Glib::PropertyProxy<db::LibFile::Ptr> 	
+LibraryCellRenderer::property_libfile()
+{
+	return Glib::PropertyProxy<db::LibFile::Ptr>(this, "libfile");
+}
+
+
+}
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/

Added: trunk/src/ui/librarycellrenderer.h
==============================================================================
--- (empty file)
+++ trunk/src/ui/librarycellrenderer.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,74 @@
+/*
+ * niepce - ui/librarycellrenderer.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+#ifndef __UI_LIBRARY_CELL_RENDERER_H__
+#define __UI_LIBRARY_CELL_RENDERER_H__
+
+#include <gtkmm/cellrendererpixbuf.h>
+#include <cairomm/surface.h>
+
+#include "db/libfile.h"
+
+namespace ui {
+
+class LibraryCellRenderer
+	: public Gtk::CellRendererPixbuf
+{
+public:
+	LibraryCellRenderer();
+
+	virtual void 	get_size_vfunc (Gtk::Widget& widget, 
+									const Gdk::Rectangle* cell_area, 
+									int* x_offset, int* y_offset, 
+									int* width, int* height) const;
+	virtual void 	render_vfunc (const Glib::RefPtr<Gdk::Drawable>& window, 
+								  Gtk::Widget& widget, 
+								  const Gdk::Rectangle& background_area, 
+								  const Gdk::Rectangle& cell_area, 
+								  const Gdk::Rectangle& expose_area, 
+								  Gtk::CellRendererState flags);
+
+    void set_size(int _size)
+        { m_size = _size; }
+    int size() const
+        { return m_size; }
+
+	Glib::PropertyProxy_ReadOnly<db::LibFile::Ptr> 	property_libfile() const;
+	Glib::PropertyProxy<db::LibFile::Ptr> 	property_libfile();
+
+private:
+    int                                 m_size;
+	Glib::Property<db::LibFile::Ptr>    m_libfileproperty;
+
+	Cairo::RefPtr<Cairo::ImageSurface>  m_raw_format_emblem;
+	Cairo::RefPtr<Cairo::ImageSurface>  m_rawjpeg_format_emblem;
+	Cairo::RefPtr<Cairo::ImageSurface>  m_img_format_emblem;
+	Cairo::RefPtr<Cairo::ImageSurface>  m_video_format_emblem;
+	Cairo::RefPtr<Cairo::ImageSurface>  m_unknown_format_emblem;
+
+	Cairo::RefPtr<Cairo::ImageSurface>  m_star;
+	Cairo::RefPtr<Cairo::ImageSurface>  m_unstar;
+};
+
+
+}
+
+#endif

Added: trunk/src/ui/librarymainview.cpp
==============================================================================
--- (empty file)
+++ trunk/src/ui/librarymainview.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,89 @@
+/*
+ * niepce - ui/librarymainview.cpp
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtkmm/togglebutton.h>
+
+#include "utils/debug.h"
+#include "ui/librarymainview.h"
+
+namespace ui {
+
+	LibraryMainView::LibraryMainView()
+		: Gtk::VBox(),
+		  m_currentpage(-1)
+	{
+		set_spacing(4);
+		m_mainbar.set_layout(Gtk::BUTTONBOX_START);
+		m_mainbar.set_spacing(4);
+		m_notebook.set_show_tabs(false);
+		pack_start(m_mainbar, Gtk::PACK_SHRINK);
+		pack_start(m_notebook);
+	}
+
+	int
+	LibraryMainView::append_page(Gtk::Widget & w, const Glib::ustring & label)
+	{
+		int idx;
+		
+		Gtk::ToggleButton* button = Gtk::manage(new Gtk::ToggleButton(label));
+		m_mainbar.pack_start(*button);
+		idx = m_notebook.append_page(w, label);
+		sigc::connection conn = button->signal_toggled().connect(
+			sigc::bind(sigc::mem_fun(this, &LibraryMainView::set_current_page),
+					   idx, button));
+		if(m_currentpage == -1) {
+			set_current_page(idx, button);
+		}
+		if((int)m_buttons.size() < idx + 1) {
+			m_buttons.resize(idx + 1);
+		}
+		m_buttons[idx] = std::make_pair(button, conn);
+		return idx;
+	}
+	
+	void LibraryMainView::activate_page(int idx)
+	{
+		if(m_currentpage != idx) {
+			Gtk::ToggleButton * btn = m_buttons[idx].first;
+			set_current_page(idx, btn);
+		}
+	}
+
+
+	void LibraryMainView::set_current_page(int idx, Gtk::ToggleButton * btn)
+	{
+        if(m_currentpage == idx) {
+            // just preempt. Make sure the button is still active.
+            // otherwise it cause an infinite loop.
+			m_buttons[m_currentpage].second.block();
+			m_buttons[m_currentpage].first->set_active(true);
+			m_buttons[m_currentpage].second.unblock();
+            return;
+        }
+		m_notebook.set_current_page(idx);
+		if(m_currentpage >= 0) {
+			m_buttons[m_currentpage].second.block();
+			m_buttons[m_currentpage].first->set_active(false);
+			m_buttons[m_currentpage].second.unblock();
+		}
+		btn->set_active(true);
+		m_currentpage = idx;
+	}
+}
+

Added: trunk/src/ui/librarymainview.h
==============================================================================
--- (empty file)
+++ trunk/src/ui/librarymainview.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,58 @@
+/*
+ * niepce - ui/librarymainview.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIBRARY_MAIN_VIEW_H__
+#define __LIBRARY_MAIN_VIEW_H__
+
+#include <vector>
+#include <utility>
+
+#include <gtkmm/notebook.h>
+#include <gtkmm/box.h>
+#include <gtkmm/buttonbox.h>
+
+namespace Gtk {
+	class ToggleButton;
+}
+
+namespace ui {
+
+
+	class LibraryMainView
+		: public Gtk::VBox
+	{
+	public:
+		LibraryMainView();
+
+		int append_page(Gtk::Widget & w, const Glib::ustring & label);
+		void activate_page(int);
+	protected:
+		
+		void set_current_page(int, Gtk::ToggleButton *);
+	private:
+		Gtk::HButtonBox         m_mainbar;
+		Gtk::Notebook           m_notebook;
+		int                     m_currentpage;
+		std::vector<std::pair<Gtk::ToggleButton*, sigc::connection> > m_buttons;
+	};
+
+}
+
+
+#endif

Added: trunk/src/ui/librarymainviewcontroller.cpp
==============================================================================
--- (empty file)
+++ trunk/src/ui/librarymainviewcontroller.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,202 @@
+/*
+ * niepce - ui/librarymainviewcontroller.cpp
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <gtk/gtkcelllayout.h>
+
+#include <glibmm/i18n.h>
+#include <glibmm/ustring.h>
+
+#include <gtkmm/celllayout.h>
+#include <gtkmm/cellrenderer.h>
+
+#include "utils/debug.h"
+#include "niepce/notifications.h"
+#include "db/library.h"
+#include "framework/application.h"
+#include "framework/widgets/dock.h"
+#include "librarymainviewcontroller.h"
+#include "niepcewindow.h"
+#include "metadatapanecontroller.h"
+#include "librarycellrenderer.h"
+
+namespace ui {
+
+void LibraryMainViewController::on_lib_notification(const framework::Notification::Ptr &n)
+{
+    DBG_ASSERT(n->type() == niepce::NOTIFICATION_LIB, 
+               "wrong notification type");
+    if(n->type() == niepce::NOTIFICATION_LIB) {
+        db::LibNotification ln = boost::any_cast<db::LibNotification>(n->data());
+        switch(ln.type) {
+        case db::Library::NOTIFY_METADATA_QUERIED:
+        {
+            db::LibMetadata::Ptr lm
+                = boost::any_cast<db::LibMetadata::Ptr>(ln.param);
+            DBG_OUT("received metadata");
+            m_metapanecontroller->display(lm->id(), lm.get());
+            break;
+        }
+        case db::Library::NOTIFY_METADATA_CHANGED:
+        {
+            DBG_OUT("metadata changed");
+            boost::array<int, 3> m = boost::any_cast<boost::array<int, 3> >(ln.param);
+            if(m[0] == m_metapanecontroller->displayed_file()) {
+                // FIXME: actually just update the metadata
+                getLibraryClient()->requestMetadata(m[0]);
+            }
+            break;
+        }
+        default:
+            break;
+        }
+    }
+}
+
+
+
+Gtk::Widget * LibraryMainViewController::buildWidget()
+{
+    m_librarylistview.set_model(m_model);
+    m_librarylistview.set_selection_mode(Gtk::SELECTION_SINGLE);
+    m_librarylistview.property_row_spacing() = 0;
+    m_librarylistview.property_column_spacing() = 0;
+    m_librarylistview.property_spacing() = 0;
+
+    // the main cell
+    LibraryCellRenderer * libcell = Gtk::manage(new LibraryCellRenderer());
+
+    GtkCellLayout *cl = GTK_CELL_LAYOUT(m_librarylistview.gobj());
+    DBG_ASSERT(cl, "No cell layout");
+    gtk_cell_layout_pack_start(cl, GTK_CELL_RENDERER(libcell->gobj()), 
+                               FALSE);
+    gtk_cell_layout_add_attribute(cl, 
+                                  GTK_CELL_RENDERER(libcell->gobj()),
+                                  "pixbuf", m_model->columns().m_pix.index());
+    gtk_cell_layout_add_attribute(cl,
+                                  GTK_CELL_RENDERER(libcell->gobj()),
+                                  "libfile", m_model->columns().m_libfile.index());
+
+    m_scrollview.add(m_librarylistview);
+    m_scrollview.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+    m_lib_splitview.pack1(m_scrollview);
+    m_dock = new framework::Dock();
+    m_metapanecontroller = MetaDataPaneController::Ptr(new MetaDataPaneController(*m_dock));
+    add(m_metapanecontroller);
+    m_lib_splitview.pack2(m_dock->getWidget());
+    (void)m_metapanecontroller->buildWidget();
+
+    m_databinders.add_binder(new framework::ConfigDataBinder<int>(
+                                 m_lib_splitview.property_position(),
+                                 framework::Application::app()->config(),
+                                 "meta_pane_splitter"));
+		
+    m_mainview.append_page(m_lib_splitview, _("Library"));
+
+
+    m_darkroom = darkroom::DarkroomModule::Ptr(
+        new darkroom::DarkroomModule(m_actionGroup, getLibraryClient()));
+    add(m_darkroom);
+    m_mainview.append_page(*m_darkroom->widget(), _("Darkroom"));
+
+    // TODO PrintModuleController
+    // m_mainview.append_page(, _("Print"));
+    return &m_mainview;
+}
+
+
+void LibraryMainViewController::on_ready()
+{
+}
+
+
+void LibraryMainViewController::on_selected(int id)
+{
+    DBG_OUT("selected callback %d", id);
+    if(id > 0) {
+        getLibraryClient()->requestMetadata(id);
+    }		
+    else  {
+        m_metapanecontroller->display(0, NULL);
+    }
+}
+
+void LibraryMainViewController::on_image_activated(int id)
+{
+    DBG_OUT("on image activated %d", id);
+    Gtk::TreeIter iter = m_model->get_iter_from_id(id);
+    if(iter) {
+        db::LibFile::Ptr libfile = (*iter)[m_model->columns().m_libfile];
+        m_darkroom->set_image(libfile);
+        m_mainview.activate_page(1);
+    }
+}
+	
+
+libraryclient::LibraryClient::Ptr LibraryMainViewController::getLibraryClient()
+{
+    return	boost::dynamic_pointer_cast<NiepceWindow>(m_parent.lock())->getLibraryClient();
+}
+
+
+Gtk::IconView * LibraryMainViewController::image_list()
+{ 
+    return & m_librarylistview; 
+}
+
+int LibraryMainViewController::get_selected()
+{
+    int id = 0;
+    Glib::RefPtr<Gtk::TreeSelection> selection;
+
+    Gtk::IconView::ArrayHandle_TreePaths paths = m_librarylistview.get_selected_items();
+    if(!paths.empty()) {
+        Gtk::TreePath path(*(paths.begin()));
+        DBG_OUT("found path %s", path.to_string().c_str());
+        Gtk::TreeRow row = *(m_model->get_iter(path));
+        if(row) {
+            DBG_OUT("found row");
+            db::LibFile::Ptr libfile = row[m_model->columns().m_libfile];
+            if(libfile) {
+                id = libfile->id();
+            }
+        }
+    }
+    DBG_OUT("get_selected %d", id);
+    return id;
+}
+
+void LibraryMainViewController::select_image(int id)
+{
+    DBG_OUT("library select %d", id);
+    Gtk::TreePath path = m_model->get_path_from_id(id);
+    m_librarylistview.select_path(path);
+}
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/

Added: trunk/src/ui/librarymainviewcontroller.h
==============================================================================
--- (empty file)
+++ trunk/src/ui/librarymainviewcontroller.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,107 @@
+/*
+ * niepce - ui/librarymainviewcontroller.h
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __UI_LIBRARYMAINVIEWCONTROLLER_H__
+#define __UI_LIBRARYMAINVIEWCONTROLLER_H__
+
+
+#include <gtkmm/iconview.h>
+#include <gtkmm/liststore.h>
+#include <gtkmm/treestore.h>
+#include <gtkmm/scrolledwindow.h>
+#include <gtkmm/paned.h>
+
+#include "librarymainview.h"
+#include "db/libfile.h"
+#include "libraryclient/libraryclient.h"
+#include "framework/controller.h"
+#include "framework/notification.h"
+#include "metadatapanecontroller.h"
+#include "selectioncontroller.h"
+#include "modules/darkroom/darkroommodule.h"
+#include "imageliststore.h"
+
+namespace Gtk {
+	class Widget;
+}
+namespace framework {
+class Dock;
+}
+
+namespace ui {
+
+	class LibraryMainViewController
+		: public framework::Controller,
+		  public IImageSelectable
+	{
+	public:
+		typedef boost::shared_ptr<LibraryMainViewController> Ptr;
+		typedef boost::weak_ptr<LibraryMainViewController> WeakPtr;
+
+		LibraryMainViewController(const Glib::RefPtr<Gtk::ActionGroup> & actions,
+								  const Glib::RefPtr<ImageListStore> & store)
+			: m_actionGroup(actions),
+			  m_model(store)
+			{
+			}
+
+		void on_lib_notification(const framework::Notification::Ptr &);
+
+		/** called when somehing is selected by the shared selection */
+		void on_selected(int id);
+		void on_image_activated(int id);
+
+		virtual Gtk::IconView * image_list();
+		virtual int get_selected();
+		virtual void select_image(int id);
+	protected:
+		virtual Gtk::Widget * buildWidget();
+		virtual void on_ready();
+	private:
+		libraryclient::LibraryClient::Ptr getLibraryClient();
+		Glib::RefPtr<Gtk::ActionGroup> m_actionGroup;
+
+		// managed widgets...
+		LibraryMainView              m_mainview;
+		Gtk::IconView                m_librarylistview;
+		Gtk::ScrolledWindow          m_scrollview;
+		// library split view
+		Gtk::HPaned                  m_lib_splitview;
+        framework::Dock              *m_dock;
+		MetaDataPaneController::Ptr  m_metapanecontroller;
+
+        darkroom::DarkroomModule::Ptr m_darkroom;
+
+		Glib::RefPtr<ImageListStore> m_model;
+	};
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/
+
+#endif

Added: trunk/src/ui/metadatapanecontroller.cpp
==============================================================================
--- (empty file)
+++ trunk/src/ui/metadatapanecontroller.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,126 @@
+/*
+ * niepce - ui/metadatapanecontroller.cpp
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <boost/bind.hpp>
+
+#include <glibmm/i18n.h>
+#include <gtkmm/label.h>
+#include <gtkmm/entry.h>
+#include <gtkmm/stock.h>
+
+#include <exempi/xmpconsts.h>
+
+#include "utils/debug.h"
+#include "utils/exempi.h"
+#include "framework/metadatawidget.h"
+#include "metadatapanecontroller.h"
+
+using namespace xmp;
+
+namespace ui {
+
+	
+	const MetaDataSectionFormat *
+	MetaDataPaneController::get_format() 
+	{
+		static const MetaDataFormat s_camerainfo_format[] = {
+			{ _("Make:"), NS_TIFF, "Make", META_DT_STRING, true },
+			{ _("Model:"), NS_TIFF, "Model", META_DT_STRING, true },
+			{ _("Lens:"), NS_EXIF_AUX, "Lens", META_DT_STRING, true },
+			{ NULL, NULL, NULL, META_DT_NONE, true }
+		};
+		static const MetaDataFormat s_shootinginfo_format[] = {
+			{ _("Exposure Program:"), NS_EXIF, "ExposureProgram", META_DT_STRING, true },
+			{ _("Speed:"), NS_EXIF, "ExposureTime", META_DT_STRING, true },
+			{ _("Aperture:"), NS_EXIF, "FNumber", META_DT_STRING, true },
+			{ _("ISO:"), NS_EXIF, "ISOSpeedRatings", META_DT_STRING_ARRAY, true },
+			{ _("Exposure Bias:"), NS_EXIF, "ExposureBiasValue", META_DT_STRING, true },
+			// this one is fishy as it hardcode the prefix.
+			{ _("Flash:"), NS_EXIF, "Flash/exif:Fired", META_DT_STRING, true },
+			{ _("Flash compensation:"), NS_EXIF_AUX, "FlashCompensation", META_DT_STRING, true },
+			{ _("Focal length:"), NS_EXIF, "FocalLength", META_DT_STRING, true },
+			{ _("White balance:"), NS_EXIF, "WhiteBalance", META_DT_STRING, true },
+			{ _("Date:"), NS_EXIF, "DateTimeOriginal", META_DT_DATE, false },
+			{ NULL, NULL, NULL, META_DT_NONE, true }
+		};
+		static const MetaDataFormat s_iptc_format[] = {
+			{ _("Rating:"), NS_XAP, "Rating", META_DT_STAR_RATING, false },
+			{ _("Label:"), NS_XAP, "Label", META_DT_STRING, false },            
+			{ _("Keywords:"), NS_DC, "subject", META_DT_STRING_ARRAY, false },
+			{ NULL, NULL, NULL, META_DT_NONE, true }			
+		};
+		static const MetaDataSectionFormat s_format[] = {
+			{ _("Camera Information"),
+			  s_camerainfo_format
+			},
+			{ _("Shooting Information"),
+			  s_shootinginfo_format
+			},
+			{ _("IPTC"),
+			  s_iptc_format
+			},
+			{ _("Rights"),
+			  NULL
+			},
+			{ NULL, NULL
+			}
+		};
+		return s_format;
+	}
+
+	MetaDataPaneController::MetaDataPaneController(framework::Dock &dock)
+		: Dockable(dock, "Metadata", _("Image Properties"), 
+                   Gtk::Stock::PROPERTIES.id, DockItem::DOCKED_STATE),
+          m_fileid(0)
+	{
+	}
+
+    MetaDataPaneController::~MetaDataPaneController()
+    {
+    }
+	
+	Gtk::Widget * MetaDataPaneController::buildWidget()
+	{
+		m_widget = &DockItem::getWidget();
+
+		const MetaDataSectionFormat * formats = get_format();
+		
+		const MetaDataSectionFormat * current = formats;
+		while(current->section) {
+			framework::MetaDataWidget *w = Gtk::manage(new framework::MetaDataWidget(current->section));
+            DockItem::get_vbox()->pack_start(*w, Gtk::PACK_SHRINK, 0);
+			w->set_data_format(current);
+			m_widgets.push_back(w);
+			current++;
+		}
+
+		return m_widget;
+	}
+
+
+	void MetaDataPaneController::display(int file_id, const utils::XmpMeta * meta)
+	{
+        m_fileid = file_id;
+		DBG_OUT("displaying metadata");
+		std::for_each(m_widgets.begin(), m_widgets.end(),
+					  boost::bind(&framework::MetaDataWidget::set_data_source,
+								  _1, meta));
+	}
+
+}

Added: trunk/src/ui/metadatapanecontroller.h
==============================================================================
--- (empty file)
+++ trunk/src/ui/metadatapanecontroller.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,70 @@
+/*
+ * niepce - ui/metadatapanecontroller.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UI_METADATAPANECONTROLLER_H__
+#define __UI_METADATAPANECONTROLLER_H__
+
+#include <gtkmm/box.h>
+
+#include "utils/exempi.h"
+#include "framework/dockable.h"
+
+namespace xmp {
+	struct MetaDataSectionFormat;
+}
+namespace framework {
+	class MetaDataWidget;
+    class Dock;
+}
+
+namespace ui {
+	
+	class MetaDataPaneController
+		: public framework::Dockable
+	{
+	public:
+		typedef boost::shared_ptr<MetaDataPaneController> Ptr;
+		MetaDataPaneController(framework::Dock &);
+        ~MetaDataPaneController();
+		virtual Gtk::Widget * buildWidget();
+		void display(int file_id, const utils::XmpMeta * meta);
+        int displayed_file() const 
+            { return m_fileid; }
+	private:
+		std::vector<framework::MetaDataWidget *> m_widgets;
+
+		static const xmp::MetaDataSectionFormat * get_format();
+
+        int m_fileid;
+	};
+
+}
+
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/
+#endif

Added: trunk/src/ui/niepceapplication.cpp
==============================================================================
--- (empty file)
+++ trunk/src/ui/niepceapplication.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,80 @@
+/*
+ * niepce - ui/niepceapplication.cpp
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glibmm/i18n.h>
+#include <gtkmm/aboutdialog.h>
+
+#include "niepce/stock.h"
+#include "niepceapplication.h"
+#include "niepcewindow.h"
+
+using framework::Frame;
+using framework::Application;
+
+namespace ui {
+
+NiepceApplication::NiepceApplication()
+    : Application(PACKAGE)
+{
+    niepce::Stock::registerStockItems();
+    const char * themedir = DATADIR"/niepce/themes/";
+
+    register_theme(_("Niepce Dark"),
+                   std::string(themedir) + "niepce-dark.gtkrc");
+}
+
+Application::Ptr NiepceApplication::create()
+{
+    if (!m_application) {
+        m_application = Application::Ptr(new NiepceApplication());
+    }
+    return m_application;
+}
+
+
+Frame::Ptr NiepceApplication::makeMainFrame()
+{
+    return Frame::Ptr(new NiepceWindow);
+}
+
+void NiepceApplication::on_about()
+{
+    Gtk::AboutDialog dlg;
+//    dlg.set_name("Niepce");
+    dlg.set_program_name("Niepce Digital");
+    dlg.set_version(VERSION);
+    dlg.set_comments(Glib::ustring(_("A digital photo application.\n\nBuild options: " 
+                                     NIEPCE_BUILD_CONFIG)));
+    dlg.run();
+}
+
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/ui/niepceapplication.h
==============================================================================
--- (empty file)
+++ trunk/src/ui/niepceapplication.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,52 @@
+#ifndef _UI_NIEPCEAPPLICATION_H_
+#define _UI_NIEPCEAPPLICATION_H_
+
+/*
+ * niepce - ui/niepceapplication.h
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "framework/application.h"
+
+namespace ui {
+
+	class NiepceApplication
+		: public framework::Application
+	{
+	public:
+		static framework::Application::Ptr create();
+
+		virtual framework::Frame::Ptr makeMainFrame();
+	protected:
+		NiepceApplication();
+		virtual void on_about();
+	};
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+
+#endif

Added: trunk/src/ui/niepcewindow.cpp
==============================================================================
--- (empty file)
+++ trunk/src/ui/niepcewindow.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,559 @@
+/*
+ * niepce - ui/niepcewindow.cpp
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <iostream>
+#include <string>
+#include <boost/bind.hpp>
+
+#include <glibmm/i18n.h>
+#include <gtkmm/window.h>
+#include <gtkmm/action.h>
+#include <gtkmm/box.h>
+#include <gtkmm/stock.h>
+#include <gtkmm/separator.h>
+#include <gtkmm/filechooserdialog.h>
+#include <gtkmm/combobox.h>
+
+#include "niepce/notifications.h"
+#include "niepce/stock.h"
+#include "utils/debug.h"
+#include "utils/moniker.h"
+#include "utils/boost.h"
+#include "db/library.h"
+#include "libraryclient/libraryclient.h"
+#include "framework/application.h"
+#include "framework/configuration.h"
+#include "framework/notificationcenter.h"
+#include "framework/configdatabinder.h"
+#include "framework/undo.h"
+
+#include "eog-thumb-view.h"
+#include "niepcewindow.h"
+#include "librarymainviewcontroller.h"
+#include "importdialog.h"
+#include "selectioncontroller.h"
+
+using libraryclient::LibraryClient;
+using framework::Application;
+using framework::Configuration;
+using framework::NotificationCenter;
+using framework::UndoHistory;
+
+namespace ui {
+
+
+NiepceWindow::NiepceWindow()
+    : framework::Frame("mainWindow-frame")
+{
+}
+
+
+NiepceWindow::~NiepceWindow()
+{
+    Application::Ptr pApp = Application::app();
+    pApp->uiManager()->remove_action_group(m_refActionGroup);
+}
+
+Gtk::Widget * 
+NiepceWindow::buildWidget()
+{
+    Gtk::Window & win(gtkWindow());
+
+    Application::Ptr pApp = Application::app();
+
+    m_selection_controller = SelectionController::Ptr(new SelectionController);
+    add(m_selection_controller);
+
+    init_actions();
+    init_ui();
+
+    m_lib_notifcenter.reset(new NotificationCenter());
+
+    Glib::ustring name("camera");
+    set_icon_from_theme(name);		
+
+    m_lib_notifcenter->subscribe(niepce::NOTIFICATION_LIB,
+                                 boost::bind(&ImageListStore::on_lib_notification, 
+                                             m_selection_controller->list_store(), _1));
+    m_lib_notifcenter->subscribe(niepce::NOTIFICATION_THUMBNAIL,
+                                 boost::bind(&ImageListStore::on_tnail_notification, 
+                                             m_selection_controller->list_store(), _1));
+
+    // main view
+    m_mainviewctrl = LibraryMainViewController::Ptr(
+        new LibraryMainViewController(m_refActionGroup,
+                                      m_selection_controller->list_store()));
+    m_lib_notifcenter->subscribe(niepce::NOTIFICATION_LIB,
+                                 boost::bind(&LibraryMainViewController::on_lib_notification, 
+                                             BIND_SHARED_PTR(LibraryMainViewController, m_mainviewctrl)
+                                             , _1));
+    add(m_mainviewctrl);
+    // workspace treeview
+    m_workspacectrl = WorkspaceController::Ptr( new WorkspaceController() );
+    m_lib_notifcenter->subscribe(niepce::NOTIFICATION_LIB,
+                                 boost::bind(&WorkspaceController::on_lib_notification, 
+                                             m_workspacectrl, _1));
+    m_lib_notifcenter->subscribe(niepce::NOTIFICATION_COUNT,
+                                 boost::bind(&WorkspaceController::on_count_notification,
+                                             m_workspacectrl, _1));
+    add(m_workspacectrl);
+
+    m_hbox.set_border_width(4);
+    m_hbox.pack1(*(m_workspacectrl->widget()), Gtk::EXPAND);
+    m_hbox.pack2(*(m_mainviewctrl->widget()), Gtk::EXPAND);
+    m_databinders.add_binder(new framework::ConfigDataBinder<int>(m_hbox.property_position(),
+                                                                  Application::app()->config(),
+                                                                  "workspace_splitter"));
+
+    win.add(m_vbox);
+
+    Gtk::Widget* pMenuBar = pApp->uiManager()->get_widget("/MenuBar");
+    m_vbox.pack_start(*pMenuBar, Gtk::PACK_SHRINK);
+    m_vbox.pack_start(m_hbox);
+
+
+    m_filmstrip = FilmStripController::Ptr(new FilmStripController(m_selection_controller->list_store()));
+    add(m_filmstrip);
+
+    m_vbox.pack_start(*(m_filmstrip->widget()), Gtk::PACK_SHRINK);
+
+    // status bar
+    m_vbox.pack_start(m_statusBar, Gtk::PACK_SHRINK);
+    m_statusBar.push(Glib::ustring(_("Ready")));
+
+    m_selection_controller->add_selectable(m_filmstrip.get());
+    m_selection_controller->add_selectable(m_mainviewctrl.get());
+    m_selection_controller->signal_selected
+        .connect(boost::bind(&LibraryMainViewController::on_selected,
+                             BIND_SHARED_PTR(LibraryMainViewController, m_mainviewctrl)
+                             , _1));
+
+    m_selection_controller->signal_activated
+        .connect(boost::bind(&LibraryMainViewController::on_image_activated,
+                             BIND_SHARED_PTR(LibraryMainViewController, m_mainviewctrl)
+                             , _1));
+
+    win.set_size_request(600, 400);
+    win.show_all_children();
+    on_open_library();
+    return &win;
+}
+
+
+void NiepceWindow::init_ui()
+{
+    Application::Ptr pApp = Application::app();
+    Glib::ustring ui_info =
+        "<ui>"
+        "  <menubar name='MenuBar'>"
+        "    <menu action='MenuLibrary'>"
+        "      <menuitem action='NewLibrary' />"
+        "      <menuitem action='OpenLibrary' />"
+        "      <separator/>"
+        "      <menuitem action='NewFolder'/>"
+        "      <menuitem action='NewProject'/>"
+        "      <menuitem action='Import'/>"
+        "      <separator/>"
+        "      <menuitem action='Close'/>"
+        "      <menuitem action='Quit'/>"
+        "    </menu>"
+        "    <menu action='MenuEdit'>"
+        "      <menuitem action='Undo'/>"
+        "      <menuitem action='Redo'/>"
+        "      <separator/>"
+        "      <menuitem action='Cut'/>"
+        "      <menuitem action='Copy'/>"
+        "      <menuitem action='Paste'/>"
+        "      <menuitem action='Delete'/>"
+        "      <separator/>"
+        "      <menuitem action='Preferences'/>"
+        "    </menu>"
+        "    <menu action='MenuImage'>"
+        "      <menuitem action='PrevImage'/>"
+        "      <menuitem action='NextImage'/>"
+        "      <separator/>"
+        "      <menuitem action='RotateLeft'/>"
+        "      <menuitem action='RotateRight'/>"			
+        "      <separator/>"
+        "      <menu action='SetRating'>"
+        "        <menuitem action='SetRating0'/>"
+        "        <menuitem action='SetRating1'/>"
+        "        <menuitem action='SetRating2'/>"
+        "        <menuitem action='SetRating3'/>"
+        "        <menuitem action='SetRating4'/>"
+        "        <menuitem action='SetRating5'/>"
+        "      </menu>"
+        "      <menu action='SetLabel'>"
+        "        <menuitem action='SetLabel6'/>"
+        "        <menuitem action='SetLabel7'/>"
+        "        <menuitem action='SetLabel8'/>"
+        "        <menuitem action='SetLabel9'/>"
+        "      </menu>"
+        "      <separator/>"
+        "      <menuitem action='DeleteImage'/>"
+        "    </menu>"
+        "    <menu action='MenuTools'>"
+        "      <menuitem action='ToggleToolsVisible'/>"
+        "      <separator/>"        
+        "    </menu>"
+        "    <menu action='MenuHelp'>"
+        "      <menuitem action='Help'/>"
+        "      <menuitem action='About'/>"
+        "    </menu>"
+        "  </menubar>"
+        "  <toolbar  name='ToolBar'>"
+        "    <toolitem action='Import'/>"
+        "    <toolitem action='Quit'/>"
+        "  </toolbar>"
+        "</ui>";
+    pApp->uiManager()->add_ui_from_string(ui_info);
+} 
+
+
+
+void NiepceWindow::init_actions()
+{
+    Glib::RefPtr<Gtk::Action> an_action;
+
+    m_refActionGroup = Gtk::ActionGroup::create();
+		
+    m_refActionGroup->add(Gtk::Action::create("MenuLibrary", _("_Library")));
+    m_refActionGroup->add(Gtk::Action::create("NewLibrary", Gtk::Stock::NEW));
+    m_refActionGroup->add(Gtk::Action::create("OpenLibrary", Gtk::Stock::OPEN),
+                          boost::bind(&NiepceWindow::on_action_file_open,
+                                      this));
+
+    m_refActionGroup->add(Gtk::Action::create("NewFolder", _("New _Folder...")));
+    m_refActionGroup->add(Gtk::Action::create("NewProject", _("New _Project...")));
+
+    m_refActionGroup->add(Gtk::Action::create("Import", _("_Import...")),
+                          sigc::mem_fun(this, 
+                                        &NiepceWindow::on_action_file_import));
+    m_refActionGroup->add(Gtk::Action::create("Close", Gtk::Stock::CLOSE),
+                          sigc::mem_fun(gtkWindow(), 
+                                        &Gtk::Window::hide));			
+    m_refActionGroup->add(Gtk::Action::create("Quit", Gtk::Stock::QUIT),
+                          sigc::mem_fun(*Application::app(), 
+                                        &Application::quit));	
+
+    m_refActionGroup->add(Gtk::Action::create("MenuEdit", _("_Edit")));
+    Glib::RefPtr<Gtk::Action> undo_action
+        = Gtk::Action::create("Undo", Gtk::Stock::UNDO);
+    m_refActionGroup->add(undo_action, Gtk::AccelKey("<control>Z"),
+                          boost::bind(&UndoHistory::undo,
+                                      boost::ref(Application::app()->undo_history())));
+    m_undostate_conn = Application::app()->undo_history().changed.connect(
+        boost::bind(&NiepceWindow::undo_state, this, undo_action));
+    undo_state(undo_action);
+    Glib::RefPtr<Gtk::Action> redo_action
+        = Gtk::Action::create("Redo", Gtk::Stock::REDO);
+    m_refActionGroup->add(redo_action, Gtk::AccelKey("<control><shift>Z"),
+                          boost::bind(&UndoHistory::redo,
+                                      boost::ref(Application::app()->undo_history())));
+    m_redostate_conn = Application::app()->undo_history().changed.connect(
+        boost::bind(&NiepceWindow::redo_state, this, redo_action));
+    redo_state(redo_action);
+
+    // FIXME: bind
+    m_refActionGroup->add(Gtk::Action::create("Cut", Gtk::Stock::CUT));
+    m_refActionGroup->add(Gtk::Action::create("Copy", Gtk::Stock::COPY));
+    m_refActionGroup->add(Gtk::Action::create("Paste", Gtk::Stock::PASTE));
+    m_refActionGroup->add(Gtk::Action::create("Delete", Gtk::Stock::DELETE));
+
+    m_refActionGroup->add(Gtk::Action::create("Preferences", 
+                                              Gtk::Stock::PREFERENCES),
+                          sigc::mem_fun(this,
+                                        &NiepceWindow::on_preferences));
+
+    m_refActionGroup->add(Gtk::Action::create("MenuImage", _("_Image")));
+
+    m_refActionGroup->add(Gtk::Action::create("PrevImage", Gtk::Stock::GO_BACK),
+                          Gtk::AccelKey(GDK_Left, Gdk::ModifierType(0)),
+                          boost::bind(&SelectionController::select_previous, 
+                                      BIND_SHARED_PTR(SelectionController, m_selection_controller)));
+    m_refActionGroup->add(Gtk::Action::create("NextImage", Gtk::Stock::GO_FORWARD),
+                          Gtk::AccelKey(GDK_Right, Gdk::ModifierType(0)),
+                          boost::bind(&SelectionController::select_next, 
+                                      BIND_SHARED_PTR(SelectionController, m_selection_controller)));
+    
+    an_action = Gtk::Action::create("RotateLeft", niepce::Stock::ROTATE_LEFT);
+    m_refActionGroup->add(an_action, boost::bind(&SelectionController::rotate, 
+                                                 BIND_SHARED_PTR(SelectionController, m_selection_controller)
+                                                 , -90));
+    an_action = Gtk::Action::create("RotateRight", niepce::Stock::ROTATE_RIGHT);
+    m_refActionGroup->add(an_action, boost::bind(&SelectionController::rotate, 
+                                                 BIND_SHARED_PTR(SelectionController, m_selection_controller)
+                                                 , 90));
+    
+    m_refActionGroup->add(Gtk::Action::create("SetLabel", _("Set _Label")));
+    m_refActionGroup->add(Gtk::Action::create("SetLabel6", _("Label _6")),
+                          Gtk::AccelKey("6"),
+                          boost::bind(&SelectionController::set_label, 
+                                      BIND_SHARED_PTR(SelectionController, m_selection_controller)
+                                      , 1));
+    m_refActionGroup->add(Gtk::Action::create("SetLabel7", _("Label _7")),
+                          Gtk::AccelKey("7"),
+                          boost::bind(&SelectionController::set_label, 
+                                      BIND_SHARED_PTR(SelectionController, m_selection_controller)
+                                      , 2));
+    m_refActionGroup->add(Gtk::Action::create("SetLabel8", _("Label _8")),
+                          Gtk::AccelKey("8"),
+                          boost::bind(&SelectionController::set_label, 
+                                      BIND_SHARED_PTR(SelectionController, m_selection_controller)
+                                      , 3));
+    m_refActionGroup->add(Gtk::Action::create("SetLabel9", _("Label _9")),
+                          Gtk::AccelKey("9"),
+                          boost::bind(&SelectionController::set_label, 
+                                      BIND_SHARED_PTR(SelectionController, m_selection_controller)
+                                      , 4));
+    
+    m_refActionGroup->add(Gtk::Action::create("SetRating", _("Set _Rating")));
+    m_refActionGroup->add(Gtk::Action::create("SetRating0", _("_No Rating")),
+                          Gtk::AccelKey("0"),
+                          boost::bind(&SelectionController::set_rating, 
+                                      BIND_SHARED_PTR(SelectionController, m_selection_controller), 0));
+    m_refActionGroup->add(Gtk::Action::create("SetRating1", _("_1 Star")),
+                          Gtk::AccelKey("1"),
+                          boost::bind(&SelectionController::set_rating, 
+                                      BIND_SHARED_PTR(SelectionController, m_selection_controller), 1));
+    m_refActionGroup->add(Gtk::Action::create("SetRating2", _("_2 Stars")),
+                          Gtk::AccelKey("2"),
+                          boost::bind(&SelectionController::set_rating, 
+                                      BIND_SHARED_PTR(SelectionController, m_selection_controller), 2));
+    m_refActionGroup->add(Gtk::Action::create("SetRating3", _("_3 Stars")),
+                          Gtk::AccelKey("3"),
+                          boost::bind(&SelectionController::set_rating, 
+                                      BIND_SHARED_PTR(SelectionController, m_selection_controller), 3));
+    m_refActionGroup->add(Gtk::Action::create("SetRating4", _("_4 Stars")),
+                          Gtk::AccelKey("4"),
+                          boost::bind(&SelectionController::set_rating, 
+                                      BIND_SHARED_PTR(SelectionController, m_selection_controller), 4));
+    m_refActionGroup->add(Gtk::Action::create("SetRating5", _("_5 Stars")),
+                          Gtk::AccelKey("5"),
+                          boost::bind(&SelectionController::set_rating, 
+                                      BIND_SHARED_PTR(SelectionController, m_selection_controller), 5));
+
+    m_refActionGroup->add(Gtk::Action::create("DeleteImage", Gtk::Stock::DELETE));
+
+    m_refActionGroup->add(Gtk::Action::create("MenuTools", _("_Tools")));
+    m_hide_tools_action = Gtk::ToggleAction::create("ToggleToolsVisible",
+                                                    _("_Hide tools"));
+    m_refActionGroup->add(m_hide_tools_action,
+                          sigc::mem_fun(*this, &Frame::toggle_tools_visible));
+
+    m_refActionGroup->add(Gtk::Action::create("MenuHelp", _("_Help")));
+    m_refActionGroup->add(Gtk::Action::create("Help", Gtk::Stock::HELP));
+    m_refActionGroup->add(Gtk::Action::create("About", Gtk::Stock::ABOUT),
+                          sigc::mem_fun(*Application::app(),
+                                        &Application::about));
+
+    Application::app()->uiManager()->insert_action_group(m_refActionGroup);		
+		
+    gtkWindow().add_accel_group(Application::app()
+                                ->uiManager()->get_accel_group());
+}
+
+void NiepceWindow::undo_state(const Glib::RefPtr<Gtk::Action> & action)
+{
+    framework::UndoHistory & history(Application::app()->undo_history());
+    action->set_sensitive(history.has_undo());
+    std::string s = history.next_undo();
+    action->property_label() = Glib::ustring(_("Undo ")) + s;
+}
+
+
+void NiepceWindow::redo_state(const Glib::RefPtr<Gtk::Action> & action)
+{
+    framework::UndoHistory & history(Application::app()->undo_history());
+    action->set_sensitive(history.has_redo());
+    std::string s = history.next_redo();
+    action->property_label() = Glib::ustring(_("Redo ")) + s;
+}
+
+void NiepceWindow::on_action_file_import()
+{
+    int result;
+    Configuration & cfg = Application::app()->config();
+	
+    ImportDialog::Ptr import_dialog(new ImportDialog());
+
+    result = show_modal_dialog(
+        *static_cast<Gtk::Dialog*>(import_dialog->buildWidget()));
+    switch(result) {
+    case 0:
+    {
+        // import
+        const Glib::ustring & to_import(import_dialog->to_import());
+        if(!to_import.empty()) {
+            cfg.setValue("last_import_location", to_import);
+			
+            DBG_OUT("%s", to_import.c_str());
+            m_libClient->importFromDirectory(to_import, false);
+        }
+        break;
+    }
+    case 1:
+        // cancel
+        break;
+    default:
+        break;
+    }
+}
+
+
+
+void NiepceWindow::on_action_file_quit()
+{
+    Application::app()->quit();
+}
+
+
+void NiepceWindow::on_action_file_open()
+{
+    DBG_OUT("there");
+}
+
+void NiepceWindow::on_open_library()
+{
+    Configuration & cfg = Application::app()->config();
+    std::string libMoniker;
+    int reopen = 0;
+    try {
+        reopen = boost::lexical_cast<int>(cfg.getValue("reopen_last_library", "0"));
+    }
+    catch(...)
+    {
+    }
+    if(reopen) {
+        libMoniker = cfg.getValue("lastOpenLibrary", "");
+    }
+    if(libMoniker.empty()) {
+        Gtk::FileChooserDialog dialog(gtkWindow(), _("Create library"),
+                                      Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER);
+			
+        dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+        dialog.add_button(_("Create"), Gtk::RESPONSE_OK);
+
+        int result = dialog.run();
+        Glib::ustring libraryToCreate;
+        switch(result)
+        {
+        case Gtk::RESPONSE_OK:
+            libraryToCreate = dialog.get_filename();
+            // pass it to the library
+            libMoniker = "local:";
+            libMoniker += libraryToCreate.c_str();
+            cfg.setValue("lastOpenLibrary", libMoniker);
+            DBG_OUT("created library %s", libMoniker.c_str());
+            break;
+        default:
+            break;
+        }
+			
+    }
+    else {
+        DBG_OUT("last library is %s", libMoniker.c_str());
+    }
+    open_library(libMoniker);
+}
+
+
+void NiepceWindow::preference_dialog_setup(const Glib::RefPtr<Gnome::Glade::Xml> & xml, Gtk::Dialog * dialog)
+{
+    Gtk::ComboBox * theme_combo = NULL;
+    Gtk::CheckButton * reopen_checkbutton = NULL;
+    utils::DataBinderPool * binder_pool = new utils::DataBinderPool();
+
+    dialog->signal_hide().connect(boost::bind(&utils::DataBinderPool::destroy, 
+                                              binder_pool));
+		
+    theme_combo = xml->get_widget("theme_combo", theme_combo);
+ 
+// Why are ComboBox so complicated to use?
+//    class Columns : 
+//            public Gtk::TreeModel::ColumnRecord
+//    {
+//    public:
+//        Gtk::TreeModelColumn<Glib::ustring>  label;
+//        Columns() { add(label); }
+//    };
+
+//    Columns columns;
+//    Glib::RefPtr<Gtk::ListStore> model(Gtk::ListStore::create(columns));
+
+//    theme_combo->set_model(model);
+//    const std::vector<framework::Application::ThemeDesc> & themes = framework::Application::app()->get_available_themes();
+//    std::vector<framework::Application::ThemeDesc>::const_iterator i;
+//    for(i = themes.begin(); i != themes.end(); ++i) {
+//        DBG_OUT("adding %s", i->first.c_str());
+//        Gtk::TreeIter iter = model->append();
+//        (*iter).set_value(columns.label, i->first); 
+//    }
+    theme_combo->set_active(framework::Application::app()
+                            ->get_use_custom_theme());
+    theme_combo->signal_changed().connect(
+        boost::bind(&framework::Application::set_use_custom_theme,
+                    framework::Application::app(),
+                    theme_combo->property_active()));
+
+    reopen_checkbutton = xml->get_widget("reopen_checkbutton", reopen_checkbutton);
+    binder_pool->add_binder(new framework::ConfigDataBinder<bool>(
+                                reopen_checkbutton->property_active(),
+                                framework::Application::app()->config(),
+                                "reopen_last_library"));
+}
+
+
+void NiepceWindow::on_preferences()
+{
+    DBG_OUT("on_preferences");
+    show_modal_dialog(GLADEDIR"preferences.glade", "preferences",
+                      boost::bind(&NiepceWindow::preference_dialog_setup,
+                                  this, _1, _2));
+    DBG_OUT("end on_preferences");
+}
+
+
+void NiepceWindow::open_library(const std::string & libMoniker)
+{
+    m_libClient = LibraryClient::Ptr(new LibraryClient(utils::Moniker(libMoniker),
+                                                       m_lib_notifcenter));
+    set_title(libMoniker);
+}
+
+
+void NiepceWindow::set_title(const std::string & title)
+{
+    Frame::set_title(std::string(_("Niepce Digital - ")) + title);
+}
+
+
+}
+
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/

Added: trunk/src/ui/niepcewindow.h
==============================================================================
--- (empty file)
+++ trunk/src/ui/niepcewindow.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,111 @@
+/*
+ * niepce - ui/niepcewindow.h
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _UI_NIEPCEWINDOW_H_
+#define _UI_NIEPCEWINDOW_H_
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/signals.hpp>
+
+#include <gtkmm/treemodel.h>
+#include <gtkmm/box.h>
+#include <gtkmm/menubar.h>
+#include <gtkmm/statusbar.h>
+#include <gtkmm/paned.h>
+
+#include "framework/frame.h"
+#include "framework/notificationcenter.h"
+#include "framework/configdatabinder.h"
+#include "libraryclient/libraryclient.h"
+#include "ui/librarymainviewcontroller.h"
+#include "ui/workspacecontroller.h"
+#include "ui/selectioncontroller.h"
+#include "ui/filmstripcontroller.h"
+
+namespace framework {
+	class NotificatioCenter;
+}
+
+namespace ui {
+
+class NiepceWindow
+    : public framework::Frame
+{
+public:
+    NiepceWindow();
+    virtual ~NiepceWindow();
+
+
+    virtual void set_title(const std::string & title);
+
+    libraryclient::LibraryClient::Ptr getLibraryClient()
+        { return m_libClient; }
+protected:
+    virtual Gtk::Widget * buildWidget();
+
+private:
+    void undo_state(const Glib::RefPtr<Gtk::Action> & action);
+    void redo_state(const Glib::RefPtr<Gtk::Action> & action);
+
+    void on_action_file_import();
+
+    void on_action_file_quit();
+    void on_action_file_open();
+    void on_open_library();
+
+    void preference_dialog_setup(const Glib::RefPtr<Gnome::Glade::Xml> &,
+                                 Gtk::Dialog *);
+    void on_preferences();
+
+    void init_ui();
+    void init_actions();
+
+    void open_library(const std::string & libMoniker);
+		
+    framework::NotificationCenter::Ptr  m_lib_notifcenter;
+
+    Gtk::VBox                      m_vbox;
+    Gtk::HPaned                    m_hbox;
+    LibraryMainViewController::Ptr m_mainviewctrl; // the main views stacked.
+    WorkspaceController::Ptr       m_workspacectrl;
+    FilmStripController::Ptr       m_filmstrip;
+    ui::SelectionController::Ptr   m_selection_controller;
+    Gtk::Statusbar                 m_statusBar;
+    Glib::RefPtr<Gtk::ActionGroup> m_refActionGroup;
+    libraryclient::LibraryClient::Ptr m_libClient;
+    // connections
+    boost::signals::scoped_connection m_undostate_conn;
+    boost::signals::scoped_connection m_redostate_conn;
+};
+
+}
+
+
+#endif
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/ui/preferences.glade
==============================================================================
--- (empty file)
+++ trunk/src/ui/preferences.glade	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--*- mode: xml -*-->
+<glade-interface>
+  <widget class="GtkDialog" id="preferences">
+    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+    <property name="border_width">5</property>
+    <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+        <property name="spacing">2</property>
+        <child>
+          <widget class="GtkNotebook" id="notebook1">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <child>
+              <widget class="GtkTable" id="table2">
+                <property name="visible">True</property>
+                <property name="border_width">12</property>
+                <property name="n_rows">2</property>
+                <property name="n_columns">2</property>
+                <property name="column_spacing">12</property>
+                <property name="row_spacing">12</property>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" id="reopen_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="label" translatable="yes">_Reopen Library</property>
+                    <property name="use_underline">True</property>
+                    <property name="response_id">0</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="right_attach">2</property>
+                    <property name="y_options"></property>
+                  </packing>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label1">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_General</property>
+                <property name="use_underline">True</property>
+              </widget>
+              <packing>
+                <property name="type">tab</property>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkTable" id="table1">
+                <property name="visible">True</property>
+                <property name="border_width">12</property>
+                <property name="n_rows">1</property>
+                <property name="n_columns">2</property>
+                <property name="column_spacing">12</property>
+                <property name="row_spacing">12</property>
+                <child>
+                  <widget class="GtkComboBox" id="theme_combo">
+                    <property name="visible">True</property>
+                    <property name="items" translatable="yes">System
+Dark</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="label3">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">_Theme:	</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">theme_combo</property>
+                  </widget>
+                  <packing>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options"></property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label2">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_User Interface</property>
+                <property name="use_underline">True</property>
+              </widget>
+              <packing>
+                <property name="type">tab</property>
+                <property name="position">1</property>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area1">
+            <property name="visible">True</property>
+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+            <property name="layout_style">GTK_BUTTONBOX_END</property>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <widget class="GtkButton" id="button1">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="label">gtk-close</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">0</property>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+</glade-interface>

Added: trunk/src/ui/selectioncontroller.cpp
==============================================================================
--- (empty file)
+++ trunk/src/ui/selectioncontroller.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,226 @@
+/*
+ * niepce - ui/selectioncontroller.cpp
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <boost/bind.hpp>
+
+#include <gtkmm/iconview.h>
+#include <glibmm/i18n.h>
+
+#include "utils/autoflag.h"
+#include "utils/debug.h"
+#include "framework/undo.h"
+#include "framework/command.h"
+#include "framework/application.h"
+#include "db/metadata.h"
+#include "libraryclient/libraryclient.h"
+#include "niepcewindow.h"
+#include "selectioncontroller.h"
+
+namespace ui {
+
+
+SelectionController::SelectionController()
+	: m_in_handler(false)
+{
+	m_imageliststore = ImageListStore::create();
+}
+
+void SelectionController::_added()
+{
+	m_imageliststore->set_parent_controller(m_parent);
+}
+
+void SelectionController::add_selectable(IImageSelectable * selectable)
+{ 
+    DBG_OUT("added %lx", selectable);
+	m_selectables.push_back(selectable);
+	selectable->image_list()->signal_selection_changed().connect(
+		boost::bind(&SelectionController::selected, 
+					this, selectable));
+	selectable->image_list()->signal_item_activated().connect(
+		boost::bind(&SelectionController::activated, this, _1, selectable));
+}
+
+
+void SelectionController::activated(const Gtk::TreeModel::Path & /*path*/,
+									IImageSelectable * selectable)
+{
+	utils::AutoFlag f(m_in_handler);
+	int selection = selectable->get_selected();
+	DBG_OUT("item activated %d", selection);
+	signal_activated(selection);
+}
+
+void SelectionController::selected(IImageSelectable * selectable)
+{
+	if(m_in_handler) {
+		DBG_OUT("%lx already in handler", this);
+		return;
+	}
+
+	utils::AutoFlag f(m_in_handler);
+
+	int selection = selectable->get_selected();
+	std::vector<IImageSelectable *>::iterator iter;
+	for(iter = m_selectables.begin(); iter != m_selectables.end(); iter++) {
+		if(*iter != selectable) {
+			(*iter)->select_image(selection);
+		}
+	}
+	signal_selected(selection);
+}
+
+
+libraryclient::LibraryClient::Ptr SelectionController::getLibraryClient()
+{
+    return	boost::dynamic_pointer_cast<NiepceWindow>(m_parent.lock())->getLibraryClient();
+}
+
+inline int SelectionController::get_selection()
+{
+    DBG_ASSERT(!m_selectables.empty(), "selectables list can't be empty");
+    return m_selectables[0]->get_selected();
+}
+
+
+void SelectionController::_selection_move(bool backwards)
+{
+    int selection = get_selection();
+    Gtk::TreeIter iter = m_imageliststore->get_iter_from_id(selection);
+    if(backwards) {
+        if(iter != m_imageliststore->children().begin()) {
+            iter--;
+        }
+    }
+    else {
+        iter++;
+    }
+    if(iter) {
+        // make sure the iterator is valid...
+        db::LibFile::Ptr libfile 
+            = (*iter)[m_imageliststore->columns().m_libfile];
+        selection = libfile->id();
+
+        utils::AutoFlag f(m_in_handler);
+        
+        std::for_each(m_selectables.begin(), m_selectables.end(),
+                      boost::bind(&IImageSelectable::select_image, _1,  
+                                  selection));
+        signal_selected(selection);
+    }
+}
+
+/** select the previous image. Emit the signal */
+void SelectionController::select_previous()
+{
+    _selection_move(true);
+}
+
+
+/** select the next image. Emit the signal */
+void SelectionController::select_next()
+{
+    _selection_move(false);
+}
+
+
+void SelectionController::rotate(int angle)
+{
+    DBG_OUT("angle = %d", angle);
+    int selection = get_selection();
+    if(selection >= 0) {
+        Gtk::TreeIter iter = m_imageliststore->get_iter_from_id(selection);
+        if(iter) {
+            // @todo
+        }
+    }
+}
+
+
+bool SelectionController::_set_metadata(const std::string & undo_label, const db::LibFile::Ptr & file,
+                                        int meta, int old_value, int new_value)
+{
+    framework::UndoTransaction *undo = new framework::UndoTransaction(undo_label);
+    framework::Application::app()->undo_history().add(undo);
+    framework::Command *cmd = new framework::Command;
+    cmd->redo = boost::bind(&libraryclient::LibraryClient::setMetadata,
+                            getLibraryClient(), file->id(), 
+                            meta, new_value);
+    cmd->undo = boost::bind(&libraryclient::LibraryClient::setMetadata,
+                            getLibraryClient(), file->id(),
+                            meta, old_value);
+    undo->add(cmd);
+    undo->redo();
+    return true;
+}
+
+
+void SelectionController::set_label(int label)
+{
+    DBG_OUT("label = %d", label);
+    int selection = get_selection();
+    if(selection >= 0) {
+        Gtk::TreeIter iter = m_imageliststore->get_iter_from_id(selection);
+        if(iter) {
+            db::LibFile::Ptr file = (*iter)[m_imageliststore->columns().m_libfile];
+            DBG_OUT("old label is %d", file->label());
+            int old_value = file->label();
+            _set_metadata(_("Set Label"), file, 
+                          MAKE_METADATA_IDX(db::META_NS_XMPCORE, db::META_XMPCORE_LABEL), 
+                          old_value, label);
+            // we need to set the label here so that undo/redo works
+            // consistently.
+            file->setLabel(label);
+        }
+    }
+}
+
+
+void SelectionController::set_rating(int rating)
+{
+    DBG_OUT("rating = %d", rating);
+    int selection = get_selection();
+    if(selection >= 0) {
+        Gtk::TreeIter iter = m_imageliststore->get_iter_from_id(selection);
+        if(iter) {
+            db::LibFile::Ptr file = (*iter)[m_imageliststore->columns().m_libfile];
+            DBG_OUT("old rating is %d", file->rating());
+            int old_value = file->rating();
+            _set_metadata(_("Set Rating"), file, 
+                          MAKE_METADATA_IDX(db::META_NS_XMPCORE, db::META_XMPCORE_RATING), 
+                          old_value, rating);
+            // we need to set the rating here so that undo/redo works
+            // consistently.
+            file->setRating(rating);
+        }
+    }
+}
+
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/

Added: trunk/src/ui/selectioncontroller.h
==============================================================================
--- (empty file)
+++ trunk/src/ui/selectioncontroller.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,122 @@
+/*
+ * niepce - ui/selectioncontroller.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _UI_SELECTIONCONTROLLER_H__
+#define _UI_SELECTIONCONTROLLER_H__
+
+#include <gtk/gtkiconview.h>
+
+#include <boost/signal.hpp>
+
+#include "framework/controller.h"
+#include "ui/imageliststore.h"
+
+namespace Gtk {
+	class IconView;
+	class Widget;
+}
+
+namespace ui {
+
+/** interface for selectable image. Make the controller
+ *  inherit/implement it.
+ */
+class IImageSelectable 
+{
+public:
+	virtual ~IImageSelectable() {}
+	virtual Gtk::IconView * image_list() = 0;
+    /** Return the id of the selection. <= 0 is none. */
+	virtual int get_selected() = 0;
+    /** select the image a specific id 
+     *  might emit the signals.
+     */
+	virtual void select_image(int id) = 0;
+};
+
+
+class SelectionController
+	: public framework::Controller
+{
+public:
+	typedef boost::shared_ptr<SelectionController> Ptr;
+	SelectionController();
+
+	void add_selectable(IImageSelectable *);
+
+	void activated(const Gtk::TreeModel::Path & /*path*/,
+				   IImageSelectable * selectable);
+	void selected(IImageSelectable *);
+
+
+	const Glib::RefPtr<ImageListStore> & list_store() const
+		{ return m_imageliststore; }
+
+	// the signal to call when selection is changed.
+	boost::signal<void (int)> signal_selected;
+
+	// signal for when the item is activated (ie double-click)
+	boost::signal<void (int)> signal_activated;
+
+    /////////
+    /** select the previous image. Emit the signal */
+    void select_previous();
+    /** select the next image. Emit the signal */
+    void select_next();
+    /** rotate the image in selection by %angle (trigonometric) */
+    void rotate(int angle);
+    /** set the rating of selection to %rating. */
+    void set_rating(int rating);
+    /** set the label of selection to the label with index %label. */
+    void set_label(int label);
+protected:
+	virtual void _added();
+	virtual Gtk::Widget * buildWidget()
+		{ return NULL; }
+private:
+    int get_selection();
+    libraryclient::LibraryClient::Ptr getLibraryClient();
+
+    bool _set_metadata(const std::string & undo_label, 
+                       const db::LibFile::Ptr & file,
+                       int meta, int old_value, int new_value);
+    /** move the selection and emit the signal 
+     * @param backwards true if the move is backwards.
+     */
+    void _selection_move(bool backwards);
+
+	Glib::RefPtr<ImageListStore>  m_imageliststore;
+	bool m_in_handler;
+	std::vector<IImageSelectable *> m_selectables;
+};
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/
+
+#endif

Added: trunk/src/ui/thumb-view/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/ui/thumb-view/Makefile.am	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,13 @@
+
+
+INCLUDES = -I$(srcdir) -I$(top_srcdir)/src
+
+noinst_LTLIBRARIES = libthumbview.la
+
+noinst_HEADERS = eog-thumb-nav.h eog-thumb-view.h 
+
+libthumbview_la_CPPFLAGS =  @LIBGTKMM_CFLAGS@ @LIBGLADEMM_CFLAGS@ \
+	@GNOMEVFS_CFLAGS@
+
+libthumbview_la_SOURCES = eog-thumb-nav.cpp eog-thumb-view.cpp
+

Added: trunk/src/ui/thumb-view/eog-thumb-nav.cpp
==============================================================================
--- (empty file)
+++ trunk/src/ui/thumb-view/eog-thumb-nav.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,496 @@
+/* Eye Of Gnome - Thumbnail Navigator
+ *
+ * Copyright (C) 2006 The Free Software Foundation
+ *
+ * Author: Lucas Rocha <lucasr gnome org>
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "eog-thumb-nav.h"
+#include "eog-thumb-view.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <string.h>
+
+#define EOG_THUMB_NAV_GET_PRIVATE(object) \
+	(G_TYPE_INSTANCE_GET_PRIVATE ((object), EOG_TYPE_THUMB_NAV, EogThumbNavPrivate))
+
+G_DEFINE_TYPE (EogThumbNav, eog_thumb_nav, GTK_TYPE_HBOX)
+
+#define EOG_THUMB_NAV_SCROLL_INC     1
+#define EOG_THUMB_NAV_SCROLL_MOVE    20
+#define EOG_THUMB_NAV_SCROLL_TIMEOUT 20
+
+enum
+{
+	PROP_SHOW_BUTTONS = 1,
+	PROP_THUMB_VIEW,
+	PROP_MODE
+};
+
+struct _EogThumbNavPrivate {
+	EogThumbNavMode   mode;
+
+	gboolean          show_buttons;
+
+	GtkWidget        *button_left;
+	GtkWidget        *button_right;
+	GtkWidget        *sw;
+	GtkWidget        *scale;
+	GtkWidget        *thumbview;
+};
+
+static void
+eog_thumb_nav_adj_changed (GtkAdjustment *adj, gpointer user_data)
+{
+	EogThumbNav *nav;
+	EogThumbNavPrivate *priv;
+	gdouble upper, page_size;
+
+	nav = EOG_THUMB_NAV (user_data);
+	priv = EOG_THUMB_NAV_GET_PRIVATE (nav);
+
+	g_object_get (G_OBJECT (adj),
+		      "upper", &upper,
+		      "page-size", &page_size,
+		      NULL);
+
+	gtk_widget_set_sensitive (priv->button_right, upper > page_size);
+}
+
+static void
+eog_thumb_nav_adj_value_changed (GtkAdjustment *adj, gpointer user_data)
+{
+	EogThumbNav *nav;
+	EogThumbNavPrivate *priv;
+	gdouble upper, page_size, value;
+
+	nav = EOG_THUMB_NAV (user_data);
+	priv = EOG_THUMB_NAV_GET_PRIVATE (nav);
+
+	g_object_get (G_OBJECT (adj),
+		      "upper", &upper,
+		      "page-size", &page_size,
+		      "value", &value,
+		      NULL);
+
+	gtk_widget_set_sensitive (priv->button_left, value > 0);
+
+	gtk_widget_set_sensitive (priv->button_right, 
+				  value < upper - page_size);
+}
+
+static gboolean
+eog_thumb_nav_scroll_left (gpointer user_data)
+{
+	gdouble value, move;
+	static gint i = 0;
+
+	GtkAdjustment *adj = GTK_ADJUSTMENT (user_data);
+
+	g_object_get (G_OBJECT (adj),
+		      "value", &value,
+		      NULL);
+
+	if (i == EOG_THUMB_NAV_SCROLL_MOVE || 
+	    value - EOG_THUMB_NAV_SCROLL_INC < 0) {
+		i = 0;
+		gtk_adjustment_value_changed (adj);
+		return FALSE;
+	} 
+
+	i++;
+
+	move = MIN (EOG_THUMB_NAV_SCROLL_MOVE, value);
+	gtk_adjustment_set_value (adj, value - move);
+
+	return TRUE;
+}
+
+static gboolean
+eog_thumb_nav_scroll_right (gpointer user_data)
+{
+	gdouble upper, page_size, value, move;
+	static gint i = 0;
+
+	GtkAdjustment *adj = GTK_ADJUSTMENT (user_data);
+
+	g_object_get (G_OBJECT (adj),
+		      "upper", &upper,
+		      "page-size", &page_size,
+		      "value", &value,
+		      NULL);
+
+	if (i == EOG_THUMB_NAV_SCROLL_MOVE || 
+	    value + EOG_THUMB_NAV_SCROLL_INC > upper - page_size) {
+		i = 0;
+		return FALSE;
+	} 
+
+	i++;
+
+	move = MIN (EOG_THUMB_NAV_SCROLL_MOVE, upper - page_size - value);
+	gtk_adjustment_set_value (adj, value + move);
+	gtk_adjustment_value_changed (adj);
+
+	return TRUE;
+}
+
+static void
+eog_thumb_nav_left_button_clicked (GtkButton *, gpointer user_data)
+{
+	GtkWidget *sw = EOG_THUMB_NAV (user_data)->priv->sw;
+
+	GtkAdjustment *adj = 
+		gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (sw));
+	
+	g_timeout_add (EOG_THUMB_NAV_SCROLL_TIMEOUT, eog_thumb_nav_scroll_left, adj);
+}
+
+static void
+eog_thumb_nav_right_button_clicked (GtkButton *, gpointer user_data)
+{
+	GtkWidget *sw = EOG_THUMB_NAV (user_data)->priv->sw;
+
+	GtkAdjustment *adj = 
+		gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (sw));
+	
+	g_timeout_add (EOG_THUMB_NAV_SCROLL_TIMEOUT, eog_thumb_nav_scroll_right, adj);
+}
+
+static void 
+eog_thumb_nav_get_property (GObject    *object, 
+			    guint       property_id, 
+			    GValue     *value, 
+			    GParamSpec *)
+{
+	EogThumbNav *nav = EOG_THUMB_NAV (object);
+
+	switch (property_id)
+	{
+	case PROP_SHOW_BUTTONS:
+		g_value_set_boolean (value, 
+			eog_thumb_nav_get_show_buttons (nav));
+		break;
+
+	case PROP_THUMB_VIEW:
+		g_value_set_object (value, nav->priv->thumbview);
+		break;
+
+	case PROP_MODE:
+		g_value_set_int (value, 
+			eog_thumb_nav_get_mode (nav));
+		break;
+	}
+}
+
+static void 
+eog_thumb_nav_set_property (GObject      *object, 
+			    guint         property_id, 
+			    const GValue *value, 
+			    GParamSpec   *)
+{
+	EogThumbNav *nav = EOG_THUMB_NAV (object);
+
+	switch (property_id)
+	{
+	case PROP_SHOW_BUTTONS:
+		eog_thumb_nav_set_show_buttons (nav, 
+			g_value_get_boolean (value));
+		break;
+
+	case PROP_THUMB_VIEW:
+		nav->priv->thumbview = 
+			GTK_WIDGET (g_value_get_object (value));
+		break;
+
+	case PROP_MODE:
+		eog_thumb_nav_set_mode (nav, 
+			(EogThumbNavMode)g_value_get_int (value));
+		break;
+	}
+}
+
+static GObject *
+eog_thumb_nav_constructor (GType type,
+			   guint n_construct_properties,
+			   GObjectConstructParam *construct_params)
+{
+	GObject *object;
+	EogThumbNavPrivate *priv;
+
+	object = G_OBJECT_CLASS (eog_thumb_nav_parent_class)->constructor
+			(type, n_construct_properties, construct_params);
+
+	priv = EOG_THUMB_NAV (object)->priv;
+
+	if (priv->thumbview != NULL) {
+		gtk_container_add (GTK_CONTAINER (priv->sw), priv->thumbview);
+		gtk_widget_show_all (priv->sw);
+	}
+
+	return object;
+}
+
+static void
+eog_thumb_nav_class_init (EogThumbNavClass *klass)
+{
+	GObjectClass *g_object_class = (GObjectClass *) klass;
+
+	g_object_class->constructor  = eog_thumb_nav_constructor;
+	g_object_class->get_property = eog_thumb_nav_get_property;
+	g_object_class->set_property = eog_thumb_nav_set_property;
+
+	g_object_class_install_property (g_object_class,
+	                                 PROP_SHOW_BUTTONS,
+	                                 g_param_spec_boolean ("show-buttons",
+	                                                       "Show Buttons",
+	                                                       "Whether to show navigation buttons or not",
+	                                                       TRUE,
+	                                                       (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+	g_object_class_install_property (g_object_class,
+	                                 PROP_THUMB_VIEW,
+	                                 g_param_spec_object ("thumbview",
+	                                                       "Thumbnail View",
+	                                                       "The internal thumbnail viewer widget",
+	                                                       EOG_TYPE_THUMB_VIEW,
+	                                                       (GParamFlags)(G_PARAM_CONSTRUCT_ONLY |
+								G_PARAM_READABLE | 
+								G_PARAM_WRITABLE)));
+
+	g_object_class_install_property (g_object_class,
+	                                 PROP_MODE,
+	                                 g_param_spec_int ("mode",
+	                                                   "Mode",
+	                                                   "Thumb navigator mode",
+	                                                   EOG_THUMB_NAV_MODE_ONE_ROW,
+							   EOG_THUMB_NAV_MODE_MULTIPLE_ROWS,
+							   EOG_THUMB_NAV_MODE_ONE_ROW,
+	                                                   (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+	g_type_class_add_private (g_object_class, sizeof (EogThumbNavPrivate));
+}
+
+static void
+eog_thumb_nav_init (EogThumbNav *nav)
+{
+	EogThumbNavPrivate *priv;
+	GtkAdjustment *adj;
+	GtkWidget *arrow;
+
+	nav->priv = EOG_THUMB_NAV_GET_PRIVATE (nav);
+
+	priv = nav->priv;
+
+	priv->mode = EOG_THUMB_NAV_MODE_ONE_ROW;
+
+	priv->show_buttons = TRUE;
+
+        priv->button_left = gtk_button_new ();
+	gtk_button_set_relief (GTK_BUTTON (priv->button_left), GTK_RELIEF_NONE);
+
+	arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_ETCHED_IN); 
+	gtk_container_add (GTK_CONTAINER (priv->button_left), arrow);
+
+	gtk_widget_set_size_request (GTK_WIDGET (priv->button_left), 20, 0);
+
+        gtk_box_pack_start (GTK_BOX (nav), priv->button_left, FALSE, FALSE, 0);
+
+	g_signal_connect (priv->button_left, 
+			  "clicked", 
+			  G_CALLBACK (eog_thumb_nav_left_button_clicked), 
+			  nav);
+
+	priv->sw = gtk_scrolled_window_new (NULL, NULL);
+
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->sw), 
+					     GTK_SHADOW_IN);
+
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->sw),
+					GTK_POLICY_ALWAYS,
+					GTK_POLICY_NEVER);
+
+        adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (priv->sw));
+
+	g_signal_connect (adj, 
+			  "changed", 
+			  G_CALLBACK (eog_thumb_nav_adj_changed), 
+			  nav);
+
+	g_signal_connect (adj, 
+			  "value-changed", 
+			  G_CALLBACK (eog_thumb_nav_adj_value_changed), 
+			  nav);
+
+        gtk_box_pack_start (GTK_BOX (nav), priv->sw, TRUE, TRUE, 0);
+
+        priv->button_right = gtk_button_new ();
+	gtk_button_set_relief (GTK_BUTTON (priv->button_right), GTK_RELIEF_NONE);
+
+	arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE); 
+	gtk_container_add (GTK_CONTAINER (priv->button_right), arrow);
+
+	gtk_widget_set_size_request (GTK_WIDGET (priv->button_right), 20, 0);
+
+        gtk_box_pack_start (GTK_BOX (nav), priv->button_right, FALSE, FALSE, 0);
+
+	g_signal_connect (priv->button_right, 
+			  "clicked", 
+			  G_CALLBACK (eog_thumb_nav_right_button_clicked), 
+			  nav);
+
+	gtk_adjustment_value_changed (adj);
+}
+
+GtkWidget *
+eog_thumb_nav_new (GtkWidget       *thumbview, 
+		   EogThumbNavMode  mode, 
+		   gboolean         show_buttons)
+{
+	GObject *nav;
+
+	nav = (GObject*)g_object_new (EOG_TYPE_THUMB_NAV, 
+		            "show-buttons", show_buttons,
+		            "mode", mode,
+		            "thumbview", thumbview,
+		            "homogeneous", FALSE,
+		            "spacing", 0,
+			    NULL); 
+
+	return GTK_WIDGET (nav);
+}
+
+gboolean
+eog_thumb_nav_get_show_buttons (EogThumbNav *nav)
+{
+	g_return_val_if_fail (EOG_IS_THUMB_NAV (nav), FALSE);
+
+	return nav->priv->show_buttons; 
+}
+
+void 
+eog_thumb_nav_set_show_buttons (EogThumbNav *nav, gboolean show_buttons)
+{
+	g_return_if_fail (EOG_IS_THUMB_NAV (nav));
+	g_return_if_fail (nav->priv->button_left  != NULL);
+	g_return_if_fail (nav->priv->button_right != NULL);
+
+	nav->priv->show_buttons = show_buttons;
+
+	if (show_buttons && 
+	    nav->priv->mode == EOG_THUMB_NAV_MODE_ONE_ROW) {
+		gtk_widget_show_all (nav->priv->button_left);
+		gtk_widget_show_all (nav->priv->button_right);
+	} else {
+		gtk_widget_hide_all (nav->priv->button_left);
+		gtk_widget_hide_all (nav->priv->button_right);
+	}
+}
+
+EogThumbNavMode
+eog_thumb_nav_get_mode (EogThumbNav *nav)
+{
+	g_return_val_if_fail (EOG_IS_THUMB_NAV (nav), (EogThumbNavMode)FALSE);
+
+	return nav->priv->mode; 
+}
+
+void 
+eog_thumb_nav_set_mode (EogThumbNav *nav, EogThumbNavMode mode)
+{
+	EogThumbNavPrivate *priv;
+
+	g_return_if_fail (EOG_IS_THUMB_NAV (nav));
+
+	priv = nav->priv;
+
+	priv->mode = mode;
+
+	switch (mode)
+	{
+	case EOG_THUMB_NAV_MODE_ONE_ROW:
+		gtk_icon_view_set_columns (GTK_ICON_VIEW (priv->thumbview), 
+					   G_MAXINT);
+
+		gtk_widget_set_size_request (priv->thumbview, -1, 108);
+		eog_thumb_view_set_item_height (EOG_THUMB_VIEW (priv->thumbview), 
+						100);
+
+		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->sw),
+						GTK_POLICY_ALWAYS,
+						GTK_POLICY_NEVER);
+
+		eog_thumb_nav_set_show_buttons (nav, priv->show_buttons);
+
+		break;
+
+	case EOG_THUMB_NAV_MODE_ONE_COLUMN:
+		gtk_icon_view_set_columns (GTK_ICON_VIEW (priv->thumbview), 1);
+
+		gtk_widget_set_size_request (priv->thumbview, 113, -1);
+		eog_thumb_view_set_item_height (EOG_THUMB_VIEW (priv->thumbview), 
+						-1);
+
+		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->sw),
+						GTK_POLICY_NEVER,
+						GTK_POLICY_ALWAYS);
+
+		gtk_widget_hide_all (priv->button_left);
+		gtk_widget_hide_all (priv->button_right);
+		
+		break;
+
+	case EOG_THUMB_NAV_MODE_MULTIPLE_ROWS:
+		gtk_icon_view_set_columns (GTK_ICON_VIEW (priv->thumbview), -1);
+
+		gtk_widget_set_size_request (priv->thumbview, -1, 220);
+		eog_thumb_view_set_item_height (EOG_THUMB_VIEW (priv->thumbview), 
+						-1);
+
+		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->sw),
+						GTK_POLICY_NEVER,
+						GTK_POLICY_ALWAYS);
+
+		gtk_widget_hide_all (priv->button_left);
+		gtk_widget_hide_all (priv->button_right);
+		
+		break;
+
+	case EOG_THUMB_NAV_MODE_MULTIPLE_COLUMNS:
+		gtk_icon_view_set_columns (GTK_ICON_VIEW (priv->thumbview), -1);
+
+		gtk_widget_set_size_request (priv->thumbview, 230, -1);
+		eog_thumb_view_set_item_height (EOG_THUMB_VIEW (priv->thumbview), 
+						-1);
+
+		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->sw),
+						GTK_POLICY_NEVER,
+						GTK_POLICY_ALWAYS);
+
+		gtk_widget_hide_all (priv->button_left);
+		gtk_widget_hide_all (priv->button_right);
+		
+		break;
+	}
+}

Added: trunk/src/ui/thumb-view/eog-thumb-nav.h
==============================================================================
--- (empty file)
+++ trunk/src/ui/thumb-view/eog-thumb-nav.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,79 @@
+/* Eye Of Gnome - Thumbnail Navigator 
+ *
+ * Copyright (C) 2006 The Free Software Foundation
+ *
+ * Author: Lucas Rocha <lucasr gnome org>
+ *
+ * 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 __EOG_THUMB_NAV_H__
+#define __EOG_THUMB_NAV_H__
+
+#include "eog-thumb-view.h"
+
+#include <gtk/gtk.h>
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _EogThumbNav EogThumbNav;
+typedef struct _EogThumbNavClass EogThumbNavClass;
+typedef struct _EogThumbNavPrivate EogThumbNavPrivate;
+
+#define EOG_TYPE_THUMB_NAV            (eog_thumb_nav_get_type ())
+#define EOG_THUMB_NAV(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), EOG_TYPE_THUMB_NAV, EogThumbNav))
+#define EOG_THUMB_NAV_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),  EOG_TYPE_THUMB_NAV, EogThumbNavClass))
+#define EOG_IS_THUMB_NAV(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), EOG_TYPE_THUMB_NAV))
+#define EOG_IS_THUMB_NAV_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),  EOG_TYPE_THUMB_NAV))
+#define EOG_THUMB_NAV_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),  EOG_TYPE_THUMB_NAV, EogThumbNavClass))
+
+typedef enum {
+	EOG_THUMB_NAV_MODE_ONE_ROW,
+	EOG_THUMB_NAV_MODE_ONE_COLUMN,
+	EOG_THUMB_NAV_MODE_MULTIPLE_ROWS,
+	EOG_THUMB_NAV_MODE_MULTIPLE_COLUMNS
+} EogThumbNavMode;
+
+struct _EogThumbNav {
+	GtkHBox base_instance;
+
+	EogThumbNavPrivate *priv;
+};
+
+struct _EogThumbNavClass {
+	GtkHBoxClass parent_class;
+};
+
+GType	         eog_thumb_nav_get_type          (void) G_GNUC_CONST;
+
+GtkWidget       *eog_thumb_nav_new               (GtkWidget         *thumbview,
+						  EogThumbNavMode    mode, 
+	             			          gboolean           show_buttons);
+
+gboolean         eog_thumb_nav_get_show_buttons  (EogThumbNav       *nav);
+
+void             eog_thumb_nav_set_show_buttons  (EogThumbNav       *nav, 
+                                                  gboolean           show_buttons);
+
+EogThumbNavMode  eog_thumb_nav_get_mode          (EogThumbNav       *nav);
+
+void             eog_thumb_nav_set_mode          (EogThumbNav       *nav, 
+                                                  EogThumbNavMode    mode);
+
+G_END_DECLS
+
+#endif /* __EOG_THUMB_NAV_H__ */

Added: trunk/src/ui/thumb-view/eog-thumb-view.cpp
==============================================================================
--- (empty file)
+++ trunk/src/ui/thumb-view/eog-thumb-view.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,787 @@
+/* Eye Of Gnome - Thumbnail View
+ *
+ * Copyright (C) 2006 The Free Software Foundation
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * C++-ization: Hubert Figuiere <hub figuiere net>
+ * Original Author: Claudio Saavedra <csaavedra alumnos utalca cl>
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "eog-thumb-view.h"
+//#include "eog-list-store.h"
+
+#ifdef HAVE_EXIF
+#include "eog-exif-util.h"
+#include <libexif/exif-data.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <string.h>
+#include <libgnomevfs/gnome-vfs-mime-utils.h>
+#include <libgnomevfs/gnome-vfs-mime-handlers.h>
+
+#include "utils/boost.h"
+#include "db/libfile.h"
+
+#define EOG_THUMB_VIEW_SPACING 0
+
+#define EOG_THUMB_VIEW_GET_PRIVATE(object) \
+	(G_TYPE_INSTANCE_GET_PRIVATE ((object), EOG_TYPE_THUMB_VIEW, EogThumbViewPrivate))
+
+G_DEFINE_TYPE (EogThumbView, eog_thumb_view, GTK_TYPE_ICON_VIEW)
+
+//static EogImage* eog_thumb_view_get_image_from_path (EogThumbView      *tb,
+//						     GtkTreePath       *path);
+
+static void      eog_thumb_view_popup_menu          (EogThumbView      *widget, 
+						     GdkEventButton    *event);
+
+struct _EogThumbViewPrivate {
+	gint start_thumb; /* the first visible thumbnail */
+	gint end_thumb;   /* the last visible thumbnail  */
+	GtkWidget *menu;  /* a contextual menu for thumbnails */
+	GtkCellRenderer *pixbuf_cell;
+	Glib::RefPtr<ui::ImageListStore> store;
+};
+
+/* Drag 'n Drop */
+enum {
+	TARGET_PLAIN,
+	TARGET_PLAIN_UTF8,
+	TARGET_URILIST
+};
+
+static GtkTargetEntry target_table[] = {
+	{ const_cast<gchar*>("text/uri-list"), 0, TARGET_URILIST },
+};
+
+
+static void
+eog_thumb_view_finalize (GObject *object)
+{
+	EogThumbView *tb;
+	g_return_if_fail (EOG_IS_THUMB_VIEW (object));
+	tb = EOG_THUMB_VIEW (object);
+	
+	G_OBJECT_CLASS (eog_thumb_view_parent_class)->finalize (object);
+}
+
+static void
+eog_thumb_view_destroy (GtkObject *object)
+{
+	EogThumbView *tb;
+	g_return_if_fail (EOG_IS_THUMB_VIEW (object));
+	tb = EOG_THUMB_VIEW (object);
+
+	GTK_OBJECT_CLASS (eog_thumb_view_parent_class)->destroy (object);
+}
+
+static void
+eog_thumb_view_class_init (EogThumbViewClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
+	
+	gobject_class->finalize = eog_thumb_view_finalize;
+	object_class->destroy = eog_thumb_view_destroy;
+
+	g_type_class_add_private (klass, sizeof (EogThumbViewPrivate));
+}
+
+static void
+eog_thumb_view_clear_range (EogThumbView *tb, 
+			    const gint start_thumb, 
+			    const gint end_thumb)
+{
+	GtkTreePath *path;
+	GtkTreeIter iter;
+	Glib::RefPtr<ui::ImageListStore> store = eog_thumb_view_get_model(tb);
+	gint thumb = start_thumb;
+	gboolean result;
+	
+	g_assert (start_thumb <= end_thumb);
+	
+	path = gtk_tree_path_new_from_indices (start_thumb, -1);
+	for (result = gtk_tree_model_get_iter (GTK_TREE_MODEL (store->gobj()), 
+										   &iter, path);
+	     result && thumb <= end_thumb;
+	     result = gtk_tree_model_iter_next (GTK_TREE_MODEL (store->gobj()), 
+											&iter), thumb++) {
+//		eog_list_store_thumbnail_unset (get_pointer(store), &iter);
+	}
+	gtk_tree_path_free (path);
+}
+
+static void
+eog_thumb_view_add_range (EogThumbView *tb, 
+			  const gint start_thumb, 
+			  const gint end_thumb)
+{
+	GtkTreePath *path;
+	GtkTreeIter iter;
+	Glib::RefPtr<ui::ImageListStore> store = eog_thumb_view_get_model(tb);
+	gint thumb = start_thumb;
+	gboolean result;
+	
+	g_assert (start_thumb <= end_thumb);
+	
+	path = gtk_tree_path_new_from_indices (start_thumb, -1);
+	for (result = gtk_tree_model_get_iter (GTK_TREE_MODEL (store->gobj()), &iter, path);
+	     result && thumb <= end_thumb;
+	     result = gtk_tree_model_iter_next (GTK_TREE_MODEL (store->gobj()), &iter), thumb++) {
+//		eog_list_store_thumbnail_set (get_pointer(store), &iter);
+	}
+	gtk_tree_path_free (path);
+}
+
+static void
+eog_thumb_view_update_visible_range (EogThumbView *tb, 
+				     const gint start_thumb, 
+				     const gint end_thumb)
+{
+	EogThumbViewPrivate *priv = tb->priv;
+	int old_start_thumb, old_end_thumb;
+
+	old_start_thumb= priv->start_thumb;
+	old_end_thumb = priv->end_thumb;
+	
+	if (start_thumb == old_start_thumb &&
+	    end_thumb == old_end_thumb) {
+		return;
+	}
+	
+	if (old_start_thumb < start_thumb)
+		eog_thumb_view_clear_range (tb, old_start_thumb, MIN (start_thumb - 1, old_end_thumb));
+
+	if (old_end_thumb > end_thumb)
+		eog_thumb_view_clear_range (tb, MAX (end_thumb + 1, old_start_thumb), old_end_thumb);
+
+	eog_thumb_view_add_range (tb, start_thumb, end_thumb);
+	
+	priv->start_thumb = start_thumb;
+	priv->end_thumb = end_thumb;
+}
+
+static void
+tb_on_visible_range_changed_cb (EogThumbView *tb,
+                                gpointer /*user_data*/)
+{
+	GtkTreePath *path1, *path2;
+
+	if (!gtk_icon_view_get_visible_range (GTK_ICON_VIEW (tb), &path1, &path2)) {
+		return;
+	}
+	
+	if (path1 == NULL) {
+		path1 = gtk_tree_path_new_first ();
+	}
+	if (path2 == NULL) {
+		gint n_items = gtk_tree_model_iter_n_children (gtk_icon_view_get_model (GTK_ICON_VIEW (tb)), NULL);
+		path2 = gtk_tree_path_new_from_indices (n_items - 1 , -1);
+	}
+	
+	eog_thumb_view_update_visible_range (tb, gtk_tree_path_get_indices (path1) [0],
+					     gtk_tree_path_get_indices (path2) [0]);
+	
+	gtk_tree_path_free (path1);
+	gtk_tree_path_free (path2);
+}
+
+static void
+tb_on_adjustment_changed_cb (EogThumbView *tb,
+							 gpointer /*user_data*/)
+{
+	GtkTreePath *path1, *path2;
+	gint start_thumb, end_thumb;
+	
+	if (!gtk_icon_view_get_visible_range (GTK_ICON_VIEW (tb), &path1, &path2)) {
+		return;
+	}
+	
+	if (path1 == NULL) {
+		path1 = gtk_tree_path_new_first ();
+	}
+	if (path2 == NULL) {
+		gint n_items = gtk_tree_model_iter_n_children (gtk_icon_view_get_model (GTK_ICON_VIEW (tb)), NULL);
+		path2 = gtk_tree_path_new_from_indices (n_items - 1 , -1);
+	}
+
+	start_thumb = gtk_tree_path_get_indices (path1) [0];
+	end_thumb = gtk_tree_path_get_indices (path2) [0];
+
+	eog_thumb_view_add_range (tb, start_thumb, end_thumb);	
+
+	/* case we added an image, we need to make sure that the shifted thumbnail is cleared */
+	eog_thumb_view_clear_range (tb, end_thumb + 1, end_thumb + 1);
+
+	tb->priv->start_thumb = start_thumb;
+	tb->priv->end_thumb = end_thumb;
+
+	gtk_tree_path_free (path1);
+	gtk_tree_path_free (path2);
+}
+
+static void
+tb_on_parent_set_cb (GtkWidget *widget,
+					 GtkObject * /*old_parent*/,
+					 gpointer  /*user_data*/)
+{
+	EogThumbView *tb = EOG_THUMB_VIEW (widget);
+	GtkScrolledWindow *sw;
+	GtkAdjustment *hadjustment;
+	GtkAdjustment *vadjustment;
+
+	GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (tb));
+	if (!GTK_IS_SCROLLED_WINDOW (parent)) {
+		return;
+	}
+	
+	/* if we have been set to a ScrolledWindow, we connect to the callback
+	to set and unset thumbnails. */
+	sw = GTK_SCROLLED_WINDOW (parent);
+	hadjustment = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (sw));
+	vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (sw));
+	
+	/* when scrolling */
+	g_signal_connect_data (G_OBJECT (hadjustment), "value-changed",
+			       G_CALLBACK (tb_on_visible_range_changed_cb),
+			       tb, NULL, (GConnectFlags)(G_CONNECT_SWAPPED | G_CONNECT_AFTER));
+	g_signal_connect_data (G_OBJECT (vadjustment), "value-changed",
+			       G_CALLBACK (tb_on_visible_range_changed_cb),
+			       tb, NULL, (GConnectFlags)(G_CONNECT_SWAPPED | G_CONNECT_AFTER));
+	
+	/* when the adjustment is changed, ie. probably we have new images added. */
+	g_signal_connect_data (G_OBJECT (hadjustment), "changed",
+			       G_CALLBACK (tb_on_adjustment_changed_cb),
+			       tb, NULL, (GConnectFlags)(G_CONNECT_SWAPPED | G_CONNECT_AFTER));
+	g_signal_connect_data (G_OBJECT (vadjustment), "changed",
+			       G_CALLBACK (tb_on_adjustment_changed_cb),
+			       tb, NULL, (GConnectFlags)(G_CONNECT_SWAPPED | G_CONNECT_AFTER));
+
+	/* when resizing the scrolled window */
+	g_signal_connect_swapped (G_OBJECT (sw), "size-allocate",
+				  G_CALLBACK (tb_on_visible_range_changed_cb),
+				  tb);
+}
+
+#if 0
+static gboolean
+tb_on_button_press_event_cb (GtkWidget *tb, GdkEventButton *event, 
+                             gpointer /*user_data*/)
+{
+    GtkTreePath *path;
+    
+    /* Ignore double-clicks and triple-clicks */
+    if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
+    {
+	    path = gtk_icon_view_get_path_at_pos (GTK_ICON_VIEW (tb), 
+						  (gint) event->x, (gint) event->y);
+	    if (path == NULL) {
+		    return FALSE;
+	    }
+
+	    gtk_icon_view_unselect_all (GTK_ICON_VIEW (tb));
+	    gtk_icon_view_select_path (GTK_ICON_VIEW (tb), path);
+
+	    eog_thumb_view_popup_menu (EOG_THUMB_VIEW (tb), event);
+	    
+	    gtk_tree_path_free (path);
+
+	    return TRUE;
+    }
+    
+    return FALSE;
+}
+#endif
+
+static void
+tb_on_drag_data_get_cb (GtkWidget        *widget,
+						GdkDragContext   */*drag_context*/,
+						GtkSelectionData *data,
+						guint             /*info*/,
+						guint             /*time*/,
+						gpointer          /*user_data*/) 
+{
+	GList *list;
+	GList *node;
+	db::LibFile *image;
+	const char *str;
+	gchar *uris = NULL;
+	gchar *tmp_str;
+
+	list = eog_thumb_view_get_selected_images (EOG_THUMB_VIEW (widget));
+
+	for (node = list; node != NULL; node = node->next) {
+		image = static_cast<db::LibFile*>(node->data);
+		str = image->uri().c_str();
+		
+		/* build the "text/uri-list" string */
+		if (uris) {
+			tmp_str = g_strconcat (uris, str, "\r\n", NULL);
+			g_free (uris);
+		} else {
+			tmp_str = g_strconcat (str, "\r\n", NULL);
+		}
+		uris = tmp_str;
+	}
+	gtk_selection_data_set (data, data->target, 8, 
+				(guchar*) uris, strlen (uris));
+	g_free (uris);
+	g_list_free (list);
+	
+}
+
+#ifdef HAVE_GTK_TOOLTIP
+static gboolean 
+tb_on_query_tooltip_cb (GtkWidget  *widget,
+			gint        x,
+			gint        y,
+			gboolean    keyboard_mode,
+			GtkTooltip *tooltip,
+			gpointer    user_data)
+{
+	GtkTreePath *path;
+	EogImage *image;
+	gchar *tooltip_string;
+	gchar *bytes;
+	gint width, height;
+	gchar *uri_str, *mime_str;
+	const gchar *type_str;
+	GError *error = NULL;
+#ifdef HAVE_EXIF
+	ExifData *exif_data;
+#endif	
+
+	if (!gtk_icon_view_get_tooltip_context (GTK_ICON_VIEW (widget), 
+						&x, &y, keyboard_mode,
+						NULL, &path, NULL)) {
+		return FALSE;
+	}
+	
+	image = eog_thumb_view_get_image_from_path (EOG_THUMB_VIEW (widget),
+						    path);
+	gtk_tree_path_free (path);
+
+	if (image == NULL) {
+		return FALSE;
+	}
+
+	if (!eog_image_has_data (image, EOG_IMAGE_DATA_EXIF)) {
+		eog_image_load (image, EOG_IMAGE_DATA_EXIF, NULL, &error);
+
+		if (error) {
+			/* Here, error typically means no exif data found */
+			g_error_free (error);
+			error = NULL;
+		}
+	}
+
+#ifdef HAVE_EXIF
+	exif_data = (ExifData *) eog_image_get_exif_info (image);		
+#endif
+
+	if (!eog_image_has_data (image, EOG_IMAGE_DATA_DIMENSION)) {
+		eog_image_load (image,
+				EOG_IMAGE_DATA_DIMENSION,
+				NULL, 
+				&error);
+	}
+
+	bytes = gnome_vfs_format_file_size_for_display (eog_image_get_bytes (image));
+
+	eog_image_get_size (image, &width, &height);
+	
+	uri_str = eog_image_get_uri_for_display (image);
+
+	mime_str = gnome_vfs_get_mime_type (uri_str);
+	type_str = gnome_vfs_mime_get_description (mime_str);
+
+	tooltip_string = g_strdup_printf ("<b><big>%s</big></b>\n"
+					  "%i x %i %s\n"
+					  "%s\n"
+					  "%s",
+					  eog_image_get_caption (image),
+					  width, 
+					  height, 
+					  ngettext ("pixel", "pixels", height), 
+					  bytes,
+					  type_str);
+
+#ifdef HAVE_EXIF
+	if (exif_data) {
+		gchar *extra_info, *tmp, *date;
+		
+		date = eog_exif_util_format_date (
+				eog_exif_util_get_value (exif_data, EXIF_TAG_DATE_TIME));
+
+		extra_info = g_strdup_printf ("\n%s %s", _("Taken on"), date);
+
+		tmp = g_strconcat (tooltip_string, extra_info, NULL);
+
+		g_free (date);
+		g_free (extra_info);
+		g_free (tooltip_string);
+
+		tooltip_string = tmp;
+	}
+#endif
+
+	gtk_tooltip_set_markup (tooltip, tooltip_string);
+	
+	g_free (uri_str);
+	g_free (mime_str);
+	g_free (bytes);
+ 	g_free (tooltip_string);
+	g_object_unref (image);
+	
+	return TRUE;
+}
+#endif /* HAVE_GTK_TOOLTIP */
+
+static void
+eog_thumb_view_init (EogThumbView *tb)
+{
+	tb->priv = EOG_THUMB_VIEW_GET_PRIVATE (tb);
+	
+	tb->priv->pixbuf_cell = gtk_cell_renderer_pixbuf_new ();
+
+	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (tb), 
+	      	  		    tb->priv->pixbuf_cell, 
+	      			    FALSE);
+	
+	g_object_set (tb->priv->pixbuf_cell, 
+	              "follow-state", TRUE, 
+	              "height", 100, 
+	              "yalign", 0.5, 
+	              "xalign", 0.5, 
+	              NULL);
+	
+	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (tb),
+	                                tb->priv->pixbuf_cell, 
+									"pixbuf", ui::ImageListStore::Columns::STRIP_THUMB_INDEX,
+	                                NULL);
+
+	gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (tb),
+ 					  GTK_SELECTION_MULTIPLE);
+	
+	gtk_icon_view_set_column_spacing (GTK_ICON_VIEW (tb),
+					  EOG_THUMB_VIEW_SPACING);
+
+	gtk_icon_view_set_row_spacing (GTK_ICON_VIEW (tb),
+				       EOG_THUMB_VIEW_SPACING);
+
+#ifdef HAVE_GTK_TOOLTIP
+	g_object_set (tb, "has-tooltip", TRUE, NULL);
+
+	g_signal_connect (tb, 
+			  "query-tooltip",
+			  G_CALLBACK (tb_on_query_tooltip_cb), 
+			  NULL);
+#endif /* HAVE_GTK_TOOLTIP */	
+	
+	tb->priv->start_thumb = 0;
+	tb->priv->end_thumb = 0;
+	tb->priv->menu = NULL;
+	
+	g_signal_connect (G_OBJECT (tb), "parent-set", 
+			  G_CALLBACK (tb_on_parent_set_cb), NULL);
+
+	gtk_icon_view_enable_model_drag_source (GTK_ICON_VIEW (tb), (GdkModifierType)0,
+						target_table, G_N_ELEMENTS (target_table), 
+						(GdkDragAction)GDK_ACTION_COPY);
+
+	g_signal_connect (G_OBJECT (tb), "drag-data-get", 
+			  G_CALLBACK (tb_on_drag_data_get_cb), NULL);
+}
+
+GtkWidget *
+eog_thumb_view_new (const Glib::RefPtr<ui::ImageListStore> & store)
+{
+	EogThumbView *tb;	
+
+	tb = (EogThumbView *)g_object_new (EOG_TYPE_THUMB_VIEW, NULL);
+	gtk_icon_view_set_model (GTK_ICON_VIEW (tb), GTK_TREE_MODEL(store->gobj()));
+	tb->priv->store = store;
+
+	return GTK_WIDGET (tb);
+}
+
+void
+eog_thumb_view_set_model (EogThumbView * tb, 
+						  const Glib::RefPtr<ui::ImageListStore> & store)
+{
+//	gint index;
+	g_return_if_fail (EOG_IS_THUMB_VIEW (tb));
+	
+	tb->priv->store = store;
+
+//	index = eog_list_store_get_initial_pos (get_pointer(store));
+
+	gtk_icon_view_set_model (GTK_ICON_VIEW (tb), GTK_TREE_MODEL(store->gobj()));
+
+//	if (index >= 0) {
+//		GtkTreePath *path = gtk_tree_path_new_from_indices (index, -1);
+//		gtk_icon_view_select_path (GTK_ICON_VIEW (tb), path);
+//		gtk_icon_view_scroll_to_path (GTK_ICON_VIEW (tb), path, FALSE, 0, 0);
+//		gtk_tree_path_free (path);
+//	}
+}
+
+Glib::RefPtr<ui::ImageListStore> eog_thumb_view_get_model    (EogThumbView *view)
+{
+	g_return_val_if_fail (EOG_IS_THUMB_VIEW (view), 
+						  Glib::RefPtr<ui::ImageListStore>());
+	return view->priv->store;
+}
+
+void
+eog_thumb_view_set_item_height (EogThumbView *tb, gint height)
+{
+	g_return_if_fail (EOG_IS_THUMB_VIEW (tb));
+
+	g_object_set (tb->priv->pixbuf_cell, 
+	              "height", height, 
+	              NULL);
+}
+
+static void
+eog_thumb_view_get_n_selected_helper (GtkIconView * /*tb*/,
+									  GtkTreePath * /*path*/,
+				      gpointer data)
+{
+	/* data is of type (guint *) */
+	(*(guint *) data) ++;
+}
+
+guint
+eog_thumb_view_get_n_selected (EogThumbView *tb)
+{
+	guint count = 0;
+	gtk_icon_view_selected_foreach (GTK_ICON_VIEW (tb),
+					eog_thumb_view_get_n_selected_helper,
+					(&count));
+	return count;
+}
+
+#if 0
+static EogImage *
+eog_thumb_view_get_image_from_path (EogThumbView *tb, GtkTreePath *path)
+{
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	EogImage *image;
+
+	model = gtk_icon_view_get_model (GTK_ICON_VIEW (tb));
+	gtk_tree_model_get_iter (model, &iter, path);
+
+	gtk_tree_model_get (model, &iter,
+			    EOG_LIST_STORE_EOG_IMAGE, &image,
+			    -1);
+			    
+	return image;
+}
+
+EogImage *
+eog_thumb_view_get_first_selected_image (EogThumbView *tb)
+{
+	/* The returned list is not sorted! We need to find the 
+	   smaller tree path value => tricky and expensive. Do we really need this?
+	*/
+	GtkTreePath *path;
+	EogImage *image;
+	gchar *text_path;
+	GList *list = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (tb));
+
+	if (list == NULL) {
+		return NULL;
+	}
+
+	path = (GtkTreePath *) (list->data);
+
+	/* debugging purposes */
+	image = eog_thumb_view_get_image_from_path (tb, path);
+	text_path = gtk_tree_path_to_string (path);
+	g_free (text_path);
+
+	g_list_foreach (list, (GFunc) gtk_tree_path_free , NULL);
+	g_list_free (list);
+
+	return image;
+}
+#endif
+
+GList *
+eog_thumb_view_get_selected_images (EogThumbView *tb)
+{
+	GList *l, *item;
+	GList *list = NULL;
+
+	GtkTreePath *path;
+
+	l = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (tb));
+
+	for (item = l; item != NULL; item = item->next) {
+		path = (GtkTreePath *) item->data;
+// FIXME
+//		list = g_list_prepend (list, eog_thumb_view_get_image_from_path (tb, path));
+		gtk_tree_path_free (path);
+	}
+
+	g_list_free (l);
+	list = g_list_reverse (list);
+
+	return list;
+}
+
+#if 0
+void
+eog_thumb_view_set_current_image (EogThumbView *tb, 
+								  const db::LibFile::Ptr &image,
+								  gboolean deselect_other)
+{
+	GtkTreePath *path;
+	Glib::RefPtr<ui::ImageListStore> store;
+	gint pos;
+
+	store = eog_thumb_view_get_model(tb);
+	pos = eog_list_store_get_pos_by_image (get_pointer(store), image);
+	path = gtk_tree_path_new_from_indices (pos, -1);
+
+	if (path == NULL) {
+		return;
+	}
+
+	if (deselect_other) {
+		gtk_icon_view_unselect_all (GTK_ICON_VIEW (tb));
+	}
+	
+	gtk_icon_view_select_path (GTK_ICON_VIEW (tb), path);
+	gtk_icon_view_scroll_to_path (GTK_ICON_VIEW (tb), path, FALSE, 0, 0);
+
+	gtk_tree_path_free (path);
+}
+
+void
+eog_thumb_view_select_single (EogThumbView *tb, 
+			      EogThumbViewSelectionChange change)
+{
+	GtkTreePath *path = NULL;
+	GList *list;
+	gint n_items;
+
+	g_return_if_fail (EOG_IS_THUMB_VIEW (tb));
+	Glib::RefPtr<ui::ImageListStore> store = eog_thumb_view_get_model (tb);
+
+	n_items = eog_list_store_length (get_pointer(store));
+
+	if (n_items == 0) {
+		return;
+	}
+	
+	if (eog_thumb_view_get_n_selected (tb) == 0) {
+		switch (change) {
+		case EOG_THUMB_VIEW_SELECT_RIGHT:
+		case EOG_THUMB_VIEW_SELECT_FIRST:
+			path = gtk_tree_path_new_first ();
+			break;
+		case EOG_THUMB_VIEW_SELECT_LEFT:
+		case EOG_THUMB_VIEW_SELECT_LAST:
+			path = gtk_tree_path_new_from_indices (n_items - 1, -1);
+		}
+	} else {
+		list = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (tb));
+		path = gtk_tree_path_copy ((GtkTreePath *) list->data);
+		g_list_foreach (list, (GFunc) gtk_tree_path_free , NULL);
+		g_list_free (list);
+		
+		gtk_icon_view_unselect_all (GTK_ICON_VIEW (tb));
+		
+		switch (change) {
+		case EOG_THUMB_VIEW_SELECT_LEFT:
+			if (!gtk_tree_path_prev (path)) {
+				gtk_tree_path_free (path);
+				path = gtk_tree_path_new_from_indices (n_items - 1, -1);
+			}
+			break;
+		case EOG_THUMB_VIEW_SELECT_RIGHT:
+			if (gtk_tree_path_get_indices (path) [0] == n_items - 1) {
+				gtk_tree_path_free (path);
+				path = gtk_tree_path_new_first ();
+			} else {
+				gtk_tree_path_next (path);
+			}
+			break;
+		case EOG_THUMB_VIEW_SELECT_FIRST:
+			gtk_tree_path_free (path);
+			path = gtk_tree_path_new_first ();
+			break;
+		case EOG_THUMB_VIEW_SELECT_LAST:
+			gtk_tree_path_free (path);
+			path = gtk_tree_path_new_from_indices (n_items - 1, -1);
+		}
+	}
+
+	gtk_icon_view_select_path (GTK_ICON_VIEW (tb), path);
+	gtk_icon_view_scroll_to_path (GTK_ICON_VIEW (tb), path, FALSE, 0, 0);
+	gtk_tree_path_free (path);
+}
+
+
+void
+eog_thumb_view_set_thumbnail_popup (EogThumbView *tb,
+				    GtkMenu      *menu)
+{
+	g_return_if_fail (EOG_IS_THUMB_VIEW (tb));
+	g_return_if_fail (tb->priv->menu == NULL);
+
+	tb->priv->menu = (GtkWidget*)g_object_ref (menu);
+
+	gtk_menu_attach_to_widget (GTK_MENU (tb->priv->menu), 
+				   GTK_WIDGET (tb), 
+				   NULL);
+
+	g_signal_connect (G_OBJECT (tb), "button_press_event",
+			  G_CALLBACK (tb_on_button_press_event_cb), NULL);
+	
+}
+#endif
+
+
+static void 
+eog_thumb_view_popup_menu (EogThumbView *tb, GdkEventButton *event)
+{
+	GtkWidget *popup;
+	int button, event_time;
+
+	popup = tb->priv->menu;
+	
+	if (event) {
+		button = event->button;
+		event_time = event->time;
+	} else {
+		button = 0;
+		event_time = gtk_get_current_event_time ();
+	}
+	
+	gtk_menu_popup (GTK_MENU (popup), NULL, NULL, NULL, NULL, 
+			button, event_time);
+}

Added: trunk/src/ui/thumb-view/eog-thumb-view.h
==============================================================================
--- (empty file)
+++ trunk/src/ui/thumb-view/eog-thumb-view.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,91 @@
+/* Eye Of Gnome - Thumbnail View
+ *
+ * Copyright (C) 2006 The Free Software Foundation
+ *
+ * Author: Claudio Saavedra <csaavedra alumnos utalca cl>
+ *
+ * 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 EOG_THUMB_VIEW_H
+#define EOG_THUMB_VIEW_H
+
+#include <gtk/gtkiconview.h>
+#include "ui/imageliststore.h"
+
+/*#include "eog-image.h"*/
+//#include "eog-list-store.h"
+
+#include "db/libfile.h"
+
+G_BEGIN_DECLS
+
+#define EOG_TYPE_THUMB_VIEW            (eog_thumb_view_get_type ())
+#define EOG_THUMB_VIEW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EOG_TYPE_THUMB_VIEW, EogThumbView))
+#define EOG_THUMB_VIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  EOG_TYPE_THUMB_VIEW, EogThumbViewClass))
+#define EOG_IS_THUMB_VIEW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EOG_TYPE_THUMB_VIEW))
+#define EOG_IS_THUMB_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  EOG_TYPE_THUMB_VIEW))
+#define EOG_THUMB_VIEW_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  EOG_TYPE_THUMB_VIEW, EogThumbViewClass))
+
+typedef struct _EogThumbView EogThumbView;
+typedef struct _EogThumbViewClass EogThumbViewClass;
+typedef struct _EogThumbViewPrivate EogThumbViewPrivate;
+
+typedef enum {
+	EOG_THUMB_VIEW_SELECT_LEFT = 0,
+	EOG_THUMB_VIEW_SELECT_RIGHT,
+	EOG_THUMB_VIEW_SELECT_FIRST,
+	EOG_THUMB_VIEW_SELECT_LAST
+} EogThumbViewSelectionChange;
+
+struct _EogThumbView {
+	GtkIconView icon_view;
+	EogThumbViewPrivate *priv;
+};
+
+struct _EogThumbViewClass {
+	 GtkIconViewClass icon_view_class;
+};
+
+GType       eog_thumb_view_get_type 		    (void) G_GNUC_CONST;
+
+GtkWidget  *eog_thumb_view_new 			    (const Glib::RefPtr<ui::ImageListStore> & store);
+
+void	    eog_thumb_view_set_model 		    (EogThumbView *view, 
+												 const Glib::RefPtr<ui::ImageListStore> & store);
+Glib::RefPtr<ui::ImageListStore> eog_thumb_view_get_model    (EogThumbView *view);
+
+void        eog_thumb_view_set_item_height          (EogThumbView *view,
+						     gint          height);
+
+guint	    eog_thumb_view_get_n_selected 	    (EogThumbView *view);
+
+//EogImage   *eog_thumb_view_get_first_selected_image (EogThumbView *view);
+
+GList      *eog_thumb_view_get_selected_images 	    (EogThumbView *view);
+
+void        eog_thumb_view_select_single 	    (EogThumbView *view, 
+						     EogThumbViewSelectionChange change);
+
+void        eog_thumb_view_set_current_image	    (EogThumbView *view, 
+						     const db::LibFile::Ptr&     image,
+						     gboolean     deselect_other);
+
+//void        eog_thumb_view_set_thumbnail_popup      (EogThumbView *view, 
+//						     GtkMenu      *menu);
+
+G_END_DECLS
+
+#endif /* EOG_THUMB_VIEW_H */

Added: trunk/src/ui/workspacecontroller.cpp
==============================================================================
--- (empty file)
+++ trunk/src/ui/workspacecontroller.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,236 @@
+/*
+ * niepce - ui/workspacecontroller.cpp
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <boost/any.hpp>
+#include <boost/bind.hpp>
+
+#include <glibmm/i18n.h>
+
+#include <gtkmm/icontheme.h>
+#include <gtkmm/box.h>
+
+#include "utils/debug.h"
+#include "niepce/notifications.h"
+#include "db/library.h" // FIXME uh oh. this shouldn't be
+#include "libraryclient/libraryclient.h"
+#include "framework/application.h"
+#include "niepcewindow.h"
+#include "workspacecontroller.h"
+
+
+using framework::Application;
+
+namespace ui {
+
+	WorkspaceController::WorkspaceController()
+		: framework::Controller()
+	{
+		Glib::RefPtr< Gtk::IconTheme > icon_theme(Application::app()->getIconTheme());
+		try {
+			m_icons[ICON_FOLDER] = icon_theme->load_icon(
+				Glib::ustring("folder"), 16, Gtk::ICON_LOOKUP_USE_BUILTIN);
+			m_icons[ICON_PROJECT] = icon_theme->load_icon(
+				Glib::ustring("applications-accessories"), 16, 
+				Gtk::ICON_LOOKUP_USE_BUILTIN);
+			m_icons[ICON_ROLL] = icon_theme->load_icon(
+				Glib::ustring("emblem-photos"), 16,
+				Gtk::ICON_LOOKUP_USE_BUILTIN);
+			// FIXME use an icon that make more sense.
+			m_icons[ICON_KEYWORD] = icon_theme->load_icon(
+				Glib::ustring("application-certificate"), 16, 
+				Gtk::ICON_LOOKUP_USE_BUILTIN);
+		}
+		catch(const Gtk::IconThemeError & e)
+		{
+			ERR_OUT("Exception %s.", e.what().c_str());
+		}
+	}
+
+	libraryclient::LibraryClient::Ptr WorkspaceController::getLibraryClient()
+	{
+		return	boost::dynamic_pointer_cast<NiepceWindow>(m_parent.lock())->getLibraryClient();
+	}
+
+
+	void WorkspaceController::on_lib_notification(const framework::Notification::Ptr &n)
+	{
+		DBG_OUT("notification for workspace");
+		if(n->type() == niepce::NOTIFICATION_LIB) {
+			db::LibNotification ln = boost::any_cast<db::LibNotification>(n->data());
+			switch(ln.type) {
+			case db::Library::NOTIFY_ADDED_FOLDERS:
+			{
+				db::LibFolder::ListPtr l 
+					= boost::any_cast<db::LibFolder::ListPtr>(ln.param);
+				DBG_OUT("received added folders # %d", l->size());
+				for_each(l->begin(), l->end(), 
+						 boost::bind(&WorkspaceController::add_folder_item, 
+									 this, _1));
+				break;
+			}
+			case db::Library::NOTIFY_ADDED_KEYWORD:
+			{
+				db::Keyword::Ptr k
+					= boost::any_cast<db::Keyword::Ptr>(ln.param);
+				DBG_ASSERT(k, "keyword must not be NULL");
+				add_keyword_item(k);
+				break;
+			}
+			case db::Library::NOTIFY_ADDED_KEYWORDS:
+			{
+				db::Keyword::ListPtr l
+					= boost::any_cast<db::Keyword::ListPtr>(ln.param);
+				DBG_ASSERT(l, "keyword list must not be NULL");
+				for_each(l->begin(), l->end(), 
+						 boost::bind(&WorkspaceController::add_keyword_item, 
+									 this, _1));
+				break;
+			}
+			case db::Library::NOTIFY_FOLDER_COUNTED:
+			{
+				std::pair<int,int> count(boost::any_cast<std::pair<int,int> >(ln.param));
+				DBG_OUT("count for folder %d is %d", count.first, count.second);
+				std::map<int, Gtk::TreeIter>::iterator iter
+					= m_folderidmap.find( count.first );
+				if(iter != m_folderidmap.end()) {
+					Gtk::TreeRow row = *(iter->second);
+					row[m_librarycolumns.m_count] = boost::lexical_cast<Glib::ustring>(count.second);
+				}
+
+				break;
+			}
+			default:
+				break;
+			}
+		}
+	}
+
+	void WorkspaceController::on_count_notification(const framework::Notification::Ptr &n)
+	{
+		if(n->type() == niepce::NOTIFICATION_COUNT) {
+			DBG_OUT("received NOTIFICATION_COUNT");
+		}
+	}
+
+
+	void WorkspaceController::on_libtree_selection()
+	{
+		Glib::RefPtr<Gtk::TreeSelection> selection = m_librarytree.get_selection();
+		Gtk::TreeModel::iterator selected = selection->get_selected();
+		if((*selected)[m_librarycolumns.m_type] == FOLDER_ITEM)
+		{
+			int id = (*selected)[m_librarycolumns.m_id];
+			getLibraryClient()->queryFolderContent(id);
+		}
+		else if((*selected)[m_librarycolumns.m_type] == KEYWORD_ITEM)
+		{
+			int id = (*selected)[m_librarycolumns.m_id];
+			getLibraryClient()->queryKeywordContent(id);			
+		}
+		else 
+		{
+			DBG_OUT("selected something not a folder");
+		}
+	}
+
+	void WorkspaceController::add_keyword_item(const db::Keyword::Ptr & k)
+	{
+		Gtk::TreeModel::iterator iter(add_item(m_treestore, m_keywordsNode->children(), 
+											   m_icons[ICON_KEYWORD], k->keyword(), k->id(), 
+											   KEYWORD_ITEM));
+//		getLibraryClient()->countKeyword(f->id());
+		m_keywordsidmap[k->id()] = iter;
+	}
+
+	void WorkspaceController::add_folder_item(const db::LibFolder::Ptr & f)
+	{
+		Gtk::TreeModel::iterator iter(add_item(m_treestore, m_folderNode->children(), 
+											   m_icons[ICON_ROLL], 
+											   f->name(), f->id(), FOLDER_ITEM));
+		getLibraryClient()->countFolder(f->id());
+		m_folderidmap[f->id()] = iter;
+	}
+
+	Gtk::TreeModel::iterator
+	WorkspaceController::add_item(const Glib::RefPtr<Gtk::TreeStore> &treestore,
+								  const Gtk::TreeNodeChildren & childrens,
+								  const Glib::RefPtr<Gdk::Pixbuf> & icon,
+								  const Glib::ustring & label, 
+								  int id, int type) const
+	{
+		Gtk::TreeModel::iterator iter;
+		Gtk::TreeModel::Row row;
+		iter = treestore->append(childrens);
+		row = *iter;
+		row[m_librarycolumns.m_icon] = icon;
+		row[m_librarycolumns.m_label] = label; 
+		row[m_librarycolumns.m_id] = id;
+		row[m_librarycolumns.m_type] = type;
+		row[m_librarycolumns.m_count] = "--";
+		return iter;
+	}
+
+
+	Gtk::Widget * WorkspaceController::buildWidget()
+	{
+		m_treestore = Gtk::TreeStore::create(m_librarycolumns);
+		m_librarytree.set_model(m_treestore);
+
+		m_folderNode = add_item(m_treestore, m_treestore->children(),
+								m_icons[ICON_FOLDER], 
+								Glib::ustring(_("Pictures")), 0,
+								FOLDERS_ITEM);
+		m_projectNode = add_item(m_treestore, m_treestore->children(),
+								 m_icons[ICON_PROJECT], 
+								 Glib::ustring(_("Projects")), 0,
+								 PROJECTS_ITEM);
+		m_keywordsNode = add_item(m_treestore, m_treestore->children(),
+								  m_icons[ICON_KEYWORD],
+								  Glib::ustring(_("Keywords")), 0,
+								  KEYWORDS_ITEM);
+
+		m_librarytree.set_headers_visible(false);
+		m_librarytree.append_column("", m_librarycolumns.m_icon);
+		int num = m_librarytree.append_column("", m_librarycolumns.m_label);
+		Gtk::TreeViewColumn * col = m_librarytree.get_column(num - 1);
+		col->set_expand(true);
+		num = m_librarytree.append_column("", m_librarycolumns.m_count);
+		col = m_librarytree.get_column(num - 1);
+		col->set_alignment(1.0);
+
+		// TODO make it a mnemonic
+		m_label.set_text_with_mnemonic(Glib::ustring(_("_Workspace")));
+		m_label.set_mnemonic_widget(m_librarytree);
+		m_vbox.pack_start(m_label, Gtk::PACK_SHRINK);
+		m_vbox.pack_start(m_librarytree);
+
+		m_librarytree.get_selection()->signal_changed().connect (
+			sigc::mem_fun(this, 
+						  &WorkspaceController::on_libtree_selection));
+
+		return &m_vbox;
+	}
+	
+	void WorkspaceController::on_ready()
+	{
+		getLibraryClient()->getAllFolders();
+		getLibraryClient()->getAllKeywords();
+	}
+
+}

Added: trunk/src/ui/workspacecontroller.h
==============================================================================
--- (empty file)
+++ trunk/src/ui/workspacecontroller.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,133 @@
+/*
+ * niepce - ui/workspacecontroller.h
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+#ifndef __UI_WORKSPACECONTROLLER_H__
+#define __UI_WORKSPACECONTROLLER_H__
+
+#include <boost/array.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <glibmm/ustring.h>
+
+#include <gtkmm/treeview.h>
+#include <gtkmm/label.h>
+#include <gtkmm/treestore.h>
+
+#include "db/libfolder.h"
+#include "framework/controller.h"
+#include "framework/notification.h"
+
+namespace Gtk {
+}
+
+
+namespace ui {
+
+	class WorkspaceController
+		: public framework::Controller
+	{
+	public:
+		typedef boost::shared_ptr<WorkspaceController> Ptr;
+
+		enum {
+			FOLDERS_ITEM,
+			PROJECTS_ITEM,
+			KEYWORDS_ITEM,
+			FOLDER_ITEM,
+			PROJECT_ITEM,
+			KEYWORD_ITEM
+		};
+
+		WorkspaceController();
+		class WorkspaceTreeColumns 
+			: public Gtk::TreeModelColumnRecord
+		{
+		public:
+			
+			WorkspaceTreeColumns()
+				{ 
+					add(m_icon);
+					add(m_id);
+					add(m_label);  
+					add(m_type);
+					add(m_count);
+				}
+			Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf> > m_icon;
+			Gtk::TreeModelColumn<int> m_id;
+			Gtk::TreeModelColumn<Glib::ustring> m_label;
+			Gtk::TreeModelColumn<int> m_type;
+			Gtk::TreeModelColumn<Glib::ustring> m_count;
+		};
+
+		virtual void on_ready();
+
+		void on_lib_notification(const framework::Notification::Ptr &);
+		void on_count_notification(const framework::Notification::Ptr &);
+		void on_libtree_selection();
+
+	protected:
+		virtual Gtk::Widget * buildWidget();
+
+	private:
+		libraryclient::LibraryClient::Ptr getLibraryClient();
+
+		/** add a folder item to the treeview */
+		void add_folder_item(const db::LibFolder::Ptr & f);
+		/** add a keyword item to the treeview */
+		void add_keyword_item(const db::Keyword::Ptr & k);
+		/** add a tree item in the treeview 
+		 * @param treestore the treestore to add to
+		 * @param childrens the children subtree to add to
+		 * @param icon the icon for the item
+		 * @param label the item label
+		 * @param id the item id (in the database)
+		 * @paran type the type of node
+		 */
+		Gtk::TreeModel::iterator add_item(const Glib::RefPtr<Gtk::TreeStore> & treestore, 
+										  const Gtk::TreeNodeChildren & childrens,
+										  const Glib::RefPtr<Gdk::Pixbuf> & icon,
+										  const Glib::ustring & label, 
+										  int id, int type) const;
+		enum {
+			ICON_FOLDER = 0,
+			ICON_PROJECT,
+			ICON_ROLL,
+			ICON_KEYWORD,
+			_ICON_SIZE
+		};
+		boost::array< Glib::RefPtr<Gdk::Pixbuf>, _ICON_SIZE > m_icons;
+		WorkspaceTreeColumns           m_librarycolumns;
+		Gtk::VBox                      m_vbox;
+		Gtk::Label                     m_label;
+		Gtk::TreeView                  m_librarytree;
+		Gtk::TreeModel::iterator       m_folderNode;  /**< the folder node */
+		Gtk::TreeModel::iterator       m_projectNode; /**< the project node */
+		Gtk::TreeModel::iterator       m_keywordsNode; /**< the keywords node */
+		Glib::RefPtr<Gtk::TreeStore>   m_treestore;   /**< the treestore */
+		std::map<int, Gtk::TreeIter>   m_folderidmap;
+		std::map<int, Gtk::TreeIter>   m_projectidmap;
+		std::map<int, Gtk::TreeIter>   m_keywordsidmap;
+	};
+
+
+}
+
+#endif

Added: trunk/src/utils/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/utils/Makefile.am	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,78 @@
+
+DIST_SUBDIR = db
+
+INCLUDES = -I$(top_srcdir)/src/ @EXEMPI_CFLAGS@ @LIBXML2_CFLAGS@
+
+TESTS = testmoniker testfiles testgeometry testxmp testufrawmeta \
+	teststringutils test_db test_db2 test_db3 test_db4
+
+EXTRA_DIST = test.xmp test2.ufraw
+
+check_PROGRAMS = testmoniker testfiles testgeometry testxmp testufrawmeta \
+	teststringutils test_db test_db2 test_db3 test_db4
+
+testmoniker_SOURCES = testmoniker.cpp
+testmoniker_LDADD = libniepceutils.a \
+	@BOOST_THREAD_LIBS@
+
+testfiles_SOURCES = testfiles.cpp
+testfiles_LDADD = libniepceutils.a \
+	@BOOST_FILESYSTEM_LIBS@ @BOOST_THREAD_LIBS@
+
+testgeometry_SOURCES = testgeometry.cpp
+testgeometry_LDADD = libniepceutils.a \
+	@BOOST_THREAD_LIBS@
+
+testxmp_SOURCES = testxmp.cpp
+testxmp_LDADD = libniepceutils.a \
+	@EXEMPI_LIBS@ @BOOST_FILESYSTEM_LIBS@ @BOOST_THREAD_LIBS@
+
+testufrawmeta_SOURCES = testufrawmeta.cpp
+testufrawmeta_CXXFLAGS =  -g -O0
+testufrawmeta_LDADD = libniepceutils.a ../niepce/libniepceglobals.a \
+	@EXEMPI_LIBS@ @BOOST_FILESYSTEM_LIBS@ @LIBXML2_LIBS@ \
+	@BOOST_THREAD_LIBS@
+
+teststringutils_SOURCES = teststringutils.cpp
+teststringutils_LDADD = 
+
+test_db_SOURCES = db/test_db.cpp
+test_db_LDADD = libniepceutils.a \
+	@BOOST_THREAD_LIBS \
+	@SQLITE3_LIBS@
+
+test_db2_SOURCES = db/test_db2.cpp
+test_db2_LDADD = libniepceutils.a \
+	@BOOST_THREAD_LIBS \
+	@SQLITE3_LIBS@
+
+test_db3_SOURCES = db/test_db3.cpp
+test_db3_LDADD = libniepceutils.a \
+	@BOOST_THREAD_LIBS \
+	@SQLITE3_LIBS@
+
+test_db4_SOURCES = db/test_db4.cpp
+test_db4_LDADD = libniepceutils.a \
+	@BOOST_THREAD_LIBS \
+	@SQLITE3_LIBS@
+
+noinst_LIBRARIES = libniepceutils.a
+
+libniepceutils_a_SOURCES = exception.h exception.cpp \
+	mtqueue.h string.h \
+	autoflag.h \
+	exempi.h exempi.cpp \
+	ufrawmeta.h ufrawmeta.cpp \
+	buffer.h logstreamutils.h \
+	moniker.h moniker.cpp \
+	debug.h debug.cpp boost.h \
+	files.h files.cpp \
+	geometry.h geometry.cpp \
+	thread.h thread.cpp worker.h \
+	databinder.h databinder.cpp \
+	db/iconnectiondriver.h db/iconnectionmanagerdriver.h \
+	db/insertstatement.cpp db/insertstatement.h\
+	db/sqlstatement.cpp db/sqlstatement.h\
+	db/sqlite/sqlitecnxdrv.cpp db/sqlite/sqlitecnxdrv.h \
+	db/sqlite/sqlitecnxmgrdrv.cpp db/sqlite/sqlitecnxmgrdrv.h
+

Added: trunk/src/utils/autoflag.h
==============================================================================
--- (empty file)
+++ trunk/src/utils/autoflag.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,47 @@
+/*
+ * niepce - utils/autoflag.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _UTILS_AUTOFLAG_H_
+#define _UTILS_AUTOFLAG_H_
+
+#include "utils/debug.h"
+
+namespace utils {
+
+class AutoFlag
+{
+public:
+	AutoFlag(bool & f)
+		: m_flag(f)
+		{
+			m_flag = true;
+		}
+	~AutoFlag()
+		{
+			m_flag = false;
+		}
+
+private:
+	bool & m_flag;
+};
+
+}
+
+#endif

Added: trunk/src/utils/boost.h
==============================================================================
--- (empty file)
+++ trunk/src/utils/boost.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,61 @@
+/*
+ * niepce - utils/boost.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+#ifndef _UTILS_BOOST_H_
+#define _UTILS_BOOST_H_
+
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+
+namespace Glib {
+
+	/** Dereference Glib::RefPtr<> for use in boost::bind */
+	template< class T_CppObject > inline
+	T_CppObject *get_pointer(const Glib::RefPtr< T_CppObject >& p)
+	{
+		return p.operator->();
+	}
+
+}
+
+namespace utils {
+
+/** function to make a shared_ptr<> form a weak_ptr<> 
+ * idea from http://lists.boost.org/boost-users/2007/01/24384.php
+ */
+template<class T> 
+boost::shared_ptr<T> shared_ptr_from( boost::weak_ptr<T> const & wpt )
+{
+    return boost::shared_ptr<T>( wpt ); // throws on wpt.expired()
+} 
+
+}
+
+/** need to convert a weak_ptr<> to a shared_ptr<> in the bind() 
+ * this allow breaking some circular references.
+ */
+#define BIND_SHARED_PTR(_type, _spt) \
+    boost::bind(&utils::shared_ptr_from<_type>, boost::weak_ptr<_type>(_spt))
+
+
+#endif

Added: trunk/src/utils/buffer.h
==============================================================================
--- (empty file)
+++ trunk/src/utils/buffer.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; indent-tabs-mode:nil; c-basic-offset:4; -*- */
+
+/*
+ *This file is part of the Nemiver Project.
+ *
+ *Nemiver 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,
+ *or (at your option) any later version.
+ *
+ *Nemiver 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 Nemiver;
+ *see the file COPYING.
+ *If not, write to the Free Software Foundation,
+ *Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *See COPYRIGHT file copyright information.
+ */
+#ifndef __NEMIVER_BUFFER_H__
+#define __NEMIVER_BUFFER_H__
+
+namespace utils {
+
+class Buffer {
+    char * m_data ;
+    unsigned long m_len ;
+
+public:
+
+    Buffer (): m_data (NULL), m_len (0)
+    {}
+
+    Buffer (const char *a_buf, unsigned long a_len)
+    {
+        m_data = const_cast<char*>(a_buf) ;
+        m_len = a_len ;
+    }
+
+    Buffer (const Buffer &a_buf) : m_data (a_buf.m_data), m_len (a_buf.m_len)
+    {}
+
+    void set
+        (const char* a_buf, unsigned long a_len)
+    {
+        m_data = const_cast<char*> (a_buf);
+        m_len = a_len ;
+    }
+
+    Buffer& operator= (Buffer &a_buf)
+    {
+        if (this == &a_buf)
+            return *this ;
+        m_data = a_buf.m_data ;
+        m_len = a_buf.m_len ;
+    }
+
+    const char* get_data () const
+    {
+        return m_data ;
+    }
+
+    unsigned long get_len () const
+    {
+        return m_len;
+    }
+};//end class Bufer
+
+}//end namespace common
+
+#endif //__NEMIVER_BUFFER_H__
+

Added: trunk/src/utils/databinder.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/databinder.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,45 @@
+/*
+ * niepce - utils/databinder.cpp
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "debug.h"
+#include "databinder.h"
+
+namespace utils {
+
+DataBinderPool::~DataBinderPool()
+{
+	for(iterator iter = begin(); iter != end(); iter++)
+	{
+		boost::checked_delete(*iter);
+	}
+}
+
+
+void DataBinderPool::add_binder(DataBinderBase *binder)
+{
+	push_back(binder);
+}
+
+void DataBinderPool::destroy(DataBinderPool *pool)
+{
+	delete pool;
+}
+
+
+}

Added: trunk/src/utils/databinder.h
==============================================================================
--- (empty file)
+++ trunk/src/utils/databinder.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,57 @@
+/*
+ * niepce - utils/databinder.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+
+#ifndef _UTILS_DATABINDER_H_
+#define _UTILS_DATABINDER_H_
+
+#include <vector>
+
+#include <boost/utility.hpp>
+
+
+namespace utils {
+
+/** @brief the base class for all the data binders */
+class DataBinderBase
+	: public boost::noncopyable
+{
+public:
+	virtual ~DataBinderBase() 
+		{}
+};
+
+/** @brief a pool of data binders */
+class DataBinderPool
+	: private std::vector< DataBinderBase* >
+{
+public:
+	~DataBinderPool();
+	/** add a data binder to the pool. the pool will own the pointer */
+	void add_binder(DataBinderBase *);
+	static void destroy(DataBinderPool *pool);
+};
+
+
+}
+
+
+#endif

Added: trunk/src/utils/db/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/utils/db/Makefile.am	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,3 @@
+
+
+DIST_SUBDIR = sqlite

Added: trunk/src/utils/db/iconnectiondriver.h
==============================================================================
--- (empty file)
+++ trunk/src/utils/db/iconnectiondriver.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; indent-tabs-mode:nil; c-basic-offset: 4  -*- */
+
+/*
+ *This file is part of the Nemiver Project.
+ *
+ *Nemiver 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,
+ *or (at your option) any later version.
+ *
+ *Nemiver 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 Nemiver;
+ *see the file COPYING.
+ *If not, write to the Free Software Foundation,
+ *Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *See COPYRIGHT file copyright information.
+ */
+#ifndef __NEMIVER_I_CONNECTION_DRIVER_H__
+#define __NEMIVER_I_CONNECTION_DRIVER_H__
+
+#include <stdint.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/function.hpp>
+
+namespace utils {
+class Buffer ;
+//class UString ;
+}
+
+namespace db {
+
+enum ColumnType {
+    COLUMN_TYPE_INT=1,
+    COLUMN_TYPE_BIG_INT=1<<1,
+    COLUMN_TYPE_DECIMAL=1<<2,
+    COLUMN_TYPE_DOUBLE=1<<3,
+    COLUMN_TYPE_DATETIME=1<<3,
+    COLUMN_TYPE_STRING=1<<4,
+    COLUMN_TYPE_BLOB=1<<5,
+    COLUMN_TYPE_UNKNOWN=1<<30// should be last
+};
+
+class SQLStatement;
+
+class IConnectionDriver {
+
+public:
+    typedef boost::shared_ptr<IConnectionDriver> Ptr;
+
+    typedef boost::function<void (void)> f0_t;
+
+    virtual ~IConnectionDriver ()
+    {}
+
+    virtual void close () = 0;
+
+    virtual const char* get_last_error () const = 0;
+
+    virtual bool start_transaction () = 0 ;
+
+    virtual bool commit_transaction () = 0 ;
+
+    virtual bool rollback_transaction () = 0 ;
+
+    virtual bool execute_statement (const SQLStatement &a_statement) = 0;
+    
+    virtual bool create_function0(const std::string & name, const f0_t & f) = 0;
+
+    virtual bool should_have_data () const = 0;
+
+    virtual bool read_next_row () = 0;
+
+    virtual int64_t last_row_id() = 0;
+
+    virtual unsigned int get_number_of_columns () const = 0;
+
+    virtual bool get_column_type (uint32_t a_offset,
+                                  enum ColumnType &) const = 0;
+
+    virtual bool get_column_name (uint32_t a_offset,
+                                  utils::Buffer &a_name) const = 0;
+
+    virtual bool get_column_content (uint32_t a_offset,
+                                     utils::Buffer &a_column_content) const = 0;
+
+    virtual bool get_column_content (uint32_t a_offset,
+                                     int32_t &a_column_content) const = 0;
+
+    virtual bool get_column_content (uint32_t a_offset,
+                                     int64_t &a_column_content) const = 0;
+
+    virtual bool get_column_content (uint32_t a_offset,
+                                     double& a_column_content) const = 0;
+
+    virtual bool get_column_content (uint32_t a_offset,
+                                     std::string& a_column_content) const = 0;
+
+};//end IConnectionDriver
+
+
+}//end common
+
+#endif //__NEMIVER_I_CONNECTION_DRIVER_H__
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/utils/db/iconnectionmanagerdriver.h
==============================================================================
--- (empty file)
+++ trunk/src/utils/db/iconnectionmanagerdriver.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; indent-tabs-mode:nil; c-basic-offset:4; -*- */
+
+/*
+ *This file is part of the Nemiver Project.
+ *
+ *Nemiver 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,
+ *or (at your option) any later version.
+ *
+ *Nemiver 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 Nemiver;
+ *see the file COPYING.
+ *If not, write to the Free Software Foundation,
+ *Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *See COPYRIGHT file copyright information.
+ */
+#ifndef __NEMIVER_I_CONNECTION_MANAGER_DRIVER_H__
+#define __NEMIVER_I_CONNECTION_MANAGER_DRIVER_H__
+
+#include <string>
+#include <boost/shared_ptr.hpp>
+
+#include "iconnectiondriver.h"
+
+namespace db {
+
+class IConnectionManagerDriver ;
+
+class DBDesc {
+    std::string m_type ;
+    std::string m_host ;
+    unsigned long m_port ;
+    std::string m_name ;
+
+public:
+    DBDesc ()
+    {}
+    DBDesc (const std::string &a_host,
+            const unsigned long &a_port,
+            const std::string &a_db_name) 
+        : m_host(a_host),
+          m_port(a_port),
+          m_name(a_db_name)
+        {
+        }
+
+    const std::string host () const
+    {
+        return m_host;
+    }
+    unsigned long port () const
+    {
+        return m_port;
+    }
+    const std::string name () const
+    {
+        return m_name;
+    }
+    const std::string type () const
+    {
+        return m_type;
+    }
+
+    void set_host (const std::string &a_host)
+    {
+        m_host = a_host;
+    }
+    void set_port (const unsigned long &a_port)
+    {
+        m_port = a_port;
+    }
+    void set_name (const std::string &a_name)
+    {
+        m_name = a_name;
+    }
+    void set_type (const std::string &a_type)
+    {
+        m_type = a_type;
+    }
+};//end class DBDesc
+
+class  IConnectionManagerDriver {
+public:
+    typedef boost::shared_ptr<IConnectionManagerDriver> Ptr;
+    IConnectionManagerDriver()
+    {
+    }
+
+    virtual ~IConnectionManagerDriver () {};
+    virtual IConnectionDriver::Ptr connect_to_db(const DBDesc &a_db_desc,
+                                                 const std::string &a_user,
+                                                 const std::string &a_pass) = 0;
+};//end class IConnectionManagerDriver
+
+}//end namespace common
+
+#endif //__NEMIVER_I_CONNECTION_MANAGER_DRIVER_H__
+

Added: trunk/src/utils/db/insertstatement.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/db/insertstatement.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; indent-tabs-mode:nil; c-basic-offset:4; -*- */
+
+/*
+ *This file is part of the Nemiver Project.
+ *
+ *Nemiver 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,
+ *or (at your option) any later version.
+ *
+ *Nemiver 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 Nemiver;
+ *see the file COPYING.
+ *If not, write to the Free Software Foundation,
+ *Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *See COPYRIGHT file copyright information.
+ */
+
+#include "utils/logstreamutils.h"
+#include "utils/exception.h"
+#include "insertstatement.h"
+
+namespace db {
+
+struct InsertStatementPriv {
+    std::string table_name ;
+    ColumnList columns ;
+    std::string string_repr ;
+};//end InsertStatementPriv
+
+InsertStatement::InsertStatement (const std::string &a_table_name,
+                                  ColumnList &a_columns)
+{
+    m_priv = new InsertStatementPriv ;
+    m_priv->table_name = a_table_name ;
+    m_priv->columns = a_columns ;
+}
+
+InsertStatement::~InsertStatement ()
+{
+    if (m_priv) {
+        delete m_priv ;
+        m_priv = NULL ;
+    }
+}
+
+void
+InsertStatement::set (const std::string &a_table_name, ColumnList &a_columns)
+{
+    m_priv->table_name = a_table_name ;
+    m_priv->columns = a_columns ;
+    m_priv->string_repr = "" ;
+}
+
+const std::string&
+InsertStatement::to_string () const
+{
+    std::string str ;
+
+//    LOG_FUNCTION_SCOPE_NORMAL_DD ;
+
+    if (m_priv->string_repr == "") {
+        RETURN_VAL_IF_FAIL (m_priv->table_name != "", m_priv->string_repr) ;
+        RETURN_VAL_IF_FAIL (m_priv->columns.size () != 0,
+                            m_priv->string_repr) ;
+        RETURN_VAL_IF_FAIL (m_priv->columns.size ()
+                            == m_priv->columns.size (),
+                            m_priv->string_repr) ;
+        str = "INSERT INTO " + m_priv->table_name + "( " ;
+        std::string col_names, col_values ;
+        for (ColumnList::iterator it = m_priv->columns.begin () ;
+                it != m_priv->columns.end () ;
+                ++it) {
+            if (col_names.size ()) {
+                col_names += ", " ;
+                col_values += ", " ;
+            }
+            col_names += it->get_name () ;
+            if (it->get_auto_increment ()) {
+                col_values += "null" ;//for mysql and sqlite
+                //TODO: find an elegant way to consider
+                //the case of other databases.
+            } else {
+                col_values += "\'" + it->get_value () + "\'" ;
+            }
+        }
+        str += col_names + ") VALUES (" + col_values + ")";
+        m_priv->string_repr = str ;
+    }
+    return m_priv->string_repr ;
+}
+
+const ColumnList&
+InsertStatement::get_columns () const
+{
+    return m_priv->columns ;
+}
+
+const std::string&
+InsertStatement::get_table_name () const
+{
+    return m_priv->table_name ;
+}
+
+
+}//end namespace common
+

Added: trunk/src/utils/db/insertstatement.h
==============================================================================
--- (empty file)
+++ trunk/src/utils/db/insertstatement.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; indent-tabs-mode:nil; c-basic-offset:4; -*- */
+
+/*
+ *This file is part of the Nemiver Project.
+ *
+ *Nemiver 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,
+ *or (at your option) any later version.
+ *
+ *Nemiver 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 Nemiver;
+ *see the file COPYING.
+ *If not, write to the Free Software Foundation,
+ *Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *See COPYRIGHT file copyright information.
+ */
+#ifndef __NEMIVER_INSERT_STATEMENT_H__
+#define __NEMIVER_INSERT_STATEMENT_H__
+
+#include "sqlstatement.h"
+
+namespace db {
+
+struct InsertStatementPriv ;
+class InsertStatement : public SQLStatement
+{
+    friend struct InsertStatementPriv;
+    InsertStatementPriv *m_priv ;
+
+    //forbid copy
+    InsertStatement (const InsertStatement &) ;
+    InsertStatement& operator= (const InsertStatement &) ;
+
+public:
+
+    InsertStatement(const std::string &a_table_name,
+                     ColumnList &a_columns) ;
+    ~InsertStatement() ;
+
+    const std::string& to_string() const ;
+
+    const ColumnList& get_columns() const ;
+
+    const std::string& get_table_name() const ;
+
+    void set (const std::string &a_table_name, ColumnList &a_columns) ;
+};//end InsertStatement
+
+}// end namespace common
+
+#endif //__NEMIVER_INSERT_STATEMENT_H__
+

Added: trunk/src/utils/db/sqlite/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/utils/db/sqlite/Makefile.am	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,4 @@
+
+
+SOURCES =	sqlitecnxmgrdrv.cpp sqlitecnxmgrdrv.h \
+	sqlitecnxdrv.cpp sqlitecnxdrv.h
\ No newline at end of file

Added: trunk/src/utils/db/sqlite/sqlitecnxdrv.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/db/sqlite/sqlitecnxdrv.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,587 @@
+/*
+ * (c) 2007-2008 Hubert Figuiere
+ *
+ ***********************************************************
+ *This file was part of the Nemiver Project.
+ *
+ *Nemiver 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,
+ *or (at your option) any later version.
+ *
+ *Nemiver 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 Nemiver;
+ *see the file COPYING.
+ *If not, write to the Free Software Foundation,
+ *Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *See COPYRIGHT file copyright information.
+ */
+
+
+#include "config.h"
+
+#include <string.h>
+#include <string>
+#include <list>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+
+#include <sqlite3.h>
+#include "utils/exception.h"
+#include "utils/buffer.h"
+#include "utils/debug.h"
+#include "db/sqlstatement.h"
+#include "sqlitecnxdrv.h"
+
+namespace db { namespace sqlite {
+	
+	
+		struct Sqlite3Unref {
+			void operator () (sqlite3 *a_ptr)
+				{
+					sqlite3_close(a_ptr) ;
+				}
+		};//end struct Sqlite3Unref
+	
+		template<class PointerType,
+				 class UnreferenceFunctor>
+		class SafePtr
+		{
+		protected:
+			mutable PointerType *m_pointer ;
+		
+		
+		public:
+			explicit SafePtr (const PointerType *a_pointer) 
+				:	m_pointer(const_cast<PointerType*>(a_pointer))
+				{
+				}
+		
+			SafePtr() 
+				: m_pointer(NULL)
+				{
+				}
+
+			~SafePtr ()
+				{
+					unreference();
+				}
+		
+			operator bool () const
+				{
+					return (m_pointer != NULL);
+				}
+		
+			void
+			reset (const PointerType *a_pointer)
+				{
+					if (a_pointer != m_pointer) {
+						unreference() ;
+						m_pointer = const_cast<PointerType*>(a_pointer) ;
+					}
+				}
+		
+			PointerType*
+			get () const
+				{
+					return m_pointer ;
+				}
+		
+			void
+			unreference ()
+				{
+					if (m_pointer) {
+						UnreferenceFunctor do_unref ;
+						do_unref (m_pointer) ;
+					}
+				}
+
+		};//end class SafePtr
+
+
+
+		typedef SafePtr<sqlite3, Sqlite3Unref> Sqlite3SafePtr ;
+
+
+		struct SqliteCnxDrv::Priv {
+			//sqlite3 database connection handle.
+			//Must be de-allocated by a call to
+			//sqlite3_close ();
+			Sqlite3SafePtr sqlite ;
+
+			//the current prepared sqlite statement.
+			//It must be deallocated after the result set
+			//has been read, before we can close the db,
+			//or before another statement is prepared.
+			sqlite3_stmt *cur_stmt ;
+
+			//the result of the last sqlite3_step() function, or -333
+			int last_execution_result ;
+            
+            std::list<boost::function<void (void)> > userfunctions;
+
+			Priv():
+				sqlite(NULL),
+				cur_stmt(NULL),
+				last_execution_result(-333)
+				{
+				}
+
+			bool step_cur_statement() ;
+
+			bool check_offset(uint32_t a_offset) ;
+		};
+
+		bool
+		SqliteCnxDrv::Priv::step_cur_statement ()
+		{
+			RETURN_VAL_IF_FAIL (cur_stmt, false) ;
+			last_execution_result = sqlite3_step (cur_stmt) ;
+			bool result (false) ;
+
+		decide:
+			switch (last_execution_result) {
+			case SQLITE_BUSY:
+				//db file is locked. Let's try again a couple of times.
+				for (int i=0;i<2;++i) {
+					sleep (1) ;
+					last_execution_result = sqlite3_step (cur_stmt) ;
+					if (last_execution_result != SQLITE_BUSY)
+						goto decide ;
+				}
+				result = false ;
+				break ;
+			case SQLITE_DONE:
+				//the statement was successfuly executed and
+				//there is no more data to fecth.
+				//go advertise the good news
+				result = true ;
+				break ;
+			case SQLITE_ROW:
+				//the statement was successfuly executed and
+				//there is some rows waiting to be fetched.
+				//go advertise the good news.
+				result = true ;
+				break;
+			case SQLITE_ERROR:
+				LOG_ERROR (std::string("sqlite3_step() encountered a runtime error:")
+						   + sqlite3_errmsg (sqlite.get ())) ;
+				if (cur_stmt) {
+					sqlite3_finalize (cur_stmt) ;
+					cur_stmt = NULL ;
+				}
+				result = false ;
+				break;
+			case SQLITE_MISUSE:
+				LOG_ERROR ("seems like sqlite3_step() has been called too much ...") ;
+				if (cur_stmt) {
+					sqlite3_finalize (cur_stmt) ;
+					cur_stmt = NULL ;
+				}
+				result = false ;
+				break;
+			default:
+				LOG_ERROR ("got an unknown error code from sqlite3_step") ;
+				if (cur_stmt) {
+					sqlite3_finalize (cur_stmt) ;
+					cur_stmt = NULL ;
+				}
+				result = false ;
+				break;
+			}
+			return result ;
+		}
+
+		bool
+		SqliteCnxDrv::Priv::check_offset (uint32_t a_offset)
+		{
+			if (!cur_stmt
+				|| (static_cast<long> (a_offset) >= sqlite3_column_count (cur_stmt)))
+				return false ;
+			return true ;
+		}
+
+		SqliteCnxDrv::SqliteCnxDrv (sqlite3 *a_sqlite_handle)
+		{
+			boost::recursive_mutex::scoped_lock lock(m_mutex);
+			THROW_IF_FAIL (a_sqlite_handle) ;
+			m_priv.reset (new Priv) ;
+			m_priv->sqlite.reset (a_sqlite_handle) ;
+		}
+
+		SqliteCnxDrv::~SqliteCnxDrv ()
+		{
+			boost::recursive_mutex::scoped_lock lock(m_mutex);
+			close () ;
+			m_priv.reset(NULL);
+		}
+
+	    sqlite3 * SqliteCnxDrv::sqlite_handle() const
+		{
+			if (m_priv && m_priv->sqlite) {
+				return m_priv->sqlite.get ();
+			}
+			return NULL;
+		}
+
+		const char*
+		SqliteCnxDrv::get_last_error () const
+		{
+			boost::recursive_mutex::scoped_lock lock(m_mutex);
+			if (m_priv && m_priv->sqlite) {
+				return sqlite3_errmsg (m_priv->sqlite.get ());
+			}
+			return NULL ;
+		}
+
+		bool
+		SqliteCnxDrv::start_transaction ()
+		{
+			THROW_IF_FAIL (m_priv && m_priv->sqlite) ;
+			return execute_statement (SQLStatement ("begin transaction")) ;
+		}
+
+		bool
+		SqliteCnxDrv::commit_transaction ()
+		{
+			THROW_IF_FAIL (m_priv && m_priv->sqlite) ;
+			return execute_statement (SQLStatement ("commit")) ;
+		}
+
+		bool
+		SqliteCnxDrv::rollback_transaction ()
+		{
+			THROW_IF_FAIL (m_priv && m_priv->sqlite) ;
+			return execute_statement (SQLStatement ("rollback")) ;
+		}
+
+		bool
+		SqliteCnxDrv::execute_statement (const SQLStatement &a_statement)
+		{
+		  boost::recursive_mutex::scoped_lock lock(m_mutex);
+
+		  THROW_IF_FAIL (m_priv && m_priv->sqlite) ;
+		  DBG_OUT("sql string: %s", a_statement.to_string().c_str()) ;
+		  
+		  //if the previous statement
+		  //(also contains the resulting context of a query
+		  //execution) hasn't been deleted, delete it before
+		  //we go forward.
+		  if (m_priv->cur_stmt) {
+			sqlite3_finalize (m_priv->cur_stmt) ;
+			m_priv->cur_stmt = NULL ;
+			m_priv->last_execution_result = SQLITE_OK ;
+		  }
+			
+		  if (a_statement.to_string().empty())
+			return false ;
+			
+		  int status = sqlite3_prepare (m_priv->sqlite.get (),
+										a_statement.to_string ().c_str (),
+										a_statement.to_string ().size(),
+										&m_priv->cur_stmt,
+										NULL) ;
+
+		  if (status != SQLITE_OK) {
+			ERR_OUT("sqlite3_prepare() failed, returning: %d: %s: sql was: '%s'",
+					status, get_last_error(), a_statement.to_string().c_str()) ;
+			return false ;
+		  }
+
+		  const std::vector< SQLStatement::binder_t > & bindings = a_statement.bindings();
+		  for(std::vector< SQLStatement::binder_t >::const_iterator iter = bindings.begin();
+			  iter != bindings.end(); iter++) 
+		  {
+			ColumnType ctype = iter->get<0>();
+			int idx = iter->get<1>();
+			switch(ctype) {
+			case COLUMN_TYPE_STRING:
+			{
+			  try {
+				std::string text(boost::any_cast<std::string>(iter->get<2>()));
+				sqlite3_bind_text(m_priv->cur_stmt, idx, text.c_str(), 
+								  text.size(), SQLITE_STATIC);
+			  }
+			  catch(...)
+			  {
+				DBG_OUT("e");
+			  }
+			  break;
+			}
+			case COLUMN_TYPE_BLOB:
+			{
+			  try {
+				const utils::Buffer* blob(boost::any_cast<const utils::Buffer*>(iter->get<2>()));
+				sqlite3_bind_blob(m_priv->cur_stmt, idx, blob->get_data(), 
+								  blob->get_len(), SQLITE_STATIC);
+			  }
+			  catch(...)
+			  {
+				DBG_OUT("e");
+			  }
+			  break;
+			}
+			default:
+			  break;
+			}
+		  }
+		  
+		  THROW_IF_FAIL (m_priv->cur_stmt) ;
+		  if (!should_have_data ()) {
+			return m_priv->step_cur_statement () ;
+		  }
+
+		  return true ;
+		}
+
+        namespace {
+            
+        void wrapper(sqlite3_context* ctx, int, sqlite3_value**)
+        {
+            boost::function<void (void)> *f;
+            f = (boost::function<void (void)>*)sqlite3_user_data(ctx);
+            (*f)();
+        }
+
+        }
+
+        bool 
+        SqliteCnxDrv::create_function0(const std::string & name, 
+                                       const db::IConnectionDriver::f0_t & f)
+        {
+            m_priv->userfunctions.push_back(f);
+            sqlite3_create_function(m_priv->sqlite.get(), name.c_str(), 
+                                    0, SQLITE_ANY, 
+                                    (void*)&(m_priv->userfunctions.back()), 
+                                    &wrapper, NULL, NULL);
+
+            return true;
+        }
+
+
+		bool
+		SqliteCnxDrv::should_have_data () const
+		{
+			boost::recursive_mutex::scoped_lock lock(m_mutex);
+			THROW_IF_FAIL (m_priv) ;
+
+			if (get_number_of_columns () > 0)
+				return true ;
+			return false ;
+		}
+
+		bool
+		SqliteCnxDrv::read_next_row ()
+		{
+			boost::recursive_mutex::scoped_lock lock(m_mutex);
+			THROW_IF_FAIL (m_priv) ;
+			if (m_priv->cur_stmt) {
+				if (m_priv->last_execution_result == SQLITE_DONE) {
+					return false ;
+				} else {
+					bool res = m_priv->step_cur_statement () ;
+					if (res == true) {
+						if (m_priv->last_execution_result == SQLITE_DONE) {
+							//there is no more data to fetch.
+							return false ;
+						} else {
+							return true ;
+						}
+					}
+				}
+			}
+			return false ;
+		}
+
+		unsigned int
+		SqliteCnxDrv::get_number_of_columns () const
+		{
+			boost::recursive_mutex::scoped_lock lock(m_mutex);
+			THROW_IF_FAIL (m_priv) ;
+			if (!m_priv->cur_stmt)
+				return 0 ;
+			return sqlite3_column_count (m_priv->cur_stmt) ;
+		}
+
+		bool
+		SqliteCnxDrv::get_column_content (uint32_t a_offset,
+										  utils::Buffer &a_column_content) const
+		{
+			boost::recursive_mutex::scoped_lock lock(m_mutex);
+			THROW_IF_FAIL (m_priv) ;
+
+			RETURN_VAL_IF_FAIL (m_priv->check_offset (a_offset), false) ;
+			a_column_content.set
+				(static_cast<const char*>(sqlite3_column_blob (m_priv->cur_stmt, a_offset)),
+				 sqlite3_column_bytes (m_priv->cur_stmt, a_offset)) ;
+			return true ;
+		}
+
+		bool
+		SqliteCnxDrv::get_column_content (uint32_t a_offset,
+										  int32_t &a_column_content) const
+		{
+			boost::recursive_mutex::scoped_lock lock(m_mutex);
+			THROW_IF_FAIL (m_priv) ;
+
+			RETURN_VAL_IF_FAIL (m_priv->check_offset (a_offset), false) ;
+			int type = sqlite3_column_type (m_priv->cur_stmt, a_offset) ;
+			if ((type != SQLITE_INTEGER) && (type != SQLITE_NULL)) {
+				ERR_OUT("column number %d is not of integer type", 
+						static_cast<int>(a_column_content));
+				return false ;
+			}
+			a_column_content = sqlite3_column_int (m_priv->cur_stmt, a_offset) ;
+			return true ;
+		}
+
+		bool
+		SqliteCnxDrv::get_column_content (uint32_t a_offset,
+										  int64_t &a_column_content) const
+		{
+			boost::recursive_mutex::scoped_lock lock(m_mutex);
+			THROW_IF_FAIL (m_priv) ;
+
+			RETURN_VAL_IF_FAIL (m_priv->check_offset (a_offset), false) ;
+			int type = sqlite3_column_type (m_priv->cur_stmt, a_offset) ;
+			if ((type != SQLITE_INTEGER) && (type != SQLITE_NULL)) {
+				ERR_OUT("column number %d is not of integer type", 
+						static_cast<int>(a_column_content));
+				return false ;
+			}
+			a_column_content = sqlite3_column_int64 (m_priv->cur_stmt, a_offset) ;
+			return true ;
+		}
+
+
+		bool
+		SqliteCnxDrv::get_column_content (uint32_t a_offset,
+										  double& a_column_content) const
+		{
+			boost::recursive_mutex::scoped_lock lock(m_mutex);
+			THROW_IF_FAIL (m_priv) ;
+			RETURN_VAL_IF_FAIL (m_priv->check_offset (a_offset), false) ;
+			int type = sqlite3_column_type (m_priv->cur_stmt, a_offset) ;
+			if ((type != SQLITE_FLOAT) && (type != SQLITE_NULL)) {
+				ERR_OUT("column number %d is not of type float", (int) a_offset);
+				return false ;
+			}
+			a_column_content = sqlite3_column_double (m_priv->cur_stmt, a_offset) ;
+			return true ;
+		}
+
+		bool
+		SqliteCnxDrv::get_column_content (uint32_t a_offset,
+										  std::string& a_column_content) const
+		{
+			boost::recursive_mutex::scoped_lock lock(m_mutex);
+			THROW_IF_FAIL (m_priv) ;
+
+			RETURN_VAL_IF_FAIL (m_priv->check_offset (a_offset), false) ;
+			int type = sqlite3_column_type (m_priv->cur_stmt, a_offset) ;
+			if (type == SQLITE_BLOB) {
+				ERR_OUT("column number %d is of type blob", (int) a_offset) ;
+				return false ;
+			}
+			const char * text = reinterpret_cast<const char*>(sqlite3_column_text(m_priv->cur_stmt,a_offset));
+			DBG_ASSERT(text, "column content can't be NULL");
+			if(text) {
+				a_column_content = text;
+			}
+			else {
+				ERR_OUT("text column content is NULL");
+			}
+			return true ;
+		}
+
+		bool
+		SqliteCnxDrv::get_column_type (uint32_t a_offset,
+									   enum ColumnType &a_type) const
+		{
+			boost::recursive_mutex::scoped_lock lock(m_mutex);
+			THROW_IF_FAIL (m_priv) ;
+			RETURN_VAL_IF_FAIL (m_priv->check_offset (a_offset), false) ;
+			int type = sqlite3_column_type (m_priv->cur_stmt, a_offset) ;
+
+			switch (type) {
+			case SQLITE_INTEGER:
+				a_type = COLUMN_TYPE_INT ;
+				break;
+			case SQLITE_FLOAT:
+				a_type = COLUMN_TYPE_DOUBLE ;
+				break;
+			case SQLITE_TEXT:
+				a_type = COLUMN_TYPE_STRING ;
+				break ;
+			case SQLITE_BLOB:
+				a_type = COLUMN_TYPE_BLOB ;
+				break;
+
+			case SQLITE_NULL:
+				a_type = COLUMN_TYPE_BLOB ;
+				break ;
+			default:
+				a_type = COLUMN_TYPE_UNKNOWN ;
+				break;
+			}
+			return true ;
+		}
+
+		bool
+		SqliteCnxDrv::get_column_name (uint32_t a_offset, utils::Buffer &a_name) const
+		{
+			boost::recursive_mutex::scoped_lock lock(m_mutex);
+			THROW_IF_FAIL (m_priv) ;
+			RETURN_VAL_IF_FAIL (m_priv->check_offset (a_offset), false) ;
+			const char* name = sqlite3_column_name (m_priv->cur_stmt, a_offset) ;
+			if (!name)
+				return false ;
+			a_name.set (name, strlen (name)) ;
+			return true ;
+		}
+
+		void
+		SqliteCnxDrv::close ()
+		{
+			boost::recursive_mutex::scoped_lock lock(m_mutex);
+			THROW_IF_FAIL (m_priv) ;
+
+			if (m_priv->sqlite) {
+				if (m_priv->cur_stmt) {
+					sqlite3_finalize (m_priv->cur_stmt) ;
+					m_priv->cur_stmt = NULL;
+				}
+			}
+		}
+
+		int64_t
+		SqliteCnxDrv::last_row_id()
+		{
+			boost::recursive_mutex::scoped_lock lock(m_mutex);
+			THROW_IF_FAIL (m_priv);
+			if (m_priv->sqlite) {
+				return sqlite3_last_insert_rowid(m_priv->sqlite.get());
+			}
+			return -1;
+		}
+	}//end namespace sqlite
+}//end namespace db
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/utils/db/sqlite/sqlitecnxdrv.h
==============================================================================
--- (empty file)
+++ trunk/src/utils/db/sqlite/sqlitecnxdrv.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,115 @@
+/*
+ * (c) 2007-2008 Hubert Figuiere
+ *
+ ***********************************************************
+ *This file is part of the Dodji Common Library Project.
+ *
+ *Dodji 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,
+ *or (at your option) any later version.
+ *
+ *Dodji 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 Dodji;
+ *see the file COPYING.
+ *If not, write to the Free Software Foundation,
+ *Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *See COPYRIGHT file copyright information.
+ */
+#ifndef __NEMIVER_SQLITE_CNX_DRV_H__
+#define __NEMIVER_SQLITE_CNX_DRV_H__
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/thread/recursive_mutex.hpp>
+
+#include "utils/db/iconnectiondriver.h"
+
+struct sqlite3;
+
+namespace db {
+
+
+namespace sqlite {
+
+class SqliteCnxDrv: public db::IConnectionDriver {
+    struct Priv ;
+    friend class SqliteCnxMgrDrv ;
+    boost::scoped_ptr<Priv> m_priv ;
+    mutable boost::recursive_mutex m_mutex;
+
+    friend void boost::checked_delete<SqliteCnxDrv>(SqliteCnxDrv * x);
+
+    //forbid copy
+    SqliteCnxDrv (const SqliteCnxDrv &) ;
+    SqliteCnxDrv& operator= (const SqliteCnxDrv &) ;
+
+    SqliteCnxDrv (sqlite3 *a_sqlite_handle) ;
+
+    virtual ~SqliteCnxDrv () ;
+public:
+    sqlite3* sqlite_handle() const;
+    const char* get_last_error () const ;
+
+    bool start_transaction () ;
+
+    bool commit_transaction () ;
+
+    bool rollback_transaction () ;
+
+    bool execute_statement (const db::SQLStatement &a_statement) ;
+
+    bool create_function0(const std::string & name, const f0_t & f);
+
+    bool should_have_data () const ;
+
+    bool read_next_row () ;
+
+    virtual int64_t last_row_id();
+
+    unsigned int get_number_of_columns () const ;
+
+    bool get_column_type (uint32_t a_offset,
+                          enum db::ColumnType &a_type) const ;
+
+    bool get_column_name (uint32_t a_offset, utils::Buffer &a_name) const ;
+
+    bool get_column_content (uint32_t a_offset,
+                             utils::Buffer &a_column_content) const ;
+
+    bool get_column_content (uint32_t a_offset,
+                             int32_t &a_column_content) const ;
+
+    bool get_column_content (uint32_t a_offset,
+                             int64_t &a_column_content) const ;
+
+    bool get_column_content (uint32_t a_offset,
+                             double& a_column_content) const ;
+
+    bool get_column_content (uint32_t a_offset,
+                             std::string& a_column_content) const ;
+
+    void close () ;
+};//end IConnectionDriver
+
+}//end namespace sqlite
+}//end namspace common
+
+#endif //__NEMIVER_SQLITE_CNX_DRV_H__
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+

Added: trunk/src/utils/db/sqlite/sqlitecnxmgrdrv.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/db/sqlite/sqlitecnxmgrdrv.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; indent-tabs-mode:tab; c-basic-offset:2 -*- */
+
+/*
+ *This file is part of the Nemiver Project.
+ *
+ *Nemiver 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,
+ *or (at your option) any later version.
+ *
+ *Nemiver 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 Nemiver;
+ *see the file COPYING.
+ *If not, write to the Free Software Foundation,
+ *Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *See COPYRIGHT file copyright information.
+ */
+#include "config.h"
+
+#include <string>
+
+#include <sqlite3.h>
+
+#include "utils/exception.h"
+#include "sqlitecnxdrv.h"
+#include "sqlitecnxmgrdrv.h"
+
+
+
+namespace db {
+    namespace sqlite {
+
+struct SqliteCnxMgrDrvPriv {
+};//end SqliteCnxMgrDrvPriv
+
+SqliteCnxMgrDrv::SqliteCnxMgrDrv () :
+    IConnectionManagerDriver ()
+{
+    m_priv = new SqliteCnxMgrDrvPriv () ;
+}
+
+SqliteCnxMgrDrv::~SqliteCnxMgrDrv ()
+{
+    if (!m_priv) {
+        return ;
+    }
+
+    delete m_priv ;
+    m_priv = NULL ;
+}
+
+
+IConnectionDriver::Ptr
+SqliteCnxMgrDrv::connect_to_db (const DBDesc &a_db_desc,
+                                const std::string &a_user,
+                                const std::string &a_pass)
+{
+
+    if (a_user == "") {}
+    if (a_pass == "") {}
+    sqlite3 *sqlite (NULL);
+
+    //HACK. As we are using sqlite, make sure to use a db file
+    //that is in $HOME/.nemiver/db/sqlite
+    std::string db_name(a_db_desc.name ()) ;
+#if 0
+    if (!Glib::path_is_absolute(db_name)) {
+        if (!Glib::file_test (env::get_user_db_dir (),
+                              Glib::FILE_TEST_IS_DIR)) {
+            env::create_user_db_dir () ;
+        }
+        db_name = Glib::build_filename (env::get_user_db_dir (),
+                                        db_name).c_str () ;
+    }
+#endif
+
+    int result = sqlite3_open (db_name.c_str (), &sqlite) ;
+    if (result != SQLITE_OK) {
+			THROW (std::string("could not connect to sqlite database '") 
+						 + db_name + "' : "
+						 + std::string (sqlite3_errmsg(sqlite)));
+        sqlite3_close (sqlite);
+        exit(1);
+    }
+    db::IConnectionDriver::Ptr connection_driver(new SqliteCnxDrv (sqlite)) ;
+    return connection_driver ;
+}
+
+#if 0
+class SqliteCnxMgrModule : public DynamicModule {
+    void get_info (Info &a_info) const
+    {
+        a_info.module_name = "org.nemiver.db.sqlitedriver.default" ;
+        a_info.module_description = "The nemiver database driver for sqlite."
+                                    " Implements the IConnectionManagerDriver "
+                                    "iface" ;
+        a_info.module_version = "0.0.1" ;
+    }
+
+    void do_init ()
+    {
+    }
+
+    bool lookup_interface (const std::string &a_iface_name,
+                           DynModIfaceSafePtr &a_iface)
+    {
+        if (a_iface_name == "IConnectionManagerDriver") {
+            static SqliteCnxMgrDrv s_driver (this) ;
+            s_driver.enable_refcount (false) ;
+            a_iface.reset (&s_driver, true) ;
+        } else {
+            return false ;
+        }
+        return true ;
+    }
+};//end class SqliteCnxMgrModule
+#endif
+
+}
+}
+
+extern "C" {
+#if 0
+bool
+NEMIVER_API
+nemiver_common_create_dynamic_module_instance (void **a_new_instance)
+{
+    RETURN_VAL_IF_FAIL (a_new_instance, false) ;
+
+    try {
+        nemiver::common::sqlite::SqliteCnxMgrModule *module =
+            new nemiver::common::sqlite::SqliteCnxMgrModule ;
+        *a_new_instance = module ;
+    } catch (std::exception &e) {
+        TRACE_EXCEPTION (e) ;
+        return false ;
+    } catch (Glib::Exception &e) {
+        TRACE_EXCEPTION (e) ;
+        return false ;
+    } catch (...) {
+        LOG ("Got an unknown exception") ;
+        return false ;
+    }
+    return true ;
+}
+#endif
+
+}//end extern C

Added: trunk/src/utils/db/sqlite/sqlitecnxmgrdrv.h
==============================================================================
--- (empty file)
+++ trunk/src/utils/db/sqlite/sqlitecnxmgrdrv.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; indent-tabs-mode:nil; c-basic-offset:4 -*- */
+
+/*
+ *This file is part of the Nemiver Project.
+ *
+ *Nemiver 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,
+ *or (at your option) any later version.
+ *
+ *Nemiver 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 Nemiver;
+ *see the file COPYING.
+ *If not, write to the Free Software Foundation,
+ *Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *See COPYRIGHT file copyright information.
+ */
+#ifndef __NEMIVER_SQLITE_CNX_MGR_DRV_H__
+#define __NEMIVER_SQLITE_CNX_MGR_DRV_H__
+
+#include <boost/shared_ptr.hpp>
+
+#include "utils/db/iconnectionmanagerdriver.h"
+#include "utils/db/iconnectiondriver.h"
+
+namespace db {
+namespace sqlite {
+
+struct SqliteCnxMgrDrvPriv ;
+class SqliteCnxMgrDrv 
+    : public db::IConnectionManagerDriver 
+{
+    friend struct SqliteCnxMgrDrvPriv ;
+    struct SqliteCnxMgrDrvPriv *m_priv ;
+
+    //forbid copy
+    SqliteCnxMgrDrv (const SqliteCnxMgrDrv&) ;
+    SqliteCnxMgrDrv& operator= (const SqliteCnxMgrDrv&) ;
+
+public:
+    // TODO: abstract
+    SqliteCnxMgrDrv ();
+    virtual ~SqliteCnxMgrDrv () ;
+
+    db::IConnectionDriver::Ptr connect_to_db(const db::DBDesc &a_desc,
+                                             const std::string &a_user,
+                                             const std::string &a_pass) ;
+};//end SqliteCnxMgrDrv
+
+}//end namespace sqlite
+}//end namespace db
+
+#endif //__NEMIVER_SQLITE_CNX_MGR_DRV_H__
+

Added: trunk/src/utils/db/sqlstatement.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/db/sqlstatement.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; indent-tabs-mode:nil; c-basic-offset:4; -*- */
+
+/*
+ *This file is part of the Nemiver Project.
+ *
+ *Nemiver 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,
+ *or (at your option) any later version.
+ *
+ *Nemiver 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 Nemiver;
+ *see the file COPYING.
+ *If not, write to the Free Software Foundation,
+ *Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *See COPYRIGHT file copyright information.
+ */
+
+#include "utils/exception.h"
+#include "utils/logstreamutils.h"
+#include "sqlstatement.h"
+
+#include <string>
+
+
+namespace db {
+
+struct SQLStatementPriv
+{
+    std::string sql_string ;
+};
+
+const std::string&
+SQLStatement::to_string () const
+{
+    THROW_IF_FAIL (m_priv) ;
+    return m_priv->sql_string ;
+}
+
+std::string
+SQLStatement::escape_string (const std::string &a_sql_string)
+{
+    std::string out_string ;
+
+    for(std::string::const_iterator iter = a_sql_string.begin();
+        iter != a_sql_string.end(); ++iter) {
+        char c = *iter;
+        out_string.append (1,c);
+        if (c == '\'') {
+            out_string.append (1,c);
+        }
+    }
+    return out_string ;
+}
+
+bool 
+SQLStatement::bind(int idx, const std::string & text)
+{
+    m_bindings.push_back(binder_t(COLUMN_TYPE_STRING, idx, 
+                                  boost::any(text)));
+    return true;
+}
+
+
+bool 
+SQLStatement::bind(int idx, const utils::Buffer & blob)
+{
+    m_bindings.push_back(binder_t(COLUMN_TYPE_BLOB, idx, 
+                                      boost::any(&blob)));
+    return true;
+}
+
+
+
+SQLStatement::SQLStatement (const std::string &a_sql_string)
+{
+    m_priv = new SQLStatementPriv ;
+    m_priv->sql_string = a_sql_string ;
+}
+
+SQLStatement::SQLStatement (const boost::format & _format)
+{
+    m_priv = new SQLStatementPriv ;
+    m_priv->sql_string = str(_format);
+}
+
+
+SQLStatement::SQLStatement (const SQLStatement &a_statement)
+{
+    m_priv = new SQLStatementPriv ;
+    m_priv->sql_string = a_statement.m_priv->sql_string ;
+}
+
+SQLStatement&
+SQLStatement::operator= (const SQLStatement &a_statement)
+{
+    if (this == &a_statement) {
+        return *this ;
+    }
+    m_priv->sql_string = a_statement.m_priv->sql_string ;
+    return *this ;
+}
+
+SQLStatement::~SQLStatement ()
+{
+    if (!m_priv)
+        return ;
+    delete m_priv ;
+}
+
+SQLStatement::operator const char* () const
+{
+    return to_string().c_str() ;
+}
+
+std::ostream&
+operator<< (std::ostream &a_os, const SQLStatement &a_s)
+{
+    a_os << a_s.to_string() ;
+    return a_os;
+}
+
+}//end db
+
+

Added: trunk/src/utils/db/sqlstatement.h
==============================================================================
--- (empty file)
+++ trunk/src/utils/db/sqlstatement.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; indent-tabs-mode:nil; c-basic-offset:4; -*- */
+/*
+ *This file is part of the Nemiver Project.
+ *
+ *Nemiver 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,
+ *or (at your option) any later version.
+ *
+ *Nemiver 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 Nemiver;
+ *see the file COPYING.
+ *If not, write to the Free Software Foundation,
+ *Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *See COPYRIGHT file copyright information.
+ */
+#ifndef __NEMIVER_SQL_STATEMENT_H__
+#define __NEMIVER_SQL_STATEMENT_H__
+
+#include <stdint.h>
+
+//#pragma GCC visibility push(default)
+#include <vector>
+//#pragma GCC visibility pop
+#include <string>
+#include <iostream>
+#include <boost/lexical_cast.hpp>
+#include <boost/format.hpp>
+#include <boost/tuple/tuple.hpp>
+#include <boost/any.hpp>
+
+#include "utils/buffer.h"
+#include "utils/db/iconnectiondriver.h"
+
+namespace db
+{
+
+class Column
+{
+    std::string m_name ;
+    std::string m_value ;
+    bool m_auto_increment ;
+
+public:
+
+    Column (const std::string &a_name="empty:empty",
+            const std::string &a_value="empty:empty",
+            bool a_auto_increment=false):
+            m_name (a_name),
+            m_value (a_value),
+            m_auto_increment (a_auto_increment)
+    {}
+
+    Column (const std::string &a_name,
+            uint64_t a_value,
+            bool a_auto_increment=false):
+            m_name (a_name),
+            m_value (boost::lexical_cast<std::string>(a_value)),
+            m_auto_increment (a_auto_increment)
+    {}
+
+    const std::string& get_name ()
+    {
+        return m_name ;
+    }
+    void set_name (const std::string &a_name)
+    {
+        m_name = a_name;
+    }
+    const std::string& get_value ()
+    {
+        return m_value ;
+    }
+    void set_value (const std::string &a_value)
+    {
+        m_value = a_value ;
+    }
+    void set_auto_increment (bool a_auto)
+    {
+        m_auto_increment = a_auto;
+    }
+    bool get_auto_increment ()
+    {
+        return m_auto_increment;
+    }
+};
+
+typedef std::vector<Column> ColumnList ;
+
+struct SQLStatementPriv ;
+
+class SQLStatement
+{
+    friend class Connection ;
+    friend struct SQLStatementPriv ;
+
+    SQLStatementPriv *m_priv ;
+
+public:
+
+    SQLStatement (const std::string &a_sql_string="") ;
+    SQLStatement (const boost::format & _format);
+
+    SQLStatement (const SQLStatement &) ;
+
+    SQLStatement& operator= (const SQLStatement &) ;
+
+    virtual ~SQLStatement () ;
+    virtual const std::string& to_string () const ;
+
+    static std::string escape_string (const std::string &a_sql_string) ;
+
+	bool bind(int idx, const std::string & text);
+	bool bind(int idx, const utils::Buffer & blob);
+
+    operator const char* () const ;
+    friend std::iostream& operator<< (std::iostream&, const SQLStatement&) ;
+
+    typedef boost::tuple<ColumnType, int, boost::any> binder_t;
+    const std::vector< binder_t > & bindings() const
+        { return m_bindings; }
+private:
+    std::vector< binder_t > m_bindings;
+};
+
+std::ostream & operator<< (std::ostream &, const SQLStatement &) ;
+
+}//end namespace db
+
+#endif //__NEMIVER_SQL_STATEMENT_H__

Added: trunk/src/utils/db/test_db.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/db/test_db.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,56 @@
+/*
+ * niepce - db/test_db.cpp
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <unistd.h>
+#include <sstream>
+#include <boost/scoped_ptr.hpp>
+
+#include "utils/buffer.h"
+#include "utils/debug.h"
+
+#include "sqlstatement.h"
+#include "insertstatement.h"
+#include "iconnectiondriver.h"
+#include "iconnectionmanagerdriver.h"
+#include "sqlite/sqlitecnxmgrdrv.h"
+
+#include <boost/test/minimal.hpp>
+
+
+//sqlstatement_test
+int test_main(int, char *[])
+{
+	BOOST_CHECK(1);
+
+	const char * sql = "SELECT * FROM foo WHERE bar='1'";
+	db::SQLStatement stmt(sql);
+
+	BOOST_CHECK(stmt.to_string() == sql);
+
+	db::SQLStatement stmt2 = stmt;
+
+	BOOST_CHECK(stmt2.to_string() == sql);
+	
+	BOOST_CHECK(db::SQLStatement::escape_string("d'oh") == "d''oh");
+
+	std::ostringstream ss;
+	ss << stmt;
+	BOOST_CHECK(ss.str() == sql);
+    return 0;
+}

Added: trunk/src/utils/db/test_db2.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/db/test_db2.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,58 @@
+/*
+ * niepce - db/test_db.cpp
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <unistd.h>
+#include <sstream>
+#include <boost/scoped_ptr.hpp>
+
+#include "utils/buffer.h"
+#include "utils/debug.h"
+
+#include "sqlstatement.h"
+#include "insertstatement.h"
+#include "iconnectiondriver.h"
+#include "iconnectionmanagerdriver.h"
+#include "sqlite/sqlitecnxmgrdrv.h"
+
+#include <boost/test/minimal.hpp>
+
+//insertstatement_test
+int test_main(int, char *[])
+{
+	db::ColumnList columns(2);
+
+	db::Column &c0(columns[0]);
+	c0.set_name("id");
+	c0.set_value(boost::lexical_cast<std::string>(1));
+	c0.set_auto_increment(true);
+
+	db::Column &c1(columns[1]);
+	c1.set_name("name");
+	c1.set_value("foo");
+
+	db::InsertStatement stmt("table", columns);
+	
+	std::string s = stmt.to_string();
+	BOOST_CHECK(s.size() != 0); 
+
+	BOOST_CHECK(s == "INSERT INTO table( id, name) VALUES (null, 'foo')");
+    return 0;
+}
+
+

Added: trunk/src/utils/db/test_db3.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/db/test_db3.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,83 @@
+/*
+ * niepce - db/test_db.cpp
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <unistd.h>
+#include <sstream>
+#include <boost/scoped_ptr.hpp>
+
+#include "utils/buffer.h"
+#include "utils/debug.h"
+
+#include "sqlstatement.h"
+#include "insertstatement.h"
+#include "iconnectiondriver.h"
+#include "iconnectionmanagerdriver.h"
+#include "sqlite/sqlitecnxmgrdrv.h"
+
+#include <boost/test/minimal.hpp>
+
+
+
+//insertblob_test
+int test_main(int, char *[])
+{
+	try {
+		db::IConnectionManagerDriver::Ptr mgr(new db::sqlite::SqliteCnxMgrDrv());
+		
+		db::DBDesc desc("", 0, "test.db");
+		db::IConnectionDriver::Ptr drv(mgr->connect_to_db(desc, "", ""));
+
+		db::SQLStatement schemacreate("CREATE TABLE foo "
+									  "(bar TEXT NOT NULL, "
+									  "baz BLOB)");
+		BOOST_CHECK(drv->execute_statement(schemacreate));
+
+		
+		const char * sql = "INSERT INTO foo (bar, baz) VALUES(?1, ?2);";
+		db::SQLStatement stmt(sql);
+		std::string s("zaphod");
+		utils::Buffer b("beeblebrox'", 11);
+		BOOST_CHECK(stmt.bind(1, s));
+		BOOST_CHECK(stmt.bind(2, b));
+		
+		BOOST_CHECK(drv->execute_statement(stmt));
+		BOOST_CHECK(!drv->should_have_data());
+
+		sql = "SELECT bar, baz FROM foo";
+		db::SQLStatement stmt2( sql );
+		BOOST_CHECK(drv->execute_statement(stmt2));
+		BOOST_CHECK(drv->should_have_data());
+		BOOST_CHECK(drv->read_next_row());
+		std::string s2;
+		utils::Buffer b2;
+		BOOST_CHECK(drv->get_column_content(0, s2));
+		BOOST_CHECK(s == s2);
+		BOOST_CHECK(drv->get_column_content(1, b2));
+		BOOST_CHECK(b.get_len() == b2.get_len());
+		BOOST_CHECK(std::string(b2.get_data()) == "beeblebrox'");
+
+		BOOST_CHECK(unlink("test.db") != -1);
+	}
+	catch(...) 
+	{
+		BOOST_CHECK(0);
+	}
+    return 0;
+}
+

Added: trunk/src/utils/db/test_db4.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/db/test_db4.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,62 @@
+/*
+ * niepce - db/test_db.cpp
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <unistd.h>
+#include <sstream>
+#include <boost/scoped_ptr.hpp>
+
+#include "utils/buffer.h"
+#include "utils/debug.h"
+
+#include "sqlstatement.h"
+#include "insertstatement.h"
+#include "iconnectiondriver.h"
+#include "iconnectionmanagerdriver.h"
+#include "sqlite/sqlitecnxmgrdrv.h"
+
+#include <boost/test/minimal.hpp>
+
+
+//connection_test
+int test_main(int, char *[])
+{
+	try {
+		db::IConnectionManagerDriver::Ptr mgr(new db::sqlite::SqliteCnxMgrDrv());
+		
+		db::DBDesc desc("", 0, "test.db");
+		db::IConnectionDriver::Ptr drv(mgr->connect_to_db(desc, "", ""));
+
+		db::SQLStatement schemacreate("CREATE TABLE foo "
+									  "(bar TEXT NOT NULL)");
+		BOOST_CHECK(drv->execute_statement(schemacreate));
+		
+		const char * sql = "SELECT * FROM foo WHERE bar='1'";
+		db::SQLStatement stmt(sql);
+		
+		BOOST_CHECK(drv->execute_statement(stmt));
+		BOOST_CHECK(drv->should_have_data());
+		BOOST_CHECK(unlink("test.db") != -1);
+	}
+	catch(...) 
+	{
+		BOOST_CHECK(0);
+	}
+    return 0;
+}
+

Added: trunk/src/utils/debug.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/debug.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,126 @@
+/*
+ * niepce - utils/debug.cpp
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <pthread.h>
+
+#if defined(NDEBUG)
+#define _SAVENDEBUG NDEBUG
+#undef NDEBUG
+#endif
+// we make sure assert is always defined.
+#include <assert.h>
+// but we need to save the state.
+#if defined(_SAVENDEBUG)
+#define NDEBUG _SAVENDEBUG
+#endif
+
+#include <boost/thread/recursive_mutex.hpp>
+
+#include "debug.h"
+
+
+
+namespace utils {
+
+	static void _vprint(const char *prefix, const char *fmt, 
+										 const char* func,	va_list marker);
+	static void _print(const char *prefix, const char *fmt, 
+					   const char* func, ...);
+
+	void dbg_print(const char *fmt, const char* func, ...)
+	{
+#ifdef DEBUG
+#define DEBUG_MSG "DEBUG: "
+		va_list marker;
+		
+		va_start(marker, func);
+		// TODO make this atomic
+		_vprint(DEBUG_MSG, fmt, func, marker);
+
+		va_end(marker);
+		
+#undef DEBUG_MSG
+#endif
+	}
+
+
+	/** assert 
+	 * 
+	 */
+	void dbg_assert(bool condvalue, const char* cond, const char* filen,
+					int linen, const char* reason)
+	{
+		if(!condvalue) {
+			_print("ASSERT: ", "%s:%d %s", cond, filen, linen, reason);
+
+		}
+	}
+
+
+	void err_print(const char *fmt, const char* func, ...)
+	{
+#define ERROR_MSG "ERROR: "
+		va_list marker;
+		
+		va_start(marker, func);
+		// TODO make this atomic
+		_vprint(ERROR_MSG, fmt, func, marker);
+
+		va_end(marker);
+		
+#undef ERROR_MSG
+	}
+
+
+	static void _print(const char *prefix, const char *fmt, 
+							const char* func, ...)
+	{
+		va_list marker;
+		
+		va_start(marker, func);
+
+		_vprint(prefix, fmt, func, marker);
+
+		va_end(marker);
+	}
+
+	static void _vprint(const char *prefix, const char *fmt, 
+							const char* func,	va_list marker)
+	{
+		static boost::recursive_mutex mutex;
+		boost::recursive_mutex::scoped_lock lock(mutex);
+		char buf[128];
+		snprintf(buf, 128, "(%d) ", (int)pthread_self());
+		fwrite(buf, 1, strlen(buf), stderr);
+		fwrite(prefix, 1, strlen(prefix), stderr);
+
+		if(func) {
+			fwrite(func, 1, strlen(func), stderr);
+			fwrite(" - ", 1, 3, stderr);
+		}
+
+		vfprintf(stderr, fmt, marker);
+		fprintf(stderr, "\n");
+	}
+
+}

Added: trunk/src/utils/debug.h
==============================================================================
--- (empty file)
+++ trunk/src/utils/debug.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,78 @@
+/*
+ * niepce - utils/debug.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UTILS_DEBUG_H__
+#define __UTILS_DEBUG_H__
+
+#ifdef __GNUC__
+// we have too mark this as a system header because otherwise GCC barfs 
+// on variadic macros.
+#pragma GCC system_header
+#endif
+
+namespace utils {
+
+#ifdef DEBUG
+#define DBG_OUT(x, ...) \
+	utils::dbg_print(x,  __FUNCTION__, ## __VA_ARGS__)
+#else
+#define DBG_OUT(x, ...) 	
+#endif
+
+#ifdef DEBUG
+#define DBG_ASSERT(cond, reason)	\
+	utils::dbg_assert(cond, #cond, __FILE__, __LINE__, reason)
+#else
+#define DBG_ASSERT(cond, reason)	\
+	assert(cond)
+#endif
+
+#define ERR_OUT(x, ...) \
+	utils::err_print(x,  __FUNCTION__, ## __VA_ARGS__)
+
+
+	/** print debug messages. printf format.
+	 * NOOP if DEBUG is not defined.
+	 * Call with the DBG_OUT macro
+	 * @param fmt the formt string, printf style
+	 * @param func the func name
+	 */
+	void dbg_print(const char* fmt, const char* func, ...);
+
+	/** assert 
+	 * @param condvalue the value of the assert, true, assert
+	 * @param cond the text of the condition
+	 * @param filen the file name __FILE__
+	 * @param linen the line number __LINE__
+	 * @param reason the reason of the assert
+	 */
+	void dbg_assert(bool condvalue, const char* cond, const char* filen,
+					int linen, const char* reason);
+
+	/** print error message. printf format.
+	 * Call with the ERR_OUT macro.
+	 * @param fmt the formt string, printf style
+	 * @param func the func name
+	 */
+	void err_print(const char *fmt, const char* func, ...);
+
+}
+
+
+#endif

Added: trunk/src/utils/exception.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/exception.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; indent-tabs-mode:nil; c-basic-offset: 4-*- */
+
+/*Copyright (c) 2005-2006 Dodji Seketeli
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS",
+ * WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE
+ * WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "exception.h"
+
+namespace utils {
+
+Exception::Exception (const char* a_reason): std::runtime_error (a_reason)
+{}
+
+Exception::Exception (const std::string &a_reason): std::runtime_error (a_reason)
+{}
+
+Exception::Exception (const Exception &a_other): std::runtime_error (a_other.what ())
+{}
+
+Exception::Exception (const std::exception &a_other):
+    std::runtime_error (a_other.what ())
+{}
+
+Exception&
+Exception::operator= (const Exception &a_other)
+{
+    if (this == &a_other) {
+        return *this;
+    }
+    std::runtime_error::operator= (a_other) ;
+    return *this ;
+}
+
+Exception::~Exception () throw ()
+{}
+
+const char*
+Exception::what () const throw ()
+{
+    return std::runtime_error::what () ;
+}
+
+}//end namespace db
+
+

Added: trunk/src/utils/exception.h
==============================================================================
--- (empty file)
+++ trunk/src/utils/exception.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; indent-tabs-mode:nil; c-basic-offset: 4-*- */
+
+/*Copyright (c) 2005-2006 Dodji Seketeli
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS",
+ * WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE
+ * WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#ifndef __NEMIVER_EXCEPTION_H__
+#define __NEMIVER_EXCEPTION_H__
+
+#include <stdexcept>
+#include <string>
+#include <iostream>
+
+#include "logstreamutils.h"
+
+namespace utils {
+
+class Exception: public std::runtime_error
+{
+public:
+    Exception (const char* a_reason) ;
+    Exception (const std::string &a_reason) ;
+    Exception (const Exception &a_other) ;
+    Exception (const std::exception &) ;
+    Exception& operator= (const Exception &a_other) ;
+    virtual ~Exception () throw ();
+    const char* what () const throw ();
+};//class Exception
+
+#define THROW_IF_FAIL(a_cond) \
+if (!(a_cond)) { \
+LOG_EXCEPTION (std::string("condition (") + #a_cond + ") failed; raising exception" ) ;\
+throw utils::Exception \
+    (std::string ("Assertion failed: ") + #a_cond)  ;\
+}
+
+#define THROW_IF_FAIL2(a_cond, a_reason) \
+if (!(a_cond)) { \
+LOG_EXCEPTION (std::string("condition (") + #a_cond + ") failed; raising exception " + a_reason);\
+throw utils::Exception (a_reason)  ;\
+}
+
+#define THROW_IF_FAIL3(a_cond, type, a_reason) \
+if (!(a_cond)) { \
+LOG_EXCEPTION (std::string("condition (") + #a_cond + ") failed; raising exception " + #type + \
+":  " + a_reason ) ; throw type (a_reason)  ;\
+}
+
+#define ABORT_IF_FAIL(a_cond, a_reason) \
+if (!(a_cond)) { \
+LOG_EXCEPTION ("condition (" << #a_cond << ") failed; raising exception " << a_reason <<"\n"); abort();\
+}
+
+#define THROW(a_reason) \
+LOG_EXCEPTION (std::string("raised exception: ") + std::string(a_reason)); \
+throw utils::Exception (std::string (a_reason))  ;
+
+#define THROW_EMPTY \
+LOG_EXCEPTION ("raised empty exception " << endl) ; \
+throw ;
+
+#define THROW_EXCEPTION(type, message) \
+LOG_EXCEPTION ("raised " << #type << ": "<< message<< "\n") ; \
+throw type (message) ;
+
+#define TRACE_EXCEPTION(exception) \
+LOG_EXCEPTION ("catched exception: " << exception.what () << "\n")
+
+#define RETHROW_EXCEPTION(exception) \
+LOG_EXCEPTION ("catched and rethrowing exception: " << exception.what() << "\n")
+
+#define RETURN_VAL_IF_FAIL(expression, value) \
+if (!(expression)) { \
+ERR_OUT("assertion %s  failed. Returning %s", #expression, #value); \
+return value ; \
+}
+
+#define RETURN_IF_FAIL(expression) \
+if (!(expression)) { \
+ERR_OUT ("assertion %s failed. Returning.", #expression); \
+return ; \
+}
+
+#ifndef NEMIVER_TRY
+#define NEMIVER_TRY try {
+#endif
+
+#ifndef NEMIVER_CATCH_NOX
+#define NEMIVER_CATCH_NOX \
+} catch (Glib::Exception &e) { \
+    LOG_ERROR (e.what ()) ; \
+} catch (std::exception &e) { \
+    LOG_ERROR (e.what ()) ; \
+} catch (...) { \
+    LOG_ERROR ("An unknown error occured") ; \
+}
+#endif
+
+#ifndef NEMIVER_CATCH_AND_RETURN_NOX
+#define NEMIVER_CATCH_AND_RETURN_NOX(a_value) \
+} catch (Glib::Exception &e) { \
+    LOG_ERROR (e.what ()) ; \
+    return a_value ; \
+} catch (std::exception &e) { \
+    LOG_ERROR (e.what ()) ; \
+    return a_value ; \
+} catch (...) { \
+    LOG_ERROR ("An unknown error occured") ; \
+    return a_value ; \
+}
+#endif
+
+}//end namespace common
+
+#endif //__NEMIVER_EXCEPTION_H__

Added: trunk/src/utils/exempi.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/exempi.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,245 @@
+/*
+ * niepce - utils/exempi.cpp
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include <boost/filesystem/convenience.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <exempi/xmp.h>
+#include <exempi/xmpconsts.h>
+
+#include "debug.h"
+#include "exempi.h"
+
+namespace bfs = boost::filesystem;
+
+namespace utils {
+
+ExempiManager::ExempiManager(const ns_defs_t* namespaces)
+{
+    if(xmp_init() && (namespaces != NULL)) {
+        const ns_defs_t* iter = namespaces;
+        for( ; iter->ns != NULL; iter++)	
+        {
+            // TODO check the return code
+            xmp_register_namespace(iter->ns,
+                                   iter->prefix, NULL);
+        }
+    }
+}
+
+
+ExempiManager::~ExempiManager()
+{
+    xmp_terminate();
+}
+
+
+XmpMeta::XmpMeta()
+    : m_xmp(),
+      m_keyword_fetched(false)		  
+{
+    m_xmp = xmp_new_empty();
+}
+
+
+/** @param file the path to the file to open 
+ * @param sidecar_only we only want the sidecar.
+ * It will locate the XMP sidecar for the file.
+ */
+XmpMeta::XmpMeta(const bfs::path & file, bool sidecar_only)
+    : m_xmp(NULL),
+      m_keyword_fetched(false)
+{
+    if(!sidecar_only) {
+        DBG_OUT("trying to load the XMP from the file");
+        xmp::ScopedPtr<XmpFilePtr> 
+            xmpfile(xmp_files_open_new(file.string().c_str(), XMP_OPEN_READ));
+        if(xmpfile != NULL) {
+            m_xmp = xmp_files_get_new_xmp(xmpfile);
+            if(xmpfile == NULL) {
+                ERR_OUT("xmpfile is NULL");
+            }
+        }
+    }
+		
+    if(m_xmp == NULL) {
+        size_t len;
+        char * buffer;
+        bfs::path sidecar = file.branch_path()
+            / (basename(file) + ".xmp");
+			
+        DBG_OUT("creating xmpmeta from %s", sidecar.string().c_str());
+        FILE * f = fopen(sidecar.string().c_str(), "rb");
+			
+        if (f != NULL) {
+            fseek(f, 0, SEEK_END);
+            len = ftell(f);
+            fseek(f, 0, SEEK_SET);
+				
+            buffer = (char*)malloc(len + 1);
+            /*size_t rlen =*/ fread(buffer, 1, len, f);
+            m_xmp = xmp_new_empty();
+            if(!xmp_parse(m_xmp, buffer, len)) {
+                xmp_free(m_xmp);
+                m_xmp = NULL;
+            }
+            free(buffer);
+        }
+    }
+}
+
+XmpMeta::~XmpMeta()
+{
+    if(m_xmp) {
+        xmp_free(m_xmp);
+    }
+}
+
+std::string XmpMeta::serialize_inline() const
+{
+    std::string buf;
+    xmp::ScopedPtr<XmpStringPtr> output(xmp_string_new());
+    if(xmp_serialize_and_format(m_xmp, output, 
+                                XMP_SERIAL_OMITPACKETWRAPPER | XMP_SERIAL_OMITALLFORMATTING, 
+                                0, "", "", 0)) {
+        buf = xmp_string_cstr(output);
+    }
+    return buf;
+}
+
+std::string XmpMeta::serialize() const
+{
+    std::string buf;
+    xmp::ScopedPtr<XmpStringPtr> output(xmp_string_new());
+    if(xmp_serialize_and_format(m_xmp, output, 
+                                XMP_SERIAL_OMITPACKETWRAPPER, 
+                                0, "\n", " ", 0)) {
+        buf = xmp_string_cstr(output);
+    }
+    return buf;
+}
+
+void XmpMeta::unserialize(const char * buffer)
+{
+    if(!buffer) 
+        return;
+    xmp_parse(m_xmp, buffer, strlen(buffer));
+}
+
+
+int32_t XmpMeta::orientation() const
+{
+    int32_t _orientation = 0;
+   
+    if(!xmp_get_property_int32(m_xmp, NS_TIFF, "Orientation", &_orientation, NULL)) {
+        ERR_OUT("get \"Orientation\" property failed: %d", xmp_get_error());
+    }
+    return _orientation;
+}
+
+
+std::string XmpMeta::label() const
+{
+    std::string _label;
+    xmp::ScopedPtr<XmpStringPtr> value(xmp_string_new());
+    if(xmp_get_property(m_xmp, NS_XAP, "Label", value, NULL)) {
+        _label = xmp_string_cstr(value);
+    }
+    return _label;
+}
+
+
+int32_t XmpMeta::rating() const
+{
+    int32_t _rating = -1;
+    if(!xmp_get_property_int32(m_xmp, NS_XAP, "Rating", &_rating, NULL)) {
+        ERR_OUT("get \"Rating\" property failed: %d", xmp_get_error());
+    }
+    return _rating;
+}
+
+
+time_t  XmpMeta::creation_date() const
+{
+    time_t date = 0;
+    XmpDateTime value;
+    if(xmp_get_property_date(m_xmp, NS_EXIF, "DateTimeOriginal", 
+                             &value, NULL)) {
+        struct tm dt;
+        dt.tm_sec = value.second;
+        dt.tm_min = value.minute;
+        dt.tm_hour = value.hour;
+        dt.tm_mday = value.day;
+        dt.tm_mon = value.month;
+        dt.tm_year = value.year - 1900;
+        dt.tm_isdst = -1;
+        // this field is supposed to be a glibc extension. oh joy.
+        dt.tm_gmtoff = value.tzSign * ((value.tzHour * 3600) +
+                                       (value.tzMinute * 60));
+        date = mktime(&dt);
+        DBG_ASSERT(date != -1, "date is -1");
+    }
+    else {
+        ERR_OUT("get \"DateTimeOriginal\" property failed: %d", xmp_get_error());
+    }
+    return date;
+}
+
+std::string XmpMeta::creation_date_str() const
+{
+    std::string s;
+    xmp::ScopedPtr<XmpStringPtr> value(xmp_string_new());
+    if(xmp_get_property(m_xmp, NS_EXIF, "DateTimeOriginal", 
+                        value, NULL)) {
+        s = xmp_string_cstr(value);
+    }
+    return s;
+}
+
+
+const std::vector< std::string > & XmpMeta::keywords() const
+{
+    if(!m_keyword_fetched) {
+        xmp::ScopedPtr<XmpIteratorPtr> iter(xmp_iterator_new(m_xmp, NS_DC, "subject", 
+                                                             XMP_ITER_JUSTLEAFNODES));
+        xmp::ScopedPtr<XmpStringPtr> value(xmp_string_new());
+        while(xmp_iterator_next(iter, NULL, NULL, value, NULL)) {
+            m_keywords.push_back(xmp_string_cstr(value));
+        }
+        m_keyword_fetched = true;
+    }
+    return m_keywords;
+}
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/

Added: trunk/src/utils/exempi.h
==============================================================================
--- (empty file)
+++ trunk/src/utils/exempi.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,172 @@
+#ifndef __UTILS_EXEMPI_H__
+#define __UTILS_EXEMPI_H__
+
+/*
+ * niepce - utils/exempi.h
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <vector>
+#include <string>
+
+#include <boost/filesystem/path.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <exempi/xmp.h>
+
+
+namespace xmp {
+
+inline
+void release(XmpIteratorPtr ptr)
+{
+    xmp_iterator_free(ptr);
+}
+
+inline
+void release(XmpStringPtr ptr)
+{
+    xmp_string_free(ptr);
+}
+
+inline
+void release(XmpFilePtr ptr)
+{
+    xmp_files_free(ptr);
+}
+
+inline 
+void release(XmpPtr ptr)
+{
+    xmp_free(ptr);
+}
+
+/**
+ * @brief a scoped pointer for Xmp opaque types
+ * @todo move to Exempi.
+ */
+template <class T>
+class ScopedPtr 
+    : boost::noncopyable
+{
+public:
+    ScopedPtr(T p)
+        : _p(p)
+        {}
+    ~ScopedPtr()
+        { if (_p) release(_p); }
+    operator T() const
+        { return _p; }
+private:
+    T _p;
+};
+
+
+enum MetaDataType {
+    META_DT_NONE = 0,
+    META_DT_STRING,
+    META_DT_STRING_ARRAY,
+    META_DT_DATE,
+    META_DT_STAR_RATING
+};
+	
+	
+struct MetaDataFormat {
+    const char * label;
+    const char * ns;
+    const char * property;
+    MetaDataType     type;
+    bool         readonly;
+};
+	
+struct MetaDataSectionFormat {
+    const char * section;
+    const MetaDataFormat * formats;
+};
+	
+
+}
+
+
+namespace utils {
+
+class ExempiManager
+    : public boost::noncopyable
+{
+public:
+    struct ns_defs_t {
+        const char *ns;
+        const char *prefix;
+    };
+    /** construct with namespaces to initialize */
+    ExempiManager(const ns_defs_t * namespaces = 0);
+    ~ExempiManager();
+};
+
+/** a high-level wrapper for xmp */
+class XmpMeta
+{
+public:
+    XmpMeta();
+    XmpMeta(const boost::filesystem::path & for_file, bool sidecar_only);
+    virtual ~XmpMeta();
+
+    bool isOk() const 
+        { return m_xmp != NULL; }
+    XmpPtr xmp() const
+        { return m_xmp; }
+    /** serialize the XMP inline */
+    std::string serialize_inline() const;
+    /** serialize the XMP (for the sidecar) */
+    std::string serialize() const;
+    /** load the XMP from the unserialized buffer 
+     * (NUL terminated) 
+     */
+    void unserialize(const char *);
+
+    int32_t orientation() const;
+    std::string label() const;
+    /** return the rating, -1 is not found (not set) */
+    int32_t rating() const;
+    time_t  creation_date() const;
+    std::string creation_date_str() const;
+    const std::vector< std::string > & keywords() const;
+private:
+    XmpMeta( const XmpMeta & ); // copy constructor
+
+    XmpMeta & operator=(const XmpMeta &); // assignment
+
+    XmpPtr m_xmp;
+    // caches
+    mutable bool m_keyword_fetched;
+    mutable std::vector< std::string > m_keywords;
+};
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+
+#endif

Added: trunk/src/utils/files.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/files.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,90 @@
+/*
+ * niepce - utils/files.cpp
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <boost/filesystem/path.hpp>
+#include <boost/filesystem/operations.hpp>
+#include <boost/filesystem/convenience.hpp>
+#include <boost/algorithm/string.hpp>
+
+#include "debug.h"
+#include "files.h"
+
+namespace bfs = boost::filesystem;
+
+namespace utils {
+
+	bool filter_none(const boost::filesystem::path & )
+	{
+		return true;
+	}
+
+
+	bool filter_xmp_out(const bfs::path & file)
+	{
+		std::string ext = extension(file);
+		boost::to_lower(ext);
+		if(ext == ".xmp") {
+			return false;
+		}
+		return true;
+	}
+
+
+	FileList::FileList( const _impltype_t & v )
+		: _impltype_t( v )
+	{
+	}
+
+	FileList::Ptr FileList::getFilesFromDirectory(const FileList::value_type & p, boost::function<bool (const value_type &)> filter)
+	{
+		if(!exists( p ) ) {
+			DBG_OUT( "directory %s do not exist", p.string().c_str() );
+			return Ptr();
+		}
+		try
+		{
+			FileList::Ptr l( new FileList() );
+			
+			bfs::directory_iterator end_itr; 
+			for ( bfs::directory_iterator itr( p );
+				  itr != end_itr;
+				  ++itr )
+			{
+				if ( !is_directory(*itr) )
+				{
+					if( filter(*itr) ) {
+						l->push_back(*itr);
+						DBG_OUT( "found file %s", itr->string().c_str() );
+					}
+				}
+			}
+			l->sort();
+			return l;
+		}
+		catch( std::exception & e )
+		{
+			ERR_OUT( "Exception: %s", e.what() );
+		}
+
+		return Ptr();
+	}
+
+
+}
+

Added: trunk/src/utils/files.h
==============================================================================
--- (empty file)
+++ trunk/src/utils/files.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,67 @@
+/*
+ * niepce - utils/files.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+
+#ifndef __UTILS_FILES_H__
+#define __UTILS_FILES_H__
+
+#include <list>
+#include <string>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/function.hpp>
+#include <boost/filesystem/path.hpp>
+
+namespace utils {
+
+	bool filter_none(const boost::filesystem::path & file);
+	bool filter_xmp_out(const boost::filesystem::path & file);
+
+	class FileList 
+		: private std::list< boost::filesystem::path >
+	{
+	public:
+		typedef boost::shared_ptr< FileList > Ptr;
+
+		typedef std::list< boost::filesystem::path >    _impltype_t;
+		typedef _impltype_t::value_type       value_type;
+		typedef _impltype_t::iterator         iterator;
+		typedef _impltype_t::const_iterator   const_iterator;
+		typedef _impltype_t::size_type        size_type;
+
+		FileList( )
+			{}
+		FileList( const _impltype_t & );
+
+		static Ptr getFilesFromDirectory(const value_type & dir,
+										 boost::function<bool (const value_type &)> filter);
+
+		const_iterator begin() const
+			{ return _impltype_t::begin(); }
+		const_iterator end() const
+			{ return _impltype_t::end(); }
+		size_type size() const
+			{ return _impltype_t::size(); }
+	};
+}
+
+
+#endif

Added: trunk/src/utils/fsutils.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/fsutils.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,30 @@
+/*
+ * niepce - utils/fsutils.cpp
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <stdlib.h>
+#include <string>
+
+#include "fsutils.h"
+
+namespace utils {
+
+
+
+}

Added: trunk/src/utils/fsutils.h
==============================================================================
--- (empty file)
+++ trunk/src/utils/fsutils.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,47 @@
+/*
+ * niepce - utils/fsutils.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UTILS_FSUTILS_H__
+#define __UTILS_FSUTILS_H__
+
+#include <string>
+#include <boost/filesystem/path.hpp>
+#include <boost/filesystem/operations.hpp>
+
+namespace utils {
+
+class DirectoryDisposer
+{
+public:
+	DirectoryDisposer(const std::string &dir)
+		: m_dir(dir)
+		{
+		}
+	~DirectoryDisposer()
+		{
+			boost::filesystem::remove_all(m_dir);
+		}
+private:
+	std::string m_dir;
+};
+
+
+}
+
+#endif

Added: trunk/src/utils/geometry.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/geometry.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,142 @@
+/*
+ * niepce - utils/geometry.cpp
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/classification.hpp>
+
+#include "debug.h"
+#include "geometry.h"
+
+namespace utils {
+
+Rect::Rect()
+{
+    _r[X] = 0;
+    _r[Y] = 0;
+    _r[W] = 0;
+    _r[H] = 0;
+}
+
+Rect::Rect(int _x, int _y, int _w, int _h)
+{
+    _r[X] = _x;
+    _r[Y] = _y;
+    _r[W] = _w;
+    _r[H] = _h;
+}
+
+
+Rect::Rect(const std::string & s)
+    throw(std::bad_cast)
+{
+    std::vector< std::string > v;
+    v.reserve(4);
+    boost::split(v, s, boost::is_any_of(" "));
+    if(v.size() < 4) {
+        throw(std::bad_cast());
+    }
+    int i = 0;
+    std::vector< std::string >::iterator iter;
+    for(iter = v.begin(); iter != v.end(); ++iter) {
+        _r[i] = boost::lexical_cast<int>(*iter);
+        i++;
+    }
+}
+
+Rect & Rect::scale(double _s)
+{
+    _r[W] = _r[W] * _s;
+    _r[H] = _r[H] * _s;
+    return *this;
+}
+
+
+Rect Rect::fit_into(const Rect & dest) const
+{
+    Rect result = *this;
+
+    double in_w = w();
+    double in_h = h();
+    double scale_w = dest.w() / in_w;
+    DBG_OUT("w %d in_w %f", dest.w(), in_w);
+    double scale_h = dest.h() / in_h;
+    DBG_OUT("h %d in_h %f", dest.h(), in_h);
+    DBG_OUT("scale w %f h %f", scale_w, scale_h);
+    double _scale = std::min(scale_w, scale_h);
+
+    result.scale(_scale);
+    if(scale_w <= scale_h) {
+        result._r[W] = dest.w();
+    }
+    if(scale_w >= scale_h) {
+        result._r[H] = dest.h();
+    }
+
+    return result;
+}
+
+
+Rect Rect::fill_into(const Rect & dest) const
+{
+    // the smallest dimension
+    Rect result = *this;
+
+    double in_w = w();
+    double in_h = h();
+    double scale_w = dest.w() / in_w;
+    DBG_OUT("w %d in_w %f", dest.w(), in_w);
+    double scale_h = dest.h() / in_h;
+    DBG_OUT("h %d in_h %f", dest.h(), in_h);
+    DBG_OUT("scale w %f h %f", scale_w, scale_h);
+    double _scale = std::max(scale_w, scale_h);
+
+    result.scale(_scale);
+
+    if(scale_w >= scale_h) {
+        result._r[W] = dest.w();
+    }
+    if(scale_w <= scale_h) {
+        result._r[H] = dest.h();
+    }
+
+    return result;
+}
+
+
+
+std::string Rect::to_string() const
+{
+    return str(boost::format("%1% %2% %3% %4%")
+               % _r[X] % _r[Y] % _r[W] % _r[H]); 
+}
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/

Added: trunk/src/utils/geometry.h
==============================================================================
--- (empty file)
+++ trunk/src/utils/geometry.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,87 @@
+/*
+ * niepce - utils/geometry.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __UTILS_GEOMETRY_H__
+#define __UTILS_GEOMETRY_H__
+
+#include <boost/array.hpp>
+#include <string>
+
+namespace utils {
+
+class Rect
+{
+public:
+    Rect();
+    Rect(int x, int y, int w, int h);
+    /** build a Rect from a string 
+     * @param s string whose format is "x y w h" as decimal ints.
+     * @throw a std::bad_cast exception if there is not 4 element
+     * or if one of them is not an int.
+     */
+    Rect(const std::string & s) 
+        throw(std::bad_cast);
+    
+    int x() const
+        { return _r[X]; }
+    int y() const
+        { return _r[Y]; }
+    int w() const
+        { return _r[W]; }
+    int h() const
+        { return _r[H]; }
+    /** convert to a string in the same format as
+     * accepted by the %Rect(const std::string & s) constructor.
+     */
+    std::string to_string() const;
+
+    Rect & scale(double _s);
+
+    /** fit into destination. */
+    Rect fit_into(const Rect & dest) const;
+    /** fill into desitnation */
+    Rect fill_into(const Rect & dest) const;
+
+    bool operator==(const Rect & r) const
+        { return _r == r._r; }
+private:
+    /** the indices */
+    enum {
+        X = 0,
+        Y,
+        W,
+        H			
+    };
+    boost::array<int, 4> _r;
+};
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0))
+  indent-tabs-mode:nil
+  fill-column:80
+  End:
+*/
+
+#endif

Added: trunk/src/utils/logstreamutils.h
==============================================================================
--- (empty file)
+++ trunk/src/utils/logstreamutils.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; indent-tabs-mode:nil; c-basic-offset: 4-*- */
+
+/*Copyright (c) 2005-2006 Dodji Seketeli
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS",
+ * WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE
+ * WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#ifndef __NMV_LOG_STREAM_UTILS_H__
+#define __NMV_LOG_STREAM_UTILS_H__
+
+#include <cassert>
+#include <cstdlib>
+#include <iostream>
+
+#include "utils/debug.h"
+
+#define NMV_DEFAULT_DOMAIN __FILE__
+
+
+#ifndef PRETTY_FUNCTION_NAME_
+#ifdef DEBUG
+#define PRETTY_FUNCTION_NAME_ __ASSERT_FUNCTION
+#else
+// non portable construct, who the hell care.
+#define PRETTY_FUNCTION_NAME_ __PRETTY_FUNCTION__
+#endif
+#endif
+
+#ifndef HERE
+#define HERE PRETTY_FUNCTION_NAME_ << ":" <<__FILE__<< ":" << __LINE__ << ":"
+#endif
+
+
+#ifndef LOG_STREAM
+#define LOG_STREAM std::cerr
+#endif
+
+#ifndef LOG_MARKER_INFO
+#define LOG_MARKER_INFO "|I|"
+#endif
+
+#ifndef LOG_MARKER_ERROR
+#define LOG_MARKER_ERROR "|E|"
+#endif
+
+#ifndef LOG_MARKER_EXCEPTION
+#define LOG_MARKER_EXCEPTION "|X|"
+#endif
+
+#ifndef LOG_LEVEL_NORMAL___
+#define LOG_LEVEL_NORMAL___ " "
+#endif
+
+#ifndef LOG_LEVEL_VERBOSE___
+#define LOG_LEVEL_VERBOSE___ " "
+#endif
+
+#ifndef LOG
+#define LOG(message) \
+LOG_STREAM << LOG_LEVEL_NORMAL___ << LOG_MARKER_INFO << HERE << message << nemiver::common::endl
+#endif
+
+#ifndef MESSAGE
+#define MESSAGE(message) \
+LOG_STREAM << LOG_LEVEL_NORMAL___ << LOG_MARKER_INFO << message << nemiver::common::endl
+#endif
+
+#ifndef LOG_D
+#define LOG_D(message, domain) \
+LOG_STREAM << domain << " - " << message << std::endl;
+#endif
+
+#ifndef LOG_DD
+#define LOG_DD(message) LOG_D(message, NMV_DEFAULT_DOMAIN)
+#endif
+
+#ifndef LOG_ERROR
+#define LOG_ERROR(message) ERR_OUT("Error %s", std::string(message).c_str())
+#endif
+
+#ifndef LOG_EXCEPTION
+#define LOG_EXCEPTION(message) ERR_OUT("Exception %s", std::string(message).c_str())
+#endif
+
+#ifndef LOG_ERROR_D
+#define LOG_ERROR_D(message, domain) \
+LOG_STREAM.push_domain (domain) ; LOG_ERROR (message) ; LOG_STREAM.pop_domain() ;
+#endif
+
+#ifndef LOG_ERROR_DD
+#define LOG_ERROR_DD(message) LOG_ERROR_D (message, NMV_DEFAULT_DOMAIN)
+#endif
+
+#ifndef LOG_VERBOSE
+#define LOG_VERBOSE(message) \
+LOG_STREAM << LOG_LEVEL_VERBOSE___ << LOG_MARKER_INFO << HERE << message << std::endl
+#endif
+
+#ifndef LOG_VERBOSE_D
+#define LOG_VERBOSE_D(message) \
+LOG_STREAM.push_domain (domain) ; LOG_VERBOSE(message) ;LOG_STREAM.pop_domain();
+#endif
+
+#ifndef LOG_SCOPE_VERBOSE
+#define LOG_SCOPE_VERBOSE(scopename) \
+nemiver::common::ScopeLogger scope_logger (scopename, nemiver::common::LogStream::LOG_LEVEL_VERBOSE) ;
+#endif
+
+#ifndef LOG_SCOPE
+#define LOG_SCOPE(scopename) \
+nemiver::common::ScopeLogger scope_logger (scopename, nemiver::common::LogStream::LOG_LEVEL_NORMAL) ;
+#endif
+
+#ifndef LOG_SCOPE_D
+#define LOG_SCOPE_D(scopename, domain) \
+nemiver::common::ScopeLogger scope_logger \
+        (scopename, nemiver::common::LogStream::LOG_LEVEL_VERBOSE, domain) ;
+#endif
+
+#ifndef LOG_SCOPE_NORMAL
+#define LOG_SCOPE_NORMAL(scopename) \
+nemiver::common::ScopeLogger scope_logger (scopename, nemiver::common::LogStream::LOG_LEVEL_NORMAL) ;
+#endif
+
+#ifndef LOG_SCOPE_NORMAL_D
+#define LOG_SCOPE_NORMAL_D(scopename, domain) \
+std::cerr << scopename << " - " domain;
+#endif
+
+#ifndef LOG_FUNCTION_SCOPE
+#define LOG_FUNCTION_SCOPE LOG_SCOPE(PRETTY_FUNCTION_NAME_)
+#endif
+
+#ifndef LOG_FUNCTION_SCOPE_D
+#define LOG_FUNCTION_SCOPE_D(domain) LOG_SCOPE_D(PRETTY_FUNCTION_NAME_, domain)
+#endif
+
+#ifndef LOG_FUNCTION_SCOPE_NORMAL
+#define LOG_FUNCTION_SCOPE_NORMAL LOG_SCOPE_NORMAL(PRETTY_FUNCTION_NAME_)
+#endif
+
+#ifndef LOG_FUNCTION_SCOPE_VERBOSE
+#define LOG_FUNCTION_SCOPE_VERBOSE LOG_SCOPE_VERBOSE(PRETTY_FUNCTION_NAME_)
+#endif
+
+#ifndef LOG_FUNCTION_SCOPE_NORMAL_D
+#define LOG_FUNCTION_SCOPE_NORMAL_D(domain) \
+    LOG_SCOPE_NORMAL_D(PRETTY_FUNCTION_NAME_, domain)
+#endif
+
+
+#ifndef LOG_REF_COUNT
+#define LOG_REF_COUNT(a_object_ptr, a_name) \
+LOG_D ("object '" \
+       << a_name \
+       << "' refcount: " \
+       << (int) a_object_ptr->get_refcount (), \
+       "refcount-domain") ;
+#endif //LOG_REF_COUNT
+
+#endif // NMV_LOG_STREAM_UTILS_H__
+

Added: trunk/src/utils/moniker.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/moniker.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,73 @@
+/*
+ * niepce - utils/moniker.cpp
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <iostream>
+#include <string>
+#include <boost/range.hpp>
+#include <boost/algorithm/string/find.hpp>
+
+#include "debug.h"
+#include "moniker.h"
+
+namespace utils {
+
+	Moniker::Moniker(const std::string & s)
+	{
+		DBG_OUT("%s", s.c_str());
+		try {
+			boost::iterator_range<std::string::const_iterator> iter 
+				= boost::find_first(s, ":");
+			if(iter) {
+				DBG_OUT("found : in %s", s.c_str());
+
+				m_scheme.append(s.begin(), iter.begin());
+				m_path.append(iter.end(), s.end());
+
+				DBG_OUT("moniker scheme %s path %s", m_scheme.c_str(), 
+								m_path.c_str());
+			}
+		}
+		catch(...) {
+			DBG_OUT("exception");
+		}
+	}
+
+	Moniker::Moniker(const std::string & _scheme, const std::string & _path)
+		: m_scheme(_scheme),
+		  m_path(_path)
+	{
+	}
+
+
+	const char *Moniker::c_str() const
+	{
+		if(m_cachedvalue.empty()) {
+			m_cachedvalue = m_scheme + ":" + m_path;
+		}
+		return m_cachedvalue.c_str();
+	}
+
+
+	std::ostream & operator<<(std::ostream & stream, const Moniker & m)
+	{
+		stream << "Moniker " << m.scheme() << " : " << m.path() << std::endl;
+		return stream;
+	}
+
+}

Added: trunk/src/utils/moniker.h
==============================================================================
--- (empty file)
+++ trunk/src/utils/moniker.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,58 @@
+/*
+ * niepce - utils/moniker.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __UTILS_MONIKER_H__
+#define __UTILS_MONIKER_H__
+
+#include <ostream>
+#include <string>
+
+
+namespace utils {
+
+	/** Moniker are URL like specifiers
+			In the form of scheme:path.
+
+			@todo implement nested monikers
+	 */
+	class Moniker
+	{
+	public:
+		explicit Moniker(const std::string & s);
+		Moniker(const std::string & scheme, const std::string & path);
+
+		const std::string & scheme() const
+			{ return m_scheme; }
+		const std::string & path() const
+			{ return m_path; }
+		const char *c_str() const;
+	private:
+		std::string m_scheme;
+		std::string m_path;
+
+		mutable std::string m_cachedvalue;
+	};
+
+	std::ostream & operator<<(std::ostream & stream, const Moniker & m);
+
+}
+
+
+#endif

Added: trunk/src/utils/mtqueue.h
==============================================================================
--- (empty file)
+++ trunk/src/utils/mtqueue.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,104 @@
+/*
+ * niepce - utils/mtqueue.h
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+#ifndef __NIEPCE_UTILS_MTQUEUE_H__
+#define __NIEPCE_UTILS_MTQUEUE_H__
+
+#include <deque>
+#include <boost/thread/recursive_mutex.hpp>
+
+namespace utils {
+
+	/** This class implement a simple deque protected 
+	 * by mutexes.
+	 *
+	 * It is not STL compliant.
+	 */
+	template < class T >
+	class MtQueue
+	{
+	public:
+		typedef boost::recursive_mutex mutex_t;
+		typedef T                      value_type;
+
+		MtQueue();
+		virtual ~MtQueue();
+
+		void add(const T &);
+		T pop();
+		bool empty() const;
+		void clear();
+		mutex_t & mutex() const
+			{ return m_mutex; }
+	private:
+		std::deque<T> m_queue;
+		mutable mutex_t     m_mutex;
+	};
+
+
+	template < class T >
+	MtQueue<T>::MtQueue()
+		: m_queue(),
+		  m_mutex()
+	{
+	}
+
+	template < class T >
+	MtQueue<T>::~MtQueue()
+	{
+		mutex_t::scoped_lock lock(m_mutex);		
+	}
+
+	template < class T > void
+	MtQueue<T>::add(const T &op)
+	{
+		mutex_t::scoped_lock lock(m_mutex);
+		m_queue.push_back(op);
+	}
+
+	
+	template < class T >
+	T MtQueue<T>::pop()
+	{
+		T elem;
+		mutex_t::scoped_lock lock(m_mutex);		
+		elem = m_queue.front();
+		m_queue.pop_front();
+		return elem;
+	}
+
+
+	template < class T >
+	bool MtQueue<T>::empty() const
+	{
+		mutex_t::scoped_lock lock(m_mutex);
+		return m_queue.empty();
+	}
+
+	template < class T >
+	void MtQueue<T>::clear() 
+	{
+		mutex_t::scoped_lock lock(m_mutex);
+		m_queue.clear();
+	}
+}
+
+#endif

Added: trunk/src/utils/stringutils.h
==============================================================================
--- (empty file)
+++ trunk/src/utils/stringutils.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,51 @@
+/*
+ * niepce - utils/stringutils.h
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __UTILS_STRINGUTILS_H__
+#define __UTILS_STRINGUTILS_H__
+
+#include <boost/range/value_type.hpp>
+
+namespace utils {
+
+/** join elements with a separator 
+ * boost has such an algorithm in later versions. This is not it, but does a
+ * similar thing.
+ */
+template<typename S, typename C>
+typename boost::range_value< S >::type join(const S & input, const C & separator)
+{
+	typename boost::range_value< S >::type joint;
+	typename S::const_iterator iter(input.begin());
+	bool first = true;
+	for( ; iter != input.end(); iter++) {
+		if(!first) {
+			joint += separator;
+		}
+		joint += *iter;
+		first = false;
+	}
+	return joint;
+}
+
+
+}
+
+#endif

Added: trunk/src/utils/test.xmp
==============================================================================
--- (empty file)
+++ trunk/src/utils/test.xmp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,182 @@
+<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Public XMP Toolkit Core 3.5">
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";>
+  <rdf:Description rdf:about=""
+    xmlns:tiff="http://ns.adobe.com/tiff/1.0/";>
+   <tiff:Make>Canon</tiff:Make>
+   <tiff:Model>Canon EOS 20D</tiff:Model>
+   <tiff:Orientation>1</tiff:Orientation>
+   <tiff:ImageWidth>3504</tiff:ImageWidth>
+   <tiff:ImageLength>2336</tiff:ImageLength>
+  </rdf:Description>
+  <rdf:Description rdf:about=""
+    xmlns:exif="http://ns.adobe.com/exif/1.0/";>
+   <exif:ExifVersion>0221</exif:ExifVersion>
+   <exif:ExposureTime>5/1</exif:ExposureTime>
+   <exif:ShutterSpeedValue>-2321928/1000000</exif:ShutterSpeedValue>
+   <exif:FNumber>8/1</exif:FNumber>
+   <exif:ApertureValue>6/1</exif:ApertureValue>
+   <exif:ExposureProgram>1</exif:ExposureProgram>
+   <exif:ISOSpeedRatings>
+    <rdf:Seq>
+     <rdf:li>100</rdf:li>
+    </rdf:Seq>
+   </exif:ISOSpeedRatings>
+   <exif:DateTimeOriginal>2006-12-07T23:37:30-05:00</exif:DateTimeOriginal>
+   <exif:DateTimeDigitized>2006-12-07T23:37:30-05:00</exif:DateTimeDigitized>
+   <exif:ExposureBiasValue>0/2</exif:ExposureBiasValue>
+   <exif:MaxApertureValue>3625/1000</exif:MaxApertureValue>
+   <exif:MeteringMode>5</exif:MeteringMode>
+   <exif:Flash rdf:parseType="Resource">
+    <exif:Fired>False</exif:Fired>
+    <exif:Return>0</exif:Return>
+    <exif:Mode>2</exif:Mode>
+    <exif:Function>False</exif:Function>
+    <exif:RedEyeMode>False</exif:RedEyeMode>
+   </exif:Flash>
+   <exif:FocalLength>24/1</exif:FocalLength>
+   <exif:CustomRendered>0</exif:CustomRendered>
+   <exif:ExposureMode>1</exif:ExposureMode>
+   <exif:WhiteBalance>0</exif:WhiteBalance>
+   <exif:SceneCaptureType>0</exif:SceneCaptureType>
+   <exif:FocalPlaneXResolution>3504000/885</exif:FocalPlaneXResolution>
+   <exif:FocalPlaneYResolution>2336000/590</exif:FocalPlaneYResolution>
+   <exif:FocalPlaneResolutionUnit>2</exif:FocalPlaneResolutionUnit>
+  </rdf:Description>
+  <rdf:Description rdf:about=""
+    xmlns:xap="http://ns.adobe.com/xap/1.0/";>
+   <xap:ModifyDate>2006-12-07T23:37:30-05:00</xap:ModifyDate>
+   <xap:MetadataDate>2007-10-12T10:46:36-04:00</xap:MetadataDate>
+  </rdf:Description>
+  <rdf:Description rdf:about=""
+    xmlns:aux="http://ns.adobe.com/exif/1.0/aux/";>
+   <aux:SerialNumber>420103070</aux:SerialNumber>
+   <aux:LensInfo>24/1 85/1 0/0 0/0</aux:LensInfo>
+   <aux:Lens>24.0-85.0 mm</aux:Lens>
+   <aux:ImageNumber>185</aux:ImageNumber>
+   <aux:FlashCompensation>-2/1</aux:FlashCompensation>
+   <aux:OwnerName>unknown</aux:OwnerName>
+   <aux:Firmware>1.1.0</aux:Firmware>
+  </rdf:Description>
+  <rdf:Description rdf:about=""
+    xmlns:crs="http://ns.adobe.com/camera-raw-settings/1.0/";>
+   <crs:AlreadyApplied>False</crs:AlreadyApplied>
+  </rdf:Description>
+  <rdf:Description rdf:about=""
+    xmlns:dc="http://purl.org/dc/elements/1.1/";>
+   <dc:creator>
+    <rdf:Seq>
+     <rdf:li>unknown</rdf:li>
+    </rdf:Seq>
+   </dc:creator>
+   <dc:subject>
+    <rdf:Bag>
+     <rdf:li>choir</rdf:li>
+     <rdf:li>night</rdf:li>
+     <rdf:li>ontario</rdf:li>
+     <rdf:li>ottawa</rdf:li>
+     <rdf:li>parliament of canada</rdf:li>
+    </rdf:Bag>
+   </dc:subject>
+  </rdf:Description>
+  <rdf:Description rdf:about=""
+    xmlns:crss="http://ns.adobe.com/camera-raw-saved-settings/1.0/";
+    xmlns:crs="http://ns.adobe.com/camera-raw-settings/1.0/";>
+   <crss:SavedSettings>
+    <rdf:Bag>
+     <rdf:li rdf:parseType="Resource">
+      <crss:Name>16/12/06 9:08:59 AM: Import</crss:Name>
+      <crss:Type>Snapshot</crss:Type>
+      <crss:Parameters rdf:parseType="Resource">
+       <crs:WhiteBalance>As Shot</crs:WhiteBalance>
+       <crs:Exposure>0.00</crs:Exposure>
+       <crs:Shadows>5</crs:Shadows>
+       <crs:Brightness>+50</crs:Brightness>
+       <crs:Contrast>+25</crs:Contrast>
+       <crs:Saturation>0</crs:Saturation>
+       <crs:Sharpness>25</crs:Sharpness>
+       <crs:LuminanceSmoothing>0</crs:LuminanceSmoothing>
+       <crs:ColorNoiseReduction>25</crs:ColorNoiseReduction>
+       <crs:ChromaticAberrationR>0</crs:ChromaticAberrationR>
+       <crs:ChromaticAberrationB>0</crs:ChromaticAberrationB>
+       <crs:VignetteAmount>0</crs:VignetteAmount>
+       <crs:ShadowTint>0</crs:ShadowTint>
+       <crs:RedHue>0</crs:RedHue>
+       <crs:RedSaturation>0</crs:RedSaturation>
+       <crs:GreenHue>0</crs:GreenHue>
+       <crs:GreenSaturation>0</crs:GreenSaturation>
+       <crs:BlueHue>0</crs:BlueHue>
+       <crs:BlueSaturation>0</crs:BlueSaturation>
+       <crs:FillLight>0</crs:FillLight>
+       <crs:Vibrance>0</crs:Vibrance>
+       <crs:HighlightRecovery>0</crs:HighlightRecovery>
+       <crs:Clarity>0</crs:Clarity>
+       <crs:Defringe>0</crs:Defringe>
+       <crs:HueAdjustmentRed>0</crs:HueAdjustmentRed>
+       <crs:HueAdjustmentOrange>0</crs:HueAdjustmentOrange>
+       <crs:HueAdjustmentYellow>0</crs:HueAdjustmentYellow>
+       <crs:HueAdjustmentGreen>0</crs:HueAdjustmentGreen>
+       <crs:HueAdjustmentAqua>0</crs:HueAdjustmentAqua>
+       <crs:HueAdjustmentBlue>0</crs:HueAdjustmentBlue>
+       <crs:HueAdjustmentPurple>0</crs:HueAdjustmentPurple>
+       <crs:HueAdjustmentMagenta>0</crs:HueAdjustmentMagenta>
+       <crs:SaturationAdjustmentRed>0</crs:SaturationAdjustmentRed>
+       <crs:SaturationAdjustmentOrange>0</crs:SaturationAdjustmentOrange>
+       <crs:SaturationAdjustmentYellow>0</crs:SaturationAdjustmentYellow>
+       <crs:SaturationAdjustmentGreen>0</crs:SaturationAdjustmentGreen>
+       <crs:SaturationAdjustmentAqua>0</crs:SaturationAdjustmentAqua>
+       <crs:SaturationAdjustmentBlue>0</crs:SaturationAdjustmentBlue>
+       <crs:SaturationAdjustmentPurple>0</crs:SaturationAdjustmentPurple>
+       <crs:SaturationAdjustmentMagenta>0</crs:SaturationAdjustmentMagenta>
+       <crs:LuminanceAdjustmentRed>0</crs:LuminanceAdjustmentRed>
+       <crs:LuminanceAdjustmentOrange>0</crs:LuminanceAdjustmentOrange>
+       <crs:LuminanceAdjustmentYellow>0</crs:LuminanceAdjustmentYellow>
+       <crs:LuminanceAdjustmentGreen>0</crs:LuminanceAdjustmentGreen>
+       <crs:LuminanceAdjustmentAqua>0</crs:LuminanceAdjustmentAqua>
+       <crs:LuminanceAdjustmentBlue>0</crs:LuminanceAdjustmentBlue>
+       <crs:LuminanceAdjustmentPurple>0</crs:LuminanceAdjustmentPurple>
+       <crs:LuminanceAdjustmentMagenta>0</crs:LuminanceAdjustmentMagenta>
+       <crs:SplitToningShadowHue>0</crs:SplitToningShadowHue>
+       <crs:SplitToningShadowSaturation>0</crs:SplitToningShadowSaturation>
+       <crs:SplitToningHighlightHue>0</crs:SplitToningHighlightHue>
+       <crs:SplitToningHighlightSaturation>0</crs:SplitToningHighlightSaturation>
+       <crs:SplitToningBalance>0</crs:SplitToningBalance>
+       <crs:ParametricShadows>0</crs:ParametricShadows>
+       <crs:ParametricDarks>0</crs:ParametricDarks>
+       <crs:ParametricLights>0</crs:ParametricLights>
+       <crs:ParametricHighlights>0</crs:ParametricHighlights>
+       <crs:ParametricShadowSplit>25</crs:ParametricShadowSplit>
+       <crs:ParametricMidtoneSplit>50</crs:ParametricMidtoneSplit>
+       <crs:ParametricHighlightSplit>75</crs:ParametricHighlightSplit>
+       <crs:SharpenRadius>+1.0</crs:SharpenRadius>
+       <crs:SharpenDetail>25</crs:SharpenDetail>
+       <crs:SharpenEdgeMasking>0</crs:SharpenEdgeMasking>
+       <crs:ConvertToGrayscale>False</crs:ConvertToGrayscale>
+       <crs:ToneCurveName>Medium Contrast</crs:ToneCurveName>
+       <crs:ToneCurve>
+        <rdf:Seq>
+         <rdf:li>0, 0</rdf:li>
+         <rdf:li>32, 22</rdf:li>
+         <rdf:li>64, 56</rdf:li>
+         <rdf:li>128, 128</rdf:li>
+         <rdf:li>192, 196</rdf:li>
+         <rdf:li>255, 255</rdf:li>
+        </rdf:Seq>
+       </crs:ToneCurve>
+      </crss:Parameters>
+     </rdf:li>
+    </rdf:Bag>
+   </crss:SavedSettings>
+  </rdf:Description>
+  <rdf:Description rdf:about=""
+    xmlns:lr="http://ns.adobe.com/lightroom/1.0/";>
+   <lr:hierarchicalSubject>
+    <rdf:Bag>
+     <rdf:li>choir</rdf:li>
+     <rdf:li>night</rdf:li>
+     <rdf:li>ontario|ottawa</rdf:li>
+     <rdf:li>ontario|ottawa|parliament of canada</rdf:li>
+    </rdf:Bag>
+   </lr:hierarchicalSubject>
+  </rdf:Description>
+ </rdf:RDF>
+</x:xmpmeta>

Added: trunk/src/utils/test2.ufraw
==============================================================================
--- (empty file)
+++ trunk/src/utils/test2.ufraw	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<UFRaw Version='7'>
+<InputFilename>/home/hub/incoming pics/20071225/img_5792.cr2</InputFilename>
+<OutputFilename>/home/hub/incoming pics/processed/20071225/img_5792.jpg</OutputFilename>
+<OutputPath>/home/hub/incoming pics/processed/20071225</OutputPath>
+<WB>Auto WB</WB>
+<Temperature>3684</Temperature>
+<Green>1.079753</Green>
+<ChannelMultipliers>1.497253 1.000000 2.023889</ChannelMultipliers>
+<Exposure>1.000000</Exposure>
+<ExposureNorm>1994</ExposureNorm>
+<RestoreDetails>clip</RestoreDetails>
+<ClipHighlights>film</ClipHighlights>
+<OutputType>4</OutputType>
+<CreateID>1</CreateID>
+<BaseManualCurve Current='yes'>
+</BaseManualCurve>
+<ManualCurve Current='yes'>
+</ManualCurve>
+<sRGBInputProfile Current='yes'>
+</sRGBInputProfile>
+<sRGBOutputProfile Current='yes'>
+</sRGBOutputProfile>
+<Make>Canon</Make>
+<Model>EOS 20D</Model>
+<Timestamp>Tue Dec 25 16:53:51 2007</Timestamp>
+<Orientation>5</Orientation>
+<ISOSpeed>1600</ISOSpeed>
+<Shutter>1/200 s</Shutter>
+<Aperture>F2.8</Aperture>
+<FocalLength>50.0 mm</FocalLength>
+<FocalLength35></FocalLength35>
+<EXIFSource>exiv2 0.15</EXIFSource>
+<Crop>0 201 2213 3522</Crop>
+<Log>
+ufraw_open: w:3522 h:2348 curvesize:0
+Reseting Exif.Image.Orientation from &apos;8&apos;  to &apos;1&apos;
+EXIF data read using exiv2, buflen 11058
+Warning: Directory Canon has an unhandled next pointer.
+Warning: Size 5352 of Exif.Canon.0x4002 exceeds 4096 bytes limit. Not decoded.
+
+Loading Canon EOS 20D image from /home/hub/incoming pics/20071225/img_5792.cr2 ...
+Black: 128, Maximum: 4095
+Exposure Normalization set to 1994 (1.04 EV)
+AHD interpolation...
+</Log>
+</UFRaw>

Added: trunk/src/utils/testfiles.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/testfiles.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,49 @@
+/*
+ * niepce - utils/testfiles.cpp
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+/** @brief unit test for files */
+
+#include <boost/test/minimal.hpp>
+#include <boost/bind.hpp>
+
+#include <stdlib.h>
+
+#include "files.h"
+
+using utils::FileList;
+
+int test_main( int, char *[] )             // note the name!
+{
+	system( "mkdir -p AAtest/sub" );
+	system( "touch AAtest/1" );
+	system( "touch AAtest/2" );
+	system( "touch AAtest/3" );
+
+	FileList::Ptr files;
+	
+	files = FileList::getFilesFromDirectory( "foo", boost::bind(utils::filter_none, _1) );
+	BOOST_CHECK( !files );
+
+	files = FileList::getFilesFromDirectory( "AAtest", boost::bind(utils::filter_none, _1));
+	BOOST_CHECK( files );
+	BOOST_CHECK( files->size() == 3 );
+	
+	system( "rm -fr AAtest" );
+	return 0;
+}
+

Added: trunk/src/utils/testgeometry.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/testgeometry.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,97 @@
+/*
+ * niepce - utils/testgeometry.cpp
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+/** @brief unit test for files */
+
+#include <boost/test/minimal.hpp>
+
+#include <stdlib.h>
+#include <vector>
+
+#include "geometry.h"
+
+using utils::Rect;
+
+int test_main( int, char *[] )             // note the name!
+{
+	Rect r1(0,1,2,3);
+	BOOST_CHECK(r1.to_string() == "0 1 2 3");
+	
+	std::string s("100 100 250 250");
+	Rect r2(s);
+	BOOST_CHECK(r2.x() == 100);
+	BOOST_CHECK(r2.y() == 100);
+	BOOST_CHECK(r2.w() == 250);
+	BOOST_CHECK(r2.h() == 250);
+	bool raised = false;
+	std::vector<std::string> vtest;
+	vtest.push_back("a b c d");
+	vtest.push_back("100 100 150");
+	for(std::vector<std::string>::iterator iter = vtest.begin();
+		iter != vtest.end(); ++iter) {
+		try {
+			Rect r3(*iter);
+		}
+		catch(std::bad_cast) {
+			raised = true;
+		}
+		BOOST_CHECK(raised);
+	}
+
+
+    Rect dest1(0, 0, 640, 480);
+    Rect dest2(0, 0, 480, 640);
+
+    Rect source1(0, 0, 2000, 1000);
+    Rect source2(0, 0, 1000, 2000);
+
+    Rect result;
+
+    // FIT
+    result = source1.fit_into(dest1);
+    std::cout << result.to_string() << std::endl;
+    BOOST_CHECK(result == Rect(0, 0, 640, 320));
+    result = source1.fit_into(dest2);
+    std::cout << result.to_string() << std::endl;
+    BOOST_CHECK(result == Rect(0, 0, 480, 239));    
+
+    result = source2.fit_into(dest1);
+    std::cout << result.to_string() << std::endl;
+    BOOST_CHECK(result == Rect(0, 0, 239, 480));
+    result = source2.fit_into(dest2);
+    std::cout << result.to_string() << std::endl;
+    BOOST_CHECK(result == Rect(0, 0, 320, 640));    
+
+    // FILL
+    result = source1.fill_into(dest1);
+    std::cout << result.to_string() << std::endl;
+    BOOST_CHECK(result == Rect(0, 0, 959, 480));
+    result = source1.fill_into(dest2);
+    std::cout << result.to_string() << std::endl;
+    BOOST_CHECK(result == Rect(0, 0, 1280, 640));    
+
+    result = source2.fill_into(dest1);
+    std::cout << result.to_string() << std::endl;
+    BOOST_CHECK(result == Rect(0, 0, 640, 1280));
+    result = source2.fill_into(dest2);
+    std::cout << result.to_string() << std::endl;
+    BOOST_CHECK(result == Rect(0, 0, 480, 959));    
+
+	return 0;
+}
+

Added: trunk/src/utils/testmoniker.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/testmoniker.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,37 @@
+/*
+ * niepce - utils/testmoniker.cpp
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+/** @brief Unit test for the Moniker class */
+
+
+#include <boost/test/minimal.hpp>
+
+#include "moniker.h"
+
+
+int test_main( int, char *[] )             // note the name!
+{
+	utils::Moniker moniker("foo:/bar/test");
+
+	BOOST_CHECK(moniker.scheme() == "foo");
+	BOOST_CHECK(moniker.path() == "/bar/test");
+
+	BOOST_CHECK(strcmp(moniker.c_str(), "foo:/bar/test") == 0);
+	return 0;
+}
+

Added: trunk/src/utils/teststringutils.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/teststringutils.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,44 @@
+/*
+ * niepce - utils/teststringutils.cpp
+ *
+ * Copyright (C) 2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+#include <boost/test/minimal.hpp>
+
+#include <iostream>
+#include <vector>
+#include <string>
+
+
+#include "stringutils.h"
+
+int test_main( int, char *[] )             // note the name!
+{
+	std::vector<std::string> v;
+	v.push_back("a");
+	v.push_back("b");
+	v.push_back("c");
+
+	std::string s = utils::join(v, ", ");
+
+	BOOST_CHECK(s == "a, b, c");
+
+	return 0;
+}
+

Added: trunk/src/utils/testufrawmeta.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/testufrawmeta.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,105 @@
+/*
+ * niepce - utils/testxmp.cpp
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+/** @brief unit test for xmp */
+
+#include <boost/test/minimal.hpp>
+
+#include <stdlib.h>
+#include <vector>
+
+#include <exempi/xmpconsts.h>
+
+#include "niepce/xmp.h"
+#include "debug.h"
+#include "exempi.h"
+#include "ufrawmeta.h"
+
+int test_main( int, char *[] )             // note the name!
+{
+	boost::filesystem::path dir;
+	const char * pdir = getenv("srcdir");
+	if(pdir == NULL) {
+		dir = ".";
+	}
+	else {
+		dir = pdir;
+	}
+	utils::ExempiManager xmpManager(niepce::xmp_namespaces);
+
+    xmp::ScopedPtr<XmpPtr> xmp(xmp_new_empty());
+
+	utils::UfrawMeta ufraw(dir / "test2.ufraw");
+
+	BOOST_CHECK(ufraw.ufraw_to_xmp(xmp));
+
+    xmp::ScopedPtr<XmpStringPtr> property(xmp_string_new());
+	BOOST_CHECK(property != NULL);
+	BOOST_CHECK(xmp_get_property(xmp, NS_CAMERA_RAW_SETTINGS,
+								 "WhiteBalance", property, NULL));
+	BOOST_CHECK(strcmp("Auto", xmp_string_cstr(property)) == 0);
+
+	int32_t t = 0;
+	BOOST_CHECK(xmp_get_property_int32(xmp, NS_CAMERA_RAW_SETTINGS,
+									   "Temperature", &t, NULL));
+	BOOST_CHECK(t == 3684);
+	double exposure = 0;
+	BOOST_CHECK(xmp_get_property_float(xmp, NS_CAMERA_RAW_SETTINGS,
+									   "Exposure", &exposure, NULL));
+	BOOST_CHECK(exposure == 1.0);
+	
+	bool hasCrop = false;
+	BOOST_CHECK(xmp_get_property_bool(xmp, NS_CAMERA_RAW_SETTINGS,
+									  "HasCrop", &hasCrop, NULL));
+	BOOST_CHECK(hasCrop);
+	
+	
+	BOOST_CHECK(xmp_get_property_int32(xmp, NS_CAMERA_RAW_SETTINGS,
+									   "CropUnits", &t, NULL));
+	BOOST_CHECK(t == 0);
+
+
+	double aDim = 0;
+	BOOST_CHECK(xmp_get_property_float(xmp, NS_CAMERA_RAW_SETTINGS,
+									   "CropLeft", &aDim, NULL));
+	BOOST_CHECK(aDim == 0);
+	BOOST_CHECK(xmp_get_property_float(xmp, NS_CAMERA_RAW_SETTINGS,
+									   "CropTop", &aDim, NULL));
+	BOOST_CHECK(aDim == 201);
+	BOOST_CHECK(xmp_get_property_float(xmp, NS_CAMERA_RAW_SETTINGS,
+									   "CropBottom", &aDim, NULL));
+	BOOST_CHECK(aDim == 3522);
+	BOOST_CHECK(xmp_get_property_float(xmp, NS_CAMERA_RAW_SETTINGS,
+									   "CropRight", &aDim, NULL));
+	BOOST_CHECK(aDim == 2213);
+
+	BOOST_CHECK(xmp_get_property_float(xmp, NS_CAMERA_RAW_SETTINGS,
+									   "CropWidth", &aDim, NULL));
+	BOOST_CHECK(aDim == 2213);
+	BOOST_CHECK(xmp_get_property_float(xmp, NS_CAMERA_RAW_SETTINGS,
+									   "CropHeight", &aDim, NULL));
+	BOOST_CHECK(aDim == 3321);
+
+	bool imported = false;
+	BOOST_CHECK(xmp_get_property_bool(xmp, niepce::NIEPCE_XMP_NAMESPACE,
+									  "ImportedFromUFraw", &imported, NULL));
+	BOOST_CHECK(imported);
+
+	return 0;
+}
+

Added: trunk/src/utils/testxmp.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/testxmp.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,53 @@
+/*
+ * niepce - utils/testxmp.cpp
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+/** @brief unit test for xmp */
+
+#include <boost/test/minimal.hpp>
+
+#include <stdlib.h>
+#include <vector>
+
+#include "exempi.h"
+
+int test_main( int, char *[] )             // note the name!
+{
+	boost::filesystem::path dir;
+	const char * pdir = getenv("srcdir");
+	if(pdir == NULL) {
+		dir = ".";
+	}
+	else {
+		dir = pdir;
+	}
+	utils::ExempiManager xmpManager;
+
+	utils::XmpMeta meta(dir / "test.xmp", true);
+	BOOST_CHECK(meta.isOk());
+	BOOST_CHECK(meta.orientation() == 1);
+	const std::vector< std::string > & keywords(meta.keywords());
+	BOOST_CHECK(keywords.size() == 5);
+	BOOST_CHECK(keywords[0] == "choir");
+	BOOST_CHECK(keywords[1] == "night");
+	BOOST_CHECK(keywords[2] == "ontario");
+	BOOST_CHECK(keywords[3] == "ottawa");
+	BOOST_CHECK(keywords[4] == "parliament of canada");
+	
+	return 0;
+}
+

Added: trunk/src/utils/thread.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/thread.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,62 @@
+/*
+ * niepce - utils/thread.cpp
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <boost/bind.hpp>
+
+#include "thread.h"
+
+namespace utils {
+
+
+	Thread::Thread()
+		: m_terminated(true),
+		  m_thrd(NULL)
+	{
+	}
+
+
+	Thread::~Thread()
+	{
+		terminate();
+	}
+
+
+
+#if 0
+	void Thread::schedule(const Op::Ptr & _op)
+	{
+		OpQueue::mutex_t::scoped_lock lock(m_ops.mutex(), true);
+		bool was_empty = m_ops.isEmpty();
+		m_ops.add(_op);
+		if(was_empty) {
+			start();
+		}
+	}
+#endif
+
+	void Thread::start()
+	{
+		m_thrd = m_threads.create_thread(
+			boost::bind(&Thread::main, this));
+// TODO add this thread to a manager for task management.
+//		thrd->join();
+	}
+
+}

Added: trunk/src/utils/thread.h
==============================================================================
--- (empty file)
+++ trunk/src/utils/thread.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,53 @@
+/*
+ * niepce - utils/thread.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __UTILS_THREAD_H__
+#define __UTILS_THREAD_H__
+
+
+#include <string>
+#include <boost/thread.hpp>
+
+
+namespace utils {
+
+	/** thread */
+	class Thread
+	{
+	public:
+		/** create the worker for the library whose dir is specified */
+		Thread();
+		virtual ~Thread();
+
+		void terminate()
+			{ m_terminated = true; }
+	protected:
+		void start();
+		virtual void main() = 0;
+		volatile bool m_terminated;
+	private:
+		boost::thread *       m_thrd;
+		boost::thread_group   m_threads;
+	};
+
+}
+
+
+#endif

Added: trunk/src/utils/ufrawmeta.cpp
==============================================================================
--- (empty file)
+++ trunk/src/utils/ufrawmeta.cpp	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,346 @@
+/*
+ * niepce - utils/ufrawmeta.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <map>
+#include <vector>
+#include <utility>
+
+#include <boost/filesystem/operations.hpp>
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/classification.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <exempi/xmpconsts.h>
+
+#include "niepce/xmp.h"
+#include "debug.h"
+#include "ufrawmeta.h"
+
+
+namespace bfs = boost::filesystem;
+
+namespace utils {
+
+	static const struct string_pair_t {
+		const char *first;
+		const char *second;
+	} s_wb_ufraw_to_xmp[] = {
+		{ "Camera WB", "As Shot" },
+		{ "camera", "As Shot" },
+		{ "Auto WB", "Auto" },
+		{ "auto", "Auto" },
+		{ "Direct sunlight", "Daylight" },
+		{ "Cloudy", "Cloudy" },
+		{ "Shade", "Shade" },
+		{ "Incandescent", "Tungsten" },
+		{ "Fluorescent", "Fluorescent" },
+		{ "Flash", "Flash" },
+		{ "Manual WB", "Custom" },
+		{ 0, 0 }
+	};
+
+
+	typedef std::map<std::string, int> tag_map_t;
+
+	enum {
+		UFRAW_TAG_UFRaw = 1,
+		UFRAW_TAG_InputFilename,
+		UFRAW_TAG_OutputFilename,
+		UFRAW_TAG_OutputPath,
+		UFRAW_TAG_WB,
+		UFRAW_TAG_Temperature,
+		UFRAW_TAG_Exposure,
+		UFRAW_TAG_Crop,
+		UFRAW_TAG_Green,
+		UFRAW_TAG_ChannelMultipliers
+	};
+
+#define TAG(x) { #x, UFRAW_TAG_##x}
+
+	static const struct tag_pair_t {
+		const char * name;
+		int          id;
+	} s_elements[] = {
+		TAG(UFRaw),
+		TAG(InputFilename),
+		TAG(OutputFilename),
+		TAG(OutputPath),
+		TAG(WB),
+		TAG(Temperature),
+		TAG(Exposure),
+		TAG(Crop),
+		TAG(Green),
+		TAG(ChannelMultipliers),
+		{ 0, 0 }
+	};
+
+
+	UfrawMeta::UfrawMeta(const bfs::path & file)
+		: m_id_file(file),
+		  m_hasSettings(false),
+		  m_startDepth(-1)
+	{
+	}
+
+
+	int UfrawMeta::get_tag_id(const xmlChar *name)
+	{
+		static tag_map_t s_tag_map;
+		// TODO lock for concurrent access
+		if(s_tag_map.empty()) {
+			const tag_pair_t* ptag = s_elements;
+			DBG_ASSERT(ptag != NULL, "p can't be NULL");
+			while(ptag->id != 0) {
+				s_tag_map.insert(std::make_pair(ptag->name, ptag->id));
+				ptag++;
+			}
+		}
+
+		tag_map_t::const_iterator iter = s_tag_map.find((const char*)name);
+		if(iter == s_tag_map.end()) {
+			return -1;
+		}
+		return iter->second;
+	}
+
+
+	static const char * convertWB(const std::string & wb)
+	{
+		static std::map<std::string, const char *> wb_map;
+		if(wb_map.empty()) {
+			const string_pair_t *ppair =  s_wb_ufraw_to_xmp;
+			while(ppair->first != 0) {
+				wb_map.insert(std::make_pair(ppair->first, ppair->second));
+				ppair++;				
+			}
+		}
+		std::map<std::string, const char *>::iterator iter = wb_map.find(wb);
+		if(iter == wb_map.end()) {
+			return NULL;
+		}
+		return iter->second;
+	}
+
+
+
+	static std::string getPropContent(xmlTextReaderPtr reader)
+	{
+		std::string retval;
+		if(xmlTextReaderRead(reader) == 1) {
+			const xmlChar * content = xmlTextReaderConstValue(reader);
+			if(content) {
+				retval = (const char*)content;
+			}
+			else {
+				DBG_OUT("content is NULL");
+			}
+		}
+		return retval;
+	}
+
+	
+
+	/** @return true if any data has been found. false means an error
+	 * (unknown content)
+	 */
+	bool UfrawMeta::ufraw_process_xml_node(xmlTextReaderPtr reader, XmpPtr xmp)
+	{
+		bool ret = true;
+		int node_type = xmlTextReaderNodeType(reader);
+		int depth = xmlTextReaderDepth(reader);
+		if(node_type == XML_READER_TYPE_ELEMENT) {
+			const xmlChar *name = xmlTextReaderConstName(reader);
+			int tag = get_tag_id(name);
+			switch(tag) {
+			case UFRAW_TAG_UFRaw:
+			{
+				if(m_startDepth == -1)
+				{
+					m_startDepth = depth;
+				}
+				xmlChar * attrib = xmlTextReaderGetAttribute(reader, 
+															 (const xmlChar*)"Version");
+				if(attrib != NULL) {
+					// other version are 3 or 5. deal with them
+					if(strcmp("7", (const char*)attrib) == 0) {
+						
+					}
+					else {
+						DBG_OUT("unknown version %s", attrib);
+						ret = false;
+					}
+					xmlFree(attrib);						
+				}
+				break;
+			}
+			case UFRAW_TAG_InputFilename:
+			case UFRAW_TAG_OutputFilename:
+			case UFRAW_TAG_OutputPath:
+				if(depth == m_startDepth + 1) {
+					switch(tag) {
+					case UFRAW_TAG_InputFilename:
+						m_input = getPropContent(reader);
+						break;
+					case UFRAW_TAG_OutputFilename:
+						m_output = getPropContent(reader);
+						break;
+					case UFRAW_TAG_OutputPath:
+						m_outputpath = getPropContent(reader);
+						break;
+					}
+				}
+				break;
+			case UFRAW_TAG_WB:
+			{
+				if(depth == m_startDepth + 1) {
+					std::string wb = getPropContent(reader);
+					const char *xmp_wb = convertWB(wb);
+					if(xmp_wb != NULL) {
+						xmp_set_property(xmp, NS_CAMERA_RAW_SETTINGS,
+										 "WhiteBalance", xmp_wb, 0);
+						m_hasSettings = true;
+					}
+					else {
+						DBG_OUT("unable to convert WB %s", wb.c_str());
+					}
+				}
+				break;
+			}
+			case UFRAW_TAG_Temperature:
+			{
+				if(depth == m_startDepth + 1) {
+					std::string temp = getPropContent(reader);
+					try {
+						int32_t t = boost::lexical_cast<int32_t>(temp);
+						xmp_set_property_int32(xmp, NS_CAMERA_RAW_SETTINGS,
+											   "Temperature", t, 0);
+						m_hasSettings = true;
+					} 
+					catch(const boost::bad_lexical_cast &)
+					{
+						ERR_OUT("exception");
+					}
+				}
+				break;
+			}
+			case UFRAW_TAG_Exposure:
+			{
+				if(depth == m_startDepth + 1) {
+					std::string exp = getPropContent(reader);
+					try {
+						double exposure = boost::lexical_cast<double>(exp);
+						xmp_set_property_float(xmp, NS_CAMERA_RAW_SETTINGS,
+											   "Exposure", exposure, 0);
+						m_hasSettings = true;
+					} 
+					catch(const boost::bad_lexical_cast &)
+					{
+						ERR_OUT("exception");					
+					}
+				}
+				break;
+			}
+			case UFRAW_TAG_Crop:
+			{ 
+				if(depth == m_startDepth + 1) {
+					std::string crop = getPropContent(reader);
+				// order is left top right bottom
+					
+					std::vector< std::string > v;
+					v.reserve(4);
+					boost::split(v, crop, boost::is_any_of(" "));
+					if(v.size() == 4) {
+						int left, top, bottom, right;
+						try {
+							std::vector< std::string >::iterator iter = v.begin();
+							left = boost::lexical_cast<int>(*iter);
+							iter++;
+							top = boost::lexical_cast<int>(*iter);
+							iter++;
+							right = boost::lexical_cast<int>(*iter);
+							iter++;
+							bottom = boost::lexical_cast<int>(*iter);
+							xmp_set_property_float(xmp, NS_CAMERA_RAW_SETTINGS,
+												   "CropLeft", left, 0);
+							xmp_set_property_float(xmp, NS_CAMERA_RAW_SETTINGS,
+												   "CropTop", top, 0);
+							xmp_set_property_float(xmp, NS_CAMERA_RAW_SETTINGS,
+												   "CropBottom", bottom, 0);
+							xmp_set_property_float(xmp, NS_CAMERA_RAW_SETTINGS,
+												   "CropRight", right, 0);
+							xmp_set_property_bool(xmp, NS_CAMERA_RAW_SETTINGS,
+												  "HasCrop", true, 0);
+							// units are pixels
+							xmp_set_property_int32(xmp, NS_CAMERA_RAW_SETTINGS,
+												   "CropUnits", 0, 0);
+							int width = right - left;
+							int height = bottom - top;
+							xmp_set_property_float(xmp, NS_CAMERA_RAW_SETTINGS,
+												   "CropWidth", width, 0);
+							xmp_set_property_float(xmp, NS_CAMERA_RAW_SETTINGS,
+												   "CropHeight", height, 0);
+						}
+						catch(const boost::bad_lexical_cast & e)
+						{
+							DBG_OUT("exception");
+						}
+					}
+				}
+				break;
+			}
+			default:
+				break;
+			}
+		}
+		return ret;
+	}
+
+
+	bool UfrawMeta::ufraw_to_xmp(XmpPtr xmp)
+	{
+		bool has_data = false;
+		if(exists(m_id_file)) {
+			xmlTextReaderPtr reader;
+			reader = xmlNewTextReaderFilename(m_id_file.string().c_str());
+			if(reader != NULL) {
+				int ret = xmlTextReaderRead(reader);
+				while(ret == 1) {
+					has_data = ufraw_process_xml_node(reader, xmp);
+					if(!has_data) {
+						break;
+					}
+					ret = xmlTextReaderRead(reader);
+				}
+				xmlFreeTextReader(reader);
+			}
+			else {
+				DBG_OUT("xml reader is NULL");
+			}
+		}
+		else {
+			DBG_OUT("file not found %s", m_id_file.string().c_str());
+		}
+		if(has_data) {
+			xmp_set_property_bool(xmp, niepce::NIEPCE_XMP_NAMESPACE, 
+								  "ImportedFromUFraw", true, 0);
+		}
+		return has_data;
+	}
+
+}

Added: trunk/src/utils/ufrawmeta.h
==============================================================================
--- (empty file)
+++ trunk/src/utils/ufrawmeta.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,54 @@
+/*
+ * niepce - utils/ufrawmeta.h
+ *
+ * Copyright (C) 2007 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @brief conversion routine for ufraw meta data (id) */
+
+#include <exempi/xmp.h>
+#include <libxml/xmlreader.h>
+
+#include <boost/filesystem/path.hpp>
+
+namespace utils {
+
+	class UfrawMeta
+	{
+	public:
+		UfrawMeta(const boost::filesystem::path & file);
+
+		/** convert the .ufraw file to a XMP meta. Scrap the Exif
+		 * @param file the name of the ufraw filename.
+		 * @return true if it loaded it.
+		 */
+		bool ufraw_to_xmp(XmpPtr xmp);
+
+	private:
+		static int get_tag_id(const xmlChar *name);
+		bool ufraw_process_xml_node(xmlTextReaderPtr reader, XmpPtr xmp);
+		
+		boost::filesystem::path m_id_file; /**< the ufraw ID file */
+
+		std::string m_input;
+		std::string m_output;
+		std::string m_outputpath;
+		bool        m_hasSettings;
+		int         m_startDepth;
+	};
+
+
+}

Added: trunk/src/utils/worker.h
==============================================================================
--- (empty file)
+++ trunk/src/utils/worker.h	Wed Dec 10 19:46:36 2008
@@ -0,0 +1,113 @@
+/*
+ * niepce - utils/worker.h
+ *
+ * Copyright (C) 2007-2008 Hubert Figuiere
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __UTILS_WORKER_H__
+#define __UTILS_WORKER_H__
+
+
+#include <string>
+#include <boost/thread.hpp>
+#include <boost/bind.hpp>
+
+#include "utils/thread.h"
+#include "utils/mtqueue.h"
+
+namespace utils {
+
+	/** worker thread for the library */
+	template <class T>
+	class Worker
+		: public Thread
+	{
+	public:
+		Worker();
+		virtual ~Worker();
+		typedef MtQueue<T> queue_t;
+
+#ifdef BOOST_AUTO_TEST_MAIN
+		queue_t & _tasks() 
+			{ return m_tasks; }
+#endif
+		void schedule(const T & );
+		void clear();
+	protected:
+		virtual void main();
+
+		queue_t      m_tasks;
+	private:
+		virtual void execute(const T & _op) = 0;
+	};
+
+	template <class T>
+	Worker<T>::Worker()
+		: Thread()
+	{
+	}
+
+	template <class T>
+	Worker<T>::~Worker()
+	{
+		typename queue_t::mutex_t::scoped_lock lock(m_tasks.mutex());
+		m_tasks.clear();
+	}
+
+	/** this is the main loop of the libray worker */
+	template <class T>
+	void Worker<T>::main()
+	{
+		m_terminated = false;
+		
+		do 
+		{
+			T op;
+			{
+				// make sure we terminate the thread before we unlock
+				// the task queue.
+				typename queue_t::mutex_t::scoped_lock lock(m_tasks.mutex());
+				if(m_tasks.empty()) {
+					m_terminated = true;
+					break;
+				}
+				op = m_tasks.pop();
+			}
+			execute(op);
+		} while(!m_terminated);
+	}
+
+	template <class T>
+	void Worker<T>::schedule(const T & _op)
+	{
+		typename queue_t::mutex_t::scoped_lock lock(m_tasks.mutex());
+		bool was_empty = m_tasks.empty();
+		m_tasks.add(_op);
+		if(was_empty) {
+			start();
+		}
+	}
+
+	template <class T>
+	void Worker<T>::clear()
+	{
+		m_tasks.clear();
+	}
+
+}
+
+#endif



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]