Initial commit master
authorBernie Innocenti <bernie@codewiz.org>
Fri, 11 Mar 2011 06:13:43 +0000 (01:13 -0500)
committerBernie Innocenti <bernie@codewiz.org>
Fri, 11 Mar 2011 06:13:43 +0000 (01:13 -0500)
83 files changed:
.gitignore [new file with mode: 0644]
BUGS [new file with mode: 0755]
ChangeLog [new file with mode: 0755]
GUIDELINES [new file with mode: 0644]
INSTALL [new file with mode: 0644]
LICENSE [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
STYLE [new file with mode: 0644]
TODO [new file with mode: 0755]
classes/ExtRequesterClass/Makefile [new file with mode: 0644]
classes/Makefile [new file with mode: 0644]
common/ClassLib.s [new file with mode: 0755]
common/GetGadgetBox.c [new file with mode: 0644]
common/Makefile [new file with mode: 0644]
common/OpenClass.c [new file with mode: 0644]
common/general.mk [new file with mode: 0644]
common/gst.c [new file with mode: 0644]
common/startup_gcc.s [new file with mode: 0644]
common/startup_sc.s [new file with mode: 0644]
common/startup_storm.s [new file with mode: 0644]
config.mk [new file with mode: 0644]
docs/ExtFrameIClass.doc [new file with mode: 0644]
docs/Intuition [new file with mode: 0644]
docs/LabelTextIClass.doc [new file with mode: 0755]
docs/NoteBOOPSI [new file with mode: 0644]
docs/empty_Makefile [new file with mode: 0644]
docs/empty_config.mk [new file with mode: 0644]
gadgets/ClockGClass/Makefile [new file with mode: 0644]
gadgets/LauncherGClass/Makefile [new file with mode: 0644]
gadgets/ListBox/ListBoxClass.c [new file with mode: 0644]
gadgets/ListBox/Makefile [new file with mode: 0644]
gadgets/ListView/BoopsiLV.readme [new file with mode: 0644]
gadgets/ListView/LVDemo.c [new file with mode: 0644]
gadgets/ListView/ListViewClass.c [new file with mode: 0644]
gadgets/ListView/ListViewClass_static.newinline.s [new file with mode: 0644]
gadgets/ListView/ListViewClass_static.oldinline.s [new file with mode: 0644]
gadgets/ListView/ListViewHooks.c [new file with mode: 0644]
gadgets/ListView/Makefile [new file with mode: 0644]
gadgets/Makefile [new file with mode: 0644]
gadgets/MeterGClass/Makefile [new file with mode: 0644]
gadgets/PIPWin/GNUmakefile.nothanks [new file with mode: 0644]
gadgets/PIPWin/Makefile [new file with mode: 0644]
gadgets/PIPWin/PIPClass.c [new file with mode: 0644]
gadgets/PIPWin/PIPClass.h [new file with mode: 0644]
gadgets/PIPWin/PIPWin.c [new file with mode: 0644]
gadgets/PIPWin/PIPWin.mdbi [new file with mode: 0755]
gadgets/PIPWin/PIPWin.project [new file with mode: 0755]
gadgets/PIPWin/PIPWin.readme [new file with mode: 0644]
gadgets/PIPWin/PIPWin.sas [new file with mode: 0755]
gadgets/PIPWin/SMakefile [new file with mode: 0644]
gadgets/ResizeGClass/Makefile [new file with mode: 0644]
gadgets/ResizeGClass/config.mk [new file with mode: 0644]
gadgets/ScrollButton/Makefile [new file with mode: 0644]
gadgets/ScrollButton/ScrollButtonClass.c [new file with mode: 0644]
gadgets/SliderBar/FrPropGClass.c [new file with mode: 0644]
gadgets/SliderBar/Makefile [new file with mode: 0644]
gadgets/SliderBar/SliderBarDemo.c [new file with mode: 0644]
gadgets/SliderBar/SliderBarGClass.c [new file with mode: 0644]
gadgets/SmartGroup/Makefile [new file with mode: 0644]
gadgets/SmartGroup/SmartGroupGClass.c [new file with mode: 0644]
gadgets/ToolManagerGClass/Makefile [new file with mode: 0644]
include/BoopsiLib.h [new file with mode: 0644]
include/BoopsiStubs.h [new file with mode: 0644]
include/CompilerSpecific.h [new file with mode: 0644]
include/DebugMacros.h [new file with mode: 0644]
include/DiagnosticMacros.h [new file with mode: 0644]
include/ListMacros.h [new file with mode: 0644]
include/README [new file with mode: 0644]
include/gadgets/FrPropGClass.h [new file with mode: 0644]
include/gadgets/ListBoxClass.h [new file with mode: 0644]
include/gadgets/ListViewClass.h [new file with mode: 0644]
include/gadgets/PIPClass.h [new file with mode: 0644]
include/gadgets/ResizeGClass.h [new file with mode: 0644]
include/gadgets/ScrollButtonClass.h [new file with mode: 0644]
include/gadgets/SliderBarGClass.h [new file with mode: 0644]
include/gadgets/SmartGroupGClass.h [new file with mode: 0644]
include/images/CVS/Entries [new file with mode: 0644]
include/images/CVS/Repository [new file with mode: 0644]
include/images/CVS/Root [new file with mode: 0644]
include/images/ExtFrameIClass.h [new file with mode: 0644]
include/images/LabelTextIClass.h [new file with mode: 0644]
include/images/VectorGlyphIClass.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..c01d83f
--- /dev/null
@@ -0,0 +1,2 @@
+*.o
+*.gadget
diff --git a/BUGS b/BUGS
new file mode 100755 (executable)
index 0000000..897dad9
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,38 @@
+Known bugs in the provided classes
+==================================
+
+ Common:
+
+ - Some of the demo programs will exit silently when they fail
+   to open any of the required classes. In this case you must
+   copy them to your SYS:Classes/[images|gadgets]/ directory
+   in order to avoid the problem. You can also solve this
+   problem by adding The dist/classes directory to the LIBS:
+   multi-assign:
+   
+     > Assign LIBS: dist/classes/ ADD
+
+   When you are done, you can cleanup by typing:
+   
+     > Assign LIBS: dist/classes/ REMOVE
+
+
+
+ ListViewClass specific:
+
+ - The clipping mode appears to work well with the original
+   layers routines and also with CyberGraphX's "SUPERLAYERS"
+   option. When using Picasso 96, the clipped window will
+   draw incorrectly and scrolling will sometimes hang the system.
+
+ - In some cases the clipped listview with big font size calls
+   Text() with a slightly negative Y coordinate in the RastPort.
+   This shows a OS bug which corrupts the item displayed on the
+   top of the list. This problem would need further investigation.
+   It happens on AGA, CyberGraphX and Picasso96.
+
+
+Known bugs in Intuition's builtin classes
+=========================================
+
+ TODO
diff --git a/ChangeLog b/ChangeLog
new file mode 100755 (executable)
index 0000000..e69de29
diff --git a/GUIDELINES b/GUIDELINES
new file mode 100644 (file)
index 0000000..71a9cbd
--- /dev/null
@@ -0,0 +1,129 @@
+$Id: GUIDELINES,v 1.1 2000/01/12 20:37:03 bernie Exp $
+
+
+OpenBoopsi Guidelines
+=====================
+
+ This is a brief guideline to explain you how the OpenBoopsi root is
+ organized, how the makefiles system work, etc. so that you can add
+ your own class without problems and even without writing your own
+ Makefile! (more or less ;-)
+
+ The OpenBoopsi tree is structured in this way:
+
+   OpenBoopsi root/
+      Makefile
+      config.mk
+
+      images/
+          Makefile
+          class1/
+              Makefile
+              config.mk
+              Class1.c
+              Class1Demo.c
+          class2/
+              etc.
+       gadgets/
+          Makefile
+          class1/
+              Makefile
+              config.mk
+              etc.
+       include/
+          common/
+              general.mk
+              BoopsiStubs.h
+              etc.
+          images/
+              Class1.h
+              etc.
+        etc.
+
+
+These are things you should know:
+
+Makefiles
+=========
+
+1) There is a global "config.mk" files where are defined all the compiler
+ specific variables, like command line options, tools, paths, etc. It is
+ divided in two sections and you usually should need to modify only the
+ first one. This file is placed in the OpenBoosi root. Run 'make help' for
+ a listing of the current configuration.
+
+2) There are different local "config.mk" files. They contain classes
+ specific informations, i.e. class name, class version, etc. They are
+ placed in every classes' directory and are required, so if you add a
+ new class to the OpenBoopsi project you must write one of this file.
+ An empty config.mk file is provided in the docs/ drawer with all the
+ explanations you will need.
+
+3) The top level Makefile will call all the makefiles in the other
+ subdirectories to build the complete distribution, as usual. However,
+ this Makefile also has a special target which sets up your build
+ environment. Without this preliminar step you won't be able to compile
+ anything. Read the INSTALL file or just run 'make help-setup' for a
+ more detailed explanation of this required step.
+
+4) The makefiles present in every classes' directory are simple dummy
+ makefiles. They just include the local "config.mk" file and the real
+ Makefile, so you just need to copy it in your class' directory without
+ any modifications.
+
+5) It is possibleto customize the local makefiles to pass custom "arguments"
+ to the true Makefile, so if e.g. your class need to link a specific object
+ you don't need to modify the general Makefile. Read the docs/empty_Makefile
+ file for a detailed explanation.
+
+6) You can obviously add to the local Makefile your own targets, e.g. for
+ creating a custom object that need to be linked to the demo program
+ (and that you will need to pass to the general Makefile via the argument
+ system described above).
+
+7) The "general.mk" file in the common/ directory is the Makefile used to
+ compile all the classes. It is included by the various local makefiles.
+ This method has three great advantages: 1) if the Makefile is changed or
+ improved it does not need to be copied in every subdirectories and 2)
+ you don't need to write your own custom Makefile, just the config.mk
+ 3) if you write a better/different Makefile other user can take
+ advantages of this just by including your Makefile instead of the
+ standard one.
+
+8) Remember that custom makefiles must support the defined targets or it
+ will be impossible to build all the distribution calling the top level
+ Makefile. You can find a list of the required targets and their purposes
+ in the general.mk file.
+
+9) If you want your own custom Makefile we suggest you to put it in the
+ common/ directory (so don't call it Makefile, give it a different name)
+ and then change your class' local Makefile to include your custom true
+ Makefile instead of the default one. In this way other people can use your
+ Makefile without the need to copy it to their subdirectories.
+
+
+Includes
+========
+
+1) All the includes must be placed in the appropriate include/ subdirectories.
+ You must leave includes in the classes subdirectories only if they e.g. contain
+ private definitions needed by the class only, i.e. no other programs or classes
+ can include it.
+
+2) The makefiles already know where they should search for the needed
+ includes and how to tell the compiler to find them, so you should not
+ worry about this.
+
+3) To include the required headers in your programs or classes just follow
+ this example:
+
+#include "common/BoopsiStubs.h"
+#include "images/VectorGlyph.h"
+#include "gadgets/ListView.h"
+#include "RequesterClass.h"
+#include "MyPrivateInclude.h"
+
+4) IMPORTANT: in the example above the last two includes look identical,
+ however the first should be placed in the include/ dir (and not
+ in one of its subdirectories!) while the latter should go in the class
+ own directory.
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..583664b
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,15 @@
+$Id: INSTALL,v 1.1 2000/01/12 20:36:51 bernie Exp $
+
+How to build the project
+========================
+
+ Run "make setup". It will create an env variable with the name of the dir
+where you have unpacked the OpenBoopsi distribution. This variable is
+required, so don't remove it! It will also be copied in ENVARC:, so you
+will need to do the setup only once. Running 'make help-setup' will
+print a brief explanation and the value that will be assigned to the
+environment variable when you do the setup.
+
+ It is also advisable to read the GUIDELINES file for a detailed
+description of the distribution structure and how the makefiles
+are organized.
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..161a3d1
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,482 @@
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 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.
+
+[This is the first released version of the library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, 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 library, or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+\f
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+\f
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License").  Each licensee is
+addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, 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 library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete 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 distribute a copy of this License along with the
+Library.
+
+  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.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+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 Library, 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 Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you 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.
+
+  If distribution of 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 satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    c) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    d) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  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.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library 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.
+
+  9. 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 Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+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.
+\f
+  11. 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 Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library 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 Library.
+
+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.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library 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.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Library 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 Library
+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 Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+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
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. 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 LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+     Appendix: How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  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 library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+    MA 02111-1307, USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..222061b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,20 @@
+##
+## $Id: Makefile,v 1.1 2000/01/12 20:37:38 bernie Exp $
+##
+##     This Makefile must be processed with GNU make 3.76.1
+##     which is available on Aminet: dev/c/make_bin.lha
+##
+## Copyright (C) 1999 by B. Innocenti & M. Cavalleri
+##
+
+SUBDIRS := common classes images gadgets
+
+include config.mk
+
+ifeq ($(COMPILER),gcc)
+       TOP := $(shell pwd)
+else
+       TOP := $(shell cd)
+endif
+
+include common/general.mk
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..b551e92
--- /dev/null
+++ b/README
@@ -0,0 +1,82 @@
+$Id: README,v 1.1 2000/01/12 20:32:09 bernie Exp $
+
+
+About `boopsi'
+==============
+
+ The achronim `boopsi' stands for Basic Object Oriented Programming System
+for Intuition. This facility is available in AmigaOS since Kickstart V36.
+boopsi is primary meant as an OOP framework for GUI components such as
+gadgets, images, texts and so on.
+
+ The system provides several boopsi classes which are the OOP counterparts
+of the plain Intuition gadgets and other important structures such as Image
+and IntuiText. There are also classes that allow building a network of
+interconnected boopsi objects so that each object can notify the others
+about its attributes. This boopsi communication system enables the gadgets
+to talk each other so they can react to user input in complex ways, freeing
+the application from most of the GUI administration duties.
+
+
+Other GUI systems
+=================
+
+ Unfortunately, the built-in Intuition classes are not very powerful,
+nor flexible enough for todays applications. That's why so many
+alternative GUI systems have appeared during the past years. Some
+of these systems are also based on boopsi, while others provide
+their own API through a shared library.
+
+ The main disadvantage with these GUI systems mentioned above is
+none of them is free. In this case we are referring to the GNU meaning
+of the word `free'. It means that the users and the developers are
+not given enough freedom. Some GUI systems are shareware, some others
+come with a license that forbids or limits their use for commercial
+applications. Few of them come with their source code and in most
+cases they don't give you the freedom to change it. On the technical
+side, the major limit of the current GUI systems is that they are
+usually mutually exclusive. You can't mix objects from two different
+systems to take advantage of the features of both.
+
+
+About the boopsi OSS project
+============================
+
+ The goal of this project is providing a comprehensive set of
+boopsi classes for general application developement. The main
+developemnt guidelines are:
+
+  - providing a pure boopsi API for GUI construction. There
+    are no additional libraries.
+
+  - integration with the system built-in classes, whenever
+    possible.
+
+  - support the current V39/V40 OS along with the current
+    RTG systems.
+
+  - Sticking to clean AmigaOS programming, except when it is
+    absolutely impossible to do so to workaround known
+    OS bugs.
+
+  - Adopting a distributed developement model. Source code
+    is freely available and anyone is allowed to improve or
+    add new functionality.
+
+  - Leave the choice to the application developer.
+    The classes will be available in several flavours,
+    including shared class libraries, linker library and
+    a monolithic shared library.
+
+  - Find the best trade-off beteen efficiency, portability
+    and configurability
+
+
+DISCLAIMER
+==========
+
+ This software is provided AS-IS, without any explicit or implied warranty.
+
+ This software is to be considered PRELIMINARY and SUBJECT TO CHANGE.
+It has not been tested accurately and it may contain BUGS that could
+easily CRASH YOUR SYSTEM.
diff --git a/STYLE b/STYLE
new file mode 100644 (file)
index 0000000..4f153d4
--- /dev/null
+++ b/STYLE
@@ -0,0 +1,185 @@
+$VER OpenBOOPSI Project Style Guide 1.1 (4.5.99)
+
+
+                         OpenBOOPSI Project Style Guide
+
+----
+
+
+0. SUMMARY
+
+    1. Introduction
+    2. Comments
+        2.1 General Rules
+        2.2 Language
+        2.3 Single line comments
+        2.4 Multi line comments
+    3. Blank Spaces
+        3.1 Tabs
+        3.2 Spaces
+        3.3 Empty lines
+    4. Naming Conventions
+        4.1 General Rules
+        4.2 Functions
+        4.3 Variables
+
+
+1. INTRODUCTION
+
+    This is a brief guide on recommended source code formatting style
+    for the OpenBOOPSI Project classes. Every source file added to this
+    project should conform as close as possible to the rules given in
+    this document to mantain consistency within the source tree.
+
+    It might happen that you're already used to a different coding
+    style. Yes, we all understand well that changing your style can be
+    frustrating for any programmer. But the need to keep the source
+    code style consistent is more important than each one's habits.
+    No one will blame you if you don't strictly follow these
+    guidelines, but we will be glad if you do.
+
+
+2. COMMENTS
+
+    2.1 GENERAL RULES
+
+        Comment as much as you can. Other programmers will read your
+        sources and since most of them will have different knowledge of
+        Amiga programming and/or different ways to do a certain thing they
+        may not understand the purpose or the inner working of some of your
+        code, so try to explain what you are doing. Obviously avoid comments
+        such as "now open a window" right above a OpenWindow() function call.
+
+    2.2 LANGUAGE
+
+        Comments must be written in english.
+
+    2.3 SINGLE LINE COMMENTS
+
+        Single line comments should follow the ANSI C syntax, i.e.
+        using the "/* */" construct. C++ comments must not be used
+        because not all C compilers do support them.
+
+    2.4 MULTI LINE COMMENTS
+
+        Comments spanning over multiple lines should be formatted this way:
+
+        /* This is a very very very
+         * very very long comment!
+         */
+
+
+3. BLANK SPACES
+
+    3.1 TABS
+
+        Tabs are four spaces wide.
+
+    3.2 SPACES
+
+        There should be a space after commas.
+
+        There should never be a space after a * when it's used with pointers,
+        e.g.:
+
+        ULONG *pointer = NULL;
+        *pointer = 45 * y;
+
+        There should be a blank space around operators, e.g.:
+
+            var1 + var2;
+            var1 & var2;
+            etc.
+
+        the only exceptions are prefix and postfix ++ and -- operators,
+        i.e.:
+
+            var--;
+            ++var;
+
+        Before a parenthesis there should never be blank spaces if
+        it follow a keyword or a function name. e.g.
+
+            if()              /* good */
+            DoMethod();       /* also good */
+            while ()          /* wrong! */
+            yourfunction ();  /* also wrong! */
+
+    3.3 EMPTY LINES
+
+        After the #include block and after the #define one there should be
+        two empty lines.
+
+        Between the declaration of the global variables and the beginning of
+        the code there should be two empty lines.
+
+        Between the end of a function and the next one there should be three
+        empty lines.
+
+        Any code block enclosed in braces should be preceded by an empty line.
+        An open brace should be on a line by itself. After a closing brace there
+        should be one empty line, except when another closing brace follows. e.g.
+
+            if(z == 1)
+            {
+                z++;
+            }
+
+            y += z + 3;
+
+            if(y > 8)
+            {
+                f = do_some_stuff();
+
+                if(f == TRUE)
+                {
+                    printf("ok!\n");
+                }
+            }
+
+            /* the following is wrong! */
+
+            if(y==1) {
+                z++;
+            }
+            y+=z +3;
+            if (y >8)
+            { etc. }
+
+
+4. NAMING CONVENTIONS
+
+    4.3 GENERAL RULES
+
+        Everything related to the classes (methods, functions, etc.) should
+        have a prefix (two or three letter maximum) that recalls the name of
+        the class. i.e. if your class is called "BananaSplit" its methods
+        should be called something like BSM_SHAKE, its attributes should look
+        like BSA_SugarSpuns, etc.
+
+    4.2 FUNCTIONS
+
+        Method functions should obviously follow the general rule and have
+        the above mentioned prefix, e.g.:
+
+        BS_GoActive();
+
+        Function names should have the first letters of each word
+        forming their names capitalized, like system's one. e.g.:
+
+        MyWonderfulFunction()
+
+    4.3 VARIABLES
+
+        Variables name must be in english and must have a meaning! Please
+        avoid names such as "foobar" or "dummy"! We hate them!
+        Some examples:
+
+        struct Window *win;       /* these are good */
+        ULONG  result;
+
+        struct BitMap *dummy1;    /* these are wrong! */
+        struct RastPort *dummy2;
+
+        Names of local variables should be all in lowercase. Global
+        variables should have their initials uppercase.
diff --git a/TODO b/TODO
new file mode 100755 (executable)
index 0000000..e69de29
diff --git a/classes/ExtRequesterClass/Makefile b/classes/ExtRequesterClass/Makefile
new file mode 100644 (file)
index 0000000..92449a0
--- /dev/null
@@ -0,0 +1,23 @@
+#
+# $Id: Makefile,v 1.1 2000/01/12 20:40:56 bernie Exp $
+#
+# Copyright (C) 1999 by Matteo Cavalleri
+#
+
+include $(TOP)/config.mk
+
+ARG_COMMON :=
+ARG_ALL := $(ARG_COMMON)
+ARG_LIB := $(ARG_COMMON)
+ARG_DEBUG := $(ARG_COMMON)
+ARG_PRIVATE := $(ARG_COMMON)
+ARG_OBJ := $(ARG_COMMON)
+
+DEP_COMMON :=
+DEP_ALL := $(DEP_COMMON)
+DEP_LIB := $(DEP_COMMON)
+DEP_DEBUG := $(DEP_COMMON)
+DEP_PRIVATE := $(DEP_COMMON)
+DEP_OBJ := $(DEP_COMMON)
+
+include $(TOP)/common/general.mk
diff --git a/classes/Makefile b/classes/Makefile
new file mode 100644 (file)
index 0000000..54b2e11
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# $Id: Makefile,v 1.1 2000/01/12 20:40:06 bernie Exp $
+#
+# Copyright (C) 1999 by Matteo Cavalleri
+#
+
+SUBDIRS := ExtRequesterClass
+
+include $(TOP)/common/general.mk
diff --git a/common/ClassLib.s b/common/ClassLib.s
new file mode 100755 (executable)
index 0000000..d9bc8c2
--- /dev/null
@@ -0,0 +1,298 @@
+******************************************************************************
+*
+* $VER: ClassLib.asm (5.9.97)
+*
+* Copyright (C) 1995,96,97 by Bernardo Innocenti
+*
+******************************************************************************
+*
+* This source is based on the RomTag.asm source I've found somewhere :-)
+*
+*
+       INCLUDE "exec/types.i"
+       INCLUDE "exec/macros.i"
+       INCLUDE "exec/libraries.i"
+       INCLUDE "exec/lists.i"
+       INCLUDE "exec/alerts.i"
+       INCLUDE "exec/initializers.i"
+       INCLUDE "exec/resident.i"
+       INCLUDE "exec/execbase.i"
+       INCLUDE "libraries/dos.i"
+
+       INCLUDE "exec/funcdef.i"
+       INCLUDE "exec/exec_lib.i"
+
+
+; BOOPSI class libraries should use this structure as the base for their
+; library data.  This allows developers to obtain the class pointer for
+; performing object-less inquiries.
+
+       STRUCTURE ClassBase,0
+       STRUCT   cl_Lib,LIB_SIZE        ; Embedded library
+       UWORD    cl_Pad                 ; Align the structure
+       APTR     cl_Class               ; Class pointer
+       APTR     cl_SegList             ; SegList pointer
+       LABEL    ClassLibrary_SIZEOF
+
+;---------------------------------------------------------------------------
+
+       XDEF    _LibFuncTable
+       XDEF    _LibDataTable
+
+       XREF    _LibName
+       XREF    _LibId
+       XREF    __UserLibInit
+       XREF    __UserLibCleanup
+
+       XREF    __GetEngine
+
+;---------------------------------------------------------------------------
+
+       SECTION Code
+
+; First executable location, must return an error to the caller
+
+       moveq   #-1,d0
+       rts
+
+;---------------------------------------------------------------------------
+
+_ROMTAG:
+       DC.W    RTC_MATCHWORD   ; UWORD RT_MATCHWORD
+       DC.L    _ROMTAG         ; APTR  RT_MATCHTAG
+       DC.L    _ENDCODE        ; APTR  RT_ENDSKIP
+       DC.B    RTF_AUTOINIT    ; UBYTE RT_FLAGS
+       DC.B    LIBVERSION      ; UBYTE RT_VERSION
+       DC.B    NT_LIBRARY      ; UBYTE RT_TYPE
+       DC.B    0               ; BYTE  RT_PRI  <--- WARNING: Using negative values here will cause trouble!
+       DC.L    _LibName        ; APTR  RT_NAME
+       DC.L    _LibId          ; APTR  RT_IDSTRING
+       DC.L    _LibInitTable   ; APTR  RT_INIT
+
+
+* The RomTag specified that we were RTF_AUTOINIT. This means that rt_Init
+* points to the table below. (Without RTF_AUTOINIT it would point to a
+* routine to run.)
+*
+* Our library base is a standard struct Library, followed by a WORD
+* pad, a pointer to the boopsi Class structure of the external
+* boopsi class and a pointer to our SegList.  The SegList pointer
+* will be returned by LibExpunge() in order to have our code UnloadSeg()'ed
+* The Class pointer will be initialized by UserLibInit().
+
+_LibInitTable:
+       dc.l    ClassLibrary_SIZEOF
+       dc.l    _LibFuncTable
+       dc.l    _LibDataTable
+       dc.l    _LibInit
+
+
+
+* Table of functions included in this library; the first 4 are the same
+* for any library and for internal Exec use only.
+
+
+_LibFuncTable:
+       dc.l    _LibOpen
+       dc.l    _LibClose
+       dc.l    _LibExpunge
+       dc.l    _LibExtFunc
+       dc.l    __GetEngine
+       dc.l    -1
+
+;V_DEF MACRO
+;      dc.w    \1 + (* - _LibFuncTable)
+;      ENDM
+;
+;_LibFuncTable:
+;      dc.w    -1              ; It's weird: the cool way didn't work for me :-(
+;      V_DEF   _LibOpen
+;      V_DEF   _LibClose
+;      V_DEF   _LibExpunge
+;      V_DEF   _LibExtFunc
+;      V_DEF   __GetEngine
+;      dc.w    -1
+
+
+
+_LibDataTable
+       INITBYTE        LN_TYPE,NT_LIBRARY
+       INITLONG        LN_NAME,_LibName
+       INITBYTE        LN_PRI,-5
+       INITBYTE        LIB_FLAGS,(LIBF_SUMUSED!LIBF_CHANGED)
+       INITWORD        LIB_VERSION,LIBVERSION
+       INITWORD        LIB_REVISION,LIBREVISION
+       INITLONG        LIB_IDSTRING,_LibId
+       dc.w            0
+
+
+       CNOP    0,4
+
+
+* The following function will be called at startup time.
+*
+* Inputs:
+*      LibPtr (d0) - Pointer to the library base, initialized due to the
+*                      specifications in DataTable
+*      SegList (a0) - BPTR to the segment list
+*      _SysBase (a6) - The usual ExecBase pointer
+*
+* Result:
+*      LibPtr, if all was okay and the library may be linked into the
+*      system library list. NULL otherwise.
+*
+_LibInit:
+       move.l  d0,a1
+       move.l  a0,cl_SegList(a1)               ; Save SegList
+
+; Check CPU for 68020 or better
+       IFD             _MC68020_
+       move.w  AttnFlags(a6),d1
+       btst.w  #AFB_68020,d1
+       beq.s   fail$
+       ENDC
+
+       move.l  a6,-(sp)                        ; Save SysBase
+       move.l  d0,a6                           ; Put our base in a6
+       jsr     __UserLibInit                   ; Call user init
+       move.l  a6,a1                           ; save our base to a1
+       move.l  (sp)+,a6                        ; Retrieve SysBase
+       tst.l   d0
+       beq.s   fail$
+       rts
+
+fail$
+       bsr     FreeBase                        ; Free library base
+       moveq   #0,d0
+       rts
+
+
+* The following functions are called from exec.library/OpenLibrary(),
+* exec.library/CloseLibrary() and exec.library/ExpungeLibrary(),
+* respectively. Exec passes our library base pointer in A6.
+*
+* Task switching will be turned off while these functions are being
+* executed, so they must be as short as possible.  As the data inside
+* the library base is protected with Forbid(), these functions must
+* not make calls which would explicitly or implicitly turn on multitasking.
+* This includes opening other disk based libraries.  The problem may be
+* overcame by protecting the library base with a SignalSemaphore.
+*
+
+
+* This function is called from exec.library/OpenLibrary().
+*
+* Inputs:
+*      LibPtr (a6) - Pointer to the library base
+*      Version (d0) - The suggested version number
+*
+* Result:
+*      LibPtr, if successful, NULL otherwise
+*
+
+_LibOpen:
+       addq.w  #1,LIB_OPENCNT(a6)
+       bclr.b  #LIBB_DELEXP,LIB_FLAGS(a6)      ; Prevent delayed expunge
+       move.l  a6,d0
+       rts
+
+
+
+* This function is called from exec/CloseLibrary().
+*
+* Inputs:
+*      LibPtr (A6) - pointer to the library base as returned from OpenLibrary().
+*
+* Result:
+*      Segment list of the library (see arguments of _LibInit), if there
+*      was a delayed expunge and the library is no longer open, NULL
+*      otherwise.
+*
+_LibClose:
+       subq.w  #1,LIB_OPENCNT(a6)
+       tst.w   LIB_OPENCNT(a6)
+       bne.s   .NoExpunge
+       btst.b  #LIBB_DELEXP,LIB_FLAGS(a6)
+       beq.s   .NoExpunge
+
+       bra.s   _LibExpunge
+
+.NoExpunge
+       moveq.l #0,d0
+       rts
+
+
+
+* This function is called from exec.library/RemoveLibrary().
+*
+* Inputs:
+*      LibPtr (A6) - pointer to the library base.
+*
+* Result:
+*      Segment list of the library (see arguments of _LibInit()),
+*      if the library isn't opened currently, NULL otherwise.
+*
+
+_LibExpunge:
+
+       ; Flag library base for delayed expunge
+       bset.b  #LIBB_DELEXP,LIB_FLAGS(a6)
+       tst.w   LIB_OPENCNT(a6)         ; Only expunge if OpenCnt == 0
+       bne.s   .DoNotExpunge
+
+.NotOpen
+
+       jsr     __UserLibCleanup        ; Call user cleanup code
+       tst.l   d0
+       beq.s   .DoNotExpunge
+
+       move.l  cl_SegList(a6),-(sp)    ; Save SegList pointer
+
+       move.l  a6,a1
+       REMOVE                          ; Remove us from Exec library list.
+
+
+; Free the library base
+
+       move.l  a6,a1                   ; LibBase
+       move.l  a6,-(sp)                ; Save A6
+       move.l  4.w,a6                  ; Load SysBase
+       bsr             FreeBase                ; Free our library base
+       move.l  (sp)+,a6                ; Restore A6
+
+       move.l  (sp)+,d0                ; Return our SegList
+       rts
+
+.DoNotExpunge
+
+; NOTE: I'm falling in _LibExtFunc from here!
+
+
+
+* Dummy function to return 0
+
+_LibExtFunc:
+       moveq   #0,d0
+       rts
+
+
+* Frees our library base
+*
+* Inputs:
+*      LibBase (a1) - Pointer to Library structure.
+*      SysBase (a6) - Pointer to SysBase
+*
+FreeBase:
+       moveq.l #0,d0
+       move.l  a1,a0
+       move.w  LIB_NEGSIZE(a0),d0
+       suba.l  d0,a1                   ; Get pointer to real start of library base
+       add.w   LIB_POSSIZE(a0),d0      ; Total library size (LIB_POSSIZE + LIB_NEGSIZE)
+       jsr             _LVOFreeMem(a6)
+       rts
+
+;-----------------------------------------------------------------------
+
+_ENDCODE
+
+       END
diff --git a/common/GetGadgetBox.c b/common/GetGadgetBox.c
new file mode 100644 (file)
index 0000000..e9192f6
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+**     $Id:$
+**
+**     Copyright (C) 1999 Bernardo Innocenti
+**     All rights reserved.
+**
+**     Use 4 chars wide TABs to read this file
+**
+**     GetGadgetBox() computes the actual IBox where a gadget exists in a window.
+**     The special cases it handles are all the REL#? (relative positioning flags).
+**
+**     This function returns the gadget size in the provided
+**     IBox structure, computing the values from the coordinates
+**     in the gadget structure and the container (window or requester)
+**     where it lives.
+*/
+
+#include <intuition/intuition.h>
+
+#include <CompilerSpecific.h>
+#include <DebugMacros.h>
+#include <BoopsiLib.h>
+
+
+void GetGadgetBox(struct GadgetInfo *ginfo, struct ExtGadget *g, struct IBox *box)
+{
+       ASSERT_VALID_PTR(g)
+       ASSERT_VALID_PTR(ginfo)
+       ASSERT_VALID_PTR(box)
+
+       DB2(if (g->Flags & GFLG_EXTENDED)
+               DBPRINTF("GetGadgetBox(): GFLG_EXTENDED is set\n");)
+       DB2(if ((g->Flags & GFLG_EXTENDED) && (g->MoreFlags & GMORE_BOUNDS))
+               DBPRINTF("GetGadgetBox(): Gadget has valid bounds\n");)
+
+
+       box->Left = g->LeftEdge;
+       if (g->Flags & GFLG_RELRIGHT)
+               box->Left += ginfo->gi_Domain.Width - 1;
+
+       box->Top = g->TopEdge;
+       if (g->Flags & GFLG_RELBOTTOM)
+               box->Top += ginfo->gi_Domain.Height - 1;
+
+       box->Width = g->Width;
+       if (g->Flags & GFLG_RELWIDTH)
+               box->Width += ginfo->gi_Domain.Width;
+
+       box->Height = g->Height;
+       if (g->Flags & GFLG_RELHEIGHT)
+               box->Height += ginfo->gi_Domain.Height;
+
+       DB2(DBPRINTF("GetGadgetBox(): Left = %ld, Top = %ld, Width = %ld, Height = %ld\n",
+               box->Left, box->Top, box->Width, box->Height);)
+}
+
+
+
+/* Useful macro missing in <intuition/imageclass.h> */
+#define GADGET_BOUNDS(g) ((struct IBox *)&((struct ExtGadget *)(g))->BoundsLeftEdge)
+
+
+void GetGadgetBounds(struct GadgetInfo *ginfo, struct ExtGadget *g, struct IBox *bounds)
+{
+       struct IBox *box;
+
+       ASSERT_VALID_PTR(g)
+       ASSERT_VALID_PTR(ginfo)
+       ASSERT_VALID_PTR(bounds)
+
+
+       box = ((g->Flags & GFLG_EXTENDED) && (g->MoreFlags & GMORE_BOUNDS)) ?
+               GADGET_BOUNDS(g) : GADGET_BOX(g);
+
+       memcpy(bounds, box, sizeof(struct IBox));
+
+       if (g->Flags & GFLG_RELRIGHT)
+               bounds->Left += ginfo->gi_Domain.Width - 1;
+
+       if (g->Flags & GFLG_RELBOTTOM)
+               bounds->Top += ginfo->gi_Domain.Height - 1;
+
+       if (g->Flags & GFLG_RELWIDTH)
+               bounds->Width += ginfo->gi_Domain.Width;
+
+       if (g->Flags & GFLG_RELHEIGHT)
+               bounds->Height += ginfo->gi_Domain.Height;
+}
diff --git a/common/Makefile b/common/Makefile
new file mode 100644 (file)
index 0000000..8e6fbd6
--- /dev/null
@@ -0,0 +1,90 @@
+#
+# $Id:$
+#
+# Copyright (C) 1999 by Matteo Cavalleri
+#
+
+include $(TOP)/config.mk
+
+COMMON_OBJS = GetGadgetBox.o
+
+all: $(GST) $(APP_STARTUP) $(COMMON_OBJS)
+
+help-all:
+       @echo "This target compile the custom startup code and place it"
+       @echo "in the obj/ directory, create the GST (Global Symbol Table)"
+       @echo "for SAS/C users."
+       @echo
+
+lib:
+       $(NOP)
+
+help-lib:
+       @echo "This target does nothing."; echo
+
+debug:
+       $(NOP)
+
+help-debug:
+       @echo "This target does nothing."; echo
+
+private:
+       $(NOP)
+
+help-private:
+       @echo "This target does nothing."; echo
+
+obj: $(APP_STARTUP)
+
+help-obj:
+       @echo "This target compile the custom startup code and place it"
+       @echo "in the obj/ directory."
+       @echo
+
+clean:
+       $(RM) $(APP_STARTUP) $(GST)
+
+help-clean:
+       @echo "This target deletes the custom startup code object and the GST."
+       @echo
+
+install:
+       $(NOP)
+
+help-install:
+       @echo "This target does nothing."; echo
+
+
+###########################################################
+# Compile custom startup code for applications
+###########################################################
+
+$(APP_STARTUP): $(APP_STARTUP_SRC)
+       $(AS) $< $(TO) $@
+
+###########################################################
+# Compile common objects
+###########################################################
+
+$(COMMON_OBJS): %.o : %.c
+       $(CC) $< $(TO) $@ $(O_CFLAGS)
+
+
+###########################################################
+# Make Global Symbol Table to speed up compiling
+###########################################################
+#
+# We must define some symbols here because defining them
+# inside GST.c won't work as expected. (SAS/C bug?)
+#
+# NOTE:        The GST file does not depend on any headers because
+#      otherwise all objects would be remade each time you edit
+#      one of the header files.
+#
+
+$(GST): GST.c
+       ifeq ($(strip $(COMPILER)),sc)
+               $(CC) FROM gst.c MAKEGST $(GST) NOOBJNAME $(O_CFLAGS) \
+                DEF=INTUI_V36_NAMES_ONLY DEF=__USE_SYSBASE \
+                DEF=CLIB_ALIB_PROTOS_H
+       endif
diff --git a/common/OpenClass.c b/common/OpenClass.c
new file mode 100644 (file)
index 0000000..305f498
--- /dev/null
@@ -0,0 +1,44 @@
+struct ClassLibrary *OpenClass(CONST_STRPTR name, CONST_STRPTR prefix, ULONG version)
+
+/* Open named class. Look both in current and images/ directory
+ */
+{
+       static struct EasyStruct OpenClassES =
+       {
+               sizeof(struct EasyStruct),
+               0,
+               versiontag + 6,
+               "Couldn't open %s version %ld or greater",
+               "Ok"
+       };
+
+       struct ClassLibrary *classbase;
+       char buf[256];
+       int i;
+
+
+       if (!(classbase = (struct ClassLibrary *)OpenLibrary(name, version)))
+       {
+               /* We can't use AddPart() here because we didn't open dos.library */
+
+               /* Copy the prefix in the buffer */
+               for (i = 0; buf[i] = prefix[i]; i++);
+
+               /* Insert trailing character if missing */
+               if(buf[i] != '/')
+                       buf[++i] = '/';
+
+               /* Append the name */
+               while (buf[i++] = *name++);
+
+               /* Try again */
+               classbase = (struct ClassLibrary *)OpenLibrary(buf, version);
+
+               if (!classbase)
+               {
+                       /* Report an error */
+                       EasyRequest(NULL, &OpenClassES, NULL, buf, version);
+               }
+       }
+       return classbase;
+}
diff --git a/common/general.mk b/common/general.mk
new file mode 100644 (file)
index 0000000..edf4ea9
--- /dev/null
@@ -0,0 +1,277 @@
+#
+# $Id:$
+#
+# Copyright (C) 1999 by Matteo Cavalleri
+#
+
+# This makefile is flexible enough to be used for every
+# class of the OpenBoopsi project. You should not need to
+# modify anything
+#
+# all: create the demo program and the class library
+#      with no debugging information and code
+#
+# lib: create just the library of the public class
+#      with no debugging information and code
+#
+# private: create the demo program linked with optimized version of the class
+#
+# debug: create the demo program linked with debug version of the class
+#
+# obj: create just the class object ready to be linked
+#      to a program, with no debugging information and code
+#
+# install: copy the demos and classes to the appropriate
+#      directories
+#
+
+###########################################################
+# some variables...
+#
+BOOPSI_DEPS    := $(INCDIR)/BoopsiStubs.h $(INCDIR)/CompilerSpecific.h $(INCDIR)/DebugMacros.h
+CLASSOBJ_SHARED        := $(subst .c,_shared.o,$(CLASSSRC))
+CLASSOBJ_STATIC        := $(subst .c,_static.o,$(CLASSSRC))
+CLASSOBJ_DEBUG := $(subst .c,_dbg.o,$(CLASSSRC))
+CLASSINC       := $(subst .c,.h,$(CLASSRC))
+DEMOOBJ_STATIC := $(subst .c,_static.o,$(DEMOSRC))
+DEMOOBJ_SHARED := $(subst .c,_shared.o,$(DEMOSRC))
+DEMOOBJ_DEBUG  := $(subst .c,_dbg.o,$(DEMOSRC))
+
+# Provide a default name for the demo program
+ifeq ($(strip $(DEMOPROG)),)
+       ifneq ($(strip $(DEMOSRC)),)
+               DEMOPROG := $(subst .c,,$(DEMOSRC))
+       endif
+endif
+
+# Generate the name of the debug demo
+ifneq ($(strip $(DEMOPROG)),)
+       DEMOPROG_DEBUG := $(DEMOPROG)_debug
+endif
+
+###########################################################
+# additional compiler flags
+#
+ifeq ($(strip $(COMPILER)),gcc)
+       LIB_CFLAGS := -DDATE="\"$(CLASSDATE)"\" -DNAME="\"$(CLASSLIB)"\" \
+                       -DVERSION="\"$(CLASSVER).$(CLASSREV)"\"
+else
+       LIB_CFLAGS := $(DEF) DATE=$(CLASSDATE) $(DEF) NAME="$(CLASSLIB)" \
+                       $(DEF) VERSION="$(CLASSVER).$(CLASSREV)"
+endif
+
+O_CFLAGS += $(LIB_CFLAGS)
+D_CFLAGS += $(LIB_CFLAGS)
+
+
+###########################################################
+# additional assembler flags
+# do not modify the spaces in the lines below!
+#
+O_SFLAGS += SET "LIBVERSION=$(CLASSVER),LIBREVISION=$(CLASSREV) "
+D_SFLAGS += SET "LIBVERSION=$(CLASSVER),LIBREVISION=$(CLASSREV) "
+
+
+###########################################################
+# Main targets
+###########################################################
+#
+
+all: $(DEMOPROG) $(CLASSLIB)
+ifneq ($(strip $(SUBDIRS)),)
+       for i in $(SUBDIRS); do $(MAKE) -C $$i TOP=$(TOP) ; done
+endif
+
+help-all:
+       @echo "This target will build all the classes and demo programs."; echo
+
+
+lib: $(CLASSLIB)
+ifneq ($(strip $(SUBDIRS)),)
+       for i in $(SUBDIRS); do $(MAKE) -C $$i TOP=$(TOP) lib ; done
+endif
+
+help-lib:
+       @echo "This target will build all the classes (but not the demo programs)."; echo
+
+
+obj: $(CLASSOBJ_STATIC)
+ifneq ($(strip $(SUBDIRS)),)
+       for i in $(SUBDIRS); do $(MAKE) -C $$i TOP=$(TOP) obj ; done
+endif
+
+help-obj:
+       @echo "This target create the classes object ready to be linked as a static"
+       @echo "library, with no debugging code."; echo
+
+
+private: $(DEMOPROG)
+ifneq ($(strip $(SUBDIRS)),)
+       for i in $(SUBDIRS); do $(MAKE) -C $$i TOP=$(TOP) private ; done
+endif
+
+help-private:
+       @echo "This target will build all the demo programs with the classes linked"
+       @echo "as static libraries. No debug code and symbols are added."
+       @echo
+       @echo "This may be useful to let other users test new versions of the classes"
+       @echo "without the need to install them or without overwriting old versions"
+       @echo "that may have been installed."; echo
+
+
+debug: $(DEMOPROG_DEBUG)
+ifneq ($(strip $(SUBDIRS)),)
+       for i in $(SUBDIRS); do $(MAKE) -C $$i TOP=$(TOP) debug ; done
+endif
+
+help-debug:
+       @echo "This target will build all the demo programs with the classes linked"
+       @echo "as static libraries. Debug code and symbols are added."; echo
+
+
+install:
+ifneq ($(strip $(SUBDIRS)),)
+       for i in $(SUBDIRS); do $(MAKE) -C $$i TOP=$(TOP) install ; done
+endif
+ifneq ($(strip $(DEMOPROG)),)
+       $(CP) $(DEMOPROG) $(PREFIX)/demos/
+endif
+ifneq ($(strip $(CLASSLIB)),)
+       $(CP) $(CLASSLIB) $(PREFIX)/classes/$(INSTALLDIR)/
+endif
+
+help-install:
+       @echo "This target copy all the classes and the demos in the dist/ directory,"
+       @echo "placing the classes in the correct subdirectories."; echo
+
+
+clean:
+ifneq ($(strip $(SUBDIRS)),)
+       for i in $(SUBDIRS); do $(MAKE) -C $$i TOP=$(TOP) clean ; done
+endif
+       $(RM) $(DEMOPROG) $(CLASSLIB) $(CLASSOBJ_STATIC)
+
+help-clean:
+       @echo "This target delete all the demoprograms, all the classes and all"
+       @echo "the objects in the sources subdirectories."; echo
+
+
+help:
+       @echo "Type 'make help-(targetname) (e.g. 'make help-all') to get"
+       @echo "a detailed help for each target"; echo
+       @echo "Current package configuration:"
+       @echo "------------------------------"
+       @echo "Defined targets: all; lib; debug; private; obj; install; clean; setup;"
+       @echo "Defined subdirs: $(patsubst %,%;,$(SUBDIRS))"; echo
+       @echo "Your OpenBoopsi root directory: $(TOP)."; echo
+       @echo "Configured compiler: $(COMPILER)."
+       @echo "Configured cpu: $(CPU)."
+ifeq ($(strip $(NOSTDLIB)),0)
+       @echo "Configured demo linking method: standard startup code."
+else
+       @echo "Configured demo linking method: custom startup code."
+endif
+       @echo; echo "Happy compiling!"; echo
+
+
+###########################################################
+# build the class library
+###########################################################
+#
+$(CLASSLIB): ClassLib.o $(CLASSOBJ_SHARED) $(CLASSOBJ)
+       @$(ECHO) "Building class library..."
+       $(LDNOLIB) ClassLib.o $(CLASSOBJ_SHARED) $(CLASSOBJ) $(TO) $@ $(O_LFLAGS) $(ARG_LIB)
+       $(FLUSHLIBS)
+
+
+###########################################################
+# compile class source
+###########################################################
+#
+
+# All objects depend on their headers
+$(CLASSOBJ_SHARED) $(CLASSOBJ_STATIC) $(CLASSOBJ_DEBUG): $(CLASSINC) $(BOOPSI_DEPS)
+
+$(CLASSOBJ_SHARED): %_shared.o : %.c
+       $(CC) $< $(TO) $@ $(O_CFLAGS) -DCLASS_FLAVOUR=3
+
+$(CLASSOBJ_STATIC): %_static.o : %.c
+       $(CC) $< $(TO) $@ $(O_CFLAGS) -DCLASS_FLAVOUR=0
+
+$(CLASSOBJ_DEBUG): %_dbg.o : %.c
+       $(CC) $< $(TO) $@ $(D_CFLAGS) -DCLASS_FLAVOUR=0
+
+###########################################################
+# assemble the class library init code
+###########################################################
+#
+# All this mess is required since PhxAss can't handle
+# UNIX paths. It would also be possible to link this
+# object without converting it, but it seems that
+# this leads to a bigger executable...
+#
+ClassLib.o: $(TOP)/common/ClassLib.s Makefile
+ifeq ($(strip $(COMPILER)),gcc)
+       $(CP) $< /t
+       $(ASM) T:ClassLib.s TO T:temp.o $(O_SFLAGS)
+       $(HUNK2AOUT) /t/temp.o >NIL:
+       $(REN) obj.* $@
+       $(RM) /t/ClassLib.s /t/temp.o
+else
+       $(ASM) $< $(TO) $@ $(O_SFLAGS)
+endif
+
+
+###########################################################
+# build the demo program
+###########################################################
+#
+ifneq ($(strip $(DEMOPROG)),)
+
+$(DEMOPROG): $(DEMOOBJ_STATIC) $(DEMOOBJ) $(CLASSOBJ_STATIC) $(CLASSOBJ)
+       @$(ECHO) "Linking demo..."
+ifeq ($(strip $(NOSTDLIB)),0)
+       $(LD) $(TO) $@ $(DEMOOBJ_STATIC) $(DEMOOBJ) $(CLASSOBJ_STATIC) $(CLASSOBJ) $(O_LIBS) $(O_LFLAGS)
+else
+       $(LDNOLIB) $(TO) $@ $(APP_STARTUP) $(DEMOOBJ_STATIC) $(DEMOOBJ) $(CLASSOBJ_STATIC) $(CLASSOBJ) $(O_LIBS) $(O_LFLAGS)
+endif
+
+$(DEMOPROG_DEBUG): $(DEMOOBJ_DEBUG) $(DEMOOBJ) $(CLASSOBJ_DEBUG) $(CLASSOBJ)
+ifeq ($(strip $(NOSTDLIB)),0)
+       $(CC) $(TO) $@ $(DEMOOBJ_DEBUG) $(DEMOOBJ) $(CLASSOBJ_DEBUG) $(CLASSOBJ) $(D_LIBS) $(D_LFLAGS)
+else
+       $(LDNOLIB) $(TO) $@ $(APP_STARTUP) $(DEMOOBJ_DEBUG) $(DEMOOBJ) $(CLASSOBJ_DEBUG) $(CLASSOBJ) $(D_LIBS) $(D_LFLAGS)
+endif
+
+endif
+
+
+###########################################################
+# compile demo source
+###########################################################
+#
+
+# All objects depend on their headers
+$(DEMOOBJ_SHARED) $(DEMOOBJ_DEBUG) $(DEMOOBJ_STATIC): $(CLASSINC)
+
+$(DEMOOBJ_SHARED): %_shared.o : %.c
+       $(CC) $< $(TO) $@ $(O_CFLAGS) -DCLASS_FLAVOUR=3
+
+$(DEMOOBJ_STATIC): %_static.o : %.c
+       $(CC) $< $(TO) $@ $(O_CFLAGS) -DCLASS_FLAVOUR=0
+
+$(DEMOOBJ_DEBUG): %_dbg.o : %.c
+       $(CC) $< $(TO) $@ $(D_CFLAGS) -DCLASS_FLAVOUR=0
+
+
+###########################################################
+# GNU make quirks
+###########################################################
+
+# disable all implicit rules for suffixes known to GNU make such as .c and .o
+.SUFFIXES:
+
+# Explicitly declare standard targets as phony
+.PHONY: all lib debug private obj install clean help \
+       help-all help-lib help-debug help-private help-obj \
+       help-install help-clean
diff --git a/common/gst.c b/common/gst.c
new file mode 100644 (file)
index 0000000..ba73e8f
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+**     $Id:$
+**
+**     Copyright (C) 1997,99 by Bernardo Innocenti
+**
+**     This is a dummy source file used to make the Global Symbol Table
+**     for the boopsi classes.
+*/
+
+#include <exec/types.h>
+#include <exec/memory.h>
+#include <exec/execbase.h>
+#include <dos/dos.h>
+#include <graphics/gfxbase.h>
+#include <intuition/intuition.h>
+#include <intuition/intuitionbase.h>
+#include <intuition/screens.h>
+#include <intuition/classes.h>
+#include <intuition/classusr.h>
+#include <intuition/gadgetclass.h>
+#include <intuition/imageclass.h>
+#include <intuition/icclass.h>
+#include <graphics/gfxbase.h>
+#include <graphics/gfxmacros.h>
+#include <utility/tagitem.h>
+
+#include <proto/dos.h>
+#include <proto/exec.h>
+#include <proto/graphics.h>
+#include <proto/intuition.h>
+#include <proto/layers.h>
+#include <proto/utility.h>
+#include <clib/alib_stdio_protos.h>
+
+#include "CompilerSpecific.h"
+#include "DebugMacros.h"
+
+/* Can't include these in GST because they define some inline stuff
+ *
+ * #include "BoopsiStubs.h"
+ * #include "ListMacros.h"
+ */
+
+/* Can't include these because otherwise the GST would need to be
+ * remade each time you edit one of the headers.
+ *
+ * #define LV_GADTOOLS_STUFF
+ * #include "ScrollButtonClass.h"
+ * #include "ListViewClass.h"
+ * #include "VectorGlyphIClass.h"
+ * #include "ListBoxClass.h"
+ */
diff --git a/common/startup_gcc.s b/common/startup_gcc.s
new file mode 100644 (file)
index 0000000..ca4a05d
--- /dev/null
@@ -0,0 +1,2 @@
+.text
+       jmp     __main
diff --git a/common/startup_sc.s b/common/startup_sc.s
new file mode 100644 (file)
index 0000000..a37c6b0
--- /dev/null
@@ -0,0 +1,9 @@
+
+       XREF @_main
+
+       SECTION CODE,code
+
+_start:
+       jmp     @_main
+
+       END
diff --git a/common/startup_storm.s b/common/startup_storm.s
new file mode 100644 (file)
index 0000000..912a205
--- /dev/null
@@ -0,0 +1,9 @@
+
+       XREF __main
+
+       SECTION CODE,code
+
+_start:
+       bra     __main
+
+       END
diff --git a/config.mk b/config.mk
new file mode 100644 (file)
index 0000000..6156aa7
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,214 @@
+##
+## $Id: config.mk,v 1.1 2000/01/12 20:34:26 bernie Exp $
+##
+## Build environment configuration parameters
+## Copyright (C) 1999 by B. Innocenti & M. Cavalleri
+
+
+###########################################################
+# Package configuration
+###########################################################
+
+# CPU to compile for (eg: "68020").
+#
+CPU := 68020
+
+# Compiler to use. Possible options are:
+# sc     - SAS/C 6.58 or better
+# gcc    - gcc 2.7.2 or EGCS 1.1b
+# vbcc   - Not yet supported
+# stormc - Not yet supported
+#
+COMPILER := gcc
+
+# Additional include directories (e.g.: system headers)
+#
+C_INCLUDE_PATH := -I/gg/include -I/include
+
+# compiling options: set this variable to
+# 0 to link the demo programs with the standard
+# startup libraries, se to other values to
+# link to obj/app_startup.o
+#
+NOSTDLIB := 0
+
+
+###########################################################
+# Tools used in the Makefiles
+###########################################################
+
+NOP                    := echo
+ECHO           := echo
+FLUSHLIBS      := Avail FLUSH >NIL:
+MAKEINFO       := GG:bin/makeinfo
+FD2PRAGMA      := fd2pragma
+HUNK2AOUT      := hunk2aout
+FLEXCAT                := FlexCat
+ARCHIVER       := LZX -3 -e -r a
+
+
+###########################################################
+# Compiler, linker and assembler flags
+###########################################################
+
+# Flags for SAS/C
+#
+ifeq ($(strip $(COMPILER)),sc)
+
+       APP_STARTUP_SRC := $(TOP)/common/startup_sc.s
+
+       CC      := sc
+       AS      := PhxAss
+       ASM     := $(AS)
+       LD      := PhxLnk
+       LDNOLIB := PhxLnk
+       CP      := copy CLONE
+       REN := rename
+       RM      := delete
+
+       # GST usage must sometimes be disabled due to bugs in SAS/C 6.58
+       GST     := $(OBJDIR)/$(PROJNAME).gst
+
+       # Note: Using the "STRCONST" compiler option requires
+       #       patched versions of the OS headers to work correctly
+
+       # compiler flags
+       #
+       OPT_CFLAGS := OPTIMIZE OPTTIME OPTSCHEDULER OPTINLINELOCAL \
+                               OPTRDEPTH=4 OPTDEPTH=4 OPTCOMP=8 DATA=NEAR CODE=NEAR
+       DBG_CFLAGS := NOOPTIMIZE DEBUG=FULLFLUSH ONERROR=CONTINUE CODE=FAR \
+                               DATA=FAR DEF DEBUG=1
+       CMN_CFLAGS := PARAMS=REGISTERS STRMERGE AFP UTILLIB INCDIR=$(INCDIR) \
+                               NOSTKCHK NOCHKABORT NOICONS STRSECT=CODE GST $(GST) CPU=$(CPU)
+
+       # assembler flags
+       #
+       OPT_SFLAGS := SMALLDATA SMALLCODE ALIGN MACHINE=$(CPU) OPT !
+       DBG_SFLAGS := SYMDEBUG LINEDEBUG SET "_DEBUG=1"
+       CMN_SFLAGS := INCPATH=INCLUDE:,$(INCDIR) NOEXE QUIET
+
+       # linker flags
+       #
+       # Use the utility.library for 32bit multiplication and division.
+       #
+       # The C runtime library is never used but it's still needed because
+       # SAS/C sometimes generates code that referencess symbols such as
+       # _CXAMEMSET or _CXAMEMCPY
+       #
+       OPT_LFLAGS := NODEBUG
+       DBG_LFLAGS := NOSHORTRELOCS ADDSYM
+       CMN_LFLAGS := SMALLCODE SMALLDATA NOALVS NOICONS BATCH DEFINE \
+                               "__CXM33=__UCXM33,__CXD33=__UCXD33,__CXM22=__UCXM22,__CXD22=__UCXD22"
+
+       OPT_LIBS   :=
+       DBG_LIBS   := LIB:debug.lib LIB:small.lib
+       CMN_LIBS   := LIB:sc.lib
+
+       # misc flags
+       #
+       TO  := TO
+       OBJ := NOLINK
+       DEF := DEFINE
+
+       # Additional flags for SAS/C GST support
+       #
+       ifneq ($(strip $(GST)),)
+               CMN_CFLAGS += GST=$(GST)
+       endif
+endif
+
+#
+# Flags for gcc or egcs
+#
+ifeq ($(strip $(COMPILER)),gcc)
+
+       APP_STARTUP_SRC := $(TOP)/common/startup_gcc.s
+
+       CC      := gcc -c
+       AS      := as
+       ASM     := PhxAss
+       LD      := gcc -noixemul
+       LDNOLIB := gcc -nostartfiles -noixemul
+       CP      := cp
+       MV  := mv
+       REN := $(MV)
+       RM      := rm
+
+       # compiler flags
+       #
+       # if you have some custom include directory we suggest you to
+       # use the variable C_INCLUDE_PATH instead of adding it to
+       # the directories list below via tha argument -I
+       #
+       OPT_CFLAGS := -O2 -msmall-code -fomit-frame-pointer -mregparm -funroll-loops \
+                               -finline-functions -fno-implement-inlines
+       DBG_CFLAGS := -D_DEBUG=1 -g
+       CMN_CFLAGS := -m$(CPU) -Wundef -Wimplicit -Winline -Wreturn-type \
+                               -I$(TOP)/include/ $(C_INCLUDE_PATH)
+
+       # assembler flags (for PhxAss, not as. these are needed
+       # to compile the library startup code).
+       #
+       OPT_SFLAGS := SMALLDATA SMALLCODE ALIGN MACHINE=$(CPU) OPT !
+       DBG_SFLAGS := SYMDEBUG LINEDEBUG SET "DEBUG"
+       CMN_SFLAGS := INCPATH=INCLUDE:,$(INCDIR) NOEXE QUIET
+
+       # linker flags
+       #
+       OPT_LFLAGS := -s
+       DBG_LFLAGS :=
+       CMN_LFLAGS :=
+
+       OPT_LIBS   :=
+       DBG_LIBS   := -lamiga_debug
+       CMN_LIBS   :=
+
+       # misc flags
+       #
+       TO  := -o
+       OBJ := -c
+       DEF := -D
+endif
+
+
+
+###########################################################
+###########################################################
+# You shouldn't need to modify anything below this line
+###########################################################
+###########################################################
+
+
+
+# distribution version is compiled with these flags
+#
+O_CFLAGS := $(CMN_CFLAGS) $(OPT_CFLAGS)
+O_SFLAGS := $(CMN_SFLAGS) $(OPT_SFLAGS)
+O_LFLAGS := $(CMN_LFLAGS) $(OPT_LFLAGS)
+O_LIBS   := $(CMN_LIBS) $(OPT_LIBS)
+
+
+# debug version is compiled with these flags
+#
+D_CFLAGS := $(CMN_CFLAGS) $(DBG_CFLAGS)
+D_SFLAGS := $(CMN_SFLAGS) $(DBG_SFLAGS)
+D_LFLAGS := $(CMN_LFLAGS) $(DBG_LFLAGS)
+D_LIBS   := $(CMN_LIBS) $(DBG_LIBS)
+
+
+###########################################################
+# Paths
+###########################################################
+
+OBJDIR         := $(TOP)/obj
+INCDIR         := $(TOP)/include
+PREFIX         := $(TOP)/dist
+
+PROJNAME       := OpenBoopsi
+ARCNAME                := $(PREFIX)/$(PROJNAME).lzx
+SRCARCNAME     := $(PREFIX)/$(PROJNAME)_src.lzx
+
+# APP_STARTUP_SRC is defined in the
+# compiler specific sections
+#
+APP_STARTUP    := $(OBJDIR)/app_startup.o
diff --git a/docs/ExtFrameIClass.doc b/docs/ExtFrameIClass.doc
new file mode 100644 (file)
index 0000000..76523fb
--- /dev/null
@@ -0,0 +1,96 @@
+TABLE OF CONTENTS
+
+extframe.image/extframe.image
+\fextframe.image/extframe.image           extframe.image/extframe.image
+
+    NAME
+        extframe.image -- extended frame BBOPSI image (V1)
+
+    FUNCTION
+        The extframe class is a complete replacement of the built in
+        frameiclass provided by Intuition. It can render itself using the
+        pens specified by the application, or can use the standard DrawInfo
+        pens (like the original frameiclass) when the needed pens have not
+        been supplied. It offers some new frame type, like the well known
+        XEN frame. It offers an improved configurability (quite every aspect
+        of the frame can be configured), an extended IM_FRAMEBOX method and
+        some OS3 specific optimizations.
+        It is made to be as fast as possible and offer the possibility to
+        programmers to add new frame types with little or no changes at all
+        at the GM_RENDER method.
+        This class is a subclass of the imageclass, and support all the
+        attributes of the frameiclass class (at least it should), so you
+        must read their doc to learn the supported tags and their meaning.
+        Here I will list only the class specific tags or methods.
+
+    METHODS
+        IM_FRAMEBOX -- Ask the frame information on its size according to
+            its content. It is identical to the frameiclass method, but
+            offers a new flag beside the standard FRAMEF_SPECIFY. If you
+            specify the new FRAMEF_QUERY flag, you supply the FrameBox
+            (instead of the ContentsBox) and the frame will fill the
+            ContentsBox with the appropriate size/position value taking
+            into consideration the frame thickness, the space between the
+            frame and its contents, etc.
+
+    TAGS
+
+        IA_FrameDistance -- Space between the frame and the gadget in pixel.
+            Negative values are allowed.
+
+            Applicability is (ISG)
+
+        IA_InnerDistance -- Space between the two 'side' of a frame. For
+            example a inner distance of 0 pixel will make a FRAME_ICONDROPBOX
+            frame look like a FRAME_RIDGE frame. Currently this tag has
+            effect only on the FRAME_ICONDROPBOX frame.
+
+            Applicability is (ISG)
+
+        IA_PenBackground -- Specify the background pen. Used only if
+            you did not specify the IA_EdgesOnly attribute. If this
+            tag is not specified or is set to -1 the standard BACKGROUNDPEN
+            pen will be used.
+
+            Applicability is (ISG)
+
+        IA_PenSelBackground -- Specify the pen used to fill the frame
+            when it's in the selected state. Ignored if the frame is a
+            IA_EdgesOnly one. If it is not specified or is set to -1
+            the FILLPEN pen will be used.
+
+            Applicability is (ISG)
+
+        IA_PenShine -- Specify the pen number used for the shine pen (i.e.
+            for raised borders). If it is not specified or set to -1 the
+            standard SHINEPEN will be used.
+
+            Applicability is (ISG)
+
+        IA_PenShadow -- Specify the pen number used for the shadow pen.
+            (i.e. for recessed borders). If it is not specified or set to -1
+            the standard SHADOWPEN will be used.
+
+            Applicability is (ISG)
+
+        IA_PenHalfShine -- Specify the halfshine pen. If it is not specified
+            or set to -1 the standard SHINEPEN will be used. It's usually a
+            bit darker than the shine pen and is commonly used in the xen
+            frame.
+
+            Applicability is (ISG)
+
+        IA_PenHalfShadow -- Specify the halfshadow pen. If it is not
+            specified or set to -1 the standard SHADOWPEN will be used. It's
+            usually a bit brighter than the shadow pen and is commonly used
+            for the xen frame.
+
+            Applicability is (ISG)
+
+    BUGS
+        The tags supported by the frameiclass are not very well documented,
+        so this class may not support all the needed tags.
+
+    SEE ALSO
+        The "Image Subclasses" chapter of the Intuition Reference Manual.
+
diff --git a/docs/Intuition b/docs/Intuition
new file mode 100644 (file)
index 0000000..db7096a
--- /dev/null
@@ -0,0 +1,523 @@
+
+INDICE
+
+Introduzione
+   Come si comporta Intuition
+   Come si comporta MUI
+
+Problemi
+   Problema n. 1 (input.device bloccato; Intuition)
+   Problema n. 2 (refresh della GUI bloccato; MUI)
+
+Ipotesi di soluzioni
+   Possibile soluzione per il problema n. 1 (sotto-task IDCMP in parallelo)
+   Possibile soluzione per il problema n. 2 (classe MUI con task asincrono)
+
+Considerazioni finali
+   Funzionamento seriale dell'input.device
+   Fattibilità di un patch per Intuition
+
+
+INTRODUZIONE
+
+Come si comporta Intuition
+--------------------------
+
+L'input dell'utente viene letto, comunicando direttamente con l'hardware,
+da keyboard.device e gameport.device, che lo trasmettono all'input.device.
+
+Quest'ultimo unisce le diverse informazioni sull'input in un unico stream
+(flusso) di "eventi di input", omogeneo e sequenziale, rappresentato con
+una lista di strutture InputEvent.
+
+L'input.device passa poi tale lista ad una serie di "input handler", secondo
+un ordine basato sulla loro priorità.
+
+Ciascun handler può intraprendere delle azioni in base al contenuto della
+lista ricevuta in ingresso; può inoltre apporre modifiche alla lista stessa
+aggiungendo, rimuovendo o alterando eventi. In uscita l'handler restituisce
+la lista modificata, la quale diventerà l'ingresso per l'handler successivo.
+
+Il codice di un handler viene eseguito come una normale chiamata a funzione:
+di conseguenza esso gira sempre nel contesto del task dell'input.device.
+
+Normalmente l'handler con priorità massima è quello della commodities.library
+(che quindi intercetta gli eventi prima di chiunque altro), tranne che nel
+caso in cui essa non sia stata ancora aperta.
+
+L'handler immediatamente successivo è tipicamente quello di Intuition; la
+sua funzione è "interpretare", ed eventualmente filtrare, tutti quegli eventi
+che possono influire sullo stato della GUI o sulle attività dei programmi che
+ne fanno uso.
+
+Questo handler fondamentalmente si limita a chiamare la funzione Intuition()
+della intuition.library, passandole come unico argomento il puntatore alla
+lista di eventi ricevuto in ingresso.
+
+Intuition() verifica la natura di ogni singolo evento e, se necessario,
+reagisce ad esso eseguendo opportune azioni, come ad esempio attivare una
+finestra, modificare l'aspetto e lo stato di un gadget o inviare un messaggio
+ad un applicativo.
+
+La casistica più comune comprende:
+
+· Refresh automatico di una componente della GUI (bordo di una finestra,
+  gadget non BOOPSI, barra del titolo di uno schermo).
+
+· Visualizzazione di menu
+
+· Invocazione dell'opportuno metodo di un gadget BOOPSI
+
+· Nessuna azione visibile (es.: click all'interno di una finestra, ma non
+  su un gadget)
+
+A ciascuna di queste azioni può essere associato l'invio di un particolare
+messaggio, rappresentato da una struttura IntuiMessage, all'applicativo
+"proprietario" della finestra correntemente attiva (normalmente il task che
+l'ha aperta).
+
+Gli IntuiMessage hanno spesso una corrispondenza stretta con gli InputEvent
+da cui derivano.
+
+Al termine di queste azioni gli eventi che non possono essere utili agli
+handler successivi vengono rimossi dalla lista, altri (generalmente utili
+a console.device) vengono aggiunti, e la funzione Intuition() termina
+restituendo all'handler il puntatore alla nuova lista.
+
+Infine l'handler di Intuition restituisce a sua volta il puntatore
+all'input.device che può quindi passarlo all'handler successivo, solitamente
+quello di console.device.
+                                                               _ _ _ _ _ _
+                                                              | \ \ \ \ \ |
+                                                              |Applicativo|
+                                                              |_\_\_\_\_\_|
+                               InputEvent stream                   /|\
+                             .····················.            _ _ _|_ _ __
+                             :                    :           |            |
+ _ _ _ _ _ _ _ _        _ _ _: _ _ _     _ __ _ _ V _ _ __ _  |Messaggio ad|
+|               |      |            |   |                   | |applicativo |
+|keyboard.device|-.    |            |==>|Input handler (CX) | |__ _ _ _ _ _|
+|_ _ _ _ _ _ _ _|  \   |            |   |_ _ _ _ _ _ _ _ _ _|      /\
+ _ _ _ _ _ _ _ _    \  |            |    _ __ _ _ V _ _ __ _     _ ||_ _ _ _
+|               |    \ |input.device|   |                   |   |           |
+|gameport.device|----->|    task    |==>|Input handler (I)  |==>|Intuition()|
+|_ _ _ _ _ _ _ _|    / |            |   |_ _ _ _ _ _ _ _ _ _|   |_ _ _ _ _ _|
+ _ _ _ _ _ _ _ _    /  |            |    _ __ _ _ V _ _ __ _       ||
+|               |  /   |            |   |                   |  _ _ \/_ _
+| timer.device  |-'    |            |==>|Input handler (c.d)| |         |
+|_ _ _ _ _ _ _ _|      |__ _ _ _ _ _|   |_ _ _ _ _ _ _ _ _ _| |Refresh  |
+                                                  :           |della GUI|
+                                                 _:           |_ _ _ _ _|
+                                                / \_/              ||
+                                                  :        _ _ _ _ \/_ _ _ _
+                                                  :       |                 |
+                                                  V       |Esecuzione metodo|
+                                                /////     |_ _ _ _ _ _ _ _ _|
+
+Questo meccanismo comporta che l'unico codice eseguito nel contesto di un
+applicativo che faccia uso di Intuition è quello che si occupa di rispondere
+agli IntuiMessage ed intraprendere eventuali azioni in base al loro contenuto
+(generalmente finalizzate a svolgere il compito richiesto dall'utente più
+che a gestire l'interfaccia; tuttavia nel caso di GadTools alcune operazioni
+di gestione della GUI vengono affidate al task dell'applicativo mediante le
+funzioni GT_GetIMsg() e GT_ReplyIMsg()).
+
+In particolare, nel caso dei gadget BOOPSI, la risposta della GUI risulta
+sempre immediata e "in tempo reale", indipendentemente dalle operazioni che
+l'applicativo sta svolgendo nello stesso momento, poiché di fatto se ne
+occupa asincronamente un altro task (quello dell'input.device).
+
+
+Come si comporta MUI
+--------------------
+
+La differenza fondamentale tra MUI ed Intuition è che i suoi gadget, pur
+essendo oggetti BOOPSI, sono sottoclassi di "rootclass" piuttosto che di
+"gadgetclass". Di conseguenza non sono riconosciuti da Intuition come dei
+normali gadget e non vengono quindi gestiti automaticamente dalla funzione
+Intuition(). Tutto ciò che quest'ultima rileva quando l'utente interagisce
+con i gadget MUI è una serie di eventi IDCMP_MOUSEBUTTONS e IDCMP_MOUSEMOVE
+che essa trasmette, invariati, al task proprietario della finestra.
+
+Quest'ultimo tipicamente si trova in uno stato di attesa degli eventi.
+Per ogni evento ricevuto chiama una funzione MUI di gestione dell'input
+(per l'esattezza invoca un metodo di una particolare classe) la quale
+è poi in grado di determinare a quale gadget MUI va passato, mediante
+l'invocazione di un suo opportuno metodo, l'evento appena letto.
+                                                        _ _ _ _ _ _ _ _ _ __
+                                                       | \ \ \ \ \ \ \ \ \ \|
+                                                       |Operazioni programma|
+                                                       |_\_\_\_\_\_\_\_\_\_\|
+                 _ _ _ _ _ _ _ _ __                               /\
+ _ _ _ _ _ _    |                  |      _ _ _ _ _ _      _ _ _ _|| _ _ _ _
+|           |   |Semplice messaggio|     | \ \ \ \ \ |    | \ \ \ \ \ \ \ \ |
+|Intuition()|==>| all'applicativo  |---->|Applicativo|    |Refresh della GUI|
+|_ _ _ _ _ _|   | (IDCMP_MOUSE#?)  |     |_\_\_\_\_\_|    |_\_\_\_\_\_\_\_\_|
+                |__ _ _ _ _ _ _ _ _|          ||                  /\
+                                        _ _ _ \/_ _ __     _ _ _ _|| _ _ _ _
+                                       | \ \ \ \ \ \ \|   | \ \ \ \ \ \ \ \ |
+                                       |Gestione input|==>|Esecuzione metodo|
+                                       |_\_\_\_\_\_\_\|   |_\_\_\_\_\_\_\_\_|
+
+Con MUI, di conseguenza, la risposta della GUI (almeno con le classi di
+gadget standard fornite con il pacchetto) risulta sincrona alle operazioni
+del task dell'applicativo, in quanto avviene nel suo stesso contesto.
+
+
+PROBLEMI
+
+Problema n. 1 (input.device bloccato; Intuition)
+------------------------------------------------
+
+La gestione dell'input secondo il modello di Intuition, cioè tramite un
+handler, ha il vantaggio di garantire pressoché sempre una risposta immediata
+da parte della GUI, anche nel caso in cui l'applicativo non fosse pronto a
+reagire all'input ricevuto.
+
+Tuttavia, poiché l'handler di Intuition opera sempre, come già illustrato,
+nel contesto dell'input.device, qualsiasi trasmissione di eventi di input
+nel sistema risulta completamente bloccata fino alla conclusione delle sue
+operazioni.
+
+Il problema si manifesta in modo evidente soprattutto con i gadget BOOPSI;
+infatti l'handler invoca opportuni metodi di tali gadget quando l'input
+dell'utente è diretto a loro o comporta una modifica del loro stato. Nel
+caso di metodi particolarmente complessi, come quelli dei gadget associati
+ai DataType, il tempo di esecuzione può essere elevato, e di conseguenza
+la trasmissione dell'input nell'intero sistema può subire una sospensione di
+durata non trascurabile.
+
+Il sintomo più tipico è il blocco del puntatore del mouse, ma anche altre
+attività del sistema operativo che si basino su una regolare ricezione di
+eventi di input (ad esempio quelli generati dal timer.device) rischiano di
+venire ostacolate con conseguenze imprevedibili.
+
+
+Problema n. 2 (refresh della GUI bloccato; MUI)
+-----------------------------------------------
+
+Il sistema MUI presenta un problema per certi versi opposto, ma ugualmente
+fastidioso per l'utente. Con i gadget di MUI le operazioni svolte nel
+contesto del task dell'input.device sono sempre molto limitate e di breve
+durata; si tratta per lo più di comunicare una serie di click dei pulsanti
+del mouse (e spostamenti di quest'ultimo) all'applicativo associato al canale
+IDCMP della finestra attiva.
+
+Ogni operazione relativa alla GUI viene poi effettuata nel contesto del task
+che riceve i messaggi in questione. Nel caso esso sia occupato in qualche
+elaborazione e quindi non si trovi in attesa di segnali dalla porta IDCMP,
+come è facile immaginare, nessun tipo di aggiornamento della GUI verrà
+attuato fino alla conclusione delle operazioni.
+
+L'utente, in questo caso, non osservando alcuna risposta dai gadget con cui
+tenta di interagire, può avere l'impressione che l'intero programma sia
+bloccato e non semplicemente impegnato in altre attività.
+
+Nel caso migliore l'utente si fa una cattiva opinione del programma; inoltre
+l'incertezza sul da farsi può portare l'utente a danneggiare inavvertitamente
+il lavoro che sta svolgendo mediante l'applicativo. Ad esempio, un tentativo
+di selezione ripetuta dello stesso gadget (nella speranza di una ripresa del
+funzionamento del programma) può provocare effetti collaterali indesiderati
+nell'istante in cui l'applicativo ricomincia a gestire l'input e si occupa di
+quello nel frattempo ricevuto.
+
+
+IPOTESI DI SOLUZIONI
+
+Possibile soluzione per il problema n. 1 (sotto-task IDCMP in parallelo)
+------------------------------------------------------------------------
+
+Una possibile soluzione al primo problema, attuabile al livello di Intuition,
+è la gestione multithreaded degli eventi di input trasmessi al suo handler
+dall'input.device, che in tal modo si libererebbe molto rapidamente dopo
+la chiamata alla funzione Intuition(). Come realizzare questo nella pratica?
+
+Un'idea potrebbe essere associare ad ogni schermo e finestra un task
+dedicato, creato all'apertura ed eliminato alla chiusura. Ciascuno di questi
+task riceverebbe solo gli eventi che lo riguardano e potrebbe gestirli del
+tutto asincronamente al resto del sistema e in particolare all'input.device.
+
+Per rendere possibile ciò sarebbe necessario apportare alcune modifiche alla
+funzione Intuition():
+
+1. Essa dovrebbe, preliminarmente, esaminare la lista di InputEvent ricevuta
+   dall'input.device e in base ad essa costruire una seconda lista privata
+   copiando tutti gli eventi che riguardano la GUI.
+
+2. Fatto ciò, tutti gli eventi che non devono essere rilevati dagli altri
+   handler dovrebbero essere rimossi dalla lista principale.
+
+3. La lista privata di eventi dovrebbe poi essere separata in sottoliste,
+   ognuna indirizzata all'opportuna finestra (o all'opportuno schermo).
+
+4. Ogni sottolista dovrebbe essere spedita, tramite un opportuno messaggio,
+   al task associato alla finestra (o schermo) destinazione.
+
+5. A questo punto Intuition() potrebbe terminare subito la sua esecuzione,
+   senza aspettare alcuna risposta dai task a cui ha spedito i messaggi;
+   in questo modo l'input.device sarebbe subito libero di proseguire la
+   sua attività.
+
+Una possibile variante è il passaggio (subito dopo il punto 2) della lista
+privata ad un task "intermedio" che si occupi di eseguire le operazioni
+rimanenti; in questo caso Intuition() potrebbe terminare prima del punto 3.
+Questo ipotetico task intermedio potrebbe ad esempio venire creato in modo
+automatico all'apertura della intuition.library.
+
+Occorre prestare molta attenzione all'implementazione del punto 2, in quanto
+è necessario stabilire delle regole precise in base alle quali si possa
+determinare IN ANTICIPO e senza ambiguità se un evento riguarda o no la GUI e
+se esso debba venire trasmesso o no ai successivi handler. Questo potrebbe,
+oggettivamente, non essere un compito facile.
+
+In particolare, se un evento deve essere modificato da Intuition e poi
+trasmesso al resto degli handler, bisogna che:
+
+- Si sappia già come modificarlo PRIMA di passarlo al task destinatario,
+  per non dover aspettare che questo termini fornendo informazioni sulla
+  modifica da effettuare (soluzione non sempre applicabile), oppure
+
+- Il task destinatario, dopo avere modificato personalmente l'evento, si
+  occupi di REINSERIRLO nella catena di InputEvent a monte dell'handler di
+  Intuition. Questo si può realizzare ad esempio aggiungendolo ad una coda
+  di eventi globale periodicamente letta dall'handler, oppure sfruttando
+  la commodities.library. Non basta chiamare direttamente Intuition() con
+  l'evento in questione poiché si rimarrebbe nel contesto del task.
+  Le regole per realizzare ciò potrebbero risultare abbastanza complicate.
+
+Ammettendo di risolvere questi problemi, restano da esaminare i compiti dei
+singoli task destinatari.
+
+Quelli associati alle finestre svolgerebbero esattamente gli stessi compiti
+che attualmente sono svolti dalla funzione Intuition(), cioè aggiornare lo
+stato della GUI, invocare opportuni metodi di eventuali gadget BOOPSI
+selezionati e spedire IntuiMessage all'applicativo che "possiede" la finestra
+(con l'unica avvertenza, ovvia del resto, di assicurarsi che il task sia
+completamente rientrante ed esegua una corretta arbitrazione sui dati globali
+di IntuitionBase).
+
+I task associati agli schermi non dovrebbero fare altro che gestire la
+selezione degli unici due possibili gadget, quello di trascinamento e quello
+di profondità, nonché la disattivazione di tutte le finestre in caso di
+click del mouse su una zona libera dello schermo, l'autoscrolling e lo screen
+dragging tramite Amiga sinistro + mouse.
+
+Si potrebbe anche pensare ad un ulteriore task indipendente che gestisca
+semplicemente il movimento del puntatore, ma questo è un compito talmente
+banale che potrebbe essere svolto direttamente (e più rapidamente) dalla
+funzione Intuition().
+                                                           _ _ _ _ _ _
+                                                          | \ \ \ \ \ |
+                                                          |Applicativo|
+                                                          |_\_\_\_\_\_|
+        InputEvent stream                                      /|\
+      .····················.                                    |
+      :                    :                             ::::::::::::::::
+ _ _ _: _ _ _     _ __ _ _ V _ _ __ _                    ::Messaggio ad::
+|            |   |                   |                   ::applicativo:::
+|            |==>|Input handler (CX) |                   ::::::::::::::::
+|            |   |_ _ _ _ _ _ _ _ _ _|                          /\
+|            |    _ __ _ _ V _ _ __ _     _ _ _ _ _ _           ||
+|input.device|   |                   |   |           |   :::::::::::::::::
+|    task    |==>|Input handler (I)  |==>|Intuition()|-->::Task finestra::
+|            |   |_ _ _ _ _ _ _ _ _ _|   |_ _ _ _ _ _|   :::::::::::::::::
+|            |    _ __ _ _ V _ _ __ _                           ||
+|            |   |                   |                          \/
+|            |==>|Input handler (c.d)|                     :::::::::::::
+|__ _ _ _ _ _|   |_ _ _ _ _ _ _ _ _ _|                     ::Refresh::::
+                           :                               ::della GUI::
+                          _:                               :::::::::::::
+                         / \_/                                  ||
+                           :                                    \/
+                           :                           :::::::::::::::::::::
+                           V                           ::Esecuzione metodo::
+                         /////                         :::::::::::::::::::::
+
+
+Per concludere, si può osservare che in realtà sarebbe abbastanza raro che
+la stessa lista contenga eventi indirizzati a finestre diverse; l'utente,
+in circostanze normali, non dovrebbe essere più veloce dell'elaborazione
+dell'input da parte del sistema. Un'eccezione sono gli eventi di timer (che
+non sono generati dall'utente e sono sempre diretti a tutte le finestre).
+
+Alla luce di questa considerazione si potrebbe pensare di lasciare al
+programmatore (o addirittura all'utente) la facoltà di decidere quali
+finestre debbano essere dotate di un task IDCMP dedicato, e quali invece
+possano semplicemente appoggiarsi ad un task IDCMP globale.
+
+
+Possibile soluzione per il problema n. 2 (classe MUI con task asincrono)
+------------------------------------------------------------------------
+
+Il problema di MUI potrebbe risolversi molto facilmente con una riscrittura
+delle classi di base del sistema.
+
+L'idea è che il dispatcher di un gadget MUI non dovrebbe eseguire in sequenza
+(e nel contesto dell'applicativo) il refresh della GUI e poi la chiamata alla
+funzione di callback associata al gadget.
+
+Ogni istanza di una classe di gadget MUI dovrebbe avere un proprio task
+privato a cui affidare la chiamata alla funzione di callback, rendendola
+così asincrona rispetto al refresh grafico. Tale task potrebbe essere creato
+dal metodo OM_NEW della classe e distrutto in seguito da OM_DISPOSE.
+Eventuali valori di ritorno diretti all'applicativo potrebbero essere
+comunicati mediante messaggi (gestiti automaticamente dal task del gadget).
+
+Il refresh grafico dei gadget, invece, essendo generalmente un'operazione
+abbastanza rapida, potrebbe essere affidato ad un unico task globale; una
+possibilità sarebbe quella di creare questo task all'apertura della libreria
+MUIMaster.
+
+                               ::::::::::::::::::::::::   :::::::::::::::::::
+                               ::Operazioni programma::<==::Task del gadget::
+                               ::::::::::::::::::::::::   :::::::::::::::::::
+                 _ _ _ _ _ _ _ _ __                               /|\
+ _ _ _ _ _ _    |                  |    _ _ _   _ _ __     _ _ _ _ | _ _ _ _
+|           |   |Semplice messaggio|   | \ \ \ \ \ \ \|   | \ \ \ \ \ \ \ \ |
+|Intuition()|==>| all'applicativo  |-+ |Gestione input|==>|Esecuzione metodo|
+|_ _ _ _ _ _|   | (IDCMP_MOUSE#?)  | | |_\_\_\_\_\_\_\|   |_\_\_\_\_\_\_\_\_|
+                |__ _ _ _ _ _ _ _ _| |        /\                   |
+                                     |    _ _ ||_ _ _             \|/
+                                     |   | \ \ \ \ \ |  ;;;;;;;;;;;;;;;;;;;;;
+                                     +-->|Applicativo|  ;;Refresh della GUI;;
+                                         |_\_\_\_\_\_|  ;;;;;;;;;;;;;;;;;;;;;
+
+Se le operazioni di refresh non necessitano di restituire alcun valore al
+dispatcher dell'oggetto (oppure alla funzione di MUI che gestisce l'input)
+quest'ultimo verrebbe disimpegnato immediatamente; in caso contrario sarebbe
+sufficiente che il task preposto al refresh svolgesse tutte le sue operazioni
+prima di rispondere al dispatcher con ReplyMsg(), e copiasse il valore di
+ritorno in un apposito campo del messaggio "restituito" al mittente.
+
+Questo ovviamente terrebbe impegnato il task dell'applicativo per un tempo
+leggermente superiore, ma le operazioni di refresh grafico puro e semplice
+non dovrebbero, nella maggior parte dei casi, richiedere più di qualche
+millisecondo (come del resto accade per Intuition nel caso di gadget non
+troppo complicati). Chiaramente se tale comportamento fosse richiesto per
+TUTTI i gadget sarebbe più conveniente evitare del tutto l'uso di un task
+separato per il refresh, mantenendo però quello dedicato al callback.
+
+Sarebbero possibili diverse variazioni sul tema: ad esempio, l'ordine in cui
+vengono "richiamati" i due task potrebbe influenzare, sia pure di poco, la
+prontezza del refresh e quindi della risposta all'utente da parte della GUI;
+oppure in certi casi potrebbe essere conveniente creare "al volo" il task
+preposto al callback in modo che l'applicativo possa eseguire più volte
+contemporaneamente la stessa funzione (se la sua natura lo consente).
+
+In ogni caso questa soluzione eliminerebbe alla radice il problema del
+"blocco" apparente della risposta della GUI durante un'intensa attività
+dell'applicativo, senza per questo tornare al modello "mono-threaded"
+adottato attualmente da Intuition. L'alternativa proposta per quest'ultimo
+(vedi paragrafo precedente) risolverebbe in teoria anche il problema di MUI,
+ma solo se i gadget MUI fossero istanze di "gadgetclass", cosa che al
+momento, in pratica, non accade.
+
+
+CONSIDERAZIONI FINALI
+
+Funzionamento seriale dell'input.device
+---------------------------------------
+
+Come già detto, l'input.device propaga lo stream di eventi di input
+attraverso i vari handler in cascata. Gli handler non sono altro che
+funzioni chiamate, una dopo l'altra, dal task dell'input.device; ogni
+handler riceve in ingresso il valore di uscita del precedente (e cioè
+il puntatore ad una particolare lista di InputEvent).
+
+Questo è un meccanismo intrinsecamente seriale; inoltre esso implica che gli
+handler di priorità più alta (come quello di Intuition) debbano esaminare,
+tra gli altri, molti eventi che non li riguardano direttamente e che vanno
+semplicemente trasmessi agli handler successivi.
+
+Come aumentare il parallelismo nelle operazioni dell'input.device?
+
+Ingenuamente si potrebbe pensare di avere diverse code, ognuna gestita da
+un task indipendente, tra cui suddividere gli eventi in base al tipo degli
+handler che tali code alimentano; ovviamente occorrerebbe che gli handler
+di una coda non abbiano mai la necessità di trasmettere eventi a quelli di
+un'altra coda.
+
+Purtroppo in pratica questo non è una soluzione, dato che porterebbe a
+ricreare la situazione corrente. Infatti normalmente gli handler esistenti
+nel sistema sono pochi (per lo più Commodities, Intuition e console.device)
+ed è difficile individuare tipi di eventi che non interessino, anche solo
+potenzialmente, almeno due di essi.
+
+In particolare, la quasi totalità degli eventi di input generati dall'utente
+interessa in qualche modo Intuition, che viene ad essere un vero e proprio
+"collo di bottiglia".
+
+Più vantaggioso risulterebbe applicare questa soluzione al livello dei
+singoli handler; ciascuno di essi, a meno che il suo funzionamento sia
+molto semplice, potrebbe gestire l'input in arrivo ripartendolo in modo
+opportuno tra diversi task paralleli (come illustrato riguardo al problema
+di Intuition).
+
+In questo modo sorgerebbe però il problema della trasmissione del flusso
+di input lungo la catena di handler. Ognuno di essi potrebbe trasmettere
+al successivo solo gli eventi che può scartare immediatamente come "non
+rilevanti" per le sue attività, ma gli sarebbe preclusa la possibilità di
+modificare gli eventi utilizzati, o crearne di nuovi in base ai risultati
+della loro elaborazione, e reinserirli nella posizione originale: questa
+operazione generalmente richiederebbe la conoscenza di dati non disponibili
+a priori e, d'altra parte, una sincronizzazione con i suoi task per ottenere
+da essi tali dati annullerebbe ovviamente tutti i vantaggi del parallelismo.
+
+E` dunque necessario, volendo impiegare un simile modello:
+
+· Stabilire delle regole estremamente precise per decidere quando l'ordine
+  relativo di due eventi vada mantenuto oppure sia ininfluente;
+
+· Limitare al massimo la necessità di trasmettere informazioni tra un
+  handler e il successivo (nel caso di entità strettamente connesse, come
+  Intuition e console.device, questo potrebbe rivelarsi un problema serio);
+
+· Assicurarsi, ad esempio mediante semafori, che venga sempre mantenuta la
+  consistenza interna dei valori che definiscono lo stato di un handler (e
+  quindi dell'entità software che si appoggia ad esso) anche in presenza
+  di numerosi sotto-task che possono accedere a tali valori.
+
+Ad esempio, se l'utente attiva una finestra di Shell e poi preme un tasto
+sulla tastiera, è necessario che il console.device riceva l'evento "finestra
+attiva" PRIMA di quello "tasto premuto", nonostante il fatto che il secondo
+potrebbe venire trasmesso immediatamente mentre il primo richiederebbe una
+più lunga elaborazione da parte di Intuition.
+
+Ancora, quando l'utente trascina un gadget proporzionale sarebbe auspicabile
+che la posizione relativa di knob e puntatore del mouse rimanesse costante,
+anche se gli spostamenti dei due oggetti vengono gestiti da due diversi task.
+
+La trasmissione di eventi agli handler successivi potrebbe ad esempio essere
+implementata mediante una coda ausiliaria globale svuotata periodicamente,
+accessibile a tutti i sotto-task di un handler, purché si presti la massima
+attenzione a mantenerla sempre perfettamente ordinata e ad inserire in essa
+anche quegli eventi (e solo quelli) che, pur non dovendo essere elaborati
+dall'handler corrente, possono risentire dell'ordine in cui vengono trasmessi
+rispetto a certi altri eventi.
+
+Queste considerazioni non pretendono certamente di costituire una soluzione
+completa, preferibile ad altre o facilmente realizzabile; si tratta solo di
+riflessioni su alcuni aspetti del problema di cui probabilmente converrebbe
+tenere conto.
+
+
+Fattibilità di un patch per Intuition
+-------------------------------------
+
+E` possibile, tramite un patch, applicare all'attuale versione di Intuition
+i criteri appena esposti?
+
+Probabilmente no.
+
+L'idea sarebbe di applicare un patch alla funzione Intuition() per forzare
+il passaggio dell'input ad un task separato che se ne occupi, permettendo
+all'handler di Intuition di terminare subito la sua esecuzione. Ma anche
+immaginando di risolvere quasi tutti i problemi prima descritti, resta sempre
+l'impossibilità di garantire la consistenza dei dati PRIVATI contenuti nella
+struttura IntuitionBase, a cui sarebbe facile, in questo modo, effettuare
+un accesso asincrono.
+
+Non è escluso che si possa trovare una soluzione anche a questo problema, ma
+con ogni probabilità a questo punto conviene aspettare che sia Amiga Inc. ad
+implementare nel sistema operativo una gestione dell'input più efficiente.
+
diff --git a/docs/LabelTextIClass.doc b/docs/LabelTextIClass.doc
new file mode 100755 (executable)
index 0000000..669699c
--- /dev/null
@@ -0,0 +1,181 @@
+TABLE OF CONTENTS
+
+labeltext.image/labeltext.image
+\flabeltext.image/labeltext.image           labeltext.image/labeltext.image
+
+    NAME
+        labeltext.image -- label BBOPSI image (V1)
+
+    FUNCTION
+        The labeltext class is a little BOOPSI class that provides a complete
+        label handling. A label can be placed inside or outside a gadget
+        (above, below, left, right or inside) with different alignments
+        (centered, left aligned, right aligned, etc.) with a couple of
+        different rendering methods (normal, hilighted, 3d). It is able to
+        use the standard DrawInfo pens when no custom pens are supplied and
+        the system default font when no custom font is supplied.
+        It supports various image states (i.e. normal, selected, disabled,
+        etc.) This class has also a nice API for easy layer clipping when it
+        is necessary.
+
+    METHODS
+        IM_DRAW -- Tell the class to render itself. Before calling this
+            method for the first time you should first invoke the IM_FRAMEBOX
+            method and then the IM_DRAW one using the FrameBox Left and Top
+            field or the Image LeftEdge and TopEdge fields. Note that these
+            coordinates are NOT exactly the ones the label will use, i.e.
+            if you do a Text() call using that coordinates you'll get a text
+            slightly shifted. In other words if you calculate yourself the
+            coordinates of the label you won't be able to draw it in the right
+            place. This is required for an easy layer clipping support.
+            NB: You must alway supply the imp_Offset coordinates, even if the
+            Image->LeftEdge and Image->TopEdge field already contain the right
+            values.
+
+        IM_FRAMEBOX -- This method tell the class to calculate its position
+            relative to the ContentsBox coordinates and according to its
+            alignment attributes. For this reason the ContenstBox is usually
+            filled with the gadgets coordinates. Moreover the FrameBox structure
+            is filled with the coordinates of the rectangle required by the
+            label to draw itself. In this way the gadget can easily perform
+            the correct layer clipping if necessary. Example:
+
+
+            struct IBox cbox, fbox;
+            struct Rectangle rect;
+
+            DoMethod((Object *)gad->GadgetText, IM_FRAMEBOX, (ULONG)&(cbox), (ULONG)&fbox, NULL);
+
+            rect.MinX = MAX(msg->gpr_GInfo->gi_Window->BorderLeft, fbox.Left);
+            rect.MinY = MAX(msg->gpr_GInfo->gi_Window->BorderTop, fbox.Top);
+            rect.MaxX = MIN(msg->gpr_GInfo->gi_Window->Width - msg->gpr_GInfo->gi_Window->BorderRight,
+                            fbox.Left + fbox.Width - 1);
+            rect.MaxY = MIN(msg->gpr_GInfo->gi_Window->Height - msg->gpr_GInfo->gi_Window->BorderBottom,
+                            fbox.Top + fbox.Height - 1);
+
+            OrRectRegion(new_region, &rect);
+            old_region = InstallClipRegion(msg->gpr_GInfo->gi_Layer, new_region);
+
+
+            The same rectangle is then usually used to tell the label to draw
+            or erase itself (using the Left and Top field) but it is not
+            required, since the class modifies its coordinates with the values
+            it calculated the last time IM_FRAMEBOX was invoked (i.e. you may
+            alternatively supply the img->LeftEdge and img->TopEdge values).
+
+            If you specify the FRAMEF_SPECIFY flag the label will check if it
+            fit in the ContentsBox rectangle. If not the FrameBox Left and
+            Top fields will be set to -1. You can blindly pass this values
+            to the IM_DRAW or IM_ERASE method, since the class knows that it
+            has not to render itself. The FrameBox Width and Height fields
+            are set to an arbitrary value to avoid problems if the gadget do
+            layer clipping without checking the result of the IM_FRAMEBOX
+            method.
+
+            NOTE: If you change some attributes (e.g. the font) you should call
+            the IM_FRAMEBOX method again, so that the label can recalculate
+            its position and size.
+
+    TAGS
+
+        ELA_Label -- Pointer to the string the class will print. You MUST
+            supply this tag when you create an object. The label will not
+            deallocate the string when disposed.
+
+            Applicability is (ISG)
+
+        ELA_LabelType -- Specify the way the label should render itself. See
+            the include file for the currently supported values.
+
+            Applicability is (ISG)
+
+        ELA_Distance -- The distance from the gadget borders. Note that the
+            label does not know about frame thickness. e.g. if you draw a
+            label inside a gadget, left aligned with a distance of 0 pixel,
+            the label will likely draw itself over the left border of the
+            frame. If you want to avoid this you must set this attribute to
+            a value at least equal to the frame thickness.
+
+            Applicability is (ISG)
+
+        ELA_Position -- Tell the label where it should draw itself, i.e.
+            above the gadget, below, to the left, etc. Note that 'gadget'
+            is somewhat misleading, since the label calculates its position
+            relative to any rectangle. i.e. when you call the IM_FRAMEBOX
+            method, you usually pass as the ContentsBox the gadget
+            coordinates, but if you want you could, e.g. tell the label
+            to center itself in the window titlebar.
+            See the include file for the values of this tag.
+
+            Applicability is (ISG)
+
+        ELA_Align -- Set the alignment of the label. If the label is placed
+            above or below the gadget the label can be centered, left aligned
+            or right aligned. If placed to the side of the gadget it can be
+            vertically centered or aligned to the top or to the bottom of the
+            gadget. If it is placed inside it can be centered, aligned to the
+            top or bottom (always horizontally centered) or aligned to the
+            left or right (vertically centered).
+
+            Applicability is (ISG)
+
+        ELA_Font -- Specify the font the label will use to draw itself.
+            It must be a TextFont pointer, i.e. the one you'll get with
+            the OpenFont() function. The label will not close the font
+            when disposed.
+
+            Applicability is (ISG)
+
+        ELA_PenBackgound -- Specify the pen number used for the background
+            pen. If it is not specified or set to -1 the standard DrawInfo
+            BACKGROUNDPEN will be used. Currently it is used only to erase
+            the label.
+
+            Applicability is (ISG)
+
+        ELA_PenShine -- Specify the pen number used for the shine pen.
+            If it is not specified or set to -1 the standard SHINEPEN will
+            be used. Currently this is used for the 'hilight' and '3d' labels.
+
+            Applicability is (ISG)
+
+        ELA_PenShadow -- Specify the pen number used for the shadow pen.
+             If it is not specified or set to -1 the standard SHADOWPEN will
+             be used. This is the pen used for 'normal' and '3d' labels.
+
+            Applicability is (ISG)
+
+        ELA_PenHalfShine -- Specify the halfshine pen. If it is not specified
+            or set to -1 the standard SHINEPEN will be used. It's usually a
+            bit darker than the shine pen and is currently used for the
+            disabled state.
+
+            Applicability is (ISG)
+
+        ELA_PenHalfShadow -- Specify the halfshadow pen. If it is not
+            specified or set to -1 the standard SHADOWPEN will be used.
+            It'susually a bit brighter than the shadow pen and is currently
+            used for the disabled state.
+
+            Applicability is (ISG)
+
+    NOTE
+        Usually a single BOOPSI image (e.g. a frame) may be used by different
+        gadgets at the same time. This is true even for the textlabel class,
+        but there are a couple of little "problem" or side effects that may
+        be encountered. First every time the IM_FRAMEBOX method is called the
+        image LeftEdge and TopEdge fields are changed, so the gadgets should
+        take care of storing the result of this method by their own (i.e. they
+        should save the FrameBox Left and top fields somewhere), since if they
+        call the IM_DRAW method using the image coordinates they may use the
+        result of another gadget's IM_FRAMEBOX call. Second the text to be
+        printed (and maybe even the font) should be set every time before a
+        IM_DRAW method since it's very unlikely that different gadgets need to
+        display the same string.
+        Currently I don't know if there's some nice and elegant way to solve
+        this, so you should create a different instance of this class for every
+        gadget that will use a lebeltext object.
+
+    SEE ALSO
+        The "Image Subclasses" chapter of the Intuition Reference Manual.
+
diff --git a/docs/NoteBOOPSI b/docs/NoteBOOPSI
new file mode 100644 (file)
index 0000000..b227ff8
--- /dev/null
@@ -0,0 +1,149 @@
+
+Alcune note sulla struttura GadgetInfo
+--------------------------------------
+
+Apparentemente l'input handler di Intuition riutilizza sempre, quando ciò è
+possibile, la stessa struttura GadgetInfo quando invoca un metodo di un
+gadget custom. Del resto il singolo task dell'input.device può solo invocare
+un metodo dopo l'altro in sequenza e nella maggioranza dei casi non necessita
+di avere contemporaneamente più di una struttura GadgetInfo.
+
+Nella struttura GadgetInfo un gadget custom riceve nel campo gi_Layer il
+puntatore al layer associato al "contenitore logico" in cui si trova il
+gadget stesso.
+
+· Un gadget custom situato in una finestra normale, oppure nella zona interna
+  di una finestra GIMMEZEROZERO riceve in gi_Layer il puntatore al layer di
+  Window.RPort. Il "contenitore logico" è l'area dell'intera finestra, oppure
+  l'area della sua zona interna.
+
+· Un gadget custom situato nella zona esterna di una finestra GIMMEZEROZERO
+  (GZZGADGET) riceve in gi_Layer il puntatore al layer di Window.BorderRPort.
+  Il "contenitore logico" è l'area dell'intera finestra.
+
+· Un gadget custom situato in un requester (REQGADGET) riceve in gi_Layer il
+  puntatore al layer del requester (Requester.ReqLayer).
+  Il "contenitore logico" è l'area del requester.
+
+· Un gadget custom situato nella titlebar di uno schermo (SCRGADGET) riceve
+  in gi_Layer il puntatore al layer della titlebar (Screen.BarLayer).
+  Il "contenitore logico" è l'area della titlebar.
+
+· Un gadget custom situato in un gruppo (cioè appartenente alla lista interna
+  di un gadget "groupgclass") riceve in gi_Layer un valore che dipende dal
+  "contenitore logico" del gruppo stesso, ricadendo così in uno dei quattro
+  casi precedenti.
+
+Il campo gi_RastPort è uguale a gi_Screen->BarLayer->rp quando il gadget
+appartiene ad una finestra o ad un requester, e punta a gi_Screen.RastPort
+quando invece il gadget appartiene alla titlebar dello schermo.
+Ignoro le ragioni di questo fatto; in ogni caso questa RastPort per
+qualche motivo non deve venire utilizzata per il rendering (nel caso di
+un gadget SCRGADGET essa non ha nemmeno un layer).
+
+Chiamando ObtainGIRPort() si ottiene un clone della RastPort di gi_Layer, che
+punta allo stesso layer, il quale punta alla RastPort originale (quella della
+finestra interna, o quella del bordo per le finestre GIMMEZEROZERO, ecc.).
+
+Inoltre ObtainGIRPort() esegue un LockLayer() su gi_Layer. Apparentemente
+gi_Layer ha già tre lock quando Intuition invoca il metodo GM_RENDER (solo due
+nel caso di un gadget SCRGADGET) mentre non è bloccato nel caso di tutti gli
+altri metodi.
+
+Per il metodo GM_RENDER il valore di gpr_RPort è esattamente lo stesso che
+si otterrebbe chiamando ObtainGIRPort(), il che suggerisce che tale RastPort
+clonata sia unica e conservata da qualche parte. In effetti a quanto ho visto
+finora ObtainGIRPort() restituisce sempre lo stesso indirizzo, qualunque sia
+il contesto.
+
+I valori di gi_Screen, gi_Window e gi_Requester sono inizializzati in modo
+opportuno (oppure nulli se non applicabili, a parte gi_Screen).
+
+Tabella di esempio per un gadget in una finestra non GZZ:
+
+ +-------------------------------------------------------------------------+
+ |                                              gi_Window: W               |
+ |                                  gi_Layer: RL, LWindow: W, LRastPort: R |
+ | gi_RastPort:         S, BitMap: bm, Layer: SL, LWindow: 0, LRastPort: S |
+ | gi_Window->RPort:    R, BitMap: bm, Layer: RL, LWindow: W, LRastPort: R |
+ | gi_Window->BorderRP: 0, BitMap: --, Layer: --, LWindow: -, LRastPort: - |
+ | gpr_RPort:           C, BitMap: bm, Layer: RL, LWindow: W, LRastPort: R |
+ | ObtainGIRPort():     C, BitMap: bm, Layer: RL, LWindow: W, LRastPort: R |
+ +-------------------------------------------------------------------------+
+
+Tabella di esempio per un gadget non GZZ in una finestra GZZ:
+
+ +-------------------------------------------------------------------------+
+ |                                              gi_Window: W               |
+ |                                  gi_Layer: RL, LWindow: W, LRastPort: R |
+ | gi_RastPort:         S, BitMap: bm, Layer: SL, LWindow: 0, LRastPort: S |
+ | gi_Window->RPort:    R, BitMap: bm, Layer: RL, LWindow: W, LRastPort: R |
+ | gi_Window->BorderRP: B, BitMap: bm, Layer: BL, LWindow: W, LRastPort: B |
+ | gpr_RPort:           C, BitMap: bm, Layer: RL, LWindow: W, LRastPort: R |
+ | ObtainGIRPort():     C, BitMap: bm, Layer: RL, LWindow: W, LRastPort: R |
+ +-------------------------------------------------------------------------+
+
+Tabella di esempio per un gadget GZZ in una finestra GZZ:
+
+ +-------------------------------------------------------------------------+
+ |                                              gi_Window: W               |
+ |                                  gi_Layer: BL, LWindow: W, LRastPort: B |
+ | gi_RastPort:         S, BitMap: bm, Layer: SL, LWindow: 0, LRastPort: S |
+ | gi_Window->RPort:    R, BitMap: bm, Layer: RL, LWindow: W, LRastPort: R |
+ | gi_Window->BorderRP: B, BitMap: bm, Layer: BL, LWindow: W, LRastPort: B |
+ | gpr_RPort:           C, BitMap: bm, Layer: BL, LWindow: W, LRastPort: B |
+ | ObtainGIRPort():     C, BitMap: bm, Layer: BL, LWindow: W, LRastPort: B |
+ +-------------------------------------------------------------------------+
+
+Il campo gi_Domain contiene larghezza e altezza del "contenitore logico" del
+gadget. Nel caso di un gadget SCRGADGET, l'altezza è stranamente quella dello
+schermo, mentre sarebbe più corretto se fosse quella della titlebar. Inoltre
+un gadget che appartenga ad un gruppo ha come "contenitore logico" quello che
+contiene il gruppo, e non il gruppo stesso (probabilmente in quanto il gruppo
+non ha un layer proprio).
+Gli offset contenuti in gi_Domain sono la distanza della posizione (espressa
+in coordinate di schermo) del layer associato al "contenitore logico" del
+gadget dalla posizione (sempre in coordinate di schermo) del suo "contenitore
+fisico primario" (cioè lo schermo per i gadget SCRGADGET e la finestra per
+tutti gli altri, anche se REQGADGET oppure appartenenti ad un gruppo).
+
+La funzione DoGadgetMethodA() crea una struttura GadgetInfo appropriata
+in base al tipo di gadget di cui viene invocato il metodo. Se è di tipo
+GZZGADGET, REQGADGET o SCRGADGET viene scelto il layer corretto, e anche
+gi_Domain viene inizializzato adeguatamente.
+
+Un problema è che "gadgetclass" e "icclass" nell'implementare il metodo
+OM_NOTIFY non chiamano DoGadgetMethodA() per invocare il metodo OM_UPDATE
+dell'oggetto ICA_TARGET, bensì gli passano direttamente il puntatore a
+GadgetInfo ricevuto, che fa riferimento al contesto dell'oggetto notificante
+e non, come sarebbe corretto, a quello dell'oggetto notificato!
+
+Non si devono annidare chiamate a ObtainGIRPort(), altrimenti la RastPort
+clonata (sempre la stessa) può venire modificata ad ogni chiamata. In
+realtà probabilmente se si rimane nello stesso contesto la RastPort clonata
+viene reinizializzata ogni volta con gli stessi valori, per cui non cambia
+niente. Il vero pericolo sta nell'invocare un metodo di un altro gadget che
+chiami ObtainGIRPort() mentre si è ancora all'interno del proprio blocco
+ObtainGIRPort().
+In entrambi i casi comunque può verificarsi un errore 01000008 (Semaphore
+in illegal state).
+Del resto chiamare tanto ObtainGIRPort() quanto DoGadgetMethodA() all'interno
+di un blocco ObtainGIRPort() dovrebbe essere scorretto comunque (sono due
+funzioni di Intuition).
+
+
+Note varie
+----------
+
+GACT_IMMEDIATE viene sempre rispettato anche per i gadget custom o BOOPSI.
+Il valore di Code del messaggio IDCMP_GADGETDOWN è sempre zero e non può
+essere impostato dal dispatcher in alcun metodo (GM_HITTEST/GM_GOACTIVE).
+
+GACT_RELVERIFY è necessario affinché l'uso di GMR_VERIFY + gpi_Termination
+generi effettivamente un messaggio IDCMP_GADGETUP; in sua assenza esso non
+viene generato. Viceversa la sua presenza non garantisce l'invio di tali
+messaggi; ciò è deciso dal dispatcher nei metodi GM_GOACTIVE e GM_HANDLEINPUT.
+
+La documentazione del metodo IM_FRAMEBOX sul RKM è sbagliata! Viene invertito
+il ruolo di imp_ContentsBox e imp_FrameBox.
+
diff --git a/docs/empty_Makefile b/docs/empty_Makefile
new file mode 100644 (file)
index 0000000..c17c250
--- /dev/null
@@ -0,0 +1,96 @@
+#
+# $Id:$
+#
+# Copyright (C) 1999 by Matteo Cavalleri
+#
+
+## This is the 'local' makefile, i.e. the makefile
+## that is placed in very classes' directory.
+## You can simply copy it in your class' directory
+## and rename it to 'Makefile', or, if you want, you
+## can customize it.
+##
+## We also suggest you to delete the lines beginning
+## with two '##' like the ones you are reading now.
+## This will make the processing of this file faster
+## since make won't have to strip tons of comments.
+##
+include $(BOOPSITOP)/config.mk
+##
+## This include the local configuration
+## for your class, i.e. the file with
+## the class name, version, etc.
+## It must be present and properly
+## customized. Read the Empty_config.mk
+## file in the docs/ directory for a more
+## detailed explanation.
+##
+include config.mk
+
+##
+## These variables will be used by the general.mk
+## files, so you may use them to pass "arguments"
+## to it, e.g. if you need to link a specific object
+## to your demo program or class.
+##
+## the ARG_COMMON is the arguments that will be used
+## for all the targets, the other variables will be
+## used only for the specific target
+##
+## example
+##
+## ARG_ALL := $(ARG_COMMON) MyObj.o
+##
+ARG_COMMON :=
+ARG_ALL := $(ARG_COMMON)
+ARG_LIB := $(ARG_COMMON)
+ARG_DEBUG := $(ARG_COMMON)
+ARG_PRIVATE := $(ARG_COMMON)
+ARG_OBJ := $(ARG_COMMON)
+ARG_INSTALL := $(ARG_COMMON)
+
+##
+## These variables will be used by the general.mk
+## files, so you may use them to pass "arguments"
+## to it, e.g. if you need to add new dependencies
+## to the various targets
+##
+## the DEP_COMMON is the arguments that will be used
+## for all the targets, the other variables will be
+## used only for the specific target
+##
+## example
+##
+## DEP_ALL := $(DEP_COMMON) MyObj.o
+##
+DEP_COMMON :=
+DEP_ALL := $(DEP_COMMON)
+DEP_LIB := $(DEP_COMMON)
+DEP_DEBUG := $(DEP_COMMON)
+DEP_PRIVATE := $(DEP_COMMON)
+DEP_OBJ := $(DEP_COMMON)
+
+##
+## This is the real makefile. If you
+## don't like it you can include your
+## own makefile. We suggest you to put it
+## in the common/ directory (with a name
+## different from 'general.mk') and to include
+## it rather than writing it here, so that
+## other user can use it for their classes.
+##
+## example
+##
+## include $(BOOPSITOP)/common/mycustommakefile.mk
+##
+include $(BOOPSITOP)/common/general.mk
+
+##
+## Below you can add your own custom targets that are
+## needed for your class.
+##
+## example
+##
+## MyObj.o: MyObj.c
+##    $(CC) $^ $(O) $@
+##
diff --git a/docs/empty_config.mk b/docs/empty_config.mk
new file mode 100644 (file)
index 0000000..6f9a513
--- /dev/null
@@ -0,0 +1,137 @@
+#
+# $Id:$
+#
+# Copyright (C) 1999 by Matteo Cavalleri
+#
+
+## This is an empty local config.mk file. You should fill
+## the required fields, copy it to your class directory
+## and rename it to "config.mk".
+## Detailed explanations of the various variables are
+## given below. Please be sure to give the right value
+## to all the variables, or you may get unpredictable
+## results!!
+##
+## We also suggest you to delete the lines beginning with
+## two '##' like the ones you are reading now. This will
+## make the processing of this file faster since make 
+## won't have to strip tons of comments.
+
+###########################################################
+# your stuff
+#
+
+##
+## CLASSLIB is the name of the compiled class, like
+## "label.image" or "listview.gadget", etc.
+## This is required by the makefile. It will also
+## define the symbol "NAME" with this variable's
+## value so that it can be used in your source.
+##
+## example:
+##
+## CLASSLIB := button.gadget
+##
+CLASSLIB :=
+##
+## CLASSSRC is the name of the source code of
+## your class, extension included.
+## This is required by the Makefile.
+##
+## example
+##
+## CLASSSRC := ButtonGClass.c
+##
+CLASSSRC :=
+##
+## DEMOSRC is the name of the source code
+## of your demo program, extension included.
+## This is required by the Makefile.
+##
+## example
+##
+## DEMOSRC := ButtonGDemo.c
+##
+DEMOSRC  :=
+##
+## SUBDIR is the directory name where your class
+## will be copied when you will execute the
+## 'install' target. It must be "images" if your
+## class is a BOOPSI image, "gadgets" if your class
+## is a BOOPSI gadget or just an empty string if
+## your class is a general BOOPSI object.
+##
+## example
+##
+## SUBDIR := gadgets
+##
+SUBDIR   :=
+
+
+###########################################################
+# class version information
+#
+
+##
+## CLASSVER is the version number of your class library
+## e.g. if your library is version 1.2 CLASSVER is 1.
+## It is required to compile the library startup code,
+## and will also be used to define a symbol named VERSION,
+## so that you can use it in your source code.
+##
+## example
+##
+## CLASSVER := 1
+##
+CLASSVER  :=
+##
+## CLASSREV is the revision number of your class library
+## e.g. if your library is version 1.2 CLASSREV is 2.
+## It is required to compile the library startup code,
+## and will also be used to define a symbol named VERSION,
+## so that you can use it in your source code.
+##
+## example
+##
+## CLASSREV := 2
+##
+CLASSREV  :=
+##
+## CLASSDATE is the date of your class library. It follow
+## the standard AmigaOS rules, so it must be enclosed in
+## parenthesis and have the following format: Day.Month.Year
+## It is used to define a symbol named DATE so that you can
+## use it in your source code.
+##
+## example
+##
+## CLASSDATE := (6.4.99)
+##
+CLASSDATE :=
+
+
+###########################################################
+# variable to make the linked library version
+#
+
+##
+## PRV variables is used to compile the class as a linked
+## library instead of the shared version. It is simply
+## used to define a preprocessor simbol with the name
+## you give here.
+## It's up to you to create a class that can be compiled
+## as a shared or static library depending on the
+## definition of this symbol. The current makefile
+## define it for a couple of targets, like 'debug', so
+## we suggest you to implement this features. It's very
+## easy and in most cases requires just some copy & paste
+## from existing classes. Look at them or ask in the
+## OpenBoopsi mailing list if you need a more detailed
+## explanation.
+##
+## example
+##
+## PRV := PRIVATE_GADGETCLASS
+##
+PRV :=
+
diff --git a/gadgets/ClockGClass/Makefile b/gadgets/ClockGClass/Makefile
new file mode 100644 (file)
index 0000000..eb79630
--- /dev/null
@@ -0,0 +1,27 @@
+#
+# $Id:$
+#
+# Copyright (C) 1999 by Matteo Cavalleri
+#
+
+include $(BOOPSITOP)/config.mk
+include config.mk
+
+ARG_COMMON :=
+ARG_ALL := $(ARG_COMMON) $(BOOPSITOP)/common/OpenClass.o
+ARG_LIB := $(ARG_COMMON)
+ARG_DEBUG := $(ARG_COMMON) $(BOOPSITOP)/common/OpenClass.o $(DEF) $(PRV)
+ARG_PRIVATE := $(ARG_COMMON)
+ARG_OBJ := $(ARG_COMMON)
+
+DEP_COMMON :=
+DEP_ALL := $(DEP_COMMON) OpenClass.o
+DEP_LIB := $(DEP_COMMON)
+DEP_DEBUG := $(DEP_COMMON) OpenClass.o
+DEP_PRIVATE := $(DEP_COMMON)
+DEP_OBJ := $(DEP_COMMON)
+
+include $(BOOPSITOP)/common/general.mk
+
+OpenClass.o: OpenClass.c
+       $(MAKE) -C $(BOOPSITOP)/common/ $@
diff --git a/gadgets/LauncherGClass/Makefile b/gadgets/LauncherGClass/Makefile
new file mode 100644 (file)
index 0000000..95064ba
--- /dev/null
@@ -0,0 +1,28 @@
+#
+# $Id:$
+#
+# Copyright (C) 1999 by Matteo Cavalleri
+#
+
+include $(BOOPSITOP)/config.mk
+include config.mk
+
+ARG_COMMON :=
+ARG_ALL := $(ARG_COMMON) $(BOOPSITOP)/common/OpenClass.o
+ARG_LIB := $(ARG_COMMON)
+ARG_DEBUG := $(ARG_COMMON) $(BOOPSITOP)/common/OpenClass.o $(DEF) $(PRV)
+ARG_PRIVATE := $(ARG_COMMON)
+ARG_OBJ := $(ARG_COMMON)
+
+DEP_COMMON :=
+DEP_ALL := $(DEP_COMMON) OpenClass.o
+DEP_LIB := $(DEP_COMMON)
+DEP_DEBUG := $(DEP_COMMON) OpenClass.o
+DEP_PRIVATE := $(DEP_COMMON)
+DEP_OBJ := $(DEP_COMMON)
+
+include config.mk
+include $(BOOPSITOP)/common/general.mk
+
+OpenClass.o: OpenClass.c
+       $(MAKE) -C $(BOOPSITOP)/common/ $@
diff --git a/gadgets/ListBox/ListBoxClass.c b/gadgets/ListBox/ListBoxClass.c
new file mode 100644 (file)
index 0000000..e0c2ddf
--- /dev/null
@@ -0,0 +1,547 @@
+/*
+**     $Id: ListBoxClass.c,v 1.3 1999/01/30 13:25:03 bernie Exp $
+**
+**     Copyright (C) 1997,98,99 Bernardo Innocenti (<bernardo.innocenti@usa.net>)
+**     All rights reserved.
+**
+**     Use 4 chars wide TABs to read this file
+**
+**     GadTools-like `boopsi' ListView group class
+*/
+
+#define USE_BUILTIN_MATH
+#define INTUI_V36_NAMES_ONLY
+#define INTUITION_IOBSOLETE_H
+#define __USE_SYSBASE
+#define  CLIB_ALIB_PROTOS_H            /* Avoid dupe defines of boopsi funcs */
+
+#include <exec/types.h>
+#include <exec/memory.h>
+#include <intuition/intuition.h>
+#include <intuition/intuitionbase.h>
+#include <intuition/classes.h>
+#include <intuition/gadgetclass.h>
+#include <intuition/icclass.h>
+#include <intuition/imageclass.h>
+
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <proto/utility.h>
+
+#ifdef __STORM__
+       #pragma header
+#endif
+
+#define LV_GADTOOLS_STUFF
+#include <gadgets/ListBoxClass.h>
+
+#include <CompilerSpecific.h>
+#include <DebugMacros.h>
+#include <BoopsiStubs.h>
+#include <BoopsiLib.h>
+
+
+
+/* Sliders dimensions
+ */
+#define VSLIDER_WIDTH  18
+#define HSLIDER_HEIGHT 18
+
+
+/* Per-object instance data */
+struct LBData
+{
+       /* struct Gadget *ThisGadget; (not used) */
+
+       /* Group children */
+       Object *ListView;
+       Object *HSlider;
+       Object *VSlider;
+       Object *LVToVSliderIC;
+       Object *LVToHSliderIC;
+
+       Object *Model;                  /* The ic object that makes our children talk to each other */
+       Object *Frame;                  /* The frame to put around the child objects */
+
+       /* Frame size */
+       LONG    FrameWidth, FrameHeight;
+
+       /* These two have the same meaning, but we keep both updated
+        * because the Rectangle structure (MinX, MinY, MaxX, MaxY)
+        * is more handy in some cases, while the IBox structure
+        * (Left/Top/Width/Height) is best for other cases.
+        */
+       struct IBox              GBox;
+       struct Rectangle GRect;
+};
+
+
+/* Global class data */
+struct LBClassData
+{
+       Class                           *SliderBarGClass;
+       struct ClassLibrary     *SliderBarBase;
+};
+
+
+
+#define SLIDERBARGCLASS_PTR ( ((struct LBClassData *)(cl->cl_UserData))->SliderBarGClass )
+
+
+
+extern Class *ListViewClass;
+
+
+/* Local function prototypes */
+static void            LB_GMLayout             (Class *cl, struct Gadget *g, struct gpLayout *msg);
+static ULONG   LB_OMSet                (Class *cl, struct Gadget *g, struct opUpdate *msg);
+static ULONG   LB_OMGet                (Class *cl, struct Gadget *g, struct opGet *msg);
+static ULONG   LB_OMNew                (Class *cl, struct Gadget *g, struct opSet *msg);
+static void            LB_OMDispose    (Class *cl, struct Gadget *g, Msg msg);
+
+static void            CreateVSlider   (Class *cl, struct Gadget *g, struct LBData *lb, struct DrawInfo *dri);
+static void            CreateHSlider   (Class *cl, struct Gadget *g, struct LBData *lb, struct DrawInfo *dri);
+static void            DeleteVSlider   (struct Gadget *g, struct LBData *lb);
+static void            DeleteHSlider   (struct Gadget *g, struct LBData *lb);
+
+
+
+
+/* Attribute translations for object interconnections */
+
+static LONG MapLVToHSlider[] =
+{
+       LVA_PixelLeft,          PGA_Top,
+       LVA_PixelWidth,         PGA_Total,
+       LVA_PixelHVisible,      PGA_Visible,
+       TAG_DONE
+};
+
+static LONG MapHSliderToLV[] =
+{
+       PGA_Top,                        LVA_PixelLeft,
+       TAG_DONE
+};
+
+static LONG MapLVToVSlider[] =
+{
+       LVA_PixelTop,           PGA_Top,
+       LVA_PixelHeight,        PGA_Total,
+       LVA_PixelVVisible,      PGA_Visible,
+       TAG_DONE
+};
+
+static LONG MapVSliderToLV[] =
+{
+       PGA_Top,        LVA_PixelTop,
+       TAG_DONE
+};
+
+
+
+static ULONG HOOKCALL LBDispatcher(
+       REG(a0, Class *cl),
+       REG(a2, struct Gadget *g),
+       REG(a1, Msg msg))
+{
+       ASSERT_VALID_PTR(cl)
+       ASSERT_VALID_PTR(g)
+       ASSERT_VALID_PTR(msg)
+
+       switch (msg->MethodID)
+       {
+               case GM_LAYOUT:
+                       /* This method is only supported on V39 and above */
+                       LB_GMLayout(cl, g, (struct gpLayout *)msg);
+                       return TRUE;
+
+               case OM_SET:
+               case OM_UPDATE:
+                       return LB_OMSet(cl, g, (struct opUpdate *)msg);
+
+               case OM_GET:
+                       return LB_OMGet(cl, g, (struct opGet *)msg);
+
+               case OM_NEW:
+                       return LB_OMNew(cl, g, (struct opSet *)msg);
+
+               case OM_DISPOSE:
+                       LB_OMDispose(cl, g, msg);
+                       return TRUE;
+
+               default:
+                       /* Unsupported method: let our superclass's dispatcher take
+                        * a look at it. This includes all gadget methods sent
+                        * by Intuition: GM_RENDER, GM_HANDLEINPUT, GM_GOACTIVE and
+                        * GM_GOINACTIVE. These methods are automatically forwarded
+                        * to our child gadgets by the groupgclass.
+                        */
+                       DB2 (DBPRINTF("ListBoxClass: passing unknown method 0x%lx to superclass\n", msg->MethodID);)
+                       return DoSuperMethodA (cl, (Object *)g, msg);
+       }
+}
+
+
+
+static void LB_GMLayout(Class *cl, struct Gadget *g, struct gpLayout *msg)
+{
+       struct LBData *lb = (struct LBData *) INST_DATA(cl, (Object *)g);
+
+       DB(DBPRINTF("ListBoxClass: GM_LAYOUT\n");)
+       ASSERT_VALID_PTR(lb)
+
+
+       /* Collect new gadget size */
+       GetGadgetBox(msg->gpl_GInfo, (struct ExtGadget *)g, &lv->GBox);
+       IBoxToRect(&lv->GBox, &lv->GRect);
+
+
+       /* Size our children accordingly */
+       SetAttrs(lb->ListView,
+               GA_Left,        lb->GBox.Left + lb->FrameWidth / 2,
+               GA_Top,         lb->GBox.Top + lb->FrameHeight / 2,
+               GA_Width,       lb->GBox.Width - lb->FrameWidth - VSLIDER_WIDTH,
+               GA_Height,      lb->GBox.Height - lb->FrameHeight - HSLIDER_HEIGHT,
+               TAG_DONE);
+
+       if (lb->VSlider)
+               SetAttrs(lb->VSlider,
+                       GA_Left,        lb->GRect.MaxX - VSLIDER_WIDTH + 1,
+                       GA_Top,         lb->GBox.Top,
+                       GA_Width,       VSLIDER_WIDTH,
+                       GA_Height,      lb->GBox.Height,
+                       TAG_DONE);
+
+
+       /* NOTE: it seems that the groupgclass does not forward GM_LAYOUT
+        * to its children, so we must handle this here.
+        */
+       DoMethodA(lb->ListView, (Msg)msg);
+       if (lb->VSlider)        DoMethodA(lb->VSlider, (Msg)msg);
+}
+
+
+
+static ULONG LB_OMSet(Class *cl, struct Gadget *g, struct opUpdate *msg)
+{
+       struct LBData *lb = (struct LBData *) INST_DATA(cl, (Object *)g);
+
+       DB2(DBPRINTF("ListBoxClass: OM_SET\n");)
+       ASSERT_VALID_PTR(lb)
+
+       if (lb->ListView)
+               /* Forward attributes to our listview */
+               DoMethodA (lb->ListView, (Msg)msg);
+
+       /* Also forward to our superclass */
+       return DoSuperMethodA (cl, (Object *)g, (Msg) msg);
+}
+
+
+
+static ULONG LB_OMGet(Class *cl, struct Gadget *g, struct opGet *msg)
+{
+       struct LBData *lb = (struct LBData *) INST_DATA(cl, (Object *)g);
+
+       DB2(DBPRINTF("ListBoxClass: OM_GET\n");)
+       ASSERT_VALID_PTR(lb)
+
+       /* Forward this method to our listview */
+       return DoMethodA(lb->ListView, (Msg)msg);
+}
+
+
+
+static ULONG LB_OMNew(Class *cl, struct Gadget *g, struct opSet *msg)
+{
+       struct LBData   *lb;
+       struct DrawInfo *dri;
+
+
+       DB2(DBPRINTF("ListBoxClass: OM_NEW\n");)
+
+       if (g = (struct Gadget *)DoSuperMethodA(cl, (Object *)g, (Msg)msg))
+       {
+               /* Set the GMORE_SCROLLRASTER flag */
+               if (g->Flags & GFLG_EXTENDED)
+               {
+                       DB(DBPRINTF("ListBoxClass: OM_NEW: Setting GMORE_SCROLLRASTER\n");)
+                       ((struct ExtGadget *)g)->MoreFlags |= GMORE_SCROLLRASTER;
+               }
+
+               lb = (struct LBData *) INST_DATA(cl, (Object *)g);
+               ASSERT_VALID_PTR(lb)
+
+
+               /* Clear the object instance */
+               memset(lb, 0, sizeof (struct LBData));
+
+
+               /* Store a pointer to this object (a Gadget) in the class private
+                * instance. This way we can avoid passing it along to all functions.
+                */
+               /* lb->ThisGadget = g; (not used) */
+
+
+               /* May be NULL */
+               dri = (struct DrawInfo *) GetTagData(GA_DrawInfo, NULL, msg->ops_AttrList);
+
+
+               /* Create a frame to put around us */
+               if (lb->Frame = NewObject(NULL, FRAMEICLASS,
+                       IA_EdgesOnly,   TRUE,
+                       IA_FrameType,   FRAME_BUTTON,
+                       TAG_DONE))
+               {
+                       struct IBox FrameBox, ContentsBox = { 0, 0, 0, 0 };
+
+                       /* Ask the frame about its nominal frame width and height */
+                       DoMethod((Object *)lb->Frame, IM_FRAMEBOX, &ContentsBox, &FrameBox, dri, 0);
+
+                       /* Remember it later */
+                       lb->FrameWidth = FrameBox.Width;
+                       lb->FrameHeight = FrameBox.Height;
+               }
+
+               /* Create a model object. This will be the core of the boopsi attributes
+                * network used by the sub-objects to talk each other. We pass our
+                * initalization tags to the model so it will pick up the correct
+                * ICA_TARGET and ICA_MAP, if specified with NewObject().
+                */
+               if (lb->Model = NewObjectA(NULL, MODELCLASS, NULL))
+               {
+                       /* Create the ListView and pass all creation time attributes to it.
+                        * Note that any GA_#? attributes are also passed to the listview,
+                        * so it will have the same size of its container.
+                        */
+                       if (lb->ListView = NewObject(ListViewClass, NULL,
+                               GA_Image,               lb->Frame,
+                               TAG_MORE,               msg->ops_AttrList))
+                       {
+                               /* From now on, the groupgclass will dispose this object for us */
+                               DoMethod((Object *)g, OM_ADDMEMBER, lb->ListView);
+
+                               /* Connect Model to ListView */
+                               {
+                                       APTR icobject;
+
+                                       if (icobject = NewObject(NULL, ICCLASS,
+                                               ICA_TARGET,     lb->ListView,
+                                               TAG_DONE))
+                                               if (!DoMethod(lb->Model, OM_ADDMEMBER, icobject))
+                                                       DisposeObject(icobject);
+                               }
+
+                               /* Connect ListView to Model */
+                               SetAttrs(lb->ListView,
+                                       ICA_TARGET, lb->Model,
+                                       TAG_DONE);
+
+                               /* Add sliders */
+                               CreateVSlider(cl, g, lb, dri);
+                               CreateHSlider(cl, g, lb, dri);
+
+                               /* Set the gadget width and height because the groupgclass
+                                * always forces them to 0 on creation.
+                                */
+                               {
+                                       struct TagItem *tag;
+
+                                       if (tag = FindTagItem(GA_RelWidth, msg->ops_AttrList))
+                                               SetAttrs(g,
+                                                       GA_RelWidth, tag->ti_Data,
+                                                       TAG_DONE);
+                                       else
+                                               SetAttrs (g,
+                                                       GA_Width, GetTagData(GA_Width, g->Width, msg->ops_AttrList),
+                                                       TAG_DONE);
+
+                                       if (tag = FindTagItem(GA_RelHeight, msg->ops_AttrList))
+                                               SetAttrs(g,
+                                                       GA_RelHeight, tag->ti_Data,
+                                                       TAG_DONE);
+                                       else
+                                               SetAttrs(g,
+                                                       GA_Height, GetTagData (GA_Height, g->Height, msg->ops_AttrList),
+                                                       TAG_DONE);
+
+                                       DB2(DBPRINTF("ListBoxClass: OM_NEW: size set to L=%ld T=%ld W=%ld H=%ld\n",
+                                               g->LeftEdge, g->TopEdge, g->Width, g->Height);)
+                               }
+
+                               /* TODO: Handle creation-time attributes */
+
+                               return (ULONG)g;        /* Return newly created istance */
+                       }
+               }
+
+
+               /* Dispose object without disturbing the dispatchers of our sub-classes, if any */
+               CoerceMethod(cl, (Object *)g, OM_DISPOSE);
+       }
+
+       return 0;       /* Fail */
+}
+
+
+
+static void LB_OMDispose(Class *cl, struct Gadget *g, Msg msg)
+{
+       struct LBData   *lb = (struct LBData *) INST_DATA(cl, (Object *)g);
+
+       DB(DBPRINTF("ListBoxClass: OM_DISPOSE\n");)
+       ASSERT_VALID_PTR(lb)
+
+
+       DeleteHSlider(g, lb);
+       DeleteVSlider(g, lb);
+
+       /* Dispose the child objects that aren't freed automatically by the modelclass.
+        * Note that here we are disposing the Frame before we dispose the gadgets that
+        * may be still using it. Everything will go well unless those classes will
+        * access the frame within their OM_DISPOSE methods (very silly).
+        */
+       DisposeObject(lb->Model);
+       DisposeObject(lb->Frame);
+
+       /* Our superclass will cleanup everything else now */
+       DoSuperMethodA(cl, (Object *)g, (Msg) msg);
+
+       /* From now on, our instance data is no longer available */
+}
+
+
+
+static void CreateVSlider(Class *cl, struct Gadget *g, struct LBData *lb, struct DrawInfo *dri)
+{
+       if (lb->VSlider = NewObject(SLIDERBARGCLASS_PTR, NULL,
+               GA_ID,                  g->GadgetID,    /* Same as our ID */
+               GA_DrawInfo,    dri,
+               GA_LabelImage,  lb->Frame,
+               PGA_Freedom,    FREEVERT,
+               PGA_NewLook,    TRUE,
+               PGA_Borderless, TRUE,
+               LAYOUTA_Orientation, LORIENT_VERT,
+               ICA_TARGET,             lb->Model,
+               ICA_MAP,                MapVSliderToLV,
+               TAG_DONE))
+       {
+               /* From now on, the groupgclass will dispose this object for us */
+               DoMethod((Object *)g, OM_ADDMEMBER, lb->VSlider);
+
+               /* Connect Model to ListView */
+               SetAttrs(lb->Model,
+                       ICA_TARGET, lb->ListView,
+                       TAG_DONE);
+
+               /* Connect VSlider to Model */
+               if (lb->LVToVSliderIC = NewObject(NULL, ICCLASS,
+                       ICA_TARGET,     lb->VSlider,
+                       ICA_MAP,        MapLVToVSlider,
+                       TAG_DONE))
+                       if (!DoMethod(lb->Model, OM_ADDMEMBER, lb->LVToVSliderIC))
+                               DisposeObject(lb->LVToVSliderIC);
+       }
+}
+
+
+
+static void CreateHSlider(Class *cl, struct Gadget *g, struct LBData *lb, struct DrawInfo *dri)
+{
+       /* TODO */
+}
+
+
+static void DeleteVSlider(struct Gadget *g, struct LBData *lb)
+{
+       if (lb->LVToVSliderIC)
+       {
+               ASSERT_VALID_PTR(lb->LVToVSliderIC)
+               ASSERT_VALID_PTR(lb->Model)
+
+               DoMethod (lb->Model, OM_REMMEMBER, lb->LVToVSliderIC);
+               DisposeObject (lb->LVToVSliderIC);
+               lb->LVToVSliderIC = NULL;
+       }
+
+       if (lb->VSlider)
+       {
+               ASSERT_VALID_PTR(lb->VSlider)
+
+               DoMethod ((Object *)g, OM_REMMEMBER, lb->VSlider);
+               DisposeObject (lb->VSlider);
+               lb->VSlider = NULL;
+       }
+}
+
+
+
+static void DeleteHSlider(struct Gadget *g, struct LBData *lb)
+{
+       /* TODO */
+}
+
+
+
+Class *MakeListBoxClass(void)
+{
+       Class *class;
+       struct LBClassData *classdata;
+
+       if (class = MakeClass(NULL, GROUPGCLASS, NULL, sizeof (struct LBData), 0))
+       {
+               class->cl_Dispatcher.h_Entry = (ULONG (*)()) LBDispatcher;
+
+               /* Allocate storage for global class data */
+               if (classdata = AllocMem(sizeof (struct LBClassData), MEMF_PUBLIC | MEMF_CLEAR))
+               {
+                       class->cl_UserData = (ULONG) classdata;
+
+                       if (classdata->SliderBarBase = (struct ClassLibrary *)
+                               OpenLibrary("gadgets/sliderbar.gadget", 0))
+                       {
+                               classdata->SliderBarGClass = classdata->SliderBarBase->cl_Class;
+                               ASSERT_VALID_PTR(classdata->SliderBarGClass);
+
+                               return class;
+                       }
+               }
+
+               FreeListBoxClass(class);
+       }
+
+       return NULL;
+}
+
+
+
+BOOL FreeListBoxClass(Class *class)
+{
+       struct LBClassData *classdata;
+
+       if (class)
+       {
+               ASSERT_VALID_PTR(class)
+
+               classdata = (struct LBClassData *)class->cl_UserData;
+
+               /* Try to remove the class */
+               if (FreeClass(class))
+               {
+                       if (classdata)
+                       {
+                               ASSERT_VALID_PTR(classdata)
+                               ASSERT_VALID_PTR_OR_NULL(classdata->SliderBarBase)
+
+                               /* Cleanup global class data */
+
+                               /* NULL is safe in CloseLibrary() since V36 */
+                               CloseLibrary((struct Library *)classdata->SliderBarBase);
+                               FreeMem(classdata, sizeof (struct LBClassData));
+                       }
+                       return TRUE;
+               }
+               return FALSE;
+       }
+       return TRUE;
+}
diff --git a/gadgets/ListBox/Makefile b/gadgets/ListBox/Makefile
new file mode 100644 (file)
index 0000000..4b80f8e
--- /dev/null
@@ -0,0 +1,29 @@
+#
+# $Id:$
+#
+# Copyright (C) 1999 by Matteo Cavalleri
+#
+
+include $(TOP)/config.mk
+
+#
+# Class specific information
+#
+CLASSLIB       := listbox.gadget
+CLASSSRC       := ListBoxClass.c
+DEMOSRC                :=
+INSTALLDIR     := gadgets
+
+#
+# Class version information
+#
+CLASSVER       := 1
+CLASSREV       := 7
+CLASSDATE      := (22.8.99)
+
+#
+# variable to make the linked library version
+#
+PRV :=
+
+include $(TOP)/common/general.mk
diff --git a/gadgets/ListView/BoopsiLV.readme b/gadgets/ListView/BoopsiLV.readme
new file mode 100644 (file)
index 0000000..1b1ad8d
--- /dev/null
@@ -0,0 +1,81 @@
+Short:    Fast boopsi ListView class with many features (w/ src)
+Author:   bernardo.innocenti@usa.net (Bernardo Innocenti)
+Uploader: bernardo.innocenti@usa.net (Bernardo Innocenti)
+Type:     dev/src
+Version:  2.0
+
+ The ListBoxClass is a flexible and efficient boopsi gadget class which
+replaces the original GadTools LISTVIEW_KIND gadget and offers a lot
+more functionality. It's a subclass of the groupgclass and incorporates
+several other gadgets to build a full featured listview with vertical
+and horizontal scroll bars, a surrounding border and, of course, a
+ListView object.
+
+ The ListViewClass is a subclass of Intuition's gadgetclass which
+builds a plain listview gadget without any border or scroll bars.
+It handles selection of multiple items and it can be resized on the fly.
+The GFLG_REL#? attributes are fully supported and you can even display
+a list of images and other boopsi objects. Lists with multiple columns
+are possible but not yet implemented. Horizontal scrolling would also
+be easy to add. Usually you don't need to create objects from this class
+directly because the ListBoxClass will do it for you.
+
+ The LVDemo program demonstrates the usage of both classes. The
+ListViewClass is attached to scroll bars in the window borders.
+The demo is also a useful source of information on several
+Intuition and boopsi topics.
+
+ Three different binary versions are provided:
+
+       LVDemo_Generic
+               Works even on plain 68000 and includes OS 2.0 support
+
+       LVDemo_020_OS30
+               Optimized for 68020, no OS 2.0 support
+
+       LVDemo_Trace
+               Outputs debug messages with kprintf(), view them with
+               serial terminal or sushi
+
+
+ Some features:
+
+       + Easy to use (almost a drop-in replacement for LISTVIEW_KIND)
+       + Can be resized and supports GREL_#? flags
+       + Multiple selection of items
+       + Notifies your `boopsi' sliders
+       + Multiple columns (TODO)
+       + Redraws quickly without clearing
+         (which is good for solid window sizing)
+       + Horizontal scrolling (TODO)
+       + Items can be `boopsi' images as well as texts
+       + Using arrays instead of exec lists
+       + You can use `boopsi' label images instead of plain text
+       + You can use your own custom rendering hook
+       + You can use your own item-retriving callback hook
+       + List titles (TODO)
+       + Full Keyboard control (shift, alt and control key combos
+         are supported)
+       + Asynchronous scrolling with inertia (TODO)
+       + OS 3.0 optimized (V39-only version also available)
+       + RTG friendly and optimized (no planar stuff in chunky bitmaps)
+       + Small code size! (less then 10KB)
+       + Written in C to be highly portable across compilers and CPUs
+       + Comes with source code with detailed comments
+       + Source code compiles with SAS/C, StormC and GCC
+       + Subclasses can be easlily derived from the base listview class
+
+
+The source code shows how to:
+
+       * Build a `boopsi' class on top of the gadgetclass
+       * Build a `boopsi' class on top of the groupgclass
+       * Write a `boopsi' gadget dispatcher with complex GM_DRAW
+         and GM_HANDLEINPUT
+       * Use the `boopsi' sysiclass images
+       * Connect `boopsi' objects together using the icclass
+       * Overlay the buttongclass to make a scroll button
+       * Create a sizeable window with sliders
+       * Make a C program without startup
+       * Correctly opening/closing libraries, windows and other
+         system resources
diff --git a/gadgets/ListView/LVDemo.c b/gadgets/ListView/LVDemo.c
new file mode 100644 (file)
index 0000000..6b5c2e4
--- /dev/null
@@ -0,0 +1,1004 @@
+/*
+**     $Id: LVDemo.c,v 1.3 2000/01/12 21:17:25 bernie Exp $
+**
+**     Introduction
+**     ============
+**
+**     This program demonstrates how to use the `boopsi' ListView gadget.
+**
+**     The source code shows how to create a resizable window with sliders
+**     and how to write a custom `boopsi' class on top of the gadgetclass.
+**
+**
+**     Compiling
+**     =========
+**
+**     This project can be compiled with SAS/C 6.58 or better and
+**     GeekGadget's GCC 2.95.2 or better.
+**     You get the smallest executable with SAS/C. GCC will give
+**     you quite a lot of warnings with the tag calls. Don't worry about them.
+**
+**
+**     History
+**     =======
+**
+**     0.1 (23.6.96)   First try
+**     1.0 (21.1.97)   First alpha release
+**     1.1 (24.5.97)   Lotsa bugs fixed, implemented LVA_DoubleClick
+**     1.2 (31.8.97)   Lotsa bugs fixed, implemented LVA_Clipped
+**                                     Fixed memory leak with the test list
+**     1.3 (5.9.97)    Improved initial sliders notification
+**                                     Added multiple selection (LVA_DoMultiselect)
+**                                     Fixed scrolling problems in some unusual conditions
+**     1.4     (8.9.97)        Sets GMORE_SCROLLRASTER in OM_NEW
+**                                     Multiple demo windows showing the features of the class
+**     1.5 (14.9.97)   Added LVA_ItemSpacing
+**                                     Finally fixed the LVA_Clipped mode!
+**
+**     1.6 (2.10.97)   Added StormC support
+**                                     Implemented pixel-wise vertical scrolling for clipped mode
+**                                     Reworked the class istance data and some code
+**
+**     1.7 (15.12.98)  Added ListBoxClass
+**                                     Moved ScrollButtonClass in its own file
+**                                     Removed function OpenClass()
+**                                     Updated to latest version of VectorGlyphIClass
+**
+**
+**     Known Bugs
+**     ==========
+**
+**             - This code has never been tested on V37.
+**
+**             - Middle mouse button scrolling does not work because
+**               of a bug in input.device V40.
+**
+**
+**     Copyright Notice
+**     ================
+**
+**     Copyright © 1996,97,98,00 by Bernardo Innocenti <bernie@cosmos.it>.
+**     Freely Distributable, as long as source code, documentation and
+**     executable are kept together. Permission is granted to release
+**     modified versions of this program as long as all existing copyright
+**     notices are left intact.
+**
+*/
+
+#define USE_BUILTIN_MATH
+#define INTUI_V36_NAMES_ONLY
+#define __USE_SYSBASE
+#define  CLIB_ALIB_PROTOS_H    /* Avoid including this header file because of
+                                                        * conflicting definitions in BoopsiStubs.h
+                                                        */
+#include <exec/types.h>
+#include <exec/memory.h>
+#include <exec/execbase.h>
+
+#include <dos/dos.h>
+
+#include <intuition/intuition.h>
+#include <intuition/intuitionbase.h>
+#include <intuition/screens.h>
+#include <intuition/classes.h>
+#include <intuition/gadgetclass.h>
+#include <intuition/imageclass.h>
+#include <intuition/icclass.h>
+#include <devices/timer.h>
+
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <proto/dos.h>
+#include <proto/utility.h>
+#include <proto/graphics.h>
+#include <proto/diskfont.h>
+
+#ifdef __STORM__
+       #pragma header
+#endif
+
+#include <gadgets/ScrollButtonClass.h>
+#include <gadgets/ListViewClass.h>
+//#include <gadgets/ListBoxClass.h>
+#include <images/VectorGlyphIClass.h>
+
+#include "CompilerSpecific.h"
+#include "DebugMacros.h"
+#include "BoopsiStubs.h"
+#include "ListMacros.h"
+
+
+/* Local function prototypes */
+
+LONG SAVEDS                                     main                           (void);
+static struct MsgPort          *OpenDemoWindows        (struct List *winlist);
+static void                                     CloseDemoWindows       (struct List *winlist);
+static struct LVHandle         *OpenLVWin                      (CONST_STRPTR pubscreen,
+       struct MsgPort *winport, CONST_STRPTR title, ULONG mode, BOOL useListBox,
+       ULONG left, ULONG top, ULONG width, ULONG height, ULONG moreTags, ...);
+static void                                     CloseLVWin                     (struct LVHandle *lvhandle);
+static struct Gadget           *CreateLVGadgets        (struct LVHandle *lvhandle,
+       struct TagItem *moreTags);
+static void                                     DisposeGadgets         (struct LVHandle *lvhandle);
+static void                                     CreateItems            (struct LVHandle *lvhandle);
+static void                                     CreateImages           (struct DrawInfo *dri);
+static void                                     FreeImages                     (void);
+
+
+/* Width and height for the demo vector images */
+#define IMAGES_WIDTH   56
+#define IMAGES_HEIGHT  48
+
+
+/* Gadgets IDs */
+enum
+{
+       GAD_LV, GAD_VSLIDER, GAD_HSLIDER,
+       GAD_UPBUTTON, GAD_DOWNBUTTON, GAD_LEFTBUTTON, GAD_RIGHTBUTTON,
+       GAD_COUNT
+};
+
+
+/* Images IDs */
+enum
+{
+       IMG_UP, IMG_DOWN, IMG_LEFT, IMG_RIGHT, IMG_COUNT
+};
+
+
+
+
+/* This structure describes an open ListView window */
+struct LVHandle
+{
+       struct MinNode   Link;                  /* Link LVHandle in a list of all windows       */
+       struct Window   *Win;                   /* Pointer to our window                                        */
+       struct Screen   *Scr;                   /* The screen we are opening our windows on     */
+       struct DrawInfo *DrawInfo;              /* DrawInfo for this screen                             */
+       ULONG                    Mode;                  /* ListView operating mode                                      */
+       APTR                     Items;                 /* Items attached to the ListView                       */
+       ULONG                    Total;                 /* Number of items or -1 if unknown                     */
+       struct Gadget   *Gad[GAD_COUNT];/* All our gadgets                                                      */
+       APTR                     Model;                 /* Make boopsi gadgets talk to each other       */
+       struct List              TestList;              /* Items list for LVA_#?List modes                      */
+       ULONG                   *SelectArray;   /* Array for storing multiple selections        */
+       BOOL                     UseListBox;    /* If TRUE, window uses the ListBox class       */
+};
+
+
+
+/* Version tag */
+
+UBYTE versiontag[] = "$VER: ListViewDemo 1.7 (15.12.98) by Bernardo Innocenti"
+       " (compiled with " _COMPILED_WITH ")";
+
+
+
+/* Workaround a bug in StormC header file <proto/utility.h> */
+
+#ifdef __STORM__
+       #define UTILITYBASETYPE struct Library
+#else
+       #define UTILITYBASETYPE struct UtilityBase
+#endif
+
+/* Library bases */
+struct ExecBase                                *SysBase;
+UTILITYBASETYPE                                *UtilityBase;
+struct IntuitionBase           *IntuitionBase;
+struct GfxBase                         *GfxBase;
+struct Library                         *LayersBase;
+struct Library                         *DiskfontBase;
+
+
+/* Our private `boopsi' classes */
+Class                                          *ListViewClass;
+//static Class                         *ListBoxClass;
+static Class                           *ScrollButtonClass;
+
+
+/* `boopsi' images for all windows
+ *
+ * These variables must be NULL at startup time. We are not
+ * going to explicitly initialize them because otherwise
+ * Storm C 2.0 would generate a C++-style constructor to do it :-).
+ * LoasSeg() will clear the BSS data section for us, so these
+ * variables are guaranteed to be NULL anyway.
+ */
+static struct Image            *Img[IMG_COUNT];
+static ULONG                    ImgWidth[IMG_COUNT];
+static ULONG                    ImgHeight[IMG_COUNT];
+static struct TextFont *CustomFont;
+
+
+
+/* Attribute translations for object interconnections */
+
+static LONG MapLVToHSlider[] =
+{
+       LVA_PixelLeft,          PGA_Top,
+       LVA_PixelWidth,         PGA_Total,
+       LVA_PixelHVisible,      PGA_Visible,
+       TAG_DONE
+};
+
+static LONG MapHSliderToLV[] =
+{
+       PGA_Top,                        LVA_PixelLeft,
+       TAG_DONE
+};
+
+/*
+static LONG MapLVToVSlider[] =
+{
+       LVA_Top,                        PGA_Top,
+       LVA_Total,                      PGA_Total,
+       LVA_Visible,            PGA_Visible,
+       TAG_DONE
+};
+*/
+
+static LONG MapLVToVSlider[] =
+{
+       LVA_PixelTop,           PGA_Top,
+       LVA_PixelHeight,        PGA_Total,
+       LVA_PixelVVisible,      PGA_Visible,
+       TAG_DONE
+};
+
+
+/*
+static LONG MapVSliderToLV[] =
+{
+       PGA_Top,        LVA_Top,
+       TAG_DONE
+};
+*/
+
+static LONG MapVSliderToLV[] =
+{
+       PGA_Top,        LVA_PixelTop,
+       TAG_DONE
+};
+
+
+
+static LONG MapUpButtonToLV[] =
+{
+       GA_ID,          LVA_MoveUp,
+       TAG_DONE
+};
+
+static LONG MapDownButtonToLV[] =
+{
+       GA_ID,          LVA_MoveDown,
+       TAG_DONE
+};
+
+static LONG MapLeftButtonToLV[] =
+{
+       GA_ID,          LVA_MoveLeft,
+       TAG_DONE
+};
+
+static LONG MapRightButtonToLV[] =
+{
+       GA_ID,          LVA_MoveRight,
+       TAG_DONE
+};
+
+
+
+/* Test Strings */
+
+/* StormC does not see that the expression "versiontag + 6" is constant
+ * and generates an initializer for it. The following definition
+ * works around this problem.
+ */
+#ifdef __STORM__
+       #define VERSIONTAG "ListViewDemo 1.7 by Bernardo Innocenti (compiled with " _COMPILED_WITH ")"
+#else
+       #define VERSIONTAG versiontag + 6
+#endif
+
+static STRPTR TestStrings[] =
+{
+       VERSIONTAG,
+       NULL,
+       "This `boopsi' ListView class supports all the features",
+       "of the Gadtools LISTVIEW_KIND, plus more stuff:",
+       NULL,
+       " + Easy to use (almost a drop-in replacement for LISTVIEW_KIND)",
+       " + Can be resized and supports GREL_#? flags",
+       " + Multiple selection of items",
+       " + Notifies your `boopsi' sliders",
+       " + Multiple columns (TODO)",
+       " + Redraws quickly without clearing (which is good for solid window sizing)",
+       " + Horizontal scrolling (TODO)",
+       " + Items with `boopsi' images",
+       " + Using arrays instead of exec lists",
+       " + You can use `boopsi' label images instead of plain text",
+       " + You can use your own custom rendering hook",
+       " + You can use your own item item-retriving callback hook",
+       " + List title (TODO)",
+       " + Full Keyboard control (all control, alt and shift key combinations supported)",
+       " + Asynchronous scrolling with inertia (TODO)",
+       " + OS 3.0 optimized (V39-only version also available)",
+       " + RTG friendly and optimized (no planar stuff in chunky bitmaps)",
+       " + Small code! (<10K)",
+       " + Written in C to be highly portable across compilers and CPUs",
+       " + Full commented source code included",
+       " + Source code compiles with SAS/C, StormC and GCC",
+       " + Subclasses can be easlily derived from the base listview class",
+       NULL,
+       "Please send comments to <bernardo.innocenti@usa.net>."
+};
+
+#define TESTSTRINGS_CNT (sizeof (TestStrings) / sizeof (CONST_STRPTR))
+
+
+
+LONG SAVEDS _main(void)
+
+/* Main program entry point.  When linking without startup code, this
+ * must be the first function in the first object module listed on the
+ * linker command line.  We also need to initialize SysBase and open
+ * all needed libraries manually.
+ */
+{
+       struct MinList   winlist;
+       struct MsgPort  *winport;
+       struct Library  *VectorGlyphBase = NULL;
+       LONG                     sigwait, sigrcvd;
+       LONG                     retval = RETURN_FAIL;  /* = RETURN_FAIL */
+       BOOL                     quit   = FALSE;
+
+
+       /* Initialize SysBase */
+       SysBase = *((struct ExecBase **)4UL);
+
+       /* Open system libraries */
+
+       if ((UtilityBase = (UTILITYBASETYPE *) OpenLibrary("utility.library", 37L)) &&
+               (IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 39L)) &&
+               (GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 39L)) &&
+               (LayersBase = OpenLibrary("layers.library", 39L)))
+       {
+               if ((ListViewClass = MakeListViewClass()) &&
+//                     (ListBoxClass = MakeListBoxClass()) &&
+                       (ScrollButtonClass = MakeScrollButtonClass()) &&
+                       (VectorGlyphBase = OpenLibrary("images/vectorglyph.image", 0)))
+               {
+                       NEWLIST((struct List *)&winlist);
+
+                       if (winport = OpenDemoWindows((struct List *)&winlist))
+                       {
+                               /* Pre-calculate the signal mask for Wait() */
+                               sigwait = (1 << winport->mp_SigBit) |
+                                       SIGBREAKF_CTRL_C;
+
+                               /* Now for the main loop.  As you can see, it is really
+                                * very compact.  That's the magic of boopsi! :-)
+                                */
+                               while (!quit)
+                               {
+                                       /* Sleep until something interesting occurs */
+                                       sigrcvd = Wait(sigwait);
+
+                                       /* Now handle received signals */
+
+                                       /* Break signal? */
+                                       if (sigrcvd & SIGBREAKF_CTRL_C)
+                                               quit = TRUE;
+
+                                       /* IDCMP message? */
+                                       if (sigrcvd & (1 << winport->mp_SigBit))
+                                       {
+                                               struct IntuiMessage     *msg;
+
+                                               while (msg = (struct IntuiMessage *) GetMsg(winport))
+                                               {
+                                                       switch (msg->Class)
+                                                       {
+                                                               case IDCMP_CLOSEWINDOW:
+                                                                       quit = TRUE;
+                                                                       break;
+
+                                                               default:
+                                                                       break;
+                                                       }
+                                                       ReplyMsg((struct Message *) msg);
+                                               }
+                                       }
+                               } /* End while (!quit) */
+
+                               retval = 0;     /* RETURN_OK */
+
+                               CloseDemoWindows((struct List *)&winlist);
+                       }
+
+                       FreeImages ();
+               }
+
+               /* These cannot fail. Passing NULL is ok. */
+               CloseLibrary((struct Library *)VectorGlyphBase);
+               FreeScrollButtonClass(ScrollButtonClass);
+//             FreeListBoxClass(ListBoxClass);
+               FreeListViewClass(ListViewClass);
+       }
+
+       /* Passing NULL to CloseLibrary() was illegal in pre-V37 Exec.
+        * To avoid crashing when someone attempts to run this program
+        * on an old OS, we need to test the first library base we tried
+        * to open.
+        */
+       if (UtilityBase)
+       {
+               CloseLibrary((struct Library *)LayersBase);
+               CloseLibrary((struct Library *)GfxBase);
+               CloseLibrary((struct Library *)IntuitionBase);
+               CloseLibrary((struct Library *)UtilityBase);
+       }
+
+       return retval;
+}
+
+
+
+static struct MsgPort *OpenDemoWindows(struct List *winlist)
+{
+       struct LVHandle *lvhandle;
+       struct MsgPort  *winport;
+
+       if (DiskfontBase = OpenLibrary("diskfont.library", 0L))
+       {
+               static struct TextAttr attr =
+               {
+                       "times.font",
+                       24,
+                       FSB_ITALIC,
+                       0
+               };
+
+               CustomFont = OpenDiskFont(&attr);
+               CloseLibrary(DiskfontBase);
+       }
+
+       /* Setup windows shared Message Port */
+       if (winport = CreateMsgPort())
+       {
+/*             if (lvhandle = OpenLVWin(NULL, winport,
+                       "ListBox test",
+                       LVA_StringList,         TRUE, 600, 20, 320, 128,
+                       TAG_DONE))
+                       ADDTAIL(winlist, (struct Node *)lvhandle);
+*/
+/*             if (lvhandle = OpenLVWin(NULL, winport,
+                       "LVA_TextFont = times/24/italic, LVA_Clipped = TRUE",
+                       LVA_StringList,         FALSE, 320, 320, 320, 64,
+                       LVA_TextFont,           CustomFont,
+                       LVA_Clipped,            TRUE,
+                       TAG_DONE))
+                       ADDTAIL(winlist, (struct Node *)lvhandle);
+*/
+/*             if (lvhandle = OpenLVWin(NULL, winport,
+                       "LAYOUTA_Spacing = 4",
+                       LVA_StringList,         FALSE, 256, 256, 320, 64,
+                       LAYOUTA_Spacing,        4,
+                       TAG_DONE))
+                       ADDTAIL(winlist, (struct Node *)lvhandle);
+*/
+/*             if (lvhandle = OpenLVWin(NULL, winport,
+                       "GA_ReadOnly = TRUE; LVA_Selected = 3",
+                       LVA_StringList, FALSE, 192, 192, 320, 64,
+                       GA_ReadOnly,            TRUE,
+                       LVA_Selected,           3,
+                       TAG_DONE))
+                       ADDTAIL(winlist, (struct Node *)lvhandle);
+*/
+               if (lvhandle = OpenLVWin(NULL, winport,
+                       "Single selection image list, LVA_Clipped = TRUE",
+                       LVA_ImageList, FALSE, 128, 128, 320, 128,
+                       LVA_ItemHeight,         IMAGES_HEIGHT,
+                       LVA_Clipped,            TRUE,
+                       TAG_DONE))
+                       ADDTAIL(winlist, (struct Node *)lvhandle);
+/*
+               if (lvhandle = OpenLVWin(NULL, winport,
+                       "LVA_DoMultiSelect = TRUE; LVA_StringArray",
+                       LVA_StringArray,        FALSE, 64, 64, 320, 128,
+                       LVA_DoMultiSelect,      TRUE,
+                       TAG_DONE))
+                       ADDTAIL(winlist, (struct Node *)lvhandle);
+*/
+/*             if (lvhandle = OpenLVWin(NULL, winport,
+                       "Plain, single selection string list",
+                       LVA_StringList,         FALSE, 0, 20, 320, 128,
+                       TAG_DONE))
+                       ADDTAIL(winlist, (struct Node *)lvhandle);
+*/
+               /* Abort only if no windows could be opened */
+               if (IsListEmpty(winlist))
+               {
+                       DeleteMsgPort(winport);
+                       CloseFont (CustomFont);
+                       return NULL;
+               }
+       }
+
+       return winport;
+}
+
+
+
+static void CloseDemoWindows(struct List *winlist)
+{
+       struct MsgPort  *winport = NULL;
+       struct LVHandle *lvhandle;
+
+       while (lvhandle = (struct LVHandle *) REMHEAD(winlist))
+       {
+               /* Safe way to close a shared IDCMP port window */
+
+               Forbid();
+               {
+                       struct Node *succ;
+                       struct Message *msg;
+
+                       winport = lvhandle->Win->UserPort;
+                       msg = (struct Message *) winport->mp_MsgList.lh_Head;
+
+                       /* Now remove any pending message from the shared IDCMP port */
+                       while (succ = msg->mn_Node.ln_Succ)
+                       {
+                               /* Since we are closing all our windows at once,
+                                * we don't need to check to which window this
+                                * message was addressed.
+                                */
+                               REMOVE((struct Node *)msg);
+                               ReplyMsg (msg);
+                               msg = (struct Message *) succ;
+                       }
+
+                       /* Keep intuition from freeing our port... */
+                       lvhandle->Win->UserPort = NULL;
+
+                       /* ...and from sending us any more messages. */
+                       ModifyIDCMP(lvhandle->Win, 0L);
+               }
+               Permit();
+
+               CloseLVWin(lvhandle);
+       }
+
+       DeleteMsgPort(winport); /* NULL is ok */
+
+       if (CustomFont)
+               CloseFont(CustomFont);
+}
+
+
+
+
+static struct LVHandle *OpenLVWin(CONST_STRPTR pubscreen,
+       struct MsgPort *winport, CONST_STRPTR title, ULONG mode, BOOL useListBox,
+       ULONG left, ULONG top, ULONG width, ULONG height, ULONG moreTags, ...)
+{
+       struct LVHandle *lvhandle;
+       struct Gadget   *glist;
+
+       if (lvhandle = AllocMem(sizeof (struct LVHandle), MEMF_ANY | MEMF_CLEAR))
+       {
+               if (lvhandle->Scr = LockPubScreen(pubscreen))
+               {
+                       /* GetScreenDrawInfo() never fails */
+                       lvhandle->DrawInfo = GetScreenDrawInfo(lvhandle->Scr);
+
+                       /* Set listview operating mode and ListBox flag */
+                       lvhandle->Mode                  = mode;
+                       lvhandle->UseListBox    = useListBox;
+
+                       CreateItems (lvhandle);
+
+                       if (glist = CreateLVGadgets(lvhandle, (struct TagItem *)&moreTags))
+                       {
+                               if (lvhandle->Win = OpenWindowTags(NULL,
+                                       WA_Top,                         top,
+                                       WA_Left,                        left,
+                                       WA_InnerWidth,          width,
+                                       WA_InnerHeight,         height,
+                                       WA_PubScreen,           lvhandle->Scr,
+                                       WA_Gadgets,                     glist,
+                                       WA_Title,                       title,
+                                       /* Since we are going to redraw the whole window anyway,
+                                        * we can disable backfilling to avoid flashes while
+                                        * resizing or revealing the window.
+                                        */
+                                       WA_BackFill,            useListBox ? NULL : LAYERS_NOBACKFILL,
+                                       WA_ScreenTitle,         versiontag + 6,
+                                       WA_Flags,                       WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_SIZEGADGET | WFLG_SIZEBRIGHT | WFLG_SIZEBBOTTOM |
+                                                                               WFLG_CLOSEGADGET | WFLG_SIMPLE_REFRESH | WFLG_NOCAREREFRESH,
+                                       WA_PubScreenFallBack, TRUE,
+                                       WA_AutoAdjust,          TRUE,
+                                       WA_MinWidth,            64,
+                                       WA_MinHeight,           64,
+                                       WA_MaxWidth,            -1,
+                                       WA_MaxHeight,           -1,
+                                       TAG_DONE))
+                               {
+                                       lvhandle->Win->UserPort = winport;
+                                       ModifyIDCMP(lvhandle->Win, IDCMP_CLOSEWINDOW);
+
+                                       /* We need to keep our screen locked all the time
+                                        * because we want to free the associated DrawInfo
+                                        * *after* the window has been closed and
+                                        * FreeScreenDrawInfo() wants a pointer to a *valid*
+                                        * Screen.
+                                        */
+                                       return lvhandle;
+                               }
+
+                               DisposeGadgets(lvhandle);
+                       }
+
+                       FreeScreenDrawInfo(lvhandle->Scr, lvhandle->DrawInfo);
+                       /* lvhandle->DrawInfo = NULL */
+
+                       UnlockPubScreen(NULL, lvhandle->Scr);
+               }
+               FreeMem(lvhandle, sizeof(struct LVHandle));
+       }
+       return NULL;
+}
+
+
+
+static void CloseLVWin(struct LVHandle *lvhandle)
+{
+       /* Close our window. No need to reply queued messages,
+        * Intuition is clever enough to care about this for us.
+        */
+       CloseWindow(lvhandle->Win);
+       DisposeGadgets(lvhandle);
+       UnlockPubScreen(NULL, lvhandle->Scr);
+
+       FreeVec(lvhandle->SelectArray); /* NULL is ok */
+
+       if ((lvhandle->Mode == LVA_StringList) || (lvhandle->Mode == LVA_ImageList))
+       {
+               struct Node *node;
+
+               /* Free the test list */
+               while (node = REMHEAD (&lvhandle->TestList))
+               {
+                       if (lvhandle->Mode == LVA_ImageList)
+                               DisposeObject ((Object *)node->ln_Name);
+                       FreeMem (node, sizeof (struct Node));
+               }
+       }
+
+       FreeMem (lvhandle, sizeof (struct LVHandle));
+}
+
+
+
+/*     Diagram of object interconnections for the ListView window
+ *     ==========================================================
+ *
+ *                ScrollButtonClass objects
+ *     +----------+ +------------+ +------------+ +-------------+
+ *     | UpButton | | DownButton | | LeftButton | | RightButton |
+ *     +----------+ +------------+ +------------+ +-------------+
+ *      | GA_ID =     | GA_ID =       | GA_ID =       | GA_ID =
+ *      | LVA_MoveUp  | LVA_MoveDown  | LVA_MoveLeft  | LVA_MoveRight
+ *      |             |               |               |
+ *      |  +----------+               |               |
+ *      |  |  +-----------------------+               |
+ *      |  |  |  +------------------------------------+
+ *      |  |  |  |        propgclass object     icclass object
+ *      |  |  |  |          +-----------+      +--------------+
+ *      |  |  |  |          |  HSlider  |<-----| PIPToHSlider |
+ *      |  |  |  |          +-----------+      +--------------+
+ *      |  |  |  |     PGA_Top =  |                 ^ LVA_Top  = PGA_Top
+ *      |  |  |  |     LVA_Left   |                 | LVA_Visible = PGA_Visible
+ *      |  |  |  |                |                 |
+ *      V  V  V  V                V                 |
+ *     +-----------+         ***********            |
+ *     |           |-------->*         *------------+
+ *     | ListView  |         *  Model  *
+ *     |           |<--------*         *------------+
+ *     +-----------+         ***********            |
+ *                                ^                 |
+ *                    PGA_Top =   |                 |
+ *                    LVA_Top     |                 V  icclass object
+ *                          +-----------+      +--------------+
+ *                          |  VSlider  |<-----| PIPToVSlider |
+ *                          +-----------+      +--------------+
+ *                        propgclass object     LVA_Top     = PGA_Top
+ *                                              LVA_Visible = PGA_Visible
+ */
+
+static struct Gadget *CreateLVGadgets(struct LVHandle *lvhandle,
+       struct TagItem *moreTags)
+{
+       struct Screen   *scr = lvhandle->Scr;
+       ULONG SizeWidth = 18, SizeHeight = 11;  /* Default size */
+       struct Image    *SizeImage;
+
+       /* Create a size image to get its... uhm... size */
+       if (SizeImage = NewObject(NULL, SYSICLASS,
+               SYSIA_Which,    SIZEIMAGE,
+               SYSIA_DrawInfo, lvhandle->DrawInfo,
+               TAG_DONE))
+       {
+               /* Get size gadget geometry */
+               GetAttr(IA_Width, SizeImage, &SizeWidth);
+               GetAttr(IA_Height, SizeImage, &SizeHeight);
+
+               /* And then get rid of it... */
+               DisposeObject(SizeImage);
+       }
+
+
+/*     if (lvhandle->UseListBox)
+       {
+               lvhandle->Gad[GAD_LV] = NewObject(ListBoxClass, NULL,
+                       GA_ID,                          GAD_LV,
+                       GA_Left,                        scr->WBorLeft,
+                       GA_Top,                         scr->WBorTop + scr->Font->ta_YSize + 1,
+                       GA_RelWidth,            - SizeWidth - scr->WBorLeft,
+                       GA_RelHeight,           - (scr->WBorTop + scr->Font->ta_YSize + SizeHeight + 1),
+                       GA_DrawInfo,            lvhandle->DrawInfo,
+                       lvhandle->Mode,         lvhandle->Items,
+                       LVA_Total,                      lvhandle->Total,
+                       LVA_SelectArray,        lvhandle->SelectArray,
+                       TAG_MORE,                       moreTags);
+
+               return lvhandle->Gad[GAD_LV];
+       }
+*/
+
+       /* Code to create normal ListView class and its gadgets */
+
+
+       /* No need to check this: in case of failure we would just
+        * get no images in the scroll buttons, but we can still try
+        * to open our window anyway.
+        */
+       CreateImages(lvhandle->DrawInfo);
+
+       if (lvhandle->Model = NewObjectA(NULL, MODELCLASS, NULL))
+               if (lvhandle->Gad[GAD_LV] = NewObject(ListViewClass, NULL,
+                       GA_ID,                          GAD_LV,
+                       GA_Left,                        scr->WBorLeft,
+                       GA_Top,                         scr->WBorTop + scr->Font->ta_YSize + 1,
+                       GA_RelWidth,            - SizeWidth - scr->WBorLeft,
+                       GA_RelHeight,           - (scr->WBorTop + scr->Font->ta_YSize + SizeHeight + 1),
+                       GA_DrawInfo,            lvhandle->DrawInfo,
+                       ICA_TARGET,                     lvhandle->Model,
+                       lvhandle->Mode,         lvhandle->Items,
+                       LVA_Total,                      lvhandle->Total,
+                       LVA_SelectArray,        lvhandle->SelectArray,
+                       TAG_MORE,                       moreTags))
+                       if (lvhandle->Gad[GAD_VSLIDER] = NewObject(NULL, PROPGCLASS,
+                               GA_ID,                  GAD_VSLIDER,
+                               GA_Previous,    lvhandle->Gad[GAD_LV],
+                               GA_RelRight,    - SizeWidth + 5,
+                               GA_Top,                 scr->WBorTop + scr->Font->ta_YSize + 2,
+                               GA_Width,               SizeWidth - 8,
+                               GA_RelHeight,   - (scr->WBorTop + scr->Font->ta_YSize +
+                                                               SizeHeight + ImgHeight[IMG_DOWN] + ImgHeight[IMG_UP] + 4),
+                               GA_RightBorder, TRUE,
+                               GA_DrawInfo,    lvhandle->DrawInfo,
+                               PGA_Freedom,    FREEVERT,
+                               PGA_Borderless, ((lvhandle->DrawInfo->dri_Flags & DRIF_NEWLOOK) &&
+                                                                (lvhandle->DrawInfo->dri_Depth != 1)),
+                               PGA_NewLook,    TRUE,
+                               ICA_TARGET,             lvhandle->Model,
+                               ICA_MAP,                MapVSliderToLV,
+                               TAG_DONE))
+                               if (lvhandle->Gad[GAD_HSLIDER] = NewObject(NULL, PROPGCLASS,
+                                       GA_ID,                  GAD_HSLIDER,
+                                       GA_Previous,    lvhandle->Gad[GAD_VSLIDER],
+                                       GA_RelBottom,   - SizeHeight + ((SizeHeight > 15) ? 4 : 3),
+                                       GA_Left,                scr->WBorLeft,
+                                       GA_Height,              SizeHeight - ((SizeHeight > 15)  ? 6 : 4),
+                                       GA_RelWidth,    - (SizeWidth + ImgWidth[IMG_RIGHT] + ImgWidth[IMG_LEFT] + scr->WBorLeft + 2),
+                                       GA_BottomBorder,TRUE,
+                                       GA_DrawInfo,    lvhandle->DrawInfo,
+                                       PGA_Freedom,    FREEHORIZ,
+                                       PGA_Borderless, ((lvhandle->DrawInfo->dri_Flags & DRIF_NEWLOOK) &&
+                                                                        (lvhandle->DrawInfo->dri_Depth != 1)),
+                                       PGA_NewLook,    TRUE,
+                                       ICA_TARGET,             lvhandle->Model,
+                                       ICA_MAP,                MapHSliderToLV,
+                                       TAG_DONE))
+                                       if (lvhandle->Gad[GAD_UPBUTTON] = NewObject(ScrollButtonClass, NULL,
+                                               GA_ID,                  GAD_UPBUTTON,
+                                               GA_Previous,    lvhandle->Gad[GAD_HSLIDER],
+                                               GA_RelBottom,   - SizeHeight - ImgHeight[IMG_DOWN] - ImgHeight[IMG_UP] + 1,
+                                               GA_RelRight,    - ImgWidth[IMG_DOWN] + 1,
+                                               GA_RightBorder, TRUE,
+                                               GA_DrawInfo,    lvhandle->DrawInfo,
+                                               GA_Image,               Img[IMG_UP],
+                                               ICA_TARGET,             lvhandle->Gad[GAD_LV],
+                                               ICA_MAP,                MapUpButtonToLV,
+                                               TAG_DONE))
+                                               if (lvhandle->Gad[GAD_DOWNBUTTON] = NewObject(ScrollButtonClass, NULL,
+                                                       GA_ID,                  GAD_DOWNBUTTON,
+                                                       GA_Previous,    lvhandle->Gad[GAD_UPBUTTON],
+                                                       GA_RelBottom,   - SizeHeight - ImgHeight[IMG_DOWN] + 1,
+                                                       GA_RelRight,    - ImgWidth[IMG_DOWN] + 1,
+                                                       GA_RightBorder, TRUE,
+                                                       GA_DrawInfo,    lvhandle->DrawInfo,
+                                                       GA_Image,               Img[IMG_DOWN],
+                                                       ICA_TARGET,             lvhandle->Gad[GAD_LV],
+                                                       ICA_MAP,                MapDownButtonToLV,
+                                                       TAG_DONE))
+                                                       if (lvhandle->Gad[GAD_LEFTBUTTON] = NewObject(ScrollButtonClass, NULL,
+                                                               GA_ID,                  GAD_LEFTBUTTON,
+                                                               GA_Previous,    lvhandle->Gad[GAD_DOWNBUTTON],
+                                                               GA_RelBottom,   - ImgHeight[IMG_LEFT] + 1,
+                                                               GA_RelRight,    - SizeWidth - ImgWidth[IMG_RIGHT] - ImgWidth[IMG_LEFT] + 1,
+                                                               GA_BottomBorder,TRUE,
+                                                               GA_DrawInfo,    lvhandle->DrawInfo,
+                                                               GA_Image,               Img[IMG_LEFT],
+                                                               ICA_TARGET,             lvhandle->Gad[GAD_LV],
+                                                               ICA_MAP,                MapLeftButtonToLV,
+                                                               TAG_DONE))
+                                                               if (lvhandle->Gad[GAD_RIGHTBUTTON] = NewObject(ScrollButtonClass, NULL,
+                                                                       GA_ID,                  GAD_RIGHTBUTTON,
+                                                                       GA_Previous,    lvhandle->Gad[GAD_LEFTBUTTON],
+                                                                       GA_RelBottom,   - ImgHeight[IMG_RIGHT] + 1,
+                                                                       GA_RelRight,    - SizeWidth - ImgWidth[IMG_RIGHT] + 1,
+                                                                       GA_BottomBorder,TRUE,
+                                                                       GA_DrawInfo,    lvhandle->DrawInfo,
+                                                                       GA_Image,               Img[IMG_RIGHT],
+                                                                       ICA_TARGET,             lvhandle->Gad[GAD_LV],
+                                                                       ICA_MAP,                MapRightButtonToLV,
+                                                                       TAG_DONE))
+                                                                       {
+                                                                               APTR icobject;
+
+                                                                               /* Connect VSlider to Model */
+
+                                                                               if (icobject = NewObject(NULL, ICCLASS,
+                                                                                       ICA_TARGET,     lvhandle->Gad[GAD_VSLIDER],
+                                                                                       ICA_MAP,        MapLVToVSlider,
+                                                                                       TAG_DONE))
+                                                                                       if (!DoMethod(lvhandle->Model, OM_ADDMEMBER, icobject))
+                                                                                               DisposeObject (icobject);
+
+                                                                               /* Connect HSlider to Model */
+
+                                                                               if (icobject = NewObject(NULL, ICCLASS,
+                                                                                       ICA_TARGET,     lvhandle->Gad[GAD_HSLIDER],
+                                                                                       ICA_MAP,        MapLVToHSlider,
+                                                                                       TAG_DONE))
+                                                                                       if (!DoMethod(lvhandle->Model, OM_ADDMEMBER, icobject))
+                                                                                               DisposeObject (icobject);
+
+                                                                               /* Connect Model to ListView */
+
+                                                                               SetAttrs(lvhandle->Model,
+                                                                                       ICA_TARGET, lvhandle->Gad[GAD_LV],
+                                                                                       TAG_DONE);
+
+                                                                               return lvhandle->Gad[GAD_LV];
+                                                                       }
+       DisposeGadgets(lvhandle);
+
+       return NULL;
+}
+
+
+
+static void DisposeGadgets(struct LVHandle *lvhandle)
+{
+       ULONG i;
+
+       for (i = 0; i < GAD_COUNT; i++)
+       {
+               DisposeObject(lvhandle->Gad[i]);
+               /* lvhandle->Gad[i] = NULL; */
+       }
+
+       /* Freeing the Model will also free its two targets */
+       DisposeObject(lvhandle->Model);
+       /* lvhandle->Model = NULL */
+}
+
+
+
+static void CreateItems(struct LVHandle *lvhandle)
+{
+       if ((lvhandle->Mode == LVA_StringList) || (lvhandle->Mode == LVA_ImageList))
+       {
+               struct Node             *node;
+               ULONG                    i, cnt;
+
+
+               if (lvhandle->Mode == LVA_StringList)
+                       cnt = TESTSTRINGS_CNT;
+               else /* LVA_ImageList */
+                       cnt = VG_IMGCOUNT * 8;
+
+
+               /* Build a list of nodes to test the list */
+
+               NEWLIST(&lvhandle->TestList);
+
+               for (i = 0; i < cnt; i++)
+               {
+                       if (node = AllocMem(sizeof (struct Node), MEMF_PUBLIC))
+                       {
+                               if (lvhandle->Mode == LVA_StringList)
+                                       node->ln_Name = TestStrings[i];
+                               else
+                                       node->ln_Name = (STRPTR) NewObject(NULL, VECTORGLYPHCLASS,
+                                               SYSIA_Which,    i % VG_IMGCOUNT,
+                                               SYSIA_DrawInfo, lvhandle->DrawInfo,
+                                               IA_Width,               IMAGES_WIDTH,
+                                               IA_Height,              IMAGES_HEIGHT,
+                                               TAG_DONE);
+
+                               /* Unselect all items */
+                               node->ln_Type = 0;
+
+                               ADDTAIL (&lvhandle->TestList, node);
+
+                               lvhandle->Total++;
+                       }
+               }
+
+               lvhandle->Items = &lvhandle->TestList;
+       }
+       else if (lvhandle->Mode == LVA_StringArray)
+       {
+               lvhandle->Items = TestStrings;
+               lvhandle->Total = TESTSTRINGS_CNT;
+               lvhandle->SelectArray = AllocVec (TESTSTRINGS_CNT * sizeof (ULONG),
+                       MEMF_CLEAR | MEMF_PUBLIC);
+       }
+       else /* (lvhandle->Mode == LVA_ImageArray) */
+       {
+               lvhandle->Items = NULL; /* No items     */
+               lvhandle->Total = -1;   /* Unknown      */
+       }
+}
+
+
+
+static void CreateImages(struct DrawInfo *dri)
+
+/* Create 4 arrow images for the window scroll buttons.
+ *
+ * Why bother checking for failure? The arrow images are not
+ * life critical in our program...
+ */
+{
+       static ULONG imagetypes[IMG_COUNT] = { UPIMAGE, DOWNIMAGE, LEFTIMAGE, RIGHTIMAGE };
+       ULONG i;
+
+       for (i = 0; i < IMG_COUNT; i++)
+               if (!Img[i])
+                       if (Img[i] = (struct Image *)NewObject (NULL, SYSICLASS,
+                               SYSIA_Which,    imagetypes[i],
+                               SYSIA_DrawInfo, dri,
+                               TAG_DONE))
+                       {
+                               /* Ask image width and height */
+                               GetAttr(IA_Width, Img[i], &ImgWidth[i]);
+                               GetAttr(IA_Height, Img[i], &ImgHeight[i]);
+                       }
+}
+
+
+
+static void FreeImages (void)
+{
+       ULONG i;
+
+       for (i = 0; i < IMG_COUNT; i++)
+               DisposeObject((APTR)Img[i]);    /* DisposeObject(NULL) is safe */
+}
diff --git a/gadgets/ListView/ListViewClass.c b/gadgets/ListView/ListViewClass.c
new file mode 100644 (file)
index 0000000..5a1b51f
--- /dev/null
@@ -0,0 +1,2238 @@
+/*
+**     $Id: ListViewClass.c,v 1.4 2000/01/12 21:18:06 bernie Exp $
+**
+**     Copyright (C) 1996,97,99 Bernardo Innocenti <bernie@cosmos.it>
+**     All rights reserved.
+**
+**     Use 4 chars wide TABs to read this file
+**
+**     GadTools-like `boopsi' ListView gadget class
+*/
+
+/* Definitions for system headers */
+#define USE_BUILTIN_MATH
+#define INTUI_V36_NAMES_ONLY
+#define INTUITION_IOBSOLETE_H
+#define __USE_SYSBASE
+#define  CLIB_ALIB_PROTOS_H            /* Avoid dupe defines of boopsi funcs */
+
+#include <exec/types.h>
+#include <exec/libraries.h>
+#include <intuition/intuition.h>
+#include <intuition/intuitionbase.h>
+#include <intuition/classes.h>
+#include <intuition/gadgetclass.h>
+#include <intuition/imageclass.h>
+#include <graphics/gfxbase.h>
+#include <graphics/gfxmacros.h>
+
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <proto/graphics.h>
+#include <proto/layers.h>
+#include <proto/utility.h>
+
+#include <CompilerSpecific.h>
+#include <DebugMacros.h>
+#include <DiagnosticMacros.h>
+#include <BoopsiStubs.h>
+#include <BoopsiLib.h>
+
+#define LV_GADTOOLS_STUFF
+#include <gadgets/ListViewClass.h>
+
+#ifdef __STORM__
+       #pragma header
+#endif
+
+
+
+/* ListView private instance data */
+
+
+/* Type of a listview hook function */
+typedef        ASMCALL APTR    LVHook(
+       REG(a0, struct Hook     *hook), REG(a1, APTR item), REG(a2, struct lvGetItem *lvg));
+typedef        ASMCALL APTR    LVDrawHook(
+       REG(a0, struct Hook     *hook), REG(a1, APTR item), REG(a2, struct lvDrawItem *lvdi));
+
+struct LVData
+{
+       APTR                     Items;                         /* The list/array of items                              */
+       LONG                     Top;                           /* Ordinal nr. of the top visible item  */
+       APTR                     TopPtr;                        /* Pointer to the top visible item              */
+       LONG                     Total;                         /* Total nr. of items in the list               */
+       LONG                     Visible;                       /* Number of items visible in the list  */
+       LONG                     PixelTop;                      /* Pixel-wise offset from the top               */
+       LONG                     Selected;                      /* Ordinal nr. of the selected item             */
+       APTR                     SelectedPtr;           /* Pointer to the selected item                 */
+       ULONG                    SelectCount;           /* Number of items currently selected   */
+       ULONG                    MaxSelect;                     /* Maximum nr. of selections to allow   */
+
+       /* Old values used to track scrolling amount in GM_RENDER */
+       LONG                     OldTop;
+       LONG                     OldPixelTop;
+       LONG                     OldSelected;
+       APTR                     OldSelectedPtr;
+
+       ULONG                    DragSelect;            /* Status of drag selection                             */
+       LONG                     ItemHeight;            /* Height of one item in pixels                 */
+       LONG                     Spacing;                       /* Spacing between items in pixels              */
+       LONG                     MaxScroll;                     /* Redraw all when scrolling too much   */
+       LONG                     ScrollRatio;           /* max visible/scrolled ratio                   */
+       ULONG                   *SelectArray;           /* Array of selected items. May be NULL */
+       LONG                     BackupSelected;        /* Used by RMB undo                                     */
+       LONG                     BackupPixelTop;        /* Used by RMB undo                                             */
+       WORD                     MiddleMouseY;          /* Initial Y position for MMB scrolling */
+       ULONG                    Flags;                         /* See <listviewclass.h>                                */
+       ULONG                    MaxPen;                        /* Highest pen number used                              */
+       ULONG                    DoubleClickSecs, DoubleClickMicros;
+
+       /* User or internal hooks */
+       LVHook                  *GetItemFunc;
+       LVHook                  *GetNextFunc;
+       LVHook                  *GetPrevFunc;
+       LVHook                  *DrawBeginFunc;
+       LVHook                  *DrawEndFunc;
+       LVDrawHook              *DrawItemFunc;
+       struct Hook             *CallBack;                      /* Callback hook provided by user       */
+
+       struct TextFont *Font;                          /* Font used to render text labels      */
+       struct Region   *ClipRegion;            /* Used in LVA_Clipped mode                     */
+
+
+       /* These two have the same meaning, but we keep both updated
+        * because the Rectangle structure (MinX, MinY, MaxX, MaxY)
+        * is more handy in some cases, while the IBox structure
+        * (Left/Top/Width/Height) is best for other cases.
+        */
+       struct IBox              GBox;
+       struct Rectangle GRect;
+
+       struct IBox              FrameBox;                      /* The size of our surrounding frame */
+};
+
+
+
+/* Local function prototypes */
+
+static void            LV_GMRender             (Class *cl, struct Gadget *g, struct gpRender *msg);
+static ULONG   LV_GMGoActive   (Class *cl, struct Gadget *g, struct gpInput *msg);
+static ULONG   LV_GMHandleInput(Class *cl, struct Gadget *g, struct gpInput *msg);
+static void            LV_GMGoInactive (Class *cl, struct Gadget *g, struct gpGoInactive *msg);
+static void            LV_GMLayout             (Class *cl, struct Gadget *g, struct gpLayout *msg);
+static ULONG   LV_OMSet                (Class *cl, struct Gadget *g, struct opUpdate *msg);
+static ULONG   LV_OMGet                (Class *cl, struct Gadget *g, struct opGet *msg);
+static ULONG   LV_OMNew                (Class *cl, struct Gadget *g, struct opSet *msg);
+static void            LV_OMDispose    (Class *cl, struct Gadget *g, Msg msg);
+
+static void            RedrawItems             (struct LVData *lv, struct gpRender *msg, LONG first, LONG last, APTR item);
+INLINE LONG            ItemHit                 (struct LVData *lv, WORD x, WORD y);
+INLINE APTR    GetItem                 (struct LVData *lv, LONG num);
+INLINE APTR    GetNext                 (struct LVData *lv, APTR item, LONG num);
+INLINE APTR    GetPrev                 (struct LVData *lv, APTR item, LONG num);
+INLINE ULONG   CountNodes              (struct List *list);
+static ULONG   CountSelections (struct LVData *lv);
+INLINE ULONG   IsItemSelected  (struct LVData *lv, APTR item, LONG num);
+
+/* Definitions for the builtin List hooks */
+LVHook         ListGetItem;
+LVHook         ListGetNext;
+LVHook         ListGetPrev;
+LVDrawHook     ListStringDrawItem;
+LVDrawHook     ListImageDrawItem;
+
+/* Definitions for the builtin Array hooks */
+LVHook         ArrayGetItem;
+LVDrawHook     StringDrawItem;
+LVDrawHook     ImageDrawItem;
+
+
+
+#if (CLASS_FLAVOUR & FLAVOUR_CLASSLIB)
+
+       /* Class library support functions */
+       struct ClassLibrary     *       HOOKCALL _UserLibInit           (REG(a6, struct ClassLibrary *mybase));
+       struct ClassLibrary     *       HOOKCALL _UserLibCleanup        (REG(a6, struct ClassLibrary *mybase));
+       Class *                                 HOOKCALL _GetEngine                     (REG(a6, struct ClassLibrary *mybase));
+
+       /* Library data */
+       const UBYTE LibName[] = "listview.gadget";
+       const UBYTE LibVer[] = { '$', 'V', 'E', 'R', ':', ' ' };
+       const UBYTE LibId[] = "listview.gadget 1.0 (28.8.99) © 1997-1999 Bernardo Innocenti\n";
+
+       /* Workaround a bug in StormC header file <proto/utility.h> */
+       #ifdef __STORM__
+               #define UTILITYBASETYPE struct Library
+       #else
+               #define UTILITYBASETYPE struct UtilityBase
+       #endif
+
+       /* Library bases */
+       struct ExecBase                 *SysBase                = NULL;
+       struct IntuitionBase    *IntuitionBase  = NULL;
+       UTILITYBASETYPE                 *UtilityBase    = NULL;
+       struct GfxBase                  *GfxBase                = NULL;
+       struct Library                  *LayersBase             = NULL;
+#endif
+
+
+
+/* ListView class dispatcher - Handles all supported methods
+ */
+static ULONG HOOKCALL LVDispatcher (
+       REG(a0, Class *cl),
+       REG(a2, struct Gadget *g),
+       REG(a1, Msg msg))
+{
+       ASSERT_VALID_PTR(cl)
+       ASSERT_VALID_PTR(g)
+       ASSERT_VALID_PTR(msg)
+
+       switch (msg->MethodID)
+       {
+               case GM_RENDER:
+                       LV_GMRender(cl, g, (struct gpRender *)msg);
+                       return TRUE;
+
+               case GM_GOACTIVE:
+                       return LV_GMGoActive(cl, g, (struct gpInput *)msg);
+
+               case GM_HANDLEINPUT:
+                       return LV_GMHandleInput(cl, g, (struct gpInput *)msg);
+
+               case GM_GOINACTIVE:
+                       LV_GMGoInactive(cl, g, (struct gpGoInactive *)msg);
+                       return TRUE;
+
+               case GM_LAYOUT:
+                       /* This method is only supported on V39 and above */
+                       LV_GMLayout(cl, g, (struct gpLayout *)msg);
+                       return TRUE;
+
+               case OM_SET:
+               case OM_UPDATE:
+                       return LV_OMSet(cl, g, (struct opUpdate *)msg);
+
+               case OM_GET:
+                       return LV_OMGet(cl, g, (struct opGet *)msg);
+
+               case OM_NEW:
+                       return LV_OMNew(cl, g, (struct opSet *)msg);
+
+               case OM_DISPOSE:
+                       LV_OMDispose(cl, g, msg);
+                       return TRUE;
+
+               default:
+                       /* Unsupported method: let our superclass's
+                        * dispatcher take a look at it.
+                        */
+                       return DoSuperMethodA(cl, (Object *)g, msg);
+       }
+}
+
+
+
+/* Stub for LV_GETITEM hook method
+ */
+INLINE APTR GetItem(struct LVData *lv, LONG num)
+{
+       struct lvGetItem lvgi;
+
+
+       ASSERT_VALID_PTR(lv)
+       ASSERT_VALID_PTR(lv->Items)
+       ASSERT_VALID_PTR(lv->GetItemFunc)
+       ASSERT(num >= 0)
+       ASSERT(num < lv->Total)
+
+
+       lvgi.lvgi_MethodID      = LV_GETITEM;
+       lvgi.lvgi_Number        = num;
+       lvgi.lvgi_Items         = lv->Items;
+
+       return (lv->GetItemFunc(lv->CallBack, NULL, &lvgi));
+}
+
+
+
+/* Stub for LV_GETNEXT hook method
+ */
+INLINE APTR GetNext(struct LVData *lv, APTR item, LONG num)
+{
+       struct lvGetItem lvgi;
+
+
+       ASSERT_VALID_PTR(lv)
+       ASSERT_VALID_PTR(lv->GetNextFunc)
+       ASSERT_VALID_PTR_OR_NULL(item)
+       ASSERT(num >= 0)
+       ASSERT(num < lv->Total)
+
+
+       lvgi.lvgi_MethodID      = LV_GETNEXT;
+       lvgi.lvgi_Number        = num;
+       lvgi.lvgi_Items         = lv->Items;
+
+       return (lv->GetNextFunc (lv->CallBack, item, &lvgi));
+}
+
+
+
+/* Stub for LV_GETPREV hook method
+ */
+INLINE APTR GetPrev(struct LVData *lv, APTR item, LONG num)
+{
+       struct lvGetItem lvgi;
+
+
+       ASSERT_VALID_PTR(lv)
+       ASSERT_VALID_PTR(lv->GetPrevFunc)
+       ASSERT_VALID_PTR_OR_NULL(item)
+       ASSERT(num >= 0)
+       ASSERT(num < lv->Total)
+
+
+       lvgi.lvgi_MethodID      = LV_GETPREV;
+       lvgi.lvgi_Number        = num;
+       lvgi.lvgi_Items         = lv->Items;
+
+       return (lv->GetPrevFunc (lv->CallBack, item, &lvgi));
+}
+
+
+
+INLINE void GetItemBounds(struct LVData *lv, struct Rectangle *rect, LONG item)
+
+/* Compute the bounding box to render the given item and store it in the passed
+ * Rectangle structure.
+ */
+{
+       ASSERT_VALID_PTR(lv)
+       ASSERT_VALID_PTR(rect)
+       ASSERT(item < lv->Total)
+       ASSERT(item >= 0)
+
+       rect->MinX = lv->GRect.MinX;
+       rect->MaxX = lv->GRect.MaxX;
+       rect->MinY = lv->ClipRegion ?
+               (lv->GRect.MinY + item * (lv->ItemHeight + lv->Spacing) - lv->PixelTop) :
+               (lv->GRect.MinY + (item - lv->Top) * (lv->ItemHeight + lv->Spacing));
+       rect->MaxY = rect->MinY + lv->ItemHeight - 1;
+}
+
+
+
+/* Checks if the given item is selected
+ */
+INLINE ULONG IsItemSelected(struct LVData *lv, APTR item, LONG num)
+{
+       ASSERT_VALID_PTR(lv)
+       ASSERT_VALID_PTR_OR_NULL(item)
+       ASSERT(num >= 0)
+       ASSERT(num < lv->Total)
+
+
+       if (lv->Flags & LVF_DOMULTISELECT)
+       {
+               if (lv->SelectArray)
+               {
+                       ASSERT(num < lv->Total)
+
+                       return lv->SelectArray[num];
+               }
+               else if (lv->Flags & LVF_LIST)
+               {
+                       if (!item)
+                               item = GetItem(lv, num);
+
+                       ASSERT_VALID_PTR(item)
+
+                       return item ? (ULONG)(((struct Node *)item)->ln_Type) : 0;
+               }
+
+               return 0;
+       }
+       else
+               return ((ULONG)(num == lv->Selected));
+}
+
+
+
+/* Return the number of nodes in a list
+ */
+INLINE ULONG CountNodes(struct List *list)
+{
+       struct Node *node;
+       ULONG count = 0;
+
+       if (list)
+       {
+               ASSERT_VALID_PTR(list)
+
+               for (node = list->lh_Head; (node = node->ln_Succ); count++)
+                       ASSERT_VALID_PTR_OR_NULL(node);
+       }
+
+       return count;
+}
+
+
+
+/* Count the number of selections in a multiselect listview
+ */
+static ULONG CountSelections(struct LVData *lv)
+{
+       ULONG count = 0;
+
+       ASSERT_VALID_PTR(lv)
+
+
+       if (lv->Flags & LVF_DOMULTISELECT)
+       {
+               if (lv->SelectArray)
+               {
+                       int i;
+
+                       ASSERT_VALID_PTR(lv->SelectArray)
+
+                       for (i = 0; i < lv->Total; i++)
+                               if (lv->SelectArray[i])
+                                       count++;
+               }
+               else if ((lv->Flags & LVF_LIST) && lv->Items)
+               {
+                       struct Node *node;
+
+                       ASSERT_VALID_PTR(lv->Items)
+
+                       for (node = ((struct List *)lv->Items)->lh_Head; (node = node->ln_Succ); count++)
+                               ASSERT_VALID_PTR_OR_NULL(node);
+               }
+       }
+
+       return count;
+}
+
+
+
+static void RedrawItems(struct LVData *lv, struct gpRender *msg, LONG first, LONG last, APTR item)
+
+/* Redraw items from <min> to <max>.  No sanity checks are performed
+ * to ensure that all items between <min> and <max> are really visible.
+ */
+{
+       struct lvDrawItem lvdi;
+       LONG selected;
+
+
+       ASSERT_VALID_PTR(lv)
+       ASSERT_VALID_PTR(msg)
+       ASSERT(first <= last)
+       ASSERT(last < lv->Total)
+
+       DB2( DBPRINTF ("  RedrawItems (first = %ld, last = %ld)\n", first, last);)
+
+
+       lvdi.lvdi_Current       = first;
+       lvdi.lvdi_Items         = lv->Items;
+       lvdi.lvdi_RastPort      = msg->gpr_RPort;
+       lvdi.lvdi_DrawInfo      = msg->gpr_GInfo->gi_DrInfo;
+       lvdi.lvdi_Flags         = lv->Flags;
+
+       GetItemBounds (lv, &lvdi.lvdi_Bounds, first);
+
+       if (!item)
+       {
+               lvdi.lvdi_MethodID = LV_GETITEM;
+               item = lv->GetItemFunc (lv->CallBack, NULL, (struct lvGetItem *)&lvdi);
+       }
+
+       if (lv->DrawBeginFunc)
+       {
+               lvdi.lvdi_MethodID      = LV_DRAWBEGIN;
+               lv->DrawBeginFunc (lv->CallBack, item, (struct lvDrawBegin *)&lvdi);
+       }
+
+       for (;;)
+       {
+               if (lv->Flags & LVF_DOMULTISELECT)
+               {
+                       if (lv->SelectArray)
+                               /* Array selection */
+                               selected = lv->SelectArray[lvdi.lvdi_Current];
+                       else
+                               if (lv->Flags & LVF_LIST)
+                                       /* Node selection */
+                                       selected = (((struct Node *)item)->ln_Type);
+                               else
+                                       selected = 0;
+               }
+               else
+                       /* Single selection */
+                       selected = (lvdi.lvdi_Current == lv->Selected);
+
+               lvdi.lvdi_State = selected ? LVR_SELECTED : LVR_NORMAL;
+
+               lvdi.lvdi_MethodID      = LV_DRAW;
+               lv->DrawItemFunc (lv->CallBack, item, &lvdi);
+
+               if (++lvdi.lvdi_Current > last)
+                       break;
+
+               lvdi.lvdi_MethodID      = LV_GETNEXT;
+               item = lv->GetNextFunc (lv->CallBack, item, (struct lvGetNext *)&lvdi);
+
+               lvdi.lvdi_Bounds.MinY += lv->ItemHeight + lv->Spacing;
+               lvdi.lvdi_Bounds.MaxY += lv->ItemHeight + lv->Spacing;
+       }
+
+       if (lv->DrawEndFunc)
+       {
+               lvdi.lvdi_MethodID      = LV_DRAWEND;
+               lv->DrawEndFunc (lv->CallBack, item, (struct lvDrawEnd *)&lvdi);
+       }
+}
+
+
+
+static void LV_GMRender(Class *cl, struct Gadget *g, struct gpRender *msg)
+{
+       struct LVData   *lv = INST_DATA (cl, g);
+       struct RastPort *rp = msg->gpr_RPort;
+
+       ASSERT_VALID_PTR(lv)
+       ASSERT_VALID_PTR(rp)
+
+       DB2 (DBPRINTF ("GM_RENDER: msg->gpr_Redraw = %s\n",
+               (msg->gpr_Redraw == GREDRAW_TOGGLE) ? "GREDRAW_TOGGLE" :
+               ((msg->gpr_Redraw == GREDRAW_REDRAW) ? "GREDRAW_REDRAW" :
+               ((msg->gpr_Redraw == GREDRAW_UPDATE) ? "GREDRAW_UPDATE" :
+               "*** Unknown GREDRAW mode ***")) );)
+
+
+       if (lv->Flags & LVF_DONTDRAW)
+               return;
+
+       if (lv->Items && lv->Visible)
+       {
+               struct TextFont *oldfont = NULL;
+               struct Region *oldregion;
+               BOOL layerupdating = FALSE;
+
+               if (rp->Font != lv->Font)
+               {
+                       oldfont = rp->Font;
+                       SetFont (rp, lv->Font);
+               }
+
+               if (lv->ClipRegion)
+               {
+                       ASSERT_VALID_PTR(lv->ClipRegion)
+                       ASSERT_VALID_PTR(msg->gpr_RPort->Layer)
+                       DB2(DBPRINTF ("Calling InstallClipRegion()...\n");)
+
+                       /* Workaround for installing clip regions in
+                        * updating layers within a boopsi gadget dispatcher,
+                        * as suggested by Jochen Bechen.
+                        */
+//                     if (msg->gpr_RPort->Layer->Flags & LAYERUPDATING)
+//                     {
+//                             layerupdating = TRUE;
+//                             EndUpdate(msg->gpr_RPort->Layer, FALSE);
+//                     }
+                       oldregion = InstallClipRegion(rp->Layer, lv->ClipRegion);
+               }
+
+               switch (msg->gpr_Redraw)
+               {
+                       case GREDRAW_TOGGLE:    /* Toggle selected item */
+                       {
+                               BOOL    drawnew = (lv->Selected >= lv->Top) && (lv->Selected < lv->Top + lv->Visible),
+                                               drawold = (lv->OldSelected >= lv->Top) && (lv->OldSelected < lv->Top + lv->Visible);
+
+                               if (drawold || drawnew)
+                               {
+                                       struct lvDrawItem        lvdi;
+                                       lvdi.lvdi_Items         = lv->Items;
+                                       lvdi.lvdi_RastPort      = rp;
+                                       lvdi.lvdi_DrawInfo      = msg->gpr_GInfo->gi_DrInfo;
+                                       lvdi.lvdi_Flags         = lv->Flags;
+
+
+                                       if (lv->DrawBeginFunc)
+                                       {
+                                               lvdi.lvdi_MethodID      = LV_DRAWBEGIN;
+                                               lv->DrawBeginFunc(lv->CallBack, NULL, (struct lvDrawBegin *)&lvdi);
+                                       }
+
+                                       lvdi.lvdi_MethodID      = LV_DRAW;
+
+                                       if (drawnew)
+                                       {
+                                               GetItemBounds (lv, &lvdi.lvdi_Bounds, lv->Selected);
+                                               lvdi.lvdi_State = IsItemSelected (lv, lv->SelectedPtr, lv->Selected) ?
+                                                       LVR_SELECTED : LVR_NORMAL;
+                                               lvdi.lvdi_Current = lv->Selected;
+
+                                               lv->DrawItemFunc (lv->CallBack, lv->SelectedPtr, &lvdi);
+                                       }
+
+                                       if (drawold)
+                                       {
+                                               GetItemBounds (lv, &lvdi.lvdi_Bounds, lv->OldSelected);
+                                               lvdi.lvdi_State = IsItemSelected (lv, lv->OldSelectedPtr, lv->OldSelected) ?
+                                                       LVR_SELECTED : LVR_NORMAL;
+                                               lvdi.lvdi_Current = lv->OldSelected;
+
+                                               lv->DrawItemFunc (lv->CallBack, lv->OldSelectedPtr, &lvdi);
+                                       }
+
+                                       if (lv->DrawEndFunc)
+                                       {
+                                               lvdi.lvdi_MethodID      = LV_DRAWEND;
+                                               lv->DrawEndFunc (lv->CallBack, NULL, (struct lvDrawEnd *)&lvdi);
+                                       }
+                               }
+
+                               lv->OldSelected = lv->Selected;
+                               lv->OldSelectedPtr = lv->SelectedPtr;
+
+                               break;
+                       }
+
+                       case GREDRAW_REDRAW:    /* Redraw everything */
+                       {
+                               LONG    ycoord;
+
+                               /* Set the background pen */
+                               SetAPen (rp, msg->gpr_GInfo->gi_DrInfo->dri_Pens[BACKGROUNDPEN]);
+                               /* SetAPen (rp, -1); Used to debug clearing code */
+
+                               /* Now clear the spacing between the items */
+                               if (lv->Spacing && lv->Items && lv->Visible)
+                               {
+                                       LONG i, lastitem;
+
+                                       ycoord = lv->GRect.MinY + lv->ItemHeight;
+                                       lastitem = min (lv->Visible, lv->Total - lv->Top) - 1;
+
+                                       for (i = 0 ; i < lastitem; i++)
+                                       {
+                                               RectFill (rp, lv->GRect.MinX, ycoord,
+                                                       lv->GRect.MaxX, ycoord + lv->Spacing - 1);
+
+                                               ycoord += lv->ItemHeight + lv->Spacing;
+                                       }
+                               }
+                               else
+                                       ycoord = lv->GRect.MinY + min (lv->Visible, lv->Total - lv->Top)
+                                               * lv->ItemHeight;
+
+                               /* Now let's clear bottom part of gadget */
+                               RectFill (rp, lv->GRect.MinX, ycoord,
+                                       lv->GRect.MaxX, lv->GRect.MaxY);
+
+                               /* Finally, draw the items */
+                               RedrawItems (lv, msg, lv->Top,
+                                       min (lv->Top + lv->Visible, lv->Total) - 1, lv->TopPtr);
+
+                               break;
+                       }
+
+                       case GREDRAW_UPDATE:    /* Scroll ListView */
+                       {
+                               LONG scroll_dy, scroll_height;
+
+                               if (lv->ClipRegion)
+                               {
+                                       /* Calculate scrolling amount in pixels */
+                                       if (!(scroll_dy = lv->PixelTop - lv->OldPixelTop))
+                                               /* Do nothing if called improperly */
+                                               break;
+
+                                       /* Scroll everything */
+                                       scroll_height = lv->GBox.Height;
+                               }
+                               else
+                               {
+                                       if (!(lv->Top - lv->OldTop))
+                                               /* Do nothing if called improperly */
+                                               break;
+
+                                       /* Calculate scrolling amount in pixels */
+                                       scroll_dy = (lv->Top - lv->OldTop) * (lv->ItemHeight + lv->Spacing);
+
+                                       /* Only scroll upto last visible item */
+                                       scroll_height = lv->Visible * (lv->ItemHeight + lv->Spacing) - lv->Spacing;
+                               }
+
+                               if (abs(scroll_dy) > lv->MaxScroll)
+                               {
+                                       /* Redraw everything when listview has been scrolled too much */
+                                       RedrawItems (lv, msg, lv->Top,
+                                               min (lv->Top + lv->Visible, lv->Total) - 1, lv->TopPtr);
+                               }
+                               else
+                               {
+                                       /* Optimize scrolling on planar displays if possible */
+                                       SetMaxPen (rp, lv->MaxPen);
+
+                                       /* We use ClipBlit() to scroll the listview because it doesn't clear
+                                        * the scrolled region like ScrollRaster() would do.  Unfortunately,
+                                        * ClipBlit() does not scroll along the damage regions, so we also
+                                        * call ScrollRaster() with the mask set to 0, which will scroll the
+                                        * layer damage regions without actually modifying the display.
+                                        */
+
+                                       if (scroll_dy > 0)      /* Scroll Down */
+                                       {
+                                               ClipBlit (rp, lv->GBox.Left, lv->GBox.Top + scroll_dy,
+                                                       rp, lv->GBox.Left, lv->GBox.Top,
+                                                       lv->GBox.Width, scroll_height - scroll_dy,
+                                                       0x0C0);
+
+                                                       if (lv->ClipRegion)
+                                                       {
+                                                               /* NOTE: We subtract 1 pixel to avoid an exact division which would
+                                                                *       render one item beyond the end when the slider is dragged
+                                                                *       all the way down.
+                                                                */
+                                                               RedrawItems(lv, msg,
+                                                                       (lv->OldPixelTop + lv->GBox.Height) / (lv->ItemHeight + lv->Spacing),
+                                                                       (lv->PixelTop + lv->GBox.Height - 1) / (lv->ItemHeight + lv->Spacing),
+                                                                       NULL);
+                                                       }
+                                                       else
+                                                               RedrawItems(lv, msg,
+                                                                       lv->Visible + lv->OldTop,
+                                                                       lv->Visible + lv->Top - 1,
+                                                                       NULL);
+                                       }
+                                       else                            /* Scroll Up */
+                                       {
+                                               ClipBlit(rp, lv->GBox.Left, lv->GBox.Top,
+                                                       rp, lv->GBox.Left, lv->GBox.Top - scroll_dy,
+                                                       lv->GBox.Width, scroll_height + scroll_dy,
+                                                       0x0C0);
+
+
+                                                       if (lv->ClipRegion)
+                                                               RedrawItems(lv, msg,
+                                                                       lv->PixelTop / (lv->ItemHeight + lv->Spacing),
+                                                                       lv->OldPixelTop / (lv->ItemHeight + lv->Spacing),
+                                                                       NULL);
+                                                       else
+                                                               RedrawItems(lv, msg,
+                                                                       lv->Top,
+                                                                       lv->OldTop - 1,
+                                                                       lv->TopPtr);
+                                       }
+
+                                       /* This will scroll the layer damage regions without actually
+                                        * scrolling the display, but only if our layer really needs it.
+                                        */
+                                       if (NeedZeroScrollRaster(rp->Layer))
+                                       {
+                                               UBYTE oldmask = rp->Mask; /* Would GetRPAttr() be better? */
+
+                                               DB2 (DBPRINTF ("  Calling ScrollRaster()\n");)
+                                               SetWriteMask(rp, 0);
+                                               ScrollRaster(rp, 0, scroll_dy,
+                                                       lv->GRect.MinX, lv->GRect.MinY,
+                                                       lv->GRect.MaxX,
+                                                       lv->GRect.MaxY);
+
+                                               SetWriteMask(rp, oldmask);
+                                       }
+
+                                       /* Restore MaxPen in our RastPort */
+                                       SetMaxPen(rp, -1);
+                               }
+
+                               /* Update OldTop to the current Top item and
+                                * OldPixelTop to the current PixelTop position.
+                                */
+                               lv->OldTop = lv->Top;
+                               lv->OldPixelTop = lv->PixelTop;
+
+                               break;
+                       }
+
+                       default:
+                               break;
+               }
+
+               if (lv->ClipRegion)
+               {
+                       /* Restore old clipping region in our layer */
+                       DB2 (DBPRINTF ("GM_RENDER: Resoring old ClipRegion\n");)
+                       ASSERT_VALID_PTR_OR_NULL(oldregion)
+                       InstallClipRegion (rp->Layer, oldregion);
+                       if (layerupdating)
+                               BeginUpdate(msg->gpr_RPort->Layer);
+               }
+
+               if (oldfont)
+                       SetFont (rp, oldfont);
+       }
+       else if (msg->gpr_Redraw == GREDRAW_REDRAW)
+       {
+               /* Clear all gadget contents */
+               SetAPen (rp, msg->gpr_GInfo->gi_DrInfo->dri_Pens[BACKGROUNDPEN]);
+               RectFill (rp, lv->GRect.MinX, lv->GRect.MinY, lv->GRect.MaxX, lv->GRect.MaxY);
+       }
+
+
+       /* Last but not least, draw the frame if we have one associated */
+
+       if ((msg->gpr_Redraw == GREDRAW_REDRAW) && GadgetHasFrame(g))
+       {
+               DB2 (DBPRINTF("ListViewClass: Rendering my own frame\n");)
+
+               DoMethod((Object *)g->GadgetRender, IM_DRAWFRAME,
+                       msg->gpr_RPort,                                                                         /* imp_RPort            */
+                       (lv->FrameBox.Left << 16) | (lv->FrameBox.Top),         /* imp_Offset           */
+                       IDS_NORMAL,                                                                                     /* imp_State            */
+                       msg->gpr_GInfo->gi_DrInfo,                                                      /* imp_DrInfo           */
+                       (lv->FrameBox.Width << 16) | (lv->FrameBox.Height));/* imp_Dimensions   */
+       }
+}
+
+
+
+static ULONG LV_GMGoActive(Class *cl, struct Gadget *g, struct gpInput *msg)
+{
+       struct LVData *lv = INST_DATA (cl, g);
+
+       ASSERT_VALID_PTR(lv)
+       DB2 (DBPRINTF ("GM_GOACTIVE: gpi_IEvent = $%lx\n", msg->gpi_IEvent);)
+
+
+       if (!lv->Items)
+               return GMR_NOREUSE;
+
+       g->Flags |= GFLG_SELECTED;
+
+       /* Do not process InputEvent when the gadget has been
+        * activated by ActivateGadget().
+        */
+       if (!msg->gpi_IEvent)
+               return GMR_MEACTIVE;
+
+       /* Note: The input event that triggered the gadget
+        * activation (usually a mouse click) should be passed
+        * to the GM_HANDLEINPUT method, so we fall down to it.
+        */
+       return LV_GMHandleInput (cl, g, msg);
+}
+
+
+
+INLINE LONG ItemHit (struct LVData *lv, UNUSED(WORD x), WORD y)
+
+/* Determine which item has been hit with gadget relative
+ * coordinates x and y.
+ */
+{
+       return ((y + lv->PixelTop) / (lv->ItemHeight + lv->Spacing));
+}
+
+
+
+static ULONG LV_GMHandleInput (Class *cl, struct Gadget *g, struct gpInput *msg)
+{
+       struct LVData           *lv = INST_DATA (cl, g);
+       struct InputEvent       *ie = msg->gpi_IEvent;
+       ULONG                            result = GMR_MEACTIVE;
+
+       ASSERT_VALID_PTR(lv)
+/*     DB2 (DBPRINTF ("GM_HANDLEINPUT: ie_Class = $%lx, ie->ie_Code = $%lx, "
+               "gpi_Mouse.X = %ld, gpi_Mouse.Y = %ld\n",
+               ie->ie_Class, ie->ie_Code, msg->gpi_Mouse.X, msg->gpi_Mouse.Y);)
+*/
+       switch (ie->ie_Class)
+       {
+               case IECLASS_RAWKEY:
+               {
+                       LONG tags[5];
+                       LONG pos;
+
+                       switch (ie->ie_Code)
+                       {
+                               case CURSORUP:
+                                       if ((lv->Flags & LVF_READONLY) || (ie->ie_Qualifier & IEQUALIFIER_CONTROL))
+                                       {
+                                               if (ie->ie_Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
+                                                       pos = lv->Top - lv->Visible / 2;
+                                               else
+                                                       pos = lv->Top - 1;
+
+                                               if (pos < 0) pos = 0;
+
+                                               tags[0] = LVA_Top;
+                                               tags[1] = pos;
+                                               tags[2] = TAG_DONE;
+                                       }
+                                       else
+                                       {
+                                               if (ie->ie_Qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
+                                                       pos = 0;
+                                               else if (ie->ie_Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
+                                                       pos = lv->Selected - lv->Visible + 1;
+                                               else
+                                                       pos = lv->Selected - 1;
+
+                                               if (pos < 0) pos = 0;
+
+                                               tags[0] = LVA_Selected;
+                                               tags[1] = pos;
+                                               tags[2] = LVA_MakeVisible;
+                                               tags[3] = pos;
+                                               tags[4] = TAG_DONE;
+                                       }
+                                       break;
+
+                               case CURSORDOWN:
+                                       if ((lv->Flags & LVF_READONLY) || (ie->ie_Qualifier & IEQUALIFIER_CONTROL))
+                                       {
+                                               if (ie->ie_Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
+                                                       pos = lv->Top + lv->Visible / 2;
+                                               else
+                                                       pos = lv->Top + 1;
+
+                                               tags[0] = LVA_Top;
+                                               tags[1] = pos;
+                                               tags[2] = TAG_DONE;
+                                       }
+                                       else
+                                       {
+                                               if (ie->ie_Qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
+                                                       pos = lv->Total - 1;
+                                               else if (ie->ie_Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
+                                                       pos = lv->Selected + lv->Visible - 1;
+                                               else
+                                                       pos = lv->Selected + 1;
+
+                                               tags[0] = LVA_Selected;
+                                               tags[1] = pos;
+                                               tags[2] = LVA_MakeVisible;
+                                               tags[3] = pos;
+                                               tags[4] = TAG_DONE;
+                                       }
+                                       break;
+
+                               default:
+                                       tags[0] = TAG_DONE;
+
+                       } /* End switch (ie->ie_Code) */
+
+                       if (tags[0] != TAG_DONE)
+                               DoMethod ((Object *)g, OM_UPDATE, tags, msg->gpi_GInfo,
+                                       (ie->ie_Qualifier & IEQUALIFIERB_REPEAT) ? OPUF_INTERIM : 0);
+
+                       break;
+               }
+
+               case IECLASS_RAWMOUSE:
+               {
+                       LONG selected;
+
+                       switch (ie->ie_Code)
+                       {
+                               case SELECTDOWN:
+
+                                       /* Check for click outside gadget box */
+
+                                       if ((msg->gpi_Mouse.X < 0) ||
+                                               (msg->gpi_Mouse.X >= lv->GBox.Width) ||
+                                               (msg->gpi_Mouse.Y < 0) ||
+                                               (msg->gpi_Mouse.Y >= lv->GBox.Height))
+                                       {
+                                               result = GMR_REUSE;
+                                               break;
+                                       }
+
+                                       /* Start dragging mode */
+                                       lv->Flags |= LVF_DRAGGING;
+
+                                       if (lv->Flags & LVF_READONLY)
+                                               break;
+
+                                       /* Select an item */
+                                       selected = ItemHit (lv, msg->gpi_Mouse.X, msg->gpi_Mouse.Y);
+
+                                       /* No action when selecting over blank space in the bottom */
+                                       if ((selected < 0) || (selected >= lv->Total))
+                                               break;
+
+                                       /* Backup current selection for RMB undo */
+                                       lv->BackupSelected = lv->Selected;
+                                       lv->BackupPixelTop = lv->PixelTop;
+
+                                       if (selected == lv->Selected)
+                                       {
+                                               /* Check for double click */
+                                               if (DoubleClick (lv->DoubleClickSecs, lv->DoubleClickMicros,
+                                                       ie->ie_TimeStamp.tv_secs, ie->ie_TimeStamp.tv_micro))
+                                                       UpdateAttrs ((Object *)g, msg->gpi_GInfo, 0,
+                                                               LVA_DoubleClick, selected,
+                                                               TAG_DONE);
+                                       }
+
+                                       if (lv->Flags & LVF_DOMULTISELECT)
+                                               /* Setup for multiple items drag selection */
+                                               lv->DragSelect = IsItemSelected (lv, NULL, selected) ?
+                                                       LVA_DeselectItem : LVA_SelectItem;
+                                       else if (g->Activation & GACT_TOGGLESELECT)
+                                       {
+                                               /* Setup for single item toggle */
+                                               lv->DragSelect = LVA_Selected;
+                                               if (selected == lv->Selected)
+                                                       selected = ~0;
+                                       }
+                                       else /* Single selection */
+                                               /* Setup for single item drag selection */
+                                               lv->DragSelect = LVA_Selected;
+
+                                       UpdateAttrs ((Object *)g, msg->gpi_GInfo, 0,
+                                               lv->DragSelect, selected,
+                                               TAG_DONE);
+
+                                       /* Save double click info */
+                                       lv->DoubleClickSecs = ie->ie_TimeStamp.tv_secs;
+                                       lv->DoubleClickMicros = ie->ie_TimeStamp.tv_micro;
+                                       break;
+
+                               case MENUDOWN:
+                                       /* Undo selection & position when RMB is pressed */
+                                       if (lv->Flags & (LVF_DRAGGING | LVF_SCROLLING))
+                                       {
+                                               /* Stop dragging and scrolling modes */
+                                               lv->Flags &= ~(LVF_DRAGGING | LVF_SCROLLING);
+
+                                               if ((lv->BackupSelected != lv->Selected) ||
+                                                       (lv->BackupPixelTop != lv->PixelTop))
+                                               {
+                                                       UpdateAttrs ((Object *)g, msg->gpi_GInfo, 0,
+                                                               (lv->Flags & LVF_READONLY) ?
+                                                                       TAG_IGNORE : LVA_Selected, lv->BackupSelected,
+                                                               LVA_PixelTop, lv->BackupPixelTop,
+                                                               TAG_DONE);
+                                               }
+                                       }
+                                       else
+                                               /* Deactivate gadget on menu button press */
+                                               result = GMR_REUSE;
+
+                                       break;
+
+                               case MIDDLEDOWN:
+                                       /* Argh, input.device never sends this event in V40! */
+                                       DB1 (DBPRINTF ("MIDDLEDOWN received\n");)
+
+                                       /* Start MMB scrolling */
+                                       lv->BackupPixelTop = lv->PixelTop;
+                                       lv->BackupSelected = lv->Selected;
+                                       lv->MiddleMouseY = msg->gpi_Mouse.Y;
+                                       lv->Flags |= LVF_DRAGGING;
+                                       break;
+
+                               case SELECTUP:
+
+                                       /* Stop dragging mode */
+                                       lv->Flags &= ~LVF_DRAGGING;
+
+                                       if (g->Activation & GACT_RELVERIFY)
+                                       {
+                                               /* Send IDCMP_GADGETUP message to our parent window */
+                                               msg->gpi_Termination = &lv->Selected;
+                                               result = GMR_NOREUSE | GMR_VERIFY;
+                                       }
+                                       break;
+
+                               case MIDDLEUP:
+                                       /* Argh, input.device never sends this event in V40! */
+                                       DB1 (DBPRINTF ("scrolling off\n");)
+
+                                       /* Stop MMB scrolling */
+                                       lv->Flags &= ~LVF_SCROLLING;
+                                       break;
+
+                               default: /* Mouse moved */
+
+                                       /* Holding LMB? */
+                                       if (lv->Flags & LVF_DRAGGING)
+                                       {
+                                               /* Select an item */
+                                               selected = ItemHit (lv, msg->gpi_Mouse.X, msg->gpi_Mouse.Y);
+
+                                               /* Moved over another item inside the currently displayed list? */
+                                               if ((selected != lv->Selected) && !(lv->Flags & LVF_READONLY)
+                                                       && (selected >= lv->Top) && (selected < lv->Top + lv->Visible))
+                                               {
+                                                       /* Single selection */
+
+                                                       /* Call our OM_UPDATE method to change the attributes.
+                                                        * This will also send notification to targets and
+                                                        * update the contents of the gadget.
+                                                        */
+                                                       UpdateAttrs ((Object *)g, msg->gpi_GInfo, 0,
+                                                               lv->DragSelect, selected,
+                                                               TAG_DONE);
+                                               }
+                                       }
+
+                                       /* Holding MMB? */
+                                       if (lv->Flags & LVF_SCROLLING)
+                                       {
+                                               DB1 (DBPRINTF ("  scrolling\n");)
+                                               selected = (msg->gpi_Mouse.Y - lv->MiddleMouseY)
+                                                       + lv->BackupPixelTop;
+
+                                               UpdateAttrs ((Object *)g, msg->gpi_GInfo, 0,
+                                                       LVA_PixelTop, selected < 0 ? 0 : selected,
+                                                       TAG_DONE);
+                                       }
+
+                       } /* End switch (ie->ie_Code) */
+
+                       break;
+               }
+
+               case IECLASS_TIMER:
+
+                       /* Holding LMB? */
+                       if (lv->Flags & LVF_DRAGGING)
+                       {
+                               /* Mouse above the upper item? */
+                               if ((msg->gpi_Mouse.Y < 0) && lv->Top)
+                               {
+                                       /* Scroll up */
+                                       UpdateAttrs ((Object *)g, msg->gpi_GInfo, 0,
+                                               LVA_MoveUp,     1,
+                                               (lv->Flags & LVF_READONLY) ? TAG_IGNORE : LVA_Selected, lv->Top - 1,
+                                               TAG_DONE);
+                               }
+                               /* Mouse below the bottom item? */
+                               else if (msg->gpi_Mouse.Y / (lv->ItemHeight + lv->Spacing) >= lv->Visible)
+                               {
+                                       /* Scroll down */
+                                       UpdateAttrs ((Object *)g, msg->gpi_GInfo, 0,
+                                               LVA_MoveDown,           1,
+                                               (lv->Flags & LVF_READONLY) ? TAG_IGNORE : LVA_Selected, lv->Top + lv->Visible,
+                                               TAG_DONE);
+                               }
+                       }
+                       break;
+
+               default:
+                       ;
+
+       } /* End switch (ie->ie_Class) */
+
+       return result;
+}
+
+
+
+static void LV_GMGoInactive (Class *cl, struct Gadget *g, UNUSED(struct gpGoInactive *msg))
+{
+       struct LVData *lv = INST_DATA (cl, g);
+       ASSERT_VALID_PTR(lv)
+
+       DB1 (DBPRINTF ("ListViewClass: GM_GOINACTIVE\n");)
+
+       /* Stop dragging and scrolling modes */
+       lv->Flags &= ~(LVF_DRAGGING | LVF_SCROLLING);
+
+       /* Mark gadget inactive */
+       g->Flags &= ~GFLG_SELECTED;
+}
+
+
+
+static void LV_GMLayout(Class *cl, struct Gadget *g, struct gpLayout *msg)
+{
+       struct LVData *lv = INST_DATA (cl, g);
+       LONG visible;
+
+       DB2 (DBPRINTF ("ListViewClass: GM_LAYOUT\n");)
+       ASSERT_VALID_PTR(lv)
+       ASSERT_VALID_PTR(msg->gpl_GInfo)
+       ASSERT_VALID_PTR(msg->gpl_GInfo->gi_DrInfo)
+       ASSERT_VALID_PTR(msg->gpl_GInfo->gi_DrInfo->dri_Font)
+
+
+       /* We shouldn't draw inside the GM_LAYOUT method: the
+        * GM_REDRAW method will be called by Intuition shortly after.
+        */
+       lv->Flags |= LVF_DONTDRAW;
+
+       /* Collect new gadget size */
+       GetGadgetBox(msg->gpl_GInfo, (struct ExtGadget *)g, &lv->GBox);
+       IBoxToRect(&lv->GBox, &lv->GRect);
+
+       if (GadgetHasFrame(g))
+               /* Calculate dimensions of our framing image */
+               DoMethod((Object *)g->GadgetRender,
+                       IM_FRAMEBOX, &lv->GBox, &lv->FrameBox, msg->gpl_GInfo->gi_DrInfo, 0);
+
+       /* Calculate clipping region for gadget LVA_Clipped mode */
+       if (lv->ClipRegion)
+       {
+               /* Remove previous clipping rectangle, if any */
+               ClearRegion (lv->ClipRegion);
+
+               /* Install a clipping rectangle around the gadget box.
+                * We don't check for failure because we couldn't do
+                * anything otherwise.
+                */
+               OrRectRegion (lv->ClipRegion, &lv->GRect);
+       }
+
+       /* Setup Font if not yet done */
+       if (!lv->Font)
+       {
+               lv->Font = msg->gpl_GInfo->gi_DrInfo->dri_Font;
+               if (!lv->ItemHeight)
+                       lv->ItemHeight = lv->Font->tf_YSize;
+       }
+
+       if (lv->ItemHeight)
+       {
+               if (lv->ClipRegion)
+                       /* Allow displaying an incomplete item at the bottom of the listview,
+                        * plus one incomplete item at the top.
+                        */
+                       visible = (lv->GBox.Height + lv->ItemHeight + lv->Spacing - 1) /
+                               (lv->ItemHeight + lv->Spacing);
+               else
+                       /* get maximum number of items fitting in the listview height.
+                        * Ignore spacing for the last visible item.
+                        */
+                       visible = (lv->GBox.Height + lv->Spacing) / (lv->ItemHeight + lv->Spacing);
+       }
+       else
+               visible = 0;
+
+       lv->MaxScroll = lv->GBox.Height / lv->ScrollRatio;
+
+
+       /* Send initial notification to our sliders, or update them to
+        * the new values. The slieders will get the correct size also
+        * in the special case where the list is attached at creation
+        * time and the sliders are attached later using a model object.
+        *
+        * The private class attribute LVA_Visible will handle everything for us.
+        */
+       UpdateAttrs ((Object *)g, msg->gpl_GInfo, 0,
+               LVA_Visible,    visible,
+               TAG_DONE);
+
+       /* Re-enable drawing */
+       lv->Flags &= ~LVF_DONTDRAW;
+}
+
+
+
+static ULONG LV_OMSet(Class *cl, struct Gadget *g, struct opUpdate *msg)
+{
+       struct LVData   *lv = INST_DATA (cl, g);
+       struct TagItem  *ti,
+                                       *tstate = msg->opu_AttrList;
+       ULONG   result;
+       UWORD   action = 0;     /* See flag definitions below */
+
+       ASSERT_VALID_PTR(lv)
+       ASSERT_VALID_PTR_OR_NULL(tstate)
+
+       DB2 (DBPRINTF ((msg->MethodID == OM_SET) ?
+               "ListViewClass: OM_SET\n" :
+               "ListViewClass: OM_UPDATE:\n");)
+
+
+       /* Definitions for the ations to be taken right after
+        * scanning the attributes list in OM_SET/OM_UPDATE.
+        * For speed reasons we pack them together in a single variable,
+        * so we can set and test multiple flags in once.
+        */
+       #define LVF_DO_SUPER_METHOD     (1<<0)
+       #define LVF_REDRAW                      (1<<1)
+       #define LVF_SCROLL                      (1<<2)
+       #define LVF_TOGGLESELECT        (1<<3)
+       #define LVF_NOTIFY                      (1<<4)
+       #define LVF_NOTIFYALL           (1<<5)
+
+
+       while ((ti = NextTagItem (&tstate)))
+               switch (ti->ti_Tag)
+               {
+                       case GA_ID:
+                               if (msg->MethodID == OM_SET)
+                               {
+                                       DB2 (DBPRINTF ("  GA_ID, %ld\n", ti->ti_Data);)
+
+                                       /* Avoid forwarding all taglists to superclass because of GA_ID */
+                                       g->GadgetID = ti->ti_Data;
+                               }
+                               break;
+
+                       case LVA_Selected:
+                               DB2 (DBPRINTF ("  LVA_Selected, %ld\n", ti->ti_Data);)
+
+                               if (lv->Items)
+                               {
+                                       LONG newselected = ti->ti_Data;
+
+                                       if (newselected != ~0)
+                                               newselected = (newselected >= lv->Total) ?
+                                                       (lv->Total - 1) : newselected;
+
+                                       if (lv->Selected != newselected)
+                                       {
+                                               if (((lv->Selected >= lv->Top) &&
+                                                       (lv->Selected < lv->Top + lv->Visible)) ||
+                                                       ((newselected >= lv->Top) &&
+                                                       (newselected < lv->Top + lv->Visible)))
+                                                       action |= LVF_TOGGLESELECT;
+
+                                               lv->Selected = newselected;
+
+                                               if (newselected == ~0)
+                                                       lv->SelectedPtr = NULL;
+                                               else
+                                                       lv->SelectedPtr = GetItem(lv, newselected);
+
+                                               action |= LVF_NOTIFY;
+                                       }
+                               }
+                               break;
+
+                       case LVA_Top:
+                               DB2 (DBPRINTF ("  LVA_Top, %ld\n", ti->ti_Data);)
+
+                               if ((lv->Top != (LONG)ti->ti_Data) && lv->Items)
+                               {
+                                       /* This will scroll the listview contents when needed */
+
+                                       lv->Top = (((LONG)ti->ti_Data + lv->Visible) >= lv->Total) ?
+                                               ((lv->Total <= lv->Visible) ? 0 : (lv->Total - lv->Visible))
+                                               : (LONG)ti->ti_Data;
+                                       lv->PixelTop = lv->Top * (lv->ItemHeight + lv->Spacing);
+
+                                       /* TODO: optimize for some special cases:
+                                        * Top == oldtop + 1 and Top == oldtop - 1
+                                        */
+                                       lv->TopPtr = GetItem(lv, lv->Top);
+                                       action |= LVF_SCROLL | LVF_NOTIFY;
+                               }
+                               break;
+
+                       case LVA_Total:
+                               DB2 (DBPRINTF ("  LVA_Total, %ld\n", ti->ti_Data);)
+
+                               /* We don't hhandle LVA_Total except when setting a new
+                                * list or array of items.
+                                */
+                               break;
+
+                       case LVA_SelectItem:
+                               DB2 (DBPRINTF ("  LVA_SelectItem, %ld\n", ti->ti_Data);)
+
+                               /* Check LVA_MaxSelect */
+                               if (lv->SelectCount >= lv->MaxSelect)
+                                       DisplayBeep (msg->opu_GInfo ? msg->opu_GInfo->gi_Screen : NULL);
+                               else if (lv->Items)
+                               {
+                                       LONG newselected = ((LONG)ti->ti_Data >= lv->Total) ?
+                                               (lv->Total - 1) : (LONG)ti->ti_Data;
+
+                                       if (((lv->Selected >= lv->Top) &&
+                                               (lv->Selected < lv->Top + lv->Visible)) ||
+                                               ((newselected >= lv->Top) &&
+                                               (newselected < lv->Top + lv->Visible)))
+                                               action |= LVF_TOGGLESELECT;
+
+                                       lv->Selected = newselected;
+                                       lv->SelectedPtr = GetItem(lv, newselected);
+
+                                       if (!IsItemSelected(lv, lv->SelectedPtr, newselected))
+                                       {
+                                               lv->SelectCount++;
+
+                                               if (lv->SelectArray)
+                                                       lv->SelectArray[newselected] = lv->SelectCount;
+                                               else if (lv->Flags & LVF_LIST)
+                                                       ((struct Node *)lv->SelectedPtr)->ln_Type = lv->SelectCount;
+                                       }
+                                       action |= LVF_NOTIFY;
+                               }
+                               break;
+
+                       case LVA_DeselectItem:
+                               DB2 (DBPRINTF ("  LVA_DeselectItem, %ld\n", ti->ti_Data);)
+
+                               if (lv->Items)
+                               {
+                                       LONG newselected = ((LONG)ti->ti_Data >= lv->Total) ?
+                                               (lv->Total - 1) : (LONG)ti->ti_Data;
+
+                                       if (((lv->Selected >= lv->Top) &&
+                                               (lv->Selected < lv->Top + lv->Visible)) ||
+                                               ((newselected >= lv->Top) &&
+                                               (newselected < lv->Top + lv->Visible)))
+                                               action |= LVF_TOGGLESELECT;
+
+                                       lv->Selected = newselected;
+                                       lv->SelectedPtr = GetItem(lv, newselected);
+
+                                       if (IsItemSelected(lv, lv->SelectedPtr, newselected))
+                                       {
+                                               lv->SelectCount--;
+
+                                               if (lv->SelectArray)
+                                                       lv->SelectArray[lv->Selected] = 0;
+                                               else if (lv->Flags & LVF_LIST)
+                                                       ((struct Node *)lv->SelectedPtr)->ln_Type = 0;
+
+                                               action |= LVF_NOTIFY;
+                                       }
+                               }
+                               break;
+
+                       case LVA_ToggleItem:
+                               DB2 (DBPRINTF ("  LVA_ToggleItem, %ld\n", ti->ti_Data);)
+
+                               if (lv->Items)
+                               {
+                                       LONG newselected = ((LONG)ti->ti_Data >= lv->Total) ?
+                                               (lv->Total - 1) : (LONG)ti->ti_Data;
+
+                                       if (((lv->Selected >= lv->Top) &&
+                                               (lv->Selected < lv->Top + lv->Visible)) ||
+                                               ((newselected >= lv->Top) &&
+                                               (newselected < lv->Top + lv->Visible)))
+                                               action |= LVF_TOGGLESELECT;
+
+                                       lv->Selected = newselected;
+                                       lv->SelectedPtr = GetItem(lv, newselected);
+
+                                       if (IsItemSelected(lv, lv->SelectedPtr, lv->Selected))
+                                       {
+                                               /* Deselect */
+                                               lv->SelectCount--;
+
+                                               if (lv->SelectArray)
+                                                       lv->SelectArray[lv->Selected] = 0;
+                                               else if (lv->Flags & LVF_LIST)
+                                                       ((struct Node *)lv->SelectedPtr)->ln_Type = 0;
+                                       }
+                                       else
+                                       {
+                                               /* Check LVA_MaxSelect */
+                                               if (lv->SelectCount >= lv->MaxSelect)
+                                                       DisplayBeep (msg->opu_GInfo ? msg->opu_GInfo->gi_Screen : NULL);
+                                               else
+                                               {
+                                                       /* Select */
+                                                       lv->SelectCount++;
+
+                                                       if (lv->SelectArray)
+                                                               lv->SelectArray[lv->Selected] = lv->SelectCount;
+                                                       else if (lv->Flags & LVF_LIST)
+                                                               ((struct Node *)lv->SelectedPtr)->ln_Type = lv->SelectCount;
+                                               }
+                                       }
+
+                                       action |= LVF_NOTIFY;
+                               }
+                               break;
+
+                       case LVA_ClearSelected:
+                               DB2 (DBPRINTF ("  LVA_ClearSelected, %ld\n", ti->ti_Data);)
+
+                               if (lv->Items)
+                               {
+                                       LONG newselected = (LONG)ti->ti_Data;
+                                       LONG i;
+
+                                       if (((lv->Selected >= lv->Top) &&
+                                               (lv->Selected < lv->Top + lv->Visible)) ||
+                                               ((newselected >= lv->Top) &&
+                                               (newselected < lv->Top + lv->Visible)))
+                                               action |= LVF_TOGGLESELECT;
+
+                                       lv->Selected = ~0;
+                                       lv->SelectedPtr = NULL;
+                                       lv->SelectCount = 0;
+
+
+                                       /* Clear the selections */
+
+                                       if (lv->SelectArray)
+                                               for (i = 0; i < lv->Total; i++)
+                                                       lv->SelectArray[i] = 0;
+                                       else if (lv->Flags & LVF_LIST)
+                                       {
+                                               struct Node *node;
+
+                                               for (node = ((struct List *)lv->Items)->lh_Head;
+                                                       node->ln_Succ;
+                                                       node = node->ln_Succ)
+                                               {
+                                                       node->ln_Type = 0;
+                                                       ASSERT_VALID_PTR_OR_NULL(node);
+                                               }
+                                       }
+
+                                       /* TODO: check if total redraw is really needed */
+                                       action |= LVF_REDRAW | LVF_NOTIFY;
+                               }
+                               break;
+
+                       case LVA_MakeVisible:
+                       {
+                               LONG itemnum = (LONG)ti->ti_Data;
+
+                               DB2 (DBPRINTF ("  LVA_MakeVisible, %ld\n", ti->ti_Data);)
+
+                               if (itemnum < 0)
+                                       itemnum = 0;
+
+                               if (itemnum >= lv->Total)
+                                       itemnum = lv->Total - 1;
+
+                               if (itemnum < lv->Top)
+                               {
+                                       /* Scroll up */
+
+                                       lv->Top = itemnum;
+                                       lv->TopPtr = GetItem(lv, lv->Top);
+                                       action |= LVF_SCROLL | LVF_NOTIFY;
+                               }
+                               else if (itemnum >= lv->Top + lv->Visible)
+                               {
+                                       /* Scroll down */
+
+                                       lv->Top = itemnum - lv->Visible + 1;
+                                       lv->TopPtr = GetItem(lv, lv->Top);
+                                       action |= LVF_SCROLL | LVF_NOTIFY;
+                               }
+                               break;
+                       }
+
+                       case LVA_MoveUp:
+                               DB2 (DBPRINTF ("  LVA_MoveUp, %ld\n", ti->ti_Data);)
+
+                               if ((lv->Top > 0) && lv->Items)
+                               {
+                                       lv->Top--;
+                                       lv->TopPtr = GetPrev (lv, lv->TopPtr, lv->Top);
+                                       action |= LVF_SCROLL | LVF_NOTIFY;
+                               }
+                               break;
+
+                       case LVA_MoveDown:
+                               DB2 (DBPRINTF ("  LVA_MoveDown, %ld\n", ti->ti_Data);)
+
+                               if ((lv->Top + lv->Visible < lv->Total) && lv->Items)
+                               {
+                                       lv->Top++;
+                                       lv->TopPtr = GetNext (lv, lv->TopPtr, lv->Top);
+                                       action |= LVF_SCROLL | LVF_NOTIFY;
+                               }
+                               break;
+
+                       case LVA_MoveLeft:
+                               DB2 (DBPRINTF ("  Unimplemented attr: LVA_MoveLeft\n");)
+                               break;
+
+                       case LVA_MoveRight:
+                               DB2 (DBPRINTF ("  Unimplemented attr: LVA_MoveRight\n");)
+                               break;
+
+                       case LVA_StringList:
+                               DB2 (DBPRINTF ("  LVA_StringList, $%lx\n", ti->ti_Data);)
+
+                               if (ti->ti_Data == (ULONG)~0L)
+                                       lv->Items = NULL;
+                               else
+                               {
+                                       ASSERT_VALID_PTR_OR_NULL(ti->ti_Data)
+
+                                       lv->Items = (void *)ti->ti_Data;
+                                       lv->GetItemFunc         = ListGetItem;
+                                       lv->GetNextFunc         = ListGetNext;
+                                       lv->GetPrevFunc         = ListGetPrev;
+                                       lv->DrawItemFunc        = ListStringDrawItem;
+                                       lv->Flags |= LVF_LIST;
+
+                                       lv->Total = GetTagData (LVA_Total, ~0, msg->opu_AttrList);
+                                       if (lv->Total == ~0)
+                                               lv->Total = CountNodes(lv->Items);
+
+                                       lv->SelectCount = CountSelections (lv);
+
+                                       action |= LVF_REDRAW | LVF_NOTIFYALL;
+                               }
+                               break;
+
+                       case LVA_StringArray:
+                               DB2 (DBPRINTF ("  LVA_StringArray, $%lx\n", ti->ti_Data);)
+
+                               if (ti->ti_Data == (ULONG)~0L)
+                                       lv->Items = NULL;
+                               else
+                               {
+                                       ASSERT_VALID_PTR_OR_NULL(ti->ti_Data)
+
+                                       lv->Items = (void *)ti->ti_Data;
+                                       lv->GetItemFunc         = ArrayGetItem;
+                                       lv->GetNextFunc         = ArrayGetItem;
+                                       lv->GetPrevFunc         = ArrayGetItem;
+                                       lv->DrawItemFunc        = StringDrawItem;
+                                       lv->Flags &= ~LVF_LIST;
+
+                                       lv->Total = GetTagData (LVA_Total, ~0, msg->opu_AttrList);
+                                       if ((lv->Total == ~0) && lv->Items)
+                                       {
+                                               /* Count items */
+                                               ULONG i = 0;
+                                               while (((APTR *)lv->Items)[i]) i++;
+                                               lv->Total = i;
+                                       }
+
+                                       lv->SelectCount = CountSelections(lv);
+
+                                       action |= LVF_REDRAW | LVF_NOTIFYALL;
+                               }
+                               break;
+
+                       case LVA_ImageList:
+                               DB2 (DBPRINTF ("  LVA_ImageList, $%lx\n", ti->ti_Data);)
+
+                               if (ti->ti_Data == (ULONG)~0L)
+                                       lv->Items = NULL;
+                               else
+                               {
+                                       ASSERT_VALID_PTR_OR_NULL(ti->ti_Data)
+
+                                       lv->Items = (void *) ti->ti_Data;
+                                       lv->GetItemFunc         = ListGetItem;
+                                       lv->GetNextFunc         = ListGetNext;
+                                       lv->GetPrevFunc         = ListGetPrev;
+                                       lv->DrawItemFunc        = ListImageDrawItem;
+                                       lv->Flags |= LVF_LIST;
+
+                                       lv->Total = GetTagData (LVA_Total, ~0, msg->opu_AttrList);
+                                       if (lv->Total == ~0)
+                                               lv->Total = CountNodes(lv->Items);
+
+                                       lv->SelectCount = CountSelections(lv);
+
+                                       action |= LVF_REDRAW | LVF_NOTIFYALL;
+                               }
+                               break;
+
+                       case LVA_ImageArray:
+                               DB2 (DBPRINTF ("  LVA_ImageArray, $%lx\n", ti->ti_Data);)
+
+                               if (ti->ti_Data == (ULONG)~0L)
+                                       lv->Items = NULL;
+                               else
+                               {
+                                       ASSERT_VALID_PTR_OR_NULL(ti->ti_Data)
+
+                                       lv->Items = (void *) ti->ti_Data;
+                                       lv->GetItemFunc         = ArrayGetItem;
+                                       lv->GetNextFunc         = ArrayGetItem;
+                                       lv->GetPrevFunc         = ArrayGetItem;
+                                       lv->DrawItemFunc        = ImageDrawItem;
+                                       lv->Flags &= ~LVF_LIST;
+
+                                       lv->Total = GetTagData (LVA_Total, ~0, msg->opu_AttrList);
+                                       if ((lv->Total == ~0) && lv->Items)
+                                       {
+                                               /* Count items */
+                                               ULONG i = 0;
+                                               while (((APTR *)lv->Items)[i]) i++;
+       &nbs