[Pkg-wmaker-commits] [wmpinboard] 01/30: initial commit
Doug Torrance
dtorrance-guest at moszumanska.debian.org
Wed Jan 27 23:31:36 UTC 2016
This is an automated email from the git hooks/post-receive script.
dtorrance-guest pushed a commit to branch master
in repository wmpinboard.
commit 8c6d76d5b1853411d07c64aadf1c4495555e84f6
Author: Brian Bidulock <bidulock at openss7.org>
Date: Sun May 24 18:19:53 2015 -0600
initial commit
- this is wmpinboard-1.0 that used to be on dockapps.org and is
no longer. I imported it to git and github from the tarball
that I had laying around.
---
.gitignore | 32 +
AUTHORS | 8 +
COPYING | 340 +++++++
CREDITS | 9 +
INSTALL | 182 ++++
Makefile.am | 31 +
NEWS | 1 +
README | 54 +
TODO | 27 +
acconfig.h | 12 +
autogen.sh | 21 +
configure.ac | 107 ++
configure.in | 103 ++
man/Makefile.am | 8 +
man/wmpinboard.1 | 983 ++++++++++++++++++
man/wmpinboard.pod | 1022 +++++++++++++++++++
src/Makefile.am | 17 +
src/getopt.c | 1004 ++++++++++++++++++
src/getopt.h | 134 +++
src/getopt1.c | 187 ++++
src/memcmp.c | 394 ++++++++
src/memcmp.h | 8 +
src/misc.c | 117 +++
src/misc.h | 24 +
src/notes.c | 1013 +++++++++++++++++++
src/notes.h | 67 ++
src/wmpinboard.c | 2299 ++++++++++++++++++++++++++++++++++++++++++
src/wmpinboard.h | 143 +++
src/xmisc.c | 434 ++++++++
src/xmisc.h | 35 +
themes-kit/HOWTO | 95 ++
themes-kit/abar.xpm | 42 +
themes-kit/bbar.xpm | 56 +
themes-kit/board.xpm | 135 +++
themes-kit/default.wmpbtheme | 258 +++++
themes-kit/digits.xpm | 15 +
themes-kit/sample.wmpbtheme | 130 +++
wmpb-convert.pl | 39 +
wmpinboard.lsm | 11 +
wmpinboard.spec.in | 48 +
xpm/abar.xpm | 42 +
xpm/bbar.xpm | 56 +
xpm/digits.xpm | 15 +
xpm/pinboard.xpm | 136 +++
44 files changed, 9894 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f679783
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,32 @@
+aclocal.m4
+autoconf.h
+autoconf.h.in
+autoconf.h.in~
+config.h
+config.h.in
+config.h.in~
+autom4te.cache/
+ChangeLog
+config.log
+config.status
+configure
+cscope.*
+.deps/
+Makefile
+Makefile.in
+wmblob
+*.o
+scripts/
+stamp-h*
+.*.sw[nop]
+*.tar.*
+_install
+*.log
+config.guess
+config.sub
+depcomp
+install-sh
+mkinstalldirs
+missing
+compile
+.cvsignore
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..3297402
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,8 @@
+wmpinboard and its accompanying documentation and pixmaps have been
+created by Marco G"otze, <gomar at mindless.com>. Mail bug reports,
+comments, suggestions, and the like to this address.
+
+The package also contains portions of the GNU C library, which, just as
+ThoughtTracker, is distributed under the terms of the GNU GPL.
+
+For additional information, see the CREDITS file.
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d60c31a
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
+
+ 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.)
+
+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.
+
+ 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.
+
+ 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
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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/CREDITS b/CREDITS
new file mode 100644
index 0000000..8bef9fa
--- /dev/null
+++ b/CREDITS
@@ -0,0 +1,9 @@
+ wmpinboard credits
+--------------------
+ - written by Marco G"otze, <mailto:gomar at mindless.com>
+ - closer looks at other WM dock apps as well as the Xlib documentation were
+ of some help
+ - the images were created using The GIMP
+ - wmpinboard uses XIC-related keyboard handling code taken from RXVT's source
+ - thanks to various people for making suggestions and pointing out bugs
+
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..b42a17a
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,182 @@
+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 awhile. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+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.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=PATH' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ 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. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--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
index 0000000..356c693
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,31 @@
+## Process this file with automake to produce Makefile.in
+
+SUBDIRS = src man
+EXTRA_DIST = CREDITS TODO wmpb-convert.pl wmpinboard.lsm wmpinboard.spec.in man/Makefile.* man/wmpinboard.* xpm/* themes-kit/*
+
+bin-dist: all
+ @test -x ../make_bin_package || ( echo "Not in development environment."; false )
+ strip src/$(PACKAGE)
+ ../make_bin_package $(PACKAGE) $(VERSION) i386-linux-glibc wmpb-convert.pl
+
+rpm-dist: dist
+ cp $(PACKAGE)-$(VERSION).tar.gz ../SOURCES
+ rpm -ba $(PACKAGE).spec
+
+on-site: dist bin-dist rpm-dist
+ cp ChangeLog $(HOME)/public_html/stuff/$(PACKAGE)
+ cp $(PACKAGE).lsm $(HOME)/public_html/stuff/$(PACKAGE)
+ pod2html man/$(PACKAGE).pod >$(HOME)/public_html/stuff/$(PACKAGE)/$(PACKAGE).1.html
+ rm -f *~~
+ mv ../*RPMS/$(PACKAGE)*rpm ../ON-SITE
+ mv $(PACKAGE)-$(VERSION).tar.gz ../ON-SITE
+ cd ../ON-SITE && \
+ mkdir $(PACKAGE)-tmp && \
+ cd $(PACKAGE)-tmp && \
+ cp ../$(PACKAGE)*gz . && \
+ gz2bz2 $(PACKAGE)*gz && \
+ mv $(PACKAGE)*.bz2 .. && \
+ cd .. && \
+ rmdir $(PACKAGE)-tmp && \
+ mv $(PACKAGE)* $(HOME)/public_html/stuff/$(PACKAGE)
+
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..3cf8b2b
--- /dev/null
+++ b/NEWS
@@ -0,0 +1 @@
+See the ChangeLog.
diff --git a/README b/README
new file mode 100644
index 0000000..d63fd94
--- /dev/null
+++ b/README
@@ -0,0 +1,54 @@
+ wmpinboard
+============
+
+ License
+---------
+
+wmpinboard is distributed under the terms of the GNU General Public license,
+revision 2, or--at your choice--any later revision. A copy of said document
+is included with this source package.
+
+ Requirements
+--------------
+
+ - an ANSI-compliant C compiler (preferably, GCC or one of its descendants)
+ - X11R6 including header files (installed separately as "xdevel" or similar
+ by some distributions)
+ - the XPM library
+ - wmpinboard is recommended to be run on a high-color (15bit or above) display
+
+wmpinboard has reportedly been successfully compiled on the following
+platforms:
+
+ - Linux 2.0.x through 2.2.x, various setups
+ - FreeBSD 3.1
+ - Solaris 2.6
+
+Reports about other platforms are always welcome.
+
+ Installation
+--------------
+
+If you're too lazy to read the generic instructions in the INSTALL
+file, or already know how to install autoconf'ed programs in principle,
+here are the quick instructions that should suffice in 99% of all cases:
+
+ $ ./configure
+ $ make
+ $ make install-strip
+
+The default prefix is "/usr/local". If you want the binary and man
+page to go anywhere else, use configure's "--prefix" switch (see
+`configure --help`). For further compile-time options, see configure's
+"--help" output.
+
+ Upgrading from earlier versions
+---------------------------------
+
+If you're upgrading to wmpinboard 0.7+ from an earlier version, existing notes
+will get lost due to some major changes concerning its data file format.
+You can avoid this by running the included PERL script as the user in
+question beforehand:
+
+ $ perl /path/to/wmpinboard-x.x/wmpb-convert.pl
+
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..a7260fe
--- /dev/null
+++ b/TODO
@@ -0,0 +1,27 @@
+ wmpinboard
+============
+
+ Things TO DO
+--------------
+
+ - wait some more time, finally declare it stable and name it v1.0
+
+ Things perhaps TO DO
+----------------------
+
+ Things I'm NOT going TO DO
+----------------------------
+
+ - Make wmpinboard pop up any external windows. Why?
+
+ See the man page, section "FREQUENTLY ASKED QUESTIONS".
+
+ - Render rough sketches of the notes' contents in pinboard view.
+
+ Judging by my personal experience, the notes' texts tend to cover them
+ uniformly due to the limitation in size, so doing this would invariably
+ result in just a mess of dark blobs.
+
+
+Further suggestions? Mail to <gomar at mindless.com>.
+
diff --git a/acconfig.h b/acconfig.h
new file mode 100644
index 0000000..066d034
--- /dev/null
+++ b/acconfig.h
@@ -0,0 +1,12 @@
+/* define if the XPM library is available */
+#undef HAVE_LIBXPM
+
+/* define if there is an 8-bit-clean memcmp() */
+#undef HAVE_MEMCMP
+
+/* define the edit mode timeout in seconds (0 disables) */
+#undef TIMEOUT
+
+/* define if wear & tear of notes (creases) is to be simulated */
+#undef CREASES
+
diff --git a/autogen.sh b/autogen.sh
new file mode 100644
index 0000000..082fc28
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+#
+# autogen.sh glue for wmpinboard
+# $Id: autogen.sh,v 1.3 2001/07/03 02:14:16 hmh Exp $
+#
+set -e
+
+# The idea is that we make sure we're always using an up-to-date
+# version of all the auto* script chain for the build. The GNU autotools
+# are rather badly designed in that area.
+
+aclocal
+autoheader
+
+#we don't use symlinks because of debian's build system,
+#but they would be a better choice.
+[ -r /usr/share/automake/missing ] && cp -f /usr/share/automake/missing .
+[ -r /usr/share/automake/install-sh ] && cp -f /usr/share/automake/install-sh .
+
+automake
+autoconf
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..ab8849d
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,107 @@
+dnl Process this file with autoconf to produce a configure script.
+
+AC_INIT([wmpinboard], [1.0])
+AM_INIT_AUTOMAKE([-Wall silent-rules])
+AM_CONFIG_HEADER(config.h)
+
+AC_PROG_CC
+dnl don't want the -g flag
+CFLAGS=`echo "x $CFLAGS"|sed 's/^x //; s/\(^\| \)-g\($\| \)/ /g'`
+AC_ISC_POSIX
+AC_LANG_C
+
+dnl use POSIX and BSD stuff where available
+CFLAGS="$CFLAGS -D_BSD_SOURCE -D_POSIX_SOURCE=199506L"
+
+dnl additional compile flags when using gcc
+if test x$GCC = xyes; then
+ CFLAGS="$CFLAGS -Wall -ansi -pedantic"
+fi
+
+dnl X11 stuff
+AC_PATH_XTRA
+AC_CHECK_LIB(Xpm, main, AC_DEFINE([HAVE_LIBXPM], 1,
+ [define if the XPM library is available]),
+ AC_MSG_ERROR(The XPM library is required but wasn't found.),
+ $X_LIBS -lX11 -lXext)
+
+AC_CHECK_LIB(ife, sense)
+
+AC_C_CONST
+AC_C_INLINE
+dnl check for C headers and functions
+dnl more or less optional ones first
+AC_CHECK_HEADERS([getopt.h malloc.h signal.h string.h unistd.h])
+AC_CHECK_FUNC(getopt, , need_getopt=yes)
+AC_CHECK_FUNC(getopt_long, , need_getopt_long=yes)
+AC_FUNC_MEMCMP
+if test x"$ac_cv_func_memcmp_clean" = xyes; then
+ AC_DEFINE([HAVE_MEMCMP], 1,
+ [define if there is an 8-bit-clean memcmp()])
+else
+ need_memcmp=yes
+fi
+dnl required ones
+AC_CHECK_FUNC(strchr, ,
+ AC_MSG_ERROR(Your C library seems to be lacking the strchr() function.))
+AC_CHECK_FUNC(strcasecmp, ,
+ AC_MSG_ERROR(Your C library seems to be lacking the strcasecmp() function.))
+AC_CHECK_FUNC(ualarm, ,
+ AC_MSG_ERROR(Your C library seems to be lacking the ualarm() function.))
+AC_CHECK_FUNC(usleep, ,
+ AC_MSG_ERROR(Your C library seems to be lacking the usleep() function.))
+AC_CHECK_FUNC(kill, ,
+ AC_MSG_ERROR(Your C library seems to be lacking the kill() function.))
+
+dnl === compile-time options ===
+dnl TIMEOUT
+AC_ARG_ENABLE(timeout,
+[ --enable-timeout[=TIME] set edit mode timeout to TIME seconds (default = 60)],
+[case "${enableval}" in
+ yes) timeout=60 ;;
+ no) timeout=0 ;;
+ *) timeout=`echo "${enableval}"|sed 's/[^0-9]//g'`
+ if test x"$timeout" = x; then
+ AC_MSG_ERROR(Bad value ${enableval} for --enable-timeout.)
+ fi
+ if test $timeout -gt 0 -a $timeout -lt 5; then
+ timeout=5
+ AC_MSG_WARN(Timeout value to low: increasing to 5 seconds.)
+ fi
+ ;;
+esac], [timeout=60])
+AC_DEFINE_UNQUOTED(TIMEOUT, ${timeout},
+ [define the edit mode timeout in seconds (0 disables)])
+
+dnl CREASES
+AC_ARG_ENABLE(creases,
+[ --enable-creases enable wear and tear (creases) (default = yes)],
+[case "${enableval}" in
+ yes) creases=yes ;;
+ no) creases=no ;;
+ *) AC_MSG_ERROR(Bad value ${enableval} for --enable-creases.) ;;
+esac], [creases=yes])
+if test x"$creases" = xyes; then
+ AC_DEFINE([CREASES], 1,
+ [define if wear & tear of notes (creases) is to be simulated])
+fi
+
+dnl determine what conditional stuff has to be compiled
+if test x"$need_memcmp" = xyes; then
+ CONDITIONAL_SOURCES="$CONDITIONAL_SOURCES memcmp.c"
+fi
+if test x"$need_getopt" = xyes; then
+ CONDITIONAL_SOURCES="$CONDITIONAL_SOURCES getopt.c"
+fi
+if test x"$need_getopt_long" = xyes; then
+ CONDITIONAL_SOURCES="$CONDITIONAL_SOURCES getopt1.c"
+fi
+AC_SUBST(CONDITIONAL_SOURCES)
+
+AC_OUTPUT([
+Makefile
+src/Makefile
+man/Makefile
+wmpinboard.spec
+])
+
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000..1e1a681
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,103 @@
+dnl Process this file with autoconf to produce a configure script.
+
+AC_INIT(configure.in)
+AM_INIT_AUTOMAKE(wmpinboard, 1.0)
+AM_CONFIG_HEADER(config.h)
+
+AC_PROG_CC
+dnl don't want the -g flag
+CFLAGS=`echo "x $CFLAGS"|sed 's/^x //; s/\(^\| \)-g\($\| \)/ /g'`
+AC_ISC_POSIX
+AC_LANG_C
+
+dnl use POSIX and BSD stuff where available
+CFLAGS="$CFLAGS -D_BSD_SOURCE -D_POSIX_SOURCE=199506L"
+
+dnl additional compile flags when using gcc
+if test x$GCC = xyes; then
+ CFLAGS="$CFLAGS -Wall -ansi -pedantic"
+fi
+
+dnl X11 stuff
+AC_PATH_XTRA
+AC_CHECK_LIB(Xpm, main, AC_DEFINE(HAVE_LIBXPM),
+ AC_MSG_ERROR(The XPM library is required but wasn't found.),
+ $X_LIBS -lX11 -lXext)
+
+AC_CHECK_LIB(ife, sense)
+
+AC_C_CONST
+AC_C_INLINE
+dnl check for C headers and functions
+dnl more or less optional ones first
+AC_CHECK_HEADERS([getopt.h malloc.h signal.h string.h unistd.h])
+AC_CHECK_FUNC(getopt, , need_getopt=yes)
+AC_CHECK_FUNC(getopt_long, , need_getopt_long=yes)
+AC_FUNC_MEMCMP
+if test x"$ac_cv_func_memcmp_clean" = xyes; then
+ AC_DEFINE(HAVE_MEMCMP)
+else
+ need_memcmp=yes
+fi
+dnl required ones
+AC_CHECK_FUNC(strchr, ,
+ AC_MSG_ERROR(Your C library seems to be lacking the strchr() function.))
+AC_CHECK_FUNC(strcasecmp, ,
+ AC_MSG_ERROR(Your C library seems to be lacking the strcasecmp() function.))
+AC_CHECK_FUNC(ualarm, ,
+ AC_MSG_ERROR(Your C library seems to be lacking the ualarm() function.))
+AC_CHECK_FUNC(usleep, ,
+ AC_MSG_ERROR(Your C library seems to be lacking the usleep() function.))
+AC_CHECK_FUNC(kill, ,
+ AC_MSG_ERROR(Your C library seems to be lacking the kill() function.))
+
+dnl === compile-time options ===
+dnl TIMEOUT
+AC_ARG_ENABLE(timeout,
+[ --enable-timeout[=TIME] set edit mode timeout to TIME seconds (default = 60)],
+[case "${enableval}" in
+ yes) timeout=60 ;;
+ no) timeout=0 ;;
+ *) timeout=`echo "${enableval}"|sed 's/[^0-9]//g'`
+ if test x"$timeout" = x; then
+ AC_MSG_ERROR(Bad value ${enableval} for --enable-timeout.)
+ fi
+ if test $timeout -gt 0 -a $timeout -lt 5; then
+ timeout=5
+ AC_MSG_WARN(Timeout value to low: increasing to 5 seconds.)
+ fi
+ ;;
+esac], [timeout=60])
+AC_DEFINE_UNQUOTED(TIMEOUT, ${timeout})
+
+dnl CREASES
+AC_ARG_ENABLE(creases,
+[ --enable-creases enable wear and tear (creases) (default = yes)],
+[case "${enableval}" in
+ yes) creases=yes ;;
+ no) creases=no ;;
+ *) AC_MSG_ERROR(Bad value ${enableval} for --enable-creases.) ;;
+esac], [creases=yes])
+if test x"$creases" = xyes; then
+ AC_DEFINE(CREASES)
+fi
+
+dnl determine what conditional stuff has to be compiled
+if test x"$need_memcmp" = xyes; then
+ CONDITIONAL_SOURCES="$CONDITIONAL_SOURCES memcmp.c"
+fi
+if test x"$need_getopt" = xyes; then
+ CONDITIONAL_SOURCES="$CONDITIONAL_SOURCES getopt.c"
+fi
+if test x"$need_getopt_long" = xyes; then
+ CONDITIONAL_SOURCES="$CONDITIONAL_SOURCES getopt1.c"
+fi
+AC_SUBST(CONDITIONAL_SOURCES)
+
+AC_OUTPUT([
+Makefile
+src/Makefile
+man/Makefile
+wmpinboard.spec
+])
+
diff --git a/man/Makefile.am b/man/Makefile.am
new file mode 100644
index 0000000..a2843c7
--- /dev/null
+++ b/man/Makefile.am
@@ -0,0 +1,8 @@
+## Process this file with automake to produce Makefile.in
+
+man_MANS = wmpinboard.1
+
+EXTRA_DIST = $(man_MANS) *.pod
+
+wmpinboard.1: wmpinboard.pod
+ -pod2man --release=$(VERSION) --center=wmpinboard wmpinboard.pod wmpinboard.1
diff --git a/man/wmpinboard.1 b/man/wmpinboard.1
new file mode 100644
index 0000000..f27b792
--- /dev/null
+++ b/man/wmpinboard.1
@@ -0,0 +1,983 @@
+.\" Automatically generated by Pod::Man version 1.02
+.\" Thu Apr 13 13:01:39 2000
+.\"
+.\" Standard preamble:
+.\" ======================================================================
+.de Sh \" Subsection heading
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+
+.fi
+..
+.\" Set up some character translations and predefined strings. \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote. | will give a
+.\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used
+.\" to do unbreakable dashes and therefore won't be available. \*(C` and
+.\" \*(C' expand to `' in nroff, nothing in troff, for use with C<>
+.tr \(*W-|\(bv\*(Tr
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+. ds -- \(*W-
+. ds PI pi
+. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+. ds L" ""
+. ds R" ""
+. ds C` `
+. ds C' '
+'br\}
+.el\{\
+. ds -- \|\(em\|
+. ds PI \(*p
+. ds L" ``
+. ds R" ''
+'br\}
+.\"
+.\" If the F register is turned on, we'll generate index entries on stderr
+.\" for titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and
+.\" index entries marked with X<> in POD. Of course, you'll have to process
+.\" the output yourself in some meaningful fashion.
+.if \nF \{\
+. de IX
+. tm Index:\\$1\t\\n%\t"\\$2"
+. .
+. nr % 0
+. rr F
+.\}
+.\"
+.\" For nroff, turn off justification. Always turn off hyphenation; it
+.\" makes way too many mistakes in technical documents.
+.hy 0
+.if n .na
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear. Run. Save yourself. No user-serviceable parts.
+.bd B 3
+. \" fudge factors for nroff and troff
+.if n \{\
+. ds #H 0
+. ds #V .8m
+. ds #F .3m
+. ds #[ \f1
+. ds #] \fP
+.\}
+.if t \{\
+. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+. ds #V .6m
+. ds #F 0
+. ds #[ \&
+. ds #] \&
+.\}
+. \" simple accents for nroff and troff
+.if n \{\
+. ds ' \&
+. ds ` \&
+. ds ^ \&
+. ds , \&
+. ds ~ ~
+. ds /
+.\}
+.if t \{\
+. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+. \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+. \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+. \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+. ds : e
+. ds 8 ss
+. ds o a
+. ds d- d\h'-1'\(ga
+. ds D- D\h'-1'\(hy
+. ds th \o'bp'
+. ds Th \o'LP'
+. ds ae ae
+. ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ======================================================================
+.\"
+.IX Title "WMPINBOARD 1"
+.TH WMPINBOARD 1 "0.99.3" "2000-04-13" "wmpinboard"
+.UC
+.SH "NAME"
+\&\fBwmpinboard\fR \- a \fBWindow Maker\fR dock app resembling a miniature pinboard
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+.Vb 1
+\& wmpinboard [options]
+.Ve
+.Sh "What wmpinboard is"
+.IX Subsection "What wmpinboard is"
+\&\fBwmpinboard\fR is a \fBWindow Maker\fR dock applet resembling a miniature
+pinboard. It's intended to somewhat relieve heavily littered desktops
+by allowing you to place reminders on a graphical on-screen pinboard
+rather than producing a mess of real notes all around your keyboard
+(thus being environmentally A Good Thing, too \fB;\-)\fR ). It supports
+arbitrary 6x10 X fonts and has XLocale support, enabling you to enter
+locale-dependent special characters if set up appropriately. Besides
+text, you can add small monochrome sketches to your notes or simply
+encircle or underline words as a means of emphasis, and alarms can be
+set to explicitly remind you of things. Above all, \fBwmpinboard\fR is
+animated in redundant ways to make it look even more attractive, and
+themeability provides for a way of adapting its appearance to that of
+the rest of your desktop.
+.Sh "What wmpinboard \s-1ISN\s0'T"
+.IX Subsection "What wmpinboard ISN'T"
+Clearly, \fBwmpinboard\fR doesn't allow you to keep an unlimited number of
+notes holding arbitrary amounts of information, and that's not what
+it's meant to do. Just as real notes offer limited space, so do those
+simulated by \fBwmpinboard\fR. Besides, as a dock applet, it aims at
+being small and neat and yet useful in a way, and that's what it is,
+too, or considered to be by some people, anyway. If you need room for
+more comprehensive reminders, use another program, either additionally
+or exclusively. There's a variety of such out there, but their niche
+is different from that which \fBwmpinboard\fR claims.
+.SH "OPTIONS"
+.IX Header "OPTIONS"
+\&\fBwmpinboard\fR's command-line options can be roughly divided into four
+groups: configuration directives, run-time options, command-line
+actions, and general options. Generally, \fBwmpinboard\fR supports
+GNU-style long options (which may be abbreviated unambiguously) as well
+as short ones for parameters used more commonly.
+.Sh "Configuration directives"
+.IX Subsection "Configuration directives"
+This type of command-line options changes some aspect of
+\&\fBwmpinboard\fR's configuration that is saved along with the data and
+thus set in a more permanent way. Only one such parameter may be
+specified per call, and there mustn't be another instance running.
+.Ip "\fB\f(CB\*(C`\-\-font=FONT\*(C'\fB\fR" 2
+.IX Item "--font=FONT"
+Makes \fBwmpinboard\fR use the specified font; \fB\f(CB\*(C`FONT\*(C'\fB\fR can be one of
+the shortcuts listed when running the program with "\fB\f(CB\*(C`\-\-help\*(C'\fB\fR" as a
+parameter, or a complete X descriptor of a fixed size 6x10 font. The
+pinboard \fImust be empty\fR in order for this option to be applicable.
+For more details, see the section on "FREQUENTLY ASKED QUESTIONS".
+.Ip "\fB\f(CB\*(C`\-\-theme=FILE\*(C'\fB\fR" 2
+.IX Item "--theme=FILE"
+Configures \fBwmpinboard\fR to load the specified theme when started
+interactively the next time. \fB\f(CB\*(C`FILE\*(C'\fB\fR is the location of a
+\&\fBwmpinboard\fR theme file (typically with a file name extension of
+\&\fI.wmpbtheme\fR). If it can't be loaded when run interactively, the
+program will revert to its default theme. If \fB\f(CB\*(C`FILE\*(C'\fB\fR is an empty
+string or \*(L"default\*(R", the use of a custom theme will be deactivated.
+.Sp
+Themes affect \fBwmpinboard\fR's appearance, in particular, its pinboard,
+edit mode and alarm panel pixmaps, the latter's digits, and possibly
+the location of the pinboard mode label area (via which notes are
+created). For downloading themes, or if you're inclined to create one
+yourself and need instructions, check out the program's home page (see
+the section on "AUTHOR" or \fBwmpinboard\fR's "\fB\f(CB\*(C`\-\-help\*(C'\fB\fR" output for the \s-1URL\s0). The
+themes kit containing instructions and samples on how to create theme
+files for \fBwmpinboard\fR that can be downloaded there is also included
+with the source package of the program.
+.Ip "\fB\f(CB\*(C`\-\-alarm\-cmd=CMD\*(C'\fB\fR" 2
+.IX Item "--alarm-cmd=CMD"
+Configures \fB\f(CB\*(C`CMD\*(C'\fB\fR as the command to be executed on alarms. E.g.,
+you could use "\f(CW\*(C`xkbbell\*(C'\fR" to cause the program to beep on such
+occassions, or make it run some sound-playing program. To reset the
+alarm command to none, make \fB\f(CB\*(C`CMD\*(C'\fB\fR a zero-length string.
+.Sh "Run-time options"
+.IX Subsection "Run-time options"
+.Ip "\fB\f(CB\*(C`\-d DISPLAY\*(C'\fB\fR or \fB\f(CB\*(C`\-\-display=DISPLAY\*(C'\fB\fR" 2
+.IX Item "-d DISPLAY or --display=DISPLAY"
+Uses the specified X display rather than the default one.
+.Ip "\fB\f(CB\*(C`\-c\*(C'\fB\fR or \fB\f(CB\*(C`\-\-click\-to\-focus\*(C'\fB\fR" 2
+.IX Item "-c or --click-to-focus"
+This turns on some emulation of a click-based keyboard focus mode. See
+the section on "FREQUENTLY ASKED QUESTIONS".
+.Ip "\fB\f(CB\*(C`\-t TIME\*(C'\fB\fR or \fB\f(CB\*(C`\-\-timeout=TIME\*(C'\fB\fR" 2
+.IX Item "-t TIME or --timeout=TIME"
+Sets the edit mode timeout (i.e., the number of seconds of idleness
+after which edit mode is terminated automatically) to \fB\f(CB\*(C`TIME\*(C'\fB\fR
+seconds. The compile-time default is 60s, but this may have been
+changed for your particular build; run with \fB\f(CB\*(C`\-v\*(C'\fB\fR if in doubt to
+check that. Specifying a value of 0 (zero) will disable the timeout.
+.Ip "\fB\f(CB\*(C`\-n\*(C'\fB\fR or \fB\f(CB\*(C`\-\-normal\-state\*(C'\fB\fR" 2
+.IX Item "-n or --normal-state"
+Forces \fBwmpinboard\fR to run in so-called NormalState, which is
+preferred by \fBAfterStep\fR's \fBWharf\fR.
+.Ip "\fB\f(CB\*(C`\-w\*(C'\fB\fR or \fB\f(CB\*(C`\-\-withdrawn\-state\*(C'\fB\fR" 2
+.IX Item "-w or --withdrawn-state"
+Forces the program to run in so-called WithdrawnState, as desired by
+the \fBWindow Maker\fR dock. This option and the previous one are mutually
+exclusive. Note also that \fBwmpinboard\fR tries to auto-detect whether
+\&\fBWindow Maker\fR is running and sets itself up accordingly. Using \fB\f(CB\*(C`\-n\*(C'\fB\fR
+or \fB\f(CB\*(C`\-w\*(C'\fB\fR should only be necessary in case those heuristics fail on
+your system for some reason or other.
+.Ip "\fB\f(CB\*(C`\-\-light\*(C'\fB\fR" 2
+.IX Item "--light"
+Use this switch to suppress animations.
+.Sh "Command-line actions"
+.IX Subsection "Command-line actions"
+Even though \fBwmpinboard\fR is by design an interactive application,
+there may be occasions when it comes in handy to be able to
+access/manipulate notes from the command line. That's why the program
+offers a set of command-line options allowing for basic operations of
+that kind. Still, it should be kept in mind that \fBwmpinboard\fR is
+primarily meant to be used interactively.
+.PP
+All of the options below will, if an interactive instance of
+\&\fBwmpinboard\fR is running in the background, cause that to save its data
+(and quit \fIedit mode\fR, if necessary), and if any changes are made by
+the respective option, the interactive instance will then be signalled
+to re-read the data file. Even though the implemented methods of
+inter-process communication should generally be sufficiently safe
+with respect to avoiding data file corruption, it's in theory possible
+to undermine the concept and cause damage that way\*(--yet this won't
+happen unless you deliberately take pains to achieve the goal.
+Generally, everything should work fine as long as you don't try running
+multiple non-interactive instances of \fBwmpinboard\fR simultaneously.
+.PP
+Only one of the below actions can be specified per call to \fBwmpinboard\fR.
+.Ip "\fB\f(CB\*(C`\-\-dump\*(C'\fB\fR" 2
+.IX Item "--dump"
+This dumps the contents of all notes, replacing line breaks by spaces
+(unless preceded by a hyphen) and shortening sequences of blanks. The
+list of dumped strings will be sorted by color groups. If you use
+special characters in your notes, make sure your terminal's running
+with the same character set as \fBwmpinboard\fR, or what you'll see might
+have a garbage-like quality.
+.Sp
+Each line of output represents one note and is prefixed by the internal
+number \fIcurrently\fR identifying the respective note and, if an alarm is
+configured for the respective note, time and date (or \*(L"daily\*(R").
+.Ip "\fB\f(CB\*(C`\-\-dump\-raw\*(C'\fB\fR" 2
+.IX Item "--dump-raw"
+Unlike the \*(L"cooked\*(R" dump described above, this just dumps the raw
+contents of all notes without applying any kind of formatting. May
+come in handy if your notes happen to contain E-mail addresses or other
+things for which lines 10 characters wide are too narrow.
+.Ip "\fB\f(CB\*(C`\-\-del=NUMBER\*(C'\fB\fR" 2
+.IX Item "--del=NUMBER"
+This option will remove the note identified by \fB\f(CB\*(C`NUMBER\*(C'\fB\fR from the
+pinboard. \fB\f(CB\*(C`NUMBER\*(C'\fB\fR is a number determined by the output of either
+dump option, which should be consulted right before using this one,
+since note numbers may change when notes are moved around on the board
+or others are removed.
+.Ip "\fB\f(CB\*(C`\-\-add=STRING\*(C'\fB\fR" 2
+.IX Item "--add=STRING"
+When run with this option, \fBwmpinboard\fR will add a new note (provided
+the maximal number of notes has not yet been reached) at a random
+position on the board, with contents \fB\f(CB\*(C`STRING\*(C'\fB\fR, word-wrapping the
+text at the end of the note's lines where necessary (after white space
+and hyphens). If due to this wrapping, the entire string cannot be
+stored on the note, the remainder will be discarded silently.
+.Sp
+In order to create a note with a certain color, the string can be
+prefixed by a color code specifying the group of colors which a random
+color is to be selected from (code letters are recognized
+case-insensitively):
+.Sp
+.Vb 4
+\& %G - green
+\& %Y - yellow/white
+\& %R - reddish
+\& %B - blue
+.Ve
+Alternatively or additionally, you may specify a position code as \*(L"%1\*(R"
+through \*(L"%9\*(R", defining an approximate position on the board where the
+note is to be placed. Each of the nine figures corresponds to a ninth
+of the board with its index assigned in accordance with the layout of
+your keypad (i.e., \*(L"%1\*(R" meaning lower left, \*(L"%9\*(R" upper right corner,
+and so forth).
+.Sp
+Thus,
+.Sp
+.Vb 1
+\& wmpinboard --add '%g%5test'
+.Ve
+will place a green note saying \*(L"test\*(R" at the center of the board.
+.Sp
+(Note: The \*(L"%\*(R" character can be escaped by a second one if you want to
+add an un-prefixed string starting with a percent character.)
+.Ip "\fB\f(CB\*(C`\-\-add\-raw=STRING\*(C'\fB\fR" 2
+.IX Item "--add-raw=STRING"
+Via this option, a new note can be added from the command line
+(provided that this won't exceed the maximum number of notes).
+\&\fB\f(CB\*(C`STRING\*(C'\fB\fR specifies the \fIraw\fR contents of the note, as printed by
+\&\fB\f(CB\*(C`\-\-dump\-raw\*(C'\fB\fR. The same set of color group and position codes as
+for the previous option applies.
+.Sh "General options"
+.IX Subsection "General options"
+.Ip "\fB\f(CB\*(C`\-h\*(C'\fB\fR or \fB\f(CB\*(C`\-\-help\*(C'\fB\fR" 2
+.IX Item "-h or --help"
+This prints a help screen listing command line options together with
+brief descriptions.
+.Ip "\fB\f(CB\*(C`\-i\*(C'\fB\fR or \fB\f(CB\*(C`\-\-info\*(C'\fB\fR" 2
+.IX Item "-i or --info"
+Prints information about the current user configuration (font, theme,
+alarm command) and some useless statistics.
+.Ip "\fB\f(CB\*(C`\-v\*(C'\fB\fR or \fB\f(CB\*(C`\-\-version\*(C'\fB\fR" 2
+.IX Item "-v or --version"
+This prints some more detailed version information, in particular,
+which compile-time settings this binary was built with.
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+\&\fBwmpinboard\fR operates in basically two different modes, namely, the
+\&\fIpinboard view\fR and \fIedit mode\fR. Furthermore, a \fIpanel\fR of buttons
+granting access to extended options can be popped up in \fIedit mode\fR,
+which in turn allows you to display the \fIalarm panel\fR to configure
+alarm settings for the current note.
+.Sh "Pinboard view"
+.IX Subsection "Pinboard view"
+This is \fBwmpinboard\fR's normal mode of operation. A potentially
+chaotic arrangement of tiny squares on a beige-colored oblong (default
+theme) is meant to resemble notes pinned to a cork board. Possible
+actions include:
+.Ip "\(bu" 2
+\&\fIAdd\fR a note, by left-clicking on the board's \*(L"\s-1TO\s0 \s-1DO\s0\*(R" label. This
+creates a new, blank, randomly-colored note at a random position on the
+board and puts \fBwmpinboard\fR in \fIedit mode\fR (see below). If you
+prefer to place a new note at a certain position before being prompted
+to enter its contents, this can be done by moving the mouse cursor
+after having clicked on the label. This will realize the note and
+allow you to drag it to a position of your choice. \fBwmpinboard\fR will
+switch to \fIedit mode\fR as soon as you release the mouse button.
+.Ip "\(bu" 2
+\&\fIEdit\fR/\fIview\fR a note, by left-clicking on a note. This switches
+to \fIedit mode\fR (described below).
+.Ip "\(bu" 2
+\&\fIMove\fR a note, by dragging it using the right mouse button.
+This also raises the note in question on top of all others.
+Depending on its horizontal position, the note will be tilted
+automatically. As a side-effect, a single brief right-click
+can be used to raise a note on top of overlapping ones without
+moving it.
+.Sp
+By dragging a note with the left mouse button, you can move it without
+changing its level with respect to other notes, i.e., without raising
+it on top of all others.
+.Sh "Edit mode"
+.IX Subsection "Edit mode"
+This mode serves two purposes: on the one hand, it presents you with a
+\&\*(L"full-size\*(R" view of a note's contents, on the other, you can use the
+occasion to edit it. Due to its limited size, a note can hold up to 10
+characters on 6 lines (minus one on the last, i.e., 59 characters
+altogether), plus a monochrome sketch of some kind. Possibly actions:
+.Ip "\(bu" 2
+\&\fIEnter\fR text. \fBwmpinboard\fR supports user-selectable fonts and dead
+keys, so you should be able to enter any characters that are usually
+accessible via your keyboard and have them displayed correctly.
+Furthermore, the cursor can be moved around using the arrow keys (or
+EMACS-style via [Ctrl]\-[N]/[P]/[F]/[B], if you are so inclined).
+Alternatively, it can be placed explicitly by left-clicking where you
+want it to be. Other special keys that are supported include:
+.RS 2
+.Ip "[PgUp]/[PgDn]" 2
+.IX Item "[PgUp]/[PgDn]"
+Places the cursor on character 1/59, respectively.
+.Ip "[Home]/[End]" 2
+.IX Item "[Home]/[End]"
+Places the cursor at the textual start or end of the current line.
+.Ip "[Del]" 2
+.IX Item "[Del]"
+Deletes the character currently under the text cursor and shifts the
+remaining text on the current line to the left; if the current line is
+blank, removes it and shifts all lines below up by one line.
+.Ip "[Backspace]" 2
+.IX Item "[Backspace]"
+See [Del], but affects the character on the left of the cursor.
+.Ip "[Ins]" 2
+.IX Item "[Ins]"
+Toggles inserting/overwriting of existing text; the current mode is
+indicated by a cursor change (block cursor means insert mode).
+.Ip "[Enter]" 2
+.IX Item "[Enter]"
+In insert mode, wraps the current line at the cursor's position; in
+overwrite mode (underscore cursor), merely moves the cursor to the
+start of the next line.
+.Ip "[Ctrl]\-[Y], \-[Z]" 2
+.IX Item "[Ctrl]-[Y], -[Z]"
+Removes an entire (intermediate) line, shifting those below up by one,
+and places the cursor at the start of the current line.
+.Ip "[Esc]" 2
+.IX Item "[Esc]"
+Quits \fIedit mode\fR and returns to the \fIpinboard view\fR.
+.Ip "[Shift]\-[Left]/[Right]" 2
+.IX Item "[Shift]-[Left]/[Right]"
+Cycles through all notes currently on the pinboard.
+.Ip "[Shift]\-[Up]/[Down]" 2
+.IX Item "[Shift]-[Up]/[Down]"
+Cycles through all notes that are \fIroughly\fR the same color as the
+current one. For this purpose, colors have internally been divided
+into four groups: green, white/yellow, reddish, blue.
+.Ip "([Shift]\-)[Tab]" 2
+.IX Item "([Shift]-)[Tab]"
+Cycles (backwards) through availabe note colors.
+.RE
+.RS 2
+.RE
+.Ip "\(bu" 2
+\&\fICut'n'paste\fR text. Despite the limitations implied, \fBwmpinboard\fR
+has support for cutting & pasting to and from the X clipboard:
+.RS 2
+.Ip "\(bu" 2
+In order to copy text to the clipboard, select the desired segment via
+either the left or the right mouse button: the left one will copy the
+text post-formatted as done by the command line switch \fB\f(CB\*(C`\-\-dump\*(C'\fB\fR
+(see the section on "OPTIONS"); the right button will copy the raw selection.
+Similarly, a left double click will select the word (i.e., all
+adjoining non-blank characters) at the respective position, a right one
+will do the same but neglect line breaks as delimiters. Additionally,
+you can copy a note's entire raw contents by pressing [Ctrl]\-[R];
+[Ctrl]\-[C] will do the same with applied post-formatting.
+.Ip "\(bu" 2
+To paste the clipboard's contents, press the middle button wherever the
+insertion is supposed to happen. This will insert the clipboard's
+current contents, trying to word-wrap the text (at white space and
+after hyphens). If in insert mode, following text will be shifted
+towards the end of the note, trying to word-wrap that as well.
+.Sp
+If you wish to paste something without word-wrapping (e.g., an \s-1URL\s0
+containing a hyphen), paste it via [Ctrl]\-[I] (think [i]nsert). This
+will paste the clipboard's raw contents at the current location of the
+cursor, shifting subsequent text if in insert mode (not trying to
+word-wrap that either).
+.RE
+.RS 2
+.Sp
+Obvious limitations you should be aware of include:
+.RS 2
+.RE
+.Ip "\(bu" 2
+As is usually the case (about \fBwmpinboard\fR, anyway), size matters.
+As you know, a note can hold only up to 59 characters, so trying to
+paste longer strings will result in those being truncated.
+.Ip "\(bu" 2
+If the text to be pasted is formatted in some way or other, this won't
+be the case any more after it has been pasted: \fBwmpinboard\fR replaces
+new line characters by blanks and, when pasting using the mouse, tries
+to word-wrap text.
+.Ip "\(bu" 2
+The information stored in the cut buffer needn't necessarily be
+compatible with \fBwmpinboard\fR in that it may be encoded with another
+character set.
+.RE
+.RS 2
+.RE
+.Ip "\(bu" 2
+\&\fILeave\fR \fIedit mode\fR. This is achieved by left-clicking on the
+triangle in the lower right-hand side corner. If the note is
+completely empty, it will be removed from the board. In any
+case, this returns to the \fIpinboard view\fR.
+.Ip "\(bu" 2
+\&\fIPop up\fR a \fIpanel\fR with some further options to choose from. This
+is done by right-clicking on the aforementioned triangle. To
+learn what the \fIpanel\fR is there for, see the corresponding section
+below.
+.Ip "\(bu" 2
+\&\fIDraw\fR a sketch. This mode can be activated via the \fIpanel\fR, and
+deactivated by either right-clicking somewhere on the note
+or opening the \fIpanel\fR again. While in drawing mode, the mouse
+pointer is pencil-shaped, and drawing can be done by keeping
+the left mouse button pressed and dragging the mouse, just as
+you'd expect. Sketch and text may overlap each other, but keyboard
+input is ignored while in drawing mode.
+.Ip "\(bu" 2
+\&\fIErase\fR a sketch. Just like DRAWing mode, this mode is entered
+via the \fIpanel\fR, and can be quit just like the former. In erase
+mode, the text is hidden, so you needn't guess whether a pixel
+belongs to an entered character or a drawn sketch. Note that
+the erase cursor's point is slightly larger than the one used
+when drawing.
+.PP
+Note: \fBwmpinboard\fR remembers where you left the text cursor after
+you last edited a note and restores this position when you edit it the
+next time.
+.Sh "Edit mode panel"
+.IX Subsection "Edit mode panel"
+This \fIpanel\fR is intended to provide easy access to some options
+affecting \fIedit mode\fR or the current note in general. The \fIpanel\fR
+looks like this (letters denoting the buttons for reference below):
+.PP
+.Vb 5
+\& +---+---+---+---+
+\& | a | c | e | g |
+\& +---+---+---+---+
+\& | b | d | f | h |
+\& +---+---+---+---+
+.Ve
+The buttons bear tiny icons which are meant to suggest what they do,
+which isn't all that easy on a 12x12 pixels area. \fB\f(CB\*(C`:^)\*(C'\fB\fR
+.PP
+Here's a description of what each button does:
+.Ip "(a)" 4
+.IX Item "(a)"
+Left-clicking on this button opens and closes the \fIalarm panel\fR (see
+below), which allows you to configure alarm settings for the note being
+edited. When the \fIalarm panel\fR is visible, the alarm is activated.
+To turn it off, press the button again and make the panel disappear.
+.Ip "(b)" 4
+.IX Item "(b)"
+This button allows one to cycle through all colors available for notes
+(20 of them). Clicking on it won't close the \fIpanel\fR, so this can be
+done repeatedly. Using the left mouse button traverses the colors in
+ascending, using the right button in descending order. \fINote:\fR colors
+can also be changed via a keyboard shortcut in \fIedit mode\fR (see that
+section).
+.Ip "(c)" 4
+.IX Item "(c)"
+This button closes the \fIpanel\fR and returns to \fIedit mode\fR, with
+the sketch-\fIdraw\fRing feature enabled (see above).
+.Ip "(d)" 4
+.IX Item "(d)"
+Closes the \fIpanel\fR and returns to \fIedit mode\fR, with the
+sketch-\fIeras\fRing feature enabled (see above). Don't panic if entered
+text vanishes all of a sudden when you do this: this is because
+\&\fBwmpinboard\fR intentionally hides it to eradicate the need for you to
+make wild guesses as to what's entered text and which pixels belong to
+a sketch.
+.Ip "(e)" 4
+.IX Item "(e)"
+This button removes all entered text on the current note and places the
+text cursor on the very first character. Besides, it closes the
+\&\fIpanel\fR, thus returning to \fIedit mode\fR.
+.Ip "(f)" 4
+.IX Item "(f)"
+Pressing this button completely removes a drawn sketch on the current
+note and returns to \fIedit mode\fR.
+.Ip "(g)" 4
+.IX Item "(g)"
+This option removes the *entire* note from the board and returns to
+\&\fIpinboard view\fR.
+.Ip "(h)" 4
+.IX Item "(h)"
+This button merely closes the \fIpanel\fR (and thus puts you back in
+\&\fIedit mode\fR). The same can be achieved by simply right-clicking in
+this view.
+.Sh "Alarm panel"
+.IX Subsection "Alarm panel"
+This panel can be accessed from the \fIedit mode panel\fR described above.
+If the panel is visible while the \fIedit mode panel\fR is on, the alarm is
+set, otherwise, it's disabled.
+.PP
+The \fIalarm panel\fR consists of six distinct clickable areas. The
+digits to the left and right of the colon are the hour and minute which
+an alarm is to be set for. Below them, a date can be specified in
+month, day order. On the right, there are two toggle buttons (which
+can be either green (on) or red (off) and are mutually exclusive). The
+top one represents a daily alarm whereas the bottom one indicates/sets
+a date-specific one.
+.PP
+The hour, minute, month, and day of month fields can each be
+incremented or decremented by left- or right-clicking on them,
+respectively. Clicking on one of the toggle buttons configures the
+alarm as the respective type.
+.PP
+As the above description implies, there are two distinct kinds of
+alarms: daily and date-specific ones.
+.PP
+Alarms are generally executed only when in \fIpinboard view\fR and not
+moving any notes about. If you're keeping the program busy at the time
+an alarm would have to occur, it will be delayed until you're finished
+(i.e., let the program return to idle \fIpinboard view\fR). The same holds
+if an alarm occurs while another one is running.
+.PP
+If all prerequisites are given and an alarm becomes due, the
+corresponding note is displayed in edit view, and the display starts to
+flash on and off, along with the alarm command being executed (see
+the section on "OPTIONS"). To stop the blinking, click on the note. From then on,
+the note will be in \fIedit mode\fR.
+.PP
+For daily alarms, the entered date is ignored, and as the name suggests,
+they're run every day at the specified time. To deactivate such an
+alarm, you have to open the \fIedit mode panel\fR and click button (a) to
+make the \fIalarm panel\fR disappear, which turns the alarm off.
+.PP
+In contrast, date-specific alarms are executed only once, at the
+specified day and time. Since a year cannot be specified (explicitly),
+the alarm will be run on this date's next occurrence within a year from
+when the alarm was set. After that, the alarm will automatically be
+disabled. If a date-specific alarm becomes due while \fBwmpinboard\fR
+isn't running, it will be displayed as soon as the program is started
+the next time\*(--which does \fBnot\fR go for daily alarms.
+.PP
+The default mode for alarms is date-specific, and time and date are
+initialized with the next full hour when the alarm panel is opened the
+first time for a given note.
+.PP
+Internally, alarm times are stored in universal format, i.e., if you
+change the time zone after having set an alarm, the time will stay
+universally the same but will differ relative to the new time zone from
+what absolute time you originally set. This behavior is intended.
+.PP
+Finally, it should be mentioned that there are a few limitations with
+respect to command line actions (such as \fB\f(CB\*(C`\-\-add\*(C'\fB\fR, \fB\f(CB\*(C`\-\-del\*(C'\fB\fR,
+\&\fB\f(CB\*(C`\-i\*(C'\fB\fR, etc.). See the section on "RESTRICTIONS".
+.SH "FREQUENTLY ASKED QUESTIONS"
+.IX Header "FREQUENTLY ASKED QUESTIONS"
+.Ip "\(bu" 2
+\&\fIQ:\fR Is a \*(L"pinboard\*(R" this small really of any use?
+.Sp
+\&\fIA:\fR Of course the limited size imposes certain restrictions, but if
+you think about it, you'll agree that a real life pinboard reminds you
+of things by the mere existence of notes being pinned to it. In order
+to read what they say, you have to step close and, possibly, detach the
+note.
+.Sp
+Quite similarly, \fBwmpinboard\fR reminds you of things by facing you with
+colored representations of notes on your screen. To find out what it
+was you intended them to remind you of, all you have to do is click on
+a note, which will then be displayed full size. Furthermore, the alarm
+feature introduced in a later version allows for even more explicit
+reminders and thus renders \fBwmpinboard\fR even more powerful in a way
+than any real-life cork board. \fB\f(CB\*(C`:\-)\*(C'\fB\fR
+.Sp
+By choosing from a variety of possible colors, you can assign
+particular colors to certain kinds of reminders, which may further
+enhance \fBwmpinboard\fR's usability. Moreover, you can place certain notes
+on certain areas of the board to emphasize their category, urgency,
+etc. It's up to you what to make of it.
+.Sp
+Finally, by adding drawing capabilities, I've definitely overcome the
+contents quantity barrier imposed by the maximum number of 59
+characters, for as everyone knows, a picture is worth more than a
+thousand words. *grin*
+.Ip "\(bu" 2
+\&\fIQ:\fR I don't live in an English-speaking country, so what about extended
+characters (umlauts, accents, cyrillic alphabet)?
+.Sp
+\&\fIA:\fR \fBwmpinboard\fR allows you to use an arbitrary 8bit X font, provided
+that its characters are of a fixed size of 6x10 (or, deprecated but
+possible, anything <= 7x10) pixels. The default font is \*(L"6x10\*(R" (more
+precisely, it's called
+\&\*(L"\-misc-fixed-medium-r-normal\*(--10\-100\-75\-75\-c-*\-ISO8859\-1\*(R"), an
+\&\s-1ISO8859\-1\s0 (Latin1) font which should be part of every XFree
+installation.
+.Sp
+In order to make \fBwmpinboard\fR use another font, run it as
+.Sp
+.Vb 1
+\& $ wmpinboard --font=FONT
+.Ve
+where \fB\s-1FONT\s0\fR is either a shortcut for a compiled-in font name (see
+the section on "OPTIONS" for a list of those) or a valid, complete X font
+descriptor. This is a configuration directive, meaning that no other
+instance of \fBwmpinboard\fR may be running at the time. Note that this
+\&\fIonly works if there are no more notes on the board\fR. It's
+intentionally been made impossible to change the font while there are
+notes saved in \fBwmpinboard\fR's data file, since this might result in
+garbage being displayed. Of course even a font specified via a
+shortcut has to exist on your system in order to be usable.
+.Sp
+If a configured custom font cannot be loaded or has invalid dimensions,
+\&\fBwmpinboard\fR will try to revert. Note that this won't affect the font
+name saved along with the data, though.
+.Ip "\(bu" 2
+\&\fIQ:\fR How can I disable those vexing, superfluous animations?
+.Sp
+\&\fIA:\fR
+.Sp
+.Vb 1
+\& $ wmpinboard --light
+.Ve
+.Ip "\(bu" 2
+\&\fIQ:\fR Why aren't those animations smooth all of the time? Sometimes it
+looks like they're being skipped entirely.
+.Sp
+\&\fIA:\fR This presumably is a multitasking issue: depending on the current
+system load and \fBwmpinboard\fR's/the X server's recent \s-1CPU\s0 usage
+history, it may take a moment until the scheduling has been adapted to
+the suddenly increased \s-1CPU\s0 load implied by displaying the animation,
+and short as it is, it may already be finished until this has happened,
+i.e., it's the X server lagging behind in updating the program's
+display if \fBwmpinboard\fR's been idle for some time prior to that. It
+may sound paradoxical, but the effect is the more likely to show the
+lower the system's load is. I don't see a way to avoid this
+effect\*(--either this, or you turn off animations altogether.
+.Ip "\(bu" 2
+\&\fIQ:\fR When I leave \fBwmpinboard\fR idle in edit mode for some time, edit
+mode is terminated automatically. Is that intended?
+.Sp
+\&\fIA:\fR Yes. After 60 idle seconds (that's the default; see
+the section on "OPTIONS") in edit mode (no mouse click and no keyboard input), edit
+mode is terminated automatically. If the note being edited happens to
+be blank, it will be discarded (or removed if an existing note is being
+edited).
+.Sp
+This timeout can, however, be adjusted according to your preferences or
+turned off using the \fB\f(CB\*(C`\-t\*(C'\fB\fR parameter. See the section on "OPTIONS" for this.
+.Ip "\(bu" 2
+\&\fIQ:\fR When does \fBwmpinboard\fR save its data?
+.Sp
+\&\fIA:\fR Notes data is saved on each of these occasions:
+.RS 2
+.Ip "\(bu" 2
+whenever edit mode is terminated
+.Ip "\(bu" 2
+when you switch notes in edit mode (via [Shift]\-[arrow\ key])
+.Ip "\(bu" 2
+when a note has been moved on the board
+.Ip "\(bu" 2
+when an interactive instance is running and you run \fBwmpinboard\fR from
+the command line, making it dump, add, or delete notes
+.Ip "\(bu" 2
+when killed via \s-1SIGINT\s0 or \s-1SIGTERM\s0 and edit mode is active
+.RE
+.RS 2
+.Sp
+Notes are saved to a file called \fI.wmpinboarddata\fR in your home
+directory (see the section on "FILES").
+.RE
+.Ip "\(bu" 2
+\&\fIQ:\fR I've tried my best and littered the entire pinboard with quite a
+lot of notes. Now I can't seem to be able to add another one.
+.Sp
+\&\fIA:\fR There's a compile-time limit of 20 notes. I think more notes on
+this tiny a board really don't make any sense.
+.Ip "\(bu" 2
+\&\fIQ:\fR I've explicitly configured my window manager for click-based
+rather than mouse-following focus, but \fBwmpinboard\fR's focus follows
+the mouse regardless. Can I change this?
+.Sp
+\&\fIA:\fR By default, \fBwmpinboard\fR actively claims the keyboard input
+focus (if it's in note edit mode) whenever the pointer is moved over the
+application's area. Since \fBwmpinboard\fR is a dock applet, i.e., a
+withdrawn rather than a \*(L"real\*(R" X window, it can't be assigned a focus
+in the same way as to the latter ones. However, running \fBwmpinboard\fR
+with the parameter \fB\f(CB\*(C`\-c\*(C'\fB\fR will make it emulate some sort of
+click-based focusing. This means, it actively claims the keyboard
+focus only on these occasions:
+.RS 2
+.Ip "\(bu" 2
+when a new note is created (\fInot\fR when you click on an existing
+note\*(--you'll have to explicitly click on the editing area to make it
+claim focus; this way, you can just view a note without any change to
+keyboard focus)
+.Ip "\(bu" 2
+when you click somewhere on the text area in edit mode
+.RE
+.RS 2
+.Sp
+Once keyboard-focused, \fBwmpinboard\fR will keep it until another window
+is explicitly focused (usually by clicking on its title bar or
+border). To focus \fBwmpinboard\fR again when it's lost focus, you'll
+have to click on its text area in edit mode. This click will only
+focus the application and not have the usual cursor-positioning effect.
+.Sp
+This feature is to be considered experimental since I'm not sure of how
+much use it really is. A mouse-following focus is the recommended mode
+of operation.
+.RE
+.Ip "\(bu" 2
+\&\fIQ:\fR I've noticed that after a while, some sort of darker stains
+appear on my notes. Is that a bug in some drawing routine?
+.Sp
+\&\fIA:\fR No, this is not a bug. These \*(L"stains\*(R" are meant to resemble
+creases, caused by frequent handling of a particular note (wear & tear,
+you see?). In fact, they're added with a certain probability whenever
+you view a note by clicking on it (note that leafing through notes via
+[Shift]\-[arrow\ keys] is \fInot\fR affected), when you clear its textual
+or drawn contents via the \fIedit mode panel\fR (very outwearing, that
+\&\fB;\-)\fR\ ), and when a note is moved. This feature can be disabled at
+compile time by running \fIconfigure\fR with \f(CW\*(C`\-\-disable\-creases\*(C'\fR as a
+parameter.
+.Sp
+To prevent the question, no, worn-out notes cannot be ironed to get rid
+of the creases. Sorry. \fB\f(CB\*(C`:\-p\*(C'\fB\fR
+.Ip "\(bu" 2
+\&\fIQ:\fR Is \fBwmpinboard\fR compatible with \fBAfterStep\fR's \fBWharf\fR?
+.Sp
+\&\fIA:\fR \fBwmpinboard\fR tries to autodetect whether \fBWindow Maker\fR is
+running and sets itself up accordingly. If this doesn't work for you
+for some reason, you can explicitly make it run in either Withdrawn- or
+NormalState using the \fB\f(CB\*(C`\-w\*(C'\fB\fR or \fB\f(CB\*(C`\-n\*(C'\fB\fR flag, respectively. See
+the section on "OPTIONS".
+.Sp
+Swallowing evidently works with \fBAfterStep\fR 1.6.10; I don't know about
+earlier versions. A \fBWharf\fR config line you might try is this:
+.Sp
+.Vb 1
+\& *Wharf wmpinboard nil MaxSwallow "wmpinboard" wmpinboard &
+.Ve
+Besides, \fBwmpinboard\fR has been reported to work with \fBBlackbox\fR.
+.Ip "\(bu" 2
+\&\fIQ:\fR I have X running at a color depth of 8 bits, i.e., in palette
+mode, and \fBwmpinboard\fR obviously requires too many colors and thus
+looks real messy (or doesn't run at all, complaining about \*(L"not enough
+free color cells\*(R"). What can I do about this?
+.Sp
+\&\fIA:\fR As of version 0.99.1, the recommended solution is to upgrade
+whatever component of your system restricts you to a palette mode.
+.Ip "\(bu" 2
+\&\fIQ:\fR Can I run multiple instances of \fBwmpinboard\fR as the same user,
+simultaneously?
+.Sp
+\&\fIA:\fR No, this is certainly not a good idea. The run-time behavior may
+be unpredictable, and your data file can get corrupted. Therefore, any
+\&\fBwmpinboard\fR process that's to be run interactively first checks
+whether another interactive instance is running, and if so, refuses to
+run.
+.Ip "\(bu" 2
+\&\fIQ:\fR I've just upgraded from a pre-0.7 version of \fBwmpinboard\fR and
+noticed that its data file format has changed completely since. Is
+there a way to upgrade and yet keep my existing notes?
+.Sp
+\&\fIA:\fR There's a \fBPerl\fR script doing the conversion included with the
+distribution (the source one, anyway). If your package didn't include
+that, feel free to mail to the author (see the section on "AUTHOR" at the end of
+this documentation).
+.Ip "\(bu" 2
+\&\fIQ:\fR I find a mere 59 characters per note to be a real limitation.
+How about making \fBwmpinboard\fR pop up an external window with more room
+for that? Or how about assigning that job to an external editor?
+.Sp
+\&\fIA:\fR There's a variety of comprehensive programs for keeping notes out
+there, offering this functionality but being rather heavy-weight since
+they are linked against one \s-1GUI\s0 library or another (\fBCoolNotes\fR or
+\&\fBKNotes\fR come to mind). On the other hand, I couldn't find a
+\&\fB\s-1WM\s0\fR\-conforming reminder I could omnipresently stick to my desktop
+anywhere, so I wrote \fBwmpinboard\fR. I wanted it to be small, neat,
+easy to use, and yet useful in a way.
+.Sp
+I hope that's about what the program is currently like. And I'd prefer
+to keep it like that rather than inflate it by linking against a \s-1GUI\s0
+library\*(--eventually, the note editing code would outweigh the rest of
+the application by a factor, and people would probably still create
+notes mostly shorter than 60 characters. If you restrict your memos to
+keywords and abbreviations, that's quite a lot.
+.Sp
+I want \fBwmpinboard\fR to remain an applet in that it doesn't open up
+external windows and use (if just temporarily) additional desktop
+space. I explicitly wrote it to have something omnipresent at a fixed
+position on my desktop. I find it preferable to have the notes pop up
+in place rather than somewhere else on the screen.
+.Sp
+Personally, I use other programs for larger notes too; \fBwmpinboard\fR
+has been designed for things smaller in size and greater in urgency,
+it's in no way meant to be a comprehensive knowledge base application
+of any kind.
+.Sp
+Summing up, I think a dock applet should be small both regarding its
+on-screen representation and the resources it uses. That's why I
+don't intend to add any pop-up dialogs or similar things to
+\&\fBwmpinboard\fR.
+.Ip "\(bu" 2
+\&\fIQ:\fR I've tried the program, yet I can't help finding it utterly
+useless. What shall I do?
+.Sp
+\&\fIA:\fR The solution is simple. Just don't use it.
+.Ip "\(bu" 2
+\&\fIQ:\fR Will your answer to this question be \*(L"no\*(R"?
+.Sp
+\&\fIA:\fR Nope.
+.SH "HINTS"
+.IX Header "HINTS"
+.Ip "\(bu" 2
+A good way of making the best of the organizational features offered by
+\&\fBwmpinboard\fR is to use certain colors and locations on the pinboard to
+indicate urgency and contents of a note. For example, you might use
+each of the color groups for a certain kind of reminder, because that
+enables you to leaf through all notes with related contents via
+[Shift]\-[arrow\ keys] in edit mode. Besides, you might assign each
+corner of the board a specific urgency, altogether allowing you to
+derive a note's type from its color and its urgency from its location
+on the board. Thanks again to the ability to leaf through all notes
+belonging to the same group of colors, notes with similar contents will
+still be clustered in a way.
+.SH "UNDOCUMENTED FEATURES"
+.IX Header "UNDOCUMENTED FEATURES"
+This piece of documentation doesn't cover any undocumented features.
+.SH "FILES"
+.IX Header "FILES"
+.Ip "\fI~/.wmpinboarddata\fR" 2
+.IX Item "~/.wmpinboarddata"
+the user's \fBwmpinboard\fR data file
+.Ip "\fI~/.wmpinboarddata.new\fR" 2
+.IX Item "~/.wmpinboarddata.new"
+temporary file created momentarily when saving data
+.SH "ENVIRONMENT VARIABLES"
+.IX Header "ENVIRONMENT VARIABLES"
+.Ip "$HOME" 2
+.IX Item "$HOME"
+the user's home directory
+.SH "RESTRICTIONS"
+.IX Header "RESTRICTIONS"
+.Ip "\(bu" 2
+\&\fBwmpinboard\fR relies on a dock app tile size of at least 64x64 pixels.
+In fact, using smaller tiles renders the applet rather useless, as,
+e.g., opening the edit mode panel becomes impossible.
+.Ip "\(bu" 2
+Unpredictable effects may be the results if a command line action is
+taken while an alarm is running and others are due simultaneously.
+If just a single alarm is active and no others are due, that alarm will
+be cancelled when the two instances of \fBwmpinboard\fR synchronize.
+If more instances are due during the process, a race conditions arises
+that can't be solved satisfactorily due to the program's internal
+structure and organization. Still, in theory, nothing critical will
+happen, and the most you'll lose will be an alarm or two.
+.Ip "\(bu" 2
+\&\fBwmpinboard\fR data files are not designed to be portable across
+architectures. Due to differences in data type representations that
+are likely otherwise, a datafile can only be reliably used by program
+binaries running on machines of the same architecture.
+.Ip "\(bu" 2
+Certainly of no interest to anyone, but mentioned for the sake of
+completeness: \fBwmpinboard\fR's alarm features will start to fail past
+04:14:07 on Jan 19, 2037, which is due to the legacy Un*x time format.
+.SH "BUGS"
+.IX Header "BUGS"
+If you stumble on any bugs, feel free to mail the author. The same
+goes if you encounter any problems running/using the program. Be sure
+to include any information you consider relevant, i.e., at a minimum,
+the version of \fBwmpinboard\fR you're using as well as your \s-1OS\s0 and X
+versions.
+.PP
+Also, further suggestions are always welcome. Please check the \fI\s-1TODO\s0\fR
+file that's part of the distribution to see if what you're about to
+suggest isn't already on my \*(L"to do\*(R" list, or has been suggested earlier
+and was rejected for one reason or other.
+.SH "SEE ALSO"
+.IX Header "SEE ALSO"
+\&\fIwmaker\fR\|(1)
+.SH "AUTHOR"
+.IX Header "AUTHOR"
+\&\fBwmpinboard\fR is copyrighted (c) 1998\-2000 by Marco Go\*:tze,
+<gomar at mindless.com>. It is distributed under the terms of the
+\&\s-1GNU\s0 General Public License, revision 2 or any later revision thereof.
+Use at your own risk.
+.PP
+New releases of and themes for \fBwmpinboard\fR can be found at
+<http://www.tu-ilmenau.de/~gomar/stuff/wmpinboard/>, or
+that was true at least by the time this document was last updated.
diff --git a/man/wmpinboard.pod b/man/wmpinboard.pod
new file mode 100644
index 0000000..0e85b40
--- /dev/null
+++ b/man/wmpinboard.pod
@@ -0,0 +1,1022 @@
+=head1 NAME
+
+B<wmpinboard> - a B<Window Maker> dock app resembling a miniature pinboard
+
+=head1 SYNOPSIS
+
+ wmpinboard [options]
+
+=head2 What wmpinboard is
+
+B<wmpinboard> is a B<Window Maker> dock applet resembling a miniature
+pinboard. It's intended to somewhat relieve heavily littered desktops
+by allowing you to place reminders on a graphical on-screen pinboard
+rather than producing a mess of real notes all around your keyboard
+(thus being environmentally A Good Thing, too B<;-)> ). It supports
+arbitrary 6x10 X fonts and has XLocale support, enabling you to enter
+locale-dependent special characters if set up appropriately. Besides
+text, you can add small monochrome sketches to your notes or simply
+encircle or underline words as a means of emphasis, and alarms can be
+set to explicitly remind you of things. Above all, B<wmpinboard> is
+animated in redundant ways to make it look even more attractive, and
+themeability provides for a way of adapting its appearance to that of
+the rest of your desktop.
+
+=head2 What wmpinboard ISN'T
+
+Clearly, B<wmpinboard> doesn't allow you to keep an unlimited number of
+notes holding arbitrary amounts of information, and that's not what
+it's meant to do. Just as real notes offer limited space, so do those
+simulated by B<wmpinboard>. Besides, as a dock applet, it aims at
+being small and neat and yet useful in a way, and that's what it is,
+too, or considered to be by some people, anyway. If you need room for
+more comprehensive reminders, use another program, either additionally
+or exclusively. There's a variety of such out there, but their niche
+is different from that which B<wmpinboard> claims.
+
+=head1 OPTIONS
+
+B<wmpinboard>'s command-line options can be roughly divided into four
+groups: configuration directives, run-time options, command-line
+actions, and general options. Generally, B<wmpinboard> supports
+GNU-style long options (which may be abbreviated unambiguously) as well
+as short ones for parameters used more commonly.
+
+=head2 Configuration directives
+
+This type of command-line options changes some aspect of
+B<wmpinboard>'s configuration that is saved along with the data and
+thus set in a more permanent way. Only one such parameter may be
+specified per call, and there mustn't be another instance running.
+
+=over 2
+
+=item B<C<--font=FONT>>
+
+Makes B<wmpinboard> use the specified font; B<C<FONT>> can be one of
+the shortcuts listed when running the program with "B<C<--help>>" as a
+parameter, or a complete X descriptor of a fixed size 6x10 font. The
+pinboard I<must be empty> in order for this option to be applicable.
+For more details, see L<"FREQUENTLY ASKED QUESTIONS">.
+
+=item B<C<--theme=FILE>>
+
+Configures B<wmpinboard> to load the specified theme when started
+interactively the next time. B<C<FILE>> is the location of a
+B<wmpinboard> theme file (typically with a file name extension of
+F<.wmpbtheme>). If it can't be loaded when run interactively, the
+program will revert to its default theme. If B<C<FILE>> is an empty
+string or "default", the use of a custom theme will be deactivated.
+
+Themes affect B<wmpinboard>'s appearance, in particular, its pinboard,
+edit mode and alarm panel pixmaps, the latter's digits, and possibly
+the location of the pinboard mode label area (via which notes are
+created). For downloading themes, or if you're inclined to create one
+yourself and need instructions, check out the program's home page (see
+L<"AUTHOR"> or B<wmpinboard>'s "B<C<--help>>" output for the URL). The
+themes kit containing instructions and samples on how to create theme
+files for B<wmpinboard> that can be downloaded there is also included
+with the source package of the program.
+
+=item B<C<--alarm-cmd=CMD>>
+
+Configures B<C<CMD>> as the command to be executed on alarms. E.g.,
+you could use "C<xkbbell>" to cause the program to beep on such
+occassions, or make it run some sound-playing program. To reset the
+alarm command to none, make B<C<CMD>> a zero-length string.
+
+=back
+
+=head2 Run-time options
+
+=over 2
+
+=item B<C<-d DISPLAY>> or B<C<--display=DISPLAY>>
+
+Uses the specified X display rather than the default one.
+
+=item B<C<-c>> or B<C<--click-to-focus>>
+
+This turns on some emulation of a click-based keyboard focus mode. See
+L<"FREQUENTLY ASKED QUESTIONS">.
+
+=item B<C<-t TIME>> or B<C<--timeout=TIME>>
+
+Sets the edit mode timeout (i.e., the number of seconds of idleness
+after which edit mode is terminated automatically) to B<C<TIME>>
+seconds. The compile-time default is 60s, but this may have been
+changed for your particular build; run with B<C<-v>> if in doubt to
+check that. Specifying a value of 0 (zero) will disable the timeout.
+
+=item B<C<-n>> or B<C<--normal-state>>
+
+Forces B<wmpinboard> to run in so-called NormalState, which is
+preferred by B<AfterStep>'s B<Wharf>.
+
+=item B<C<-w>> or B<C<--withdrawn-state>>
+
+Forces the program to run in so-called WithdrawnState, as desired by
+the B<Window Maker> dock. This option and the previous one are mutually
+exclusive. Note also that B<wmpinboard> tries to auto-detect whether
+B<Window Maker> is running and sets itself up accordingly. Using B<C<-n>>
+or B<C<-w>> should only be necessary in case those heuristics fail on
+your system for some reason or other.
+
+=item B<C<--light>>
+
+Use this switch to suppress animations.
+
+=back
+
+=head2 Command-line actions
+
+Even though B<wmpinboard> is by design an interactive application,
+there may be occasions when it comes in handy to be able to
+access/manipulate notes from the command line. That's why the program
+offers a set of command-line options allowing for basic operations of
+that kind. Still, it should be kept in mind that B<wmpinboard> is
+primarily meant to be used interactively.
+
+All of the options below will, if an interactive instance of
+B<wmpinboard> is running in the background, cause that to save its data
+(and quit I<edit mode>, if necessary), and if any changes are made by
+the respective option, the interactive instance will then be signalled
+to re-read the data file. Even though the implemented methods of
+inter-process communication should generally be sufficiently safe
+with respect to avoiding data file corruption, it's in theory possible
+to undermine the concept and cause damage that way--yet this won't
+happen unless you deliberately take pains to achieve the goal.
+Generally, everything should work fine as long as you don't try running
+multiple non-interactive instances of B<wmpinboard> simultaneously.
+
+Only one of the below actions can be specified per call to B<wmpinboard>.
+
+=over 2
+
+=item B<C<--dump>>
+
+This dumps the contents of all notes, replacing line breaks by spaces
+(unless preceded by a hyphen) and shortening sequences of blanks. The
+list of dumped strings will be sorted by color groups. If you use
+special characters in your notes, make sure your terminal's running
+with the same character set as B<wmpinboard>, or what you'll see might
+have a garbage-like quality.
+
+Each line of output represents one note and is prefixed by the internal
+number I<currently> identifying the respective note and, if an alarm is
+configured for the respective note, time and date (or "daily").
+
+=item B<C<--dump-raw>>
+
+Unlike the "cooked" dump described above, this just dumps the raw
+contents of all notes without applying any kind of formatting. May
+come in handy if your notes happen to contain E-mail addresses or other
+things for which lines 10 characters wide are too narrow.
+
+=item B<C<--del=NUMBER>>
+
+This option will remove the note identified by B<C<NUMBER>> from the
+pinboard. B<C<NUMBER>> is a number determined by the output of either
+dump option, which should be consulted right before using this one,
+since note numbers may change when notes are moved around on the board
+or others are removed.
+
+=item B<C<--add=STRING>>
+
+When run with this option, B<wmpinboard> will add a new note (provided
+the maximal number of notes has not yet been reached) at a random
+position on the board, with contents B<C<STRING>>, word-wrapping the
+text at the end of the note's lines where necessary (after white space
+and hyphens). If due to this wrapping, the entire string cannot be
+stored on the note, the remainder will be discarded silently.
+
+In order to create a note with a certain color, the string can be
+prefixed by a color code specifying the group of colors which a random
+color is to be selected from (code letters are recognized
+case-insensitively):
+
+ %G - green
+ %Y - yellow/white
+ %R - reddish
+ %B - blue
+
+Alternatively or additionally, you may specify a position code as "%1"
+through "%9", defining an approximate position on the board where the
+note is to be placed. Each of the nine figures corresponds to a ninth
+of the board with its index assigned in accordance with the layout of
+your keypad (i.e., "%1" meaning lower left, "%9" upper right corner,
+and so forth).
+
+Thus,
+
+ wmpinboard --add '%g%5test'
+
+will place a green note saying "test" at the center of the board.
+
+(Note: The "%" character can be escaped by a second one if you want to
+add an un-prefixed string starting with a percent character.)
+
+=item B<C<--add-raw=STRING>>
+
+Via this option, a new note can be added from the command line
+(provided that this won't exceed the maximum number of notes).
+B<C<STRING>> specifies the I<raw> contents of the note, as printed by
+B<C<--dump-raw>>. The same set of color group and position codes as
+for the previous option applies.
+
+=back
+
+=head2 General options
+
+=over 2
+
+=item B<C<-h>> or B<C<--help>>
+
+This prints a help screen listing command line options together with
+brief descriptions.
+
+=item B<C<-i>> or B<C<--info>>
+
+Prints information about the current user configuration (font, theme,
+alarm command) and some useless statistics.
+
+=item B<C<-v>> or B<C<--version>>
+
+This prints some more detailed version information, in particular,
+which compile-time settings this binary was built with.
+
+=back
+
+=head1 DESCRIPTION
+
+B<wmpinboard> operates in basically two different modes, namely, the
+I<pinboard view> and I<edit mode>. Furthermore, a I<panel> of buttons
+granting access to extended options can be popped up in I<edit mode>,
+which in turn allows you to display the I<alarm panel> to configure
+alarm settings for the current note.
+
+=head2 Pinboard view
+
+This is B<wmpinboard>'s normal mode of operation. A potentially
+chaotic arrangement of tiny squares on a beige-colored oblong (default
+theme) is meant to resemble notes pinned to a cork board. Possible
+actions include:
+
+=over 2
+
+=item *
+
+I<Add> a note, by left-clicking on the board's "TO DO" label. This
+creates a new, blank, randomly-colored note at a random position on the
+board and puts B<wmpinboard> in I<edit mode> (see below). If you
+prefer to place a new note at a certain position before being prompted
+to enter its contents, this can be done by moving the mouse cursor
+after having clicked on the label. This will realize the note and
+allow you to drag it to a position of your choice. B<wmpinboard> will
+switch to I<edit mode> as soon as you release the mouse button.
+
+=item *
+
+I<Edit>/I<view> a note, by left-clicking on a note. This switches
+to I<edit mode> (described below).
+
+=item *
+
+I<Move> a note, by dragging it using the right mouse button.
+This also raises the note in question on top of all others.
+Depending on its horizontal position, the note will be tilted
+automatically. As a side-effect, a single brief right-click
+can be used to raise a note on top of overlapping ones without
+moving it.
+
+By dragging a note with the left mouse button, you can move it without
+changing its level with respect to other notes, i.e., without raising
+it on top of all others.
+
+=back
+
+=head2 Edit mode
+
+This mode serves two purposes: on the one hand, it presents you with a
+"full-size" view of a note's contents, on the other, you can use the
+occasion to edit it. Due to its limited size, a note can hold up to 10
+characters on 6 lines (minus one on the last, i.e., 59 characters
+altogether), plus a monochrome sketch of some kind. Possibly actions:
+
+=over 2
+
+=item *
+
+I<Enter> text. B<wmpinboard> supports user-selectable fonts and dead
+keys, so you should be able to enter any characters that are usually
+accessible via your keyboard and have them displayed correctly.
+Furthermore, the cursor can be moved around using the arrow keys (or
+EMACS-style via [Ctrl]-[N]/[P]/[F]/[B], if you are so inclined).
+Alternatively, it can be placed explicitly by left-clicking where you
+want it to be. Other special keys that are supported include:
+
+=over 2
+
+=item [PgUp]/[PgDn]
+
+Places the cursor on character 1/59, respectively.
+
+=item [Home]/[End]
+
+Places the cursor at the textual start or end of the current line.
+
+=item [Del]
+
+Deletes the character currently under the text cursor and shifts the
+remaining text on the current line to the left; if the current line is
+blank, removes it and shifts all lines below up by one line.
+
+=item [Backspace]
+
+See [Del], but affects the character on the left of the cursor.
+
+=item [Ins]
+
+Toggles inserting/overwriting of existing text; the current mode is
+indicated by a cursor change (block cursor means insert mode).
+
+=item [Enter]
+
+In insert mode, wraps the current line at the cursor's position; in
+overwrite mode (underscore cursor), merely moves the cursor to the
+start of the next line.
+
+=item [Ctrl]-[Y], -[Z]
+
+Removes an entire (intermediate) line, shifting those below up by one,
+and places the cursor at the start of the current line.
+
+=item [Esc]
+
+Quits I<edit mode> and returns to the I<pinboard view>.
+
+=item [Shift]-[Left]/[Right]
+
+Cycles through all notes currently on the pinboard.
+
+=item [Shift]-[Up]/[Down]
+
+Cycles through all notes that are I<roughly> the same color as the
+current one. For this purpose, colors have internally been divided
+into four groups: green, white/yellow, reddish, blue.
+
+=item ([Shift]-)[Tab]
+
+Cycles (backwards) through availabe note colors.
+
+=back
+
+=item *
+
+I<Cut'n'paste> text. Despite the limitations implied, B<wmpinboard>
+has support for cutting & pasting to and from the X clipboard:
+
+=over 2
+
+=item *
+
+In order to copy text to the clipboard, select the desired segment via
+either the left or the right mouse button: the left one will copy the
+text post-formatted as done by the command line switch B<C<--dump>>
+(see L<"OPTIONS">); the right button will copy the raw selection.
+Similarly, a left double click will select the word (i.e., all
+adjoining non-blank characters) at the respective position, a right one
+will do the same but neglect line breaks as delimiters. Additionally,
+you can copy a note's entire raw contents by pressing S<[Ctrl]-[R]>;
+S<[Ctrl]-[C]> will do the same with applied post-formatting.
+
+=item *
+
+To paste the clipboard's contents, press the middle button wherever the
+insertion is supposed to happen. This will insert the clipboard's
+current contents, trying to word-wrap the text (at white space and
+after hyphens). If in insert mode, following text will be shifted
+towards the end of the note, trying to word-wrap that as well.
+
+If you wish to paste something without word-wrapping (e.g., an URL
+containing a hyphen), paste it via S<[Ctrl]-[I]> (think [i]nsert). This
+will paste the clipboard's raw contents at the current location of the
+cursor, shifting subsequent text if in insert mode (not trying to
+word-wrap that either).
+
+=back
+
+Obvious limitations you should be aware of include:
+
+=over 2
+
+=item *
+
+As is usually the case (about B<wmpinboard>, anyway), size matters.
+As you know, a note can hold only up to 59 characters, so trying to
+paste longer strings will result in those being truncated.
+
+=item *
+
+If the text to be pasted is formatted in some way or other, this won't
+be the case any more after it has been pasted: B<wmpinboard> replaces
+new line characters by blanks and, when pasting using the mouse, tries
+to word-wrap text.
+
+=item *
+
+The information stored in the cut buffer needn't necessarily be
+compatible with B<wmpinboard> in that it may be encoded with another
+character set.
+
+=back
+
+=item *
+
+I<Leave> I<edit mode>. This is achieved by left-clicking on the
+triangle in the lower right-hand side corner. If the note is
+completely empty, it will be removed from the board. In any
+case, this returns to the I<pinboard view>.
+
+=item *
+
+I<Pop up> a I<panel> with some further options to choose from. This
+is done by right-clicking on the aforementioned triangle. To
+learn what the I<panel> is there for, see the corresponding section
+below.
+
+=item *
+
+I<Draw> a sketch. This mode can be activated via the I<panel>, and
+deactivated by either right-clicking somewhere on the note
+or opening the I<panel> again. While in drawing mode, the mouse
+pointer is pencil-shaped, and drawing can be done by keeping
+the left mouse button pressed and dragging the mouse, just as
+you'd expect. Sketch and text may overlap each other, but keyboard
+input is ignored while in drawing mode.
+
+=item *
+
+I<Erase> a sketch. Just like DRAWing mode, this mode is entered
+via the I<panel>, and can be quit just like the former. In erase
+mode, the text is hidden, so you needn't guess whether a pixel
+belongs to an entered character or a drawn sketch. Note that
+the erase cursor's point is slightly larger than the one used
+when drawing.
+
+=back
+
+Note: B<wmpinboard> remembers where you left the text cursor after
+you last edited a note and restores this position when you edit it the
+next time.
+
+=head2 Edit mode panel
+
+This I<panel> is intended to provide easy access to some options
+affecting I<edit mode> or the current note in general. The I<panel>
+looks like this (letters denoting the buttons for reference below):
+
+ +---+---+---+---+
+ | a | c | e | g |
+ +---+---+---+---+
+ | b | d | f | h |
+ +---+---+---+---+
+
+The buttons bear tiny icons which are meant to suggest what they do,
+which isn't all that easy on a 12x12 pixels area. B<C<:^)>>
+
+Here's a description of what each button does:
+
+=over 4
+
+=item (a)
+
+Left-clicking on this button opens and closes the I<alarm panel> (see
+below), which allows you to configure alarm settings for the note being
+edited. When the I<alarm panel> is visible, the alarm is activated.
+To turn it off, press the button again and make the panel disappear.
+
+=item (b)
+
+This button allows one to cycle through all colors available for notes
+(20 of them). Clicking on it won't close the I<panel>, so this can be
+done repeatedly. Using the left mouse button traverses the colors in
+ascending, using the right button in descending order. I<Note:> colors
+can also be changed via a keyboard shortcut in I<edit mode> (see that
+section).
+
+=item (c)
+
+This button closes the I<panel> and returns to I<edit mode>, with
+the sketch-I<draw>ing feature enabled (see above).
+
+=item (d)
+
+Closes the I<panel> and returns to I<edit mode>, with the
+sketch-I<eras>ing feature enabled (see above). Don't panic if entered
+text vanishes all of a sudden when you do this: this is because
+B<wmpinboard> intentionally hides it to eradicate the need for you to
+make wild guesses as to what's entered text and which pixels belong to
+a sketch.
+
+=item (e)
+
+This button removes all entered text on the current note and places the
+text cursor on the very first character. Besides, it closes the
+I<panel>, thus returning to I<edit mode>.
+
+=item (f)
+
+Pressing this button completely removes a drawn sketch on the current
+note and returns to I<edit mode>.
+
+=item (g)
+
+This option removes the *entire* note from the board and returns to
+I<pinboard view>.
+
+=item (h)
+
+This button merely closes the I<panel> (and thus puts you back in
+I<edit mode>). The same can be achieved by simply right-clicking in
+this view.
+
+=back
+
+=head2 Alarm panel
+
+This panel can be accessed from the I<edit mode panel> described above.
+If the panel is visible while the I<edit mode panel> is on, the alarm is
+set, otherwise, it's disabled.
+
+The I<alarm panel> consists of six distinct clickable areas. The
+digits to the left and right of the colon are the hour and minute which
+an alarm is to be set for. Below them, a date can be specified in
+month, day order. On the right, there are two toggle buttons (which
+can be either green (on) or red (off) and are mutually exclusive). The
+top one represents a daily alarm whereas the bottom one indicates/sets
+a date-specific one.
+
+The hour, minute, month, and day of month fields can each be
+incremented or decremented by left- or right-clicking on them,
+respectively. Clicking on one of the toggle buttons configures the
+alarm as the respective type.
+
+As the above description implies, there are two distinct kinds of
+alarms: daily and date-specific ones.
+
+Alarms are generally executed only when in I<pinboard view> and not
+moving any notes about. If you're keeping the program busy at the time
+an alarm would have to occur, it will be delayed until you're finished
+(i.e., let the program return to idle I<pinboard view>). The same holds
+if an alarm occurs while another one is running.
+
+If all prerequisites are given and an alarm becomes due, the
+corresponding note is displayed in edit view, and the display starts to
+flash on and off, along with the alarm command being executed (see
+L<"OPTIONS">). To stop the blinking, click on the note. From then on,
+the note will be in I<edit mode>.
+
+For daily alarms, the entered date is ignored, and as the name suggests,
+they're run every day at the specified time. To deactivate such an
+alarm, you have to open the I<edit mode panel> and click button (a) to
+make the I<alarm panel> disappear, which turns the alarm off.
+
+In contrast, date-specific alarms are executed only once, at the
+specified day and time. Since a year cannot be specified (explicitly),
+the alarm will be run on this date's next occurrence within a year from
+when the alarm was set. After that, the alarm will automatically be
+disabled. If a date-specific alarm becomes due while B<wmpinboard>
+isn't running, it will be displayed as soon as the program is started
+the next time--which does B<not> go for daily alarms.
+
+The default mode for alarms is date-specific, and time and date are
+initialized with the next full hour when the alarm panel is opened the
+first time for a given note.
+
+Internally, alarm times are stored in universal format, i.e., if you
+change the time zone after having set an alarm, the time will stay
+universally the same but will differ relative to the new time zone from
+what absolute time you originally set. This behavior is intended.
+
+Finally, it should be mentioned that there are a few limitations with
+respect to command line actions (such as B<C<--add>>, B<C<--del>>,
+B<C<-i>>, etc.). See L<"RESTRICTIONS">.
+
+=head1 FREQUENTLY ASKED QUESTIONS
+
+=over 2
+
+=item *
+
+I<Q:> Is a "pinboard" this small really of any use?
+
+I<A:> Of course the limited size imposes certain restrictions, but if
+you think about it, you'll agree that a real life pinboard reminds you
+of things by the mere existence of notes being pinned to it. In order
+to read what they say, you have to step close and, possibly, detach the
+note.
+
+Quite similarly, B<wmpinboard> reminds you of things by facing you with
+colored representations of notes on your screen. To find out what it
+was you intended them to remind you of, all you have to do is click on
+a note, which will then be displayed full size. Furthermore, the alarm
+feature introduced in a later version allows for even more explicit
+reminders and thus renders B<wmpinboard> even more powerful in a way
+than any real-life cork board. B<C<:-)>>
+
+By choosing from a variety of possible colors, you can assign
+particular colors to certain kinds of reminders, which may further
+enhance B<wmpinboard>'s usability. Moreover, you can place certain notes
+on certain areas of the board to emphasize their category, urgency,
+etc. It's up to you what to make of it.
+
+Finally, by adding drawing capabilities, I've definitely overcome the
+contents quantity barrier imposed by the maximum number of 59
+characters, for as everyone knows, a picture is worth more than a
+thousand words. *grin*
+
+=item *
+
+I<Q:> I don't live in an English-speaking country, so what about extended
+characters (umlauts, accents, cyrillic alphabet)?
+
+I<A:> B<wmpinboard> allows you to use an arbitrary 8bit X font, provided
+that its characters are of a fixed size of 6x10 (or, deprecated but
+possible, anything E<lt>= 7x10) pixels. The default font is "6x10" (more
+precisely, it's called
+"-misc-fixed-medium-r-normal--10-100-75-75-c-*-ISO8859-1"), an
+ISO8859-1 (Latin1) font which should be part of every XFree
+installation.
+
+In order to make B<wmpinboard> use another font, run it as
+
+ $ wmpinboard --font=FONT
+
+where B<FONT> is either a shortcut for a compiled-in font name (see
+L<"OPTIONS"> for a list of those) or a valid, complete X font
+descriptor. This is a configuration directive, meaning that no other
+instance of B<wmpinboard> may be running at the time. Note that this
+I<only works if there are no more notes on the board>. It's
+intentionally been made impossible to change the font while there are
+notes saved in B<wmpinboard>'s data file, since this might result in
+garbage being displayed. Of course even a font specified via a
+shortcut has to exist on your system in order to be usable.
+
+If a configured custom font cannot be loaded or has invalid dimensions,
+B<wmpinboard> will try to revert. Note that this won't affect the font
+name saved along with the data, though.
+
+=item *
+
+I<Q:> How can I disable those vexing, superfluous animations?
+
+I<A:>
+
+ $ wmpinboard --light
+
+=item *
+
+I<Q:> Why aren't those animations smooth all of the time? Sometimes it
+looks like they're being skipped entirely.
+
+I<A:> This presumably is a multitasking issue: depending on the current
+system load and B<wmpinboard>'s/the X server's recent CPU usage
+history, it may take a moment until the scheduling has been adapted to
+the suddenly increased CPU load implied by displaying the animation,
+and short as it is, it may already be finished until this has happened,
+i.e., it's the X server lagging behind in updating the program's
+display if B<wmpinboard>'s been idle for some time prior to that. It
+may sound paradoxical, but the effect is the more likely to show the
+lower the system's load is. I don't see a way to avoid this
+effect--either this, or you turn off animations altogether.
+
+=item *
+
+I<Q:> When I leave B<wmpinboard> idle in edit mode for some time, edit
+mode is terminated automatically. Is that intended?
+
+I<A:> Yes. After 60 idle seconds (that's the default; see
+L<"OPTIONS">) in edit mode (no mouse click and no keyboard input), edit
+mode is terminated automatically. If the note being edited happens to
+be blank, it will be discarded (or removed if an existing note is being
+edited).
+
+This timeout can, however, be adjusted according to your preferences or
+turned off using the B<C<-t>> parameter. See L<"OPTIONS"> for this.
+
+=item *
+
+I<Q:> When does B<wmpinboard> save its data?
+
+I<A:> Notes data is saved on each of these occasions:
+
+=over 2
+
+=item *
+
+whenever edit mode is terminated
+
+=item *
+
+when you switch notes in edit mode (via [Shift]-[S<arrow key>])
+
+=item *
+
+when a note has been moved on the board
+
+=item *
+
+when an interactive instance is running and you run B<wmpinboard> from
+the command line, making it dump, add, or delete notes
+
+=item *
+
+when killed via SIGINT or SIGTERM and edit mode is active
+
+=back
+
+Notes are saved to a file called F<.wmpinboarddata> in your home
+directory (see L<"FILES">).
+
+=item *
+
+I<Q:> I've tried my best and littered the entire pinboard with quite a
+lot of notes. Now I can't seem to be able to add another one.
+
+I<A:> There's a compile-time limit of 20 notes. I think more notes on
+this tiny a board really don't make any sense.
+
+=item *
+
+I<Q:> I've explicitly configured my window manager for click-based
+rather than mouse-following focus, but B<wmpinboard>'s focus follows
+the mouse regardless. Can I change this?
+
+I<A:> By default, B<wmpinboard> actively claims the keyboard input
+focus (if it's in note edit mode) whenever the pointer is moved over the
+application's area. Since B<wmpinboard> is a dock applet, i.e., a
+withdrawn rather than a "real" X window, it can't be assigned a focus
+in the same way as to the latter ones. However, running B<wmpinboard>
+with the parameter B<C<-c>> will make it emulate some sort of
+click-based focusing. This means, it actively claims the keyboard
+focus only on these occasions:
+
+=over 2
+
+=item *
+
+when a new note is created (I<not> when you click on an existing
+note--you'll have to explicitly click on the editing area to make it
+claim focus; this way, you can just view a note without any change to
+keyboard focus)
+
+=item *
+
+when you click somewhere on the text area in edit mode
+
+=back
+
+Once keyboard-focused, B<wmpinboard> will keep it until another window
+is explicitly focused (usually by clicking on its title bar or
+border). To focus B<wmpinboard> again when it's lost focus, you'll
+have to click on its text area in edit mode. This click will only
+focus the application and not have the usual cursor-positioning effect.
+
+This feature is to be considered experimental since I'm not sure of how
+much use it really is. A mouse-following focus is the recommended mode
+of operation.
+
+=item *
+
+I<Q:> I've noticed that after a while, some sort of darker stains
+appear on my notes. Is that a bug in some drawing routine?
+
+I<A:> No, this is not a bug. These "stains" are meant to resemble
+creases, caused by frequent handling of a particular note (wear & tear,
+you see?). In fact, they're added with a certain probability whenever
+you view a note by clicking on it (note that leafing through notes via
+[Shift]-[S<arrow keys>] is I<not> affected), when you clear its textual
+or drawn contents via the I<edit mode panel> (very outwearing, that
+S<B<;-)> )>, and when a note is moved. This feature can be disabled at
+compile time by running F<configure> with C<--disable-creases> as a
+parameter.
+
+To prevent the question, no, worn-out notes cannot be ironed to get rid
+of the creases. Sorry. B<C<:-p>>
+
+=item *
+
+I<Q:> Is B<wmpinboard> compatible with B<AfterStep>'s B<Wharf>?
+
+I<A:> B<wmpinboard> tries to autodetect whether B<Window Maker> is
+running and sets itself up accordingly. If this doesn't work for you
+for some reason, you can explicitly make it run in either Withdrawn- or
+NormalState using the B<C<-w>> or B<C<-n>> flag, respectively. See
+L<"OPTIONS">.
+
+Swallowing evidently works with B<AfterStep> 1.6.10; I don't know about
+earlier versions. A B<Wharf> config line you might try is this:
+
+ *Wharf wmpinboard nil MaxSwallow "wmpinboard" wmpinboard &
+
+Besides, B<wmpinboard> has been reported to work with B<Blackbox>.
+
+=item *
+
+I<Q:> I have X running at a color depth of 8 bits, i.e., in palette
+mode, and B<wmpinboard> obviously requires too many colors and thus
+looks real messy (or doesn't run at all, complaining about "not enough
+free color cells"). What can I do about this?
+
+I<A:> As of version 0.99.1, the recommended solution is to upgrade
+whatever component of your system restricts you to a palette mode.
+
+=item *
+
+I<Q:> Can I run multiple instances of B<wmpinboard> as the same user,
+simultaneously?
+
+I<A:> No, this is certainly not a good idea. The run-time behavior may
+be unpredictable, and your data file can get corrupted. Therefore, any
+B<wmpinboard> process that's to be run interactively first checks
+whether another interactive instance is running, and if so, refuses to
+run.
+
+=item *
+
+I<Q:> I've just upgraded from a pre-0.7 version of B<wmpinboard> and
+noticed that its data file format has changed completely since. Is
+there a way to upgrade and yet keep my existing notes?
+
+I<A:> There's a B<Perl> script doing the conversion included with the
+distribution (the source one, anyway). If your package didn't include
+that, feel free to mail to the author (see L<"AUTHOR"> at the end of
+this documentation).
+
+=item *
+
+I<Q:> I find a mere 59 characters per note to be a real limitation.
+How about making B<wmpinboard> pop up an external window with more room
+for that? Or how about assigning that job to an external editor?
+
+I<A:> There's a variety of comprehensive programs for keeping notes out
+there, offering this functionality but being rather heavy-weight since
+they are linked against one GUI library or another (B<CoolNotes> or
+B<KNotes> come to mind). On the other hand, I couldn't find a
+B<WM>-conforming reminder I could omnipresently stick to my desktop
+anywhere, so I wrote B<wmpinboard>. I wanted it to be small, neat,
+easy to use, and yet useful in a way.
+
+I hope that's about what the program is currently like. And I'd prefer
+to keep it like that rather than inflate it by linking against a GUI
+library--eventually, the note editing code would outweigh the rest of
+the application by a factor, and people would probably still create
+notes mostly shorter than 60 characters. If you restrict your memos to
+keywords and abbreviations, that's quite a lot.
+
+I want B<wmpinboard> to remain an applet in that it doesn't open up
+external windows and use (if just temporarily) additional desktop
+space. I explicitly wrote it to have something omnipresent at a fixed
+position on my desktop. I find it preferable to have the notes pop up
+in place rather than somewhere else on the screen.
+
+Personally, I use other programs for larger notes too; B<wmpinboard>
+has been designed for things smaller in size and greater in urgency,
+it's in no way meant to be a comprehensive knowledge base application
+of any kind.
+
+Summing up, I think a dock applet should be small both regarding its
+on-screen representation and the resources it uses. That's why I
+don't intend to add any pop-up dialogs or similar things to
+B<wmpinboard>.
+
+=item *
+
+I<Q:> I've tried the program, yet I can't help finding it utterly
+useless. What shall I do?
+
+I<A:> The solution is simple. Just don't use it.
+
+=item *
+
+I<Q:> Will your answer to this question be "no"?
+
+I<A:> Nope.
+
+=back
+
+=head1 HINTS
+
+=over 2
+
+=item *
+
+A good way of making the best of the organizational features offered by
+B<wmpinboard> is to use certain colors and locations on the pinboard to
+indicate urgency and contents of a note. For example, you might use
+each of the color groups for a certain kind of reminder, because that
+enables you to leaf through all notes with related contents via
+[Shift]-[S<arrow keys>] in edit mode. Besides, you might assign each
+corner of the board a specific urgency, altogether allowing you to
+derive a note's type from its color and its urgency from its location
+on the board. Thanks again to the ability to leaf through all notes
+belonging to the same group of colors, notes with similar contents will
+still be clustered in a way.
+
+=back
+
+=head1 UNDOCUMENTED FEATURES
+
+This piece of documentation doesn't cover any undocumented features.
+
+=head1 FILES
+
+=over 2
+
+=item F<~/.wmpinboarddata>
+
+the user's B<wmpinboard> data file
+
+=item F<~/.wmpinboarddata.new>
+
+temporary file created momentarily when saving data
+
+=back
+
+=head1 ENVIRONMENT VARIABLES
+
+=over 2
+
+=item $HOME
+
+the user's home directory
+
+=back
+
+=head1 RESTRICTIONS
+
+=over 2
+
+=item *
+
+B<wmpinboard> relies on a dock app tile size of at least 64x64 pixels.
+In fact, using smaller tiles renders the applet rather useless, as,
+e.g., opening the edit mode panel becomes impossible.
+
+=item *
+
+Unpredictable effects may be the results if a command line action is
+taken while an alarm is running and others are due simultaneously.
+If just a single alarm is active and no others are due, that alarm will
+be cancelled when the two instances of B<wmpinboard> synchronize.
+If more instances are due during the process, a race conditions arises
+that can't be solved satisfactorily due to the program's internal
+structure and organization. Still, in theory, nothing critical will
+happen, and the most you'll lose will be an alarm or two.
+
+=item *
+
+B<wmpinboard> data files are not designed to be portable across
+architectures. Due to differences in data type representations that
+are likely otherwise, a datafile can only be reliably used by program
+binaries running on machines of the same architecture.
+
+=item *
+
+Certainly of no interest to anyone, but mentioned for the sake of
+completeness: B<wmpinboard>'s alarm features will start to fail past
+04:14:07 on Jan 19, 2037, which is due to the legacy Un*x time format.
+
+=back
+
+=head1 BUGS
+
+If you stumble on any bugs, feel free to mail the author. The same
+goes if you encounter any problems running/using the program. Be sure
+to include any information you consider relevant, i.e., at a minimum,
+the version of B<wmpinboard> you're using as well as your OS and X
+versions.
+
+Also, further suggestions are always welcome. Please check the F<TODO>
+file that's part of the distribution to see if what you're about to
+suggest isn't already on my "to do" list, or has been suggested earlier
+and was rejected for one reason or other.
+
+=head1 SEE ALSO
+
+wmaker(1)
+
+=head1 AUTHOR
+
+B<wmpinboard> is copyrighted (c) 1998-2000 by Marco GE<ouml>tze,
+E<lt>gomar at mindless.comE<gt>. It is distributed under the terms of the
+GNU General Public License, revision 2 or any later revision thereof.
+Use at your own risk.
+
+New releases of and themes for B<wmpinboard> can be found at
+E<lt>http://www.tu-ilmenau.de/~gomar/stuff/wmpinboard/E<gt>, or
+that was true at least by the time this document was last updated.
+
+=cut
+
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..24841fe
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,17 @@
+## Process this file with automake to produce Makefile.in
+
+INCLUDES = $(X_CFLAGS)
+LIBS = $(X_LIBS) -lX11 -lXext -lXpm
+
+bin_PROGRAMS = wmpinboard
+
+wmpinboard_SOURCES = @CONDITIONAL_SOURCES@ \
+ misc.c misc.h \
+ notes.c notes.h \
+ wmpinboard.c wmpinboard.h \
+ xmisc.c xmisc.h
+
+EXTRA_wmpinboard_SOURCES = \
+ getopt.c getopt1.c getopt.h \
+ memcmp.c memcmp.h
+
diff --git a/src/getopt.c b/src/getopt.c
new file mode 100644
index 0000000..0f41fb6
--- /dev/null
+++ b/src/getopt.c
@@ -0,0 +1,1004 @@
+/* Getopt for GNU.
+ NOTE: getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to roland at gnu.ai.mit.edu
+ before changing it!
+
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97
+ Free Software Foundation, Inc.
+
+ This file is part of the GNU C Library. Its master source is NOT part of
+ the C library, however. The master source lives in /gd/gnu/lib.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
+ Ditto for AIX 3.2 and <stdlib.h>. */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if !defined (__STDC__) || !__STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+ contain conflicting prototypes for getopt. */
+#include <stdlib.h>
+#include <unistd.h>
+#endif /* GNU C library. */
+
+#ifdef VMS
+#include <unixlib.h>
+#if HAVE_STRING_H - 0
+#include <string.h>
+#endif
+#endif
+
+#if defined (WIN32) && !defined (__CYGWIN32__)
+/* It's not Unix, really. See? Capital letters. */
+#include <windows.h>
+#define getpid() GetCurrentProcessId()
+#endif
+
+#ifndef _
+/* This is for other GNU distributions with internationalized messages.
+ When compiling libc, the _ macro is predefined. */
+#ifdef HAVE_LIBINTL_H
+#include <libintl.h>
+#define _(msgid) gettext (msgid)
+#else
+#define _(msgid) (msgid)
+#endif
+#endif
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg = NULL;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns -1, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+/* 1003.2 says this must be 1 before any call. */
+int optind = 1;
+
+/* Formerly, initialization of getopt depended on optind==0, which
+ causes problems with re-calling getopt as programs generally don't
+ know that. */
+
+int __getopt_initialized = 0;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return -1 with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+}
+ordering;
+
+/* Value of POSIXLY_CORRECT environment variable. */
+static char *posixly_correct;
+
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+ because there are many ways it can cause trouble.
+ On some systems, it contains special magic macros that don't work
+ in GCC. */
+#include <string.h>
+#define my_index strchr
+#else
+
+/* Avoid depending on library functions or files
+ whose names are inconsistent. */
+
+char *getenv();
+
+static char *
+ my_index(str, chr)
+ const char *str;
+ int chr;
+{
+ while (*str)
+ {
+ if (*str == chr)
+ return (char *) str;
+ str++;
+ }
+ return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+ If not using GCC, it is ok not to declare it. */
+#ifdef __GNUC__
+/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
+ That was relevant to code that was here before. */
+#if !defined (__STDC__) || !__STDC__
+/* gcc with -traditional declares the built-in strlen to return int,
+ and has done so at least since version 2.4.5. -- rms. */
+extern int strlen(const char *);
+
+#endif /* not __STDC__ */
+#endif /* __GNUC__ */
+
+#endif /* not __GNU_LIBRARY__ */
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+#ifdef _LIBC
+/* Bash 2.0 gives us an environment variable containing flags
+ indicating ARGV elements that should not be considered arguments. */
+
+static const char *nonoption_flags;
+static int nonoption_flags_len;
+
+static int original_argc;
+static char *const *original_argv;
+
+/* Make sure the environment variable bash 2.0 puts in the environment
+ is valid for the getopt call we must make sure that the ARGV passed
+ to getopt is that one passed to the process. */
+static void store_args(int argc, char *const *argv) __attribute__((unused));
+ static void
+ store_args(int argc, char *const *argv)
+{
+ /* XXX This is no good solution. We should rather copy the args so
+ that we can compare them later. But we must not use malloc(3). */
+ original_argc = argc;
+ original_argv = argv;
+}
+text_set_element(__libc_subinit, store_args);
+#endif
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+#if defined (__STDC__) && __STDC__
+static void exchange(char **);
+
+#endif
+
+static void
+ exchange(argv)
+ char **argv;
+{
+ int bottom = first_nonopt;
+ int middle = last_nonopt;
+ int top = optind;
+ char *tem;
+
+ /* Exchange the shorter segment with the far end of the longer segment.
+ That puts the shorter segment into the right place.
+ It leaves the longer segment in the right place overall,
+ but it consists of two parts that need to be swapped next. */
+
+ while (top > middle && middle > bottom)
+ {
+ if (top - middle > middle - bottom)
+ {
+ /* Bottom segment is the short one. */
+ int len = middle - bottom;
+ register int i;
+
+ /* Swap it with the top part of the top segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ }
+ /* Exclude the moved bottom segment from further swapping. */
+ top -= len;
+ }
+ else
+ {
+ /* Top segment is the short one. */
+ int len = top - middle;
+ register int i;
+
+ /* Swap it with the bottom part of the bottom segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ }
+ /* Exclude the moved top segment from further swapping. */
+ bottom += len;
+ }
+ }
+
+ /* Update records for the slots the non-options now occupy. */
+
+ first_nonopt += (optind - last_nonopt);
+ last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made. */
+
+#if defined (__STDC__) && __STDC__
+static const char *_getopt_initialize(int, char *const *, const char *);
+
+#endif
+static const char *
+ _getopt_initialize(argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ /* Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ first_nonopt = last_nonopt = optind = 1;
+
+ nextchar = NULL;
+
+ posixly_correct = getenv("POSIXLY_CORRECT");
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (posixly_correct != NULL)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+
+#ifdef _LIBC
+ if (posixly_correct == NULL
+ && argc == original_argc && argv == original_argv)
+ {
+ /* Bash 2.0 puts a special variable in the environment for each
+ command it runs, specifying which ARGV elements are the results of
+ file name wildcard expansion and therefore should not be
+ considered as options. */
+ char var[100];
+
+ sprintf(var, "_%d_GNU_nonoption_argv_flags_", getpid());
+ nonoption_flags = getenv(var);
+ if (nonoption_flags == NULL)
+ nonoption_flags_len = 0;
+ else
+ nonoption_flags_len = strlen(nonoption_flags);
+ }
+ else
+ nonoption_flags_len = 0;
+#endif
+
+ return optstring;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns -1.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int
+ _getopt_internal(argc, argv, optstring, longopts, longind, long_only)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+ const struct option *longopts;
+ int *longind;
+ int long_only;
+{
+ optarg = NULL;
+
+ if (!__getopt_initialized || optind == 0)
+ {
+ optstring = _getopt_initialize(argc, argv, optstring);
+ optind = 1; /* Don't scan ARGV[0], the program name. */
+ __getopt_initialized = 1;
+ }
+
+ /* Test whether ARGV[optind] points to a non-option argument.
+ Either it does not have option syntax, or there is an environment flag
+ from the shell indicating it is not an option. The later information
+ is only used when the used in the GNU libc. */
+#ifdef _LIBC
+#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \
+ || (optind < nonoption_flags_len \
+ && nonoption_flags[optind] == '1'))
+#else
+#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#endif
+
+ if (nextchar == NULL || *nextchar == '\0')
+ {
+ /* Advance to the next ARGV-element. */
+
+ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
+ moved back by the user (who may also have changed the arguments). */
+ if (last_nonopt > optind)
+ last_nonopt = optind;
+ if (first_nonopt > optind)
+ first_nonopt = optind;
+
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange((char **) argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc && NONOPTION_P)
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* The special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !strcmp(argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange((char **) argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return -1;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if (NONOPTION_P)
+ {
+ if (ordering == REQUIRE_ORDER)
+ return -1;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Skip the initial punctuation. */
+
+ nextchar = (argv[optind] + 1
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ /* Decode the current option-ARGV-element. */
+
+ /* Check whether the ARGV-element is a long option.
+
+ If long_only and the ARGV-element has the form "-f", where f is
+ a valid short option, don't consider it an abbreviated form of
+ a long option that starts with f. Otherwise there would be no
+ way to give the -f short option.
+
+ On the other hand, if there's a long option "fubar" and
+ the ARGV-element is "-fu", do consider that an abbreviation of
+ the long option, just like "--fu", and not "-f" with arg "u".
+
+ This distinction seems to be the most useful approach. */
+
+ if (longopts != NULL
+ && (argv[optind][1] == '-'
+ || (long_only && (argv[optind][2] || !my_index(optstring, argv[optind][1])))))
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = -1;
+ int option_index;
+
+ for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp(p->name, nextchar, nameend - nextchar))
+ {
+ if ((unsigned int) (nameend - nextchar)
+ == (unsigned int) strlen(p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf(stderr, _("%s: option `%s' is ambiguous\n"),
+ argv[0], argv[optind]);
+ nextchar += strlen(nextchar);
+ optind++;
+ optopt = 0;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = nameend + 1;
+ else
+ {
+ if (opterr)
+ if (argv[optind - 1][1] == '-')
+ /* --option */
+ fprintf(stderr,
+ _("%s: option `--%s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+ else
+ /* +option or -option */
+ fprintf(stderr,
+ _("%s: option `%c%s' doesn't allow an argument\n"),
+ argv[0], argv[optind - 1][0], pfound->name);
+
+ nextchar += strlen(nextchar);
+
+ optopt = pfound->val;
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf(stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]);
+ nextchar += strlen(nextchar);
+ optopt = pfound->val;
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen(nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+ || my_index(optstring, *nextchar) == NULL)
+ {
+ if (opterr)
+ {
+ if (argv[optind][1] == '-')
+ /* --option */
+ fprintf(stderr, _("%s: unrecognized option `--%s'\n"),
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf(stderr, _("%s: unrecognized option `%c%s'\n"),
+ argv[0], argv[optind][0], nextchar);
+ }
+ nextchar = (char *) "";
+ optind++;
+ optopt = 0;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next short option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index(optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (opterr)
+ {
+ if (posixly_correct)
+ /* 1003.2 specifies the format of this message. */
+ fprintf(stderr, _("%s: illegal option -- %c\n"),
+ argv[0], c);
+ else
+ fprintf(stderr, _("%s: invalid option -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ return '?';
+ }
+ /* Convenience. Treat POSIX -W foo same as long option --foo */
+ if (temp[0] == 'W' && temp[1] == ';')
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = 0;
+ int option_index;
+
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+ /* 1003.2 specifies the format of this message. */
+ fprintf(stderr, _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ return c;
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+
+ /* optarg is now the argument, see if it's in the
+ table of longopts. */
+
+ for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp(p->name, nextchar, nameend - nextchar))
+ {
+ if ((unsigned int) (nameend - nextchar) == strlen(p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf(stderr, _("%s: option `-W %s' is ambiguous\n"),
+ argv[0], argv[optind]);
+ nextchar += strlen(nextchar);
+ optind++;
+ return '?';
+ }
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = nameend + 1;
+ else
+ {
+ if (opterr)
+ fprintf(stderr, _("\
+%s: option `-W %s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+
+ nextchar += strlen(nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf(stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]);
+ nextchar += strlen(nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen(nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ nextchar = NULL;
+ return 'W'; /* Let the application handle it. */
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = NULL;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+ /* 1003.2 specifies the format of this message. */
+ fprintf(stderr,
+ _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+ getopt(argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ return _getopt_internal(argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+#endif /* Not ELIDE_CODE. */
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of `getopt'. */
+
+int
+ main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = getopt(argc, argv, "abc:d:0123456789");
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf("option %c\n", c);
+ break;
+
+ case 'a':
+ printf("option a\n");
+ break;
+
+ case 'b':
+ printf("option b\n");
+ break;
+
+ case 'c':
+ printf("option c with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf("%s ", argv[optind++]);
+ printf("\n");
+ }
+
+ exit(0);
+}
+
+#endif /* TEST */
diff --git a/src/getopt.h b/src/getopt.h
new file mode 100644
index 0000000..2fa12f7
--- /dev/null
+++ b/src/getopt.h
@@ -0,0 +1,134 @@
+/* Declarations for getopt.
+ Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc.
+
+ This file is part of the GNU C Library. Its master source is NOT part of
+ the C library, however. The master source lives in /gd/gnu/lib.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+ extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns -1, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+ extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+ extern int opterr;
+
+/* Set to an option character which was unrecognized. */
+
+ extern int optopt;
+
+/* Describe the long-named options requested by the application.
+ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+ of `struct option' terminated by an element containing a name which is
+ zero.
+
+ The field `has_arg' is:
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
+
+ If the field `flag' is not NULL, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+ struct option
+ {
+#if defined (__STDC__) && __STDC__
+ const char *name;
+#else
+ char *name;
+#endif
+ /* has_arg can't be an enum because some compilers complain about
+ type mismatches in all the code that assumes it is an int. */
+ int has_arg;
+ int *flag;
+ int val;
+ };
+
+/* Names for the values of the `has_arg' field of `struct option'. */
+
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+#if defined (__STDC__) && __STDC__
+#ifdef __GNU_LIBRARY__
+/* Many other libraries have conflicting prototypes for getopt, with
+ differences in the consts, in stdlib.h. To avoid compilation
+ errors, only prototype getopt for the GNU C library. */
+ extern int getopt(int argc, char *const *argv, const char *shortopts);
+#else /* not __GNU_LIBRARY__ */
+ extern int getopt();
+#endif /* __GNU_LIBRARY__ */
+ extern int getopt_long(int argc, char *const *argv, const char *shortopts,
+ const struct option *longopts, int *longind);
+ extern int getopt_long_only(int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind);
+
+/* Internal only. Users should not call this directly. */
+ extern int _getopt_internal(int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only);
+#else /* not __STDC__ */
+ extern int getopt();
+ extern int getopt_long();
+ extern int getopt_long_only();
+
+ extern int _getopt_internal();
+#endif /* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GETOPT_H */
diff --git a/src/getopt1.c b/src/getopt1.c
new file mode 100644
index 0000000..fe414f3
--- /dev/null
+++ b/src/getopt1.c
@@ -0,0 +1,187 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+ Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc.
+
+ This file is part of the GNU C Library. Its master source is NOT part of
+ the C library, however. The master source lives in /gd/gnu/lib.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "getopt.h"
+
+#if !defined (__STDC__) || !__STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+int
+ getopt_long(argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal(argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+ If an option that starts with '-' (not '--') doesn't match a long option,
+ but does match a short option, it is parsed as a short option
+ instead. */
+
+int
+ getopt_long_only(argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal(argc, argv, options, long_options, opt_index, 1);
+}
+
+#endif /* Not ELIDE_CODE. */
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+ main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 0, 0, 0},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long(argc, argv, "abc:d:0123456789",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf("option %s", long_options[option_index].name);
+ if (optarg)
+ printf(" with arg %s", optarg);
+ printf("\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf("option %c\n", c);
+ break;
+
+ case 'a':
+ printf("option a\n");
+ break;
+
+ case 'b':
+ printf("option b\n");
+ break;
+
+ case 'c':
+ printf("option c with value `%s'\n", optarg);
+ break;
+
+ case 'd':
+ printf("option d with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf("%s ", argv[optind++]);
+ printf("\n");
+ }
+
+ exit(0);
+}
+
+#endif /* TEST */
diff --git a/src/memcmp.c b/src/memcmp.c
new file mode 100644
index 0000000..6c87ad2
--- /dev/null
+++ b/src/memcmp.c
@@ -0,0 +1,394 @@
+/* Copyright (C) 1991, 1993, 1995, 1997, 1998 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Torbjorn Granlund (tege at sics.se).
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#undef __ptr_t
+#if defined __cplusplus || (defined __STDC__ && __STDC__)
+# define __ptr_t void *
+#else /* Not C++ or ANSI C. */
+# undef const
+# define const
+# define __ptr_t char *
+#endif /* C++ or ANSI C. */
+
+#ifndef __P
+# if defined __GNUC__ || (defined __STDC__ && __STDC__)
+# define __P(args) args
+# else
+# define __P(args) ()
+# endif /* GCC. */
+#endif /* Not __P. */
+
+#if defined HAVE_STRING_H || defined _LIBC
+# include <string.h>
+#endif
+
+#undef memcmp
+
+#ifdef _LIBC
+
+# include <memcopy.h>
+# include <endian.h>
+
+# if __BYTE_ORDER == __BIG_ENDIAN
+# define WORDS_BIGENDIAN
+# endif
+
+#else /* Not in the GNU C library. */
+
+# include <sys/types.h>
+
+/* Type to use for aligned memory operations.
+ This should normally be the biggest type supported by a single load
+ and store. Must be an unsigned type. */
+# define op_t unsigned long int
+# define OPSIZ (sizeof(op_t))
+
+/* Threshold value for when to enter the unrolled loops. */
+# define OP_T_THRES 16
+
+/* Type to use for unaligned operations. */
+typedef unsigned char byte;
+
+# ifndef WORDS_BIGENDIAN
+# define MERGE(w0, sh_1, w1, sh_2) (((w0) >> (sh_1)) | ((w1) << (sh_2)))
+# else
+# define MERGE(w0, sh_1, w1, sh_2) (((w0) << (sh_1)) | ((w1) >> (sh_2)))
+# endif
+
+#endif /* In the GNU C library. */
+
+#ifdef WORDS_BIGENDIAN
+# define CMP_LT_OR_GT(a, b) ((a) > (b) ? 1 : -1)
+#else
+# define CMP_LT_OR_GT(a, b) memcmp_bytes ((a), (b))
+#endif
+
+/* BE VERY CAREFUL IF YOU CHANGE THIS CODE! */
+
+/* The strategy of this memcmp is:
+
+ 1. Compare bytes until one of the block pointers is aligned.
+
+ 2. Compare using memcmp_common_alignment or
+ memcmp_not_common_alignment, regarding the alignment of the other
+ block after the initial byte operations. The maximum number of
+ full words (of type op_t) are compared in this way.
+
+ 3. Compare the few remaining bytes. */
+
+#ifndef WORDS_BIGENDIAN
+/* memcmp_bytes -- Compare A and B bytewise in the byte order of the machine.
+ A and B are known to be different.
+ This is needed only on little-endian machines. */
+
+static int memcmp_bytes __P((op_t, op_t));
+
+# ifdef __GNUC__
+__inline
+# endif
+static int
+memcmp_bytes (a, b)
+ op_t a, b;
+{
+ long int srcp1 = (long int) &a;
+ long int srcp2 = (long int) &b;
+ op_t a0, b0;
+
+ do
+ {
+ a0 = ((byte *) srcp1)[0];
+ b0 = ((byte *) srcp2)[0];
+ srcp1 += 1;
+ srcp2 += 1;
+ }
+ while (a0 == b0);
+ return a0 - b0;
+}
+#endif
+
+static int memcmp_common_alignment __P((long, long, size_t));
+
+/* memcmp_common_alignment -- Compare blocks at SRCP1 and SRCP2 with LEN `op_t'
+ objects (not LEN bytes!). Both SRCP1 and SRCP2 should be aligned for
+ memory operations on `op_t's. */
+#ifdef __GNUC__
+__inline
+#endif
+static int
+memcmp_common_alignment (srcp1, srcp2, len)
+ long int srcp1;
+ long int srcp2;
+ size_t len;
+{
+ op_t a0, a1;
+ op_t b0, b1;
+
+ switch (len % 4)
+ {
+ default: /* Avoid warning about uninitialized local variables. */
+ case 2:
+ a0 = ((op_t *) srcp1)[0];
+ b0 = ((op_t *) srcp2)[0];
+ srcp1 -= 2 * OPSIZ;
+ srcp2 -= 2 * OPSIZ;
+ len += 2;
+ goto do1;
+ case 3:
+ a1 = ((op_t *) srcp1)[0];
+ b1 = ((op_t *) srcp2)[0];
+ srcp1 -= OPSIZ;
+ srcp2 -= OPSIZ;
+ len += 1;
+ goto do2;
+ case 0:
+ if (OP_T_THRES <= 3 * OPSIZ && len == 0)
+ return 0;
+ a0 = ((op_t *) srcp1)[0];
+ b0 = ((op_t *) srcp2)[0];
+ goto do3;
+ case 1:
+ a1 = ((op_t *) srcp1)[0];
+ b1 = ((op_t *) srcp2)[0];
+ srcp1 += OPSIZ;
+ srcp2 += OPSIZ;
+ len -= 1;
+ if (OP_T_THRES <= 3 * OPSIZ && len == 0)
+ goto do0;
+ /* Fall through. */
+ }
+
+ do
+ {
+ a0 = ((op_t *) srcp1)[0];
+ b0 = ((op_t *) srcp2)[0];
+ if (a1 != b1)
+ return CMP_LT_OR_GT (a1, b1);
+
+ do3:
+ a1 = ((op_t *) srcp1)[1];
+ b1 = ((op_t *) srcp2)[1];
+ if (a0 != b0)
+ return CMP_LT_OR_GT (a0, b0);
+
+ do2:
+ a0 = ((op_t *) srcp1)[2];
+ b0 = ((op_t *) srcp2)[2];
+ if (a1 != b1)
+ return CMP_LT_OR_GT (a1, b1);
+
+ do1:
+ a1 = ((op_t *) srcp1)[3];
+ b1 = ((op_t *) srcp2)[3];
+ if (a0 != b0)
+ return CMP_LT_OR_GT (a0, b0);
+
+ srcp1 += 4 * OPSIZ;
+ srcp2 += 4 * OPSIZ;
+ len -= 4;
+ }
+ while (len != 0);
+
+ /* This is the right position for do0. Please don't move
+ it into the loop. */
+ do0:
+ if (a1 != b1)
+ return CMP_LT_OR_GT (a1, b1);
+ return 0;
+}
+
+static int memcmp_not_common_alignment __P((long, long, size_t));
+
+/* memcmp_not_common_alignment -- Compare blocks at SRCP1 and SRCP2 with LEN
+ `op_t' objects (not LEN bytes!). SRCP2 should be aligned for memory
+ operations on `op_t', but SRCP1 *should be unaligned*. */
+#ifdef __GNUC__
+__inline
+#endif
+static int
+memcmp_not_common_alignment (srcp1, srcp2, len)
+ long int srcp1;
+ long int srcp2;
+ size_t len;
+{
+ op_t a0, a1, a2, a3;
+ op_t b0, b1, b2, b3;
+ op_t x;
+ int shl, shr;
+
+ /* Calculate how to shift a word read at the memory operation
+ aligned srcp1 to make it aligned for comparison. */
+
+ shl = 8 * (srcp1 % OPSIZ);
+ shr = 8 * OPSIZ - shl;
+
+ /* Make SRCP1 aligned by rounding it down to the beginning of the `op_t'
+ it points in the middle of. */
+ srcp1 &= -OPSIZ;
+
+ switch (len % 4)
+ {
+ default: /* Avoid warning about uninitialized local variables. */
+ case 2:
+ a1 = ((op_t *) srcp1)[0];
+ a2 = ((op_t *) srcp1)[1];
+ b2 = ((op_t *) srcp2)[0];
+ srcp1 -= 1 * OPSIZ;
+ srcp2 -= 2 * OPSIZ;
+ len += 2;
+ goto do1;
+ case 3:
+ a0 = ((op_t *) srcp1)[0];
+ a1 = ((op_t *) srcp1)[1];
+ b1 = ((op_t *) srcp2)[0];
+ srcp2 -= 1 * OPSIZ;
+ len += 1;
+ goto do2;
+ case 0:
+ if (OP_T_THRES <= 3 * OPSIZ && len == 0)
+ return 0;
+ a3 = ((op_t *) srcp1)[0];
+ a0 = ((op_t *) srcp1)[1];
+ b0 = ((op_t *) srcp2)[0];
+ srcp1 += 1 * OPSIZ;
+ goto do3;
+ case 1:
+ a2 = ((op_t *) srcp1)[0];
+ a3 = ((op_t *) srcp1)[1];
+ b3 = ((op_t *) srcp2)[0];
+ srcp1 += 2 * OPSIZ;
+ srcp2 += 1 * OPSIZ;
+ len -= 1;
+ if (OP_T_THRES <= 3 * OPSIZ && len == 0)
+ goto do0;
+ /* Fall through. */
+ }
+
+ do
+ {
+ a0 = ((op_t *) srcp1)[0];
+ b0 = ((op_t *) srcp2)[0];
+ x = MERGE(a2, shl, a3, shr);
+ if (x != b3)
+ return CMP_LT_OR_GT (x, b3);
+
+ do3:
+ a1 = ((op_t *) srcp1)[1];
+ b1 = ((op_t *) srcp2)[1];
+ x = MERGE(a3, shl, a0, shr);
+ if (x != b0)
+ return CMP_LT_OR_GT (x, b0);
+
+ do2:
+ a2 = ((op_t *) srcp1)[2];
+ b2 = ((op_t *) srcp2)[2];
+ x = MERGE(a0, shl, a1, shr);
+ if (x != b1)
+ return CMP_LT_OR_GT (x, b1);
+
+ do1:
+ a3 = ((op_t *) srcp1)[3];
+ b3 = ((op_t *) srcp2)[3];
+ x = MERGE(a1, shl, a2, shr);
+ if (x != b2)
+ return CMP_LT_OR_GT (x, b2);
+
+ srcp1 += 4 * OPSIZ;
+ srcp2 += 4 * OPSIZ;
+ len -= 4;
+ }
+ while (len != 0);
+
+ /* This is the right position for do0. Please don't move
+ it into the loop. */
+ do0:
+ x = MERGE(a2, shl, a3, shr);
+ if (x != b3)
+ return CMP_LT_OR_GT (x, b3);
+ return 0;
+}
+
+int
+memcmp (s1, s2, len)
+ const __ptr_t s1;
+ const __ptr_t s2;
+ size_t len;
+{
+ op_t a0;
+ op_t b0;
+ long int srcp1 = (long int) s1;
+ long int srcp2 = (long int) s2;
+ op_t res;
+
+ if (len >= OP_T_THRES)
+ {
+ /* There are at least some bytes to compare. No need to test
+ for LEN == 0 in this alignment loop. */
+ while (srcp2 % OPSIZ != 0)
+ {
+ a0 = ((byte *) srcp1)[0];
+ b0 = ((byte *) srcp2)[0];
+ srcp1 += 1;
+ srcp2 += 1;
+ res = a0 - b0;
+ if (res != 0)
+ return res;
+ len -= 1;
+ }
+
+ /* SRCP2 is now aligned for memory operations on `op_t'.
+ SRCP1 alignment determines if we can do a simple,
+ aligned compare or need to shuffle bits. */
+
+ if (srcp1 % OPSIZ == 0)
+ res = memcmp_common_alignment (srcp1, srcp2, len / OPSIZ);
+ else
+ res = memcmp_not_common_alignment (srcp1, srcp2, len / OPSIZ);
+ if (res != 0)
+ return res;
+
+ /* Number of bytes remaining in the interval [0..OPSIZ-1]. */
+ srcp1 += len & -OPSIZ;
+ srcp2 += len & -OPSIZ;
+ len %= OPSIZ;
+ }
+
+ /* There are just a few bytes to compare. Use byte memory operations. */
+ while (len != 0)
+ {
+ a0 = ((byte *) srcp1)[0];
+ b0 = ((byte *) srcp2)[0];
+ srcp1 += 1;
+ srcp2 += 1;
+ res = a0 - b0;
+ if (res != 0)
+ return res;
+ len -= 1;
+ }
+
+ return 0;
+}
+
+#ifdef weak_alias
+# undef bcmp
+weak_alias (memcmp, bcmp)
+#endif
diff --git a/src/memcmp.h b/src/memcmp.h
new file mode 100644
index 0000000..c0948f0
--- /dev/null
+++ b/src/memcmp.h
@@ -0,0 +1,8 @@
+#ifndef MEMCMP_H_INCLUDED
+#define MEMCMP_H_INCLUDED
+
+extern int memcmp __P ((__const __ptr_t __s1, __const __ptr_t __s2,
+ size_t __n));
+
+#endif /* MEMCMP_H_INCLUDED */
+
diff --git a/src/misc.c b/src/misc.c
new file mode 100644
index 0000000..31d23d0
--- /dev/null
+++ b/src/misc.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 1998-2000 by Marco G"otze.
+ *
+ * This code is part of the wmpinboard source package, which is
+ * distributed under the terms of the GNU GPL2.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sys/stat.h>
+
+#include "wmpinboard.h"
+
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+extern const char *rc_file_name;
+
+/*
+ * prints an error message and exits
+ */
+void
+die(const char *text)
+{
+ fprintf(stderr, "Fatal error: %s\n", text);
+ exit(EXIT_FAILURE);
+}
+
+/*
+ * allocs <amount> bytes and die()s if that fails
+ */
+void*
+smalloc(long amount)
+{
+ void *p = 0;
+
+ p = malloc(amount);
+ if (!p) die("Out of memory!");
+ return p;
+}
+
+/*
+ * returns a true value if a text string is to be considered empty
+ * (if width > 0, limit the check to width characters)
+ */
+int
+string_empty(char *ptr, int width)
+{
+ int i = 0;
+
+ while (*ptr && (!width || i++ < width))
+ if (*ptr++ != ' ') return 0;
+ return 1;
+}
+
+/*
+ * flushes a possibly running interactive instance, returns a true value if
+ * another instance *is* evidently running
+ */
+int
+flush_instance(int pid)
+{
+ struct stat buf;
+ time_t t;
+ char *s;
+ int i;
+ int running = 0;
+
+ if (pid != (int) getpid()) { /* not fulfilled if file doesn't exist */
+ s = smalloc(strlen(getenv("HOME")) + strlen(rc_file_name) + 1);
+ strcpy(s, getenv("HOME"));
+ strcat(s, rc_file_name);
+ stat(s, &buf);
+ t = buf.st_mtime;
+ /* make that other instance confirm its presence by modifying the file */
+ if (kill(pid, SIGUSR1) != -1) {
+ for (i = 0; i < 3; i++) { /* wait up to 3 secs for a modification */
+ stat(s, &buf);
+ if (buf.st_mtime != t) break;
+ sleep(1);
+ }
+ running = buf.st_mtime != t;
+ /* we assume that modifying the file is safe now */
+ }
+ free(s);
+ }
+ return running;
+}
+
+/*
+ * u()ncsarbmel sht eoctnnesto fs<>
+ * m(solt ysudef roc notss' ,osw eod'n todi tnip-alec)
+ */
+char*
+csarbmel(const char *s)
+{
+ char ch, *t;
+ int i;
+
+ t = smalloc(strlen(s)+1);
+ strcpy(t, s);
+ for (i = 0; i+1 < strlen(t); i += 2) {
+ ch = t[i];
+ t[i] = t[i+1];
+ t[i+1] = ch;
+ }
+ return t;
+}
+
diff --git a/src/misc.h b/src/misc.h
new file mode 100644
index 0000000..5d71daa
--- /dev/null
+++ b/src/misc.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 1998-2000 by Marco G"otze.
+ *
+ * This code is part of the wmpinboard source package, which is
+ * distributed under the terms of the GNU GPL2.
+ */
+
+#ifndef MISC_H_INCLUDED
+#define MISC_H_INCLUDED
+
+#include "wmpinboard.h"
+
+#define WARN(msg) fprintf(stderr, "Warning: %s\n", msg)
+
+void die(const char*);
+void *smalloc(long);
+int string_empty(char*, int);
+int flush_instance(int);
+char *csarbmel(const char *);
+void block_sigs();
+void unblock_sigs();
+
+#endif /* MISC_H_INCLUDED */
+
diff --git a/src/notes.c b/src/notes.c
new file mode 100644
index 0000000..8fd2320
--- /dev/null
+++ b/src/notes.c
@@ -0,0 +1,1013 @@
+/*
+ * Copyright (C) 1998-2000 by Marco G"otze.
+ *
+ * This code is part of the wmpinboard source package, which is
+ * distributed under the terms of the GNU GPL2.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include "wmpinboard.h"
+#include "misc.h"
+#include "xmisc.h"
+#include "notes.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifndef HAVE_MEMCMP
+#include "memcmp.h"
+#endif
+
+/* a list of the upper left corners of the separate areas of the alarm panel,
+ numbered from left to right and from top to bottom (double digits followed
+ by switches) */
+typedef struct { int x, y; } coords_t;
+const coords_t abar_areas[6] = {
+ /* double digits, 12x9 each */
+ { 3, 4 }, { 19, 4 },
+ { 3, 15 }, { 19, 15 },
+ /* switches, 8x8 each */
+ { 47, 4 }, { 47, 16 }
+};
+
+/*
+ * adds a new note and returns its number (or -1 if notes limit exceeded)
+ */
+int
+add_note()
+{
+ int cols[C_NUM];
+ int rep = 20;
+ int i, j, n;
+
+ if (notes_count >= MAX_NOTES)
+ return -1;
+ n = notes_count++;
+
+ /* find a reasonable color */
+ for (i = 0; i < C_NUM; i++) cols[i] = 1;
+ for (i = 0; i < n; i++) cols[ndata[i].col] = 0;
+ for (i = j = 0; i < C_NUM; i++)
+ if (cols[i]) cols[j++] = i;
+ ndata[n].col = j ? cols[rand() % j] : rand() % C_NUM;
+
+ /* choose a random place to pin it, some pixels off any other note's origin
+ (possible if MAX_NOTES is sufficiently small) */
+ do {
+ ndata[n].x = 6 + rand() % 36;
+ ndata[n].y = 2 + rand() % 44;
+ j = 1;
+ for (i = 0; i < n; i++)
+ if (ndata[i].x - ndata[n].x < 5 && ndata[i].x - ndata[n].x > -5 &&
+ ndata[i].y - ndata[n].y < 5 && ndata[i].y - ndata[n].y > -5)
+ {
+ j = 0;
+ break;
+ }
+ } while (!j && --rep);
+
+ memset(ndata[n].text, 32, 59);
+ ndata[n].text[59] = '\0';
+ ndata[n].cursor = 0;
+ memset(ndata[n].sketch, 0, 512);
+ memset(ndata[n].creases, 0, 32);
+ ndata[n].a_time = -1;
+ ndata[n].a_flags = ALARM_DATE;
+
+ state.counter++;
+
+ return n;
+}
+
+/*
+ * removes a note
+ */
+void
+remove_note(int number)
+{
+ int i;
+
+ if (number >= notes_count) return;
+ /* in case memcpy() can't do overlapping copies... */
+ for (i = number; i < notes_count-1; i++)
+ memcpy(&ndata[i], &ndata[i+1], sizeof(data_t));
+ notes_count--;
+}
+
+/*
+ * moves a note on top of all others and returns the quote's new ID
+ */
+int
+raise_note(int number)
+{
+ data_t tmp;
+ int i;
+
+ if (number >= notes_count-1 || number < 0) return number;
+ memcpy(&tmp, &ndata[number], sizeof(data_t));
+ /* in case memcpy() can't do overlapping copies... */
+ for (i = number; i < notes_count-1; i++)
+ memcpy(&ndata[i], &ndata[i+1], sizeof(data_t));
+ memcpy(&ndata[notes_count-1], &tmp, sizeof(data_t));
+ return notes_count-1;
+}
+
+/*
+ * returns a value corresponding to how tilted a note is to be displayed
+ */
+inline int
+note_tilt(int note)
+{
+ return ndata[note].x < 14 ? 0 : (ndata[note].x < 36 ? 1 : 2);
+}
+
+/*
+ * returns true if the note in question is to be considered empty
+ */
+int
+note_empty(int note)
+{
+ char *ptr = ndata[note].sketch;
+ int i;
+
+ if (!string_empty(ndata[note].text, 0)) return 0;
+ for (i = 0; i < 511; i++, ptr++) /* last byte ignored */
+ if (*ptr) return 0;
+ return 1;
+}
+
+/*
+ * creates templates for the icons suiting the specified note
+ */
+void
+color_notes(int note)
+{
+ XGetSubImage(display, app, 64, 0, 16, 48, ~0, ZPixmap, img, 64, 0);
+ if (ndata[note].sketch[511] & 1)
+ replace_color(img, 64, 0, 16, 48, C_EXTRA, palette[ndata[note].col].fg);
+ else
+ replace_color(img, 64, 0, 16, 48, C_EXTRA, palette[ndata[note].col].bg);
+ replace_color(img, 64, 0, 16, 48, C_INNER, palette[ndata[note].col].bg);
+#ifdef CREASES
+ render_wear(note);
+#endif
+}
+
+/*
+ * draws a note's miniature representation on the pinboard
+ */
+void
+pin_note(int note)
+{
+ merge_masked(img, 64, 16*note_tilt(note), ndata[note].x,
+ ndata[note].y, 16, 16, C_OUTER);
+}
+
+/*
+ * renders the pinboard with all of its notes excluding <exclude> if >= 0
+ */
+void
+render_pinboard(int exclude)
+{
+ int i;
+
+ XPutImage(display, app, normalGC, img, 0, 0, 0, 0, 64, 64);
+ for (i = 0; i < notes_count; i++)
+ if (i != exclude) {
+ color_notes(i);
+ pin_note(i);
+ }
+}
+
+/*
+ * if (<x>, <y>) is within a quote's area, returns the quote's ID, otherwise -1
+ */
+int
+selected_note(int x, int y)
+{
+ int i;
+
+ for (i = notes_count-1; i >= 0; i--)
+ if (x >= ndata[i].x && x < ndata[i].x+16 &&
+ y >= ndata[i].y && y < ndata[i].y+16 &&
+ XGetPixel(img, 128+x-ndata[i].x, note_tilt(i)*16+y-ndata[i].y) !=
+ C_OUTER)
+ {
+ return i;
+ }
+ return -1;
+}
+
+/*
+ * prints a letter identified by a note and a position in its text string, and
+ * overlays it by a possible sketch if sketch has a true value
+ */
+void
+print_letter(int note, int letter, int sketch)
+{
+ int a = 2+6*(letter%10), b = 2+10*(letter/10);
+
+
+ if (sketch) {
+ XFillRectangle(display, app, fillGC, a, b, 6, 10);
+#ifdef CREASES
+ render_edit_wear_area(note, a, b, 6, 10);
+#endif
+ draw_sketch_area(note, a, b, 6, 10);
+ }
+ XDrawString(display, app, fontGC, a, b + font->ascent,
+ &ndata[note].text[letter], 1);
+}
+
+/*
+ * prints a note's entire text *without* drawing a possibly existing sketch
+ */
+void
+print_text(int note)
+{
+ int i;
+
+ for (i = 0; i < 59; i++) print_letter(note, i, 0);
+}
+
+/*
+ * shifts part of a note's text to the right, inserting <ch> (step >= 1)
+ */
+void
+shift_string(int note, int at, int margin, int step, int ch)
+{
+ int i;
+
+ if (step < 1) return;
+
+ for (i = margin; i >= at+step; i--) {
+ ndata[note].text[i] = ndata[note].text[i-step];
+ print_letter(note, i, 1);
+ }
+ for (i = at; i < at+step; i++) {
+ ndata[note].text[i] = ch;
+ print_letter(note, i, 1);
+ }
+}
+
+/*
+ * draws a rectangular section of a sketch, according to the data saved along
+ * with a note
+ */
+void
+draw_sketch_area(int note, int x, int y, int w, int h)
+{
+ int i, j;
+
+ for (i = x; i < x+w; i++)
+ for (j = y; j < y+h; j++)
+ if (ndata[note].sketch[8*j + i/8] & (1<<(i%8)))
+ XDrawPoint(display, app, fontGC, i, j);
+}
+
+/*
+ * returns the number of the button bar's button corresponding to the given
+:* coordinates, or -1
+ */
+int
+bbar_button(int x, int y)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ if (x >= 4 + (i%4)*14 && x < 18 + (i%4)*14 && y >= 32 + (i/4)*14 &&
+ y < 46 + (i/4)*14)
+ {
+ return i;
+ }
+ return -1;
+}
+
+/*
+ * returns the number of that area on the alarm panel that corresponds to the
+ * given coordinates, or -1
+ */
+int
+abar_area(int x, int y)
+{
+ int i;
+
+ /* relativate positions */
+ x -= 3;
+ y -= 4;
+ for (i = 0; i < 6; i++) {
+ coords_t c = abar_areas[i];
+ if (x >= c.x && x < (c.x + (i < 4 ? 12 : 8)) &&
+ y >= c.y && y < (c.y + (i < 4 ? 9 : 8)))
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/*
+ * displays note <note> in edit mode view
+ */
+void
+render_note(int note)
+{
+ int i;
+
+ XSetForeground(display, fillGC, palette[ndata[note].col].bg);
+ XSetForeground(display, fontGC, BlackPixel(display, DefaultScreen(display)));
+ XSetBackground(display, fontGC, palette[ndata[note].col].bg);
+ set_mask(0);
+ XFillRectangle(display, app, fillGC, 0, 0, 64, 64);
+ XDrawRectangle(display, app, fontGC, 0, 0, 63, 63);
+ XSetForeground(display, fontGC, palette[ndata[note].col].fg);
+ for (i = 55; i < 62; i += 2) /* draw triangle in the bottom right corner */
+ XDrawLine(display, app, fontGC, i, 62, 62, i);
+}
+
+/*
+ * prepares edit mode for note <note> (does NOT update the display)
+ */
+void
+init_edit_mode(int note)
+{
+#ifdef CREASES
+ XGCValues gcv;
+
+ gcv.foreground = palette[ndata[note].col].cr;
+ XChangeGC(display, creaseGC, GCForeground, &gcv);
+#endif
+ render_note(note);
+#ifdef CREASES
+ render_edit_wear(note);
+#endif
+ print_text(note);
+ draw_sketch(note);
+}
+
+/*
+ * updates a double digit area on the alarm panel
+ */
+inline void
+render_abar_number(int field)
+{
+ int a, b;
+
+ /* split two-digit number into separate digits */
+ b = state.a_edit[field] % 10;
+ a = (state.a_edit[field] / 10) % 10;
+ /* copy appropriate digits */
+ XCopyArea(display, digits, abar, normalGC, 6*a, 0, 6, 9,
+ abar_areas[field].x, abar_areas[field].y);
+ XCopyArea(display, digits, abar, normalGC, 6*b, 0, 6, 9,
+ abar_areas[field].x+7, abar_areas[field].y);
+}
+
+/*
+ * updates the alarm panel's switches for a given note
+ */
+inline void
+render_abar_switches(int note)
+{
+ XPutImage(display, abar, normalGC, img,
+ (ndata[note].a_flags & ALARM_DATE) ? 78 : 70, 55, 47, 4, 8, 8);
+ XPutImage(display, abar, normalGC, img,
+ (ndata[note].a_flags & ALARM_DATE) ? 70 : 78, 55, 47, 16, 8, 8);
+}
+
+/*
+ * renders the alarm panel for a given note
+ */
+void
+render_abar(int note)
+{
+ int i;
+
+ for (i = 0; i < 4; i++) render_abar_number(i);
+ render_abar_switches(note);
+}
+
+/*
+ * converts <note>'s contents to what's referred to as "cooked" format (as
+ * opposed to raw data); returns a malloc()ed string that has to be freed
+ * later on!
+ */
+char*
+cook(int note, int pos, int len)
+{
+ char *s, *t;
+ int i;
+
+ s = smalloc(70);
+ for (t = s, i = pos; i < (pos+len > 59 ? 59 : pos+len); i++) {
+ if (!string_empty(&ndata[note].text[i], (i >= 50 ? 59 : 10*(i/10+1))-i))
+ {
+ if (t > s && !(i%10) && t[-1] != ' ' && t[-1] != '-')
+ *t++ = ' '; /* replace virtual newlines by spaces */
+ if ((t > s && (ndata[note].text[i] != ' ' || t[-1] != ' ') &&
+ !(t[-1] == '-'
+ && string_empty(&ndata[note].text[10*(i/10)], i%10+1))) ||
+ (t == s && ndata[note].text[i] != ' '))
+ {
+ *t++ = ndata[note].text[i];
+ }
+ }
+ }
+ *t = '\0';
+ return s;
+}
+
+/*
+ * dumps all notes' contents on STDOUT (raw dump unless <cooked> is true)
+ */
+void
+dump_notes(int cooked)
+{
+ char *s;
+ int c = 0, i, alarms;
+
+ /* determine if any notes have alarms configured */
+ time_next_alarm();
+ alarms = state.alarm.note >= 0;
+
+ /* print existing notes */
+ for (; c < 4; c++)
+ for (i = 0; i < notes_count; i++)
+ if (c_group[ndata[i].col] == c) {
+ s = cooked ? cook(i, 0, 59) : ndata[i].text;
+ if (alarms) { /* any alarms set at all? */
+ if (ndata[i].a_flags & ALARM_ON) {
+ explode_time(i);
+ if (ndata[i].a_flags & ALARM_DATE) { /* date-specific alarm */
+ printf("%02d-%02d-%02d %02d:%02d #%02d: %s\n",
+ state.a_edit[4]%100, state.a_edit[2], state.a_edit[3],
+ state.a_edit[0], state.a_edit[1], i, s);
+ } else { /* daily alarm */
+ printf("daily at %02d:%02d #%02d: %s\n", state.a_edit[0],
+ state.a_edit[1], i, s);
+ }
+ } else /* no alarm set for this note */
+ printf("%14s #%02d: %s\n", "", i, s);
+ } else /* no alarms set at all */
+ printf("#%02d: %s\n", i, s);
+ if (cooked) free(s);
+ }
+}
+
+/*
+ * returns the (serial) number of the character (<x>, <y>) belongs to on a
+ * note; prohibiting char #59 if <strict>
+ */
+int
+char_at(int x, int y, int strict)
+{
+ int i;
+
+ if (x < 2) x = 2;
+ if (x > 62) x = 62; /* intentional! */
+ if (y > 61) y = 61;
+ if (y < 2) y = 2;
+ i = (x-2)/6 + ((y-2)/10)*10;
+ if (i > 59) i = 59;
+ return i;
+}
+
+#ifdef CREASES
+/*
+ * applies random wear & tear to a note (the default probability is multiplied
+ * by <factor>)
+ */
+void
+wear_note(int note)
+{
+ /* prefabricated crease patterns */
+ static const int pats = 8;
+ static const unsigned char pat[8][5][5] = {
+ { { 1,0,0,0,0 },
+ { 0,1,0,0,0 },
+ { 0,0,1,0,0 },
+ { 0,0,0,1,0 },
+ { 0,0,0,0,1 } },
+ { { 0,0,0,0,1 },
+ { 0,0,0,1,0 },
+ { 0,0,1,0,0 },
+ { 0,1,0,0,0 },
+ { 1,0,0,0,0 } },
+ { { 0,0,0,1,0 },
+ { 0,0,0,1,0 },
+ { 0,0,1,0,0 },
+ { 1,1,0,0,0 },
+ { 0,0,0,0,0 } },
+ { { 0,1,0,0,0 },
+ { 0,1,0,0,0 },
+ { 0,0,1,0,0 },
+ { 0,0,0,1,1 },
+ { 0,0,0,0,0 } },
+ { { 0,0,0,0,0 },
+ { 0,0,0,1,1 },
+ { 0,0,1,0,0 },
+ { 0,1,0,0,0 },
+ { 0,1,0,0,0 } },
+ { { 0,0,0,0,0 },
+ { 1,1,0,0,0 },
+ { 0,0,1,0,0 },
+ { 0,0,0,1,0 },
+ { 0,0,0,1,0 } },
+ { { 0,0,0,0,0 },
+ { 0,1,0,0,0 },
+ { 0,0,1,0,0 },
+ { 0,0,0,1,0 },
+ { 0,0,0,0,0 } },
+ { { 0,0,0,0,0 },
+ { 0,0,0,1,0 },
+ { 0,0,1,0,0 },
+ { 0,1,0,0,0 },
+ { 0,0,0,0,0 } }
+ };
+ int i, j, k = 0;
+
+ /* determine number of crease bits */
+ for (i = 0; i < 32; i++)
+ for (j = 0; j < 8; j++)
+ k += ndata[note].creases[i]>>(j) & 1;
+ /* don't cover more than 35% of a note's area with creases */
+ if (100*k/256 < 35 && !(rand() % (4+k/64))) {
+ int x, y;
+ int t = rand() % pats; /* choose a prefab crease pattern to apply... */
+ int a = rand() % 11; /* ...as well as its location */
+ int b = rand() % 11;
+
+ for (y = 0; y < 5; y++)
+ for (x = 0; x < 5; x++)
+ if (pat[t][y][x])
+ ndata[note].creases[2*(b+y) + (a+x<8)] |= 1<<(7-(a+x)%8);
+ }
+}
+
+/*
+ * makes an otherwise pre-rendered note look worn out
+ */
+void
+render_wear(int note)
+{
+ int x, y, z;
+
+ for (z = 0; z <= 48; z += 16)
+ for (y = 0; y < 16; y++)
+ for (x = 0; x < 16; x++)
+ if (ndata[note].creases[2*y+x/8]>>(7-x%8) & 1 &&
+ XGetPixel(img, 64+x, z+y) == palette[ndata[note].col].bg)
+ {
+ XPutPixel(img, 64+x, z+y, palette[ndata[note].col].cr);
+ }
+}
+
+/*
+ * renders wear & tear in edit mode in a specific window
+ */
+void
+render_edit_wear_area_win(Window win, int note, int a, int b, int w, int h)
+{
+#define m(x, y) (m[2*(y) + (x)/8]>>(7-(x)%8) & 1)
+#define l(x, y) (l[8*(y) + (x)/8]>>(7-(x)%8) & 1)
+ static unsigned char l[(64/8)*64]; /* high res crease map */
+ static unsigned char m[32]; /* low res crease map */
+ int x, y, z;
+
+ /* lazily render a high-res crease pattern */
+ if (memcmp(m, ndata[note].creases, sizeof(m))) {
+ memcpy(m, ndata[note].creases, sizeof(m));
+ memset(l, 0, sizeof(l));
+ /* now we have to somehow extrapolate a 64x64 crease pattern from a 16x16
+ sketch... */
+ for (z = 0; z < 3; z++) /* three passes */
+ for (y = 2; y < 62; y++)
+ for (x = 2; x < 62; x++) {
+ int sx = x/4, sy = y/4;
+ int xx = x%4, yy = y%4;
+ int xi = (xx<<1)%3, yi = (yy<<1)%3;
+ int xd = xx<2 ? -1 : 1, yd = yy<2 ? -1 : 1;
+
+ if (z < 2 && ( /* during passes #0 and #1 */
+ (!z && xi && yi && m(sx, sy) &&
+ (m(sx+xd, sy+yd) || m(sx, sy+yd) || m(sx+xd, sy) ) &&
+ ((xd<0 && sx<2) || (xd>0 && sx>14) || (yd<0 && sy<2) ||
+ (yd>0 && sy>14) || m(sx+2*xd, sy+2*yd))
+ ) || (z && (
+ (!xi && !yi && l(x-xd, y-yd) && l(x+2*xd, y+2*yd)) ||
+ (!xi && yi && l(x-xd, y) && l(x+2*xd, y)) ||
+ (xi && !yi && l(x, y-yd) && l(x, y+2*yd))
+ ))))
+ {
+ l[8*y + x/8] |= 1<<(7-x%8);
+ }
+ /* during pass #2, remove isolated dots */
+ if (z == 2 && l(x, y) && !(l(x-1, y-1) || l(x, y-1) ||
+ l(x+1, y-1) || l(x-1, y) || l(x+1, y) || l(x-1, y+1) ||
+ l(x, y+1) || l(x+1, y+1)))
+ {
+ l[8*y + x/8] &= 0xff - (1<<(7-x%8));
+ }
+ }
+ }
+ for (y = b; y < b+h; y++)
+ for (x = a; x < a+w; x++)
+ if (x > 1 && y > 1 && x < 62 && y < 62 && x+y <= 116 && l(x, y))
+ XDrawPoint(display, win, creaseGC, x, y);
+#undef m
+#undef l
+}
+#endif
+
+#ifdef FUNSTUFF
+
+/*
+ * atek sparppoirta ecaitnoo npsceai lcoacisno,sr terusnt ur efit ah tahppnede
+ */
+void
+check_occasion(int d, int m, int y)
+{
+ static const unsigned char sketch0[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x3d, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xe7, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xaf, 0x4d, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x5d, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x0d, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x1f, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x78, 0x9f, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0xfe, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x60, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x30, 0xf0, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x60, 0xf0, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x3e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x03, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
+ };
+ static const unsigned char sketch1[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x03, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x03, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x03, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x07, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0a, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0a, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0a, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x18, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x1b, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x30, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x66, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x48, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x61, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x97, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x90, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0d, 0xc0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x80, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x60, 0x40, 0x80, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x20, 0x80, 0x03, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x56, 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0xe0, 0xf8, 0x1f, 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0xf8, 0x6f, 0xfd, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x03, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x07, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
+ };
+ char *s;
+ int note;
+
+ if (!notes_count && !(state.state_bits & 1)) { /* ewclmo een wsures */
+ if ((note = add_note()) >= 0) {
+ ndata[note].col = 15;
+ memcpy(&ndata[note].sketch[256], sketch0, 256);
+ s = csarbmel(
+ "EWCLMO EotmwipbnaodrhTnaskf rortiygni tuo!t ");
+ strcpy(ndata[note].text, s);
+ free(s);
+ ndata[note].cursor = 50;
+ state.state_bits |= 1;
+ }
+ } else if (notes_count)
+ state.state_bits |= 1; /* purgdadeu essrw not'g tet ihs */
+
+ if (m == 12 && d >= 25 && d <= 26) { /* mXsa */
+ if (!(state.state_bits & 2) && (note = add_note()) >= 0) {
+ ndata[note].col = 14;
+ memcpy(ndata[note].sketch, sketch1, 512);
+ s = csarbmel(
+ " A M REYR MXSA T O Y UO ! ");
+ strcpy(ndata[note].text, s);
+ free(s);
+ state.state_bits |= 2;
+ }
+ } else state.state_bits &= ~2;
+
+ if (m == 1 && d <= 3) { /* eN weYra */
+ if (!(state.state_bits & 4) && (note = add_note()) >= 0) {
+ ndata[note].col = 11;
+ ndata[note].sketch[511] = 0x01;
+ s = csarbmel(
+ "nOeca agni aeyrah saapssde.. .A ynaw,y A H PAYP EN WEYRA!");
+ strcpy(ndata[note].text, s);
+ free(s);
+ state.state_bits |= 4;
+ }
+ } else state.state_bits &= ~4;
+}
+#endif
+
+/*
+ * pastes <text> into note #<note>, starting at <offset>; returns the serial
+ * number of the character right after the pasted string's end; overwrites
+ * previous contents; tries to word-wrap unless <raw> has a true value
+ */
+int
+paste_string(int note, int offset, const char *s, int raw)
+{
+ const char *t;
+ int i, j;
+
+ if (!raw) { /* paste "cooked" */
+ for (i = offset; *s && i < 59; ) {
+ for (; *s && (*s == ' ' || *s == '\t' || *s == '\n'); s++);
+ for (t = s; *t && (t == s || *t == '-' || *(t-1) != '-') &&
+ *t != ' ' && *t != '\t' && *t != '\n'; t++);
+ j = t-s;
+ if (i < 50 && i%10 && i/10 != (i+j-1)/10) i = (i/10+1)*10; /* next l.? */
+ if (j && i <= 58) {
+ if (i+j >= 58) { /* word too long for note? */
+ strncpy(&ndata[note].text[i], s, 59-i);
+ i = 59;
+ } else {
+ strncpy(&ndata[note].text[i], s, j);
+ i += j;
+ }
+ if (*(t-1) != '-' && i%10) i++; /* insert blank? */
+ s = t;
+ }
+ }
+ if (ndata[note].text[i] == ' ') /* place cursor right after the text */
+ for (; i > offset && ndata[note].text[i-1] == ' '; i--);
+ return i;
+ } else { /* paste raw */
+ i = strlen(s);
+ if (offset+i > 59) i = 59-offset;
+ strncpy(&ndata[note].text[offset], s, i);
+ return offset+i;
+ }
+}
+
+/*
+ * pastes <data> into note #<note> at offset <pos>, inserting if <ins>,
+ * word-wrapping unless <raw>
+ */
+void
+paste(int note, int pos, const char *data, int ins, int raw)
+{
+ char *s = 0;
+ int i;
+
+ if (ins) {
+ if (raw) {
+ s = smalloc(60);
+ strcpy(s, ndata[note].text);
+ } else {
+ /* want to wrap subsequent text later, so we have to save it cooked */
+ s = cook(note, 0, 59);
+ }
+ memset(&ndata[note].text[pos], ' ', 59-pos);
+ } else
+ memset(&ndata[note].text[pos], ' ', pos+strlen(data) > 59 ? 59-pos :
+ strlen(data));
+ i = paste_string(note, pos, data, raw);
+ if (ins) {
+ if (i <= 58) paste_string(note, i, &s[pos], raw);
+ free(s);
+ }
+ ndata[note].cursor = i > 58 ? 58 : i;
+}
+
+/*
+ * checks whether state.a_edit is valid (mday range w/respect to month...),
+ * sets the hidden year field to a sensible value; the return value is boolean
+ */
+int
+check_time()
+{
+ struct tm data, *ptr;
+ time_t tt;
+
+ tt = time(0);
+ ptr = localtime(&tt);
+ memcpy(&data, ptr, sizeof(struct tm));
+ data.tm_isdst = -1; /* let mktime() decide */
+ data.tm_year = state.a_edit[2]-1 > ptr->tm_mon ||
+ (state.a_edit[2]-1 == ptr->tm_mon && (state.a_edit[3] > ptr->tm_mday ||
+ (state.a_edit[3] == ptr->tm_mday && (state.a_edit[0] > ptr->tm_hour ||
+ (state.a_edit[0] == ptr->tm_hour && (state.a_edit[1] > ptr->tm_min ||
+ (state.a_edit[1] == ptr->tm_min && !ptr->tm_sec))))))) ? ptr->tm_year :
+ ptr->tm_year+1;
+ data.tm_mon = state.a_edit[2]-1;
+ data.tm_mday = state.a_edit[3];
+ data.tm_hour = state.a_edit[0];
+ data.tm_min = state.a_edit[1];
+ /* check if date is convertible, i.e., valid */
+ tt = mktime(&data);
+ if (tt == -1) return 0;
+ /* check if date has been adapted -> return failure */
+ if (data.tm_mon != state.a_edit[2]-1 || data.tm_mday != state.a_edit[3])
+ return 0;
+ /* adapt date and return */
+ state.a_edit[4] = (unsigned char) data.tm_year; /* char suffices */
+ return 1;
+}
+
+/*
+ * transforms ndata[note].a_time to state.a_edit
+ */
+void
+explode_time(int note)
+{
+ struct tm *ptr;
+
+ if (ndata[note].a_time != -1) {
+ ptr = localtime(&ndata[note].a_time);
+ state.a_edit[0] = ptr->tm_hour;
+ state.a_edit[1] = ptr->tm_min;
+ state.a_edit[4] = ptr->tm_year;
+ } else { /* initialize on first call */
+ time_t tt;
+ time(&tt);
+ tt += 60*60;
+ ptr = localtime(&tt);
+ state.a_edit[0] = ptr->tm_hour;
+ state.a_edit[1] = 0; /* minute */
+ state.a_edit[4] = (unsigned char) ptr->tm_year;
+ }
+ state.a_edit[2] = ptr->tm_mon+1;
+ state.a_edit[3] = ptr->tm_mday;
+}
+
+/*
+ * transforms state.a_edit to ndata[note].a_time
+ */
+void
+implode_time(int note)
+{
+ struct tm data, *ptr;
+ time_t tt;
+
+ time(&tt);
+ ptr = localtime(&tt);
+ memcpy(&data, ptr, sizeof(struct tm));
+ data.tm_isdst = -1; /* let mktime determine */
+ data.tm_hour = state.a_edit[0];
+ data.tm_min = state.a_edit[1];
+ data.tm_sec = 0;
+ /* the following 3 ain't required for daily notes, but this way, the date
+ will be saved even in this case, so we set them */
+ data.tm_year = state.a_edit[4];
+ data.tm_mon = state.a_edit[2]-1;
+ data.tm_mday = state.a_edit[3];
+ ndata[note].a_time = mktime(&data); /* shouldn't fail */
+}
+
+/*
+ * evaluating its a_flags, returns an updated value of a note's a_time field,
+ * considering <now> the current time if != -1
+ */
+time_t
+adapt_time(int note, time_t now)
+{
+ time_t res = ndata[note].a_time;
+
+ /* adaption is only necessary in the case of a daily alarm */
+ if (!(ndata[note].a_flags & ALARM_DATE)) {
+ struct tm atm, *ptr;
+
+ if (now == -1) time(&now);
+ ptr = localtime(&res); /* break down alarm time */
+ memcpy(&atm, ptr, sizeof(struct tm));
+ ptr = localtime(&now); /* break down current time */
+ atm.tm_year = ptr->tm_year;
+ atm.tm_mon = ptr->tm_mon;
+ atm.tm_mday = ptr->tm_mday;
+ atm.tm_sec = 0; /* just making sure */
+ res = mktime(&atm);
+ if (res < now) res += 24*60*60; /* NOT `<='! */
+ }
+
+ return res;
+}
+
+/*
+ * sets the state variable's alarm sub structure's values for the next alarm
+ */
+/*
+ Together with appropriate calls from wmpinboard.c, this function results
+ in the following strategy for scheduling alarms:
+
+ - firstly, alarms with ALARM_DATE set are executed only once and get
+ deactivated afterwards; daily alarms remain active even after an alarm
+ - if the program is started after some alarm time was reach, the alarm
+ is displayed if (and only if) it's a date-specific alarm
+ - if an alarm is running while another one becomes due, this subsequent
+ alarm will be run after the user has closed the other note
+ - to achieve the latter, time_next_alarm() considers all notes with an
+ alarm time equal to or greater than the previous one; if they're equal,
+ the new alarm's note's ID must be greater than that of the previous
+ alarm (this realizes a determined order for concurrent alarms and
+ allows the program to avoid running one daily alarm several times in
+ a row)
+*/
+void
+time_next_alarm()
+{
+ time_t prev_time = state.alarm.time;
+ int i, prev_note = state.alarm.note;
+
+ if (state.alarm.note >= 0 && !state.alarm.run) prev_note = -1;
+ if (prev_time == -1 || prev_time > time(0)) prev_time = time(0);
+ state.alarm.time = -1;
+ state.alarm.note = -1;
+ state.alarm.run = 0;
+ for (i = 0; i < notes_count; i++)
+ if (ndata[i].a_flags & ALARM_ON) {
+ time_t tt = adapt_time(i, prev_time);
+ if (((ndata[i].a_flags & ALARM_DATE) || (tt == prev_time &&
+ i > prev_note) || (tt > prev_time)) &&
+ (state.alarm.time == -1 || tt < state.alarm.time))
+ {
+ state.alarm.time = tt;
+ state.alarm.note = i;
+ }
+ }
+ if (state.alarm.note < 0 && prev_note >= 0 &&
+ (ndata[prev_note].a_flags & ALARM_ON))
+ { /* if there is just one daily alarm, it hasn't been set again above, so
+ let's do it here explicitly */
+ state.alarm.note = prev_note;
+ state.alarm.time = adapt_time(prev_note, prev_time+1);
+ }
+}
+
diff --git a/src/notes.h b/src/notes.h
new file mode 100644
index 0000000..b3b04db
--- /dev/null
+++ b/src/notes.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 1998-2000 by Marco G"otze.
+ *
+ * This code is part of the wmpinboard source package, which is
+ * distributed under the terms of the GNU GPL2.
+ */
+
+#ifndef NOTES_H_INCLUDED
+#define NOTES_H_INCLUDED
+
+#include <time.h>
+
+#include "wmpinboard.h"
+
+#define draw_sketch(note) draw_sketch_area(note, 1, 1, 62, 62)
+
+int add_note();
+void remove_note(int);
+int raise_note(int);
+int note_tilt(int);
+int note_empty(int);
+void color_notes(int);
+void pin_note(int);
+void render_pinboard(int);
+void render_note(int);
+int selected_note(int, int);
+void print_letter(int, int, int);
+void print_text(int);
+void shift_string(int, int, int, int, int);
+void draw_sketch_area(int, int, int, int, int);
+int bbar_button(int, int);
+int abar_area(int, int);
+void init_edit_mode(int);
+void render_abar_number(int);
+void render_abar_switches(int);
+void render_abar(int);
+char *cook(int, int, int);
+void dump_notes(int);
+int char_at(int, int, int);
+int paste_string(int, int, const char*, int);
+void paste(int, int, const char*, int, int);
+int check_time();
+void explode_time(int);
+void implode_time(int);
+time_t adapt_time(int, time_t);
+void time_next_alarm();
+
+#ifdef CREASES
+
+#include <X11/Xlib.h>
+
+#define render_edit_wear_area(note, x, y, w, h) \
+ render_edit_wear_area_win(app, note, x, y, w, h)
+#define render_edit_wear(note) render_edit_wear_area(note, 1, 1, 62, 62)
+
+void wear_note(int);
+void render_wear(int);
+void render_edit_wear_area_win(Window, int, int, int, int, int);
+
+#endif
+
+#ifdef FUNSTUFF
+void check_occasion(int, int, int);
+#endif
+
+#endif /* NOTES_H_INCLUDED */
+
diff --git a/src/wmpinboard.c b/src/wmpinboard.c
new file mode 100644
index 0000000..c1a6235
--- /dev/null
+++ b/src/wmpinboard.c
@@ -0,0 +1,2299 @@
+/*
+ * Copyright (C) 1998-2000 by Marco G"otze.
+ *
+ * This code is part of the wmpinboard source package, which is
+ * distributed under the terms of the GNU GPL2.
+ */
+
+#include <ctype.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/cursorfont.h>
+#include <X11/keysym.h>
+#include <X11/xpm.h>
+#include <X11/extensions/shape.h>
+
+#include "wmpinboard.h"
+#include "misc.h"
+#include "notes.h"
+#include "xmisc.h"
+
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "../xpm/pinboard.xpm"
+#include "../xpm/bbar.xpm"
+#include "../xpm/abar.xpm"
+#include "../xpm/digits.xpm"
+
+const char *rc_file_name = "/.wmpinboarddata"; /* to be prepended with dir */
+
+const char c_group[C_NUM] = /* group available note colors */
+ { 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 0, 0, 0 };
+
+#define NUM_FONTS 3
+const struct { /* shortcuts for known fonts */
+ char name[8];
+ char desc[40];
+ char font[60];
+} fonts[NUM_FONTS] = {
+ { "Default", "default font (ISO8859-1)", "-*-fixed-*--10-*-iso8859-1" },
+ { "Latin2", "Latin2 (ISO8859-2)", "-*-fixed-*--10-*-iso8859-2" },
+ { "Russian", "cyrillic font (KOI8-R)", "-*-fixed-*--10-*-koi8-r" }
+};
+const char *default_font = fonts[0].font;
+
+opts_t opts = { 0, 0, 0, -1, TIMEOUT, 1, "", "", "" };
+
+palette_t palette[C_NUM+1];
+Cursor cursors[3]; /* alternate X cursors */
+
+int label_coords[4] = { 12, 0, 39, 9 }; /* default "TO DO" label coords */
+
+data_t ndata[MAX_NOTES]; /* this holds all the notes */
+int notes_count = 0;
+
+state_t state = { 0, { 0, 0 }, 0, -1, -1, 0, 0, M_NOOP, -1, -1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, { -1, -1, 0, 0, None } };
+
+Display *display;
+Window mainwin, iconwin, win; /* win = visible window */
+Pixmap app = None, bbar = None, abar = None, digits = None;
+XImage *img = 0;
+GC normalGC = 0, fontGC = 0, fillGC = 0;
+#ifdef CREASES
+GC creaseGC = 0;
+#endif
+XFontStruct *font = 0;
+
+sigset_t sync_sig_set; /* signals used for syncing with other instances */
+int sync_sig_block_counter = 0;
+
+ /*************/
+/******************************* FUNCTIONS ***********************************/
+ /*************/
+
+void s_block();
+void s_unblock();
+int notes_io(int);
+void load_theme(const char*);
+void init(void);
+void done(void);
+int try_font(const char*, int);
+void load_font(void);
+void set_kbfocus(int);
+void action(actions, const void*);
+void help(void);
+void parse_argv(int, char**);
+void draw_cursor(int, int);
+void set_cursor(int, int);
+void sel_text(int);
+void draw_pixel(int, int);
+void set_mode(modes);
+void quit_edit_mode(int, int);
+void timer(unsigned int);
+void animate_bbar(int);
+void animate_abar(int);
+void animate_note(int);
+void animate_alarm();
+void handle_ButtonPress(XEvent*);
+void handle_ButtonRelease(XEvent*);
+void handle_MotionNotify(XEvent*);
+void handle_KeyPress(XEvent*);
+void handle_sigs(int);
+
+/*
+ * blocks the signals in sync_sig_set
+ */
+inline void
+s_block()
+{
+ if (!sync_sig_block_counter++)
+ sigprocmask(SIG_BLOCK, &sync_sig_set, 0);
+}
+
+/*
+ * inverse of the above
+ */
+inline void
+s_unblock()
+{
+ if (!--sync_sig_block_counter)
+ sigprocmask(SIG_UNBLOCK, &sync_sig_set, 0);
+}
+
+/*
+ * reads or writes the data file; returns the PID the file was last written by
+ *
+ * file format revisions:
+ * 0 = v0.7+: introduced binary data file format
+ * 1 = v0.8+: added <data_t.{cursor,sketch}>
+ * 2 = v0.9+: added <data_t.creases>, <state.state_bits>
+ * 3 = v0.9.1+: added <PID>
+ * 4 = v0.10+: added <theme>
+ * 5 = v0.99: added <counter>
+ * 6 = v0.99.1: added <alarm_cmd>, <data_t.a_{time,flags}>
+ */
+int
+notes_io(int save)
+{
+ /* size of per-note records for specific revisions */
+ static const int size_0 = sizeof(struct { int a, b, c; char d[60]; });
+ static const int size_1 =
+ sizeof(struct { int a, b, c; char d[60]; int e; char f[512]; });
+ static const int size_2 =
+ sizeof(struct { int a, b, c; char d[60]; int e; char f[512]; char g[32]; });
+ static const int size_6 = sizeof(struct { int a, b, c; char d[60]; int e;
+ char f[512]; char g[32]; time_t h; char i; });
+ static const char *ext = ".new";
+ static const char *header = "WMPB6"; /* data file header */
+ char s[STRING_BUF_SIZE];
+ char t[STRING_BUF_SIZE];
+ FILE *file;
+ int pid = (int) getpid();
+ static int sizes[6];
+ sizes[0] = size_0;
+ sizes[1] = size_1;
+ sizes[2] = size_2;
+ sizes[3] = size_2;
+ sizes[4] = size_2;
+ sizes[5] = size_2;
+ sizes[6] = size_6;
+
+ s_block();
+
+ s[sizeof(s)-1] = '\0';
+ strncpy(s, getenv("HOME"), sizeof(s));
+ if (sizeof(s)-strlen(s)-1 < strlen(rc_file_name)+strlen(ext))
+ die("Buffer too small in notes_io().");
+ strcat(s, rc_file_name);
+ if (save) { /* save to temporary file first, later rename it */
+ strcpy(t, s);
+ strcat(s, ext);
+ }
+
+ file = fopen(s, save ? "wb" : "rb");
+ if (!file) {
+ if (save)
+ die("Couldn't open data file for writing.");
+ else {
+ s_unblock();
+ return pid; /* just don't read in anything if opening the file failed */
+ }
+ }
+
+ if (save) { /*** SAVE ***/
+ fwrite(header, 5, 1, file);
+ fputs(opts.font, file);
+ fputc('\n', file);
+ fputs(opts.theme, file);
+ fputc('\n', file);
+ fputs(opts.alarm_cmd, file);
+ fputc('\n', file);
+ fwrite(&pid, sizeof(pid), 1, file);
+ fwrite(&state.state_bits, sizeof(state.state_bits), 1, file);
+ fwrite(&state.counter, sizeof(state.counter), 1, file);
+ fwrite(ndata, sizeof(data_t), notes_count, file);
+ fclose(file);
+
+ strcpy(s, t);
+ strcat(s, ext);
+ if (rename(s, t) == -1) {
+ fprintf(stderr, "Fatal error: Failed to rename file `%s' to `%s'.\n", t,
+ s);
+ exit(EXIT_FAILURE);
+ }
+ } else { /*** LOAD ***/
+ int rev;
+
+ if (!fread(s, 5, 1, file) || strncmp(s, header, 4)) { /* check header */
+ fprintf(stderr, "Fatal error: Corrupt data file encountered.\n"
+ "Delete `~%s' to start over.\n", rc_file_name);
+ fclose(file);
+ exit(EXIT_FAILURE);
+ }
+ /* file format revision check */
+ s[5] = '\0';
+ rev = strtol(&s[4], 0, 10);
+ if (rev > 6) {
+ fprintf(stderr, "Fatal error: The data file seems to have been created "
+ "by a more recent version\nof wmpinboard. Try and find a newer "
+ "release, or remove `~%s'\nand lose all existing notes.\n",
+ rc_file_name);
+ fclose(file);
+ exit(EXIT_FAILURE);
+ }
+
+ fgets(opts.font, sizeof(opts.font), file);
+ if (strlen(opts.font) && opts.font[strlen(opts.font)-1] == '\n')
+ opts.font[strlen(opts.font)-1] = '\0';
+
+ if (rev >= 4) {
+ fgets(opts.theme, sizeof(opts.theme), file);
+ if (strlen(opts.theme) && opts.theme[strlen(opts.theme)-1] == '\n')
+ opts.theme[strlen(opts.theme)-1] = '\0';
+ }
+ if (rev >= 6) {
+ fgets(opts.alarm_cmd, sizeof(opts.alarm_cmd), file);
+ if (strlen(opts.alarm_cmd) && opts.alarm_cmd[strlen(opts.alarm_cmd)-1] ==
+ '\n')
+ {
+ opts.alarm_cmd[strlen(opts.alarm_cmd)-1] = '\0';
+ }
+ }
+ if (rev >= 3) fread(&pid, sizeof(pid), 1, file); /* last writer's PID */
+ if (rev >= 2) /* state.state_bits */
+ fread(&state.state_bits, sizeof(state.state_bits), 1, file);
+ else
+ state.state_bits = 0;
+ if (rev >= 5) /* counter */
+ fread(&state.counter, sizeof(state.counter), 1, file);
+ else
+ state.counter = 0;
+
+ notes_count = 0;
+ while (notes_count < MAX_NOTES)
+ if (fread(&ndata[notes_count], sizes[rev], 1, file)) {
+ switch (rev) {
+ case 0:
+ ndata[notes_count].cursor = 0;
+ memset(ndata[notes_count].sketch, 0, 512);
+ case 1:
+ memset(ndata[notes_count].creases, 0, 32);
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ ndata[notes_count].a_time = -1;
+ ndata[notes_count].a_flags = ALARM_DATE;
+ default:
+ notes_count++;
+ }
+ } else break;
+ if (!state.counter) state.counter = notes_count;
+ fclose(file);
+ state.alarm.time = -1;
+ state.alarm.note = -1;
+ time_next_alarm();
+ }
+
+ s_unblock();
+ return pid;
+}
+
+/*
+ * loads theme from <filename>
+ */
+void
+load_theme(const char *filename)
+{
+#ifdef LOW_COLOR
+ if (strlen(filename))
+ WARN("Skipped loading the configured theme since themes aren't\nsupported"
+ "at low color depths.");
+#else
+ int coords[4], eof = 0, i;
+ char s[STRING_BUF_SIZE], *t, *p, *q;
+ struct stat buf;
+ FILE *file;
+
+ if (!strlen(filename)) return;
+ file = fopen(filename, "r");
+ if (!file) {
+ WARN("Failed to open theme file; check whether the location you specified\n"
+ "is valid. Reverting to default.");
+ return;
+ }
+ if (!fgets(s, sizeof(s), file) || strncmp(s, "WMPBtheme", 9)) {
+ fclose(file);
+ WARN("Configured theme file is corrupted! Reverting to default.");
+ return;
+ }
+
+ /* we'll need at most <file size> bytes to buffer the XPM data read from it */
+ lstat(filename, &buf);
+ if (!(t = malloc(buf.st_size))) {
+ fclose(file);
+ WARN("Skipped loading configured theme due to memory shortage.");
+ return;
+ }
+
+ /* parse theme headers... */
+ if (s[strlen(s)-1] == '\n') s[strlen(s)-1] = '\0';
+ memset(s, ' ', 9); /* kludge */
+ do {
+ if ((p = strchr(s, '#'))) *p = '\0'; /* # comments */
+ p = s;
+ while (*p) {
+ for (; *p == ' ' || *p == '\t'; p++);
+ for (q = p; isalpha(*q); q++);
+ if (q > p) {
+ if (!strncasecmp(p, "label", q-p)) {
+ for (p = q; *p == ' ' || *p == '\t'; p++);
+ if (*p == '=') {
+ for (p++; *p == ' ' || *p == '\t'; p++);
+ for (i = 0; *p && i < 4; i++) {
+ coords[i] = (int) strtol(p, &p, 10);
+ for (; *p && (*p < '0' || *p > '9'); p++);
+ }
+ q = p;
+ if (i >= 4)
+ if (
+ coords[0] < 0 || coords[0] > 51 ||
+ coords[2] < 0 || coords[2] > 51 ||
+ coords[1] < 0 || coords[1] > 59 ||
+ coords[3] < 0 || coords[3] > 59 ||
+ coords[0] >= coords[2] || coords[1] >= coords[3])
+ {
+ WARN("Theme header `label' followed by coordinates outside the"
+ "\nallowed range--ignored.");
+ } else {
+ if ((coords[2]-coords[0])*(coords[3]-coords[1]) >= 16)
+ memcpy(&label_coords, &coords, sizeof(coords));
+ else
+ WARN("Label Label area defined by the configured theme's "
+ "headers is too small--ignored.");
+ }
+ else
+ WARN("Theme header `label' followed by improper specification--"
+ "ignored.");
+ } else {
+ WARN("Theme header `label' lacks specification.");
+ q = p;
+ }
+ } else
+ fprintf(stderr, "Warning: Invalid header field in theme: `%s'.\n", p);
+ }
+ for (p = q; *p && *p != ' ' && *p != '\t'; p++);
+ }
+ eof = !fgets(s, sizeof(s), file);
+ if (strlen(s) && s[strlen(s)-1] == '\n') s[strlen(s)-1] = '\0';
+ } while (!eof && strlen(s));
+
+ /* now read all pixmap data sections from the remainder of the file... */
+ while (!eof) {
+ Pixmap pic;
+ XpmAttributes attr;
+
+ attr.valuemask = XpmExactColors | XpmCloseness;
+ attr.exactColors = False;
+ attr.closeness = 65536;
+ for (p = t; !(eof = !fgets(s, sizeof(s), file)) && strlen(s) && *s != '\n';)
+ {
+ strcpy(p, s);
+ p += strlen(s);
+ }
+ if (!strstr(t, "char")) continue; /* silently ignore garbage */
+ if (XpmCreatePixmapFromBuffer(display, RootWindow(display,
+ DefaultScreen(display)), t, &pic, 0, &attr) == XpmSuccess)
+ {
+ if (attr.width == 52 && attr.height == 60) /* board pixmap */
+ XGetSubImage(display, pic, 0, 0, 52, 60, ~0, ZPixmap, img, 6, 2);
+ else if (attr.width == 58 && attr.height == 30) /* bbar pixmap */
+ XCopyArea(display, pic, bbar, normalGC, 0, 0, 58, 30, 0, 0);
+ else if (attr.width == 58 && attr.height == 28) /* abar pixmap */
+ XCopyArea(display, pic, abar, normalGC, 0, 0, 58, 28, 0, 0);
+ else if (attr.width == 60 && attr.height == 9) /* abar digits */
+ XCopyArea(display, pic, digits, normalGC, 0, 0, 60, 9, 0, 0);
+ else /* invalid pixmap */
+ WARN("Configured theme contains pixmap of invalid dimensions--"
+ "ignored.");
+ XFreePixmap(display, pic);
+ } else
+ WARN("Encountered invalid pixmap data in configured theme--ignored.");
+ }
+ free(t);
+#endif
+}
+
+/*
+ * tries to exit properly
+ */
+void
+done(void)
+{
+ if (normalGC) XFreeGC(display, normalGC);
+ if (fontGC) XFreeGC(display, fontGC);
+ if (fillGC) XFreeGC(display, fillGC);
+#ifdef CREASES
+ if (creaseGC) XFreeGC(display, creaseGC);
+#endif
+ if (font) XFreeFont(display, font);
+ if (app != None) XFreePixmap(display, app);
+ if (bbar != None) XFreePixmap(display, bbar);
+ if (abar != None) XFreePixmap(display, abar);
+ if (digits != None) XFreePixmap(display, digits);
+ if (state.alarm.buffer != None) XFreePixmap(display, state.alarm.buffer);
+ if (img) XDestroyImage(img);
+ XCloseDisplay(display);
+ if (opts.display) free(opts.display);
+}
+
+/*
+ * handler for caught signals
+ */
+void
+handle_sigs(int sig)
+{
+ /* note: the kludges below will result in the application being terminated
+ if the applet was destroyed via the respective WM option but not yet
+ terminated (apparently WM just destroys the window in this case) */
+ XWindowAttributes attr;
+ const char *s = 0;
+
+ s_block();
+
+ switch (sig) {
+ case SIGALRM: /* used in animation timing */
+ state.alarmed = 1;
+ break;
+ case SIGUSR1:
+ XGetWindowAttributes(display, win, &attr); /* kludge */
+ /* quit edit mode w/saving; if not in edit mode, just save */
+ if (state.mode == M_MOVE) set_mode(M_NOOP);
+ if (state.mode != M_NOOP)
+ quit_edit_mode(0, 1); /* saves */
+ else
+ notes_io(1);
+ break;
+ case SIGUSR2:
+ XGetWindowAttributes(display, win, &attr); /* kludge */
+ /* quit edit mode w/o saving... */
+ if (state.mode != M_NOOP && state.mode != M_MOVE) quit_edit_mode(0, 0);
+ notes_io(0); /* reread data */
+ notes_io(1); /* rewrite data file */
+ render_pinboard(-1);
+ redraw_window();
+ break;
+ case SIGINT:
+ s = "SIGINT";
+ case SIGTERM:
+ if (!s) s = "SIGTERM";
+ fprintf(stderr, "Caught %s. Trying to exit cleanly...\n", s);
+ if (state.mode != M_NOOP) {
+ if (note_empty(state.cur_note)) remove_note(state.cur_note);
+ notes_io(1);
+ }
+ exit(EXIT_SUCCESS);
+ }
+
+ s_unblock();
+}
+
+/*
+ * initializes the application's X window, installs a signal handler
+ */
+void
+init(void)
+{
+ struct sigaction sigact;
+ XWMHints wmhints;
+ XSizeHints shints;
+ Atom atom;
+ XTextProperty name;
+ XGCValues gcv;
+ unsigned long gcm;
+ int screen;
+
+ /* set up syncsig_set */
+ sigemptyset(&sync_sig_set);
+ sigaddset(&sync_sig_set, SIGUSR1);
+ sigaddset(&sync_sig_set, SIGUSR2);
+ /* install signal handler */
+ sigact.sa_handler = handle_sigs;
+ sigemptyset(&sigact.sa_mask);
+ sigact.sa_flags = 0;
+ if (sigaction(SIGALRM, &sigact, 0) < 0 ||
+ sigaction(SIGUSR1, &sigact, 0) < 0 ||
+ sigaction(SIGUSR2, &sigact, 0) < 0 ||
+ sigaction(SIGINT, &sigact, 0) < 0 ||
+ sigaction(SIGTERM, &sigact, 0) < 0)
+ {
+ die("Unable to install signal handlers.");
+ }
+
+ if (!(display = XOpenDisplay(opts.display))) {
+ fprintf(stderr, "Fatal error: Can't open display `%s'.\n",
+ XDisplayName(opts.display));
+ exit(EXIT_FAILURE);
+ }
+
+ /* try to set up done() to be called upon exit() */
+ if (atexit(done)) die("Failed to set up exit procedure.");
+
+ screen = DefaultScreen(display);
+
+ atom = XInternAtom(display, "WM_DELETE_WINDOW", 0);
+ if (atom == None) die("No window manager running.");
+
+ /* try to autodetect whether we're running Window Maker... */
+ if (opts.window_state < 0) /* no window state explicitly specified */
+ opts.window_state = XInternAtom(display, "_WINDOWMAKER_WM_FUNCTION", 1) !=
+ None ? WithdrawnState : NormalState;
+
+ mainwin = create_win();
+ wmhints.window_group = mainwin;
+ wmhints.initial_state = opts.window_state;
+ wmhints.flags = StateHint | WindowGroupHint | IconWindowHint;
+ if (opts.animate) wmhints.flags |= XUrgencyHint; /* of any use at all? */
+ if (opts.window_state == WithdrawnState) {
+ iconwin = create_win();
+ wmhints.icon_window = iconwin;
+ win = iconwin;
+ } else {
+ wmhints.icon_window = None;
+ win = mainwin;
+ }
+ XSetWMHints(display, mainwin, &wmhints);
+ XSetWMProtocols(display, mainwin, &atom, 1);
+
+ shints.min_width = 64;
+ shints.min_height = 64;
+ shints.max_width = 64;
+ shints.max_height = 64;
+ shints.x = 0;
+ shints.y = 0;
+ shints.flags = PMinSize | PMaxSize | USPosition;
+ XSetWMNormalHints(display, mainwin, &shints);
+
+ XSelectInput(display, win, ButtonPressMask | ExposureMask |
+ ButtonReleaseMask | PointerMotionMask | StructureNotifyMask |
+ KeyPressMask | EnterWindowMask | LeaveWindowMask | FocusChangeMask);
+ if (!XStringListToTextProperty(&opts.name, 1, &name))
+ die("Can't allocate window name.");
+ XSetWMName(display, mainwin, &name);
+
+ set_mask(1);
+ app = get_xpm((char**) pinboard_xpm);
+ XMapSubwindows(display, win);
+
+ gcm = GCForeground | GCBackground | GCGraphicsExposures;
+ gcv.foreground = WhitePixel(display, screen);
+ gcv.background = BlackPixel(display, screen);
+ gcv.graphics_exposures = 0;
+ normalGC = XCreateGC(display, RootWindow(display, screen), gcm, &gcv);
+}
+
+/*
+ * tries to load font name, dies upon an error if <fatal> is true
+ */
+int
+try_font(const char *name, int fatal)
+{
+ if (font) return 1;
+ if (!name || !*name) return 0;
+ font = XLoadQueryFont(display, name);
+ if (font) {
+ if (font->max_bounds.rbearing - font->min_bounds.lbearing > 7 ||
+ font->max_bounds.ascent + font->max_bounds.descent > 10)
+ {
+ fprintf(stderr, "Warning: The font `%s'\n"
+ "lacks a fixed character cell size of 6x10.\n", name);
+ XFreeFont(display, font);
+ font = 0;
+ } else {
+ if (!notes_count)
+ strncpy(opts.font, name, sizeof(opts.font));
+ return 1;
+ }
+ } else
+ fprintf(stderr, "Warning: The font `%s' doesn't exist.\n", name);
+ if (fatal)
+ die("No alternatives left, aborting.");
+ else
+ fprintf(stderr, "Trying to revert...\n");
+ return 0;
+}
+
+/*
+ * loads the font to be used
+ */
+void
+load_font(void)
+{
+ XGCValues gcv;
+ unsigned long gcm;
+
+ try_font(opts.font, 0);
+ try_font(default_font, 1);
+
+ gcm = GCForeground | GCBackground | GCFillStyle | GCLineStyle | GCFont |
+ GCGraphicsExposures;
+ gcv.foreground = C_OUTER;
+ gcv.background = C_INNER;
+ gcv.font = font->fid;
+ gcv.fill_style = FillSolid;
+ gcv.line_style = LineSolid;
+ gcv.graphics_exposures = 0;
+ fontGC = XCreateGC(display, app, gcm, &gcv);
+#ifdef CREASES
+ gcm ^= GCBackground | GCFont;
+ creaseGC = XCreateGC(display, app, gcm, &gcv);
+#endif
+}
+
+/*
+ * (un)sets the keyboard input focus to wmpinboard's window
+ */
+void
+set_kbfocus(int get_it)
+{
+ if (get_it)
+ XSetInputFocus(display, win, RevertToPointerRoot, CurrentTime);
+ else if (!opts.click_to_focus)
+ XSetInputFocus(display, PointerRoot, RevertToNone, CurrentTime);
+}
+
+/*
+ * takes an action as specified by <type> (for command-line options affection
+ * notes rather than interactive behavior)
+ */
+void
+action(actions type, const void *data)
+{
+ const char *s;
+ int pid, running;
+ int i, j, k;
+ FILE *file;
+
+ pid = notes_io(0);
+ running = flush_instance(pid);
+ if (running) pid = notes_io(0);
+ switch (type) {
+ case C_FONT:
+ if (running) die("Can't change font while another instance is running.");
+ if (notes_count) die("Can't change font if the pinboard isn't empty.");
+ for (i = 0; i < NUM_FONTS; i++)
+ if (!strcasecmp(data, fonts[i].name)) {
+ strcpy(opts.font, fonts[i].font);
+ break;
+ }
+ if (i >= NUM_FONTS) { /* not a predefined font */
+ if (strlen(data) >= sizeof(opts.font)) {
+ /* avoid trouble when retrieving saved data... */
+ fprintf(stderr, "Fatal error: Specified font descriptor exceeds "
+ "buffer size of %d bytes.\n", sizeof(opts.font));
+ exit(EXIT_FAILURE);
+ }
+ strcpy(opts.font, data);
+ }
+ notes_io(1);
+ fprintf(stderr, "Successfully changed the font.\n");
+ break;
+ case C_THEME:
+ if (running) die("Can't change theme while another instance is running.");
+ if (!strlen(data) || !strcasecmp(data, "default")) {
+ opts.theme[0] = '\0';
+ fprintf(stderr, "Reverted to default theme.\n");
+ } else {
+ if (strlen(data) >= sizeof(opts.theme)) {
+ fprintf(stderr, "Fatal error: Specified theme file location exceeds "
+ "buffer size of %d bytes.\n", sizeof(opts.theme));
+ exit(EXIT_FAILURE);
+ }
+ strcpy(opts.theme, data);
+ if (!(file = fopen(opts.theme, "r")))
+ WARN("Can't open specified theme file.");
+ else
+ fclose(file);
+ fprintf(stderr, "Successfully configured for specified theme.\n");
+ }
+ notes_io(1);
+ break;
+ case C_ALARM_CMD:
+ if (!strlen(data)) {
+ opts.alarm_cmd[0] = '\0';
+ fprintf(stderr, "Disabled alarm command.\n");
+ } else {
+ if (strlen(data) >= sizeof(opts.alarm_cmd)) {
+ fprintf(stderr, "Fatal error: Specified theme file location exceeds "
+ "buffer size of %d bytes.\n", sizeof(opts.alarm_cmd));
+ exit(EXIT_FAILURE);
+ }
+ strcpy(opts.alarm_cmd, data);
+ fprintf(stderr, "Successfully configured specified alarm command.\n");
+ }
+ notes_io(1);
+ if (running) {
+ kill(pid, SIGUSR2);
+ sleep(1); /* don't return to the prompt too quickly... */
+ }
+ break;
+ case A_DUMP:
+ dump_notes(1);
+ break;
+ case A_DUMP_RAW:
+ dump_notes(0);
+ break;
+#ifdef CREASES
+ case A_IRON:
+ for (i = 0; i < notes_count; memset(ndata[i++].creases, 0, 32));
+ notes_io(1);
+ if (running) {
+ kill(pid, SIGUSR2);
+ sleep(1); /* don't return to the prompt too quickly... */
+ }
+ fprintf(stderr,
+ "Hey, ironing isn't part of my job contract, you know...\n");
+ break;
+#endif
+ case A_DEL:
+ i = (int) strtol((const char*) data, 0, 10);
+ if (i < 0 || i >= notes_count) die("Specified note doesn't exist.");
+ remove_note(i);
+ notes_io(1);
+ if (running) {
+ kill(pid, SIGUSR2);
+ sleep(1); /* don't return to the prompt too quickly... */
+ }
+ fprintf(stderr, "Deleted note #%d.\n", i);
+ break;
+ case A_ADD: case A_ADD_RAW:
+ if ((k = add_note()) < 0) die("Maximal number of notes reached.");
+ s = (const char*) data;
+ while (*s == '%') {
+ if (!strncmp("%%", s, 2))
+ s++;
+ else if (strlen(s) >= 2 && *s == '%') { /* color/position code given */
+ if (isalpha(s[1])) { /* color code? */
+ i = 0;
+ switch (toupper(s[1])) {
+ case 'G': i = 0; break;
+ case 'Y': i = 1; break;
+ case 'R': i = 2; break;
+ case 'B': i = 3; break;
+ default: die("Unknown color code.");
+ }
+ while (c_group[ndata[k].col] != i) ndata[k].col = rand() % C_NUM;
+ }
+ if (!isalpha(s[1])) { /* position code? */
+ i = s[1] - '0';
+ if (!i--) die("Invalid position code.");
+ ndata[k].x = 6+i%3*12 + rand()%12;
+ ndata[k].y = 3+(2-i/3)*14 + rand()%14;
+ }
+ s += 2;
+ }
+ }
+ if (!strlen(s)) die("Won't add blank note.");
+ i = paste_string(k, 0, s, type == A_ADD_RAW);
+ ndata[k].cursor = i > 58 ? 58 : i;
+ notes_io(1);
+ if (running) {
+ kill(pid, SIGUSR2);
+ sleep(1); /* don't return to the prompt too quickly... */
+ }
+ fprintf(stderr, "Added note #%d.\n", k);
+ break;
+ case A_EXPORT:
+ k = strtol((const char*) data, 0, 10);
+ if (k < 0 || k > notes_count-1) die("Specified note doesn't exist.");
+ puts("static const char sketch[512] = {");
+ for (i = 0; i < 64; i++) {
+ printf(" ");
+ for (j = 0; j < 8; j++) {
+ if (j) printf(", ");
+ printf("0x%02x", (unsigned char) ndata[k].sketch[i*8+j]);
+ }
+ if (i < 63) printf(",");
+ printf("\n");
+ }
+ puts("};");
+ break;
+ case M_INFO:
+ for (i = 0, k = 0; i < notes_count; i++)
+ if (ndata[i].a_flags & ALARM_ON) k++;
+ printf("user configuration:\n"
+ " - font: %s\n"
+ " - theme: %s\n"
+ " - alarm command: %s\n",
+ strlen(opts.font) ? opts.font : fonts[0].font,
+ strlen(opts.theme) ? opts.theme : "[default]",
+ strlen(opts.alarm_cmd) ? opts.alarm_cmd : "[none]");
+ printf("\nuseless statistics:\n"
+ " - wmpinboard has saved you (at least) %d real note%s so far\n"
+ " - there %s currently %d note%s on your board\n"
+ " - %d note%s\n",
+ state.counter, (state.counter != 1 ? "s" : ""),
+ (notes_count != 1 ? "are" : "is"), notes_count,
+ (notes_count != 1 ? "s" : ""), k,
+ (k != 1 ? "s have alarms set" : " has an alarm set"));
+ }
+ exit(EXIT_SUCCESS);
+}
+
+/*
+ * prints a help screen and exits
+ */
+void
+help(void)
+{
+ int i;
+
+ printf("wmpinboard v" VERSION "\n\n"
+ "Copyright (C) 1998-2000 by Marco G\"otze, <mailto:gomar at mindless.com>.\n"
+ "This program is distributed under the terms of the GNU GPL2.\n\n"
+ "usage: %s [options]\n\n"
+ "configuration directives (see the documentation for detailed information):\n"
+ " --font=FONT use the specified font; FONT can be one of the\n"
+ " following:\n",
+ opts.name);
+ for (i = 0; i < NUM_FONTS; i++)
+ printf(" %-8s %s\n", fonts[i].name, fonts[i].desc);
+ printf(
+ " or a complete X descriptor of a fixed size 6x10\n"
+ " font\n"
+ " --theme=FILE use the specified theme rather than the default\n"
+ " board/panel images (\"default\" or zero-length\n"
+ " string reverts to default)\n"
+ " --alarm-cmd=CMD execute the specified command on alarms\n"
+ " (\"\" disables)\n\n"
+ "run-time options:\n"
+ " -d DISP, --display=DISP use the specified X display\n"
+ " -n, --normal-state force NormalState (AS Wharf) \\ mutually\n"
+ " -w, --withdrawn-state force WithdrawnState (WM dock) / exclusive\n"
+ " -t TIME, --timeout=TIME set edit mode timeout to TIME seconds\n"
+ " (default %ds, 0 disables)\n"
+ " -c, --click-to-focus emulate click-based keyboard focus\n"
+ " --light no animations\n\n"
+ "command-line actions:\n"
+ " --dump dump the contents of all notes\n"
+ " --dump-raw dump the *raw* contents of all notes\n"
+ " --del=NUMBER delete note NUMBER (as identified by a dump)\n"
+ " --add=STRING add a note with STRING as its contents, trying\n"
+ " to word-wrap the text\n"
+ " --add-raw=STRING add a note with STRING as its *raw* contents\n\n"
+ "general options:\n"
+ " -h, --help print this help\n"
+ " -i, --info print user configuration and statistical\n"
+ " information\n"
+ " -v, --version print some more detailed version information\n\n"
+ "See the wmpinboard(1) man page for more information, hints, and explanations.\n"
+ "For themes and updates, check out the program's home page at\n"
+ "<http://www.tu-ilmenau.de/~gomar/stuff/wmpinboard/>.\n",
+ TIMEOUT);
+ exit(EXIT_FAILURE);
+}
+
+/*
+ * parses the list of command line arguments and initializes the application's
+ * opts structure accordingly; handles non-interactive actions, and eventually
+ * reads in the data file if the instance is to be run in interactive mode
+ */
+void
+parse_argv(int argc, char **argv)
+{
+ static const struct option long_opts[] = {
+ { "font", required_argument, 0, 'f' },
+ { "theme", required_argument, 0, 'p' },
+ { "alarm-cmd", required_argument, 0, 'r' },
+
+ { "display", required_argument, 0, 'd' },
+ { "normal-state", no_argument, 0, 'n' },
+ { "withdrawn-state", no_argument, 0, 'w' },
+ { "time", required_argument, 0, 't' },
+ { "click-to-focus", no_argument, 0, 'c' },
+ { "light", no_argument, 0, 'l' },
+
+ { "dump", no_argument, 0, 'u' },
+ { "dump-raw", no_argument, 0, 'y' },
+ { "del", required_argument, 0, 'e' },
+ { "add", required_argument, 0, 'a' },
+ { "add-raw", required_argument, 0, 'b' },
+#ifdef CREASES
+ { "iron", no_argument, 0, 'z' },
+#endif
+ { "export-sketch", required_argument, 0, 'x' },
+
+ { "help", no_argument, 0, 'h' },
+ { "info", no_argument, 0, 'i' },
+ { "version", no_argument, 0, 'v' },
+ { 0, 0, 0, 0 }
+ };
+ static const char short_opts[] = "d:nwt:chiv";
+
+ if (rindex(argv[0], '/'))
+ opts.name = (char*) rindex(argv[0], '/') + 1;
+ else
+ opts.name = argv[0];
+
+ for(;;) {
+ int idx = 0, c;
+
+ if ((c = getopt_long(argc, argv, short_opts, long_opts, &idx)) == -1)
+ break;
+ switch (c) {
+ case 'f': action(C_FONT, optarg);
+ case 'p': action(C_THEME, optarg);
+ case 'r': action(C_ALARM_CMD, optarg);
+ case 'd': /* display */
+ if (opts.display) free(opts.display);
+ opts.display = smalloc(strlen(optarg)+1);
+ strcpy(opts.display, optarg);
+ break;
+ case 'n': /* NormalState */
+ opts.window_state = NormalState;
+ break;
+ case 'w': /* WithdrawnState */
+ opts.window_state = WithdrawnState;
+ break;
+ case 't': /* timeout */
+ opts.timeout = strtol(optarg, 0, 10);
+ if (opts.timeout < 0) opts.timeout = -opts.timeout;
+ break;
+ case 'c': /* click-to-focus emulation */
+ opts.click_to_focus = 1;
+ break;
+ case 'l': /* light (no animations) */
+ opts.animate = 0;
+ break;
+ case 'u': action(A_DUMP, 0);
+ case 'y': action(A_DUMP_RAW, 0);
+ case 'e': action(A_DEL, optarg);
+ case 'a': action(A_ADD, optarg);
+ case 'b': action(A_ADD_RAW, optarg);
+#ifdef CREASES
+ case 'z': action(A_IRON, 0);
+#endif
+ case 'x': action(A_EXPORT, optarg);
+ case 'h': help();
+ case 'i': action(M_INFO, 0);
+ case 'v':
+ printf("wmpinboard v" VERSION "\n\ncompile-time configuration:\n"
+ " - maximal number of notes is %d\n"
+#if TIMEOUT == 0
+ " - edit mode timeout is disabled by default\n"
+#else
+ " - default edit mode timeout is %d seconds\n"
+#endif
+ " - wear & tear of notes (creases) is "
+#ifdef CREASES
+ "enabled\n"
+#else
+ "disabled\n"
+#endif
+#ifndef FUNSTUFF
+ " - FUNSTUFF is disabled :-/\n"
+#endif
+ , MAX_NOTES
+#if TIMEOUT != 0
+ , TIMEOUT
+#endif
+ );
+ exit(EXIT_SUCCESS);
+ default : exit(EXIT_FAILURE);
+ }
+ }
+ if (optind < argc) help(); /* exits */
+ if (flush_instance(notes_io(0)))
+ die("Another interactive instance is running.");
+}
+
+/*
+ * draws a text cursor at character <pos> (block cursor if <block> is true)
+ * _without_ updating the display
+ */
+void
+draw_cursor(int pos, int block)
+{
+ unsigned long c = palette[ndata[state.cur_note].col].fg;
+ unsigned long d = palette[ndata[state.cur_note].col].bg;
+ int i, j;
+
+ XGetSubImage(display, app, 2+6*(pos%10), 2+10*(pos/10), 6, 10, ~0, ZPixmap,
+ img, 64, 54);
+ for (i = 64; i < 70; i++)
+ for (j = 63; j > (block ? 53 : 62); j--)
+ XPutPixel(img, i, j, XGetPixel(img, i, j) == c ? d : c);
+ XPutImage(display, app, normalGC, img, 64, 54, 2+6*(pos%10), 2+10*(pos/10),
+ 6, 10);
+}
+
+/*
+ * in edit mode, moves the cursor to a new position and updates the display
+ * if <update> has a true value
+ */
+void
+set_cursor(int new_pos, int update)
+{
+ int in_sel = 0;
+
+ if (new_pos > 58) return;
+ in_sel = (state.sel_from >= 0 && state.sel_to >= 0 &&
+ ((state.sel_from <= new_pos && new_pos < state.sel_to) ||
+ (state.sel_to <= new_pos && new_pos < state.sel_from)));
+ if (!in_sel || state.insert)
+ print_letter(state.cur_note, ndata[state.cur_note].cursor, 1);
+ draw_cursor(ndata[state.cur_note].cursor = new_pos, !in_sel && state.insert);
+ if (update) redraw_window();
+}
+
+/*
+ * selects text in the character range state.sel_from..<to>, previously
+ * unselecting that between state.sel_from..state.sel_to
+ */
+void
+sel_text(int to)
+{
+ int i, t;
+
+ if (to == state.sel_to) return;
+ if (state.sel_to >= 0) {
+ i = state.sel_from < state.sel_to ? state.sel_from : state.sel_to;
+ t = state.sel_from < state.sel_to ? state.sel_to : state.sel_from;
+ for (; i < t; i++) print_letter(state.cur_note, i, 1);
+ }
+ print_letter(state.cur_note, ndata[state.cur_note].cursor, 1);
+ i = state.sel_from < to ? state.sel_from : to;
+ t = state.sel_from < to ? to : state.sel_from;
+ for (; i < t; i++) draw_cursor(i, 1);
+ state.sel_to = to;
+ set_cursor(ndata[state.cur_note].cursor, 1);
+}
+
+/*
+ * clears the selection if in edit mode and text is selected
+ */
+void
+clear_selection()
+{
+ if (state.mode == M_EDIT && state.sel_from >= 0) {
+ state.sel_from = state.sel_to = -1;
+ init_edit_mode(state.cur_note);
+ set_cursor(ndata[state.cur_note].cursor, 1);
+ }
+}
+
+/*
+ * in sketch mode, draws a pixel at (x, y)
+ */
+void
+draw_pixel(int x, int y)
+{
+ if (!x || !y || x > 62 || y > 62 || x+y >= 115) return;
+ XDrawPoint(display, win, state.sketchGC, x, y); /* actual drawing */
+ if (state.mode == M_DRAW) /* save bits */
+ ndata[state.cur_note].sketch[x/8 + y*8] |= 1<<(x%8);
+ else
+ ndata[state.cur_note].sketch[x/8 + y*8] &= ~(1<<(x%8));
+}
+
+/*
+ * sets the internal mode indicator and installs a corresponding mouse cursor
+ */
+void
+set_mode(modes new)
+{
+ switch (state.mode = new) {
+ case M_MOVE:
+ XDefineCursor(display, win, cursors[state.cur_note >= 0 ? 0 : 1]);
+ break;
+ case M_DRAW: case M_ERAS:
+ XDefineCursor(display, win, cursors[2]);
+ break;
+ case M_EDIT:
+ state.selecting = 0;
+ state.sel_from = state.sel_to = -1;
+ if (!opts.click_to_focus) set_kbfocus(1);
+ default:
+ XUndefineCursor(display, win);
+ }
+}
+
+/*
+ * returns from M_EDIT, M_DRAW, M_ERAS, or M_BBAR to M_NOOP; destroys the
+ * current note if empty or <destroy> is true
+ */
+void
+quit_edit_mode(int destroy, int save)
+{
+ if (state.mode == M_BBAR) {
+ if (ndata[state.cur_note].a_flags & ALARM_ON) animate_abar(0);
+ animate_bbar(0);
+ }
+ if (destroy || (state.cur_note >= 0 && note_empty(state.cur_note))) {
+ remove_note(state.cur_note);
+ destroy = 1;
+ }
+ animate_note(destroy ? 6 : 5);
+ set_mode(M_NOOP);
+ if (save) notes_io(1); /* should be last when called from signal handler */
+ time_next_alarm();
+}
+
+/*
+ * sets a timer expiring every <intv> microseconds
+ */
+void
+timer(unsigned int intv)
+{
+#ifndef __GLIBC__
+ struct itimerval val = { { 0, intv }, { 0, intv } };
+
+ setitimer(ITIMER_REAL, &val, 0);
+#else
+ ualarm(intv, intv);
+#endif
+}
+
+/*
+ * adds some eyecandy to the popping-up of the button bar (slides in if <in>
+ * is true, otherwise, out)
+ */
+void
+animate_bbar(int in)
+{
+ int y;
+
+ init_edit_mode(state.cur_note);
+ set_cursor(ndata[state.cur_note].cursor, 0);
+ if (in) { /* slide in */
+ if (opts.animate) {
+ redraw_window();
+ timer(PANEL_ANI_INT);
+ for (y = 27; y >= 0; y -= 3) {
+ state.alarmed = 0;
+ XCopyArea(display, bbar, win, normalGC, 0, 0, 58, 30-y, 3, 34+y);
+ flush_expose();
+ while (!state.alarmed);
+ }
+ alarm(0);
+ XCopyArea(display, bbar, win, normalGC, 0, 0, 58, 30, 3, 31);
+ XCopyArea(display, app, win, normalGC, 3, 61, 58, 3, 3, 61);
+ flush_expose();
+ /* for future refreshes... */
+ XCopyArea(display, bbar, app, normalGC, 0, 0, 58, 30, 3, 31);
+ } else { /* no animation */
+ XCopyArea(display, bbar, app, normalGC, 0, 0, 58, 30, 3, 31);
+ redraw_window();
+ }
+ } else { /* slide out */
+ if (opts.animate) {
+ timer(PANEL_ANI_INT);
+ for (y = 31; y <= 58; y += 3) {
+ state.alarmed = 0;
+ XCopyArea(display, app, win, normalGC, 3, y, 58, 3, 3, y);
+ XCopyArea(display, bbar, win, normalGC, 0, 0, 58, 61-y, 3, y+3);
+ flush_expose();
+ while (!state.alarmed);
+ }
+ alarm(0);
+ XCopyArea(display, app, win, normalGC, 3, 61, 58, 3, 3, 61);
+ flush_expose();
+ } else /* no animation */
+ redraw_window();
+ }
+}
+
+/*
+ * like animate_bbar, but for abar (to be called when bbar is already visible)
+ */
+void
+animate_abar(int in)
+{
+ int y;
+
+ if (in) { /* slide in */
+ explode_time(state.cur_note);
+ render_abar(state.cur_note);
+ if (opts.animate) {
+ redraw_window();
+ timer(PANEL_ANI_INT);
+ for (y = 28; y >= 3; y -= 3) {
+ state.alarmed = 0;
+ XCopyArea(display, abar, win, normalGC, 0, 0, 58, 31-y, 3, y);
+ flush_expose();
+ while (!state.alarmed);
+ }
+ alarm(0);
+ XCopyArea(display, abar, win, normalGC, 0, 0, 58, 28, 3, 4);
+ flush_expose();
+ /* for future refreshes... */
+ XCopyArea(display, abar, app, normalGC, 0, 0, 58, 28, 3, 4);
+ } else { /* no animation */
+ XCopyArea(display, abar, app, normalGC, 0, 0, 58, 28, 3, 4);
+ redraw_window();
+ }
+ } else { /* slide out */
+ implode_time(state.cur_note);
+ /* render app as note plus bbar */
+ init_edit_mode(state.cur_note);
+ set_cursor(ndata[state.cur_note].cursor, 0);
+ XCopyArea(display, win, app, normalGC, 3, 31, 58, 30, 3, 31);
+ if (opts.animate) {
+ timer(PANEL_ANI_INT);
+ for (y = 4; y <= 25; y += 3) {
+ state.alarmed = 0;
+ XCopyArea(display, app, win, normalGC, 3, y, 58, 3, 3, y);
+ XCopyArea(display, abar, win, normalGC, 0, 0, 58, 28-y, 3, y+3);
+ flush_expose();
+ while (!state.alarmed);
+ }
+ alarm(0);
+ XCopyArea(display, app, win, normalGC, 3, 28, 58, 3, 3, 28);
+ flush_expose();
+ } else /* no animation */
+ redraw_window();
+ }
+}
+
+/*
+ * animates the switching between two notes (replaces what's currently
+ * being displayed by state.cur_note), in a way specified by <style>
+ *
+ * 0 = right -> left 2 = bottom -> top 4 = pinboard -> edit mode
+ * 1 = left -> right 3 = top -> bottom 5 = edit mode -> pinboard
+ * 6 = note destruction
+ */
+void
+animate_note(int style)
+{
+ static const int seq[10] = { 2, 3, 6, 9, 12, 12, 9, 6, 3, 2 };
+ XRectangle mask[5] = { { 6, 2, 52, 60 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 } };
+ int i, j;
+
+ if (!opts.animate) { /* animations disabled */
+ if (style < 5) { /* display note */
+ set_mask(0);
+ init_edit_mode(state.cur_note);
+ set_cursor(ndata[state.cur_note].cursor, 1);
+ } else { /* display pinboard */
+ set_mask(1);
+ render_pinboard(-1);
+ redraw_window();
+ }
+ return;
+ }
+ switch (style) {
+ case 0: /* slide right -> left */
+ init_edit_mode(state.cur_note);
+ set_cursor(ndata[state.cur_note].cursor, 0);
+ timer(NOTE_ANI_INT);
+ for (i = 2, j = 0; j < 10; i += seq[++j]) {
+ state.alarmed = 0;
+ XCopyArea(display, win, win, normalGC, seq[j], 0, 64-seq[j], 64, 0, 0);
+ XCopyArea(display, app, win, normalGC, i-seq[j], 0, seq[j], 64,
+ 64-seq[j], 0);
+ flush_expose();
+ while (!state.alarmed);
+ }
+ break;
+ case 1: /* slide left -> right */
+ init_edit_mode(state.cur_note);
+ set_cursor(ndata[state.cur_note].cursor, 0);
+ timer(NOTE_ANI_INT);
+ for (i = 2, j = 0; j < 10; i += seq[++j]) {
+ state.alarmed = 0;
+ XCopyArea(display, win, win, normalGC, 0, 0, 64-seq[j], 64, seq[j], 0);
+ XCopyArea(display, app, win, normalGC, 64-i, 0, seq[j], 64, 0, 0);
+ flush_expose();
+ while (!state.alarmed);
+ }
+ break;
+ case 2: /* slide top -> bottom */
+ init_edit_mode(state.cur_note);
+ set_cursor(ndata[state.cur_note].cursor, 0);
+ timer(NOTE_ANI_INT);
+ for (i = 2, j = 0; j < 10; i += seq[++j]) {
+ state.alarmed = 0;
+ XCopyArea(display, win, win, normalGC, 0, 0, 64, 64-seq[j], 0, seq[j]);
+ XCopyArea(display, app, win, normalGC, 0, 64-i, 64, seq[j], 0, 0);
+ flush_expose();
+ while (!state.alarmed);
+ }
+ break;
+ case 3: /* slide bottom -> top */
+ init_edit_mode(state.cur_note);
+ set_cursor(ndata[state.cur_note].cursor, 0);
+ timer(NOTE_ANI_INT);
+ for (i = 2, j = 0; j < 10; i += seq[++j]) {
+ state.alarmed = 0;
+ XCopyArea(display, win, win, normalGC, 0, seq[j], 64, 64-seq[j], 0, 0);
+ XCopyArea(display, app, win, normalGC, 0, i-seq[j], 64, seq[j], 0,
+ 64-seq[j]);
+ flush_expose();
+ while (!state.alarmed);
+ }
+ break;
+ case 4: /* pinboard view -> edit mode */
+ mask[1].x = mask[1].y = 0;
+ init_edit_mode(state.cur_note);
+ set_cursor(ndata[state.cur_note].cursor, 0);
+ timer(NOTE_ANI_INT);
+ for (i = 2, j = 0; j < 10; i += seq[++j]) {
+ state.alarmed = 0;
+ mask[1].width = mask[1].height = i;
+ XShapeCombineRectangles(display, win, ShapeBounding, 0, 0, mask, 2,
+ ShapeSet, 0);
+ XCopyArea(display, app, win, normalGC, 64-i, 64-i, i, i, 0, 0);
+ flush_expose();
+ while (!state.alarmed);
+ }
+ break;
+ case 5: /* edit mode -> pinboard view */
+ render_pinboard(-1);
+ timer(NOTE_ANI_INT);
+ for (i = 2, j = 0; j < 10; i += seq[++j]) {
+ state.alarmed = 0;
+ mask[1].x = mask[1].y = i;
+ mask[1].width = mask[1].height = 64-i;
+ XCopyArea(display, win, win, normalGC, i-seq[j], i-seq[j], 64-i, 64-i,
+ i, i);
+ XCopyArea(display, app, win, normalGC, i-seq[j], i-seq[j], seq[j], 64,
+ i-seq[j], i-seq[j]);
+ XCopyArea(display, app, win, normalGC, i, i-seq[j], 64-i, seq[j],
+ i, i-seq[j]);
+ XShapeCombineRectangles(display, win, ShapeBounding, 0, 0, mask, 2,
+ ShapeSet, 0);
+ flush_expose();
+ while (!state.alarmed);
+ }
+ break;
+ case 6: /* note destruction */
+ render_pinboard(-1);
+ timer((int) 1.5*NOTE_ANI_INT);
+ /* mask[1].x = mask[1].y = mask[2].y = mask[3].x = 0; */
+ for (i = 4; i <= 32; i += 4) {
+ state.alarmed = 0;
+ for (j = 1; j < 5; j++) mask[j].width = mask[j].height = 32-i;
+ mask[2].x = mask[3].y = mask[4].x = mask[4].y = 32+i;
+ XCopyArea(display, win, win, normalGC, 4, 4, 32-i, 32-i, 0, 0);
+ XCopyArea(display, win, win, normalGC, 28+i, 4, 32-i, 32-i, 32+i, 0);
+ XCopyArea(display, win, win, normalGC, 4, 28+i, 32-i, 32-i, 0, 32+i);
+ XCopyArea(display, win, win, normalGC, 28+i , 28+i, 32-i, 32-i, 32+i,
+ 32+i);
+ XCopyArea(display, app, win, normalGC, 32-i, 0, 2*i, 64, 32-i, 0);
+ XCopyArea(display, app, win, normalGC, 0, 32-i, 64, 2*i, 0, 32-i);
+ XShapeCombineRectangles(display, win, ShapeBounding, 0, 0, mask, 5,
+ ShapeSet, 0);
+ flush_expose();
+ while (!state.alarmed);
+ }
+ }
+ alarm(0);
+}
+
+/*
+ * displays the next phase of the alarm animation (flashing)
+ */
+void
+animate_alarm()
+{
+ XCopyArea(display, state.alarm.buffer, win, normalGC,
+ state.alarm.phase*64, 0, 64, 64, 0, 0);
+ flush_expose();
+ state.alarm.phase = !state.alarm.phase;
+}
+
+/*
+ * called from main event loop whenever a ButtonPress event occurs
+ */
+void
+handle_ButtonPress(XEvent *event)
+{
+ int i;
+
+ if (state.button) return;
+ state.button = event->xbutton.button;
+ switch (state.mode) {
+ case M_NOOP: /* drag a note? */
+ state.cur_note = selected_note(event->xbutton.x, event->xbutton.y);
+ if (state.cur_note >= 0) { /* drag existing note */
+ state.dx = event->xbutton.x - ndata[state.cur_note].x;
+ state.dy = event->xbutton.y - ndata[state.cur_note].y;
+ state.moved = 0;
+ render_pinboard(state.cur_note);
+ XCopyArea(display, app, app, normalGC,
+ ndata[state.cur_note].x, ndata[state.cur_note].y, 16, 16, 64, 48);
+ color_notes(state.cur_note);
+ pin_note(state.cur_note);
+ redraw_window(); /* necessary in case of a single raising click */
+ state.mode = M_MOVE; /* don't set drag cursor immediately */
+ } else if (
+ event->xbutton.x >= 6+label_coords[0] &&
+ event->xbutton.x <= 6+label_coords[2] &&
+ event->xbutton.y >= 2+label_coords[1] &&
+ event->xbutton.y <= 2+label_coords[3] &&
+ selected_note(event->xbutton.x, event->xbutton.y) < 0 &&
+ notes_count < MAX_NOTES-1)
+ { /* possibly drag new note from "TO DO" label */
+ state.moved = 0;
+ set_mode(M_MOVE);
+ }
+ break;
+ case M_EDIT: /* claim focus in click-based focus emulation mode */
+ if (!opts.click_to_focus || state.clicks_count) {
+ if (state.button != 2) {
+ if (state.lp_btn == state.button &&
+ event->xbutton.time-state.lp_time < DCLICK_LIMIT)
+ {
+ state.sel_from = i = char_at(event->xbutton.x, event->xbutton.y, 0);
+ for (; state.sel_from &&
+ ndata[state.cur_note].text[state.sel_from-1] != ' ' &&
+ (state.button != 1 || state.sel_from%10 > 0); state.sel_from--);
+ for (; i < 59 && ndata[state.cur_note].text[i] != ' ' &&
+ (state.button != 1 || state.sel_from/10 == (i+1)/10 || i%10);
+ i++);
+ sel_text(i);
+ cb_copy(&ndata[state.cur_note].text[state.sel_from], i -
+ state.sel_from);
+ } else {
+ clear_selection();
+ state.sel_from = char_at(event->xbutton.x, event->xbutton.y, 0);
+ state.selecting = 1;
+ }
+ }
+ }
+ if (opts.click_to_focus && event->xbutton.x+event->xbutton.y < 115) {
+ if (!state.clicks_count) set_kbfocus(1);
+ state.clicks_count++;
+ }
+ break;
+ case M_BBAR: /* remember which button was *pressed* */
+ state.bbar_pressed = state.abar_pressed = -1;
+ /* button pressed on bbar? */
+ i = bbar_button(event->xbutton.x, event->xbutton.y);
+ if (i >= 0) { /* bbar button */
+ state.bbar_pressed = i;
+ } else if (ndata[state.cur_note].a_flags & ALARM_ON) { /* abar? */
+ i = abar_area(event->xbutton.x, event->xbutton.y);
+ if (i >= 0) state.abar_pressed = i;
+ }
+ break;
+ case M_DRAW: case M_ERAS: /* draw in either sketch mode */
+ if (state.button == 1) {
+ draw_pixel(event->xbutton.x, event->xbutton.y);
+#ifdef CREASES
+ if (state.mode == M_ERAS) {
+ render_edit_wear_area_win(win, state.cur_note, event->xbutton.x,
+ event->xbutton.y, 1, 1);
+ }
+#endif
+ }
+ break;
+ default: /* keep the compiler happy */
+ break;
+ }
+ state.lp_btn = state.button;
+ state.lp_time = event->xbutton.time;
+}
+
+/*
+ * called from main event loop whenever a ButtonRelease event occurs
+ */
+void
+handle_ButtonRelease(XEvent *event)
+{
+ XGCValues gcv;
+ char *s;
+ int i;
+
+ if (event->xbutton.button != state.button) return;
+ switch (state.mode) {
+ case M_NOOP: case M_MOVE: /* add new note or edit existing one? */
+ if (!state.moved && state.button == 1) { /* left-click */
+ if (
+ event->xbutton.x >= 6+label_coords[0] &&
+ event->xbutton.x <= 6+label_coords[2] &&
+ event->xbutton.y >= 2+label_coords[1] &&
+ event->xbutton.y <= 2+label_coords[3] &&
+ selected_note(event->xbutton.x, event->xbutton.y) < 0)
+ { /* add new note */
+ if ((state.cur_note = add_note()) >= 0) {
+ animate_note(4);
+ set_kbfocus(1);
+ set_mode(M_EDIT);
+ } else /* beep if maximum number of notes reached */
+ XBell(display, 0);
+ } else if (state.cur_note >= 0 && state.cur_note ==
+ selected_note(event->xbutton.x, event->xbutton.y))
+ { /* open existing note in edit mode */
+#ifdef CREASES
+ wear_note(state.cur_note);
+#endif
+ animate_note(4);
+ state.clicks_count = 0;
+ set_mode(M_EDIT);
+ }
+ } else { /* moved */
+ if (state.cur_note >= 0) {
+ if (string_empty(ndata[state.cur_note].text, 0)) { /* new note */
+ animate_note(4);
+ set_kbfocus(1);
+ set_mode(M_EDIT);
+ } else { /* existing note dragged via either left or right MB */
+#ifdef CREASES
+ wear_note(state.cur_note);
+#endif
+ notes_io(1);
+ if (state.button == 1) { /* keep level */
+ render_pinboard(-1);
+ redraw_window();
+ } else { /* raise */
+ state.cur_note = raise_note(state.cur_note);
+#ifdef CREASES
+ pin_note(state.cur_note);
+ redraw_window();
+#endif
+ }
+ time_next_alarm(); /* note IDs may have changed */
+ }
+ }
+ }
+ if (state.mode != M_EDIT) set_mode(M_NOOP);
+ break;
+ case M_EDIT: case M_DRAW: case M_ERAS:
+ i = char_at(event->xbutton.x, event->xbutton.y, 0);
+ if (event->xbutton.x + event->xbutton.y >= 115 &&
+ (!state.selecting || state.sel_from == i))
+ { /* clicked triangle? */
+ if (state.button > 1) { /* open panel */
+ animate_bbar(1);
+ if (ndata[state.cur_note].a_flags & ALARM_ON) {
+ animate_abar(1);
+ check_time(state.cur_note); /* adjusts hidden year field */
+ }
+ if (!opts.click_to_focus) set_kbfocus(0);
+ set_mode(M_BBAR);
+ } else { /* end edit mode */
+ if (state.mode == M_DRAW || state.mode == M_ERAS) {
+ XFreeGC(display, state.sketchGC); /* free temporary GC */
+ if (M_ERAS) { /* restore normal note display first */
+ print_text(state.cur_note);
+ draw_sketch(state.cur_note);
+ set_cursor(ndata[state.cur_note].cursor, 1);
+ }
+ set_mode(M_EDIT);
+ }
+ quit_edit_mode(0, 1);
+ if (!opts.click_to_focus) set_kbfocus(0);
+ }
+ } else {
+ if (state.mode == M_EDIT) {
+ if (!opts.click_to_focus || state.clicks_count > 1) {
+ if (state.button == 2) {
+ ndata[state.cur_note].cursor = char_at(event->xbutton.x,
+ event->xbutton.y, 1);
+ state.raw_paste = 0;
+ cb_paste(state.cur_note, state.insert);
+ init_edit_mode(state.cur_note);
+ set_cursor(ndata[state.cur_note].cursor, 1);
+ } else if (state.selecting && state.sel_from != i) {
+ sel_text(i);
+ if (state.sel_from > state.sel_to) {
+ i = state.sel_from;
+ state.sel_from = state.sel_to;
+ state.sel_to = i;
+ }
+ if (state.button == 1) { /* copy cooked */
+ s = cook(state.cur_note, state.sel_from, state.sel_to -
+ state.sel_from);
+ cb_copy(s, -1);
+ free(s);
+ } else { /* copy raw */
+ cb_copy(&ndata[state.cur_note].text[state.sel_from],
+ state.sel_to - state.sel_from);
+ }
+ } else
+ set_cursor(char_at(event->xbutton.x, event->xbutton.y, 1), 1);
+ }
+ } else if (state.button > 1) { /* revert from sketch to edit mode */
+ XFreeGC(display, state.sketchGC);
+ init_edit_mode(state.cur_note);
+ set_cursor(ndata[state.cur_note].cursor, 1);
+ set_mode(M_EDIT);
+ }
+ }
+ state.selecting = 0;
+ break;
+ case M_BBAR: /* actions in panel mode */
+ if (state.bbar_pressed >= 0) {
+ if (state.bbar_pressed == bbar_button(event->xbutton.x,
+ event->xbutton.y)) /* clicked on panel? */
+ {
+ switch (state.bbar_pressed) {
+ case 0: /* open/close alarm panel */
+ if (!(ndata[state.cur_note].a_flags & ALARM_ON)) {
+ /* open alarm panel, turn alarm on */
+ animate_abar(1);
+ ndata[state.cur_note].a_flags |= ALARM_ON;
+ } else { /* close alarm panel, turn alarm off */
+ animate_abar(0);
+ ndata[state.cur_note].a_flags &= ~ALARM_ON;
+ }
+ break;
+ case 4: /* change note color */
+ i = ndata[state.cur_note].col;
+ if (state.button > 1) { /* previous color */
+ if (--ndata[state.cur_note].col < 0)
+ ndata[state.cur_note].col = C_NUM-1;
+ } else /* next color */
+ if (++ndata[state.cur_note].col == C_NUM)
+ ndata[state.cur_note].col = 0;
+ init_edit_mode(state.cur_note);
+ set_cursor(ndata[state.cur_note].cursor, 0);
+ XCopyArea(display, bbar, app, normalGC, 0, 0, 58, 30, 3, 31);
+ if (ndata[state.cur_note].a_flags & ALARM_ON)
+ XCopyArea(display, abar, app, normalGC, 0, 0, 58, 28, 3, 4);
+ redraw_window();
+ break;
+ case 1: case 5: /* enter draw/erase mode */
+ gcv.foreground = state.bbar_pressed == 1 ?
+ palette[ndata[state.cur_note].col].fg :
+ palette[ndata[state.cur_note].col].bg;
+ state.sketchGC = XCreateGC(display, app, GCForeground, &gcv);
+ if (ndata[state.cur_note].a_flags & ALARM_ON) animate_abar(0);
+ animate_bbar(0);
+ render_note(state.cur_note);
+#ifdef CREASES
+ render_edit_wear(state.cur_note);
+#endif
+ if (state.bbar_pressed == 1) { /* erase: hide text */
+ print_text(state.cur_note);
+ set_cursor(ndata[state.cur_note].cursor, 0);
+ }
+ draw_sketch(state.cur_note);
+ redraw_window();
+ set_mode(state.bbar_pressed == 1 ? M_DRAW : M_ERAS);
+ break;
+ case 2: /* clear note's text */
+ memset(ndata[state.cur_note].text, 32, 59);
+#ifdef CREASES
+ wear_note(state.cur_note);
+ wear_note(state.cur_note);
+#endif
+ if (ndata[state.cur_note].a_flags & ALARM_ON) animate_abar(0);
+ animate_bbar(0);
+ init_edit_mode(state.cur_note);
+ set_cursor(0, 1);
+ set_mode(M_EDIT);
+ break;
+ case 6: /* clear sketch */
+ memset(ndata[state.cur_note].sketch, 0, 511); /* not last byte */
+#ifdef CREASES
+ wear_note(state.cur_note);
+ wear_note(state.cur_note);
+#endif
+ if (ndata[state.cur_note].a_flags & ALARM_ON) animate_abar(0);
+ animate_bbar(0);
+ init_edit_mode(state.cur_note);
+ set_cursor(ndata[state.cur_note].cursor, 1);
+ set_mode(M_EDIT);
+ break;
+ case 3: /* remove note */
+ quit_edit_mode(1, 1);
+ if (!opts.click_to_focus) set_kbfocus(0);
+ break;
+ case 7: /* close button bar */
+ if (ndata[state.cur_note].a_flags & ALARM_ON) animate_abar(0);
+ animate_bbar(0);
+ set_mode(M_EDIT);
+ }
+ }
+ } else if (state.abar_pressed >= 0) {
+ if (state.abar_pressed == abar_area(event->xbutton.x,
+ event->xbutton.y)) /* clicked on panel? */
+ {
+ if (state.abar_pressed < 4) { /* clicked on number */
+ char c = state.a_edit[state.abar_pressed];
+ char delta = state.button == 1 ? 1 : -1;
+
+ switch (state.abar_pressed) {
+ case 0: c = (24+c+delta)%24; break;
+ case 1: c = (60+c+delta)%60; break;
+ case 2: c = (11+c+delta)%12+1; break;
+ case 3: c = (30+c+delta)%31+1;
+ }
+ state.a_edit[state.abar_pressed] = c;
+ /* check validity of date, adapt month or day if necessary */
+ while (!check_time())
+ if (state.abar_pressed == 2) { /* month was changed */
+ if (state.a_edit[3] > 1)
+ state.a_edit[3]--;
+ else
+ break; /* just making sure */
+ } else { /* presumably, the day was changed */
+ state.a_edit[3] = 1;
+ break;
+ }
+ /* update display */
+ render_abar_number(state.abar_pressed);
+ /* always update day in case the date had to be corrected */
+ if (state.abar_pressed != 3) render_abar_number(3);
+ } else { /* clicked on switch */
+ if (state.abar_pressed == 4) /* daily alarm */
+ ndata[state.cur_note].a_flags &= ~ALARM_DATE;
+ else /* specific date */
+ ndata[state.cur_note].a_flags |= ALARM_DATE;
+ check_time(state.cur_note); /* updates the hidden year field */
+ render_abar_switches(state.cur_note);
+ }
+ /* update display */
+ XCopyArea(display, abar, app, normalGC, 0, 0, 58, 28, 3, 4);
+ XCopyArea(display, abar, win, normalGC, 0, 0, 58, 28, 3, 4);
+ }
+ }
+ break;
+ case M_ALRM:
+ if (ndata[state.cur_note].a_flags & ALARM_DATE)
+ ndata[state.cur_note].a_flags &= ~ALARM_ON;
+ notes_io(1);
+ init_edit_mode(state.cur_note);
+ set_cursor(ndata[state.cur_note].cursor, 1);
+ redraw_window();
+ set_mode(M_EDIT);
+ time(&state.idle);
+ }
+ state.button = 0;
+}
+
+/*
+ * called from main event loop whenever a MotionNotify event occurs
+ */
+void
+handle_MotionNotify(XEvent *event)
+{
+ int i, j;
+
+ switch (state.mode) {
+ case M_MOVE: /* note's being dragged, update position */
+ if (state.cur_note >= 0) { /* update note that's being moved's pos. */
+ /* the drag cursor isn't set immediately... */
+ if (!state.moved) {
+ set_mode(M_MOVE);
+ state.moved = 1;
+ }
+ i = ndata[state.cur_note].x;
+ j = ndata[state.cur_note].y;
+ ndata[state.cur_note].x = event->xbutton.x - state.dx;
+ ndata[state.cur_note].x = ndata[state.cur_note].x < 6 ? 6 :
+ ndata[state.cur_note].x;
+ ndata[state.cur_note].x = ndata[state.cur_note].x > 42 ? 42 :
+ ndata[state.cur_note].x;
+ ndata[state.cur_note].y = event->xbutton.y - state.dy;
+ ndata[state.cur_note].y = ndata[state.cur_note].y < 2 ? 2 :
+ ndata[state.cur_note].y;
+ ndata[state.cur_note].y = ndata[state.cur_note].y > 46 ? 46 :
+ ndata[state.cur_note].y;
+ XCopyArea(display, app, app, normalGC, 64, 48, 16, 16, i, j);
+ XCopyArea(display, app, app, normalGC, ndata[state.cur_note].x,
+ ndata[state.cur_note].y, 16, 16, 64, 48);
+ pin_note(state.cur_note);
+ redraw_window();
+ time(&state.idle);
+ } else { /* create note by dragging it "off" the "TO DO" label */
+ state.cur_note = add_note();
+ ndata[state.cur_note].x = event->xbutton.x-8;
+ ndata[state.cur_note].y = event->xbutton.y < 8 ? 0 :
+ event->xbutton.y-8;
+ state.dx = 8;
+ state.dy = event->xbutton.y - ndata[state.cur_note].y;
+ state.moved = 0;
+ XCopyArea(display, app, app, normalGC,
+ ndata[state.cur_note].x, ndata[state.cur_note].y, 16, 16, 64, 48);
+ color_notes(state.cur_note);
+ set_mode(M_MOVE); /* update cursor */
+ pin_note(state.cur_note);
+ redraw_window();
+ time(&state.idle);
+ }
+ break;
+ case M_DRAW: case M_ERAS: /* draw in either sketch mode */
+ if (state.button == 1) {
+ draw_pixel(event->xbutton.x, event->xbutton.y);
+ if (state.mode == M_ERAS) { /* enlarge the cursor a bit when erasing */
+ draw_pixel(event->xbutton.x-1, event->xbutton.y);
+ draw_pixel(event->xbutton.x+1, event->xbutton.y);
+ draw_pixel(event->xbutton.x, event->xbutton.y-1);
+ draw_pixel(event->xbutton.x, event->xbutton.y+1);
+#ifdef CREASES
+ render_edit_wear_area_win(win, state.cur_note, event->xbutton.x-1,
+ event->xbutton.y, 3, 1);
+ render_edit_wear_area_win(win, state.cur_note, event->xbutton.x,
+ event->xbutton.y-1, 1, 3);
+#endif
+ }
+ time(&state.idle);
+ }
+ break;
+ case M_EDIT:
+ if (state.selecting) {
+ sel_text(char_at(event->xbutton.x, event->xbutton.y, 0));
+ time(&state.idle);
+ }
+ break;
+ default: /* keep the compilter happy */
+ break;
+ }
+}
+
+/*
+ * called from main event loop whenever a KeyPress event occurs
+ */
+void
+handle_KeyPress(XEvent *event)
+{
+ KeySym ksym;
+ unsigned char ch[4];
+ char *s;
+ int i, j = 0;
+
+ if (state.mode != M_EDIT || event->xkey.state & 0xdfe8 ||
+ (InputContext && XFilterEvent(event, win)))
+ {
+ XSendEvent(display, RootWindow(display, DefaultScreen(display)), True,
+ KeyPressMask, event);
+ if (state.mode == M_EDIT) set_kbfocus(1); /* make sure we keep focus */
+ return;
+ }
+ time(&state.idle);
+
+ clear_selection();
+ if (event->xkey.state & ControlMask) { /* [Ctrl]-anything -> special fn.s */
+ /* InputContext intentionally ignored here... */
+ XLookupString(&event->xkey, (char*) ch, sizeof(ch), &ksym, &state.compose);
+ switch (ksym) {
+ case XK_c: case XK_C: /* copy cooked */
+ state.sel_from = 0;
+ sel_text(59);
+ s = cook(state.cur_note, 0, 59);
+ cb_copy(s, -1);
+ free(s);
+ return;
+ case XK_r: case XK_R: /* copy raw */
+ state.sel_from = 0;
+ sel_text(59);
+ cb_copy(ndata[state.cur_note].text, -1);
+ return;
+ case XK_i: case XK_I: /* paste raw */
+ state.raw_paste = 1;
+ cb_paste(state.cur_note, state.insert);
+ init_edit_mode(state.cur_note);
+ set_cursor(ndata[state.cur_note].cursor, 1);
+ return;
+ case XK_y: case XK_Y: case XK_z: case XK_Z: /* zap line */
+ for (i = 10*(ndata[state.cur_note].cursor/10); i < 59; i++)
+ ndata[state.cur_note].text[i] =
+ i < 49 ? ndata[state.cur_note].text[i+10] : ' ';
+ init_edit_mode(state.cur_note);
+ set_cursor(10*(ndata[state.cur_note].cursor/10), 1);
+ return;
+ case XK_n: case XK_N: /* EMACS-style down */
+ ksym = XK_Down;
+ break;
+ case XK_p: case XK_P: /* EMACS-style up */
+ ksym = XK_Up;
+ break;
+ case XK_f: case XK_F: /* EMACS-style right */
+ ksym = XK_Right;
+ break;
+ case XK_b: case XK_B: /* EMACS-style left */
+ ksym = XK_Left;
+ break;
+ default:
+ return;
+ }
+ } else {
+ if (InputContext) {
+ j = XmbLookupString(InputContext, &event->xkey, (char*) ch, sizeof(ch),
+ &ksym, 0);
+ } else {
+ j = XLookupString(&event->xkey, (char*) ch, sizeof(ch), &ksym,
+ &state.compose);
+ }
+ if (!j && (ksym & 0xff00) == 0x0600) { /* cyrillic keysyms */
+ ch[0] = (unsigned char) (ksym & 0x00ff);
+ ch[1] = '\0';
+ if (ch[0] >= 0xbf || ch[0] == 0xa3 || ch[0] == 0xb3)
+ j = 1; /* filter extended KOI8 characters */
+ }
+ }
+ switch (ksym) {
+ case XK_Return: case XK_KP_Enter:
+ if (ndata[state.cur_note].cursor >= 50) break;
+ if (state.insert) {
+ shift_string(state.cur_note, 10*(ndata[state.cur_note].cursor/10+1), 58,
+ ndata[state.cur_note].cursor%10, ' ');
+ shift_string(state.cur_note, ndata[state.cur_note].cursor, 58,
+ 10-ndata[state.cur_note].cursor%10, ' ');
+ }
+ set_cursor(10*(ndata[state.cur_note].cursor/10+1), 1);
+ break;
+ case XK_BackSpace: case 0xfd1a:
+ if (!ndata[state.cur_note].cursor) break;
+ if (ndata[state.cur_note].cursor == 58 &&
+ ndata[state.cur_note].text[58] != ' ')
+ { /* special behavior when on very last character */
+ ndata[state.cur_note].text[58] = ' ';
+ set_cursor(ndata[state.cur_note].cursor, 1);
+ break;
+ }
+ print_letter(state.cur_note, ndata[state.cur_note].cursor--, 1);
+ case XK_Delete: case XK_KP_Delete:
+ j = 1; /* delete entire line if empty... */
+ if (((ksym != XK_BackSpace && ksym != 0xfd1a) ||
+ ndata[state.cur_note].cursor%10 == 9) &&
+ string_empty(&ndata[state.cur_note].text[10*
+ (ndata[state.cur_note].cursor/10)], 10))
+ {
+ ndata[state.cur_note].cursor = 10*(ndata[state.cur_note].cursor/10);
+ j = 10;
+ }
+ if (ksym == XK_BackSpace || ksym == 0xfd1a) {
+ if (ndata[state.cur_note].cursor%10 == 9 &&
+ ndata[state.cur_note].text[ndata[state.cur_note].cursor] == ' ')
+ {
+ for (; ndata[state.cur_note].text[ndata[state.cur_note].cursor-1] ==
+ ' ' && ndata[state.cur_note].cursor >
+ 10*(ndata[state.cur_note].cursor/10);)
+ {
+ ndata[state.cur_note].text[ndata[state.cur_note].cursor--] = ' ';
+ }
+ }
+ }
+
+ for (i = ndata[state.cur_note].cursor; i < (j == 1 &&
+ ndata[state.cur_note].cursor/10 < 5 ?
+ 10*(ndata[state.cur_note].cursor/10+1)-j : 59-j); i++)
+ {
+ ndata[state.cur_note].text[i] = ndata[state.cur_note].text[i+j];
+ ndata[state.cur_note].text[i+j] = ' ';
+ print_letter(state.cur_note, i, 1);
+ }
+ if (ndata[state.cur_note].cursor%10 == 9 ||
+ ndata[state.cur_note].cursor == 58)
+ { /* exceptions to the loop */
+ ndata[state.cur_note].text[ndata[state.cur_note].cursor] = ' ';
+ }
+ if (j > 1) /* only when lines have been shifted */
+ init_edit_mode(state.cur_note);
+ else
+ print_letter(state.cur_note, i, 1);
+ set_cursor(ndata[state.cur_note].cursor, 1);
+ break;
+ case XK_Up: case XK_KP_Up:
+ if ((event->xkey.state & 0x01) == 0x01) { /* next note w/similar color */
+ for (i = state.cur_note == notes_count-1 ? 0 : state.cur_note+1;
+ i != state.cur_note;)
+ {
+ if (c_group[ndata[i].col] == c_group[ndata[state.cur_note].col])
+ break;
+ if (++i >= notes_count) i = 0;
+ }
+ if (i == state.cur_note) break;
+ if (note_empty(state.cur_note)) remove_note(state.cur_note);
+ notes_io(1);
+ state.cur_note = i;
+ animate_note(2);
+ } else /* move ndata[state.cur_note].cursor */
+ if (ndata[state.cur_note].cursor > 9)
+ set_cursor(ndata[state.cur_note].cursor-10, 1);
+ break;
+ case XK_Down: case XK_KP_Down:
+ if ((event->xkey.state & 0x01) == 0x01) { /* prev. note w/simil. color */
+ for (i = state.cur_note ? state.cur_note-1 : notes_count-1;
+ i != state.cur_note;)
+ {
+ if (c_group[ndata[i].col] == c_group[ndata[state.cur_note].col])
+ break;
+ if (--i < 0) i = notes_count-1;
+ }
+ if (i == state.cur_note) break;
+ if (note_empty(state.cur_note)) remove_note(state.cur_note);
+ notes_io(1);
+ state.cur_note = i;
+ animate_note(3);
+ } else /* move ndata[state.cur_note].cursor */
+ if (ndata[state.cur_note].cursor < 49)
+ set_cursor(ndata[state.cur_note].cursor+10, 1);
+ break;
+ case XK_Left: case XK_KP_Left:
+ if ((event->xkey.state & 0x01) == 0x01) { /* previous note */
+ if (notes_count == 1) break;
+ if (note_empty(state.cur_note)) remove_note(state.cur_note);
+ notes_io(1);
+ if (--state.cur_note < 0) state.cur_note = notes_count-1;
+ animate_note(1);
+ } else /* move cursor */
+ if (ndata[state.cur_note].cursor)
+ set_cursor(ndata[state.cur_note].cursor-1, 1);
+ break;
+ case XK_Right: case XK_KP_Right:
+ if ((event->xkey.state & 0x01) == 0x01) { /* next note */
+ if (notes_count == 1) break;
+ if (note_empty(state.cur_note)) {
+ remove_note(state.cur_note);
+ state.cur_note = notes_count-1;
+ }
+ notes_io(1);
+ if (++state.cur_note == notes_count) state.cur_note = 0;
+ animate_note(0);
+ } else /* move cursor */
+ if (ndata[state.cur_note].cursor < 58)
+ set_cursor(ndata[state.cur_note].cursor+1, 1);
+ break;
+ case XK_Tab: case XK_ISO_Left_Tab: /* change color */
+ i = ndata[state.cur_note].col;
+ if ((ksym == XK_Tab && (event->xkey.state & 0x01) == 0x01) ||
+ ksym == XK_ISO_Left_Tab)
+ { /* previous color */
+ if (--ndata[state.cur_note].col < 0)
+ ndata[state.cur_note].col = C_NUM-1;
+ } else if (++ndata[state.cur_note].col == C_NUM)
+ ndata[state.cur_note].col = 0;
+ init_edit_mode(state.cur_note);
+ set_cursor(ndata[state.cur_note].cursor, 0);
+ redraw_window();
+ break;
+ case XK_Home: case XK_KP_Home:
+ set_cursor(10*(ndata[state.cur_note].cursor/10), 1);
+ break;
+ case XK_End: case XK_KP_End:
+ i = j = ndata[state.cur_note].cursor < 50 ?
+ 10*(ndata[state.cur_note].cursor/10)+9 : 58;
+ if (ndata[state.cur_note].text[j] == ' ')
+ for (; i > 10*(j/10) && ndata[state.cur_note].text[i-1] == ' '; i--);
+ set_cursor(i, 1);
+ break;
+ case XK_Prior: case XK_KP_Prior: /* page up */
+ set_cursor(0, 1);
+ break;
+ case XK_Next: case XK_KP_Next: /* page down */
+ set_cursor(58, 1);
+ break;
+ case XK_Insert: case XK_KP_Insert:
+ state.insert = !state.insert;
+ set_cursor(ndata[state.cur_note].cursor, 1);
+ break;
+ case XK_Escape:
+ quit_edit_mode(0, 1);
+ if (!opts.click_to_focus) set_kbfocus(0);
+ break;
+ default:
+ if (j) {
+ if (state.insert)
+ shift_string(state.cur_note, ndata[state.cur_note].cursor,
+ ndata[state.cur_note].cursor < 50 ?
+ 10*(ndata[state.cur_note].cursor/10)+9 : 58, 1, ch[0]);
+ else {
+ ndata[state.cur_note].text[ndata[state.cur_note].cursor] = ch[0];
+ print_letter(state.cur_note, ndata[state.cur_note].cursor, 1);
+ }
+ set_cursor(ndata[state.cur_note].cursor < 58 ?
+ ndata[state.cur_note].cursor+1 : ndata[state.cur_note].cursor, 1);
+ }
+ }
+}
+ /********/
+/*********************************** MAIN ************************************/
+ /********/
+int
+main(int argc, char **argv)
+{
+#ifdef FUNSTUFF
+ time_t tt = 0, t;
+ struct tm ltt, lt;
+#endif
+ XEvent event;
+ XGCValues gcv;
+ int i, j;
+
+ srand(time(0));
+ umask(077); /* users' notes needn't be world-readable */
+
+ parse_argv(argc, argv); /* evaluate command line parameters */
+ init(); /* initialize X window */
+ XSetCommand(display, mainwin, argv, argc);
+ XMapWindow(display, mainwin);
+ init_xlocale(); /* initialize input context */
+
+ /* initialize internal images, palette, cursors */
+ bbar = get_xpm((char**) bbar_xpm);
+ abar = get_xpm((char**) abar_xpm);
+ digits = get_xpm((char**) digits_xpm);
+ img = XGetImage(display, app, 0, 0, 86, 64, ~0, ZPixmap);
+ XGetSubImage(display, abar, 47, 4, 8, 8, ~0, ZPixmap, img, 70, 55);
+ XGetSubImage(display, abar, 47, 16, 8, 8, ~0, ZPixmap, img, 78, 55);
+ for (i = 0; i < C_NUM; i++) {
+ palette[i].bg = XGetPixel(img, 80, i);
+ palette[i].fg = XGetPixel(img, 81, i);
+ palette[i].cr = XGetPixel(img, 82, i);
+ }
+ palette[C_NUM].fg = XGetPixel(img, 80, 63); /* C_INNER */
+ palette[C_NUM].bg = XGetPixel(img, 81, 63); /* C_OUTER */
+ palette[C_NUM].cr = XGetPixel(img, 82, 63); /* C_EXTRA */
+ gcv.foreground = C_OUTER; /* dummy value */
+ gcv.graphics_exposures = 0;
+ fillGC = XCreateGC(display, app, GCForeground | GCGraphicsExposures, &gcv);
+ cursors[0] = XCreateFontCursor(display, XC_fleur); /* when moving a note */
+ cursors[1] = XCreateFontCursor(display, XC_dotbox); /* drag-create */
+ cursors[2] = XCreateFontCursor(display, XC_pencil); /* for sketch mode */
+
+ load_font();
+ load_theme(opts.theme);
+ notes_io(1); /* saves PID */
+ render_pinboard(-1);
+ redraw_window();
+ set_mode(M_NOOP);
+
+ for(;;) { /*** MAIN EVENT LOOP ***/
+ while (XPending(display)) {
+ XNextEvent(display, &event);
+
+ s_block(); /* BEGIN OF SYNC-PROHIBITING SECTION */
+
+ switch (event.type) {
+ case Expose:
+ redraw_window();
+ break;
+ case ButtonPress:
+ handle_ButtonPress(&event);
+ time(&state.idle);
+ break;
+ case ButtonRelease:
+ if (event.xbutton.button == state.button)
+ handle_ButtonRelease(&event);
+ time(&state.idle);
+ break;
+ case MotionNotify:
+ handle_MotionNotify(&event);
+ break;
+ case EnterNotify:
+ if (state.mode == M_EDIT && !opts.click_to_focus) set_kbfocus(1);
+ state.clicks_count = 0;
+ break;
+ case LeaveNotify:
+ if (state.mode == M_EDIT && !opts.click_to_focus) set_kbfocus(0);
+ break;
+ case KeyPress:
+ handle_KeyPress(&event);
+ break;
+ case SelectionClear:
+ clear_selection();
+ cb_clear();
+ break;
+ case SelectionNotify:
+ if (state.mode == M_EDIT) {
+ cb_paste_external(event.xselection.requestor,
+ event.xselection.property, 1, state.cur_note, state.insert,
+ state.raw_paste);
+ init_edit_mode(state.cur_note);
+ set_cursor(ndata[state.cur_note].cursor, 1);
+ }
+ break;
+ case SelectionRequest:
+ handle_SelectionRequest(&(event.xselectionrequest));
+ }
+ }
+ if (opts.timeout && (state.mode == M_EDIT || state.mode == M_BBAR ||
+ state.mode == M_DRAW || state.mode == M_ERAS) &&
+ time(0)-state.idle > opts.timeout)
+ {
+ quit_edit_mode(0, 1); /* no set_kbfocus(0) here */
+ }
+#ifdef FUNSTUFF
+ if (state.mode == M_NOOP) {
+ time(&t);
+ if (t-tt > 10*60 && localtime_r(&tt, <t) && localtime_r(&t, <)) {
+ if (ltt.tm_mday != lt.tm_mday || ltt.tm_mon != lt.tm_mon ||
+ ltt.tm_year != lt.tm_year)
+ {
+ i = notes_count;
+ j = state.state_bits;
+ check_occasion(lt.tm_mday, 1+lt.tm_mon, 1900+lt.tm_year);
+ if (i != notes_count || j != state.state_bits) { /* anything done? */
+ notes_io(1);
+ render_pinboard(-1);
+ redraw_window();
+ }
+ }
+ tt = t;
+ }
+ }
+#endif
+ if (state.mode == M_ALRM)
+ animate_alarm();
+ else if (state.mode == M_NOOP && state.alarm.note >= 0 &&
+ state.alarm.time <= time(0))
+ { /* alarm due */
+ state.cur_note = state.alarm.note;
+ state.alarm.run = 1;
+ if (ndata[state.cur_note].a_flags & ALARM_DATE)
+ ndata[state.cur_note].a_flags &= ~ALARM_ON;
+ animate_note(4);
+ set_mode(M_ALRM);
+ if (strlen(opts.alarm_cmd)) {
+ char buf[STRING_BUF_SIZE+2];
+ strcpy(buf, opts.alarm_cmd);
+ strcat(buf, " &");
+ system(buf);
+ }
+ prepare_alarm_anim();
+ state.alarm.phase = 0;
+ animate_alarm();
+ }
+
+ s_unblock(); /* END OF SYNC-PROHIBITING SECTION */
+
+ /* sleep for a while... */
+ switch (state.mode) {
+ case M_NOOP: usleep(100000L); break;
+ case M_ALRM: usleep(500000L); break; /* animation timing, 0.5s */
+ default: usleep( 10000L);
+ }
+ }
+}
+
+/*
+ CONCEPTUAL NOTES
+
+ bbar =
+ show panel +---------+
+ mainwin[, iconwin] ,------------------------|::panel::| 30
+ (each) = | app = |:::::::::|
+ +----------+ | +----------+--+ +---------+
+ | | `->| |::| 58 \
+ | visible | display | draw |::| 48 dto. for abar
+ 64 | area | <=========== | buffer |::|
+ | | 64x64 | +-++
+ | | ,-->| <->|| 16
+ +----------+ | +----------+-++
+ 64 | 64 16\3
+ | \ used when
+ | moving a note server-side
+ ........................|...............................................
+ |
+ restore | client-side
+ ,----------->-+
+ | board | draw
+ img = | 16 +-<--------[bitfield]
+ +----------+-++ pin | sketch
+ |::::::::::| |------>-+
+ |:copy:of::| || notes |
+ 64 |:pristine:| || |
+ |:pinboard:+++| copy |
+ |:::::::10{||---<--->-'
+ +----------++-+ character/write it back
+ 64 6\16
+ /\ :: = const
+ / \ used to overlay a
+ / character with the cursor
+ /
+ buffer of (8+8)x8 @ 70,55 for alarm panel switches
+*/
+
diff --git a/src/wmpinboard.h b/src/wmpinboard.h
new file mode 100644
index 0000000..f9f81b3
--- /dev/null
+++ b/src/wmpinboard.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 1998-2000 by Marco G"otze.
+ *
+ * This code is part of the wmpinboard source package, which is
+ * distributed under the terms of the GNU GPL2.
+ */
+
+#ifndef WMPINBOARD_H_INCLUDED
+#define WMPINBOARD_H_INCLUDED
+
+#include <time.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define MAX_NOTES 20 /* maximal number of notes */
+
+#define STRING_BUF_SIZE 128 /* size of buffers for font descr. & suchlike */
+
+#define C_NUM 20 /* number of colors */
+#define C_INNER palette[C_NUM].fg /* mask color (interior of notes etc.) */
+#define C_OUTER palette[C_NUM].bg /* mask color (exterior of notes etc.) */
+#define C_EXTRA palette[C_NUM].cr /* additional mask color */
+
+/* program modes with respect to interactive behavior... */
+typedef enum {
+ M_NOOP, /* "normal" mode (pinboard view) */
+ M_EDIT, /* edit mode */
+ M_MOVE, /* note being dragged (implies M_NOOP) */
+ M_BBAR, /* button bar being displayed (implies M_EDIT) */
+ M_DRAW, /* sketch mode, drawing (implies M_EDIT) */
+ M_ERAS, /* sketch mode, erasing (implies M_EDIT) */
+ M_ALRM /* alarm active */
+} modes;
+
+typedef enum {
+ /* command line actions */
+ A_DUMP, /* "cooked" dump */
+ A_DUMP_RAW, /* raw dump */
+ A_IRON, /* "iron" notes */
+ A_DEL, /* delete a now */
+ A_ADD, /* add "cooked" */
+ A_ADD_RAW, /* add raw */
+ A_EXPORT, /* export sketch */
+ /* configuration options */
+ C_FONT, /* change font */
+ C_THEME, /* change board/panel pixmap */
+ C_ALARM_CMD, /* change alarm command */
+ /* miscellani */
+ M_INFO /* prints miscellanous information */
+} actions;
+
+#define PANEL_ANI_INT 2000L /* usecs interval for panel animation */
+#define NOTE_ANI_INT 15000L /* usecs interval for note animation */
+
+#define DCLICK_LIMIT 500 /* ms */
+
+#define FUNSTUFF
+
+typedef enum {
+ ALARM_ON = 1<<0,
+ ALARM_DATE = 1<<1
+} alarm_flags_e;
+
+typedef struct {
+ int col;
+ int x, y;
+ char text[10*6];
+ int cursor;
+ char sketch[(64/8)*64]; /* bitfield; last byte used for other purposes... */
+ char creases[(16/8)*16]; /* lower-res bitfield representing creases */
+ time_t a_time; /* alarm time */
+ unsigned char a_flags; /* alarm flags */
+} data_t;
+
+typedef struct {
+ unsigned long fg, bg, cr; /* foreground, background, crease color */
+} palette_t;
+
+typedef struct { /* options and parameters */
+ char *name; /* the program's file name (argv[0]) */
+ char *display; /* alternate X display to connect to */
+ int click_to_focus; /* true if keyboard focus requires a click */
+ int window_state; /* NormalState, WithdrawnState? */
+ int timeout; /* timeout value in seconds */
+ int animate; /* use animations? */
+ char font[STRING_BUF_SIZE]; /* font descriptor to remember */
+ char theme[STRING_BUF_SIZE]; /* theme file to remember */
+ char alarm_cmd[STRING_BUF_SIZE]; /* alarm command */
+} opts_t;
+
+typedef struct { /* program state information */
+ GC sketchGC; /* temporary GC in sketch mode */
+ XComposeStatus compose; /* keyboard compose status */
+ int clicks_count; /* while emulating click-based focusing */
+ int cur_note; /* note currently being processed */
+ int moved; /* true if a note was *moved* (not just raised) */
+ int button, dx, dy; /* mouse-related stuff */
+ modes mode; /* program's current mode of operation */
+ int bbar_pressed; /* *pressed* panel button */
+ int abar_pressed; /* area on alarm panel that last received a click */
+ int insert; /* insert state in edit mode? */
+ int selecting; /* selection in progress? */
+ int sel_from, sel_to; /* used when selecting text via the mouse */
+ int lp_btn; /* button last pressed */
+ Time lp_time; /* time the last button was *pressed* */
+ time_t idle; /* for the timeout feature */
+ volatile int alarmed; /* used in animation timing */
+ unsigned int state_bits; /* bit vector with special information */
+ int raw_paste; /* next paste to be raw? */
+ int counter; /* total number of notes ever created */
+ struct {
+ time_t time; /* time_t of next alarm that's due */
+ int note; /* note that alarm.time is set for */
+ int run; /* true if the specified alarm was run */
+ int phase; /* phase of the animation */
+ Pixmap buffer; /* buffer for the animation phases */
+ } alarm;
+ unsigned char a_edit[5]; /* hour, minute, month, day, year being edited */
+} state_t;
+
+extern Display *display;
+extern Window win;
+extern XImage *img;
+extern Pixmap app, bbar, abar, digits;
+extern GC normalGC, fontGC, fillGC;
+#ifdef CREASES
+extern GC creaseGC;
+#endif
+extern XFontStruct *font;
+
+extern const char c_group[C_NUM];
+extern int notes_count;
+extern data_t ndata[MAX_NOTES];
+extern palette_t palette[C_NUM+1];
+extern state_t state;
+
+#endif /* WMPINBOARD_H_INCLUDED */
+
diff --git a/src/xmisc.c b/src/xmisc.c
new file mode 100644
index 0000000..1eb217b
--- /dev/null
+++ b/src/xmisc.c
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 1998-2000 by Marco G"otze.
+ *
+ * This code is part of the wmpinboard source package, which is
+ * distributed under the terms of the GNU GPL2.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+
+#include <X11/X.h>
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <X11/Xlocale.h>
+#include <X11/Xmd.h>
+#include <X11/xpm.h>
+#include <X11/extensions/shape.h>
+
+#include "wmpinboard.h"
+#include "misc.h"
+#include "notes.h"
+#include "xmisc.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+typedef CARD32 Atom32;
+
+XIC InputContext = 0;
+static char *rs_inputMethod = 0;
+static char *rs_preeditType = 0;
+static unsigned char *cb_buffer = 0;
+
+/*
+ * replaces <o_col> by <n_col> in an XImage ([no]_col are XGetPixel return
+ * values!)
+ */
+void
+replace_color(XImage *img, int x, int y, int w, int h, unsigned long o_col,
+ unsigned long n_col)
+{
+ int i, j;
+
+ for (i = x; i < x+w; i++)
+ for (j = y; j < y+h; j++)
+ if (XGetPixel(img, i, j) == o_col)
+ XPutPixel(img, i, j, n_col);
+}
+
+/*
+ * puts <dest> on app, copying pixels only where <src>'s color is !=
+ * <mask_col>'s
+ */
+void
+merge_masked(XImage *src, int sx, int sy, int dx, int dy, int w, int h,
+ unsigned long mask_col)
+{
+ GC tempGC;
+ XGCValues gcv;
+ unsigned long p;
+ int i, j;
+
+ gcv.foreground = mask_col; /* dummy value */
+ tempGC = XCreateGC(display, app, GCForeground, &gcv);
+ for (i = 0; i < w; i++)
+ for (j = 0; j < h; j++) {
+ p = XGetPixel(src, sx+i, sy+j);
+ if (p != mask_col) {
+ XSetForeground(display, tempGC, p);
+ XDrawPoint(display, app, tempGC, dx+i, dy+j);
+ }
+ }
+ XFreeGC(display, tempGC);
+}
+
+/*
+ * converts a pixmap structure to a Pixmap
+ */
+void
+get_xpm_with_mask(char *pixmap_bytes[], Pixmap *pic, Pixmap *mask)
+{
+ XpmAttributes attr;
+
+ attr.valuemask = XpmExactColors | XpmCloseness;
+ attr.exactColors = False;
+ attr.closeness = 65536;
+ if (XpmCreatePixmapFromData(display, RootWindow(display,
+ DefaultScreen(display)), pixmap_bytes, pic, mask, &attr) != XpmSuccess)
+ {
+ die("Not enough free color cells.");
+ }
+}
+
+/*
+ * calls get_xpm_with_mask() but returns just the pixmap
+ */
+Pixmap
+get_xpm(char *pixmap_bytes[])
+{
+ Pixmap pic, mask;
+ get_xpm_with_mask(pixmap_bytes, &pic, &mask);
+ return pic;
+}
+
+/*
+ * flushes Expose events
+ */
+void
+flush_expose(void)
+{
+ XEvent dummy;
+
+ while (XCheckTypedWindowEvent(display, win, Expose, &dummy));
+}
+
+/*
+ * redraws the application's window
+ */
+void
+redraw_window(void)
+{
+ flush_expose();
+ XCopyArea(display, app, win, normalGC, 0, 0, 64, 64, 0, 0);
+}
+
+/*
+ * sets either the app's shape mask for normal operation or reverts to none
+ * (edit mode)
+ */
+void
+set_mask(int set)
+{
+ static XRectangle none = { 0, 0, 64, 64 };
+ static XRectangle mask = { 6, 2, 52, 60 };
+
+ if (set)
+ XShapeCombineRectangles(display, win, ShapeBounding, 0, 0, &mask, 1,
+ ShapeSet, 0);
+ else
+ XShapeCombineRectangles(display, win, ShapeBounding, 0, 0, &none, 1,
+ ShapeSet, 0);
+}
+
+/*
+ * sets up XIC-orientated keyboard handling
+ * [stolen from the RXVT, minor adaptations]
+ */
+void
+init_xlocale(void)
+{
+ char *p, *s, buf[32], tmp[1024];
+ XIM xim = 0;
+ XIMStyle input_style = 0;
+ XIMStyles *xim_styles = 0;
+ int found;
+
+ InputContext = 0;
+ setlocale(LC_ALL, "");
+
+ if (!rs_inputMethod || !*rs_inputMethod) {
+ if ((p = XSetLocaleModifiers("@im=none")) && *p)
+ xim = XOpenIM(display, 0, 0, 0);
+ } else {
+ strcpy(tmp, rs_inputMethod);
+ for (s = tmp; *s;) {
+ char *end, *next_s;
+
+ for (; *s && isspace(*s); s++);
+ if (!*s) break;
+ end = s;
+ for (; *end && (*end != ','); end++);
+ next_s = end--;
+ for (; (end >= s) && isspace(*end); end--);
+ *(end + 1) = '\0';
+
+ if (*s) {
+ strcpy(buf, "@im=");
+ strcat(buf, s);
+ if ((p = XSetLocaleModifiers(buf)) && *p
+ && (xim = XOpenIM(display, 0, 0, 0)))
+ {
+ break;
+ }
+ }
+ if (!*next_s) break;
+ s = (next_s + 1);
+ }
+ }
+
+ if (!xim && (p = XSetLocaleModifiers("")) && *p)
+ xim = XOpenIM(display, 0, 0, 0);
+
+ if (!xim) {
+#ifdef DEBUG_IC
+ fprintf(stderr, "Failed to open input method.\n");
+#endif
+ return;
+ }
+
+ if (XGetIMValues(xim, XNQueryInputStyle, &xim_styles, 0) || !xim_styles) {
+ XCloseIM(xim);
+#ifdef DEBUG_IC
+ fprintf(stderr, "Input method doesn't support any style.\n");
+#endif
+ return;
+ }
+
+ strcpy(tmp, (rs_preeditType ? rs_preeditType : "Root"));
+ for (found = 0, s = tmp; *s && !found;) {
+ unsigned short i;
+ char *end, *next_s;
+
+ while (*s && isspace(*s)) s++;
+ if (!*s) break;
+ end = s;
+ while (*end && (*end != ',')) end++;
+ next_s = end--;
+ while ((end >= s) && isspace(*end)) *end-- = 0;
+
+ if (!strcmp(s, "OverTheSpot"))
+ input_style = (XIMPreeditPosition | XIMStatusArea);
+ else if (!strcmp(s, "OffTheSpot"))
+ input_style = (XIMPreeditArea | XIMStatusArea);
+ else if (!strcmp(s, "Root"))
+ input_style = (XIMPreeditNothing | XIMStatusNothing);
+
+ for (i = 0; i < xim_styles->count_styles; i++) {
+ if (input_style == xim_styles->supported_styles[i]) {
+ found = 1;
+ break;
+ }
+ }
+ s = next_s;
+ }
+ XFree(xim_styles);
+
+ if (found == 0) {
+ XCloseIM(xim);
+#ifdef DEBUG_IC
+ fprintf(stderr, "Input method doesn't support my preedit type.\n");
+#endif
+ return;
+ }
+
+ if (input_style != (XIMPreeditNothing | XIMStatusNothing)) {
+ XCloseIM(xim);
+#ifdef DEBUG_IC
+ fprintf(stderr, "This program only supports the `Root' preedit type.\n");
+#endif
+ return;
+ }
+
+ InputContext = XCreateIC(xim, XNInputStyle, input_style,
+ XNClientWindow, win, XNFocusWindow, win, 0);
+
+ if (!InputContext) {
+ XCloseIM(xim);
+ }
+}
+
+/*
+ * creates a dock-app-tailored window
+ */
+Window
+create_win()
+{
+ Window win;
+ XClassHint hint;
+ XSetWindowAttributes attr;
+
+ win = XCreateSimpleWindow(display,
+ RootWindow(display, DefaultScreen(display)), 0, 0, 64, 64, 0, 0, 0);
+ hint.res_name = (char*) "wmpinboard";
+ hint.res_class = (char*) "WMPINBOARD";
+ XSetClassHint(display, win, &hint);
+ attr.background_pixmap = ParentRelative;
+ XChangeWindowAttributes(display, win, CWBackPixmap, &attr);
+ return win;
+}
+
+/*
+ * copies <text> to the X clipboard
+ */
+void
+cb_copy(const char *text, int len)
+{
+ int l;
+
+ if (!text) return;
+
+ if (cb_buffer) free(cb_buffer);
+ l = len < 0 ? strlen(text) : len;
+ cb_buffer = smalloc(l+1);
+ strncpy(cb_buffer, text, l);
+ cb_buffer[l] = 0;
+
+ XSetSelectionOwner(display, XA_PRIMARY, win, CurrentTime);
+ if (XGetSelectionOwner(display, XA_PRIMARY) != win)
+ WARN("Failed to set XA_PRIMARY ownership.");
+ XChangeProperty(display, DefaultRootWindow(display), XA_CUT_BUFFER0,
+ XA_STRING, 8, PropModeReplace, cb_buffer, l);
+}
+
+/*
+ * responds to a SelectionRequest event
+ * [once again, thanks to the RXVT source]
+ */
+void
+handle_SelectionRequest(XSelectionRequestEvent *rq)
+{
+ XEvent ev;
+ Atom32 target_list[2];
+ static Atom xa_targets = None;
+
+ if (xa_targets == None) xa_targets = XInternAtom(display, "TARGETS", 0);
+
+ ev.xselection.type = SelectionNotify;
+ ev.xselection.property = None;
+ ev.xselection.display = rq->display;
+ ev.xselection.requestor = rq->requestor;
+ ev.xselection.selection = rq->selection;
+ ev.xselection.target = rq->target;
+ ev.xselection.time = rq->time;
+
+ if (rq->target == xa_targets) {
+ target_list[0] = (Atom32) xa_targets;
+ target_list[1] = (Atom32) XA_STRING;
+ XChangeProperty(display, rq->requestor, rq->property, rq->target,
+ 8*sizeof(target_list[0]), PropModeReplace,
+ (unsigned char*) target_list,
+ sizeof(target_list)/sizeof(target_list[0]));
+ ev.xselection.property = rq->property;
+ } else if (rq->target == XA_STRING) {
+ XChangeProperty(display, rq->requestor, rq->property, rq->target,
+ 8, PropModeReplace, cb_buffer, strlen((char*) cb_buffer));
+ ev.xselection.property = rq->property;
+ }
+ XSendEvent(display, rq->requestor, 0, 0, &ev);
+}
+
+/*
+ * handle's the user's request to paste text into a note
+ */
+void
+cb_paste(int note, int ins)
+{
+ Atom prop;
+
+ if (cb_buffer) {
+ paste(note, ndata[note].cursor, (const char*) cb_buffer, ins,
+ state.raw_paste);
+ } else if (XGetSelectionOwner(display, XA_PRIMARY) == None) {
+ cb_paste_external(DefaultRootWindow(display), XA_CUT_BUFFER0, 0, note,
+ ins, state.raw_paste);
+ } else {
+ prop = XInternAtom(display, "VT_SELECTION", 0);
+ XConvertSelection(display, XA_PRIMARY, XA_STRING, prop, win, CurrentTime);
+ }
+}
+
+/*
+ * pastes the current contents of the clipboard into <note> at <pos>, inserting
+ * or overwriting depending on <ins>, trying to word-wrap unless <raw>; moves
+ * the cursor
+ */
+void
+cb_paste_external(Window window, unsigned prop, int Delete, int note, int ins,
+ int raw)
+{
+ unsigned long bytes_after, nitems;
+ unsigned char *data;
+ Atom actual_type;
+ int actual_fmt;
+
+ if (prop == None) return;
+ if ((XGetWindowProperty(display, window, prop, 0, 64, Delete,
+ AnyPropertyType, &actual_type, &actual_fmt, &nitems, &bytes_after,
+ &data) != Success))
+ {
+ XFree(data);
+ return;
+ }
+ if (nitems) paste(note, ndata[note].cursor, (const char*) data, ins, raw);
+ XFree(data);
+}
+
+/*
+ * frees the copy made of a selected string
+ */
+void
+cb_clear()
+{
+ if (cb_buffer) {
+ free(cb_buffer);
+ cb_buffer = 0;
+ }
+}
+
+/*
+ * prepares the buffer which portions are copied from during an alarm animation
+ */
+void
+prepare_alarm_anim()
+{
+ unsigned long wh = WhitePixel(display, DefaultScreen(display));
+ unsigned long bl = BlackPixel(display, DefaultScreen(display));
+ XImage *i;
+ int x, y;
+
+ if (state.alarm.buffer != None) XFreePixmap(display, state.alarm.buffer);
+ /* create buffer pixmap */
+ state.alarm.buffer = XCreatePixmap(display, win, 128, 64,
+ DefaultDepth(display, DefaultScreen(display)));
+ /* copy current edit view (note: strangely, if we copy from win and are
+ running WM and this happens during WM start-up (so the dock isn't
+ finished while executing this), wmpinboard gets terminated) */
+ XCopyArea(display, app, state.alarm.buffer, normalGC, 0, 0, 64, 64, 0, 0);
+ /* create inverted version */
+ i = XGetImage(display, state.alarm.buffer, 0, 0, 64, 64, ~0, ZPixmap);
+ for (y = 0; y < 64; y++)
+ for (x = 0; x < 64; x++)
+ XPutPixel(i, x, y,
+ XGetPixel(i, x, y) == palette[ndata[state.cur_note].col].fg ? wh : bl);
+ XPutImage(display, state.alarm.buffer, normalGC, i, 0, 0, 64, 0, 64, 64);
+ XDestroyImage(i);
+}
+
diff --git a/src/xmisc.h b/src/xmisc.h
new file mode 100644
index 0000000..dfd9a25
--- /dev/null
+++ b/src/xmisc.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 1998-2000 by Marco G"otze.
+ *
+ * This code is part of the wmpinboard source package, which is
+ * distributed under the terms of the GNU GPL2.
+ */
+
+#ifndef XMISC_H_INCLUDED
+#define XMISC_H_INCLUDED
+
+#include <X11/Xlib.h>
+
+#include "wmpinboard.h"
+
+void replace_color(XImage*, int, int, int, int, unsigned long, unsigned long);
+void merge_masked(XImage*, int, int, int, int, int, int, unsigned long);
+
+void get_xpm_with_mask(char**, Pixmap*, Pixmap*);
+Pixmap get_xpm(char**);
+void flush_expose(void);
+void redraw_window(void);
+void set_mask(int);
+void init_xlocale(void);
+Window create_win(void);
+void cb_copy(const char*, int);
+void handle_SelectionRequest(XSelectionRequestEvent *rq);
+void cb_paste(int, int);
+void cb_paste_external(Window, unsigned, int, int, int, int);
+void cb_clear(void);
+void prepare_alarm_anim();
+
+extern XIC InputContext;
+
+#endif /* XMISC_H_INCLUDED */
+
diff --git a/themes-kit/HOWTO b/themes-kit/HOWTO
new file mode 100644
index 0000000..23139d0
--- /dev/null
+++ b/themes-kit/HOWTO
@@ -0,0 +1,95 @@
+
+ How to create a theme for wmpinboard v0.99.1+
+ ================================================
+
+ Overview
+----------
+
+wmpinboard is themeable in that its pinboard, edit mode and alarm
+panels, and the digits used on the alarm panel, as well as the location
+of the board's "TO DO" label (via which notes are created) are run-time
+configurable using theme files. A wmpinboard theme file is a text file
+with a recommended file name extension of ".wmpbtheme".
+
+ Theme file format
+-------------------
+
+As stated above, a wmpinboard theme file is a text file in a specific
+format. Roughly, it consists of a header section and one or two pixmap
+data sections:
+
+ WMPBtheme
+ [header fields]
+ # comments
+
+ [pixmap[s]]
+
+The various sections are separated by zero-length lines.
+
+Each theme file has to start with the keyword "WMPBtheme", designating
+the file to be a wmpinboard theme file. Note that this identifier is
+checked for case-sensitively.
+
+Following on the same line or on the next, a "label" header field may
+be specified like this:
+
+ header = x1/y1, x2/y2
+
+x1, y1, etc. are integer numbers; white space within this specification
+is optional. The delimiters in between the numbers can be chosen
+almost arbitrarily (when parsing, wmpinboard regards any non-digit
+characters as delimiters). The purpose of this field is to specify a
+rectangular area (x1/y1 upper left, x2/y2 lower right corner;
+coordinates are inclusive) identifying a modified location of the
+default "TO DO" label, relative to the board pixmap. The given
+coordinates have to fulfil the following conditions:
+
+ x1, x2 = [0..51]
+ y1, y2 = [0..59]
+ x1 < x2
+ y1 < y2
+ (x2-x1) * (y2-y1) >= 16
+
+Furthermore, the header section may contain comments, which wmpinboard
+considers everything after `#'s.
+
+Each of the pixmap sections (only one is required) has to be standard
+(C source) pixmap data WITH NO INTERMEDIATE ZERO-LENGTH LINES. Since
+themes are meant to work in high-color modes only, you needn't worry
+about the number of colors used, or other palette issues. To ease the
+creation of custom pixmaps, this package includes the default pixmaps
+as templates.
+
+Which themeable portion of wmpinboard a pixmap represents is determined
+by its size:
+
+ 52x60 pinboard
+ 58x30 edit mode panel
+ 58x28 alarm panel
+ 60x 9 alarm panel digits (6x9 each)
+
+To illustrate this formal description, the default and a sample theme
+are included as ".wmpbtheme" files. The former defines a label
+location as well as all the pixmaps, the latter a label location and an
+alternative board pixmap.
+
+ Restrictions
+--------------
+
+Generally, custom pixmap sizes are NOT supported, and neither is
+transparency, meaning you'll have to stick with the design-implied
+dimensions. Also, the panels' buttons' area-to-function mapping is
+fixed.
+
+There should be no lines longer than 127 characters in a theme file, or
+else unpredictable things may happen.
+
+ Submission of themes
+----------------------
+
+If you've created a custom theme you wish to share with other users of
+wmpinboard, mail it to <gomar at mindless.com>. It will then most likely
+be added to the program's home page at
+
+ <http://www.tu-ilmenau.de/~gomar/stuff/wmpinboard/>
+
diff --git a/themes-kit/abar.xpm b/themes-kit/abar.xpm
new file mode 100644
index 0000000..fcc2366
--- /dev/null
+++ b/themes-kit/abar.xpm
@@ -0,0 +1,42 @@
+/* XPM */
+static char * abar_xpm[] = {
+"58 28 11 1",
+" c None",
+". c #000000",
+"+ c #EFD183",
+"@ c #D2AA5C",
+"# c #6E3A1C",
+"$ c #878787",
+"% c #6B9600",
+"& c #AEFA04",
+"* c #FEFEFC",
+"= c #87003A",
+"- c #F73D88",
+"..........................................................",
+".++++++++++++++++++++++++++++++++++++++++++++++++++++++++.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@..@@@@@@@@@@....@@@#.",
+".+@@@@@@@@@@@@@@+$@@@@@@@@@@@@@@@@@@@....@@@@@@@.%%%%.@@#.",
+".+@@@@@@@@@@@@@@..@@@@@@@@@@@@@@@@$........$@@@.%&&%%%.@#.",
+".+@@@@@@@@@@@@@@$#@@@@@@@@@@@@@@@$.@@....@@.$@@.%&&&%%.@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.@@@..@@@@@.@@.%%&%%%.@#.",
+".+@@@@@@@@@@@@@@+$@@@@@@@@@@@@@@@.@@@@@@@@@@.@@.%%%%%%.@#.",
+".+@@@@@@@@@@@@@@..@@@@@@@@@@@@@@@$.@@@@@@@@.$@@@.%%%%.@@#.",
+".+@@@@@@@@@@@@@@$#@@@@@@@@@@@@@@@@$........$@@@@@....@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@...........@@@@@@@@@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@...........@@@@@@@@@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.$$$$$$$$..@@@@....@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.***==***..@@@.====.@@#.",
+".+@@@@@@@@@@@@@@+$@@@@@@@@@@@@@@@@.**===***..@@.=--===.@#.",
+".+@@@@@@@@@@@@@@..@@@@@@@@@@@@@@@@.***==***..@@.=---==.@#.",
+".+@@@@@@@@@@@@@@$#@@@@@@@@@@@@@@@@.***==***..@@.==-===.@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.***==***..@@.======.@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.********...@@@.====.@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@...........@@@@@....@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@...........@@@@@@@@@@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#.",
+".########################################################.",
+".........................................................."};
diff --git a/themes-kit/bbar.xpm b/themes-kit/bbar.xpm
new file mode 100644
index 0000000..7c7aea7
--- /dev/null
+++ b/themes-kit/bbar.xpm
@@ -0,0 +1,56 @@
+/* XPM */
+static char * bbar_xpm[] = {
+"58 30 23 1",
+" c None",
+". c #000000",
+"+ c #EFD183",
+"@ c #6E3A1C",
+"# c #D2AA5C",
+"$ c #FA0604",
+"% c #7F6602",
+"& c #FF0569",
+"* c #FFFFFF",
+"= c #FADA04",
+"- c #AEFA04",
+"; c #6B9600",
+"> c #F48989",
+", c #C16C6C",
+"' c #894E4E",
+") c #F73D88",
+"! c #87003A",
+"~ c #9E0EDC",
+"{ c #FE821C",
+"] c #62A6EF",
+"^ c #3E6599",
+"/ c #2A4668",
+"( c #F2EE04",
+"..........................................................",
+".+++++++++++++ at +++++++++++++@+++++++++++++ at ++++++++++++++.",
+".+############@+############@+############@+############@.",
+".+####...@$###@+####%.%#####@+##########&#@+#.....#....#@.",
+".+##..*.*.&$##@+####.=.#####@+###.###.#&&#@+#.---.#.;-.#@.",
+".+##.**.**.@##@+###%=.######@+##.#.##.&&##@+#.---.#.--.#@.",
+".+#.***.***.##@+###.=%##..##@+#.###.#&&###@+#.--;.#.--.#@.",
+".+#.***.***.##@+##%=.##.##.#@+#.###.&&..##@+#....##.--.#@.",
+".+#.**.****.##@+##.=%#.####. at +#....&&.##.#@+######.;--.#@.",
+".+##.*****.###@+#%=.##.####. at +#.##&&#.##.#@+#....#.;--.#@.",
+".+##..***..###@+#.=%###.##.#@+#.#&&.#.##.#@+#.-;.##.--.#@.",
+".+##%.....%###@+#..#####..##@+#.&&#.#...##@+#.--;.#.;-.#@.",
+".+#...%#%...##@+#.......##.#@+#&&#########@+#.....##...#@.",
+".+############@+###########. at +############@+############@.",
+".@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.",
+".+++++++++++++ at +++++++++++++@+++++++++++++ at ++++++++++++++.",
+".+############@+####>>>>>>##@+############@+############@.",
+".+#-###......#@+###>,,,,,,'#@+#&#...######@+##########)#@.",
+".+#--###.....#@+###>,,,,,,'#@+#&&###.#####@+#########)!#@.",
+".+#~--###....#@+###>,,,,,,'#@+#.&&###.####@+########)!##@.",
+".+#~~--#.....#@+##>,,,,,,'##@+#.#&&#..####@+########)###@.",
+".+#$~~-...#..#@+##>,,,,,,'##@+#.##&&&.####@+#######)!###@.",
+".+#$$~...###.#@+##>,,,,,,'##@+##.#.#&&####@+#))###))####@.",
+".+#{$...--####@+##]^^,,,,'##@+###...#&&###@+#))))))!####@.",
+".+#{{..~~--###@+#]^^^^^^/###@+###.####&&##@+###)))!#####@.",
+".+#({{$$~~--##@+#]^^^^^^/###@+##.######&&#@+####))!#####@.",
+".+#(({{$$~~--#@+#]^^^^^^/###@+##........&#@+#####!######@.",
+".+############@+##//////####@+############@+############@.",
+".@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.",
+".........................................................."};
diff --git a/themes-kit/board.xpm b/themes-kit/board.xpm
new file mode 100644
index 0000000..666e4d2
--- /dev/null
+++ b/themes-kit/board.xpm
@@ -0,0 +1,135 @@
+/* XPM */
+static char *board[] = {
+/* width height num_colors chars_per_pixel */
+" 52 60 68 2",
+/* colors */
+".. c #d6fe04",
+".# c #565654",
+".a c #4e4e4c",
+".b c #362a14",
+".c c #121214",
+".d c #363634",
+".e c #6be520",
+".f c #020204",
+".g c #60c021",
+".h c #f22e96",
+".i c #aefa04",
+".j c #94d205",
+".k c #9a6e2c",
+".l c #fefefc",
+".m c #d5d5d4",
+".n c #562e1c",
+".o c #6e3a1c",
+".p c #8e421c",
+".q c #0a76ec",
+".r c #f9f14a",
+".s c #d1ca45",
+".t c #f6fe7c",
+".u c #6a6a6c",
+".v c #fada04",
+".w c #d2b705",
+".x c #febe04",
+".y c #d5a005",
+".z c #dec67c",
+".A c #d6ba6c",
+".B c #6a361c",
+".C c #66311c",
+".D c #d2aa5c",
+".E c #fe821c",
+".F c #d5721d",
+".G c #fe5e1c",
+".H c #d5561d",
+".I c #4e2a1c",
+".J c #fe8e8c",
+".K c #d57b7a",
+".L c #fa0604",
+".M c #c60805",
+".N c #bf2b79",
+".O c #ad067e",
+".P c #8a0868",
+".Q c #d6b66c",
+".R c #ca9e4c",
+".S c #8503c6",
+".T c #6d049d",
+".U c #2606c4",
+".V c #24089b",
+".W c #020684",
+".X c #040c54",
+".Y c #d2b264",
+".Z c #0b63ba",
+".0 c #8a421c",
+".1 c #0acaec",
+".2 c #0caac6",
+".3 c #0aea74",
+".4 c #0cc467",
+".5 c #0eda1c",
+".6 c #10b71d",
+".7 c #2e8e0c",
+".8 c #2e7b0e",
+".9 c #ceaa5c",
+"#. c #cea654",
+"## c #caa254",
+"#a c #c79849",
+"#b c #c2a454",
+/* pixels */
+".k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.b",
+".k.n.n.n.o.o.o.o.o.o.o.o.o.o.o.p.p.p.n.n.n.o.o.o.o.o.o.o.o.o.n.n.n.n.o.o.o.o.o.o.n.n.n.n.o.o.o.o.o.o.o.b",
+".k.o.o.o.o.o.o.n.n.o.o.o.n.t.t.t.t.t.o.p.t.t.t.n.o.o.o.t.t.t.o.o.o.o.t.t.t.o.o.n.o.o.o.o.p.p.o.o.p.o.o.b",
+".k.o.o.b.b.b.b.b.b.b.b.n.o.o.n.t.o.o.o.t.o.o.o.t.o.p.n.t.n.p.t.o.o.t.n.o.o.t.p.b.b.b.b.b.b.b.b.b.k.o.o.b",
+".k.o.o.b.z.z.z.z.A.z.z.A.o.o.o.t.p.o.n.t.o.o.o.t.o.o.o.t.B.B.C.t.B.t.C.C.B.t.o.b.D.D.z.z.z.A.A.A.k.n.o.b",
+".k.n.o.b.z.z.z.A.z.A.A.A.o.n.o.t.C.C.o.t.n.n.o.t.o.o.o.t.B.B.B.t.B.t.C.C.C.t.B.b.D.D.z.A.A.D.A.z.k.o.o.b",
+".k.o.o.b.z.A.A.A.A.A.A.A.o.o.I.t.C.C.o.t.o.o.o.t.o.n.n.t.p.p.o.t.p.t.p.n.n.t.p.b.D.D.A.A.A.A.A.z.k.o.o.b",
+".k.o.p.b.A.A.A.A.A.A.A.A.o.o.o.t.n.n.p.p.t.t.t.o.o.o.o.t.t.t.t.n.C.C.t.t.t.C.C.b.D.A.A.A.A.A.A.A.k.p.o.b",
+".k.o.o.b.D.A.A.A.A.A.A.A.n.n.o.o.B.C.C.C.C.I.I.I.o.C.o.p.C.o.o.o.C.C.C.C.C.C.C.b.D.D.D.D.A.A.A.A.k.o.n.b",
+".k.p.p.b.A.A.D.D.A.A.A.A.A.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.A.D.D.D.D.A.A.D.A.k.n.o.b",
+".k.p.o.b.A.Q.Q.Q.Q.Q.A.D.A.D.D.D.D.D.A.A.D.D.D.D.R.D.D.D.D.R.D.D.R.D.D.D.D.R.R.D.D.D.D.D.D.D.D.A.k.p.o.b",
+".k.o.o.b.A.Q.Q.Q.Q.D.Q.A.D.D.D.D.D.A.D.A.A.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.A.A.A.A.A.A.D.k.p.p.b",
+".k.p.o.b.A.Q.Q.Q.Q.D.D.D.D.A.D.D.A.A.A.A.A.A.D.D.A.D.A.D.A.D.D.D.A.D.A.D.A.D.D.A.D.A.D.A.A.A.A.A.k.o.o.b",
+".k.p.B.b.A.A.Q.Q.D.Q.Q.Q.Y.D.A.A.A.A.A.A.D.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.D.A.A.A.A.A.A.A.A.k.o.o.b",
+".k.B.0.b.A.A.D.Q.D.D.Q.Q.Y.Y.D.Q.Q.A.A.A.A.A.A.D.D.A.D.D.A.A.A.D.D.A.D.D.A.A.D.A.A.D.A.A.A.A.A.A.k.o.o.b",
+".k.o.B.b.A.A.Q.D.D.D.D.D.D.D.D.D.Q.Q.A.A.A.Q.Q.Y.Y.Q.Q.D.D.A.A.A.D.D.D.D.D.A.A.D.D.A.D.D.A.A.A.A.k.p.o.b",
+".k.0.B.b.A.A.Q.Q.Q.Q.D.D.D.D.D.D.D.D.D.D.D.Q.D.Y.Y.Y.Y.Y.D.Y.D.Q.D.D.D.D.D.Q.Q.A.D.A.A.D.A.A.D.D.k.p.o.b",
+".k.C.o.b.A.A.Q.Q.Q.Y.Y.D.Q.Q.Q.Y.Y.Y.D.D.A.Q.D.D.D.Y.Y.Y.Y.Y.D.D.D.Q.Q.Y.Y.Q.Q.A.D.A.A.D.D.D.D.A.k.o.o.b",
+".k.n.B.b.A.D.D.D.Q.Q.D.D.D.Y.Q.D.D.D.D.D.D.A.D.A.D.D.D.D.Y.Y.D.D.D.D.9.9.Y.Y.Q.9.A.A.D.D.D.D.D.D.k.p.o.b",
+".k.n.n.b.A.A.A.D.D.D.Y.D.D.D.D.9.9.9.9.9.D.D.D.D.D.9.9.D.D.D.D.9.9.9#..9#..9.9.A.9.A.D.A.D.D.A.D.k.p.o.b",
+".k.n.0.b.A.A.A.A.D.D.D.D.9.9.D.9.9.9.9.9.9.9.9.9.9.9.9.9.9.9.9#.#.#.#..9.D.D#.#.#..A.D.D.A.D.D.D.k.o.o.b",
+".k.n.p.b.D.D.A.A.D.9.9.9.9#.#..D.D.D.9.9.9.9.9.9#.#..R.9.9.9#.#####..R.D.D.D.D.D.9.9.D.D.D.D.A.D.k.o.o.b",
+".k.n.p.b.D.A.D.D.D.9###.#.#.#####.#..D.A.D.D.9.R.R.R#.#.#.#..D.R.R.A.A.D.D.D.D.D.A.D.D.A.A.A.A.D.k.o.p.b",
+".k.o.o.b.D.D.D.D.D.D.9.D#.#.#.##.R#..Y.9.D.D.D.D.D.D.R#..9#..R.D#..R.R.9.D.A.D.D.D.D.D.D.A.A.D.A.k.o.p.b",
+".k.o.o.b.D.A.D.D.R.D.R.R.9.R.9.9.Q.D.D.R.R.9#a#.#a.A.D.D.D.D.D.D.D.D.A.D.D.D.D.D.D.D.D.D.D.A.A.A.k.n.o.b",
+".k.p.p.b.D.D.D.D.D.D.D#..D.9.9.R###..R###..9.A.9.9.9.D.D.9.9#b#.#.###..9.9.D.D.D.D.D.D.D.D.Q.A.A.k.o.o.b",
+".k.p.o.b.A.A.D.D.D.D.R.R.R.R.9.D.D#.#.#.#..9.9.D.D#..D.D.9.9.9##.9.9.9#..9.D.D.D.D.D.D.D.Y.Q.A.A.k.o.o.b",
+".k.o.o.b.A.A.A.A.D.D.D#.#.#..R##.9.D#.#.#..9.9.Y.D.D.D.D.9.D.D.Y.D.9.9.9.9.D.D.D.D.D.D.D.Y.Q.Q.A.k.p.o.b",
+".k.p.p.b.A.A.A.A.D.A.D.D.D.D.9.9.9.9.9.9.9.9.9.9.9.D.D.D.D.9.D.9.D.D.D#.#..D.9#.#..9.9.D.D.Q.Q.D.k.o.o.b",
+".k.o.o.b.A.A.D.D.A.D.A.Q.D.Y.Y.D.9.9.9.9.9.9.9.9.A.A.9.9.9.9.D.D.D.D.D.D.D.D.D.Y.Q.A.Q.Y.Y.D.Q.A.k.o.o.b",
+".k.o.o.b.z.z.z.A.A.D.D.A.D.D.D.9.9.A.9.9.9.9.9.9.9.9.9.9.9.9.9.9.9.9.9.9#..9.9.9.9.Y.Y.9.D.D.A.D.k.o.o.b",
+".k.n.p.b.A.A.D.D.D.D.D.D.D.9.9.9.9.9.9.9.9.9#.#.#..9.9.9.9.9.9.9.9.9.9.9.9.9.9.9.9.D.9.9.D.D.D.A.k.p.o.b",
+".k.n.o.b.D.D.D.D.D.D.D.D.9.9.9.9.9.9.9.9#..9#.#.#..D.9.9.9.9.9.9.D.D.D.9#.#..9.9.9.D.9.9.A.D.A.D.k.p.o.b",
+".k.n.n.b.z.A.z.A.A.D.A.D.D.9.9.9.9.9.9.9.D.9#.#.#.#..D#.#..9.9.D.9.Q.9.D.D.9.9.R.9.9.Y.9.A.A.A.A.k.n.o.b",
+".k.n.o.b.A.A.A.D.D.D.D.D.D.D.D.9.9#..9.9.9.D###a#a##.D.D#a.R#..D.D.D#a#a#a.R.R.R.9.9.Y.9.A.A.A.A.k.o.o.b",
+".k.n.o.b.D.D.D.D.D.D.D.D.D.D.D.A.9.R.R#..R#a.D.D.D#a#a#a.D###a#a#a#a.D#a.9#.#.#..D.Y.9.Y.D.D.A.D.k.p.o.b",
+".k.n.o.b.A.A.A.D.D.D.D.D.D.D.9.9.D.9#.#.#.#..D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.9#..Y.Y.Y.Y.D.A.D.k.p.p.b",
+".k.p.o.b.D.D.D.D.D.D.Y.Y.Y.D.9.D.D.D.9#.#.#.#.#####b#.#a.D#a#a.D#.#..D###..9.9#.#..9.Y.Y.Y.D.D.D.k.o.o.b",
+".k.o.o.b.D.D.D.D.D.D.D.D.D.D.D.D.D.9.9.9#.#.#.#####a#.#..D.D.D.D#b.9.D#..9.9.9.9.9.Y.D.D.Y.D.A.D.k.p.p.b",
+".k.n.n.b.D.D.D.Q.Y.D.Y.D.D.D.D.D.D.D.D.D#.#.#.#####..D.D#.#a#b###b.D#..D.9.D.9#a.9.D.Y.D.Q.A.A.A.k.n.o.b",
+".k.p.p.b.D.A.D.D.D.D.D.D.D.D.D.D.D.D.D.D.A.D.D.D.D.D.D.D.D.D.D###a.9#a.D#a#a#a.R#..D.Y.D.Q.A.A.A.k.o.n.b",
+".k.o.p.b.D.D.D.D.D.D.D.D.D.D.D.D.D.R.R#..R.D.D.D.D.D.D.D.D.9#a.D.D.D#a.D.D.D.D.D.D.Y.D.A.D.D.A.D.k.p.n.b",
+".k.o.p.b.D.D.A.D.D.D.D.D.D.D.D.D.D.9.9#..D.D.D.D#.#a.D.D.D.D.D.D.D.D.D#..D.D.D.D.D.Y.Q.A.A.D.A.D.k.p.n.b",
+".k.p.o.b.A.D.D.D.D.D.Q.Q.Y.D.D.D.D.D.D.D.9.9.9.9.D.D.D.D.9.9.D.D.9.9#..R.9.D.D.D.D.D.Q.A.A.D.D.D.k.o.n.b",
+".k.o.n.b.D.A.A.D.D.D.D.D.9.9.9#..9.D.D.D.D.D.9.9.9.9.9.9.9.9.9.D.D.9.9.9.9.D.D.D.9.D.D.D.A.D.D.D.k.n.n.b",
+".k.o.o.b.D.D.D.D.D.D.D.R.R.R#a.9.9.D.D.D.D.D.Y.D.9.9.9.9.9.D.D.A.D.D.9.9#..D.D.9.9.9.9.D.D.D.D.D.k.p.n.b",
+".k.o.o.b.D.D.D.D.D.D.D.D.9#..R#.##.9.9.D.D.D.D.D.9.9.9.9.9.D.D.9.A.A.D.D.9.D.9.9.D.D.D.D.D.D.D.R.k.o.o.b",
+".k.o.p.b.D.D.D.D.D.D.D.D.9.9.9.D.D.9.9.9.9.9.D.D.9.9.9.9.9.9#.#..9.9.9.D.D.9.9.9.D.Q.D.A.D.D.A.D.k.o.n.b",
+".k.o.p.b.A.D.D.D.D.D.D.A.D.9.9.D.D.9#..9#.#.#.#a#a.9.9.9.D.D#.#a#a.9.9.D#a.9##.9.9.Q.A.A.A.D.A.D.k.o.o.b",
+".k.n.o.b.A.A.D.D.D.D.D.D.A.9.9.R.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.9#.#a#b#..9.9.D.A.A.A.D.D.D.k.o.o.b",
+".k.n.o.b.D.D.D.D.D.D.D.D.D.9#..R#a.D#a#a#a.D.D.D.D#a#a#.#a#a.D.D#a#a#a#.#a#a#.#..D.D.D.D.A.D.D.D.k.o.p.b",
+".k.n.o.b.R.D.D.D.D.D.D.D.D.D.9#.#..D.R##.D.D#b#.#######..9#.#..9.D.9.9.9.9.9.9.9.D.D.D.D.D.D.D.D.k.o.p.b",
+".k.o.o.b.D.D.D.D.A.A.A.A.D.Q.D.9.9.9#.#.#..D.9.9.9#.#b#.#..9.9.9.D.D.9.9.D.D.D.D.D.D.D.D.D.D.D.R.k.o.n.b",
+".k.o.o.b.D.D.D.D.D.D.D.D.D.D.D.D.R.D.D.D.D.D.9.9.9#a.D.D.D.R.D.D.D.R.D.R.R.R.D.D.D.D.D.D.D.R.D.D.k.p.n.b",
+".k.o.o.b.R.R.R.R.D.D.D.D.D.R.R.R.R.R.R.R.D.D.D.9.R.R.R.R.R.R.D.D.R.R.R.R.R.R.R.D.D.D.D.D.D.R.R.D.k.o.n.b",
+".k.o.o.b.D.R.R.R.D.D.D.D.D.D.D.D.R.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.R.D.D.D.D.D.A.A.k.o.o.b",
+".k.o.n.b.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.o.o.b",
+".k.n.o.o.n.o.o.o.o.o.o.o.o.o.p.p.p.p.p.p.p.n.o.o.o.o.p.p.p.p.n.n.n.p.p.p.p.p.p.o.p.o.o.o.o.o.p.o.o.o.p.b",
+".k.o.o.o.o.o.p.p.p.n.n.p.o.o.o.o.o.o.n.n.n.n.n.o.o.o.o.n.o.o.o.p.p.o.o.o.o.o.o.p.p.o.p.p.o.o.o.n.n.n.p.b",
+".k.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b"
+};
diff --git a/themes-kit/default.wmpbtheme b/themes-kit/default.wmpbtheme
new file mode 100644
index 0000000..db28018
--- /dev/null
+++ b/themes-kit/default.wmpbtheme
@@ -0,0 +1,258 @@
+WMPBtheme
+#
+# sample wmpinboard theme equivalent to the default appearance
+#
+label = 12/0, 39/9
+
+/* XPM */
+static char * bbar_xpm[] = {
+"58 30 23 1",
+" c None",
+". c #000000",
+"+ c #EFD183",
+"@ c #6E3A1C",
+"# c #D2AA5C",
+"$ c #FA0604",
+"% c #7F6602",
+"& c #FF0569",
+"* c #FFFFFF",
+"= c #FADA04",
+"- c #AEFA04",
+"; c #6B9600",
+"> c #F48989",
+", c #C16C6C",
+"' c #894E4E",
+") c #F73D88",
+"! c #87003A",
+"~ c #9E0EDC",
+"{ c #FE821C",
+"] c #62A6EF",
+"^ c #3E6599",
+"/ c #2A4668",
+"( c #F2EE04",
+"..........................................................",
+".+++++++++++++ at +++++++++++++@+++++++++++++ at ++++++++++++++.",
+".+############@+############@+############@+############@.",
+".+####...@$###@+####%.%#####@+##########&#@+#.....#....#@.",
+".+##..*.*.&$##@+####.=.#####@+###.###.#&&#@+#.---.#.;-.#@.",
+".+##.**.**.@##@+###%=.######@+##.#.##.&&##@+#.---.#.--.#@.",
+".+#.***.***.##@+###.=%##..##@+#.###.#&&###@+#.--;.#.--.#@.",
+".+#.***.***.##@+##%=.##.##.#@+#.###.&&..##@+#....##.--.#@.",
+".+#.**.****.##@+##.=%#.####. at +#....&&.##.#@+######.;--.#@.",
+".+##.*****.###@+#%=.##.####. at +#.##&&#.##.#@+#....#.;--.#@.",
+".+##..***..###@+#.=%###.##.#@+#.#&&.#.##.#@+#.-;.##.--.#@.",
+".+##%.....%###@+#..#####..##@+#.&&#.#...##@+#.--;.#.;-.#@.",
+".+#...%#%...##@+#.......##.#@+#&&#########@+#.....##...#@.",
+".+############@+###########. at +############@+############@.",
+".@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.",
+".+++++++++++++ at +++++++++++++@+++++++++++++ at ++++++++++++++.",
+".+############@+####>>>>>>##@+############@+############@.",
+".+#-###......#@+###>,,,,,,'#@+#&#...######@+##########)#@.",
+".+#--###.....#@+###>,,,,,,'#@+#&&###.#####@+#########)!#@.",
+".+#~--###....#@+###>,,,,,,'#@+#.&&###.####@+########)!##@.",
+".+#~~--#.....#@+##>,,,,,,'##@+#.#&&#..####@+########)###@.",
+".+#$~~-...#..#@+##>,,,,,,'##@+#.##&&&.####@+#######)!###@.",
+".+#$$~...###.#@+##>,,,,,,'##@+##.#.#&&####@+#))###))####@.",
+".+#{$...--####@+##]^^,,,,'##@+###...#&&###@+#))))))!####@.",
+".+#{{..~~--###@+#]^^^^^^/###@+###.####&&##@+###)))!#####@.",
+".+#({{$$~~--##@+#]^^^^^^/###@+##.######&&#@+####))!#####@.",
+".+#(({{$$~~--#@+#]^^^^^^/###@+##........&#@+#####!######@.",
+".+############@+##//////####@+############@+############@.",
+".@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.",
+".........................................................."};
+
+/* XPM */
+static char *board[] = {
+/* width height num_colors chars_per_pixel */
+" 52 60 68 2",
+/* colors */
+".. c #d6fe04",
+".# c #565654",
+".a c #4e4e4c",
+".b c #362a14",
+".c c #121214",
+".d c #363634",
+".e c #6be520",
+".f c #020204",
+".g c #60c021",
+".h c #f22e96",
+".i c #aefa04",
+".j c #94d205",
+".k c #9a6e2c",
+".l c #fefefc",
+".m c #d5d5d4",
+".n c #562e1c",
+".o c #6e3a1c",
+".p c #8e421c",
+".q c #0a76ec",
+".r c #f9f14a",
+".s c #d1ca45",
+".t c #f6fe7c",
+".u c #6a6a6c",
+".v c #fada04",
+".w c #d2b705",
+".x c #febe04",
+".y c #d5a005",
+".z c #dec67c",
+".A c #d6ba6c",
+".B c #6a361c",
+".C c #66311c",
+".D c #d2aa5c",
+".E c #fe821c",
+".F c #d5721d",
+".G c #fe5e1c",
+".H c #d5561d",
+".I c #4e2a1c",
+".J c #fe8e8c",
+".K c #d57b7a",
+".L c #fa0604",
+".M c #c60805",
+".N c #bf2b79",
+".O c #ad067e",
+".P c #8a0868",
+".Q c #d6b66c",
+".R c #ca9e4c",
+".S c #8503c6",
+".T c #6d049d",
+".U c #2606c4",
+".V c #24089b",
+".W c #020684",
+".X c #040c54",
+".Y c #d2b264",
+".Z c #0b63ba",
+".0 c #8a421c",
+".1 c #0acaec",
+".2 c #0caac6",
+".3 c #0aea74",
+".4 c #0cc467",
+".5 c #0eda1c",
+".6 c #10b71d",
+".7 c #2e8e0c",
+".8 c #2e7b0e",
+".9 c #ceaa5c",
+"#. c #cea654",
+"## c #caa254",
+"#a c #c79849",
+"#b c #c2a454",
+/* pixels */
+".k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.b",
+".k.n.n.n.o.o.o.o.o.o.o.o.o.o.o.p.p.p.n.n.n.o.o.o.o.o.o.o.o.o.n.n.n.n.o.o.o.o.o.o.n.n.n.n.o.o.o.o.o.o.o.b",
+".k.o.o.o.o.o.o.n.n.o.o.o.n.t.t.t.t.t.o.p.t.t.t.n.o.o.o.t.t.t.o.o.o.o.t.t.t.o.o.n.o.o.o.o.p.p.o.o.p.o.o.b",
+".k.o.o.b.b.b.b.b.b.b.b.n.o.o.n.t.o.o.o.t.o.o.o.t.o.p.n.t.n.p.t.o.o.t.n.o.o.t.p.b.b.b.b.b.b.b.b.b.k.o.o.b",
+".k.o.o.b.z.z.z.z.A.z.z.A.o.o.o.t.p.o.n.t.o.o.o.t.o.o.o.t.B.B.C.t.B.t.C.C.B.t.o.b.D.D.z.z.z.A.A.A.k.n.o.b",
+".k.n.o.b.z.z.z.A.z.A.A.A.o.n.o.t.C.C.o.t.n.n.o.t.o.o.o.t.B.B.B.t.B.t.C.C.C.t.B.b.D.D.z.A.A.D.A.z.k.o.o.b",
+".k.o.o.b.z.A.A.A.A.A.A.A.o.o.I.t.C.C.o.t.o.o.o.t.o.n.n.t.p.p.o.t.p.t.p.n.n.t.p.b.D.D.A.A.A.A.A.z.k.o.o.b",
+".k.o.p.b.A.A.A.A.A.A.A.A.o.o.o.t.n.n.p.p.t.t.t.o.o.o.o.t.t.t.t.n.C.C.t.t.t.C.C.b.D.A.A.A.A.A.A.A.k.p.o.b",
+".k.o.o.b.D.A.A.A.A.A.A.A.n.n.o.o.B.C.C.C.C.I.I.I.o.C.o.p.C.o.o.o.C.C.C.C.C.C.C.b.D.D.D.D.A.A.A.A.k.o.n.b",
+".k.p.p.b.A.A.D.D.A.A.A.A.A.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.A.D.D.D.D.A.A.D.A.k.n.o.b",
+".k.p.o.b.A.Q.Q.Q.Q.Q.A.D.A.D.D.D.D.D.A.A.D.D.D.D.R.D.D.D.D.R.D.D.R.D.D.D.D.R.R.D.D.D.D.D.D.D.D.A.k.p.o.b",
+".k.o.o.b.A.Q.Q.Q.Q.D.Q.A.D.D.D.D.D.A.D.A.A.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.A.A.A.A.A.A.D.k.p.p.b",
+".k.p.o.b.A.Q.Q.Q.Q.D.D.D.D.A.D.D.A.A.A.A.A.A.D.D.A.D.A.D.A.D.D.D.A.D.A.D.A.D.D.A.D.A.D.A.A.A.A.A.k.o.o.b",
+".k.p.B.b.A.A.Q.Q.D.Q.Q.Q.Y.D.A.A.A.A.A.A.D.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.D.A.A.A.A.A.A.A.A.k.o.o.b",
+".k.B.0.b.A.A.D.Q.D.D.Q.Q.Y.Y.D.Q.Q.A.A.A.A.A.A.D.D.A.D.D.A.A.A.D.D.A.D.D.A.A.D.A.A.D.A.A.A.A.A.A.k.o.o.b",
+".k.o.B.b.A.A.Q.D.D.D.D.D.D.D.D.D.Q.Q.A.A.A.Q.Q.Y.Y.Q.Q.D.D.A.A.A.D.D.D.D.D.A.A.D.D.A.D.D.A.A.A.A.k.p.o.b",
+".k.0.B.b.A.A.Q.Q.Q.Q.D.D.D.D.D.D.D.D.D.D.D.Q.D.Y.Y.Y.Y.Y.D.Y.D.Q.D.D.D.D.D.Q.Q.A.D.A.A.D.A.A.D.D.k.p.o.b",
+".k.C.o.b.A.A.Q.Q.Q.Y.Y.D.Q.Q.Q.Y.Y.Y.D.D.A.Q.D.D.D.Y.Y.Y.Y.Y.D.D.D.Q.Q.Y.Y.Q.Q.A.D.A.A.D.D.D.D.A.k.o.o.b",
+".k.n.B.b.A.D.D.D.Q.Q.D.D.D.Y.Q.D.D.D.D.D.D.A.D.A.D.D.D.D.Y.Y.D.D.D.D.9.9.Y.Y.Q.9.A.A.D.D.D.D.D.D.k.p.o.b",
+".k.n.n.b.A.A.A.D.D.D.Y.D.D.D.D.9.9.9.9.9.D.D.D.D.D.9.9.D.D.D.D.9.9.9#..9#..9.9.A.9.A.D.A.D.D.A.D.k.p.o.b",
+".k.n.0.b.A.A.A.A.D.D.D.D.9.9.D.9.9.9.9.9.9.9.9.9.9.9.9.9.9.9.9#.#.#.#..9.D.D#.#.#..A.D.D.A.D.D.D.k.o.o.b",
+".k.n.p.b.D.D.A.A.D.9.9.9.9#.#..D.D.D.9.9.9.9.9.9#.#..R.9.9.9#.#####..R.D.D.D.D.D.9.9.D.D.D.D.A.D.k.o.o.b",
+".k.n.p.b.D.A.D.D.D.9###.#.#.#####.#..D.A.D.D.9.R.R.R#.#.#.#..D.R.R.A.A.D.D.D.D.D.A.D.D.A.A.A.A.D.k.o.p.b",
+".k.o.o.b.D.D.D.D.D.D.9.D#.#.#.##.R#..Y.9.D.D.D.D.D.D.R#..9#..R.D#..R.R.9.D.A.D.D.D.D.D.D.A.A.D.A.k.o.p.b",
+".k.o.o.b.D.A.D.D.R.D.R.R.9.R.9.9.Q.D.D.R.R.9#a#.#a.A.D.D.D.D.D.D.D.D.A.D.D.D.D.D.D.D.D.D.D.A.A.A.k.n.o.b",
+".k.p.p.b.D.D.D.D.D.D.D#..D.9.9.R###..R###..9.A.9.9.9.D.D.9.9#b#.#.###..9.9.D.D.D.D.D.D.D.D.Q.A.A.k.o.o.b",
+".k.p.o.b.A.A.D.D.D.D.R.R.R.R.9.D.D#.#.#.#..9.9.D.D#..D.D.9.9.9##.9.9.9#..9.D.D.D.D.D.D.D.Y.Q.A.A.k.o.o.b",
+".k.o.o.b.A.A.A.A.D.D.D#.#.#..R##.9.D#.#.#..9.9.Y.D.D.D.D.9.D.D.Y.D.9.9.9.9.D.D.D.D.D.D.D.Y.Q.Q.A.k.p.o.b",
+".k.p.p.b.A.A.A.A.D.A.D.D.D.D.9.9.9.9.9.9.9.9.9.9.9.D.D.D.D.9.D.9.D.D.D#.#..D.9#.#..9.9.D.D.Q.Q.D.k.o.o.b",
+".k.o.o.b.A.A.D.D.A.D.A.Q.D.Y.Y.D.9.9.9.9.9.9.9.9.A.A.9.9.9.9.D.D.D.D.D.D.D.D.D.Y.Q.A.Q.Y.Y.D.Q.A.k.o.o.b",
+".k.o.o.b.z.z.z.A.A.D.D.A.D.D.D.9.9.A.9.9.9.9.9.9.9.9.9.9.9.9.9.9.9.9.9.9#..9.9.9.9.Y.Y.9.D.D.A.D.k.o.o.b",
+".k.n.p.b.A.A.D.D.D.D.D.D.D.9.9.9.9.9.9.9.9.9#.#.#..9.9.9.9.9.9.9.9.9.9.9.9.9.9.9.9.D.9.9.D.D.D.A.k.p.o.b",
+".k.n.o.b.D.D.D.D.D.D.D.D.9.9.9.9.9.9.9.9#..9#.#.#..D.9.9.9.9.9.9.D.D.D.9#.#..9.9.9.D.9.9.A.D.A.D.k.p.o.b",
+".k.n.n.b.z.A.z.A.A.D.A.D.D.9.9.9.9.9.9.9.D.9#.#.#.#..D#.#..9.9.D.9.Q.9.D.D.9.9.R.9.9.Y.9.A.A.A.A.k.n.o.b",
+".k.n.o.b.A.A.A.D.D.D.D.D.D.D.D.9.9#..9.9.9.D###a#a##.D.D#a.R#..D.D.D#a#a#a.R.R.R.9.9.Y.9.A.A.A.A.k.o.o.b",
+".k.n.o.b.D.D.D.D.D.D.D.D.D.D.D.A.9.R.R#..R#a.D.D.D#a#a#a.D###a#a#a#a.D#a.9#.#.#..D.Y.9.Y.D.D.A.D.k.p.o.b",
+".k.n.o.b.A.A.A.D.D.D.D.D.D.D.9.9.D.9#.#.#.#..D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.9#..Y.Y.Y.Y.D.A.D.k.p.p.b",
+".k.p.o.b.D.D.D.D.D.D.Y.Y.Y.D.9.D.D.D.9#.#.#.#.#####b#.#a.D#a#a.D#.#..D###..9.9#.#..9.Y.Y.Y.D.D.D.k.o.o.b",
+".k.o.o.b.D.D.D.D.D.D.D.D.D.D.D.D.D.9.9.9#.#.#.#####a#.#..D.D.D.D#b.9.D#..9.9.9.9.9.Y.D.D.Y.D.A.D.k.p.p.b",
+".k.n.n.b.D.D.D.Q.Y.D.Y.D.D.D.D.D.D.D.D.D#.#.#.#####..D.D#.#a#b###b.D#..D.9.D.9#a.9.D.Y.D.Q.A.A.A.k.n.o.b",
+".k.p.p.b.D.A.D.D.D.D.D.D.D.D.D.D.D.D.D.D.A.D.D.D.D.D.D.D.D.D.D###a.9#a.D#a#a#a.R#..D.Y.D.Q.A.A.A.k.o.n.b",
+".k.o.p.b.D.D.D.D.D.D.D.D.D.D.D.D.D.R.R#..R.D.D.D.D.D.D.D.D.9#a.D.D.D#a.D.D.D.D.D.D.Y.D.A.D.D.A.D.k.p.n.b",
+".k.o.p.b.D.D.A.D.D.D.D.D.D.D.D.D.D.9.9#..D.D.D.D#.#a.D.D.D.D.D.D.D.D.D#..D.D.D.D.D.Y.Q.A.A.D.A.D.k.p.n.b",
+".k.p.o.b.A.D.D.D.D.D.Q.Q.Y.D.D.D.D.D.D.D.9.9.9.9.D.D.D.D.9.9.D.D.9.9#..R.9.D.D.D.D.D.Q.A.A.D.D.D.k.o.n.b",
+".k.o.n.b.D.A.A.D.D.D.D.D.9.9.9#..9.D.D.D.D.D.9.9.9.9.9.9.9.9.9.D.D.9.9.9.9.D.D.D.9.D.D.D.A.D.D.D.k.n.n.b",
+".k.o.o.b.D.D.D.D.D.D.D.R.R.R#a.9.9.D.D.D.D.D.Y.D.9.9.9.9.9.D.D.A.D.D.9.9#..D.D.9.9.9.9.D.D.D.D.D.k.p.n.b",
+".k.o.o.b.D.D.D.D.D.D.D.D.9#..R#.##.9.9.D.D.D.D.D.9.9.9.9.9.D.D.9.A.A.D.D.9.D.9.9.D.D.D.D.D.D.D.R.k.o.o.b",
+".k.o.p.b.D.D.D.D.D.D.D.D.9.9.9.D.D.9.9.9.9.9.D.D.9.9.9.9.9.9#.#..9.9.9.D.D.9.9.9.D.Q.D.A.D.D.A.D.k.o.n.b",
+".k.o.p.b.A.D.D.D.D.D.D.A.D.9.9.D.D.9#..9#.#.#.#a#a.9.9.9.D.D#.#a#a.9.9.D#a.9##.9.9.Q.A.A.A.D.A.D.k.o.o.b",
+".k.n.o.b.A.A.D.D.D.D.D.D.A.9.9.R.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.9#.#a#b#..9.9.D.A.A.A.D.D.D.k.o.o.b",
+".k.n.o.b.D.D.D.D.D.D.D.D.D.9#..R#a.D#a#a#a.D.D.D.D#a#a#.#a#a.D.D#a#a#a#.#a#a#.#..D.D.D.D.A.D.D.D.k.o.p.b",
+".k.n.o.b.R.D.D.D.D.D.D.D.D.D.9#.#..D.R##.D.D#b#.#######..9#.#..9.D.9.9.9.9.9.9.9.D.D.D.D.D.D.D.D.k.o.p.b",
+".k.o.o.b.D.D.D.D.A.A.A.A.D.Q.D.9.9.9#.#.#..D.9.9.9#.#b#.#..9.9.9.D.D.9.9.D.D.D.D.D.D.D.D.D.D.D.R.k.o.n.b",
+".k.o.o.b.D.D.D.D.D.D.D.D.D.D.D.D.R.D.D.D.D.D.9.9.9#a.D.D.D.R.D.D.D.R.D.R.R.R.D.D.D.D.D.D.D.R.D.D.k.p.n.b",
+".k.o.o.b.R.R.R.R.D.D.D.D.D.R.R.R.R.R.R.R.D.D.D.9.R.R.R.R.R.R.D.D.R.R.R.R.R.R.R.D.D.D.D.D.D.R.R.D.k.o.n.b",
+".k.o.o.b.D.R.R.R.D.D.D.D.D.D.D.D.R.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.R.D.D.D.D.D.A.A.k.o.o.b",
+".k.o.n.b.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.k.o.o.b",
+".k.n.o.o.n.o.o.o.o.o.o.o.o.o.p.p.p.p.p.p.p.n.o.o.o.o.p.p.p.p.n.n.n.p.p.p.p.p.p.o.p.o.o.o.o.o.p.o.o.o.p.b",
+".k.o.o.o.o.o.p.p.p.n.n.p.o.o.o.o.o.o.n.n.n.n.n.o.o.o.o.n.o.o.o.p.p.o.o.o.o.o.o.p.p.o.p.p.o.o.o.n.n.n.p.b",
+".k.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b"
+};
+
+/* XPM */
+static char * abar_xpm[] = {
+"58 28 11 1",
+" c None",
+". c #000000",
+"+ c #EFD183",
+"@ c #D2AA5C",
+"# c #6E3A1C",
+"$ c #878787",
+"% c #6B9600",
+"& c #AEFA04",
+"* c #FEFEFC",
+"= c #87003A",
+"- c #F73D88",
+"..........................................................",
+".++++++++++++++++++++++++++++++++++++++++++++++++++++++++.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@..@@@@@@@@@@....@@@#.",
+".+@@@@@@@@@@@@@@+$@@@@@@@@@@@@@@@@@@@....@@@@@@@.%%%%.@@#.",
+".+@@@@@@@@@@@@@@..@@@@@@@@@@@@@@@@$........$@@@.%&&%%%.@#.",
+".+@@@@@@@@@@@@@@$#@@@@@@@@@@@@@@@$.@@....@@.$@@.%&&&%%.@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.@@@..@@@@@.@@.%%&%%%.@#.",
+".+@@@@@@@@@@@@@@+$@@@@@@@@@@@@@@@.@@@@@@@@@@.@@.%%%%%%.@#.",
+".+@@@@@@@@@@@@@@..@@@@@@@@@@@@@@@$.@@@@@@@@.$@@@.%%%%.@@#.",
+".+@@@@@@@@@@@@@@$#@@@@@@@@@@@@@@@@$........$@@@@@....@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@...........@@@@@@@@@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@...........@@@@@@@@@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.$$$$$$$$..@@@@....@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.***==***..@@@.====.@@#.",
+".+@@@@@@@@@@@@@@+$@@@@@@@@@@@@@@@@.**===***..@@.=--===.@#.",
+".+@@@@@@@@@@@@@@..@@@@@@@@@@@@@@@@.***==***..@@.=---==.@#.",
+".+@@@@@@@@@@@@@@$#@@@@@@@@@@@@@@@@.***==***..@@.==-===.@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.***==***..@@.======.@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.********...@@@.====.@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@...........@@@@@....@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@...........@@@@@@@@@@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#.",
+".########################################################.",
+".........................................................."};
+
+/* XPM */
+static char * digits_xpm[] = {
+"60 9 3 1",
+" c None",
+". c #D2AA5C",
+"+ c #000000",
+".+++.........+++...+++.........+++...+++...+++...+++...+++..",
+"+...+.....+.....+.....+.+...+.+.....+.........+.+...+.+...+.",
+"+...+.....+.....+.....+.+...+.+.....+.........+.+...+.+...+.",
+"+...+.....+.....+.....+.+...+.+.....+.........+.+...+.+...+.",
+".............+++...+++...+++...+++...+++.........+++...+++..",
+"+...+.....+.+.........+.....+.....+.+...+.....+.+...+.....+.",
+"+...+.....+.+.........+.....+.....+.+...+.....+.+...+.....+.",
+"+...+.....+.+.........+.....+.....+.+...+.....+.+...+.....+.",
+".+++.........+++...+++.........+++...+++.........+++...+++.."};
+
diff --git a/themes-kit/digits.xpm b/themes-kit/digits.xpm
new file mode 100644
index 0000000..617f4b1
--- /dev/null
+++ b/themes-kit/digits.xpm
@@ -0,0 +1,15 @@
+/* XPM */
+static char * digits_xpm[] = {
+"60 9 3 1",
+" c None",
+". c #D2AA5C",
+"+ c #000000",
+".+++.........+++...+++.........+++...+++...+++...+++...+++..",
+"+...+.....+.....+.....+.+...+.+.....+.........+.+...+.+...+.",
+"+...+.....+.....+.....+.+...+.+.....+.........+.+...+.+...+.",
+"+...+.....+.....+.....+.+...+.+.....+.........+.+...+.+...+.",
+".............+++...+++...+++...+++...+++.........+++...+++..",
+"+...+.....+.+.........+.....+.....+.+...+.....+.+...+.....+.",
+"+...+.....+.+.........+.....+.....+.+...+.....+.+...+.....+.",
+"+...+.....+.+.........+.....+.....+.+...+.....+.+...+.....+.",
+".+++.........+++...+++.........+++...+++.........+++...+++.."};
diff --git a/themes-kit/sample.wmpbtheme b/themes-kit/sample.wmpbtheme
new file mode 100644
index 0000000..42b0914
--- /dev/null
+++ b/themes-kit/sample.wmpbtheme
@@ -0,0 +1,130 @@
+WMPBtheme
+#
+# sample wmpinboard theme
+#
+# compared to the default theme, it just moves the "TO DO" label to the bottom
+# of the board
+#
+label = 11/50, 39/59
+
+/* XPM */
+static char *board_alt[] = {
+/* width height num_colors chars_per_pixel */
+" 52 60 54 1",
+/* colors */
+". c #362a14",
+"# c #6e3a1c",
+"a c #8a421c",
+"b c #9a6e2c",
+"c c #c69a4c",
+"d c #f6fe7c",
+"e c #d6ba6c",
+"f c #562e1c",
+"g c #d2b264",
+"h c #ca9e4c",
+"i c #c2a654",
+"j c #66321c",
+"k c #d6aa54",
+"l c #d2aa54",
+"m c #dec67c",
+"n c #deb264",
+"o c #4e2a1c",
+"p c #dab25c",
+"q c #cea24c",
+"r c #d2ae5c",
+"s c #d6b664",
+"t c #d6a654",
+"u c #cea654",
+"v c #dabe6c",
+"w c #d6ae54",
+"x c #d2aa5c",
+"y c #d2a24c",
+"z c #6a361c",
+"A c #dab264",
+"B c #d2a64c",
+"C c #d6b25c",
+"D c #daae5c",
+"E c #d6b66c",
+"F c #ca9a4c",
+"G c #d6aa5c",
+"H c #ce9e4c",
+"I c #deb664",
+"J c #caa254",
+"K c #d6ae5c",
+"L c #d2ae64",
+"M c #dab664",
+"N c #e2c274",
+"O c #ceaa54",
+"P c #d2a654",
+"Q c #8e421c",
+"R c #debe74",
+"S c #ceae5c",
+"T c #d6b264",
+"U c #daae64",
+"V c #dab66c",
+"W c #ceaa5c",
+"X c #daba6c",
+"Y c #ce9a4c",
+"Z c #d29e4c",
+/* pixels */
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.",
+"bfff###########QQQfff#########ffff######ffff#######.",
+"b######ff###f######Q###f###############f####QQ##Q##.",
+"b##.............................................b##.",
+"b##.mmEEExxxxhhWhWWExxhhWcucexxxxxxxxexxxexeeeeebf#.",
+"bf#.mmEExEEEgxuxWWhJuhJuWeWWWxxWWiuuJuWWeeeeexemb##.",
+"b##.mexExxEEghhhhWxxuuuuWWxxuxxWWWJWWWuWexeeeeemb##.",
+"b#Q.eeExxxxxxxuuuhJWxuuuxWTxxxxWxxgxWWWuWggreeeebQ#.",
+"b##.xeEEEExxxxxxxxxxxEKgsssgxgxExxxxxLWWgxxggeeeb#f.",
+"bQQ.eeEEgOWggEEgggxxeXGpAMssggxxxEEgrWcWxgxEeexebf#.",
+"bQ#.eExxWWgxxgxxxxGKxvUNnnUKggxxxxWWhFhuxgxEeExebQ#.",
+"b##.eETucxxxxxxrlxuqGXpnInpKKhuWuhxuuxxxgxexxeexbQQ.",
+"bQ#.eEJFxxxgxEXXBTTRnIktpHGcexxxxxxxxxKGsXXexeeeb##.",
+"bQz.eePxxxgxeGrvAMtlwtkDCRWxWxxWWiuuuxxKURvvxreeb##.",
+"bza.eexxxxxxxxxXyCnDkwwlrrxxuxxWWWJWxxKKAnUvxseeb##.",
+"b#z.eexxxxxEeexrwyurKPllWWgxxxxWxxgxWLrTpspKTeeebQ#.",
+"baz.eeLxxWxxxexxKKKxKExgggggxgxExxxxxETADAAKeexxbQ#.",
+"bj#.eeVLWWWWxxKTggxxeExxxgggggxxxEEggVevUvvxxxxeb##.",
+"bfz.exxxLrxxxLExxxxxxexexGxxggxxxxWWggESeexxxxxxbQ#.",
+"bff.eeexxxgxxxxWWWWWxxxxxSxxxxxWWWuWuWWeWexexxexbQ#.",
+"bfa.eeeexxxxWWxWWWWWWWWrLrKLxWWuuuuWxxuuuexxexxxb##.",
+"bfQ.xxevxWWWWuuxxxWWWWrWkDPKSxuJJuhxxxxxWWxxxxexb##.",
+"bfQ.xvxKKxJOuPJJuuxexxrqytpKxOxhheexxxxxexxeeeexb#Q.",
+"b##.GxKUAKLGxxxuHugWxxxGTpykSPhxuhhWxexxxxxxeexeb#Q.",
+"b##.GvAntnqqCBKLEGxhhWcPFNKGxKxxxxexxxxxxxxxxeeebf#.",
+"bQQ.LKDppDCGAATqquhJuWeWSWxKWWiuuJuWWxxxxxxxxEeeb##.",
+"bQ#.XvUpAUqytkTKxPuuuWWxxPxxWWWJWWWuWxxxxxxxgEeeb##.",
+"b##.eeevKGGllkquxxuuuWWgxxxxWxxgxWWWWxxxxxxxgEEebQ#.",
+"bQQ.eeXexvWGxGLSWWWWWWWWWxxxxWxWxxxuuxWuuWWxxEExb##.",
+"b##.eexxexeErsgxWWWWWWWWeeWWWWxxxxxxxxxgEeEggxEeb##.",
+"b##.mmmeexxexxxWWeWWWWWWWWWWWWWWWWWWuWWWWggWxxexb##.",
+"bfQ.eexxxxxxxWWWWWWWWWuuuWWWWWWWWWWWxxxxWxWWxxxebQ#.",
+"bf#.xxxxxxxxWWWWWWWWuWuuuxxSxSWWxxxSOxLrrxWWexexbQ#.",
+"bff.memeexexxWWWWWWWxWuuuuKDKATKxVWxGCCPxrgWeeeebf#.",
+"bf#.eeexxxxxxxxWWuWWWxJFFuUIxxxxxxFFFPtPrrgWeeeeb##.",
+"bf#.xxxxxxxxxxxeWhhuhcxxxHqxxGyyxxDYKDDwUgxgxxexbQ#.",
+"bf#.eeexxxxxxxWWxWuuuuxxxDnnlGxxxnACKCDrlsgggxexbQQ.",
+"bQ#.xxxxxxgggxWxxxWuuuuJJikyIZyIDpnPlxrPuWgggxxxb##.",
+"b##.xxxxxxxxxxxxxWWWuuuJqFkDIxxGxxxxrWSxWgxxgxexbQQ.",
+"bff.xxxEgxgxxxxxxxxxuuxxxxxxxxxrSIDUrrWcWxgxEeeebf#.",
+"bQQ.xexxxxxxxxxxxxxxexxxxxxxGKDPHKHGFFcFuxgxEeeeb#f.",
+"b#Q.xxxxxxxxxxxxxhhuhxxxxxxxxWcrGKFLxPxxxgxexxexbQf.",
+"b#Q.xxexxxxxxxxxxWWuxxxxucxxxxxxxxGPxxxxxgEeexexbQf.",
+"bQ#.exxxxxEEgxxxxxxxWWWWxxxxWWxxWWuhWxxxxxEeexxxb#f.",
+"b#f.xeexxxxxWWWuWxxxxxWWWWWWWWWxxWWWWxxxWxxxexxxbff.",
+"b##.xxxxxxxhhhFWWxxxxxgxWWWWWxxexxWWuxxWWWWxxxxxbQf.",
+"b##.xxxxxxxxWuhuJWWxxxxxWWWWWxxWeexxWxWWxxxxxxxhb##.",
+"b#Q.xxxxxxxxWWWxxWWWWWxxWWWWWWuuWWWxxWWWxExexxexb#f.",
+"b#Q.exxxxxxexWWxxWuWuuuFcWWWxxuccWWxcWJWWEeeexexb##.",
+"bf#.eexxxxxxeWWhxxxxxxxxxxxxxxxPxxWuciuWWxeeexxxb##.",
+"bf#.xxxxxxxxbbbbbbbbbbbbbbbbbbbbbbbbbbbuxxxxexxxb#Q.",
+"bf#.hxxxxxxb###QQQfff#########ffff#####.xxxxxxxxb#Q.",
+"b##.xxxxeeebfddddd#Qdddf###ddd####ddd##.xxxxxxxhb#f.",
+"b##.xxxxxxxb##fd###d###d#QfdfQd##df##dQ.xxxxxhxxbQf.",
+"b##.hhhhxxxb###dQ#fd###d###dzzjdzdjjzd#.xxxxxhhxb#f.",
+"b##.xhhhxxxb#f#djj#dff#d###dzzzdzdjjjdz.hxxxxxeeb##.",
+"b#f.bbbbbbbb##odjjzd###d#ffdQQ#dQdQffdQ.bbbbbbbbb##.",
+"bf##f##########dffQQddd####ddddfjjdddjj#Q#####Q###Q.",
+"b#####QQQffQff##zjjjjooo#j#Qjz##jjjjjjj#Q#QQ###fffQ.",
+"b..................................................."
+};
diff --git a/wmpb-convert.pl b/wmpb-convert.pl
new file mode 100644
index 0000000..cfe6b9e
--- /dev/null
+++ b/wmpb-convert.pl
@@ -0,0 +1,39 @@
+#!/usr/bin/perl -w
+
+#
+# wmpinboard pre-0.7 to v0.7 data file converter
+#
+
+require 5.003;
+
+my($ifn, $ofn, $c) = ('.wmpinboardrc', '.wmpinboarddata', 0);
+
+die "This script should not be executed while an instance of wmpinboard is " .
+ "running.\n" if `ps x` =~ /\d wmpinboard/;
+
+if (open IN, "$ENV{HOME}/$ifn" and open OUT, ">$ENV{HOME}/$ofn") {
+ binmode OUT;
+ print OUT "WMPB0-*-fixed-*--10-*-iso8859-1\n";
+ while (<IN>) {
+ chomp;
+ if (@_ = /^(\d+) (\d+) (\d+) (.*)/) {
+ if (length $4 <= 39) {
+ $_[3] =~ s/.{8}/$&\n/gs;
+ $_[3] = join ' ', split /\n/, $_[3];
+ }
+ $_[3] .= ' ' x (59 - length $_[3]);
+ print OUT pack 'i3a60', @_;
+ $c++
+ } else {
+ die "Invalid data in `~/.$ifn'.\nAborting" if $_
+ }
+ }
+ close IN;
+ close OUT;
+ print "Conversion of $c note@{[$c != 1 ? 's' : '']} complete.\n";
+ unlink "$ENV{HOME}/$ifn"
+} else {
+ die "Couldn't open both `~/$ifn' and `~/$ofn'.\n" .
+ "Sure you've run this script as the right user?\n"
+}
+
diff --git a/wmpinboard.lsm b/wmpinboard.lsm
new file mode 100644
index 0000000..ef3db24
--- /dev/null
+++ b/wmpinboard.lsm
@@ -0,0 +1,11 @@
+Begin3
+Title: wmpinboard
+Version: 1.0
+Entered-date: 13APR00
+Description: Window Maker dock applet resembling a miniature pinboard
+Keywords: WindowMaker WM X11 pinboard notes memo alarm
+Author: gomar at mindless.com (Marco Goetze)
+Primary-site: http://www.tu-ilmenau.de/~gomar/stuff/wmpinboard/
+Copying-policy: GPL
+End
+
diff --git a/wmpinboard.spec.in b/wmpinboard.spec.in
new file mode 100644
index 0000000..1c27f90
--- /dev/null
+++ b/wmpinboard.spec.in
@@ -0,0 +1,48 @@
+%define name @PACKAGE@
+%define version @VERSION@
+%define release 1
+%define prefix /usr/local
+
+Summary: Window Maker dock applet resembling a miniature pinboard
+Name: %{name}
+Version: %{version}
+Release: %{release}
+Copyright: GPL
+Packager: Marco Goetze <gomar at mindless.com>
+Url: http://www.tu-ilmenau.de/~gomar/stuff/%{name}/#download
+Group: X11/Utilities
+Source: %{name}-%{version}.tar.gz
+Buildroot: /home/gomar/Projects/BUILD/%{name}-%{version}-%{release}-root
+Requires: xpm
+
+%description
+wmpinboard is a Window Maker dock applet resembling a miniature
+pinboard, intended to somewhat relieve heavily littered desktops by
+allowing you to place reminders on a graphical on-screen pinboard
+rather than producing a mess of real notes all around your keyboard.
+Features include support for arbitrary 6x10 X fonts, XLocale support,
+drawing capabilities, alarms, animations, themeability, and command
+line interoperability.
+
+%prep
+%setup
+
+%build
+CFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=%{prefix}
+if echo "$RPM_OPT_FLAGS" | grep -- -DBUILD_STATIC >/dev/null 2>&1; then
+ make static
+else
+ make
+fi
+
+%install
+make prefix=$RPM_BUILD_ROOT%{prefix} install-strip
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files
+%defattr(644,root,root)
+%doc AUTHORS COPYING CREDITS ChangeLog NEWS README TODO %{name}.lsm
+%attr(755,root,root) %{prefix}/bin/%{name}
+%attr(755,root,root) %{prefix}/man/man1/%{name}.1
diff --git a/xpm/abar.xpm b/xpm/abar.xpm
new file mode 100644
index 0000000..fcc2366
--- /dev/null
+++ b/xpm/abar.xpm
@@ -0,0 +1,42 @@
+/* XPM */
+static char * abar_xpm[] = {
+"58 28 11 1",
+" c None",
+". c #000000",
+"+ c #EFD183",
+"@ c #D2AA5C",
+"# c #6E3A1C",
+"$ c #878787",
+"% c #6B9600",
+"& c #AEFA04",
+"* c #FEFEFC",
+"= c #87003A",
+"- c #F73D88",
+"..........................................................",
+".++++++++++++++++++++++++++++++++++++++++++++++++++++++++.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@..@@@@@@@@@@....@@@#.",
+".+@@@@@@@@@@@@@@+$@@@@@@@@@@@@@@@@@@@....@@@@@@@.%%%%.@@#.",
+".+@@@@@@@@@@@@@@..@@@@@@@@@@@@@@@@$........$@@@.%&&%%%.@#.",
+".+@@@@@@@@@@@@@@$#@@@@@@@@@@@@@@@$.@@....@@.$@@.%&&&%%.@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.@@@..@@@@@.@@.%%&%%%.@#.",
+".+@@@@@@@@@@@@@@+$@@@@@@@@@@@@@@@.@@@@@@@@@@.@@.%%%%%%.@#.",
+".+@@@@@@@@@@@@@@..@@@@@@@@@@@@@@@$.@@@@@@@@.$@@@.%%%%.@@#.",
+".+@@@@@@@@@@@@@@$#@@@@@@@@@@@@@@@@$........$@@@@@....@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@...........@@@@@@@@@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@...........@@@@@@@@@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.$$$$$$$$..@@@@....@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.***==***..@@@.====.@@#.",
+".+@@@@@@@@@@@@@@+$@@@@@@@@@@@@@@@@.**===***..@@.=--===.@#.",
+".+@@@@@@@@@@@@@@..@@@@@@@@@@@@@@@@.***==***..@@.=---==.@#.",
+".+@@@@@@@@@@@@@@$#@@@@@@@@@@@@@@@@.***==***..@@.==-===.@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.***==***..@@.======.@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.********...@@@.====.@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@...........@@@@@....@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@...........@@@@@@@@@@@@#.",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#.",
+".########################################################.",
+".........................................................."};
diff --git a/xpm/bbar.xpm b/xpm/bbar.xpm
new file mode 100644
index 0000000..7c7aea7
--- /dev/null
+++ b/xpm/bbar.xpm
@@ -0,0 +1,56 @@
+/* XPM */
+static char * bbar_xpm[] = {
+"58 30 23 1",
+" c None",
+". c #000000",
+"+ c #EFD183",
+"@ c #6E3A1C",
+"# c #D2AA5C",
+"$ c #FA0604",
+"% c #7F6602",
+"& c #FF0569",
+"* c #FFFFFF",
+"= c #FADA04",
+"- c #AEFA04",
+"; c #6B9600",
+"> c #F48989",
+", c #C16C6C",
+"' c #894E4E",
+") c #F73D88",
+"! c #87003A",
+"~ c #9E0EDC",
+"{ c #FE821C",
+"] c #62A6EF",
+"^ c #3E6599",
+"/ c #2A4668",
+"( c #F2EE04",
+"..........................................................",
+".+++++++++++++ at +++++++++++++@+++++++++++++ at ++++++++++++++.",
+".+############@+############@+############@+############@.",
+".+####...@$###@+####%.%#####@+##########&#@+#.....#....#@.",
+".+##..*.*.&$##@+####.=.#####@+###.###.#&&#@+#.---.#.;-.#@.",
+".+##.**.**.@##@+###%=.######@+##.#.##.&&##@+#.---.#.--.#@.",
+".+#.***.***.##@+###.=%##..##@+#.###.#&&###@+#.--;.#.--.#@.",
+".+#.***.***.##@+##%=.##.##.#@+#.###.&&..##@+#....##.--.#@.",
+".+#.**.****.##@+##.=%#.####. at +#....&&.##.#@+######.;--.#@.",
+".+##.*****.###@+#%=.##.####. at +#.##&&#.##.#@+#....#.;--.#@.",
+".+##..***..###@+#.=%###.##.#@+#.#&&.#.##.#@+#.-;.##.--.#@.",
+".+##%.....%###@+#..#####..##@+#.&&#.#...##@+#.--;.#.;-.#@.",
+".+#...%#%...##@+#.......##.#@+#&&#########@+#.....##...#@.",
+".+############@+###########. at +############@+############@.",
+".@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.",
+".+++++++++++++ at +++++++++++++@+++++++++++++ at ++++++++++++++.",
+".+############@+####>>>>>>##@+############@+############@.",
+".+#-###......#@+###>,,,,,,'#@+#&#...######@+##########)#@.",
+".+#--###.....#@+###>,,,,,,'#@+#&&###.#####@+#########)!#@.",
+".+#~--###....#@+###>,,,,,,'#@+#.&&###.####@+########)!##@.",
+".+#~~--#.....#@+##>,,,,,,'##@+#.#&&#..####@+########)###@.",
+".+#$~~-...#..#@+##>,,,,,,'##@+#.##&&&.####@+#######)!###@.",
+".+#$$~...###.#@+##>,,,,,,'##@+##.#.#&&####@+#))###))####@.",
+".+#{$...--####@+##]^^,,,,'##@+###...#&&###@+#))))))!####@.",
+".+#{{..~~--###@+#]^^^^^^/###@+###.####&&##@+###)))!#####@.",
+".+#({{$$~~--##@+#]^^^^^^/###@+##.######&&#@+####))!#####@.",
+".+#(({{$$~~--#@+#]^^^^^^/###@+##........&#@+#####!######@.",
+".+############@+##//////####@+############@+############@.",
+".@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.",
+".........................................................."};
diff --git a/xpm/digits.xpm b/xpm/digits.xpm
new file mode 100644
index 0000000..617f4b1
--- /dev/null
+++ b/xpm/digits.xpm
@@ -0,0 +1,15 @@
+/* XPM */
+static char * digits_xpm[] = {
+"60 9 3 1",
+" c None",
+". c #D2AA5C",
+"+ c #000000",
+".+++.........+++...+++.........+++...+++...+++...+++...+++..",
+"+...+.....+.....+.....+.+...+.+.....+.........+.+...+.+...+.",
+"+...+.....+.....+.....+.+...+.+.....+.........+.+...+.+...+.",
+"+...+.....+.....+.....+.+...+.+.....+.........+.+...+.+...+.",
+".............+++...+++...+++...+++...+++.........+++...+++..",
+"+...+.....+.+.........+.....+.....+.+...+.....+.+...+.....+.",
+"+...+.....+.+.........+.....+.....+.+...+.....+.+...+.....+.",
+"+...+.....+.+.........+.....+.....+.+...+.....+.+...+.....+.",
+".+++.........+++...+++.........+++...+++.........+++...+++.."};
diff --git a/xpm/pinboard.xpm b/xpm/pinboard.xpm
new file mode 100644
index 0000000..98f18e7
--- /dev/null
+++ b/xpm/pinboard.xpm
@@ -0,0 +1,136 @@
+/* XPM */
+static char * pinboard_xpm[] = {
+"86 64 69 1",
+" c None",
+". c #D6FE04",
+"+ c #565654",
+"@ c #4E4E4C",
+"# c #362A14",
+"$ c #121214",
+"% c #363634",
+"& c #6BE520",
+"* c #020204",
+"= c #60C021",
+"- c #F22E96",
+"; c #AEFA04",
+"> c #94D205",
+", c #9A6E2C",
+"' c #FEFEFC",
+") c #D5D5D4",
+"! c #562E1C",
+"~ c #6E3A1C",
+"{ c #8E421C",
+"] c #0A76EC",
+"^ c #F9F14A",
+"/ c #D1CA45",
+"( c #F6FE7C",
+"_ c #6A6A6C",
+": c #FADA04",
+"< c #D2B705",
+"[ c #FEBE04",
+"} c #D5A005",
+"| c #DEC67C",
+"1 c #D6BA6C",
+"2 c #6A361C",
+"3 c #66311C",
+"4 c #D2AA5C",
+"5 c #FE821C",
+"6 c #D5721D",
+"7 c #FE5E1C",
+"8 c #D5561D",
+"9 c #4E2A1C",
+"0 c #FE8E8C",
+"a c #D57B7A",
+"b c #FA0604",
+"c c #C60805",
+"d c #BF2B79",
+"e c #AD067E",
+"f c #8A0868",
+"g c #D6B66C",
+"h c #CA9E4C",
+"i c #8503C6",
+"j c #6D049D",
+"k c #2606C4",
+"l c #24089B",
+"m c #020684",
+"n c #040C54",
+"o c #D2B264",
+"p c #0B63BA",
+"q c #8A421C",
+"r c #0ACAEC",
+"s c #0CAAC6",
+"t c #0AEA74",
+"u c #0CC467",
+"v c #0EDA1C",
+"w c #10B71D",
+"x c #2E8E0C",
+"y c #2E7B0E",
+"z c #CEAA5C",
+"A c #CEA654",
+"B c #CAA254",
+"C c #C79849",
+"D c #C2A454",
+".......................................................................+@#$%#+..&*=...",
+"..................................................................+%*#++-----+..;*>...",
+"......,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#......$++----------%..'*)...",
+"......,!!!~~~~~~~~~~~{{{!!!~~~~~~~~~!!!!~~~~~~!!!!~~~~~~~#......%---]]]]-----*..^*/...",
+"......,~~~~~~!!~~~!(((((~{(((!~~~(((~~~~(((~~!~~~~{{~~{~~#......$-----*_]]]--#..:*<...",
+"......,~~########!~~!(~~~(~~~(~{!(!{(~~(!~~({#########,~~#......$-----_*]]---+..[*}...",
+"......,~~#||||1||1~~~({~!(~~~(~~~(223(2(332(~#44|||111,!~#...... at ----]]------++.5*6...",
+"......,!~#|||1|111~!~(33~(!!~(~~~(222(2(333(2#44|1141|,~~#......*-----]]]]---- at .7'8...",
+"......,~~#|1111111~~9(33~(~~~(~!!({{~({({!!({#4411111|,~~#......++-----]]]----#.0*a...",
+"......,~{#11111111~~~(!!{{(((~~~~((((!33(((33#41111111,{~#.......#--]]]]------$.b'c...",
+"......,~~#41111111!!~~23333999~3~{3~~~3333333#44441111,~!#.......*---]]]]]]---%.-'d...",
+"......,{{#114411111##########################144441141,!~#.......%--------]---_+e'f...",
+"......,{~#1ggggg14144444114444h4444h44h4444hh444444441,{~#.......+------------ at _i'j...",
+"......,~~#1gggg4g1444441411444444444444444444441111114,{{#.......++-----++#*%++.k'l...",
+"......,{~#1gggg444414411111144141414441414144141411111,~~#........#++$$@+.......m'n...",
+"......,{2#11gg4gggo41111114111111111111111111411111111,~~#........_+............]'p...",
+"......,2q#114g44ggoo4gg1111114414411144144114114111111,~~#......................r*s...",
+"......,~2#11g444444444gg111ggoogg441114444411441441111,{~#.......**************.t*u...",
+"......,q2#11gggg44444444444g4ooooo4o4g44444gg141141144,{~#.......*------------*.v*w...",
+"......,3~#11gggoo4gggooo441g444ooooo444ggoogg141144441,~~#.......*------------*.x'y...",
+"......,!2#1444gg444og4444441414444oo4444zzoogz11444444,{~#.......*--]]]*_-----*.......",
+"......,!!#111444o4444zzzzz44444zz4444zzzAzAzz1z1414414,{~#.......*-----_*]]]--*.......",
+"......,!q#11114444zz4zzzzzzzzzzzzzzzzAAAAz44AAA1441444,~~#.......*-----]]]]---*.......",
+"......,!{#44114zzzzAA444zzzzzzAAhzzzABBAh44444zz444414,~~#.......*---]]]------*.......",
+"......,!{#41444zBAAABBAA4144zhhhAAAA4hh114444414411114,~{#.......*-----]]]]---*.......",
+"......,~~#444444z4AAABhAoz444444hAzAh4Ahhz414444441141,~{#.......*------]]]---*.......",
+"......,~~#4144h4hhzhzzg44hhzCAC14444444414444444444111,!~#.......*--]]]]]-----*.......",
+"......,{{#4444444A4zzhBAhBAz1zzz44zzDAABAzz44444444g11,~~#.......*----]]]]]]--*.......",
+"......,{~#114444hhhhz44AAAAzz44A44zzzBzzzAz4444444og11,~~#.......*---------]--*.......",
+"......,~~#1111444AAAhBz4AAAzzo4444z44o4zzzz4444444ogg1,{~#.......*------------*.......",
+"......,{{#1111414444zzzzzzzzzzz4444z4z444AA4zAAzz44gg4,~~#.......**************.......",
+"......,~~#1144141g4oo4zzzzzzzz11zzzz444444444og1goo4g1,~~#............................",
+"......,~~#|||11441444zz1zzzzzzzzzzzzzzzzzzAzzzzooz4414,~~#.........#%$#@+.............",
+"......,!{#114444444zzzzzzzzzAAAzzzzzzzzzzzzzzzz4zz4441,{~#........++----++#*%+........",
+"......,!~#44444444zzzzzzzzAzAAA4zzzzzz444zAAzzz4zz1414,{~#........%----------++$......",
+"......,!!#|1|114144zzzzzzz4zAAAA4AAzz4zgz44zzhzzoz1111,!~#........*--]]]]------%......",
+"......,!~#11144444444zzAzzz4BCCB44ChA444CCChhhzzoz1111,~~#........#-----_*]]---$......",
+"......,!~#444444444441zhhAhC444CCC4BCCCC4CzAAA4ozo4414,{~#........+----]*_]----$......",
+"......,!~#1114444444zz4zAAAA44444444444444444zAoooo414,{{#.......++---]]------- at ......",
+"......,{~#444444ooo4z444zAAAABBDAC4CC4AA4BAzzAAzooo444,~~#....... at -----]]]]----*......",
+"......,~~#4444444444444zzzAAABBCAA4444Dz4Azzzzzo44o414,{{#.......#------]]]---++......",
+"......,!!#444go4o444444444AAABBA44ACDBD4A4z4zCz4o4g111,!~#.......$---]]]]-----#.......",
+"......,{{#414444444444444414444444444BCzC4CCChA4o4g111,~!#.......+----]]]]]]--*.......",
+"......,~{#4444444444444hhAh44444444zC444C444444o414414,{!#...... at +---------]--%.......",
+"......,~{#4414444444444zzA4444AC444444444A44444og11414,{!#......_ at ------------+.......",
+"......,{~#144444ggo4444444zzzz4444zz44zzAhz44444g11444,~!#.......++%*#++-----++.......",
+"......,~!#41144444zzzAz44444zzzzzzzzz44zzzz444z4441444,!!#.............+@$$++#........",
+"......,~~#4444444hhhCzz44444o4zzzzz44144zzA44zzzz44444,{!#..................+_........",
+"......,~~#44444444zAhABzz44444zzzzz44z1144z4zz4444444h,~~#............................",
+"......,~{#44444444zzz44zzzzz44zzzzzzAAzzz44zzz4g414414,~!#............................",
+"......,~{#144444414zz44zAzAAACCzzz44ACCzz4CzBzzg111414,~~#............................",
+"......,!~#114444441zzh444444444444444444zACDAzz4111444,~~#............................",
+"......,!~#444444444zAhC4CCC4444CCACC44CCCACCAA44441444,~{#............................",
+"......,!~#h444444444zAA4hB44DABBBAzAAz4zzzzzzz44444444,~{#............................",
+"......,~~#444411114g4zzzAAA4zzzADAAzzz44zz44444444444h,~!#............................",
+"......,~~#444444444444h44444zzzC444h444h4hhh4444444h44,{!#............................",
+"......,~~#hhhh44444hhhhhhh444zhhhhhh44hhhhhhh444444hh4,~!#............................",
+"......,~~#4hhh44444444h44444444444444444444444h4444411,~~#............................",
+"......,~!#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,~~#............................",
+"......,!~~!~~~~~~~~~{{{{{{{!~~~~{{{{!!!{{{{{{~{~~~~~{~~~{#............................",
+"......,~~~~~{{{!!{~~~~~~!!!!!~~~~!~~~{{~~~~~~{{~{{~~~!!!{#............................",
+"......,###################################################............................",
+"......................................................................................",
+"................................................................................-.]..."};
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-wmaker/wmpinboard.git
More information about the Pkg-wmaker-commits
mailing list