Add a new image viewer - GPicView.
authorHong Jen Yee (PCMan) <pcman.tw@gmail.com>
Sun, 9 Sep 2007 15:44:52 +0000 (15:44 +0000)
committerHong Jen Yee (PCMan) <pcman.tw@gmail.com>
Sun, 9 Sep 2007 15:44:52 +0000 (15:44 +0000)
29 files changed:
AUTHORS [new file with mode: 0644]
COPYING [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
NEWS [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
configure.in [new file with mode: 0644]
data/Makefile.am [new file with mode: 0644]
data/pixmaps/clockwise.png [new file with mode: 0644]
data/pixmaps/counterclockwise.png [new file with mode: 0644]
data/pixmaps/fullscreen.png [new file with mode: 0644]
gpicview.desktop.in [new file with mode: 0644]
gpicview.png [new file with mode: 0644]
po/ChangeLog [new file with mode: 0644]
po/Makefile.in.in [new file with mode: 0644]
po/POTFILES.in [new file with mode: 0644]
po/gpicview.pot [new file with mode: 0644]
po/zh_TW.po [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]
src/gpicview.cpp [new file with mode: 0644]
src/imagelist.cpp [new file with mode: 0644]
src/imagelist.h [new file with mode: 0644]
src/mainwin.cpp [new file with mode: 0644]
src/mainwin.h [new file with mode: 0644]
src/working-area.c [new file with mode: 0644]
src/working-area.h [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..07b9a66
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+PCMan (Hong Jen Yee) <pcman.tw@gmail.com>
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..623b625
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  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
+this service 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 make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  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.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE 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.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           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
+convey 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 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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision 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, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This 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 Library General
+Public License instead of this License.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..02a4a07
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,167 @@
+Basic Installation
+==================
+
+   These are generic installation instructions.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+   The file `configure.in' is used to create `configure' by a program
+called `autoconf'.  You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.  If you're
+     using `csh' on an old version of System V, you might need to type
+     `sh ./configure' instead to prevent `csh' from trying to execute
+     `configure' itself.
+
+     Running `configure' takes a while.  While running, it prints some
+     messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Type `make install' to install the programs and any data files and
+     documentation.
+
+  4. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  
+
+Compilers and Options
+=====================
+
+   Some systems require unusual options for compilation or linking that
+the `configure' script does not know about.  You can give `configure'
+initial values for variables by setting them in the environment.  Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+     CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+     env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+   You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory.  After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+   By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc.  You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+   Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+   There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on.  Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+     CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+   If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+   If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+   `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+     Use and save the results of the tests in FILE instead of
+     `./config.cache'.  Set FILE to `/dev/null' to disable caching, for
+     debugging `configure'.
+
+`--help'
+     Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`--version'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..3446c18
--- /dev/null
@@ -0,0 +1,36 @@
+## Process this file with automake to produce Makefile.in
+
+SUBDIRS = src po data
+
+EXTRA_DIST = \
+       autogen.sh \
+       gpicview.desktop.in \
+       gpicview.png
+
+desktopdir = $(datadir)/applications
+desktop_DATA = gpicview.desktop
+
+icondir = $(datadir)/pixmaps
+icon_DATA = gpicview.png
+
+install-data-local:
+       @$(NORMAL_INSTALL)
+       if test -d $(srcdir)/pixmaps; then \
+         $(mkinstalldirs) $(DESTDIR)$(pkgdatadir)/pixmaps; \
+         for pixmap in $(srcdir)/pixmaps/*; do \
+           if test -f $$pixmap; then \
+             $(INSTALL_DATA) $$pixmap $(DESTDIR)$(pkgdatadir)/pixmaps; \
+           fi \
+         done \
+       fi
+
+dist-hook:
+       if test -d pixmaps; then \
+         mkdir $(distdir)/pixmaps; \
+         for pixmap in pixmaps/*; do \
+           if test -f $$pixmap; then \
+             cp -p $$pixmap $(distdir)/pixmaps; \
+           fi \
+         done \
+       fi
+
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..9bbea2c
--- /dev/null
@@ -0,0 +1,159 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+DIE=0
+
+if [ -n "$GNOME2_DIR" ]; then
+       ACLOCAL_FLAGS="-I $GNOME2_DIR/share/aclocal $ACLOCAL_FLAGS"
+       LD_LIBRARY_PATH="$GNOME2_DIR/lib:$LD_LIBRARY_PATH"
+       PATH="$GNOME2_DIR/bin:$PATH"
+       export PATH
+       export LD_LIBRARY_PATH
+fi
+
+(test -f $srcdir/configure.in) || {
+    echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
+    echo " top-level package directory"
+    exit 1
+}
+
+(autoconf --version) < /dev/null > /dev/null 2>&1 || {
+  echo
+  echo "**Error**: You must have \`autoconf' installed."
+  echo "Download the appropriate package for your distribution,"
+  echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
+  DIE=1
+}
+
+(grep "^AC_PROG_INTLTOOL" $srcdir/configure.in >/dev/null) && {
+  (intltoolize --version) < /dev/null > /dev/null 2>&1 || {
+    echo 
+    echo "**Error**: You must have \`intltool' installed."
+    echo "You can get it from:"
+    echo "  ftp://ftp.gnome.org/pub/GNOME/"
+    DIE=1
+  }
+}
+
+(grep "^AM_PROG_XML_I18N_TOOLS" $srcdir/configure.in >/dev/null) && {
+  (xml-i18n-toolize --version) < /dev/null > /dev/null 2>&1 || {
+    echo
+    echo "**Error**: You must have \`xml-i18n-toolize' installed."
+    echo "You can get it from:"
+    echo "  ftp://ftp.gnome.org/pub/GNOME/"
+    DIE=1
+  }
+}
+
+(grep "^AM_PROG_LIBTOOL" $srcdir/configure.in >/dev/null) && {
+  (libtool --version) < /dev/null > /dev/null 2>&1 || {
+    echo
+    echo "**Error**: You must have \`libtool' installed."
+    echo "You can get it from: ftp://ftp.gnu.org/pub/gnu/"
+    DIE=1
+  }
+}
+
+(grep "^AM_GLIB_GNU_GETTEXT" $srcdir/configure.in >/dev/null) && {
+  (grep "sed.*POTFILES" $srcdir/configure.in) > /dev/null || \
+  (glib-gettextize --version) < /dev/null > /dev/null 2>&1 || {
+    echo
+    echo "**Error**: You must have \`glib' installed."
+    echo "You can get it from: ftp://ftp.gtk.org/pub/gtk"
+    DIE=1
+  }
+}
+
+(automake --version) < /dev/null > /dev/null 2>&1 || {
+  echo
+  echo "**Error**: You must have \`automake' installed."
+  echo "You can get it from: ftp://ftp.gnu.org/pub/gnu/"
+  DIE=1
+  NO_AUTOMAKE=yes
+}
+
+
+# if no automake, don't bother testing for aclocal
+test -n "$NO_AUTOMAKE" || (aclocal --version) < /dev/null > /dev/null 2>&1 || {
+  echo
+  echo "**Error**: Missing \`aclocal'.  The version of \`automake'"
+  echo "installed doesn't appear recent enough."
+  echo "You can get automake from ftp://ftp.gnu.org/pub/gnu/"
+  DIE=1
+}
+
+if test "$DIE" -eq 1; then
+  exit 1
+fi
+
+if test -z "$*"; then
+  echo "**Warning**: I am going to run \`configure' with no arguments."
+  echo "If you wish to pass any to it, please specify them on the"
+  echo \`$0\'" command line."
+  echo
+fi
+
+case $CC in
+xlc )
+  am_opt=--include-deps;;
+esac
+
+for coin in `find $srcdir -name configure.in -print`
+do 
+  dr=`dirname $coin`
+  if test -f $dr/NO-AUTO-GEN; then
+    echo skipping $dr -- flagged as no auto-gen
+  else
+    echo processing $dr
+    ( cd $dr
+
+      aclocalinclude="$ACLOCAL_FLAGS"
+
+      if grep "^AM_GLIB_GNU_GETTEXT" configure.in >/dev/null; then
+       echo "Creating $dr/aclocal.m4 ..."
+       test -r $dr/aclocal.m4 || touch $dr/aclocal.m4
+       echo "Running glib-gettextize...  Ignore non-fatal messages."
+       echo "no" | glib-gettextize --force --copy
+       echo "Making $dr/aclocal.m4 writable ..."
+       test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4
+      fi
+      if grep "^AC_PROG_INTLTOOL" configure.in >/dev/null; then
+        echo "Running intltoolize..."
+       intltoolize --copy --force --automake
+      fi
+      if grep "^AM_PROG_XML_I18N_TOOLS" configure.in >/dev/null; then
+        echo "Running xml-i18n-toolize..."
+       xml-i18n-toolize --copy --force --automake
+      fi
+      if grep "^AM_PROG_LIBTOOL" configure.in >/dev/null; then
+       if test -z "$NO_LIBTOOLIZE" ; then 
+         echo "Running libtoolize..."
+         libtoolize --force --copy
+       fi
+      fi
+      echo "Running aclocal $aclocalinclude ..."
+      aclocal $aclocalinclude
+      if grep "^AM_CONFIG_HEADER" configure.in >/dev/null; then
+       echo "Running autoheader..."
+       autoheader
+      fi
+      echo "Running automake --gnu $am_opt ..."
+      automake --add-missing --gnu $am_opt
+      echo "Running autoconf ..."
+      autoconf
+    )
+  fi
+done
+
+conf_flags="--enable-maintainer-mode"
+
+if test x$NOCONFIGURE = x; then
+  echo Running $srcdir/configure $conf_flags "$@" ...
+  $srcdir/configure $conf_flags "$@" \
+  && echo Now type \`make\' to compile. || exit 1
+else
+  echo Skipping configure process.
+fi
diff --git a/configure.in b/configure.in
new file mode 100644 (file)
index 0000000..ee816e4
--- /dev/null
@@ -0,0 +1,47 @@
+dnl Process this file with autoconf to produce a configure script.
+
+AC_INIT(configure.in)
+AM_INIT_AUTOMAKE(gpicview, 0.1)
+AM_CONFIG_HEADER(config.h)
+AM_MAINTAINER_MODE
+
+AC_LANG_CPLUSPLUS
+dnl AC_PROG_CXX
+AM_PROG_LIBTOOL
+
+dnl AC_ISC_POSIX
+AC_PROG_CC
+AM_PROG_CC_STDC
+AC_HEADER_STDC
+
+pkg_modules="gtk+-2.0 >= 2.6.0"
+
+PKG_CHECK_MODULES(PACKAGE, [$pkg_modules])
+AC_SUBST(PACKAGE_CFLAGS)
+AC_SUBST(PACKAGE_LIBS)
+
+GETTEXT_PACKAGE=gpicview
+AC_SUBST(GETTEXT_PACKAGE)
+AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", [Gettext package.])
+
+dnl Add the languages which your application supports here.
+ALL_LINGUAS="zh_TW"
+AM_GLIB_GNU_GETTEXT
+
+AC_OUTPUT([
+Makefile
+src/Makefile
+data/Makefile
+po/Makefile.in
+gpicview.desktop
+])
+
+echo
+echo GPicView....................... : Version $VERSION
+echo
+echo Prefix..........................: $prefix
+echo
+echo The binary will be installed in $prefix/bin
+echo
+echo http://gpicview.sourceforge.net/
+echo
diff --git a/data/Makefile.am b/data/Makefile.am
new file mode 100644 (file)
index 0000000..4d8a6b7
--- /dev/null
@@ -0,0 +1,14 @@
+PIXMAPS_FILES = \
+       pixmaps/clockwise.png \
+       pixmaps/counterclockwise.png \
+       pixmaps/fullscreen.png
+
+gpicview_DATA = 
+gpicviewdir = $(datadir)/gpicview
+
+pixmaps_DATA = $(PIXMAPS_FILES)
+pixmapsdir = $(gpicviewdir)/pixmaps
+
+EXTRA_DIST = \
+       $(gpicview_DATA) \
+       $(pixmaps_DATA)
diff --git a/data/pixmaps/clockwise.png b/data/pixmaps/clockwise.png
new file mode 100644 (file)
index 0000000..1e6fd74
Binary files /dev/null and b/data/pixmaps/clockwise.png differ
diff --git a/data/pixmaps/counterclockwise.png b/data/pixmaps/counterclockwise.png
new file mode 100644 (file)
index 0000000..a705a1f
Binary files /dev/null and b/data/pixmaps/counterclockwise.png differ
diff --git a/data/pixmaps/fullscreen.png b/data/pixmaps/fullscreen.png
new file mode 100644 (file)
index 0000000..c39ed8a
Binary files /dev/null and b/data/pixmaps/fullscreen.png differ
diff --git a/gpicview.desktop.in b/gpicview.desktop.in
new file mode 100644 (file)
index 0000000..a4b8d3a
--- /dev/null
@@ -0,0 +1,16 @@
+[Desktop Entry]
+Encoding=UTF-8
+Categories=Application;Graphics;Utility;Core;GTK;Viewer;RasterGraphics;2DGraphics;Photography;
+Exec=gpicview %f
+Icon=gpicview
+StartupNotify=true
+Type=Application
+Terminal=false
+MimeType=image/bmp;image/gif;image/jpeg;image/jpg;image/png;image/tiff;image/x-bmp;image/x-pcx;image/x-tga;image/x-portable-pixmap;image/x-portable-bitmap;image/x-targa;image/x-portable-greymap;application/pcx;image/svg+xml;image/svg-xml;
+Name=GPicView Image Viewer
+Name[zh_TW]=GPicView 圖片檢視器
+GenericName=Image Viewer
+GenericName[zh_TW]=圖片檢視器
+Comment=View your images easily
+Comment[zh_TW]=輕鬆檢視數位相片和圖片
+
diff --git a/gpicview.png b/gpicview.png
new file mode 100644 (file)
index 0000000..e9d2932
Binary files /dev/null and b/gpicview.png differ
diff --git a/po/ChangeLog b/po/ChangeLog
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/po/Makefile.in.in b/po/Makefile.in.in
new file mode 100644 (file)
index 0000000..7c26df4
--- /dev/null
@@ -0,0 +1,271 @@
+# Makefile for program source directory in GNU NLS utilities package.
+# Copyright (C) 1995, 1996, 1997 by Ulrich Drepper <drepper@gnu.ai.mit.edu>
+#
+# This file file be copied and used freely without restrictions.  It can
+# be used in projects which are not available under the GNU Public License
+# but which still want to provide support for the GNU gettext functionality.
+# Please note that the actual code is *not* freely available.
+#
+# - Modified by Owen Taylor <otaylor@redhat.com> to use GETTEXT_PACKAGE
+#   instead of PACKAGE and to look for po2tbl in ./ not in intl/
+#
+# - Modified by jacob berkman <jacob@ximian.com> to install
+#   Makefile.in.in and po2tbl.sed.in for use with glib-gettextize
+
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+
+SHELL = /bin/sh
+@SET_MAKE@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+datarootdir = @datarootdir@
+datadir = @datadir@
+libdir = @libdir@
+localedir = $(libdir)/locale
+gnulocaledir = $(datadir)/locale
+gettextsrcdir = $(datadir)/glib-2.0/gettext/po
+subdir = po
+
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+MKINSTALLDIRS = $(top_srcdir)/@MKINSTALLDIRS@
+
+CC = @CC@
+GENCAT = @GENCAT@
+GMSGFMT = @GMSGFMT@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+XGETTEXT = @XGETTEXT@
+MSGMERGE = msgmerge
+
+DEFS = @DEFS@
+CFLAGS = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+
+INCLUDES = -I.. -I$(top_srcdir)/intl
+
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) $(XCFLAGS)
+
+SOURCES = 
+POFILES = @POFILES@
+GMOFILES = @GMOFILES@
+DISTFILES = ChangeLog Makefile.in.in POTFILES.in $(GETTEXT_PACKAGE).pot \
+$(POFILES) $(GMOFILES) $(SOURCES)
+
+POTFILES = \
+
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+INSTOBJEXT = @INSTOBJEXT@
+
+.SUFFIXES:
+.SUFFIXES: .c .o .po .pox .gmo .mo .msg .cat
+
+.c.o:
+       $(COMPILE) $<
+
+.po.pox:
+       $(MAKE) $(GETTEXT_PACKAGE).pot
+       $(MSGMERGE) $< $(srcdir)/$(GETTEXT_PACKAGE).pot -o $*.pox
+
+.po.mo:
+       $(MSGFMT) -o $@ $<
+
+.po.gmo:
+       file=$(srcdir)/`echo $* | sed 's,.*/,,'`.gmo \
+         && rm -f $$file && $(GMSGFMT) $(MSGFMT_OPTS) -o $$file $<
+
+.po.cat:
+       sed -f ../intl/po2msg.sed < $< > $*.msg \
+         && rm -f $@ && $(GENCAT) $@ $*.msg
+
+
+all: all-@USE_NLS@
+
+all-yes: $(CATALOGS)
+all-no:
+
+$(srcdir)/$(GETTEXT_PACKAGE).pot: $(POTFILES)
+       $(XGETTEXT) --default-domain=$(GETTEXT_PACKAGE) --directory=$(top_srcdir) \
+         --add-comments --keyword=_ --keyword=N_ \
+          --flag=g_strdup_printf:1:c-format \
+          --flag=g_string_printf:2:c-format \
+          --flag=g_string_append_printf:2:c-format \
+          --flag=g_error_new:3:c-format \
+          --flag=g_set_error:4:c-format \
+          --flag=g_markup_printf_escaped:1:c-format \
+          --flag=g_log:3:c-format \
+          --flag=g_print:1:c-format \
+          --flag=g_printerr:1:c-format \
+          --flag=g_printf:1:c-format \
+          --flag=g_fprintf:2:c-format \
+          --flag=g_sprintf:2:c-format \
+          --flag=g_snprintf:3:c-format \
+          --flag=g_scanner_error:2:c-format \
+          --flag=g_scanner_warn:2:c-format \
+         --files-from=$(srcdir)/POTFILES.in \
+       && test ! -f $(GETTEXT_PACKAGE).po \
+          || ( rm -f $(srcdir)/$(GETTEXT_PACKAGE).pot \
+               && mv $(GETTEXT_PACKAGE).po $(srcdir)/$(GETTEXT_PACKAGE).pot )
+
+install: install-exec install-data
+install-exec:
+install-data: install-data-@USE_NLS@
+install-data-no: all
+install-data-yes: all
+       if test -r "$(MKINSTALLDIRS)"; then \
+         $(MKINSTALLDIRS) $(DESTDIR)$(datadir); \
+       else \
+         $(SHELL) $(top_srcdir)/mkinstalldirs $(DESTDIR)$(datadir); \
+       fi
+       @catalogs='$(CATALOGS)'; \
+       for cat in $$catalogs; do \
+         cat=`basename $$cat`; \
+         case "$$cat" in \
+           *.gmo) destdir=$(gnulocaledir);; \
+           *)     destdir=$(localedir);; \
+         esac; \
+         lang=`echo $$cat | sed 's/\$(CATOBJEXT)$$//'`; \
+         dir=$(DESTDIR)$$destdir/$$lang/LC_MESSAGES; \
+         if test -r "$(MKINSTALLDIRS)"; then \
+           $(MKINSTALLDIRS) $$dir; \
+         else \
+           $(SHELL) $(top_srcdir)/mkinstalldirs $$dir; \
+         fi; \
+         if test -r $$cat; then \
+           $(INSTALL_DATA) $$cat $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT); \
+           echo "installing $$cat as $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT)"; \
+         else \
+           $(INSTALL_DATA) $(srcdir)/$$cat $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT); \
+           echo "installing $(srcdir)/$$cat as" \
+                "$$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT)"; \
+         fi; \
+         if test -r $$cat.m; then \
+           $(INSTALL_DATA) $$cat.m $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m; \
+           echo "installing $$cat.m as $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m"; \
+         else \
+           if test -r $(srcdir)/$$cat.m ; then \
+             $(INSTALL_DATA) $(srcdir)/$$cat.m \
+               $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m; \
+             echo "installing $(srcdir)/$$cat as" \
+                  "$$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m"; \
+           else \
+             true; \
+           fi; \
+         fi; \
+       done
+       if test "$(PACKAGE)" = "glib"; then \
+         if test -r "$(MKINSTALLDIRS)"; then \
+           $(MKINSTALLDIRS) $(DESTDIR)$(gettextsrcdir); \
+         else \
+           $(SHELL) $(top_srcdir)/mkinstalldirs $(DESTDIR)$(gettextsrcdir); \
+         fi; \
+         $(INSTALL_DATA) $(srcdir)/Makefile.in.in \
+                         $(DESTDIR)$(gettextsrcdir)/Makefile.in.in; \
+       else \
+         : ; \
+       fi
+
+# Define this as empty until I found a useful application.
+installcheck:
+
+uninstall:
+       catalogs='$(CATALOGS)'; \
+       for cat in $$catalogs; do \
+         cat=`basename $$cat`; \
+         lang=`echo $$cat | sed 's/\$(CATOBJEXT)$$//'`; \
+         rm -f $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE)$(INSTOBJEXT); \
+         rm -f $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m; \
+         rm -f $(DESTDIR)$(gnulocaledir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE)$(INSTOBJEXT); \
+         rm -f $(DESTDIR)$(gnulocaledir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m; \
+       done
+       if test "$(PACKAGE)" = "glib"; then \
+         rm -f $(DESTDIR)$(gettextsrcdir)/Makefile.in.in; \
+       fi
+
+check: all
+
+dvi info tags TAGS ID:
+
+mostlyclean:
+       rm -f core core.* *.pox $(GETTEXT_PACKAGE).po *.old.po cat-id-tbl.tmp
+       rm -fr *.o
+
+clean: mostlyclean
+
+distclean: clean
+       rm -f Makefile Makefile.in POTFILES *.mo *.msg *.cat *.cat.m
+
+maintainer-clean: distclean
+       @echo "This command is intended for maintainers to use;"
+       @echo "it deletes files that may require special tools to rebuild."
+       rm -f $(GMOFILES)
+
+distdir = ../$(GETTEXT_PACKAGE)-$(VERSION)/$(subdir)
+dist distdir: update-po $(DISTFILES)
+       dists="$(DISTFILES)"; \
+       for file in $$dists; do \
+         ln $(srcdir)/$$file $(distdir) 2> /dev/null \
+           || cp -p $(srcdir)/$$file $(distdir); \
+       done
+
+update-po: Makefile
+       $(MAKE) $(GETTEXT_PACKAGE).pot
+       tmpdir=`pwd`; \
+       cd $(srcdir); \
+       catalogs='$(CATALOGS)'; \
+       for cat in $$catalogs; do \
+         cat=`basename $$cat`; \
+         lang=`echo $$cat | sed 's/\$(CATOBJEXT)$$//'`; \
+         echo "$$lang:"; \
+         if $(MSGMERGE) $$lang.po $(GETTEXT_PACKAGE).pot -o $$tmpdir/$$lang.new.po; then \
+           if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \
+             rm -f $$tmpdir/$$lang.new.po; \
+            else \
+             if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \
+               :; \
+             else \
+               echo "msgmerge for $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \
+               rm -f $$tmpdir/$$lang.new.po; \
+               exit 1; \
+             fi; \
+           fi; \
+         else \
+           echo "msgmerge for $$cat failed!"; \
+           rm -f $$tmpdir/$$lang.new.po; \
+         fi; \
+       done
+
+# POTFILES is created from POTFILES.in by stripping comments, empty lines
+# and Intltool tags (enclosed in square brackets), and appending a full
+# relative path to them
+POTFILES: POTFILES.in
+       ( if test 'x$(srcdir)' != 'x.'; then \
+           posrcprefix='$(top_srcdir)/'; \
+         else \
+           posrcprefix="../"; \
+         fi; \
+         rm -f $@-t $@ \
+           && (sed -e '/^#/d'                                          \
+                   -e "s/^\[.*\] +//"                                  \
+                   -e '/^[     ]*$$/d'                                 \
+                   -e "s@.*@   $$posrcprefix& \\\\@" < $(srcdir)/$@.in \
+               | sed -e '$$s/\\$$//') > $@-t \
+           && chmod a-w $@-t \
+           && mv $@-t $@ )
+
+Makefile: Makefile.in.in ../config.status POTFILES
+       cd .. \
+         && CONFIG_FILES=$(subdir)/$@.in CONFIG_HEADERS= \
+              $(SHELL) ./config.status
+
+# Tell versions [3.59,3.63) of GNU make not to export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/po/POTFILES.in b/po/POTFILES.in
new file mode 100644 (file)
index 0000000..cd4a84f
--- /dev/null
@@ -0,0 +1,5 @@
+# List of source files containing translatable strings.
+
+src/gpicview.cpp
+src/mainwin.cpp
+
diff --git a/po/gpicview.pot b/po/gpicview.pot
new file mode 100644 (file)
index 0000000..f3c90a1
--- /dev/null
@@ -0,0 +1,127 @@
+# 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: 2007-09-09 23:03+0800\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"
+
+#: src/mainwin.cpp:100
+msgid "Image Viewer"
+msgstr ""
+
+#: src/mainwin.cpp:171 src/mainwin.cpp:980
+msgid "Previous"
+msgstr ""
+
+#: src/mainwin.cpp:172 src/mainwin.cpp:981
+msgid "Next"
+msgstr ""
+
+#: src/mainwin.cpp:176 src/mainwin.cpp:985
+msgid "Zoom Out"
+msgstr ""
+
+#: src/mainwin.cpp:177 src/mainwin.cpp:986
+msgid "Zoom In"
+msgstr ""
+
+#: src/mainwin.cpp:178 src/mainwin.cpp:987
+msgid "Fit Image To Window Size"
+msgstr ""
+
+#: src/mainwin.cpp:180 src/mainwin.cpp:989
+msgid "Original Size"
+msgstr ""
+
+#: src/mainwin.cpp:187
+msgid " Full Screen"
+msgstr ""
+
+#: src/mainwin.cpp:191 src/mainwin.cpp:1000
+msgid "Rotate Counterclockwise"
+msgstr ""
+
+#: src/mainwin.cpp:193 src/mainwin.cpp:1002
+msgid "Rotate Clockwise"
+msgstr ""
+
+#: src/mainwin.cpp:197 src/mainwin.cpp:1007
+msgid "Open File"
+msgstr ""
+
+#: src/mainwin.cpp:198 src/mainwin.cpp:1008
+msgid "Save File"
+msgstr ""
+
+#: src/mainwin.cpp:199
+msgid "Save File As"
+msgstr ""
+
+#: src/mainwin.cpp:200 src/mainwin.cpp:1010
+msgid "Delete File"
+msgstr ""
+
+#: src/mainwin.cpp:613
+msgid "All Supported Images"
+msgstr ""
+
+#: src/mainwin.cpp:618
+msgid "All Files"
+msgstr ""
+
+#: src/mainwin.cpp:932
+msgid ""
+"The file name you selected already exist.\n"
+"Do you want to overwrite existing file?\n"
+"(Warning: The quality of original image might be lost)"
+msgstr ""
+
+#: src/mainwin.cpp:961
+msgid ""
+"Are you sure you want to delete current file?\n"
+"\n"
+"Warning: Once deleted, the file cannot be recovered."
+msgstr ""
+
+#: src/mainwin.cpp:996
+msgid "Full Screen"
+msgstr ""
+
+#: src/mainwin.cpp:1009
+msgid "Save As"
+msgstr ""
+
+#: src/mainwin.cpp:1056
+msgid " * Some icons are taken from gimmage"
+msgstr ""
+
+#. TRANSLATORS: Replace this string with your names, one name per line.
+#: src/mainwin.cpp:1060
+msgid "translator-credits"
+msgstr ""
+
+#: src/mainwin.cpp:1065
+msgid "GPicView"
+msgstr ""
+
+#: src/mainwin.cpp:1066
+msgid "Copyright (C) 2007"
+msgstr ""
+
+#: src/mainwin.cpp:1067
+msgid ""
+"Lightweight image viewer\n"
+"\n"
+"Developed by Hon Jen Yee (PCMan)"
+msgstr ""
diff --git a/po/zh_TW.po b/po/zh_TW.po
new file mode 100644 (file)
index 0000000..e3690ad
--- /dev/null
@@ -0,0 +1,141 @@
+# 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.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: 0.1\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-09-09 23:03+0800\n"
+"PO-Revision-Date: 2007-09-09 20:55+0800\n"
+"Last-Translator: 洪任諭 <pcman.tw@gmail.com>\n"
+"Language-Team: zh_TW <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: Chinese\n"
+"X-Poedit-Country: TAIWAN\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+#: src/mainwin.cpp:100
+msgid "Image Viewer"
+msgstr "圖片檢視器"
+
+#: src/mainwin.cpp:171 src/mainwin.cpp:980
+msgid "Previous"
+msgstr "上一張"
+
+#: src/mainwin.cpp:172 src/mainwin.cpp:981
+msgid "Next"
+msgstr "下一張"
+
+#: src/mainwin.cpp:176 src/mainwin.cpp:985
+msgid "Zoom Out"
+msgstr "縮小"
+
+#: src/mainwin.cpp:177 src/mainwin.cpp:986
+msgid "Zoom In"
+msgstr "放大"
+
+#: src/mainwin.cpp:178 src/mainwin.cpp:987
+msgid "Fit Image To Window Size"
+msgstr "符合視窗大小"
+
+#: src/mainwin.cpp:180 src/mainwin.cpp:989
+msgid "Original Size"
+msgstr "原始大小"
+
+#: src/mainwin.cpp:187
+msgid " Full Screen"
+msgstr "全螢幕瀏覽"
+
+#: src/mainwin.cpp:191 src/mainwin.cpp:1000
+msgid "Rotate Counterclockwise"
+msgstr "逆時針旋轉"
+
+#: src/mainwin.cpp:193 src/mainwin.cpp:1002
+msgid "Rotate Clockwise"
+msgstr "順時針旋轉"
+
+#: src/mainwin.cpp:197 src/mainwin.cpp:1007
+msgid "Open File"
+msgstr "開啟檔案"
+
+#: src/mainwin.cpp:198 src/mainwin.cpp:1008
+msgid "Save File"
+msgstr "儲存檔案"
+
+#: src/mainwin.cpp:199
+msgid "Save File As"
+msgstr "另存新檔"
+
+#: src/mainwin.cpp:200 src/mainwin.cpp:1010
+msgid "Delete File"
+msgstr "刪除檔案"
+
+#: src/mainwin.cpp:613
+msgid "All Supported Images"
+msgstr "所有支援的影像格式"
+
+#: src/mainwin.cpp:618
+msgid "All Files"
+msgstr "所有檔案"
+
+#: src/mainwin.cpp:932
+msgid ""
+"The file name you selected already exist.\n"
+"Do you want to overwrite existing file?\n"
+"(Warning: The quality of original image might be lost)"
+msgstr ""
+"你選擇的檔案名稱已經存在\n"
+"是否確定要覆蓋原有檔案?\n"
+"(警告:原影像的品質可能會喪失)"
+
+#: src/mainwin.cpp:961
+msgid ""
+"Are you sure you want to delete current file?\n"
+"\n"
+"Warning: Once deleted, the file cannot be recovered."
+msgstr ""
+"你是否確定要刪除目前檔案?\n"
+"\n"
+"警告:一旦刪除,檔案就無法復原。"
+
+#: src/mainwin.cpp:996
+msgid "Full Screen"
+msgstr "全螢幕瀏覽"
+
+#: src/mainwin.cpp:1009
+msgid "Save As"
+msgstr "另存新檔"
+
+#: src/mainwin.cpp:1056
+msgid " * Some icons are taken from gimmage"
+msgstr " * 部份圖示是從 gimmage 取得"
+
+#. TRANSLATORS: Replace this string with your names, one name per line.
+#: src/mainwin.cpp:1060
+msgid "translator-credits"
+msgstr "洪任諭 (PCMan) <pcman.tw@gmail.com>"
+
+#: src/mainwin.cpp:1065
+msgid "GPicView"
+msgstr "GPicView 圖片檢視器"
+
+#: src/mainwin.cpp:1066
+msgid "Copyright (C) 2007"
+msgstr "版權所有 (C) 2007"
+
+#: src/mainwin.cpp:1067
+msgid ""
+"Lightweight image viewer\n"
+"\n"
+"Developed by Hon Jen Yee (PCMan)"
+msgstr ""
+"輕巧的圖片檢視器\n"
+"\n"
+"由洪任諭 (PCMan) 開發"
+
+#~ msgid " Original Size"
+#~ msgstr "原始尺寸"
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644 (file)
index 0000000..c0ef25b
--- /dev/null
@@ -0,0 +1,27 @@
+## Process this file with automake to produce Makefile.in
+
+# set the include path found by configure
+INCLUDES = $(all_includes) \
+       @PACKAGE_CFLAGS@ \
+       -I$(top_srcdir) \
+       -DPACKAGE_DATA_DIR=\""$(datadir)"\" \
+       -DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\"
+
+bin_PROGRAMS = gpicview
+gpicview_SOURCES = \
+       gpicview.cpp \
+       imagelist.cpp \
+       mainwin.cpp \
+       working-area.c
+
+# the library search path.
+# use -nodefaultlibs to prevent linking libstdc++
+gpicview_LDFLAGS = $(all_libraries) -nodefaultlibs -lc
+gpicview_LDADD = @PACKAGE_LIBS@
+gpicview_CXXFLAGS = -fno-rtti -fno-exceptions
+
+noinst_HEADERS = \
+       imagelist.h \
+       mainwin.h \
+       working-area.h
+
diff --git a/src/gpicview.cpp b/src/gpicview.cpp
new file mode 100644 (file)
index 0000000..795f0aa
--- /dev/null
@@ -0,0 +1,99 @@
+/***************************************************************************
+ *   Copyright (C) 2007 by PCMan (Hong Jen Yee)   *
+ *   pcman.tw@gmail.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.             *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include "mainwin.h"
+
+using namespace std;
+
+/*
+static GOptionEntry entries[] = 
+{
+    { NULL }
+};
+*/
+
+#define PIXMAP_DIR             PACKAGE_DATA_DIR "/gpicview/pixmaps/"
+
+void register_icons()
+{
+    GtkIconFactory* factory = gtk_icon_factory_new();
+    GdkPixbuf* pix = NULL;
+    GtkIconSet* set = NULL;
+
+#ifndef GTK_STOCK_FULLSCREEN    // before gtk+ 2.8
+#define GTK_STOCK_FULLSCREEN    "gtk-fullscreen"
+    pix = gdk_pixbuf_new_from_file( PIXMAP_DIR "fullscreen.png", NULL);
+    set = gtk_icon_set_new_from_pixbuf(pix);
+    gdk_pixbuf_unref( pix );
+    gtk_icon_factory_add( factory, GTK_STOCK_FULLSCREEN, set );
+#endif
+    pix = gdk_pixbuf_new_from_file( PIXMAP_DIR"clockwise.png", NULL);
+    set = gtk_icon_set_new_from_pixbuf(pix);
+    gdk_pixbuf_unref( pix );
+    gtk_icon_factory_add( factory, "gtk-clockwise", set );
+
+    pix = gdk_pixbuf_new_from_file( PIXMAP_DIR"counterclockwise.png", NULL);
+    set = gtk_icon_set_new_from_pixbuf(pix);
+    gdk_pixbuf_unref( pix );
+    gtk_icon_factory_add( factory, "gtk-counterclockwise", set );
+
+    gtk_icon_factory_add_default( factory );
+}
+
+int main(int argc, char *argv[])
+{
+/*
+    GError *error = NULL;
+    GOptionContext *context;
+
+    context = g_option_context_new ("- simple image viewer");
+    g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+    g_option_context_add_group (context, gtk_get_option_group (TRUE));
+    g_option_context_parse (context, &argc, &argv, &error);
+*/
+
+    gtk_init( &argc, &argv );
+
+#ifdef ENABLE_NLS
+    bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
+    bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
+    textdomain ( GETTEXT_PACKAGE );
+#endif
+
+    register_icons();
+
+    MainWin* win = MainWin::create();
+    gtk_widget_show( GTK_WIDGET(win) );
+
+    if( argc > 1 ) {
+        g_debug(argv[1]);
+        win->open( argv[1] );
+    }
+
+    gtk_main();
+    return 0;
+}
diff --git a/src/imagelist.cpp b/src/imagelist.cpp
new file mode 100644 (file)
index 0000000..dfd34c0
--- /dev/null
@@ -0,0 +1,151 @@
+/***************************************************************************
+ *   Copyright (C) 2007 by PCMan (Hong Jen Yee)   *
+ *   pcman.tw@gmail.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.             *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "imagelist.h"
+
+#include <string.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+GSList* ImageList::supported_formats = NULL;
+
+ImageList::ImageList()
+{
+    if( ! supported_formats )
+    {
+        GSList* formats = gdk_pixbuf_get_formats();
+        GSList* format;
+        for( format = formats; format; format = format->next )
+        {
+            char** exts = gdk_pixbuf_format_get_extensions( (GdkPixbufFormat*)format->data );
+            char** ext;
+            for( ext  = exts; *ext ; ++ext )
+                supported_formats = g_slist_prepend( supported_formats, *ext );
+            g_free( exts ); // g_strfreev is not needed since we stole its stirngs
+        }
+        // supported_formats = g_slist_reverse( supported_formats, *ext );
+    }
+}
+
+ImageList::~ImageList()
+{
+}
+
+bool ImageList::open_dir( const char* path, GError** error )
+{
+    if( dir_path && 0 == strcmp( path, dir_path ) )
+        return true;
+
+    close();
+
+    GDir* dir = g_dir_open( path, 0, error );
+    if( ! dir )
+        return false;
+
+    dir_path = g_strdup( path );
+
+    const char* name = NULL;
+    while( name = g_dir_read_name ( dir ) )
+    {
+//        char* file_path = g_build_filename( dir_path, name, NULL );
+        if( is_file_supported( name ) )
+            list = g_list_prepend( list, g_strdup(name) );
+//        g_free( file_path );
+    }
+    g_dir_close( dir );
+    list = g_list_reverse(list);
+    current = list;
+    return true;
+}
+
+bool ImageList::set_current( const char* name )
+{
+    if( ! list || !name )
+        return false;
+
+    GList* cur = g_list_find_custom( list, name, (GCompareFunc)strcmp );
+    if( ! cur )
+        return false;
+    current = cur;
+    return true;
+}
+
+const char* ImageList::get_first()
+{
+    current = list;
+    return get_current();
+}
+
+const char* ImageList::get_next()
+{
+    if( current && current->next )
+    {
+        current = current->next;
+        return get_current();
+    }
+    return NULL;
+}
+
+const char* ImageList::get_prev()
+{
+    if( current && current->prev )
+    {
+        current = current->prev;
+        return get_current();
+    }
+    return NULL;
+}
+
+const char* ImageList::get_last()
+{
+    current = g_list_last( list );
+    return get_current();
+}
+
+void ImageList::close()
+{
+    g_list_foreach( list, (GFunc)g_free, NULL );
+    g_list_free( list );
+    list = NULL;
+
+    g_free( dir_path );
+    dir_path = NULL;
+}
+
+bool ImageList::is_file_supported( const char* name )
+{
+    const char* ext = strrchr( name, '.' );
+    if( ! ext )
+        return false;
+    ++ext;
+
+    return !!g_slist_find_custom ( supported_formats, ext,  (GCompareFunc)g_ascii_strcasecmp);
+}
+
+char* ImageList::get_current_file_path()  const
+{
+    const char* name;
+    if( dir_path && (name = get_current()) )
+        return g_build_filename( dir_path, name, NULL );
+    return NULL;
+}
diff --git a/src/imagelist.h b/src/imagelist.h
new file mode 100644 (file)
index 0000000..9d00ef0
--- /dev/null
@@ -0,0 +1,55 @@
+/***************************************************************************
+ *   Copyright (C) 2007 by PCMan (Hong Jen Yee)   *
+ *   pcman.tw@gmail.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 IMAGELIST_H
+#define IMAGELIST_H
+
+#include <glib.h>
+
+/**
+       @author PCMan (Hong Jen Yee) <pcman.tw@gmail.com>
+*/
+class ImageList{
+public:
+    ImageList();
+    ~ImageList();
+
+    const char* get_dir() const { return dir_path; }
+    bool open_dir( const char* path, GError** error = NULL  );
+    bool set_current( const char* name );
+    const char* get_current() const { return current ? (char*)current->data : NULL; }
+    const char* get_first();
+    const char* get_next();
+    const char* get_prev();
+    const char* get_last();
+    void close();
+//    static bool is_file_supported( char* file_path );
+    static bool is_file_supported( const char* name );
+    bool is_empty() const { return (list == NULL); }
+    bool has_multiple_files() const { return (list && list->next); }
+    char* get_current_file_path()  const;
+
+protected:
+    gchar* dir_path;
+    GList* list;
+    GList* current;
+    static GSList* supported_formats;
+};
+
+#endif
diff --git a/src/mainwin.cpp b/src/mainwin.cpp
new file mode 100644 (file)
index 0000000..2f6e62a
--- /dev/null
@@ -0,0 +1,1104 @@
+/***************************************************************************
+ *   Copyright (C) 2007 by PCMan (Hong Jen Yee)   *
+ *   pcman.tw@gmail.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.             *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "mainwin.h"
+#include <new>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <string.h>
+#include <errno.h>
+
+#include "working-area.h"
+
+gpointer MainWin::_parent_class = NULL;
+
+// For drag & drop
+GtkTargetEntry drop_targets[] = 
+{
+    {"text/uri-list", 0, 0},
+    {"text/plain", 0, 1}  
+};
+
+// Begin of GObject-related stuff
+
+void MainWin::_init( GTypeInstance *instance, gpointer g_class )
+{
+    ::new( instance ) MainWin();    // placement new
+}
+
+GType MainWin::_get_type()
+{
+    static GType g_define_type_id = 0;
+    if (G_UNLIKELY (g_define_type_id == 0))
+    {
+        static const GTypeInfo g_define_type_info = {
+            sizeof (MainWin::Class),
+            (GBaseInitFunc) NULL,
+            (GBaseFinalizeFunc) NULL,
+            (GClassInitFunc) MainWin::_class_init,
+            (GClassFinalizeFunc) NULL,
+            NULL,   /* class_data */
+            sizeof (MainWin),
+            0,      /* n_preallocs */
+            (GInstanceInitFunc) MainWin::_init,
+        };
+        g_define_type_id = g_type_register_static (GTK_TYPE_WINDOW, "MainWin", &g_define_type_info, (GTypeFlags)0);
+    }
+    return g_define_type_id;
+}
+
+void MainWin::_class_init( MainWin::Class* klass )
+{
+    _parent_class = g_type_class_peek_parent (klass);
+
+    GObjectClass * obj_class;
+    GtkWidgetClass *widget_class;
+
+    obj_class = ( GObjectClass * ) klass;
+//    obj_class->set_property = _set_property;
+//   obj_class->get_property = _get_property;
+    obj_class->finalize = _finalize;
+
+    widget_class = GTK_WIDGET_CLASS ( klass );
+    widget_class->delete_event = on_delete_event;
+    widget_class->size_allocate = on_size_allocate;
+    widget_class->key_press_event = on_key_press_event;
+}
+
+void MainWin::_finalize(GObject *self)
+{
+    ((MainWin*)self)->~MainWin();
+}
+
+// End of GObject-related stuff
+
+MainWin::MainWin()
+{
+    gtk_window_set_title( (GtkWindow*)this, _("Image Viewer"));
+    gtk_window_set_icon_from_file( (GtkWindow*)this, PACKAGE_DATA_DIR"/pixmaps/gpicview.png", NULL );
+    gtk_window_set_default_size( (GtkWindow*)this, 640, 480 );
+
+    GtkWidget* box = gtk_vbox_new( FALSE, 0 );
+    gtk_container_add( (GtkContainer*)this, box);
+
+    // image area
+    evt_box = gtk_event_box_new();
+    gtk_widget_add_events( evt_box,
+                           GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK|
+                           GDK_BUTTON_RELEASE_MASK|GDK_SCROLL_MASK );
+    g_signal_connect( evt_box, "button-press-event", G_CALLBACK(on_button_press), this );
+    g_signal_connect( evt_box, "button-release-event", G_CALLBACK(on_button_release), this );
+    g_signal_connect( evt_box, "motion-notify-event", G_CALLBACK(on_mouse_move), this );
+
+    img_view = gtk_image_new();
+    gtk_container_add( (GtkContainer*)evt_box, img_view);
+
+    scroll = gtk_scrolled_window_new( NULL, NULL );
+    gtk_scrolled_window_set_shadow_type( (GtkScrolledWindow*)scroll, GTK_SHADOW_NONE );
+    gtk_scrolled_window_set_policy((GtkScrolledWindow*)scroll,
+                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+    GtkAdjustment *hadj, *vadj;
+    hadj = gtk_scrolled_window_get_hadjustment((GtkScrolledWindow*)scroll);
+    hadj->page_increment = 10;
+    gtk_adjustment_changed(hadj);
+//    g_object_set( hadj, "page-increment", 10, NULL );
+    vadj = gtk_scrolled_window_get_vadjustment((GtkScrolledWindow*)scroll);
+    vadj->page_increment = 10;
+    gtk_adjustment_changed(vadj);
+//    g_object_set( vadj, "page-increment", 10, NULL );
+    gtk_scrolled_window_add_with_viewport( (GtkScrolledWindow*)scroll, evt_box );
+
+    gtk_box_pack_start( (GtkBox*)box, scroll, TRUE, TRUE, 0 );
+
+    // build toolbar
+    tooltips = gtk_tooltips_new();
+#if GTK_CHECK_VERSION( 2, 10, 0 )
+    g_object_ref_sink( tooltips );
+#else
+    gtk_object_sink( tooltips );
+#endif
+    create_nav_bar( box );
+
+    gtk_widget_show_all( box );
+
+    hand_cursor = gdk_cursor_new_for_display( gtk_widget_get_display((GtkWidget*)this), GDK_FLEUR );
+
+    zoom_mode = ZOOM_FIT;
+
+    // Set up drag & drop
+    gtk_drag_dest_set( (GtkWidget*)this, GTK_DEST_DEFAULT_ALL, 
+                                                    drop_targets, G_N_ELEMENTS(drop_targets), GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_ASK) );
+    g_signal_connect( this, "drag-data-received", G_CALLBACK(on_drag_data_received), this );
+}
+
+MainWin::~MainWin()
+{
+    close();
+    gdk_cursor_unref( hand_cursor );
+    g_object_unref( tooltips );
+
+    // FIXME: Put this here is weird
+    gtk_main_quit();
+}
+
+void MainWin::create_nav_bar( GtkWidget* box )
+{
+    nav_bar = gtk_hbox_new( FALSE, 0 );
+
+    add_nav_btn( GTK_STOCK_GO_BACK, _("Previous"), G_CALLBACK(on_prev) );
+    add_nav_btn( GTK_STOCK_GO_FORWARD, _("Next"), G_CALLBACK(on_next) );
+
+    gtk_box_pack_start( (GtkBox*)nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
+
+    add_nav_btn( GTK_STOCK_ZOOM_OUT, _("Zoom Out"), G_CALLBACK(on_zoom_out) );
+    add_nav_btn( GTK_STOCK_ZOOM_IN, _("Zoom In"), G_CALLBACK(on_zoom_in) );
+    btn_fit = add_nav_btn( GTK_STOCK_ZOOM_FIT, _("Fit Image To Window Size"),
+                           G_CALLBACK(on_zoom_fit), true );
+    btn_orig = add_nav_btn( GTK_STOCK_ZOOM_100, _("Original Size"),
+                           G_CALLBACK(on_orig_size), true );
+
+    gtk_toggle_button_set_active( (GtkToggleButton*)btn_fit, TRUE );
+#ifndef GTK_STOCK_FULLSCREEN
+#define GTK_STOCK_FULLSCREEN    "gtk-fullscreen"
+#endif
+    add_nav_btn( GTK_STOCK_FULLSCREEN, _(" Full Screen"), G_CALLBACK(on_full_screen) );   // gtk+ 2.8+
+
+    gtk_box_pack_start( (GtkBox*)nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
+
+    add_nav_btn( "gtk-counterclockwise", _("Rotate Counterclockwise"),
+                 G_CALLBACK(on_rotate_counterclockwise) );
+    add_nav_btn( "gtk-clockwise", _("Rotate Clockwise"), G_CALLBACK(on_rotate_clockwise) );
+
+    gtk_box_pack_start( (GtkBox*)nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
+
+    add_nav_btn( GTK_STOCK_OPEN, _("Open File"), G_CALLBACK(on_open) );
+    add_nav_btn( GTK_STOCK_SAVE, _("Save File"), G_CALLBACK(on_save) );
+    add_nav_btn( GTK_STOCK_SAVE_AS, _("Save File As"), G_CALLBACK(on_save_as) );
+    add_nav_btn( GTK_STOCK_DELETE, _("Delete File"), G_CALLBACK(on_delete) );
+
+/*
+    gtk_box_pack_start( (GtkBox*)nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
+
+    add_nav_btn( GTK_STOCK_PREFERENCES, _("Preference"), G_CALLBACK(on_preference) );
+*/
+
+    GtkWidget* align = gtk_alignment_new( 0.5, 0, 0, 0 );
+    gtk_container_add( (GtkContainer*)align, nav_bar );
+    gtk_box_pack_start( (GtkBox*)box, align, FALSE, TRUE, 2 );
+}
+
+gboolean MainWin::on_delete_event( GtkWidget* widget, GdkEventAny* evt )
+{
+    gtk_widget_destroy( widget );
+    return TRUE;
+}
+
+bool MainWin::open( const char* file_path )
+{
+    close();
+    GError* err = NULL;
+    pic_orig = gdk_pixbuf_new_from_file( file_path, &err );
+    if( ! pic_orig )
+    {
+        show_error( err->message );
+        return false;
+    }
+
+    if( zoom_mode == ZOOM_FIT )
+        fit_window_size();
+    else  if( zoom_mode == ZOOM_OTHER )  // scale
+        scale_image( scale );
+    else  if( zoom_mode == ZOOM_ORIG )  // original size
+    {
+        pic = gdk_pixbuf_ref( pic_orig );
+        gtk_image_set_from_pixbuf( (GtkImage*)img_view, pic );
+        int w = gdk_pixbuf_get_width( pic );
+        int h = gdk_pixbuf_get_height( pic );
+
+/*
+        GdkRectangle area;
+        get_working_area( gtk_widget_get_screen((GtkWidget*)this), &area );
+        if( w < area.width && h < area.height )
+        {
+            gtk_scrolled_window_set_policy( (GtkScrolledWindow*)scroll,
+                                             GTK_POLICY_NEVER, GTK_POLICY_NEVER );
+            int space = 0;
+            gtk_widget_style_get( scroll, "scrollbar-spacing", &space, NULL );
+            space *= 2;
+
+            gtk_window_resize( (GtkWindow*)this, w, h );
+            gtk_scrolled_window_set_policy( (GtkScrolledWindow*)scroll,
+                                             GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
+        }
+        else
+        {
+            gtk_scrolled_window_set_policy( (GtkScrolledWindow*)scroll,
+                                             GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
+            center_image();
+        }
+*/
+        center_image();
+    }
+
+//    while (gtk_events_pending ())
+//        gtk_main_iteration ();
+
+    // build file list
+    char* dir_path = g_path_get_dirname( file_path );
+    img_list.open_dir( dir_path );
+    g_free( dir_path );
+    char* base_name = g_path_get_basename( file_path );
+    img_list.set_current( base_name );
+    g_free( base_name );
+
+    char* disp_path = g_filename_display_name( file_path );
+    gtk_window_set_title( (GtkWindow*)this, disp_path );
+    g_free( disp_path );
+
+    return true;
+}
+
+void MainWin::close()
+{
+    if( pic ) {
+        gdk_pixbuf_unref( pic );
+        pic = NULL;
+    }
+    if( pic_orig ) {
+        gdk_pixbuf_unref( pic_orig );
+        pic_orig = NULL;
+    }
+}
+
+void MainWin::show_error( const char* message )
+{
+    GtkWidget* dlg = gtk_message_dialog_new( (GtkWindow*)this,
+                                              GTK_DIALOG_MODAL,
+                                              GTK_MESSAGE_ERROR,
+                                              GTK_BUTTONS_OK,
+                                              message );
+    gtk_dialog_run( (GtkDialog*)dlg );
+    gtk_widget_destroy( dlg );
+}
+
+void MainWin::on_size_allocate( GtkWidget* widget, GtkAllocation    *allocation )
+{
+    GTK_WIDGET_CLASS(_parent_class)->size_allocate( widget, allocation );
+    MainWin* self = (MainWin*)widget;
+    if( self->zoom_mode == ZOOM_FIT )
+    {
+        while(gtk_events_pending ())
+            gtk_main_iteration(); // makes it more fluid
+
+        allocation = &self->scroll->allocation;
+        self->fit_window_size();
+    }
+}
+
+void MainWin::fit_size( int width, int height, GdkInterpType type )
+{
+    if( ! pic_orig )
+        return;
+
+    if( pic ){
+        gdk_pixbuf_unref( pic );
+        pic = NULL;
+    }
+
+    int orig_w = gdk_pixbuf_get_width(pic_orig);
+    int orig_h = gdk_pixbuf_get_height(pic_orig);
+
+    double xscale = double(width) / orig_w;
+    double yscale = double(height) / orig_h;
+    double final_scale = xscale < yscale ? xscale : yscale;
+
+    scale_image( final_scale, type );
+}
+
+
+void MainWin::fit_window_size(  GdkInterpType type )
+{
+    int space = 0;
+    gtk_widget_style_get( scroll, "scrollbar-spacing", &space, NULL );
+    space *= 2;
+    fit_size( scroll->allocation.width - space, scroll->allocation.height - space, type );
+}
+
+GtkWidget* MainWin::add_nav_btn( const char* icon, const char* tip, GCallback cb, bool toggle )
+{
+    GtkWidget* img = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_SMALL_TOOLBAR);
+    GtkWidget* btn;
+    if( G_UNLIKELY(toggle) )
+    {
+        btn = gtk_toggle_button_new();
+        g_signal_connect( btn, "toggled", cb, this );
+    }
+    else
+    {
+        btn = gtk_button_new();
+        g_signal_connect( btn, "clicked", cb, this );
+    }
+    gtk_button_set_relief( (GtkButton*)btn, GTK_RELIEF_NONE );
+    gtk_button_set_focus_on_click( (GtkButton*)btn, FALSE );
+    gtk_container_add( (GtkContainer*)btn, img );
+    gtk_tooltips_set_tip( tooltips, btn, tip, NULL );
+    gtk_box_pack_start( (GtkBox*)nav_bar, btn, FALSE, FALSE, 0 );
+    return btn;
+}
+
+void MainWin::on_zoom_fit_menu( GtkMenuItem* item, MainWin* self )
+{
+    gtk_button_clicked( (GtkButton*)self->btn_fit );
+}
+
+void MainWin::on_zoom_fit( GtkToggleButton* btn, MainWin* self )
+{
+    if( ! btn->active )
+    {
+        if( self->zoom_mode == ZOOM_FIT )
+            gtk_toggle_button_set_active( btn, TRUE );
+        return;
+    }
+    self->zoom_mode = ZOOM_FIT;
+
+    gtk_toggle_button_set_active( (GtkToggleButton*)self->btn_orig, FALSE );
+
+    self->fit_window_size();
+}
+
+void MainWin::on_full_screen( GtkWidget* btn, MainWin* self )
+{
+    if( ! self->full_screen )
+    {
+        gtk_widget_hide( gtk_widget_get_parent(self->nav_bar) );
+        gtk_window_fullscreen( (GtkWindow*)self );
+    }
+    else
+    {
+        gtk_widget_show( gtk_widget_get_parent(self->nav_bar) );
+        gtk_window_unfullscreen( (GtkWindow*)self );
+    }
+    self->full_screen = ! self->full_screen;
+}
+
+void MainWin::on_orig_size_menu( GtkToggleButton* btn, MainWin* self )
+{
+    gtk_button_clicked( (GtkButton*)self->btn_orig );
+}
+
+void MainWin::on_orig_size( GtkToggleButton* btn, MainWin* self )
+{
+    // This callback could be called from activate signal of menu item.
+    if( GTK_IS_MENU_ITEM(btn) )
+    {
+        gtk_button_clicked( (GtkButton*)self->btn_orig );
+        return;
+    }
+
+    if( ! btn->active )
+    {
+        if( self->zoom_mode == ZOOM_ORIG )
+            gtk_toggle_button_set_active( btn, TRUE );
+        return;
+    }
+    self->zoom_mode = ZOOM_ORIG;
+    self->scale = 1.0;
+
+    gtk_toggle_button_set_active( (GtkToggleButton*)self->btn_fit, FALSE );
+
+    if( ! self->pic_orig )
+        return;
+
+    if( self->pic )
+        gdk_pixbuf_unref( self->pic );
+    self->pic = gdk_pixbuf_ref( self->pic_orig );
+    gtk_image_set_from_pixbuf( (GtkImage*)self->img_view, self->pic );
+
+//    self->center_image(); // FIXME:  This doesn't work well. Why?
+}
+
+void MainWin::on_prev( GtkWidget* btn, MainWin* self )
+{
+    if( self->img_list.is_empty() )
+        return;
+
+    const char* name = self->img_list.get_prev();
+
+    if( ! name && self->img_list.has_multiple_files() )
+    {
+        // FIXME: need to ask user first?
+        name = self->img_list.get_last();
+    }
+
+    if( name )
+    {
+        char* file_path = self->img_list.get_current_file_path();
+        self->open( file_path );
+        g_free( file_path );
+    }
+}
+
+void MainWin::on_next( GtkWidget* btn, MainWin* self )
+{
+    if( self->img_list.is_empty() )
+        return;
+
+    const char* name = self->img_list.get_next();
+
+    if( ! name && self->img_list.has_multiple_files() )
+    {
+        // FIXME: need to ask user first?
+        name = self->img_list.get_first();
+    }
+
+    if( name )
+    {
+        char* file_path = self->img_list.get_current_file_path();
+        self->open( file_path );
+        g_free( file_path );
+    }
+}
+
+void MainWin::on_rotate_clockwise( GtkWidget* btn, MainWin* self )
+{
+    self->rotate_image( GDK_PIXBUF_ROTATE_CLOCKWISE );
+}
+
+void MainWin::on_rotate_counterclockwise( GtkWidget* btn, MainWin* self )
+{
+    self->rotate_image( GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE );
+}
+
+static void on_update_preview( GtkFileChooser *chooser, GtkImage* img )
+{
+    char* file = gtk_file_chooser_get_preview_filename( chooser );
+    GdkPixbuf* pix = NULL;
+    if( file )
+    {
+        pix = gdk_pixbuf_new_from_file_at_scale( file, 128, 128, TRUE, NULL );
+        g_free( file );
+    }
+    gtk_image_set_from_pixbuf( img, pix );
+    if( pix )
+        gdk_pixbuf_unref( pix );
+}
+
+void MainWin::on_save_as( GtkWidget* btn, MainWin* self )
+{
+    if( ! self->pic_orig )
+        return;
+
+    GtkFileChooser* dlg = (GtkFileChooser*)gtk_file_chooser_dialog_new( NULL, (GtkWindow*)self,
+            GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL,
+            GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_OK, NULL );
+
+    gtk_file_chooser_set_current_folder( dlg, self->img_list.get_dir() );
+
+    GtkWidget* img = gtk_image_new();
+    gtk_widget_set_size_request( img, 128, 128 );
+    gtk_file_chooser_set_preview_widget( dlg, img );
+    g_signal_connect( dlg, "update-preview", G_CALLBACK(on_update_preview), img );
+
+    GtkFileFilter *filter;
+
+    /*
+    /// TODO: determine file type from file name
+    filter = gtk_file_filter_new();
+    gtk_file_filter_set_name( filter, _("Determined by File Name") );
+    gtk_file_filter_add_pixbuf_formats( filter );
+    gtk_file_chooser_add_filter( dlg, filter );
+    */
+
+    GSList* modules = gdk_pixbuf_get_formats();
+    GSList* module;
+    for( module = modules; module; module = module->next )
+    {
+        GdkPixbufFormat* format = (GdkPixbufFormat*)module->data;
+        if( ! gdk_pixbuf_format_is_writable( format ) )
+            continue;
+
+        filter = gtk_file_filter_new();
+
+        char* desc = gdk_pixbuf_format_get_description( format );
+        char* name = gdk_pixbuf_format_get_name( format );
+        char* tmp = g_strjoin( ":  ", name, desc, NULL );
+        g_free( desc );
+        g_free( name );
+        gtk_file_filter_set_name( filter, tmp );
+        g_free( tmp );
+
+        char** mimes = gdk_pixbuf_format_get_mime_types( format ), **mime;
+        for( mime  = mimes; *mime ; ++mime )
+            gtk_file_filter_add_mime_type( filter, *mime );
+        g_strfreev( mimes );
+        gtk_file_chooser_add_filter( dlg, filter );
+    }
+
+    if( gtk_dialog_run( (GtkDialog*)dlg ) == GTK_RESPONSE_OK )
+    {
+        filter = gtk_file_chooser_get_filter( dlg );
+        const char* filter_name = gtk_file_filter_get_name( filter );
+        char* p = strstr( filter_name, ": " );
+        char* type = NULL;
+        if( ! p )   // auto detection
+        {
+            /// TODO: auto file type
+        }
+        else
+        {
+            type = g_strndup( filter_name, (p - filter_name)  );
+        }
+        char* file = gtk_file_chooser_get_filename( dlg );
+        // g_debug("type = %s", type);
+        self->save( file, type );
+        g_free( file );
+        g_free( type );
+    }
+    gtk_widget_destroy( (GtkWidget*)dlg );
+}
+
+void MainWin::on_save( GtkWidget* btn, MainWin* self )
+{
+    if( ! self->pic_orig )
+        return;
+
+    char* file_name = g_build_filename( self->img_list.get_dir(),
+                                        self->img_list.get_current(), NULL );
+    GdkPixbufFormat* info;
+    info = gdk_pixbuf_get_file_info( file_name, NULL, NULL );
+    char* type = gdk_pixbuf_format_get_name( info );
+    self->save( file_name, type, true );
+    g_free( file_name );
+    g_free( type );
+}
+
+void MainWin::on_open( GtkWidget* btn, MainWin* self )
+{
+    GtkFileChooser* dlg = (GtkFileChooser*)gtk_file_chooser_dialog_new( NULL, (GtkWindow*)self,
+            GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL,
+            GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL );
+
+    if( self->img_list.get_dir() )
+        gtk_file_chooser_set_current_folder( dlg, self->img_list.get_dir() );
+
+    GtkWidget* img = gtk_image_new();
+    gtk_widget_set_size_request( img, 128, 128 );
+    gtk_file_chooser_set_preview_widget( dlg, img );
+    g_signal_connect( dlg, "update-preview", G_CALLBACK(on_update_preview), img );
+
+    GtkFileFilter *filter = gtk_file_filter_new();
+    gtk_file_filter_set_name( filter, _("All Supported Images") );
+    gtk_file_filter_add_pixbuf_formats( filter );
+    gtk_file_chooser_add_filter( dlg, filter );
+
+    filter = gtk_file_filter_new();
+    gtk_file_filter_set_name( filter, _("All Files") );
+    gtk_file_filter_add_pattern( filter, "*" );
+    gtk_file_chooser_add_filter( dlg, filter );
+
+    char* file = NULL;
+    if( gtk_dialog_run( (GtkDialog*)dlg ) == GTK_RESPONSE_OK )
+        file = gtk_file_chooser_get_filename( dlg );
+    gtk_widget_destroy( (GtkWidget*)dlg );
+
+    if( file )
+    {
+        self->open( file );
+        g_free( file );
+    }
+}
+
+void MainWin::on_zoom_in( GtkWidget* btn, MainWin* self )
+{
+    self->zoom_mode = ZOOM_OTHER;
+    gtk_toggle_button_set_active( (GtkToggleButton*)self->btn_fit, FALSE );
+    gtk_toggle_button_set_active( (GtkToggleButton*)self->btn_orig, FALSE );
+
+    double scale = self->scale;
+    if( self->pic_orig && scale < 3.0 )
+    {
+//        busy(true);
+        (scale > 0.2) ?   scale += scale*0.2 : scale *= 1.5;
+        self->scale_image( scale );
+//        adjust_adjustment_on_zoom(oldscale);
+//        busy(false);
+    }
+}
+
+void MainWin::on_zoom_out( GtkWidget* btn, MainWin* self )
+{
+    self->zoom_mode = ZOOM_OTHER;
+    gtk_toggle_button_set_active( (GtkToggleButton*)self->btn_fit, FALSE );
+    gtk_toggle_button_set_active( (GtkToggleButton*)self->btn_orig, FALSE );
+
+    double scale = self->scale;
+    if( self->pic_orig && scale > 0.05 )
+    {
+//        busy(true);
+        (scale > 0.2) ?   scale -= scale*0.2 : scale *= 0.667;
+        self->scale_image( scale );
+//        adjust_adjustment_on_zoom(oldscale);
+//        busy(false);
+    }
+}
+
+void MainWin::on_preference( GtkWidget* btn, MainWin* self )
+{
+    self->show_error( "Not implemented yet!" );
+}
+
+void MainWin::on_quit( GtkWidget* btn, MainWin* self )
+{
+    gtk_widget_destroy( (GtkWidget*)self );
+}
+
+gboolean MainWin::on_button_press( GtkWidget* widget, GdkEventButton* evt, MainWin* self )
+{
+    if( evt->type == GDK_BUTTON_PRESS)
+    {
+        if( evt->button == 1 )    // left button
+        {
+            if( ! self->pic_orig )
+                return FALSE;
+            self->dragging = true;
+            gtk_widget_get_pointer( (GtkWidget*)self, &self->drag_old_x ,&self->drag_old_y );
+            gdk_window_set_cursor( widget->window, self->hand_cursor );
+        }
+        else if( evt->button == 3 )   // right button
+        {
+            self->show_popup_menu( evt );
+        }
+    }
+    else if( evt->type == GDK_2BUTTON_PRESS && evt->button == 1 )    // double clicked
+    {
+         on_full_screen( NULL, self );
+    }
+    
+    return FALSE;
+}
+
+gboolean MainWin::on_mouse_move( GtkWidget* widget, GdkEventMotion* evt, MainWin* self )
+{
+    if( ! self->dragging )
+    {
+/*
+        if( self->full_screen )
+        {
+            int barh;
+            gtk_widget_get_size_request( self->nav_bar, NULL, &barh );
+            if( evt->y > ( ((GtkWidget*)self)->allocation.height - barh) )
+                gtk_widget_show( self->nav_bar );
+            else
+                gtk_widget_hide( self->nav_bar );
+        }
+*/
+        return FALSE;
+    }
+    int cur_x, cur_y;
+    gtk_widget_get_pointer( (GtkWidget*)self, &cur_x ,&cur_y );
+
+    int dx = (self->drag_old_x - cur_x);
+    int dy = (self->drag_old_y - cur_y);
+
+    GtkAdjustment *hadj, *vadj;
+    hadj = gtk_scrolled_window_get_hadjustment((GtkScrolledWindow*)self->scroll);
+    vadj = gtk_scrolled_window_get_vadjustment((GtkScrolledWindow*)self->scroll);
+
+    int imgw = gdk_pixbuf_get_width( self->pic );
+    int imgh = gdk_pixbuf_get_height( self->pic );
+
+    if( ABS(dx) > 4 )
+    {
+        self->drag_old_x = cur_x;
+        if( imgw > hadj->page_size )
+        {
+            gdouble x = gtk_adjustment_get_value (hadj) + dx;
+            if( x < hadj->lower )
+                x = hadj->lower;
+            else if( (x + hadj->page_size) > hadj->upper )
+                x = hadj->upper - hadj->page_size;
+
+            if( x != hadj->value )
+                gtk_adjustment_set_value (hadj, x );
+        }
+    }
+
+    if( ABS(dy) > 4 )
+    {
+        if( imgh > vadj->page_size )
+        {
+            self->drag_old_y = cur_y;
+            gdouble y = gtk_adjustment_get_value (vadj) + dy;
+            if( y < vadj->lower )
+                y = vadj->lower;
+            else if( (y + vadj->page_size) > vadj->upper )
+                y = vadj->upper - vadj->page_size;
+
+            if( y != vadj->value )
+                gtk_adjustment_set_value (vadj, y  );
+        }
+    }
+    return FALSE;
+}
+
+gboolean MainWin::on_button_release( GtkWidget* widget, GdkEventButton* evt, MainWin* self )
+{
+    self->dragging = false;
+    gdk_window_set_cursor( widget->window, NULL );
+    return FALSE;
+}
+
+gboolean MainWin::on_key_press_event(GtkWidget* widget, GdkEventKey * key)
+{
+    MainWin* self = (MainWin*)widget;
+    switch( key->keyval )
+    {
+        case GDK_Return:
+        case GDK_space:
+        case GDK_Next:
+            on_next( NULL, self );
+            break;
+        case GDK_Prior:
+        case GDK_BackSpace:
+            on_prev( NULL, self );
+            break;
+        case GDK_KP_Add:
+        case GDK_plus:
+            on_zoom_in( NULL, self );
+            break;
+        case GDK_KP_Subtract:
+        case GDK_minus:
+            on_zoom_out( NULL, self );
+            break;
+        case GDK_Left:
+        case GDK_KP_Left:
+        case GDK_leftarrow:
+            break;
+        case GDK_Right:
+        case GDK_KP_Right:
+        case GDK_rightarrow:
+            break;
+        case GDK_KP_Down:
+        case GDK_Down:
+        case GDK_downarrow:
+            break;
+        case GDK_KP_Up:
+        case GDK_Up:
+        case GDK_uparrow:
+            break;
+        case GDK_s:
+        case GDK_S:
+            on_save( NULL, self );
+            break;
+        case GDK_l:
+        case GDK_L:
+            on_rotate_counterclockwise( NULL, self );
+            break;
+        case GDK_r:
+        case GDK_R:
+            on_rotate_clockwise( NULL, self );
+            break;
+        case GDK_f:
+        case GDK_F:
+            if( self->zoom_mode != ZOOM_FIT )
+                gtk_button_clicked((GtkButton*)self->btn_fit );
+            break;
+        case GDK_g:
+        case GDK_G:
+            if( self->zoom_mode != ZOOM_ORIG )
+                gtk_button_clicked((GtkButton*)self->btn_orig );
+            break;
+        case GDK_o:
+        case GDK_O:
+//            on_open_file( self->btn_open, self );
+            break;
+        case GDK_Escape:
+            if( self->full_screen )
+                on_full_screen( NULL, self );
+            else
+                on_quit( NULL, self );
+            break;
+        case GDK_F11:
+            on_full_screen( NULL, self );
+            break;
+
+        default:
+            GTK_WIDGET_CLASS(_parent_class)->key_press_event( widget, key );
+    }
+    return FALSE;
+}
+
+void MainWin::center_image()
+{
+    GtkAdjustment *hadj, *vadj;
+    hadj = gtk_scrolled_window_get_hadjustment((GtkScrolledWindow*)scroll);
+    vadj = gtk_scrolled_window_get_vadjustment((GtkScrolledWindow*)scroll);
+
+    int imgw = gdk_pixbuf_get_width( pic );
+    int imgh = gdk_pixbuf_get_height( pic );
+
+    if( imgw > hadj->page_size )
+        gtk_adjustment_set_value(hadj, (imgw - hadj->page_size ) / 2 );
+
+    if( imgh > vadj->page_size )
+        gtk_adjustment_set_value(vadj, (imgh - vadj->page_size ) / 2 );
+}
+
+void MainWin::rotate_image( GdkPixbufRotation angle )
+{
+    if( ! pic_orig )
+        return;
+
+    GdkPixbuf* orig;
+    orig = gdk_pixbuf_rotate_simple( pic_orig, angle );
+    gdk_pixbuf_unref( pic_orig );
+    pic_orig = orig;
+
+    gdk_pixbuf_unref( pic );
+    pic = NULL;
+    if( zoom_mode == ZOOM_FIT )
+        fit_window_size();
+    else    // original size
+    {
+        pic = gdk_pixbuf_ref( pic_orig );
+        gtk_image_set_from_pixbuf( (GtkImage*)img_view, pic );
+        center_image();
+    }
+}
+
+bool MainWin::scale_image( double new_scale, GdkInterpType type )
+{
+    if( G_UNLIKELY( new_scale == 1.0 ) )
+    {
+        gtk_toggle_button_set_active( (GtkToggleButton*)btn_orig, TRUE );
+        return true;
+    }
+
+    int orig_w = gdk_pixbuf_get_width(pic_orig);
+    int orig_h = gdk_pixbuf_get_height(pic_orig);
+
+    int width = int(orig_w * new_scale);
+    int height = int(orig_h * new_scale);
+
+    GdkPixbuf* new_pic = gdk_pixbuf_scale_simple( pic_orig, width, height, type );
+    if( G_LIKELY(new_pic) )
+    {
+        if( pic )
+            gdk_pixbuf_unref( pic );
+        pic = new_pic;
+        gtk_image_set_from_pixbuf( (GtkImage*)img_view, pic );
+        scale = new_scale;
+        return true;
+    }
+    return false;
+}
+
+bool MainWin::save( const char* file_path, const char* type, bool confirm )
+{
+    if( ! pic_orig )
+        return false;
+
+    if( confirm )   // check existing file
+    {
+        if( g_file_test( file_path, G_FILE_TEST_EXISTS ) )
+        {
+            GtkWidget* dlg = gtk_message_dialog_new( (GtkWindow*)this,
+                    GTK_DIALOG_MODAL,
+                    GTK_MESSAGE_QUESTION,
+                    GTK_BUTTONS_YES_NO,
+                    _("The file name you selected already exist.\nDo you want to overwrite existing file?\n(Warning: The quality of original image might be lost)") );
+            if( gtk_dialog_run( (GtkDialog*)dlg ) != GTK_RESPONSE_YES )
+            {
+                gtk_widget_destroy( dlg );
+                return false;
+            }
+            gtk_widget_destroy( dlg );
+        }
+    }
+
+    GError* err = NULL;
+    if( ! gdk_pixbuf_save( pic_orig, file_path, type, &err, NULL ) )
+    {
+        show_error( err->message );
+        return false;
+    }
+
+    return true;
+}
+
+void MainWin::on_delete( GtkWidget* btn, MainWin* self )
+{
+    char* file_path = self->img_list.get_current_file_path();
+    if( file_path )
+    {
+        GtkWidget* dlg = gtk_message_dialog_new( (GtkWindow*)self,
+                GTK_DIALOG_MODAL,
+                GTK_MESSAGE_QUESTION,
+                GTK_BUTTONS_YES_NO,
+                _("Are you sure you want to delete current file?\n\nWarning: Once deleted, the file cannot be recovered.") );
+        int resp = gtk_dialog_run( (GtkDialog*)dlg );
+        gtk_widget_destroy( dlg );
+
+        if( resp == GTK_RESPONSE_YES )
+        {
+            g_unlink( file_path );
+            if( errno )
+                self->show_error( g_strerror(errno) );
+            g_free( file_path );
+        }
+    }
+}
+
+void MainWin::show_popup_menu( GdkEventButton* evt )
+{
+    GtkMenuShell* popup = (GtkMenuShell*)gtk_menu_new();
+
+    GtkWidget *item;
+    add_menu_item( popup, _("Previous"), GTK_STOCK_GO_BACK, G_CALLBACK(on_prev) );
+    add_menu_item( popup, _("Next"), GTK_STOCK_GO_FORWARD, G_CALLBACK(on_next) );
+
+    gtk_menu_shell_append( popup, gtk_separator_menu_item_new() );
+
+    add_menu_item( popup, _("Zoom Out"), GTK_STOCK_ZOOM_OUT, G_CALLBACK(on_zoom_out) );
+    add_menu_item( popup, _("Zoom In"), GTK_STOCK_ZOOM_IN, G_CALLBACK(on_zoom_in) );
+    add_menu_item( popup, _("Fit Image To Window Size"), GTK_STOCK_ZOOM_OUT,
+                   G_CALLBACK(on_zoom_fit_menu) );
+    add_menu_item( popup, _("Original Size"), GTK_STOCK_ZOOM_100,
+                   G_CALLBACK(on_orig_size_menu) );
+
+#ifndef GTK_STOCK_FULLSCREEN
+// This stock item is only available after gtk+ 2.8
+#define GTK_STOCK_FULLSCREEN    "gtk-fullscreen"
+#endif
+    add_menu_item( popup, _("Full Screen"), GTK_STOCK_FULLSCREEN, G_CALLBACK(on_full_screen) );
+
+    gtk_menu_shell_append( popup, gtk_separator_menu_item_new() );
+
+    add_menu_item( popup, _("Rotate Counterclockwise"), "gtk-counterclockwise",
+                   G_CALLBACK(on_rotate_counterclockwise) );
+    add_menu_item( popup, _("Rotate Clockwise"), "gtk-clockwise",
+                   G_CALLBACK(on_rotate_clockwise) );
+
+    gtk_menu_shell_append( popup, gtk_separator_menu_item_new() );
+
+    add_menu_item( popup, _("Open File"), GTK_STOCK_OPEN, G_CALLBACK(on_open) );
+    add_menu_item( popup, _("Save File"), GTK_STOCK_SAVE, G_CALLBACK(on_save) );
+    add_menu_item( popup, _("Save As"), GTK_STOCK_SAVE_AS, G_CALLBACK(on_save_as) );
+    add_menu_item( popup, _("Delete File"), GTK_STOCK_DELETE, G_CALLBACK(on_delete) );
+
+    gtk_menu_shell_append( popup, gtk_separator_menu_item_new() );
+
+    item = gtk_image_menu_item_new_from_stock( GTK_STOCK_ABOUT, NULL );
+    g_signal_connect(item, "activate", G_CALLBACK(on_about), this);
+    gtk_menu_shell_append( popup, item );
+
+//    gtk_toggle_button_set_active( (GtkToggleButton*)btn_fit, TRUE );
+
+    gtk_widget_show_all( (GtkWidget*)popup );
+    g_signal_connect( popup, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
+    gtk_menu_popup( (GtkMenu*)popup, NULL, NULL, NULL, NULL, evt->button, evt->time );
+}
+
+GtkWidget* MainWin::add_menu_item( GtkMenuShell* menu, const char* label,
+                                   const char* icon, GCallback cb, bool toggle )
+{
+    GtkWidget* item;
+    if( G_UNLIKELY(toggle) )
+    {
+        item = gtk_check_menu_item_new_with_mnemonic( label );
+        g_signal_connect( item, "toggled", cb, this );
+    }
+    else
+    {
+        if( icon )
+        {
+            item = gtk_image_menu_item_new_with_mnemonic( label);
+            GtkWidget* img = gtk_image_new_from_stock( icon, GTK_ICON_SIZE_MENU );
+            gtk_image_menu_item_set_image( (GtkImageMenuItem*)item, img );
+        }
+        else {
+            item = gtk_menu_item_new_with_mnemonic( label );
+        }
+        g_signal_connect( item, "activate", cb, this );
+    }
+    gtk_menu_shell_append( (GtkMenuShell*)menu, item );
+}
+
+void MainWin::on_about( GtkWidget* menu, MainWin* self )
+{
+    GtkWidget * about_dlg;
+    const gchar *authors[] =
+    {
+        "洪任諭 Hong Jen Yee <pcman.tw@gmail.com>",
+        _(" * Some icons are taken from gimmage"),
+        NULL
+    };
+    /* TRANSLATORS: Replace this string with your names, one name per line. */
+    gchar *translators = _( "translator-credits" );
+
+    about_dlg = gtk_about_dialog_new ();
+    gtk_container_set_border_width ( GTK_CONTAINER ( about_dlg ), 2 );
+    gtk_about_dialog_set_version ( GTK_ABOUT_DIALOG ( about_dlg ), VERSION );
+    gtk_about_dialog_set_name ( GTK_ABOUT_DIALOG ( about_dlg ), _( "GPicView" ) );
+    gtk_about_dialog_set_copyright ( GTK_ABOUT_DIALOG ( about_dlg ), _( "Copyright (C) 2007" ) );
+    gtk_about_dialog_set_comments ( GTK_ABOUT_DIALOG ( about_dlg ), _( "Lightweight image viewer\n\nDeveloped by Hon Jen Yee (PCMan)" ) );
+    gtk_about_dialog_set_license ( GTK_ABOUT_DIALOG ( about_dlg ), "GPicView\n\nCopyright (C) 2007 Hong Jen Yee (PCMan)\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA." );
+    gtk_about_dialog_set_website ( GTK_ABOUT_DIALOG ( about_dlg ), "http://gpicview.sourceforge.net/" );
+    gtk_about_dialog_set_authors ( GTK_ABOUT_DIALOG ( about_dlg ), authors );
+    gtk_about_dialog_set_translator_credits ( GTK_ABOUT_DIALOG ( about_dlg ), translators );
+    gtk_window_set_transient_for( GTK_WINDOW( about_dlg ), GTK_WINDOW( self ) );
+
+    gtk_dialog_run( GTK_DIALOG( about_dlg ) );
+    gtk_widget_destroy( about_dlg );
+}
+
+void MainWin::on_drag_data_received( GtkWidget* widget, GdkDragContext *drag_context, 
+                int x, int y, GtkSelectionData* data, guint info, guint time, MainWin* self )
+{
+    if( ! data || data->length <= 0)
+        return;
+
+    // g_debug("drag data receved, info = %d", info);
+    char* file = NULL;
+    if( info == 0 )    // text/uri-list
+    {
+        char** uris = gtk_selection_data_get_uris( data );
+        if( uris )
+        {
+            file = g_filename_from_uri(*uris, NULL, NULL);
+            g_strfreev( uris );
+        }
+    }
+    else if( info == 1 )    // text/plain
+    {
+        file = (char*)gtk_selection_data_get_text( data );
+    }
+    if( file )
+    {
+        self->open( file );
+        g_free( file );
+    }
+}
diff --git a/src/mainwin.h b/src/mainwin.h
new file mode 100644 (file)
index 0000000..d1b6562
--- /dev/null
@@ -0,0 +1,125 @@
+/***************************************************************************
+ *   Copyright (C) 2007 by PCMan (Hong Jen Yee)   *
+ *   pcman.tw@gmail.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 MAINWIN_H
+#define MAINWIN_H
+
+#include <gtk/gtk.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "imagelist.h"
+
+/**
+       @author PCMan (Hong Jen Yee) <pcman.tw@gmail.com>
+*/
+
+#define MAIN_WIN_TYPE        (MainWin::_get_type ())
+#define MAIN_WIN(obj)        (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAIN_WIN_TYPE, MainWin))
+#define MAIN_WIN_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), MAIN_WIN_TYPE, MainWin::Class))
+#define IS_MAIN_WIN(obj)     (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAIN_WIN_TYPE))
+#define IS_MAIN_WIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MAIN_WIN_TYPE))
+#define MAIN_WIN_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), MAIN_WIN_TYPE, MainWin::Class))
+
+class MainWin : public GtkWindow
+{
+public:
+    struct Class : public GtkWindowClass
+    {
+    };
+    enum ZoomMode{ ZOOM_FIT = 0, ZOOM_ORIG, ZOOM_OTHER };
+
+    static MainWin* create(){ return (MainWin*)g_object_new ( MAIN_WIN_TYPE, NULL ); }
+
+    static void _init( GTypeInstance *instance, gpointer g_class );
+    static void _class_init( MainWin::Class* klass );
+    static void _finalize(GObject *self);
+    static GType _get_type();
+    bool open( const char* file_path );
+    void close();
+    bool save( const char* file_path, const char* type, bool confirm=true );
+    void show_error( const char* message );
+    void fit_size( int width, int height, GdkInterpType type = GDK_INTERP_BILINEAR );
+    void fit_window_size( GdkInterpType type = GDK_INTERP_BILINEAR );
+    void center_image();
+    bool scale_image( double new_scale, GdkInterpType type = GDK_INTERP_BILINEAR );
+
+protected:
+    MainWin();
+    ~MainWin();
+
+    static gpointer _parent_class;
+    GtkWidget* zoom_btn;
+    GtkWidget* img_view;
+    GtkWidget* scroll;
+    GdkPixbuf* pic_orig;
+    GdkPixbuf* pic;
+    GtkWidget* evt_box;
+    GtkWidget* nav_bar;
+//    GtkWidget* btn_zoom_in;
+//    GtkWidget* btn_zoom_out;
+    GtkWidget* btn_orig;
+    GtkWidget* btn_fit;
+    GtkTooltips* tooltips;
+    GdkCursor* hand_cursor;
+    bool full_screen;
+    ZoomMode zoom_mode;
+
+    bool dragging;
+    double scale;
+    int drag_old_x;
+    int drag_old_y;
+    ImageList img_list;
+
+    void create_nav_bar( GtkWidget* box);
+    static gboolean on_delete_event( GtkWidget* widget, GdkEventAny* evt );
+    static void on_size_allocate( GtkWidget* widget, GtkAllocation    *allocation );
+    GtkWidget* add_nav_btn( const char* icon, const char* tip, GCallback cb, bool toggle = false );
+    GtkWidget* add_menu_item(  GtkMenuShell* menu, const char* label, const char* icon, GCallback cb, bool toggle=false );
+    static void on_zoom_fit( GtkToggleButton* btn, MainWin* self );
+    static void on_zoom_fit_menu( GtkMenuItem* item, MainWin* self );
+    static void on_full_screen( GtkWidget* btn, MainWin* self );
+    static void on_next( GtkWidget* btn, MainWin* self );
+    static void on_orig_size( GtkToggleButton* btn, MainWin* self );
+    static void on_orig_size_menu( GtkToggleButton* btn, MainWin* self );
+    static void on_prev( GtkWidget* btn, MainWin* self );
+    static void on_rotate_clockwise( GtkWidget* btn, MainWin* self );
+    static void on_rotate_counterclockwise( GtkWidget* btn, MainWin* self );
+    static void on_save_as( GtkWidget* btn, MainWin* self );
+    static void on_save( GtkWidget* btn, MainWin* self );
+    static void on_open( GtkWidget* btn, MainWin* self );
+    static void on_zoom_in( GtkWidget* btn, MainWin* self );
+    static void on_zoom_out( GtkWidget* btn, MainWin* self );
+    static void on_preference( GtkWidget* btn, MainWin* self );
+    static void on_quit( GtkWidget* btn, MainWin* self );
+    static gboolean on_button_press( GtkWidget* widget, GdkEventButton* evt, MainWin* self );
+    static gboolean on_button_release( GtkWidget* widget, GdkEventButton* evt, MainWin* self );
+    static gboolean on_mouse_move( GtkWidget* widget, GdkEventMotion* evt, MainWin* self );
+    static gboolean on_key_press_event(GtkWidget* widget, GdkEventKey * key);
+    static void on_drag_data_received( GtkWidget* widget, GdkDragContext *drag_context, 
+                                                                                           int x, int y, GtkSelectionData* data, guint info, 
+                                                                                           guint time, MainWin* self );
+    void rotate_image( GdkPixbufRotation angle );
+
+protected:
+    static void on_delete( GtkWidget* btn, MainWin* self );
+    void show_popup_menu( GdkEventButton* evt );
+    static void on_about( GtkWidget* menu, MainWin* self );
+};
+
+#endif
diff --git a/src/working-area.c b/src/working-area.c
new file mode 100644 (file)
index 0000000..d309558
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Guifications - The end all, be all, toaster popup plugin
+ * Copyright (C) 2003-2004 Gary Kramlich
+ *
+ * This program is free software; you can 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.
+ */
+
+/*
+  2005.07.22 Modified by Hong Jen Yee (PCMan)
+  This piece of code detecting working area is got from Guifications, a plug-in for Gaim.
+*/
+
+# include <gdk/gdk.h>
+# include <gdk/gdkx.h>
+# include <X11/Xlib.h>
+# include <X11/Xutil.h>
+# include <X11/Xatom.h>
+
+gboolean
+gf_display_get_workarea(GdkScreen* g_screen, GdkRectangle *rect) {
+       Atom xa_desktops, xa_current, xa_workarea, xa_type;
+       Display *x_display;
+       Window x_root;
+       guint32 desktops = 0, current = 0;
+       gulong *workareas, len, fill;
+       guchar *data;
+       gint format;
+
+       GdkDisplay *g_display;
+       Screen *x_screen;
+
+       /* get the gdk display */
+       g_display = gdk_display_get_default();
+       if(!g_display)
+               return FALSE;
+
+       /* get the x display from the gdk display */
+       x_display = gdk_x11_display_get_xdisplay(g_display);
+       if(!x_display)
+               return FALSE;
+
+       /* get the x screen from the gdk screen */
+       x_screen = gdk_x11_screen_get_xscreen(g_screen);
+       if(!x_screen)
+               return FALSE;
+
+       /* get the root window from the screen */
+       x_root = XRootWindowOfScreen(x_screen);
+
+       /* find the _NET_NUMBER_OF_DESKTOPS atom */
+       xa_desktops = XInternAtom(x_display, "_NET_NUMBER_OF_DESKTOPS", True);
+       if(xa_desktops == None)
+               return FALSE;
+
+       /* get the number of desktops */
+       if(XGetWindowProperty(x_display, x_root, xa_desktops, 0, 1, False,
+                                                 XA_CARDINAL, &xa_type, &format, &len, &fill,
+                                                 &data) != Success)
+       {
+               return FALSE;
+       }
+
+       if(!data)
+               return FALSE;
+
+       desktops = *(guint32 *)data;
+       XFree(data);
+
+       /* find the _NET_CURRENT_DESKTOP atom */
+       xa_current = XInternAtom(x_display, "_NET_CURRENT_DESKTOP", True);
+       if(xa_current == None)
+               return FALSE;
+
+       /* get the current desktop */
+       if(XGetWindowProperty(x_display, x_root, xa_current, 0, 1, False,
+                                                 XA_CARDINAL, &xa_type, &format, &len, &fill,
+                                                 &data) != Success)
+       {
+               return FALSE;
+       }
+
+       if(!data)
+               return FALSE;
+
+       current = *(guint32 *)data;
+       XFree(data);
+
+       /* find the _NET_WORKAREA atom */
+       xa_workarea = XInternAtom(x_display, "_NET_WORKAREA", True);
+       if(xa_workarea == None)
+               return FALSE;
+
+       if(XGetWindowProperty(x_display, x_root, xa_workarea, 0, (glong)(4 * 32),
+                                                 False, AnyPropertyType, &xa_type, &format, &len,
+                                                 &fill, &data) != Success)
+       {
+               return FALSE;
+       }
+
+       /* make sure the type and format are good */
+       if(xa_type == None || format == 0)
+               return FALSE;
+
+       /* make sure we don't have any leftovers */
+       if(fill)
+               return FALSE;
+
+       /* make sure len divides evenly by 4 */
+       if(len % 4)
+               return FALSE;
+
+       /* it's good, lets use it */
+       workareas = (gulong *)(guint32 *)data;
+
+       rect->x = (guint32)workareas[current * 4];
+       rect->y = (guint32)workareas[current * 4 + 1];
+       rect->width = (guint32)workareas[current * 4 + 2];
+       rect->height = (guint32)workareas[current * 4 + 3];
+
+       /* clean up our memory */
+       XFree(data);
+
+       return TRUE;
+}
+
+void get_working_area( GdkScreen* screen, GdkRectangle* area )
+{
+       if( !gf_display_get_workarea(screen, area) )
+       {
+               area->x = 0;
+               area->y = 0;
+               area->width = gdk_screen_width();
+               area->height = gdk_screen_height();
+       }
+}
diff --git a/src/working-area.h b/src/working-area.h
new file mode 100644 (file)
index 0000000..9d1bdfc
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef WORKING_AREA_H
+#define WORKING_AREA_H
+
+#include <gdk/gdk.h>
+void get_working_area(GdkScreen* screen, GdkRectangle *rect);
+
+#endif
+