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
- From: hub svn gnome org
- To: svn-commits-list gnome org
- Subject: 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
- Date: Wed, 10 Dec 2008 19:46:36 +0000 (UTC)
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(¤ttime, &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"><b>Date:</b></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 '8' to '1'
+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]