Initial commit master
authorBernie Innocenti <bernie@codewiz.org>
Fri, 11 Mar 2011 06:02:27 +0000 (01:02 -0500)
committerBernie Innocenti <bernie@codewiz.org>
Fri, 11 Mar 2011 06:02:27 +0000 (01:02 -0500)
20 files changed:
BoopsiStubs.h [new file with mode: 0644]
CompilerSpecific.h [new file with mode: 0644]
Debug.h [new file with mode: 0644]
GNUmakefile [new file with mode: 0644]
GST.c [new file with mode: 0644]
LVDemo.c [new file with mode: 0644]
LVDemo.¶ [new file with mode: 0644]
ListBoxClass.c [new file with mode: 0644]
ListBoxClass.h [new file with mode: 0644]
ListMacros.h [new file with mode: 0644]
ListViewClass.c [new file with mode: 0644]
ListViewClass.h [new file with mode: 0644]
ListViewHooks.c [new file with mode: 0644]
SMakefile [new file with mode: 0644]
ScrollButtonClass.c [new file with mode: 0644]
ScrollButtonClass.h [new file with mode: 0644]
VectorGlyphIClass.h [new file with mode: 0644]
startup_gcc.s [new file with mode: 0644]
startup_sc.s [new file with mode: 0644]
startup_storm.s [new file with mode: 0644]

diff --git a/BoopsiStubs.h b/BoopsiStubs.h
new file mode 100644 (file)
index 0000000..a8202c7
--- /dev/null
@@ -0,0 +1,181 @@
+#ifndef BOOPSISTUBS_H
+#define BOOPSISTUBS_H
+/*
+**     $VER: BoopsiStubs.h 1.2 (1.9.97)
+**
+**     Copyright (C) 1997 Bernardo Innocenti. All rights reserved.
+**
+**     Use 4 chars wide TABs to read this file
+**
+**     Using these inline versions of the amiga.lib boopsi support functions
+**     results in faster and smaller code against their linked library
+**     counterparts. When debug is active, these functions will also
+**     validate the parameters you pass in.
+**
+*/
+
+#ifndef COMPILERSPECIFIC_H
+#include "CompilerSpecific.h"
+#endif /* COMPILERSPECIFIC_H */
+
+#ifndef DEBUG_H
+#include "Debug.h"
+#endif /* DEBUG_H */
+
+/* This definition will prevent the redefinition of the following stubs with
+ * their amiga.lib equivalents. This only works if you are using a patched
+ * version of <clib/alib_protos.h>
+ */
+#define USE_BOOPSI_STUBS
+
+
+
+/* the _HookPtr type is a shortcut for a pointer to a hook function */
+
+typedef ASMCALL ULONG (*_HookPtr)
+       (REG(a0, Class *), REG(a2, Object *), REG(a1, APTR));
+
+INLINE ULONG CoerceMethodA (struct IClass *cl, Object *o, Msg message)
+{
+       ASSERT_VALIDNO0(cl)
+       ASSERT_VALID(o)
+
+       return ((_HookPtr)cl->cl_Dispatcher.h_Entry) ((APTR)cl, (APTR)o, (APTR)message);
+}
+
+INLINE ULONG DoSuperMethodA (struct IClass *cl, Object *o, Msg message)
+{
+       ASSERT_VALIDNO0(cl)
+       ASSERT_VALID(o)
+
+       cl = cl->cl_Super;
+       return ((_HookPtr)cl->cl_Dispatcher.h_Entry) ((APTR)cl, (APTR)o, (APTR)message);
+}
+
+INLINE ULONG DoMethodA (Object *o, Msg message)
+{
+       Class *cl;
+       ASSERT_VALIDNO0(o)
+       cl = OCLASS (o);
+       ASSERT_VALIDNO0(cl)
+
+       return ((_HookPtr)cl->cl_Dispatcher.h_Entry) ((APTR)cl, (APTR)o, (APTR)message);
+}
+
+
+
+/* Var-args versions of the above functions.  SAS/C is clever enough to inline these,
+ * while gcc and egcs refuse to inline a function with '...' (yikes!).  The GCC
+ * versions of these functions are macro blocks similar to those  used in the
+ * inline/#?.h headers.
+ */
+#if defined(__SASC) || defined (__STORM__)
+
+       INLINE ULONG CoerceMethod (struct IClass *cl, Object *o, ULONG MethodID, ...)
+       {
+               ASSERT_VALIDNO0(cl)
+               ASSERT_VALID(o)
+
+               return ((_HookPtr)cl->cl_Dispatcher.h_Entry) ((APTR)cl, (APTR)o, (APTR)&MethodID);
+       }
+
+       INLINE ULONG DoSuperMethod (struct IClass *cl, Object *o, ULONG MethodID, ...)
+       {
+               ASSERT_VALIDNO0(cl)
+               ASSERT_VALID(o)
+
+               cl = cl->cl_Super;
+               return ((_HookPtr)cl->cl_Dispatcher.h_Entry) ((APTR)cl, (APTR)o, (APTR)&MethodID);
+       }
+
+       INLINE ULONG DoMethod (Object *o, ULONG MethodID, ...)
+       {
+               Class *cl;
+
+               ASSERT_VALIDNO0(o)
+               cl = OCLASS (o);
+               ASSERT_VALIDNO0(cl)
+
+               return ((_HookPtr)cl->cl_Dispatcher.h_Entry) ((APTR)cl, (APTR)o, (APTR)&MethodID);
+       }
+
+       /* varargs stub for the OM_NOTIFY method */
+       INLINE void NotifyAttrs (Object *o, struct GadgetInfo *gi, ULONG flags, Tag attr1, ...)
+       {
+               ASSERT_VALIDNO0(o)
+               ASSERT_VALID(gi)
+
+               DoMethod (o, OM_NOTIFY, &attr1, gi, flags);
+       }
+
+       /* varargs stub for the OM_UPDATE method */
+       INLINE void UpdateAttrs (Object *o, struct GadgetInfo *gi, ULONG flags, Tag attr1, ...)
+       {
+               ASSERT_VALIDNO0(o)
+               ASSERT_VALID(gi)
+
+               DoMethod (o, OM_UPDATE, &attr1, gi, flags);
+       }
+
+#elif defined(__GNUC__)
+
+       #define CoerceMethod(cl, o, msg...)                                                                                             \
+       ({                                                                                                                                                              \
+               ULONG _msg[] = { msg };                                                                                                         \
+               ASSERT_VALIDNO0(cl)                                                                                                                     \
+               ASSERT_VALID(o)                                                                                                                         \
+               ((_HookPtr)cl->cl_Dispatcher.h_Entry) ((APTR)cl, (APTR)o, (APTR)_msg);          \
+       })
+
+       #define DoSuperMethod(cl, o, msg...)                                                                                    \
+       ({                                                                                                                                                              \
+               Class *_cl;                                                                                                                                     \
+               ULONG _msg[] = { msg };                                                                                                         \
+               ASSERT_VALID(o)                                                                                                                         \
+               ASSERT_VALIDNO0(cl)                                                                                                                     \
+               _cl = cl = cl->cl_Super;                                                                                                        \
+               ASSERT_VALIDNO0(_cl)                                                                                                            \
+               ((_HookPtr)_cl->cl_Dispatcher.h_Entry) ((APTR)_cl, (APTR)o, (APTR)_msg);        \
+       })
+
+       #define DoMethod(o, msg...)                                                                                                             \
+       ({                                                                                                                                                              \
+               Class *_cl;                                                                                                                                     \
+               ULONG _msg[] = { msg };                                                                                                         \
+               ASSERT_VALIDNO0(o)                                                                                                                      \
+               _cl = OCLASS(o);                                                                                                                        \
+               ASSERT_VALIDNO0(_cl)                                                                                                            \
+               ((_HookPtr)_cl->cl_Dispatcher.h_Entry) ((APTR)_cl, (APTR)o, (APTR)_msg);        \
+       })
+
+       /* Var-args stub for the OM_NOTIFY method */
+       #define NotifyAttrs(o, gi, flags, attrs...)                                                                             \
+       ({                                                                                                                                                              \
+               Class *_cl;                                                                                                                                     \
+               ULONG _attrs[] = { attrs };                                                                                                     \
+               ULONG _msg[] = { OM_NOTIFY, (ULONG)_attrs, (ULONG)gi, flags };                          \
+               ASSERT_VALIDNO0(o)                                                                                                                      \
+               _cl = OCLASS(o);                                                                                                                        \
+               ASSERT_VALIDNO0(_cl)                                                                                                            \
+               ASSERT_VALID(gi)                                                                                                                        \
+               ((_HookPtr)_cl->cl_Dispatcher.h_Entry) ((APTR)_cl, (APTR)o, (APTR)_msg);        \
+       })
+
+       /* Var-args stub for the OM_UPDATE method */
+       #define UpdateAttrs(o, gi, flags, attrs...)                                                                             \
+       ({                                                                                                                                                              \
+               Class *_cl;                                                                                                                                     \
+               ULONG _attrs[] = { attrs };                                                                                                     \
+               ULONG _msg[] = { OM_UPDATE, (ULONG)_attrs, (ULONG)gi, flags };                          \
+               ASSERT_VALIDNO0(o)                                                                                                                      \
+               _cl = OCLASS(o);                                                                                                                        \
+               ASSERT_VALIDNO0(_cl)                                                                                                            \
+               ASSERT_VALID(gi)                                                                                                                        \
+               ((_HookPtr)_cl->cl_Dispatcher.h_Entry) ((APTR)_cl, (APTR)o, (APTR)_msg);        \
+       })
+#endif
+
+/* Nobody else needs this anymore... */
+#undef _HookPtr
+
+#endif /* !BOOPSISTUBS_H */
diff --git a/CompilerSpecific.h b/CompilerSpecific.h
new file mode 100644 (file)
index 0000000..6041ece
--- /dev/null
@@ -0,0 +1,304 @@
+#ifndef COMPILERSPECIFIC_H
+#define COMPILERSPECIFIC_H
+/*
+**     $VER: CompilerSpecific.h 2.3 (26.10.97)
+**
+**     Copyright (C) 1997 Bernardo Innocenti. All rights reserved.
+**
+**     Compiler specific definitions is here. You can add support
+**     for other compilers in this header. Please return any changes
+**     you make to me, so I can add them to my personal copy of this file.
+**
+**     Here is a short description of the macros defined below:
+**
+**     LIBCALL
+**             Shared library entry point, with register args
+**
+**     HOOKCALL
+**             Hook or boopsi dispatcher entry point with arguments
+**             passed in registers
+**
+**     GLOBALCALL
+**             Attribute for functions to be exported to other modules for
+**             global access within the same executable file.
+**             Usually defined to "extern", but can be overridden for special
+**             needs, such as compiling all modules together in a single
+**             object module to optimize code better.
+**
+**     XDEF
+**             Attribute for symbols to be exported to other modules for
+**             global access within the same executable file.
+**             Usually defined to an empty value.
+**
+**     XREF
+**             Attribute for symbols to be imported from other modules
+**             within the same executable file.
+**             Usually defined to "extern".
+**
+**     INLINE
+**             Please put function body inline to the calling code
+**
+**     STDARGS
+**             Function uses standard C conventions for arguments
+**
+**     ASMCALL
+**             Function takes arguments in the specified 68K registers
+**
+**     REGCALL
+**             Function takes arguments in registers choosen by the compiler
+**
+**     CONSTCALL
+**             Function does not modify any global variable
+**
+**     FORMATCALL(archetype,string_index,first_to_check)
+**             Function uses printf or scanf-like formatting
+**
+**     SAVEDS
+**             Function needs to reload context for small data model
+**
+**     INTERRUPT
+**             Function will be called from within an interrupt
+**
+**     NORETURN
+**             Function does never return
+**
+**     ALIGNED
+**             Variable must be aligned to longword boundaries
+**
+**     CHIP
+**             Variable must be stored in CHIP RAM
+**
+**     REG(reg,arg)
+**             Put argument <arg> in 68K register <reg>
+**
+**     min(a,b)
+**             Return the minimum between <a> and <b>
+**
+**     max(a,b)
+**             Return the maximum between <a> and <b>
+**
+**     abs(a)
+**             Return the absolute value of <a>
+**
+**     _COMPILED_WITH
+**             A string containing the name of the compiler
+*/
+
+#ifdef __SASC
+       /* SAS/C 6.58 or better */
+
+       #define INLINE          static __inline
+       #define STDARGS         __stdargs
+       #define ASMCALL         __asm
+       #define REGCALL         __regcall
+       #define CONSTCALL       /* unsupported */
+       #define FORMATCALL      /* unsupported */
+       #define SAVEDS          __saveds
+       #define INTERRUPT       __interrupt
+       #define NORETURN        /* unsupported */
+       #define ALIGNED         __aligned
+       #define CHIP            __chip
+       #define REG(reg,arg) register __##reg arg
+       #define _COMPILED_WITH  "SAS/C"
+
+       /* For min(), max() and abs() */
+       #define USE_BUILTIN_MATH
+       #include <string.h>
+#else
+#ifdef __GNUC__
+       /* GeekGadgets GCC 2.7.2.1 or better */
+
+       #define INLINE          static inline
+       #define STDARGS         __attribute__((stkparm))
+       #define ASMCALL         /* nothing */
+       #define REGCALL         /* nothing */
+       #define CONSTCALL       __attribute__((const))
+       #define FORMATCALL(a,s,f)       __attribute__((format(a,s,f)))
+       #define SAVEDS          __attribute__((saveds))
+       #define INTERRUPT       __attribute__((interrupt))
+       #define NORETURN        __attribute__((noreturn))
+       #define ALIGNED         __attribute__((aligned(4)))
+       #define REG(reg,arg) arg __asm(#reg)
+       #define _COMPILED_WITH  "GCC"
+
+       #define min(a,b)        (((a)<(b))?(a):(b))
+       #define max(a,b)        (((a)>(b))?(a):(b))
+       #define abs(a)          (((a)>0)?(a):-(a))
+
+       /* Inline versions of some str*() and mem*() functions
+        */
+       #define _ANSI_SOURCE
+       #include <string.h>
+       #undef _ANSI_SOURCE
+
+       INLINE STDARGS size_t strlen (const char *src)
+               { const char *tmp = src; while (*tmp) tmp++; return tmp - src; }
+
+       INLINE STDARGS int memcmp (const void *src, const void *dest, size_t n)
+       {
+               while (n--)
+               {
+                       if (*((char *)src) != *((char *)dest))
+                               return (*((char *)src) - *((char *)dest));
+                       ((char *)src)++;
+                       ((char *)dest)++;
+               }
+               return 0;
+       }
+
+       INLINE STDARGS void * memset (void *m, int c, size_t n)
+       {
+               void *r = m;
+               n++;
+               while (--n > 0)
+                       *(((unsigned char *) m)++) = (unsigned char) c;
+               return r;
+       }
+
+       /* GCC produces code which calls these two functions
+        * to initialize and copy structures and arrays.
+        *
+        *      INLINE STDARGS void bzero (void *buf, size_t len)
+        *              { while (len--) *((char *)buf)++ = 0; }
+        *
+        *      INLINE STDARGS void bcopy (const void *src, void *dest, size_t len)
+        *              { while (len--) *((char *)dest)++ = *((const char *)src)++; }
+        */
+
+#else
+#ifdef __STORM__
+       /* StormC 2.00.23 or better */
+       #define INLINE          __inline
+       #define STDARGS         /* nothing */
+       #define ASMCALL         /* nothing */
+       #define REGCALL         register
+       #define CONSTCALL       /* unsupported */
+       #define FORMATCALL      /* unsupported */
+       #define SAVEDS          __saveds
+       #define INTERRUPT       __interrupt
+       #define NORETURN        /* unsupported */
+       #define ALIGNED         /* unsupported */
+       #define CHIP            __chip
+       #define REG(reg,arg) register __##reg arg
+       #define _COMPILED_WITH  "StormC"
+
+       #define min(a,b)        (((a)<(b))?(a):(b))
+       #define max(a,b)        (((a)>(b))?(a):(b))
+       #define abs(a)          (((a)>0)?(a):-(a))
+
+       #define _INLINE_INCLUDES
+       #include <string.h>
+#else
+#ifdef __MAXON__
+       /* Maxon C/C++ 3.0 */
+
+       #define INLINE          static inline
+       #define STDARGS         /* ? */
+       #define ASMCALL         /* ? */
+       #define REGCALL         /* ? */
+       #define CONSTCALL       /* unsupported */
+       #define FORMATCALL      /* unsupported */
+       #define SAVEDS          /* unsupported */
+       #define INTERRUPT       /* unsupported */
+       #define NORETURN        /* unsupported */
+       #define ALIGNED         /* unsupported */
+       #define REG(reg,arg)    register __##reg arg
+       #define _COMPILED_WITH  "Maxon C"
+
+       /* For min(), max() and abs() */
+       #define USE_BUILTIN_MATH
+       #include <string.h>
+
+       #error Maxon C compiler support is untested. Please check all the above definitions
+#else
+#ifdef _DCC
+       /* DICE C 3.15 */
+
+       #define INLINE          static __inline
+       #define STDARGS         __stdargs
+       #define ASMCALL         /* nothing */
+       #define REGCALL         /* ? */
+       #define CONSTCALL       /* unsupported */
+       #define FORMATCALL      /* unsupported */
+       #define SAVEDS          __geta4
+       #define INTERRUPT       /* unsupported */
+       #define NORETURN        /* unsupported */
+       #define ALIGNED         __aligned
+       #define REG(reg,arg)    __##reg arg
+       #define _COMPILED_WITH  "DICE"
+
+       #define min(a,b)        (((a)<(b))?(a):(b))
+       #define max(a,b)        (((a)>(b))?(a):(b))
+       #define abs(a)          (((a)>0)?(a):-(a))
+
+       #error DICE compiler support is untested. Please check all the above definitions
+#else
+#ifdef AZTEC_C
+       /* Aztec/Manx C */
+
+       #define INLINE          static
+       #define STDARGS         /* ? */
+       #define ASMCALL         /* ? */
+       #define REGCALL         /* ? */
+       #define CONSTCALL       /* unsupported */
+       #define FORMATCALL      /* unsupported */
+       #define SAVEDS          __geta4
+       #define INTERRUPT       /* unsupported */
+       #define NORETURN        /* unsupported */
+       #define ALIGNED         __aligned
+       #define REG(reg,arg)    __##reg arg
+       #define _COMPILED_WITH  "Manx C"
+
+       #define min(a,b)        (((a)<(b))?(a):(b))
+       #define max(a,b)        (((a)>(b))?(a):(b))
+       #define abs(a)          (((a)>0)?(a):-(a))
+
+       #error Aztec/Manx C compiler support is untested. Please check all the above definitions
+#else
+       #error Please add compiler specific definitions for your compiler
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+
+
+/* CONST_STRPTR is a typedef made by a patched version of <exec/types.h>.
+ * Passing "const char *" parameters will only work if the OS protos are
+ * patched accordingly, otherwise you will get a lot of compiler warnings
+ * for const to volatile conversions.
+ *
+ * Using "const" where it is appropriate helps the compiler optimizing
+ * code better, so this mess is probably worth it. I wish one day the OS
+ * headers will come with support for the const keyword.
+ */
+#ifndef _CONST_STRPTR_DEFINED
+typedef char *CONST_STRPTR;
+#endif
+
+
+/* Special function attributes */
+
+#define LIBCALL                ASMCALL SAVEDS
+#define HOOKCALL       ASMCALL SAVEDS
+#ifdef __cplusplus
+       #define GLOBALCALL extern "C"
+#else
+       #define GLOBALCALL
+#endif
+
+/* special variable attributes */
+#define XDEF
+#define XREF extern
+
+
+/* AROS Compatibility: IPTR is a type which can store a pointer
+ * as well as a long integer.
+ */
+#ifndef IPTR
+#define IPTR LONG
+#endif /* IPTR */
+
+
+#endif /* !COMPILERSPECIFIC_H */
diff --git a/Debug.h b/Debug.h
new file mode 100644 (file)
index 0000000..ba7b825
--- /dev/null
+++ b/Debug.h
@@ -0,0 +1,103 @@
+#ifndef DEBUG_H
+#define DEBUG_H
+/*
+**     $VER: Debug.h 2.2 (19.9.98)
+**
+**     Copyright (C) 1995,96,97 Bernardo Innocenti. All rights reserved.
+**
+**     Use 4 chars wide TABs to read this file
+**
+**     Some handy debug macros which are automatically excluded when the
+**     DEBUG preprocessor sysmbol is not defined. To make debug executables,
+**     link with debug.lib or any module containing the kprintf() function.
+**
+**     Here is a short description of the macros defined below:
+**
+**     ILLEGAL
+**             Output an inline "ILLEGAL" 68K opcode, which will
+**             be interpreted as a breakpoint by most debuggers.
+**
+**     DBPRINTF
+**             Output a formatted string to the debug console. This
+**             macro uses the debug.lib kprintf() function by default.
+**
+**     ASSERT(x)
+**             Do nothing if the expression <x> evalutates to a
+**             non-zero value, output a debug message otherwise.
+**
+**     ASSERT_VALID(x)
+**             Checks if the expression <x> points to a valid
+**             memory location, and outputs a debug message
+**             otherwise. A NULL pointer is considered VALID.
+**
+**     ASSERT_VALIDNO0(x)
+**             Checks if the expression <x> points to a valid
+**             memory location, and outputs a debug message
+**             otherwise. A NULL pointer is considered INVALID.
+**
+**     DB(x)
+**             Compile the expression <x> when making a debug
+**             executable, leave it out otherwise.
+*/
+
+#ifdef DEBUG
+
+       /* Needed for TypeOfMem() */
+       #ifndef  PROTO_EXEC_H
+       #include <proto/exec.h>
+       #endif /* PROTO_EXEC_H */
+
+       #if defined(__SASC)
+
+               extern void __builtin_emit (int);
+               // #define ILLEGAL __builtin_emit(0x4AFC)
+               #define ILLEGAL 0
+               STDARGS extern void kprintf (const char *, ...);
+
+       #elif defined(__GNUC__)
+
+               /* Currently, there is no kprintf() in libamiga.a */
+               #define kprintf printf
+
+               /* GCC doesn't accept asm statemnts in the middle of an
+                * expression such as `a ? b : asm("something")'.
+                */
+               #define ILLEGAL illegal()
+               static inline int illegal(void) { asm ("illegal"); return 0; }
+               extern void STDARGS FORMATCALL(printf,1,2) kprintf (const char *, ...);
+
+       #else
+               #error Please add compiler specific definitions for your compiler
+       #endif
+
+       #if defined(__SASC) || defined (__GNUC__)
+
+               /* common definitions for ASSERT and DB macros */
+
+               #define DBPRINTF kprintf
+
+               #define ASSERT(x) ( (x) ? 0 :                                                                   \
+                       ( DBPRINTF ("\x07%s, %ld: assertion failed: " #x "\n",          \
+                       __FILE__, __LINE__) , ILLEGAL ) );
+
+               #define ASSERT_VALID(x) ( ((((APTR)(x)) == NULL) ||                             \
+                       (((LONG)(x) > 1024) &&  TypeOfMem ((APTR)(x)))) ? 0 :           \
+                       ( DBPRINTF ("\x07%s, %ld: bad address: " #x " = $%lx\n",        \
+                       __FILE__, __LINE__, (APTR)(x)) , ILLEGAL ) );
+
+               #define ASSERT_VALIDNO0(x) ( (((LONG)(x) > 1024) &&                             \
+                       TypeOfMem ((APTR)(x))) ? 0 :                                                            \
+                       ( DBPRINTF ("\x07%s, %ld: bad address: " #x " = $%lx\n",        \
+                       __FILE__, __LINE__, (APTR)(x)) , ILLEGAL ) );
+
+               #define DB(x) x
+       #endif
+
+#else
+       #define ASSERT_VALID(x)
+       #define ASSERT_VALIDNO0(x)
+       #define ASSERT(x)
+       #define DB(x)
+#endif /* DEBUG */
+
+#endif /* !DEBUG_H */
diff --git a/GNUmakefile b/GNUmakefile
new file mode 100644 (file)
index 0000000..9b8bd82
--- /dev/null
@@ -0,0 +1,92 @@
+##
+##     $VER: LVDemo_Makefile 2.1 (5.9.97)
+##
+##     Copyright (C) 1996,97 by Bernardo Innocenti
+##
+##     Makefile for GCC
+##
+
+###########################################################
+# Name of the main executable
+###########################################################
+#
+PROJ = LVDemo
+
+
+###########################################################
+# Package configuration
+###########################################################
+#
+
+# set to OS30_ONLY to leave out support for old V37
+# set to ANY_OS to make an executable for V37 with V39 support
+#
+OSVER = ANY_OS
+
+# cpu to compile for.
+#
+CPU = 68020
+
+
+###########################################################
+# Object files in this project
+###########################################################
+#
+OBJS = startup_gcc.o LVDemo.o ListViewHooks.o \
+ ListViewClass.o ListBoxClass.o ScrollButtonClass.o
+
+
+###########################################################
+# Make the project
+###########################################################
+#
+all: $(PROJ)
+
+
+###########################################################
+# Remove all targets and intermediate files
+###########################################################
+#
+clean:
+       -Delete $(PROJ) $(OBJS)
+
+
+###########################################################
+# Dependences
+###########################################################
+#
+LVDemo.c ListViewClass.c: ListViewClass.h
+
+
+###########################################################
+# GCC Release version should be compiled with these flags
+###########################################################
+#
+CC = gcc
+CFLAGS = -c -O0 -finline-functions -fno-implement-inlines \
+ -m$(CPU) -msmall-code -mregparm -fomit-frame-pointer \
+ -I/gg/include -I/include -Wunused -Wreturn-type -D$(OSVER)
+LFLAGS = -s
+LIBS = -noixemul -nostdlib
+
+
+###########################################################
+# GCC - Make the executable
+###########################################################
+#
+
+# Assemble startup code
+#
+startup_gcc.o: startup_gcc.s
+       $(AS) startup_gcc.s -o startup_gcc.o
+
+# Compile C sources and make the object files
+#
+.c.o: PIPClass.h
+       $(CC) $(*).c $(CFLAGS)
+
+# Link object files and make the executable
+#
+$(PROJ): $(OBJS)
+       $(CC) $(OBJS) -o $(PROJ) $(LFLAGS) $(LIBS)
+       @Protect $(PROJ) +e
diff --git a/GST.c b/GST.c
new file mode 100644 (file)
index 0000000..260f8e9
--- /dev/null
+++ b/GST.c
@@ -0,0 +1,45 @@
+/*
+**     GST.c
+**
+**     Copyright (C) 1997 by Bernardo Innocenti
+**
+**     This is a dummy source file used to make the Global Symbol Table
+**     for LVDemo.
+*/
+
+#include <exec/types.h>
+#include <exec/libraries.h>
+#include <exec/memory.h>
+#include <exec/execbase.h>
+#include <devices/timer.h>
+#include <intuition/classes.h>
+#include <intuition/intuition.h>
+#include <intuition/intuitionbase.h>
+#include <intuition/gadgetclass.h>
+#include <intuition/icclass.h>
+#include <intuition/imageclass.h>
+#include <intuition/screens.h>
+#include <graphics/gfxbase.h>
+#include <graphics/gfxmacros.h>
+
+#include <proto/dos.h>
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <proto/graphics.h>
+#include <proto/layers.h>
+#include <proto/utility.h>
+#include <clib/alib_stdio_protos.h>
+
+#include "CompilerSpecific.h"
+#include "Debug.h"
+
+/* Can't include these in GST because they are defining some inline functions
+ * #include "BoopsiStubs.h"
+ * #include "ListMacros.h"
+ */
+
+#define LV_GADTOOLS_STUFF
+#include "ScrollButtonClass.h"
+#include "ListViewClass.h"
+#include "VectorGlyphIClass.h"
+#include "ListBoxClass.h"
diff --git a/LVDemo.c b/LVDemo.c
new file mode 100644 (file)
index 0000000..e6900e2
--- /dev/null
+++ b/LVDemo.c
@@ -0,0 +1,1043 @@
+/*     $VER: ListViewDemo 1.6 (15.12.98) by Bernardo Innocenti
+**
+**
+**     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.7.2.1 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 by Bernardo Innocenti <bernardo.innocenti@usa.net>.
+**     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 "CompilerSpecific.h"
+#include "Debug.h"
+#include "BoopsiStubs.h"
+#include "ListMacros.h"
+
+#include "ListViewClass.h"
+#include "ListBoxClass.h"
+#include "VectorGlyphIClass.h"
+#include "ScrollButtonClass.h"
+
+/* OS version */
+
+#ifdef OS30_ONLY
+ #define WANTEDLIBVER  39L
+#else
+ #define WANTEDLIBVER  37L
+#endif
+
+
+/* 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", WANTEDLIBVER)) &&
+               (GfxBase = (struct GfxBase *)OpenLibrary ("graphics.library", WANTEDLIBVER)) &&
+               (LayersBase = OpenLibrary ("layers.library", WANTEDLIBVER)))
+       {
+               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);
+}
+
+
+
+/* No-op backfilling hook.  Since we are going to redraw the whole window
+ * anyway, we can disable backfilling.  This avoids ugly flashing while
+ * resizing or revealing the window.
+ *
+ * This function does not need the __saveds attribute because it makes no
+ * references to external data.
+ */
+
+#ifndef OS30_ONLY
+
+static ULONG BFHookFunc (void)
+{
+       return 1;       /* Do nothing */
+}
+
+static struct Hook BFHook =
+{
+       NULL, NULL,
+       (ULONG(*)())BFHookFunc,
+};
+
+#endif /* !OS30_ONLY */
+
+
+
+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))
+                       {
+
+/* I'm using this define because GCC does not support putting
+ * preprocessor directives (#ifdef/#endif) inside macro arguments.
+ */
+#ifndef OS30_ONLY
+ #define BACKFILL_TAG_VALUE    ((IntuitionBase->LibNode.lib_Version < 39) ? &BFHook : LAYERS_NOBACKFILL)
+#else
+ #define BACKFILL_TAG_VALUE    LAYERS_NOBACKFILL
+#endif /* !OS30_ONLY */
+
+                               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,
+                                       WA_BackFill,            useListBox ? NULL : BACKFILL_TAG_VALUE,
+                                       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))
+#undef BACKFILL_TAG_VALUE
+                               {
+                                       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.
+        */
+       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/LVDemo.¶ b/LVDemo.¶
new file mode 100644 (file)
index 0000000..d3914ce
--- /dev/null
+++ b/LVDemo.¶
@@ -0,0 +1,147 @@
+Storm Shell Project (0010)
+Settings (Start)
+C/C++ Environment
+"StormC:include"
+"Include:"
+Includepath (End)
+0 "LVDemo.StormHeader" 128
+0 "objects"
+C/C++ Preprozessor
+1 "_INLINE_INCLUDES" ""
+Defines (End)
+1 1 1
+C/C++ Options
+0 1 1 0 0 0 0 0 0 1 1 0 1
+C/C++ Optimizer
+9
+C/C++ Warnings
+1 1 1 1 1 1 0 1
+Assembler
+0 ""
+0 0
+Linker
+0 2 "startup_storm.o" 1 0 1 0 1 1 0
+"StormC:lib" 0 "StormC:lib/logfile" 0 1 1
+0 "_WizardSurface"
+0 0 50 0 50 0 50 0 0
+0 0 0 0 0 0 0 0
+Run
+16 "" "" "" 0
+""
+
+1 "CON://400/180/Storm Console/AUTO/WAIT/SCREEN StormScreen" "RAM:Output" "RAM:Input"
+1 0
+0 0 0 "" ""
+Settings (End)
+File
+1 "LVDemo.c"
+"LVDemo.c"
+"CompilerSpecific.h"
+"Debug.h"
+"BoopsiStubs.h"
+"ListMacros.h"
+"ListViewClass.h"
+"ListBoxClass.h"
+"VectorGlyphIClass.h"
+Storm Shell Project (Dependencies)
+"LVDemo.o" "LVDemo.debug"
+""
+"LVDemo.o" "LVDemo.debug"
+File
+1 "ListViewClass.c"
+"ListViewClass.c"
+"CompilerSpecific.h"
+"Debug.h"
+"BoopsiStubs.h"
+"ListViewClass.h"
+Storm Shell Project (Dependencies)
+"ListViewClass.o" "ListViewClass.debug"
+""
+"ListViewClass.o" "ListViewClass.debug"
+File
+1 "ListViewHooks.c"
+"ListViewHooks.c"
+"CompilerSpecific.h"
+"Debug.h"
+"ListViewClass.h"
+Storm Shell Project (Dependencies)
+"ListViewHooks.o" "ListViewHooks.debug"
+""
+"ListViewHooks.o" "ListViewHooks.debug"
+File
+1 "ListBoxClass.c"
+"ListBoxClass.c"
+"CompilerSpecific.h"
+"Debug.h"
+"BoopsiStubs.h"
+"ListViewClass.h"
+"ListBoxClass.h"
+Storm Shell Project (Dependencies)
+"ListBoxClass.o" "ListBoxClass.debug"
+""
+"ListBoxClass.o" "ListBoxClass.debug"
+Section
+1 1 100
+File
+2 "Work:SC/src/Include/CompilerSpecific.h"
+"Work:SC/src/Include/CompilerSpecific.h"
+Storm Shell Project (Dependencies)
+"" ""
+""
+File
+2 "Work:SC/src/Include/Debug.h"
+"Work:SC/src/Include/Debug.h"
+Storm Shell Project (Dependencies)
+"" ""
+""
+File
+2 "Work:SC/src/Include/BoopsiStubs.h"
+"Work:SC/src/Include/BoopsiStubs.h"
+Storm Shell Project (Dependencies)
+"" ""
+""
+File
+2 "ListViewClass.h"
+"ListViewClass.h"
+Storm Shell Project (Dependencies)
+"" ""
+""
+File
+2 "ListBoxClass.h"
+"ListBoxClass.h"
+Storm Shell Project (Dependencies)
+"" ""
+""
+File
+2 "Work:SC/src/Include/ListMacros.h"
+"Work:SC/src/Include/ListMacros.h"
+Storm Shell Project (Dependencies)
+"" ""
+""
+File
+2 "VectorGlyphIClass.h"
+"VectorGlyphIClass.h"
+Storm Shell Project (Dependencies)
+"" ""
+""
+Section
+2 1 95
+File
+12 "startup_storm.s"
+"startup_storm.s"
+Storm Shell Project (Dependencies)
+"startup_storm.o" ""
+""
+"startup_storm.o"
+Section
+12 1 90
+File
+10 "LVDemo"
+"LVDemo"
+Storm Shell Project (Dependencies)
+"" ""
+""
+"LVDemo.link"
+Section
+10 1 40
+Storm Shell Project (End)
diff --git a/ListBoxClass.c b/ListBoxClass.c
new file mode 100644 (file)
index 0000000..6c88ee3
--- /dev/null
@@ -0,0 +1,770 @@
+/*
+**     ListBoxClass.c
+**
+**     Copyright (C) 1997,98 Bernardo Innocenti
+**
+**     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 __USE_SYSBASE
+#define  CLIB_ALIB_PROTOS_H            /* Avoid dupe defs 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
+
+#include "CompilerSpecific.h"
+#include "Debug.h"
+#include "BoopsiStubs.h"
+
+#define LV_GADTOOLS_STUFF
+#include "ScrollButtonClass.h"
+#include "ListViewClass.h"
+#include "VectorGlyphIClass.h"
+#include "ListBoxClass.h"
+
+
+
+#define VSLIDER_WIDTH  14
+#define HSLIDER_HEIGHT 14
+
+
+/* Per-object instance data */
+struct LBData
+{
+       /* struct Gadget *ThisGadget; (not used) */
+
+       /* Group children */
+       Object *ListView;
+       Object *HSlider;
+       Object *VSlider;
+       Object *UpButton;
+       Object *DownButton;
+       Object *LeftButton;
+       Object *RightButton;
+       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 listbox object */
+
+       struct Image *UpImage;
+       struct Image *DownImage;
+       struct Image *LeftImage;
+       struct Image *RightImage;
+
+       /* 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           *ScrollButtonClass;
+       struct Library  *VectorGlyphBase;
+};
+
+
+extern Class *ListViewClass;
+
+
+/* Local function prototypes */
+static void            LB_GMRender             (Class *cl, struct Gadget *g, struct gpRender *msg);
+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);
+
+INLINE void            GetGadgetBox    (struct GadgetInfo *ginfo, struct ExtGadget *g, struct IBox *box, struct Rectangle *rect);
+
+
+
+/* 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
+};
+
+
+
+static ULONG HOOKCALL LBDispatcher (
+       REG(a0, Class *cl),
+       REG(a2, struct Gadget *g),
+       REG(a1, Msg msg))
+{
+       ASSERT_VALIDNO0(cl)
+       ASSERT_VALIDNO0(g)
+       ASSERT_VALIDNO0(msg)
+
+       switch (msg->MethodID)
+       {
+               case GM_RENDER:
+                       LB_GMRender (cl, g, (struct gpRender *)msg);
+                       return TRUE;
+
+               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.
+                        */
+                       /* DB(kprintf("ListBoxClass: passing unknown method 0x%lx to superclass...\n", msg->MethodID);)
+                        */
+                       return DoSuperMethodA (cl, (Object *)g, msg);
+       }
+}
+
+
+
+static void LB_GMRender (Class *cl, struct Gadget *g, struct gpRender *msg)
+{
+       struct LBData           *lb = INST_DATA (cl, g);
+
+       ASSERT_VALIDNO0(lb)
+
+
+#ifndef OS30_ONLY
+       /* Pre-V39 Intuition won't call our GM_LAYOUT method, so we must
+        * always call it before redrawing the gadget.
+        */
+       if ((IntuitionBase->LibNode.lib_Version < 39) &&
+               (msg->gpr_Redraw == GREDRAW_REDRAW))
+               LB_GMLayout (cl, g, (struct gpLayout *)msg);
+#endif /* !OS30_ONLY */
+
+
+       /* The groupgclass does not render its imagery,
+        * it only calls GM_RENDER for all its children
+        */
+       if (msg->gpr_Redraw == GREDRAW_REDRAW)
+       {
+               DB (kprintf ("ListBoxClass: GM_RENDER: msg->gpr_Redraw = GREDRAW_REDRAW\n");)
+
+               DoMethod (lb->Frame, IM_DRAWFRAME,
+                       msg->gpr_RPort,                                                         /* imp_RPort            */
+                       (lb->GBox.Left << 16) | (lb->GBox.Top),         /* imp_Offset           */
+                       IDS_NORMAL,                                                                     /* imp_State            */
+                       msg->gpr_GInfo->gi_DrInfo,                                      /* imp_DrInfo           */
+                       (lb->GBox.Width << 16) | (lb->GBox.Height));/* imp_Dimensions   */
+       }
+
+       /* Forward message to superclass so it will call it on all our children */
+       DoSuperMethodA (cl, (Object *)g, (Msg)msg);
+}
+
+
+
+static void LB_GMLayout (Class *cl, struct Gadget *g, struct gpLayout *msg)
+{
+       struct LBData   *lb = (struct LBData *) INST_DATA (cl, (Object *)g);
+
+       DB (kprintf ("ListBoxClass: GM_LAYOUT\n");)
+       ASSERT_VALIDNO0(lb)
+
+
+       /* Collect new group size */
+       GetGadgetBox (msg->gpl_GInfo, (struct ExtGadget *)g, &lb->GBox, &lb->GRect);
+
+       /* Size our children accordingly */
+       SetAttrs (lb->ListView,
+               // msg->gpl_GInfo->gi_Window, msg->gpl_GInfo->gi_Requester,
+               GA_Left,        lb->GBox.Left + 2,                                              // +lb->FrameWidth,
+               GA_Top,         lb->GBox.Top + 2,                                               // +lb->FrameHeight,
+               GA_Width,       lb->GBox.Width - lb->FrameWidth - VSLIDER_WIDTH,
+               GA_Height,      lb->GBox.Height - HSLIDER_HEIGHT,               // -lb->FrameHeight,
+               TAG_DONE);
+
+       SetAttrs (lb->VSlider,
+               // msg->gpl_GInfo->gi_Window, msg->gpl_GInfo->gi_Requester,
+               GA_Left,        lb->GRect.MaxX - 2 - VSLIDER_WIDTH,             // lb->FrameWidth,
+               GA_Top,         lb->GBox.Top + 2,                                               // lb->FrameHeight,
+               GA_Width,       VSLIDER_WIDTH,
+               GA_Height,      lb->GBox.Height - HSLIDER_HEIGHT * 3,   // g->Height - 10,
+               TAG_DONE);
+
+       SetAttrs (lb->UpButton,
+               GA_Left,        lb->GRect.MaxX - 2 - VSLIDER_WIDTH,             // lb->FrameWidth,
+               GA_Top,         lb->GRect.MaxY - 2 - HSLIDER_HEIGHT * 2,// lb->FrameHeight,
+               GA_Width,       VSLIDER_WIDTH,
+               GA_Height,      HSLIDER_HEIGHT,
+               TAG_DONE);
+
+       DB (kprintf ("ListBoxClass: GM_LAYOUT: upbutton left: %ld, upbutton top: %ld\n",
+               lb->GRect.MaxX - 2 - VSLIDER_WIDTH, lb->GRect.MaxY - 2 - HSLIDER_HEIGHT * 2);)
+
+
+       SetAttrs (lb->DownButton,
+               GA_Left,        lb->GRect.MaxX - 2 - VSLIDER_WIDTH,             // lb->FrameWidth,
+               GA_Top,         lb->GRect.MaxY - 2 - HSLIDER_HEIGHT,    // lb->FrameHeight,
+               GA_Width,       VSLIDER_WIDTH,
+               GA_Height,      HSLIDER_HEIGHT,
+               TAG_DONE);
+
+
+       /* NOTE: it seems that the groupgclass does not forward GM_LAYOUT
+        * to its children, so we must handle this here.
+        */
+
+       /* Forward GM_LAYOUT to embedded listview */
+       DoMethodA (lb->ListView, (Msg)msg);
+}
+
+
+
+static ULONG LB_OMSet (Class *cl, struct Gadget *g, struct opUpdate *msg)
+{
+       struct LBData   *lb = (struct LBData *) INST_DATA (cl, (Object *)g);
+
+       DB (kprintf ("ListBoxClass: OM_SET\n");)
+       ASSERT_VALIDNO0(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);
+
+       DB (kprintf ("ListBoxClass: OM_GET\n");)
+       ASSERT_VALIDNO0(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;
+
+
+       DB (kprintf ("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 (kprintf ("ListBoxClass: OM_NEW: Setting GMORE_SCROLLRASTER\n");)
+                       ((struct ExtGadget *)g)->MoreFlags |= GMORE_SCROLLRASTER;
+               }
+
+               lb = (struct LBData *) INST_DATA (cl, (Object *)g);
+               ASSERT_VALIDNO0(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 to all functions.
+                */
+               /* lb->ThisGadget = g; (not used) */
+
+
+               /* May be NULL */
+               dri = (struct DrawInfo *) GetTagData (GA_DrawInfo, NULL, msg->ops_AttrList);
+
+
+               /* 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 = NewObjectA (ListViewClass, NULL, msg->ops_AttrList))
+                       {
+                               /* From now no, the groupgclass will dispose this object for us */
+                               DoMethod ((Object *)g, OM_ADDMEMBER, lb->ListView);
+
+                               //SetAttrs (lb->Model,
+                               //      ICA_TARGET, lb->ListView,
+                               //      TAG_DONE);
+
+                               /* 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);
+
+
+                               /* 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;
+                               }
+
+
+                               /* 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);
+
+                                       DB (kprintf ("ListBoxClass: OM_NEW: set size 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);
+
+       ASSERT_VALIDNO0(lb)
+       DB (kprintf ("ListBoxClass: OM_DISPOSE\n");)
+
+       DeleteHSlider(g, lb);
+       DeleteVSlider(g, lb);
+
+       /* Dispose our subobjects which are not freed automatically */
+       DisposeObject (lb->Frame);
+       DisposeObject (lb->Model);
+
+       /* Our superclass will cleanup everything else now */
+       DoSuperMethodA (cl, (Object *)g, (Msg) msg);
+
+       /* From now on, our instance data is no longer available */
+}
+
+
+
+#define SCROLLBUTTON_CLASS_PTR ( ((struct LBClassData *)(cl->cl_UserData))->ScrollButtonClass )
+
+
+static void CreateVSlider (Class *cl, struct Gadget *g, struct LBData *lb, struct DrawInfo *dri)
+{
+       if (lb->VSlider = NewObject (NULL, PROPGCLASS,
+               GA_ID,                  g->GadgetID,    /* Same as our ID */
+               GA_DrawInfo,    dri,
+               PGA_Freedom,    FREEVERT,
+               PGA_NewLook,    TRUE,
+               PGA_Borderless, TRUE,
+               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);
+       }
+
+       /* We won't bother checking for failure because the image is
+        * not life-critical in our object
+        */
+       lb->UpImage = NewObject (NULL, SYSICLASS,
+               SYSIA_Which,    UPIMAGE,
+               SYSIA_DrawInfo, dri,
+               TAG_DONE);
+
+       if (lb->UpButton = NewObject (SCROLLBUTTON_CLASS_PTR, NULL,
+               GA_ID,                  g->GadgetID,
+               GA_DrawInfo,    dri,
+               GA_Image,               lb->UpImage,
+               ICA_TARGET,             lb->ListView,
+               ICA_MAP,                MapUpButtonToLV,
+               TAG_DONE))
+       {
+               /* From now on, the groupgclass will dispose this object for us */
+               DoMethod ((Object *)g, OM_ADDMEMBER, lb->UpButton);
+       }
+
+       /* We won't bother checking for failure because the image is
+        * not life-critical in our object
+        */
+       lb->DownImage = NewObject (NULL, SYSICLASS,
+               SYSIA_Which,    DOWNIMAGE,
+               SYSIA_DrawInfo, dri,
+               TAG_DONE);
+
+       if (lb->DownButton = NewObject (SCROLLBUTTON_CLASS_PTR, NULL,
+               GA_ID,                  g->GadgetID,
+               GA_DrawInfo,    dri,
+               GA_Image,               lb->DownImage,
+               ICA_TARGET,             lb->ListView,
+               ICA_MAP,                MapDownButtonToLV,
+               TAG_DONE))
+       {
+               /* From now on, the groupgclass will dispose this object for us */
+               DoMethod ((Object *)g, OM_ADDMEMBER, lb->DownButton);
+       }
+}
+
+
+
+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->DownButton)
+       {
+               ASSERT_VALID(lb->DownButton)
+
+               DoMethod ((Object *)g, OM_REMMEMBER, lb->DownButton);
+               DisposeObject (lb->DownButton);
+               lb->DownButton = NULL;
+       }
+
+       if (lb->DownImage)
+       {
+               ASSERT_VALID(lb->DownImage)
+
+               DisposeObject (lb->DownImage);
+               lb->DownImage = NULL;
+       }
+
+       if (lb->UpButton)
+       {
+               ASSERT_VALID(lb->UpButton)
+
+               DoMethod ((Object *)g, OM_REMMEMBER, lb->UpButton);
+               DisposeObject (lb->UpButton);
+               lb->UpButton = NULL;
+       }
+
+       if (lb->UpImage)
+       {
+               ASSERT_VALID(lb->UpImage)
+
+               DisposeObject (lb->UpImage);
+               lb->UpImage = NULL;
+       }
+
+       if (lb->LVToVSliderIC)
+       {
+               ASSERT_VALID(lb->LVToVSliderIC)
+               ASSERT_VALID(lb->Model)
+
+               DoMethod (lb->Model, OM_REMMEMBER, lb->LVToVSliderIC);
+               DisposeObject (lb->LVToVSliderIC);
+               lb->LVToVSliderIC = NULL;
+       }
+
+       if (lb->VSlider)
+       {
+               ASSERT_VALID(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 */
+}
+
+
+
+INLINE void GetGadgetBox (struct GadgetInfo *ginfo, struct ExtGadget *g, struct IBox *box, struct Rectangle *rect)
+
+/* Gets 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 both the provided IBox and
+ * Rectangle structures, computing the values from the coordinates of the
+ * gadget and the window where it lives.
+ */
+{
+       ASSERT_VALIDNO0(g)
+       ASSERT_VALIDNO0(ginfo)
+       ASSERT_VALIDNO0(box)
+       ASSERT_VALIDNO0(rect)
+
+       DB (if (g->Flags & GFLG_EXTENDED)
+               kprintf ("ListBoxClass: GetGadgetBox(): GFLG_EXTENDED is set\n");)
+
+       DB (if ((g->Flags & GFLG_EXTENDED) && (g->MoreFlags & GMORE_BOUNDS))
+               kprintf ("ListBoxClass: 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;
+
+       /* Convert IBox to Rectangle coordinates system */
+       rect->MinX = box->Left;
+       rect->MinY = box->Top;
+       rect->MaxX = box->Left + box->Width - 1;
+       rect->MaxY = box->Top + box->Height - 1;
+
+
+       DB (kprintf ("ListBoxClass: GetGadgetBox(): Left = %ld, Top = %ld, Width = %ld, Height = %ld\n",
+               box->Left, box->Top, box->Width, box->Height);)
+}
+
+
+
+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;
+
+                       classdata->ScrollButtonClass = MakeScrollButtonClass();
+                       classdata->VectorGlyphBase = OpenLibrary ("images/vectorglyph.image", 0);
+                       return class;
+               }
+
+               FreeListBoxClass (class);
+       }
+
+       return NULL;
+}
+
+
+
+void FreeListBoxClass (Class *class)
+{
+       struct LBClassData *classdata;
+
+       if (class)
+       {
+               ASSERT_VALID(class)
+               if (classdata = (struct LBClassData *)class->cl_UserData)
+               {
+                       ASSERT_VALID(classdata)
+                       ASSERT_VALID(classdata->ScrollButtonClass)
+                       ASSERT_VALID(classdata->VectorGlyphBase)
+
+                       /* Cleanup global class data */
+                       CloseLibrary (classdata->VectorGlyphBase); /* NULL is safe since V36 */
+                       FreeScrollButtonClass (classdata->ScrollButtonClass);
+                       FreeMem (classdata, sizeof (struct LBClassData));
+               }
+
+               FreeClass (class);
+       }
+}
diff --git a/ListBoxClass.h b/ListBoxClass.h
new file mode 100644 (file)
index 0000000..0d65bb8
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef LISTBOXCLASS_H
+#define LISTBOXCLASS_H
+/*
+**     ListBoxClass.h
+**
+**     Copyright (C) 1997 by Bernardo Innocenti
+**
+**     ListBox class built on top of the "groupgclass".
+**
+*/
+
+#define LISTBOXCLASS   "listboxclass"
+#define LISTBOXVERS            1
+
+
+Class  *MakeListBoxClass (void);
+void    FreeListBoxClass (Class *ListViewClass);
+
+
+
+/*****************/
+/* Class Methods */
+/*****************/
+
+/* This class does not define any new methods */
+
+/********************/
+/* Class Attributes */
+/********************/
+
+/* #define LBA_Dummy (TAG_USER | ('L'<<16) | ('B'<<8)) */
+
+/* This class does not define any new attributes */
+
+
+#endif /* !LISTBOXCLASS_H */
diff --git a/ListMacros.h b/ListMacros.h
new file mode 100644 (file)
index 0000000..4b7d808
--- /dev/null
@@ -0,0 +1,98 @@
+#ifndef LISTMACROS_H
+#define LISTMACROS_H
+/*
+**     $VER: ListMacros.h 2.1 (1.9.97)
+**
+**     Copyright (C) 1996,97 Bernardo Innocenti. All rights reserved.
+**
+**     Use 4 chars wide TABs to read this source
+**
+**     Some handy macros for list operations.  Using these macros is faster
+**     than calling their exec.library equivalents, but they will eventually
+**     make your code a little bigger and are also subject to common macro
+**     side effects.
+*/
+
+
+#define NEWLIST(l)     ( (l)->lh_TailPred = (struct Node *)(l),                        \
+                                       (l)->lh_Tail = 0,                                                                       \
+                                       (l)->lh_Head = (struct Node *)(&((l)->lh_Tail)) )
+
+#define ADDHEAD(l,n) ( (n)->ln_Pred = (struct Node *)(l),                              \
+                                       (n)->ln_Succ = (l)->lh_Head,                                            \
+                                       (l)->lh_Head->ln_Pred = (n),                                            \
+                                       (l)->lh_Head = (n) )
+
+#define ADDTAIL(l,n) ( (n)->ln_Succ = (struct Node *)(&((l)->lh_Tail)),        \
+                                       (n)->ln_Pred = (l)->lh_TailPred,                                        \
+                                       (l)->lh_TailPred->ln_Succ = (n),                                        \
+                                       (l)->lh_TailPred = (n) )
+
+#define REMOVE(n)      ( (n)->ln_Succ->ln_Pred = (n)->ln_Pred,                         \
+                                       (n)->ln_Pred->ln_Succ = (n)->ln_Succ )
+
+#define GETHEAD(l)     ( (l)->lh_Head->ln_Succ ? (l)->lh_Head : (struct Node *)NULL )
+
+#define GETTAIL(l)  ( (l)->lh_TailPred->ln_Succ ? (l)->lh_TailPred : (struct Node *)NULL )
+
+#define GETSUCC(n)  ( (n)->ln_Succ->ln_Succ ? (n)->ln_Succ : (struct Node *)NULL )
+
+#define GETPRED(n)  ( (n)->ln_Pred->ln_Pred ? (n)->ln_Pred : (struct Node *)NULL )
+
+
+#ifdef __GNUC__
+
+#define REMHEAD(l)                                                                                                                     \
+({                                                                                                                                                     \
+       struct Node *n = (l)->lh_Head;                                                                                  \
+       n->ln_Succ ?                                                                                                                    \
+               (l)->lh_Head = n->ln_Succ,                                                                                      \
+                       (l)->lh_Head->ln_Pred = (struct Node *)(l),                                             \
+                       n :                                                                                                                             \
+               NULL;                                                                                                                           \
+})
+
+#define REMTAIL(l)                                                                                                                     \
+({                                                                                                                                                     \
+       struct Node *n = (l)->lh_TailPred;                                                                              \
+       n->ln_Pred ?                                                                                                                    \
+               (l)->lh_TailPred = n->ln_Pred,                                                                          \
+                       (l)->lh_TailPred->ln_Succ = (struct Node *)(&((l)->lh_Tail)),   \
+                       n :                                                                                                                             \
+               NULL;                                                                                                                           \
+})
+
+
+#else
+
+/* These two can't be implemented as macros without the GCC ({...}) language extension */
+
+INLINE struct Node *REMHEAD(struct List *l)
+{
+       struct Node *n = l->lh_Head;
+
+       if (n->ln_Succ)
+       {
+               l->lh_Head = n->ln_Succ;
+               l->lh_Head->ln_Pred = (struct Node *)l;
+               return n;
+       }
+       return NULL;
+}
+
+INLINE struct Node *REMTAIL(struct List *l)
+{
+       struct Node *n = l->lh_TailPred;
+
+       if (n->ln_Pred)
+       {
+               l->lh_TailPred = n->ln_Pred;
+               l->lh_TailPred->ln_Succ = (struct Node *)(&(l->lh_Tail));
+               return n;
+       }
+       return NULL;
+}
+
+#endif
+
+#endif /* !LISTMACROS_H */
diff --git a/ListViewClass.c b/ListViewClass.c
new file mode 100644 (file)
index 0000000..d4ceb17
--- /dev/null
@@ -0,0 +1,2181 @@
+/*     ListViewClass.c
+**
+**     Copyright (C) 1996,97 Bernardo Innocenti
+**
+**     Use 4 chars wide TABs to read this file
+**
+**     GadTools-like `boopsi' ListView gadget class
+*/
+
+#define USE_BUILTIN_MATH
+#define INTUI_V36_NAMES_ONLY
+#define __USE_SYSBASE
+#define  CLIB_ALIB_PROTOS_H            /* Avoid dupe defs 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 <graphics/gfxbase.h>
+#include <graphics/gfxmacros.h>
+
+#include <proto/intuition.h>
+#include <proto/graphics.h>
+#include <proto/layers.h>
+#include <proto/utility.h>
+
+#ifdef __STORM__
+       #pragma header
+#endif
+
+#include "CompilerSpecific.h"
+#include "Debug.h"
+#include "BoopsiStubs.h"
+
+#define LV_GADTOOLS_STUFF
+#include "ListViewClass.h"
+
+
+
+/* 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;
+};
+
+
+
+/* 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, ULONG first, ULONG last, APTR item);
+INLINE LONG            ItemHit                 (struct LVData *lv, WORD x, WORD y);
+INLINE void            GetGadgetBox    (struct GadgetInfo *ginfo, struct ExtGadget *g, struct IBox *box, struct Rectangle *rect);
+INLINE APTR    GetItem                 (struct LVData *lv, ULONG num);
+INLINE APTR    GetNext                 (struct LVData *lv, APTR item, ULONG num);
+INLINE APTR    GetPrev                 (struct LVData *lv, APTR item, ULONG num);
+INLINE ULONG   CountNodes              (struct List *list);
+static ULONG   CountSelections (struct LVData *lv);
+INLINE ULONG   IsItemSelected  (struct LVData *lv, APTR item, ULONG 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;
+
+
+
+static ULONG HOOKCALL LVDispatcher (
+       REG(a0, Class *cl),
+       REG(a2, struct Gadget *g),
+       REG(a1, Msg msg))
+
+/* ListView class dispatcher - Handles all supported methods */
+{
+       ASSERT_VALIDNO0(cl)
+       ASSERT_VALIDNO0(g)
+       ASSERT_VALIDNO0(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);
+       }
+}
+
+
+
+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_VALIDNO0(lv)
+       ASSERT_VALIDNO0(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;
+}
+
+
+
+static void RedrawItems (struct LVData *lv, struct gpRender *msg, ULONG first, ULONG 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_VALIDNO0(lv)
+       ASSERT_VALIDNO0(msg)
+       ASSERT(first <= last)
+       ASSERT(last < lv->Total)
+
+       DB (kprintf ("  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_VALIDNO0(lv)
+       ASSERT_VALIDNO0(rp)
+
+       DB (kprintf ("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 ***")) );)
+
+
+#ifndef OS30_ONLY
+       /* Pre-V39 Intuition won't call our GM_LAYOUT method, so we must
+        * always call it before redrawing the gadget.
+        */
+       if ((IntuitionBase->LibNode.lib_Version < 39) &&
+               (msg->gpr_Redraw == GREDRAW_REDRAW))
+               LV_GMLayout (cl, g, (struct gpLayout *)msg);
+#endif /* !OS30_ONLY */
+
+       if (lv->Flags & LVF_DONTDRAW)
+               return;
+
+       if (lv->Items && lv->Visible)
+       {
+               struct TextFont *oldfont = NULL;
+               struct Region *oldregion = NULL;
+
+               if (rp->Font != lv->Font)
+               {
+                       oldfont = rp->Font;
+                       SetFont (rp, lv->Font);
+               }
+
+               if (lv->ClipRegion)
+               {
+                       ASSERT_VALIDNO0(lv->ClipRegion)
+                       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
+                               {
+#ifndef OS30_ONLY
+                                       if (GfxBase->LibNode.lib_Version >= 39)
+#endif /* OS30_ONLY */
+                                               /* 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);
+                                       }
+
+
+                                       /* Some layers magic adapded from "MUI.undoc",
+                                        * by Alessandro Zummo <azummo@ita.flashnet.it>
+                                        */
+                                       #define LayerCovered(l) \
+                                               ((!(l)->ClipRect) || memcmp (&(l)->ClipRect->bounds, \
+                                               &(l)->bounds, sizeof (struct Rectangle)))
+                                       #define LayerDamaged(l) \
+                                               ((l)->DamageList && (l)->DamageList->RegionRectangle)
+                                       #define NeedZeroScrollRaster(l) (LayerCovered(l) || LayerDamaged(l))
+
+
+                                       /* This will scroll the layer damage regions without actually
+                                        * scrolling the display, but only if our layer really needs it.
+                                        */
+                                       if ((rp->Layer->Flags & LAYERSIMPLE) && NeedZeroScrollRaster (rp->Layer))
+                                       {
+                                               UBYTE oldmask = rp->Mask; /* Would GetRPAttr() be better? */
+
+                                               DB (kprintf ("  Calling ScrollRaster()\n");)
+#ifdef OS30_ONLY
+                                               SetWriteMask (rp, 0);
+#else
+                                               SafeSetWriteMask (rp, 0);
+#endif /* OS30_ONLY */
+                                               ScrollRaster (rp, 0, scroll_dy,
+                                                       lv->GRect.MinX, lv->GRect.MinY,
+                                                       lv->GRect.MaxX,
+                                                       lv->GRect.MaxY);
+
+#ifdef OS30_ONLY
+                                               SetWriteMask (rp, oldmask);
+#else
+                                               SafeSetWriteMask (rp, oldmask);
+#endif /* OS30_ONLY */
+                                       }
+
+#ifndef OS30_ONLY
+                                       if (GfxBase->LibNode.lib_Version >= 39)
+#endif /* OS30_ONLY */
+                                               /* 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)
+               {
+                       ASSERT_VALIDNO0(oldregion)
+                       /* Restore old clipping region in our layer */
+                       InstallClipRegion (rp->Layer, oldregion);
+               }
+
+               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);
+       }
+}
+
+
+
+static ULONG LV_GMGoActive (Class *cl, struct Gadget *g, struct gpInput *msg)
+{
+       struct LVData           *lv = INST_DATA (cl, g);
+
+       ASSERT_VALIDNO0(lv)
+       DB (kprintf ("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, 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_VALIDNO0(lv)
+/*     DB (kprintf ("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! */
+                                       DB (kprintf ("scrolling on\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! */
+                                       DB (kprintf ("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)
+                                       {
+                                               DB (kprintf ("  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, struct gpGoInactive *msg)
+{
+       struct LVData           *lv = INST_DATA (cl, g);
+       ASSERT_VALIDNO0(lv)
+
+       DB (kprintf ("GM_GOINACTIVE\n");)
+
+       /* Stop dragging and scrolling modes */
+       lv->Flags &= ~(LVF_DRAGGING | LVF_SCROLLING);
+
+       /* Mark gadget inactive */
+       g->Flags &= ~GFLG_SELECTED;
+}
+
+
+
+INLINE void GetGadgetBox (struct GadgetInfo *ginfo, struct ExtGadget *g, struct IBox *box, struct Rectangle *rect)
+
+/* Gets 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 both the provided IBox and
+ * Rectangle structures, computing the values from the coordinates of the
+ * gadget and the window where it lives.
+ */
+{
+       ASSERT_VALIDNO0(g)
+       ASSERT_VALIDNO0(ginfo)
+       ASSERT_VALIDNO0(box)
+       ASSERT_VALIDNO0(rect)
+
+       DB (if ((g->Flags & GFLG_EXTENDED) && (g->MoreFlags & GMORE_BOUNDS))
+               kprintf ("  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;
+
+       /* Convert IBox to Rectangle coordinates system */
+       rect->MinX = box->Left;
+       rect->MinY = box->Top;
+       rect->MaxX = box->Left + box->Width - 1;
+       rect->MaxY = box->Top + box->Height - 1;
+}
+
+
+
+static void LV_GMLayout (Class *cl, struct Gadget *g, struct gpLayout *msg)
+{
+       struct LVData *lv = INST_DATA (cl, g);
+       LONG visible;
+
+       DB (kprintf ("GM_LAYOUT\n");)
+       ASSERT_VALIDNO0(lv)
+       ASSERT_VALIDNO0(msg->gpl_GInfo)
+       ASSERT_VALIDNO0(msg->gpl_GInfo->gi_DrInfo)
+       ASSERT_VALIDNO0(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, &lv->GRect);
+
+       /* 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 above */
+
+       ASSERT_VALIDNO0(lv)
+       ASSERT_VALID(tstate)
+
+       DB (kprintf ((msg->MethodID == OM_SET) ? "OM_SET:\n" : "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:
+                               DB (kprintf ("  GA_ID, %ld\n", ti->ti_Data);)
+
+                               /* Avoid sending all taglists to our superclass because of GA_ID */
+                               g->GadgetID = ti->ti_Data;
+                               break;
+
+                       case LVA_Selected:
+                               DB (kprintf ("  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:
+                               DB (kprintf ("  LVA_Top, %ld\n", ti->ti_Data);)
+
+                               if ((lv->Top != ti->ti_Data) && lv->Items)
+                               {
+                                       /* This will scroll the listview contents when needed */
+
+                                       lv->Top = ((ti->ti_Data + lv->Visible) >= lv->Total) ?
+                                               ((lv->Total <= lv->Visible) ? 0 : (lv->Total - lv->Visible))
+                                               : 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:
+                               DB (kprintf ("  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:
+                               DB (kprintf ("  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 = (ti->ti_Data >= lv->Total) ?
+                                               (lv->Total - 1) : 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:
+                               DB (kprintf ("  LVA_DeselectItem, %ld\n", ti->ti_Data);)
+
+                               if (lv->Items)
+                               {
+                                       LONG newselected = (ti->ti_Data >= lv->Total) ?
+                                               (lv->Total - 1) : 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:
+                               DB (kprintf ("  LVA_ToggleItem, %ld\n", ti->ti_Data);)
+
+                               if (lv->Items)
+                               {
+                                       LONG newselected = newselected = (ti->ti_Data >= lv->Total) ?
+                                               (lv->Total - 1) : 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:
+                               DB (kprintf ("  LVA_ClearSelected, %ld\n", ti->ti_Data);)
+
+                               if (lv->Items)
+                               {
+                                       LONG newselected = 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 = node->ln_Succ;
+                                                       node->ln_Type = 0)
+                                                       ASSERT_VALID(node);
+                                       }
+
+                                       /* TODO: check if total redraw is really needed */
+                                       action |= LVF_REDRAW | LVF_NOTIFY;
+                               }
+                               break;
+
+                       case LVA_MakeVisible:
+                       {
+                               LONG itemnum = ti->ti_Data;
+
+                               DB (kprintf ("  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:
+                               DB (kprintf ("  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:
+                               DB (kprintf ("  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:
+                               DB (kprintf ("  Unimplemented attr: LVA_MoveLeft\n");)
+                               break;
+
+                       case LVA_MoveRight:
+                               DB (kprintf ("  Unimplemented attr: LVA_MoveRight\n");)
+                               break;
+
+                       case LVA_StringList:
+                               DB (kprintf ("  LVA_StringList, $%lx\n", ti->ti_Data);)
+
+                               if (ti->ti_Data == ~0)
+                                       lv->Items = NULL;
+                               else
+                               {
+                                       ASSERT_VALID(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:
+                               DB (kprintf ("  LVA_StringArray, $%lx\n", ti->ti_Data);)
+
+                               if (ti->ti_Data == ~0)
+                                       lv->Items = NULL;
+                               else
+                               {
+                                       ASSERT_VALID(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:
+                               DB (kprintf ("  LVA_ImageList, $%lx\n", ti->ti_Data);)
+
+                               if (ti->ti_Data == ~0)
+                                       lv->Items = NULL;
+                               else
+                               {
+                                       ASSERT_VALID(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:
+                               DB (kprintf ("  LVA_ImageArray, $%lx\n", ti->ti_Data);)
+
+                               if (ti->ti_Data == ~0)
+                                       lv->Items = NULL;
+                               else
+                               {
+                                       ASSERT_VALID(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++;
+                                               lv->Total = i;
+                                       }
+
+                                       action |= LVF_REDRAW | LVF_NOTIFYALL;
+                               }
+                               break;
+
+                       case LVA_CustomList:
+                               DB (kprintf ("  LVA_CustomList, $%lx\n", ti->ti_Data);)
+
+                               if (ti->ti_Data == ~0)
+                                       lv->Items = NULL;
+                               else
+                               {
+                                       ASSERT_VALID(ti->ti_Data)
+
+                                       lv->Items = (void *) ti->ti_Data;
+                                       lv->SelectCount = CountSelections (lv);
+
+                                       action |= LVF_REDRAW | LVF_NOTIFYALL;
+                               }
+                               break;
+
+                       case LVA_Visible:
+                               DB (kprintf ("  LVA_Visible, %ld\n", ti->ti_Data);)
+
+                               /* This attribute can only be set internally, and will
+                                * trigger a full slider notification.
+                                */
+                               lv->Visible = ti->ti_Data;
+                               action |= LVF_NOTIFYALL;
+
+
+                               /* Also scroll the ListView if needed. */
+                               if (lv->ClipRegion)
+                               {
+                                       LONG height = lv->Total * (lv->ItemHeight + lv->Spacing);
+                                       LONG newtop;
+
+                                       if (lv->PixelTop + lv->GBox.Height >= height)
+                                       {
+                                               lv->PixelTop = height - lv->GBox.Height;
+                                               if (lv->PixelTop < 0)
+                                                       lv->PixelTop = 0;
+
+                                               newtop = lv->PixelTop / (lv->ItemHeight + lv->Spacing);
+                                               if (newtop != lv->Top)
+                                               {
+                                                       lv->Top = newtop;
+                                                       lv->TopPtr = GetItem (lv, newtop);
+                                               }
+                                               action |= LVF_SCROLL;
+                                       }
+                               }
+                               else if (lv->Top + lv->Visible >= lv->Total)
+                               {
+                                       lv->Top = (lv->Total <= lv->Visible) ? 0 : (lv->Total - lv->Visible);
+                                       lv->TopPtr = GetItem (lv, lv->Top);
+                                       lv->PixelTop = lv->Top * (lv->ItemHeight + lv->Spacing);
+                                       action |= LVF_SCROLL;
+                               }
+                               break;
+
+                       case LVA_SelectArray:
+                               DB (kprintf ("  LVA_SelectArray, $%lx\n", ti->ti_Data);)
+                               ASSERT_VALID(ti->ti_Data)
+
+                               lv->SelectArray = (ULONG *) ti->ti_Data;
+                               lv->SelectCount = CountSelections (lv);
+                               action |= LVF_REDRAW;
+                               break;
+
+                       case LVA_MaxSelect:
+                               DB (kprintf ("  LVA_MaxSelect, %ld\n", ti->ti_Data);)
+
+                               lv->MaxSelect = ti->ti_Data;
+                               /* NOTE: We are not checking lv->SelectCount */
+                               break;
+
+                       case LVA_PixelTop:      /* Handle pixel-wise scrolling */
+                               DB (kprintf ("  LVA_PixelTop, %ld\n", ti->ti_Data);)
+
+                               if (ti->ti_Data != lv->PixelTop && lv->Items && lv->ItemHeight)
+                               {
+                                       LONG newtop;
+
+                                       lv->PixelTop = ti->ti_Data;
+                                       action |= LVF_SCROLL;
+
+                                       newtop = lv->PixelTop / (lv->ItemHeight + lv->Spacing);
+                                       newtop = ((newtop + lv->Visible) >= lv->Total) ?
+                                               ((lv->Total <= lv->Visible) ? 0 : (lv->Total - lv->Visible))
+                                               : newtop;
+
+                                       if (newtop != lv->Top)
+                                       {
+                                               /* TODO: optimize GetItem for some special cases:
+                                                * Top = oldtop + 1 and Top = oldtop - 1
+                                                */
+                                               lv->Top = newtop;
+                                               lv->TopPtr = GetItem (lv, newtop);
+                                               action |= LVF_NOTIFY | LVF_SCROLL;
+                                       }
+                               }
+                               break;
+
+                       case LVA_ScrollRatio:
+                               DB (kprintf ("  LVA_ScrollRatio, %ld\n", ti->ti_Data);)
+                               ASSERT(ti->ti_Data != 0)
+
+                               lv->ScrollRatio = ti->ti_Data;
+                               lv->MaxScroll = lv->GBox.Height / lv->ScrollRatio;
+                               break;
+
+                       default:
+                               DB (kprintf ("  Passing unknown tag to superclass: $%lx, %ld\n",
+                                       ti->ti_Tag, ti->ti_Data);)
+
+                               /* This little optimization avoids forwarding the
+                                * OM_SET method to our superclass then there are
+                                * no unknown tags.
+                                */
+                               action |= LVF_DO_SUPER_METHOD;
+                               break;
+               }
+
+       DB(kprintf ("  TAG_DONE\n");)
+
+       /* Forward method to our superclass dispatcher, only if needed */
+
+       if (action & LVF_DO_SUPER_METHOD)
+               result = (DoSuperMethodA (cl, (Object *)g, (Msg) msg));
+       else
+               result = TRUE;
+
+
+       /* Update gadget imagery, only when needed */
+
+       if ((action & (LVF_REDRAW | LVF_SCROLL | LVF_TOGGLESELECT))
+               && msg->opu_GInfo && !(lv->Flags & LVF_DONTDRAW))
+       {
+               struct RastPort *rp;
+
+               if (rp = ObtainGIRPort (msg->opu_GInfo))
+               {
+                       /* Just redraw everything */
+                       if (action & LVF_REDRAW)
+                               DoMethod ((Object *)g, GM_RENDER, msg->opu_GInfo, rp, GREDRAW_REDRAW);
+                       else
+                       {
+                               /* Both these may happen at the same time */
+
+                               if (action & LVF_SCROLL)
+                                       DoMethod ((Object *)g, GM_RENDER, msg->opu_GInfo, rp,
+                                               GREDRAW_UPDATE);
+
+                               if (action & LVF_TOGGLESELECT)
+                                       DoMethod ((Object *)g, GM_RENDER, msg->opu_GInfo, rp,
+                                               GREDRAW_TOGGLE);
+                       }
+
+                       ReleaseGIRPort (rp);
+               }
+               DB(else kprintf ("*** ObtainGIRPort() failed!\n");)
+       }
+
+
+       /* Notify our targets about changed attributes */
+
+       if (action & LVF_NOTIFYALL)
+       {
+               DB(kprintf("OM_NOTIFY: ALL\n");)
+               DB(kprintf("  LVA_Top,           %ld\n", lv->Top);)
+               DB(kprintf("  LVA_Total,         %ld\n", lv->Total);)
+               DB(kprintf("  LVA_Visible,       %ld\n", lv->Visible);)
+               DB(kprintf("  LVA_Selected,      %ld\n", lv->Selected);)
+               DB(kprintf("  LVA_PixelTop,      %ld\n", lv->PixelTop);)
+               DB(kprintf("  LVA_PixelHeight,   %ld\n", lv->Total * (lv->ItemHeight + lv->Spacing));)
+               DB(kprintf("  LVA_PixelVVisible, %ld\n", lv->GBox.Height);)
+               DB(kprintf("  TAG_DONE\n");)
+
+               NotifyAttrs ((Object *)g, msg->opu_GInfo,
+                       (msg->MethodID == OM_UPDATE) ? msg->opu_Flags : 0,
+                       LVA_Top,                        lv->Top,
+                       LVA_Total,                      lv->Total,
+                       LVA_Visible,            lv->Visible,
+                       LVA_Selected,           lv->Selected,
+                       LVA_PixelTop,           lv->PixelTop,
+                       LVA_PixelHeight,        lv->Total * (lv->ItemHeight + lv->Spacing),
+                       LVA_PixelVVisible,      lv->ClipRegion ?
+                                                                       lv->GBox.Height :
+                                                                       lv->Visible * (lv->ItemHeight + lv->Spacing),
+                       GA_ID,                          g->GadgetID,
+                       TAG_DONE);
+       }
+       else if (action & LVF_NOTIFY)
+       {
+               IPTR tags[9];
+               int cnt = 0;
+
+               if (action & LVF_SCROLL)
+               {
+                       tags[0] = LVA_Top;                      tags[1] = lv->Top;
+                       tags[2] = LVA_PixelTop;         tags[3] = lv->Top * (lv->ItemHeight + lv->Spacing);
+                       cnt = 4;
+               }
+
+               if (action & LVF_TOGGLESELECT)
+               {
+                       tags[cnt++] = LVA_Selected;     tags[cnt++] = lv->Selected;
+               }
+
+               tags[cnt++]     = GA_ID;
+               tags[cnt++]     = g->GadgetID;
+               tags[cnt]       = TAG_DONE;
+
+               DoMethod ((Object *)g, OM_NOTIFY, tags, msg->opu_GInfo,
+                       (msg->MethodID == OM_UPDATE) ? msg->opu_Flags : 0);
+       }
+
+       return result;
+}
+
+
+
+static ULONG LV_OMGet (Class *cl, struct Gadget *g, struct opGet *msg)
+{
+       struct LVData *lv = INST_DATA (cl, g);
+
+       ASSERT_VALIDNO0(lv)
+       ASSERT_VALIDNO0(msg->opg_Storage)
+
+       DB (kprintf ("OM_GET\n");)
+
+
+       switch (msg->opg_AttrID)
+       {
+               case LVA_Selected:
+                       *msg->opg_Storage = (ULONG) lv->Selected;
+                       return TRUE;
+
+               case LVA_Top:
+                       *msg->opg_Storage = (ULONG) lv->Top;
+                       return TRUE;
+
+               case LVA_Total:
+                       *msg->opg_Storage = (ULONG) lv->Total;
+                       return TRUE;
+
+               case LVA_StringList:
+               case LVA_StringArray:
+               case LVA_ImageList:
+               case LVA_ImageArray:
+               case LVA_CustomList:
+                       *msg->opg_Storage = (ULONG) lv->Items;
+                       return TRUE;
+
+               case LVA_Visible:
+                       *msg->opg_Storage = (ULONG) lv->Visible;
+                       return TRUE;
+
+               case LVA_SelectedPtr:
+                       *msg->opg_Storage = (ULONG) lv->SelectedPtr;
+                       return TRUE;
+
+               case LVA_SelectArray:
+                       *msg->opg_Storage = (ULONG) lv->SelectArray;
+                       return TRUE;
+
+               default:
+                       return DoSuperMethodA (cl, (Object *)g, (Msg) msg);
+       }
+}
+
+
+
+static ULONG LV_OMNew (Class *cl, struct Gadget *g, struct opSet *msg)
+{
+       struct LVData   *lv;
+       struct TagItem  *tag;
+       struct DrawInfo *drawinfo;
+
+
+       DB (kprintf ("OM_NEW\n");)
+
+       if (g = (struct Gadget *)DoSuperMethodA (cl, (Object *)g, (Msg)msg))
+       {
+               /* Set the GMORE_SCROLLRASTER flag */
+               if (g->Flags & GFLG_EXTENDED)
+               {
+                       DB (kprintf ("  Setting GMORE_SCROLLRASTER\n");)
+                       ((struct ExtGadget *)g)->MoreFlags |= GMORE_SCROLLRASTER;
+               }
+
+               lv = (struct LVData *) INST_DATA (cl, (Object *)g);
+               ASSERT_VALIDNO0(lv)
+
+               /* Handle creation-time attributes */
+
+               /* Map boolean attributes */
+               {
+                       static IPTR boolMap[] =
+                       {
+                               GA_ReadOnly,            LVF_READONLY,
+                               LVA_Clipped,            LVF_CLIPPED,
+                               LVA_ShowSelected,       LVF_SHOWSELECTED,
+                               LVA_DoMultiSelect,      LVF_DOMULTISELECT,
+                               TAG_DONE
+                       };
+
+                       lv->Flags = PackBoolTags (
+                               LVF_SHOWSELECTED,
+                               msg->ops_AttrList,
+                               (struct TagItem *)boolMap);
+               }
+
+
+               /* Select font to use when drawing the Listview labels */
+
+               /* First, try to get the font from our DrawInfo... */
+
+               if (drawinfo = (struct DrawInfo *)
+                       GetTagData (GA_DrawInfo, NULL, msg->ops_AttrList))
+               {
+                       ASSERT_VALID(drawinfo)
+                       lv->Font = drawinfo->dri_Font;
+               }
+               else
+                       lv->Font = NULL;
+
+
+               /* ...then override it with LVA_TextFont */
+
+               if (tag = FindTagItem (LVA_TextFont, msg->ops_AttrList))
+               {
+                       if (tag->ti_Data)
+                       {
+                               lv->Font = (struct TextFont *)tag->ti_Data;
+                               ASSERT_VALID(lv->Font)
+                       }
+               }
+               else    /* Otherwise, try GA_TextAttr */
+               {
+                       struct TextAttr *attr;
+                       struct TextFont *font;
+
+                       if (attr = (struct TextAttr *)GetTagData (GA_TextAttr,
+                               NULL, msg->ops_AttrList))
+                       {
+                               if (font = OpenFont (attr))
+                               {
+                                       /* Must remember to close this font later */
+                                       lv->Flags |= LVF_CLOSEFONT;
+                                       lv->Font = font;
+                               }
+                       }
+               }
+
+               /* Calculate ItemHeight */
+
+               if (lv->Font)
+                       /* Get height from font Y size */
+                       lv->ItemHeight = lv->Font->tf_YSize;
+               else
+                       lv->ItemHeight = 0;
+
+               lv->ItemHeight = GetTagData (LVA_ItemHeight, lv->ItemHeight, msg->ops_AttrList);
+               lv->Spacing = GetTagData (LAYOUTA_Spacing, 0, msg->ops_AttrList);
+
+               if (tag = FindTagItem (LVA_MaxPen, msg->ops_AttrList))
+                       lv->MaxPen = tag->ti_Data;
+               else
+               {
+                       if (drawinfo)
+                               lv->MaxPen = max (
+                                       max (drawinfo->dri_Pens[BACKGROUNDPEN],
+                                               drawinfo->dri_Pens[TEXTPEN]),
+                                       max (drawinfo->dri_Pens[FILLPEN],
+                                               drawinfo->dri_Pens[FILLTEXTPEN]));
+                       else
+                               lv->MaxPen = (ULONG)-1;
+               }
+
+
+               lv->Total = GetTagData (LVA_Total, ~0, msg->ops_AttrList);
+
+               if (lv->Items = (APTR) GetTagData (LVA_StringList, NULL, msg->ops_AttrList))
+               {
+                       ASSERT_VALID(lv->Items)
+                       lv->GetItemFunc = ListGetItem;
+                       lv->GetNextFunc = ListGetNext;
+                       lv->GetPrevFunc = ListGetPrev;
+                       lv->DrawItemFunc = ListStringDrawItem;
+                       lv->Flags |= LVF_LIST;
+
+                       if (lv->Total == ~0)
+                               lv->Total = CountNodes (lv->Items);
+               }
+               else if (lv->Items = (APTR) GetTagData (LVA_StringArray, NULL, msg->ops_AttrList))
+               {
+                       ASSERT_VALID(lv->Items)
+                       lv->GetItemFunc = ArrayGetItem;
+                       lv->GetNextFunc = ArrayGetItem;
+                       lv->GetPrevFunc = ArrayGetItem;
+                       lv->DrawItemFunc = StringDrawItem;
+
+                       if (lv->Total == ~0)
+                       {
+                               /* Count items */
+                               ULONG i = 0;
+                               while (((APTR *)lv->Items)[i]) i++;
+                               lv->Total = i;
+                       }
+               }
+               else if (lv->Items = (APTR) GetTagData (LVA_ImageList, NULL, msg->ops_AttrList))
+               {
+                       ASSERT_VALID(lv->Items)
+                       lv->GetItemFunc = ListGetItem;
+                       lv->GetNextFunc = ListGetNext;
+                       lv->GetPrevFunc = ListGetPrev;
+                       lv->DrawItemFunc = ListImageDrawItem;
+                       lv->Flags |= LVF_LIST;
+
+                       if (lv->Total == ~0)
+                               lv->Total = CountNodes (lv->Items);
+               }
+               else if (lv->Items = (APTR) GetTagData (LVA_ImageArray, NULL, msg->ops_AttrList))
+               {
+                       ASSERT_VALID(lv->Items)
+                       lv->GetItemFunc = ArrayGetItem;
+                       lv->GetNextFunc = ArrayGetItem;
+                       lv->GetPrevFunc = ArrayGetItem;
+                       lv->DrawItemFunc = ImageDrawItem;
+
+                       if (lv->Total == ~0)
+                       {
+                               /* Count items */
+                               ULONG i = 0;
+                               while (((APTR *)lv->Items)[i]) i++;
+                               lv->Total = i;
+                       }
+               }
+
+               lv->SelectArray = (ULONG *)GetTagData (LVA_SelectArray, NULL, msg->ops_AttrList);
+               lv->MaxSelect = GetTagData (LVA_MaxSelect, -1, msg->ops_AttrList);
+               lv->SelectCount = CountSelections(lv);
+
+               if (lv->Visible = GetTagData (LVA_Visible, 0, msg->ops_AttrList))
+               {
+                       SetAttrs (g,
+                               GA_Height, lv->Visible * (lv->ItemHeight + lv->Spacing),
+                               TAG_DONE);
+               }
+
+               /* Initialize Top and all related values */
+
+               lv->OldTop = lv->Top = GetTagData (LVA_MakeVisible,
+                       GetTagData (LVA_Top, 0, msg->ops_AttrList), msg->ops_AttrList);
+               lv->OldPixelTop = lv->PixelTop = lv->Top * (lv->ItemHeight + lv->Spacing);
+
+               if (lv->Items)
+                       lv->TopPtr = GetItem (lv, lv->Top);
+
+               lv->ScrollRatio = GetTagData (LVA_ScrollRatio, 2, msg->ops_AttrList);
+               ASSERT(lv->ScrollRatio != 0)
+
+               if ((lv->OldSelected =
+                       lv->Selected = GetTagData (LVA_Selected, ~0, msg->ops_AttrList)) != ~0)
+                       lv->SelectedPtr = GetItem (lv, lv->Selected);
+
+               if (lv->CallBack = (struct Hook *)GetTagData (LVA_CallBack, NULL,
+                       msg->ops_AttrList))
+               {
+                       ASSERT_VALID(lv->CallBack->h_Entry)
+                       lv->DrawItemFunc = (LVDrawHook *) lv->CallBack->h_Entry;
+               }
+
+               if (lv->Flags & LVF_CLIPPED)
+                       lv->ClipRegion = NewRegion ();
+       }
+       return (ULONG)g;
+}
+
+
+
+static void LV_OMDispose (Class *cl, struct Gadget *g, Msg msg)
+{
+       struct LVData   *lv;
+
+       lv = (struct LVData *) INST_DATA (cl, (Object *)g);
+
+       ASSERT_VALIDNO0(lv)
+       DB (kprintf ("OM_DISPOSE\n");)
+
+       if (lv->ClipRegion)
+               DisposeRegion (lv->ClipRegion);
+
+       if (lv->Flags & LVF_CLOSEFONT)
+               CloseFont (lv->Font);
+
+       /* Our superclass will cleanup everything else now */
+       DoSuperMethodA (cl, (Object *)g, (Msg) msg);
+
+       /* From now on, our instance data is no longer available */
+}
+
+
+
+/* Misc support functions */
+
+INLINE ULONG CountNodes (struct List *list)
+
+/* Return the number of nodes in a list */
+{
+       struct Node *node;
+       ULONG count = 0;
+
+       if (list)
+       {
+               ASSERT_VALID(list)
+
+               for (node = list->lh_Head; node = node->ln_Succ; count++)
+                       ASSERT_VALID(node);
+       }
+
+       return count;
+}
+
+
+
+static ULONG CountSelections (struct LVData *lv)
+
+/* Count the number of selections in a multiselect listview */
+{
+       ULONG count = 0;
+
+       ASSERT_VALIDNO0(lv)
+
+
+       if (lv->Flags & LVF_DOMULTISELECT)
+       {
+               if (lv->SelectArray)
+               {
+                       int i;
+
+                       ASSERT_VALID(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(lv->Items)
+
+                       for (node = ((struct List *)lv->Items)->lh_Head; node = node->ln_Succ; count++)
+                               ASSERT_VALID(node);
+               }
+       }
+
+       return count;
+}
+
+
+
+INLINE ULONG IsItemSelected (struct LVData *lv, APTR item, ULONG num)
+
+/* Checks if the given item is selected */
+{
+       ASSERT_VALIDNO0(lv)
+       ASSERT_VALID(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_VALIDNO0(item)
+
+                       return item ? (ULONG)(((struct Node *)item)->ln_Type) : 0;
+               }
+
+               return 0;
+       }
+       else
+               return ((ULONG)(num == lv->Selected));
+}
+
+
+
+INLINE APTR GetItem (struct LVData *lv, ULONG num)
+
+/* Stub for LV_GETITEM hook method */
+{
+       struct lvGetItem lvgi;
+
+
+       ASSERT_VALIDNO0(lv)
+       ASSERT_VALIDNO0(lv->Items)
+       ASSERT_VALIDNO0(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));
+}
+
+
+
+INLINE APTR GetNext (struct LVData *lv, APTR item, ULONG num)
+
+/* Stub for LV_GETNEXT hook method */
+{
+       struct lvGetItem lvgi;
+
+
+       ASSERT_VALIDNO0(lv)
+       ASSERT_VALIDNO0(lv->GetNextFunc)
+       ASSERT_VALID(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));
+}
+
+
+
+INLINE APTR GetPrev (struct LVData *lv, APTR item, ULONG num)
+
+/* Stub for LV_GETPREV hook method */
+{
+       struct lvGetItem lvgi;
+
+
+       ASSERT_VALIDNO0(lv)
+       ASSERT_VALIDNO0(lv->GetPrevFunc)
+       ASSERT_VALID(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));
+}
+
+
+
+Class *MakeListViewClass (void)
+{
+       Class *LVClass;
+
+       if (LVClass = MakeClass (NULL, GADGETCLASS, NULL, sizeof (struct LVData), 0))
+               LVClass->cl_Dispatcher.h_Entry = (ULONG (*)()) LVDispatcher;
+
+       return LVClass;
+}
+
+
+
+void FreeListViewClass (Class *LVClass)
+{
+       ASSERT_VALID(LVClass)
+       FreeClass (LVClass);
+}
diff --git a/ListViewClass.h b/ListViewClass.h
new file mode 100644 (file)
index 0000000..98f6b19
--- /dev/null
@@ -0,0 +1,429 @@
+#ifndef LISTVIEWCLASS_H
+#define LISTVIEWCLASS_H
+/*
+**     ListViewClass.h
+**
+**     Copyright (C) 1996,97 by Bernardo Innocenti
+**
+**     ListView class built on top of the "gadgetclass".
+**
+*/
+
+#define LISTVIEWCLASS  "listviewclass"
+#define LISTVIEWVERS   1
+
+
+Class  *MakeListViewClass (void);
+void    FreeListViewClass (Class *LVClass);
+
+
+
+/*****************/
+/* Class Methods */
+/*****************/
+
+/* This class does not define any new methods */
+
+
+
+/********************/
+/* Class Attributes */
+/********************/
+
+#define LVA_Dummy                      (TAG_USER | ('L'<<16) | ('V'<<8))
+
+#define LVA_Selected           (LVA_Dummy+1)
+       /* (IGSNU) LONG - Selected item number. ~0 (-1) indicates that no
+        * item is selected.
+        */
+
+#define LVA_Top                                (LVA_Dummy+2)
+       /* (IGSNU) LONG - Number of top displayed item. Default is 0.
+        */
+
+#define LVA_Total                      (LVA_Dummy+3)
+       /* (IGSN) LONG - Total number of items in list.
+        * This attribute can be set when LVA_StringArray, LVA_ImageArray
+        * or LVA_CustomList is used. If you pass -1 or omit this tag,
+        * the ListView class will count the items in the array until it
+        * finds a NULL entry. If you know the number of nodes in your list,
+        * you will save some internal overhead by telling it to the
+        * ListView with the LVA_Total tag, expecially when using the
+        * LVA_StringList and LVA_ImageList modes. You must set LVA_Total
+        * each time you provide a new list or array, and in the same
+        * OM_SET call.
+        * Be careful: no checks are made on the values you are passing!
+        */
+
+#define LVA_SelectItem         (LVA_Dummy+4)
+       /* (SN) LONG - Add item specified by ti_Data to the
+        * selection list.
+        */
+
+#define LVA_DeselectItem       (LVA_Dummy+5)
+       /* (SN) LONG - Remove item specified by ti_Data from the
+        * selection list.
+        */
+
+#define LVA_ToggleItem         (LVA_Dummy+6)
+       /* (SN) LONG - Toggle item selection.
+        */
+
+#define LVA_ClearSelected      (LVA_Dummy+7)
+       /* (SN) LONG - Remove the selected state to AALL items.
+        */
+
+
+#define LVA_MakeVisible                (LVA_Dummy+8)
+       /* (ISU) Make this item visible by doing the minimum required scrolling.
+        */
+
+#define LVA_MoveUp                     (LVA_Dummy+9)
+#define LVA_MoveDown           (LVA_Dummy+10)
+#define LVA_MoveLeft           (LVA_Dummy+11)
+#define LVA_MoveRight          (LVA_Dummy+12)
+       /* (SU) Scroll the display up/down. left/right movement is not
+        * yet supported.
+        */
+
+#define LVA_StringList         (LVA_Dummy+13)
+#define LVA_StringArray                (LVA_Dummy+14)
+#define LVA_ImageList          (LVA_Dummy+15)
+#define LVA_ImageArray         (LVA_Dummy+16)
+#define LVA_CustomList         (LVA_Dummy+17)
+#define LVA_Labels                     LVA_StringList
+       /* (ISG) List of items to display. All structures and strings
+        * are referenced, not copied, so they must stay in memory
+        * while the ListView gadget displays them.
+        *
+        * LVA_StringList (AKA LVA_Labels) passes a pointer to a
+        * List (or MinList) of Node structures. The strings
+        * pointed by ln_Name will be displayed as item labels.
+        *
+        * LVA_StringArray specifies a pointer to an array of STRPTR which
+        * will be used as item labels.  The array must be NULL terminated
+        * unless the LVA_Count is set.
+        *
+        * LVA_ImageList passes a pointer to a List (or MinList)
+        * of Node structures. The ln_Name field of each Node structure
+        * must point to a normal Image structure or to an instance of
+        * the imageclass (or a subclass of it).
+        *
+        * LVA_ImageArray specifies a pointer to an array of pointers to
+        * Image structures or imageclass objects.  The array must be NULL
+        * terminated unless the LVA_Count attribute is used.
+        *
+        * LVA_CustomList can be used to provide a data structure which
+        * is neither a list nor an array.  Custom user functions will
+        * be called to retrieve the items. The data passed with this
+        * tag will be passed to the user functions.
+        *
+        * Setting one of these attributes to NULL causes the contents
+        * of the ListView gadget to be cleared.
+        *
+        * Setting one of these attributes to ~0 (-1) detaches the
+        * items from the ListView.  You must detach your list before
+        * adding or removing items.  This isn't required when using
+        * array oriented item lists.
+        */
+
+#define LVA_Visible                    (LVA_Dummy+18)
+       /* (IGN) ULONG - Number of visible items. When this attribute is
+        * passed on intialization, the ListView gadget will resize
+        * itself to make the desired number of lines visible. this
+        * feature is incompatible with GA_RelHeight. LVA_Visible at
+        * creation time requires a valid DrawInfo passed with the
+        * GA_DrawInfo tag.
+        */
+
+#define LVA_SelectedPtr                (LVA_Dummy+19)
+       /* (GN) Selected item pointer. Will be NULL when no items are selected.
+        */
+
+#define LVA_SelectArray                (LVA_Dummy+20)
+       /* (ISGNU) ULONG * - This array of ULONGs is only used in
+        * LVA_#?Array modes, when multiple selection is active. It will
+        * hold the selection status of all items in the list. Each
+        * element will be 0 if the related item is unselected, and
+        * it will indicate the selection order when the item is
+        * selected.
+        */
+
+#define LVA_CallBack           (LVA_Dummy+21)
+       /* (I) struct Hook * - Callback hook for various listview operations.
+        * The call back hook is called with:
+     *         A0 - struct Hook *
+     *         A1 - struct LVDrawMsg *
+     *         A2 - struct Node *
+     * The callback hook *must* check the lvdm_MethodID field of the
+     * message and only do processing if it is known. If any other
+     * value is passed, the callback hook must return LVCB_UNKNOWN.
+        */
+
+#define LVA_ShowSelected       (LVA_Dummy+23)
+       /* (I) BOOL - Enable highlighting selected items (default is TRUE).
+        * Note that this is different from the GadTools ListView default,
+        * which is not displaying the selected item.
+        */
+
+#define LVA_Clipped                    (LVA_Dummy+24)
+       /* (I) BOOL - When this attribute is set, the ListView gadget Installs
+        * a ClipRegion in its Layer whenever it redraws its items.
+        * (defaults is FALSE).
+        */
+
+#define LVA_DoMultiSelect      (LVA_Dummy+25)
+       /* (I) BOOL - Allows picking multiple items from the list
+        * (default is FALSE).
+        * When MultiSelect mode is active and a List structure is used,
+        * the ListView gadget keeps track of which items are selected
+        * by setting/clearing the ln_Type field of the Node structure.
+        * When items are passed with an array, you must also provide
+        * a second array for storing selection information (LVA_SelectArray).
+        * When item number n is selected, then the n-th BOOL of this array
+        * is set with its selection order.
+        */
+
+#define LVA_ItemHeight         (LVA_Dummy+27)
+       /* (I) ULONG - Exact height of an item. Defaults to be the Y size
+        * of the font used to draw the text labels. The listview will ask
+        * the height to its Image items if not explicitly given.
+        */
+
+#define LVA_MaxPen                     (LVA_Dummy+28)
+       /* (I) LONG - The maximum pen number used by rendering in a custom
+        * rendering callback hook. This is used to optimize the
+        * rendering and scrolling of the listview display (default is
+        * the maximum pen number used by all of TEXTPEN, BACKGROUNDPEN,
+        * FILLPEN, TEXTFILLPEN and BLOCKPEN).
+        */
+
+#define LVA_TextFont           (LVA_Dummy+29)
+       /* (I) struct TextFont * - Font to be used for rendering texts.
+        * Defaults to the default screen font.
+        */
+
+#define LVA_DoubleClick                (LVA_Dummy+30)
+       /* (N) ULONG - The item specified by ti_Data has been double clicked.
+        */
+
+#define LVA_MaxSelect          (LVA_Dummy+31)
+       /* (IS) ULONG - Maximum number of selected items to allow in multiselect
+        * mode. If you later set this tag with a more restrictive condition, the
+        * listview will NOT deselect any of the currently selected items to
+        * satisfy your request. Default is unlimited (~0).
+        */
+
+#define LVA_PixelTop           (LVA_Dummy+32)
+       /* (ISGNU) ULONG - Offset from top of list in pixel units. Useful for
+        * scrollers.
+        */
+
+#define LVA_PixelHeight                (LVA_Dummy+33)
+       /* (N) LONG - Total height of list in pixel units. Useful for scrollers.
+        */
+
+#define LVA_PixelVVisible      (LVA_Dummy+34)
+       /* (N) LONG - Number of vertical visible pixels. Useful for scrollers.
+        */
+
+#define LVA_PixelLeft          (LVA_Dummy+35)
+       /* (ISNU) LONG - Offset from left of list in pixel units. Useful for scrollers.
+        */
+
+#define LVA_PixelWidth         (LVA_Dummy+36)
+       /* (ISNG) LONG - Total width of list in pixel units. Useful for scrollers.
+        */
+
+#define LVA_PixelHVisible      (LVA_Dummy+37)
+       /* (N) Number of horizontal visible pixels. Useful for scrollers.
+        */
+
+#define LVA_Title                      (LVA_Dummy+38)
+       /* (IS) Listview title item.  This item will be drawn on the top line of
+        * the list and will not scroll.  ti_Data points to an item in the same
+        * format of the items list (e.g.: If it is LVA_StringArray, then it will
+        * be a Node * with ln_Name pointing to a text string. The item will be
+        * passed to the custom item drawing hook. (TODO)
+        */
+
+#define LVA_Columns                    (LVA_Dummy+39)
+       /* (I) (LONG) Number of columns to be displayed. Default is 1. If set
+        * to ~0, the listview will precheck all items to calculate this number
+        * automatically. (TODO)
+        */
+
+#define LVA_ColumnSpacing      (LVA_Dummy+40)
+       /* (I) ULONG - Spacing between columns in pixel units.
+        * Default is 4 pixels. (TODO)
+        */
+
+#define LVA_ColumnWidths       (LVA_Dummy+41)
+       /* (IGS) ULONG * - Points to an array of ULONGs containing the width of
+        * each column expressed in pixel units.  A value of -1 causes the
+        * ListView to automatically calculate the width of the column, by
+        * asking the width to all items. (TODO)
+        */
+
+#define LVA_ColumnSeparator    (LVA_Dummy+42)
+       /* (I) UBYTE - When the listview is in multicolumn mode, the
+        * internal text label routines will scan the string for this
+        * character, as a separator for the column labels. This defaults
+        * to '\t', so a label for a three column list view would look
+        * like this: "One\tTwo\tThree". (TODO)
+        */
+
+#define LVA_ResizeColumns      (LVA_Dummy+43)
+       /* (IS) BOOL - Allows the user to resize the columns by dragging
+        * on the listview title line. (TODO)
+        */
+
+#define LVA_SelectTick         (LVA_Dummy+44)
+       /* (I) When user selects an item, show a checkmark on the left
+        * instead of rendering the item in selected state. (TODO)
+        */
+
+#define LVA_ScrollInertia      (LVA_Dummy+45)
+       /* (IS) ULONG - Sets the scrolling inertia of the listview.
+        * Defaults to 4 for LVA_Clipped mode, 0 for a non-clipped listview.
+        * (TODO)
+        */
+
+#define LVA_ScrollRatio                (LVA_Dummy+46)
+       /* (IS) ULONG - If you ask the listview to scroll more than
+        * LVA_Visible / LVA_ScrollRatio lines, all the listview contents
+        * will be redrawn instead.  The minimum value of 1 will trigger a
+        * full redraw only when ALL the listview visible part is scrolled away.
+        * The default value is 2, which is a good compromise for items which
+        * can redraw themselves relatively quickly, such as simple text
+        * labels or bitmap images.
+        */
+
+/* Public flags */
+#define LVB_READONLY           0       /* Do not allow item selection                          */
+#define LVB_CLIPPED                    1       /* Clip item drawing inside their boxes         */
+#define LVB_SHOWSELECTED       2       /* Hilights the selected item                           */
+#define LVB_DOMULTISELECT      3       /* Allows user to pick more than one item       */
+#define LVB_SMOOTHSCROLLING    4       /* Scoll pixel by pixel                                         */
+#define LVB_RESIZECOLUMNS      5       /* Allow user to resize the columns                     */
+
+/* Internal flags - DO NOT USE */
+#define LVB_CLOSEFONT          27      /* Close the font when disposing the object     */
+#define LVB_LIST                       28      /* Using an exec List                                           */
+#define LVB_DONTDRAW           29      /* Do not perform any drawing operations        */
+#define LVB_SCROLLING          30      /* User scrolling with middle mouse button      */
+#define LVB_DRAGGING           31      /* User dragging selection with LMB                     */
+
+
+#define LVF_READONLY           (1<<LVB_READONLY)
+#define LVF_CLIPPED                    (1<<LVB_CLIPPED)
+#define LVF_SHOWSELECTED       (1<<LVB_SHOWSELECTED)
+#define LVF_DOMULTISELECT      (1<<LVB_DOMULTISELECT)
+#define LVF_SMOOTHSCROLLING    (1<<LVB_SMOOTHSCROLLING)
+#define LVF_RESIZECOLUMNS      (1<<LVB_RESIZECOLUMNS)
+
+#define LVF_CLOSEFONT          (1<<LVB_CLOSEFONT)
+#define LVF_LIST                       (1<<LVB_LIST)
+#define LVF_DONTDRAW           (1<<LVB_DONTDRAW)
+#define LVF_SCROLLING          (1<<LVB_SCROLLING)
+#define LVF_DRAGGING           (1<<LVB_DRAGGING)
+
+
+
+/* Changed attributes:
+ *
+ * GA_ToggleSelect
+ *     (I) BOOL - When TRUE, the listview gadget will allow deselecting items
+ *     by clicking on them.
+ *
+ * GA_SelectRender
+ *     (I) struct Image * - Specifies an imageclass object to be used as
+ *     cursor/selection.  The image will be drawn in IDS_SELECTED state
+ *     for the selected item and IDS_NORMAL for all all other highlighted
+ *     items.
+ *
+ * GA_Immediate
+ *     (I) BOOL - Sends interim notifications when the selected item changes.
+ *
+ * GA_TextAttr
+ *     (I) struct TextAttr * - Font to be used for rendering texts.
+ *     Defaults to the default screen font. See also LVA_TextFont.
+ *
+ * GA_ReadOnly
+ *     (I) BOOL - Prevent selection of items (default is FALSE).
+ *
+ * LAYOUTA_Spacing
+ *     (I) UWORD - Extra space to place between lines of listview
+ *     (defaults to 0).
+ *
+ */
+
+
+
+/* Do not define these if <libraries/gadtools.h> will be included too */
+
+#ifdef LV_GADTOOLS_STUFF
+
+/* The different types of messages that a listview callback hook can see */
+#define LV_DRAW                0x202L          /* draw yourself, with state    */
+
+/* Possible return values from a callback hook */
+#define LVCB_OK                        0               /* callback understands this message type               */
+#define LVCB_UNKNOWN   1               /* callback does not understand this message    */
+
+/* states for LVDrawMsg.lvdm_State */
+#define LVR_NORMAL                             0       /* the usual                            */
+#define LVR_SELECTED                   1       /* for selected gadgets         */
+#define LVR_NORMALDISABLED             2       /* for disabled gadgets         */
+#define LVR_SELECTEDDISABLED   8       /* disabled and selected        */
+
+#endif /* LV_GADTOOLS_STUFF */
+
+#define LVR_TITLE                              16      /* ListView title item          */
+
+
+/* More callback hook methods */
+
+#define LV_GETNEXT             0x203L  /* gimme next item in list              */
+#define LV_GETPREV             0x204L  /* gimme previous item in list  */
+#define LV_GETITEM             0x205L  /* gimme item handle by number  */
+
+/* These two methods can be used to optimize listview rendering
+ * operations.  You can safely assume that the rastport attributes
+ * you set inside LV_DRAWBEGIN will remain unchanged for all
+ * subsequent calls to LV_DRAW, until an LV_DRAWEND is issued.
+ * They do also provide a way to lock/unlock the list of items
+ * if the access to its item needs to be arbitrated by a semaphore.
+ */
+#define LV_DRAWBEGIN   0x206L  /* prepare to draw items                */
+#define LV_DRAWEND             0x207L  /* items drawing completed              */
+
+
+
+/* More messages */
+
+struct lvDrawItem
+{
+       ULONG                           lvdi_MethodID;  /* LV_DRAW                                              */
+       ULONG                           lvdi_Current;   /* Current item number                  */
+       APTR                            lvdi_Items;             /* Pointer to List, array, etc. */
+       struct RastPort         *lvdi_RastPort; /* where to render to                   */
+       struct DrawInfo         *lvdi_DrawInfo; /* useful to have around                */
+       struct Rectangle        lvdi_Bounds;    /* limits of where to render    */
+       ULONG                           lvdi_State;             /* how to render                                */
+       ULONG                           lvdi_Flags;             /* Current LVF_#? flags                 */
+};
+
+struct lvGetItem
+{
+       ULONG   lvgi_MethodID;  /* LV_GETITEM                                   */
+       ULONG   lvgi_Number;    /* Number of item to get                */
+       APTR    lvgi_Items;             /* Pointer to List, array, etc. */
+};
+
+#define lvGetNext      lvGetItem
+#define lvGetPrev      lvGetItem
+#define lvDrawBegin    lvGetItem       /* lvgi_Number has no useful meaning    */
+#define lvDrawEnd      lvGetItem       /* lvgi_Number has no useful meaning    */
+
+#endif /* !LISTVIEWCLASS_H */
diff --git a/ListViewHooks.c b/ListViewHooks.c
new file mode 100644 (file)
index 0000000..9d65c93
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+**     ListViewHooks.c
+**
+**     Copyright (C) 1996,97 Bernardo Innocenti
+**
+**     Use 4 chars wide TABs to read this file
+**
+**     Internal drawing and browsing hooks the listview class
+*/
+
+#define USE_BUILTIN_MATH
+#define INTUI_V36_NAMES_ONLY
+#define __USE_SYSBASE
+#define  CLIB_ALIB_PROTOS_H            /* Avoid dupe defs of boopsi funcs */
+
+#include <exec/types.h>
+#include <intuition/intuition.h>
+#include <graphics/gfxbase.h>
+#include <graphics/gfxmacros.h>
+
+#include <proto/intuition.h>
+#include <proto/graphics.h>
+
+#ifdef __STORM__
+       #pragma header
+#endif
+
+#include "CompilerSpecific.h"
+#include "Debug.h"
+
+#define LV_GADTOOLS_STUFF
+#include "ListViewClass.h"
+
+
+
+/* Definitions for builtin List hook */
+
+APTR HOOKCALL ListGetItem (
+       REG(a0, struct Hook                     *hook),
+       REG(a1, struct Node                     *node),
+       REG(a2, struct lvGetItem        *lvgi));
+APTR HOOKCALL ListGetNext (
+       REG(a0, struct Hook                     *hook),
+       REG(a1, struct Node                     *node),
+       REG(a2, struct lvGetNext        *lvgn));
+APTR HOOKCALL ListGetPrev (
+       REG(a0, struct Hook                     *hook),
+       REG(a1, struct Node                     *node),
+       REG(a2, struct lvGetPrev        *lvgp));
+ULONG HOOKCALL ListStringDrawItem (
+       REG(a0, struct Hook                     *hook),
+       REG(a1, struct Node                     *node),
+       REG(a2, struct lvDrawItem       *lvdi));
+ULONG HOOKCALL ListImageDrawItem (
+       REG(a0, struct Hook                     *hook),
+       REG(a1, struct Node                     *node),
+       REG(a2, struct lvDrawItem       *lvdi));
+
+
+/* Definitions for builtin Array hook */
+
+APTR HOOKCALL ArrayGetItem (
+       REG(a0, struct Hook                     *hook),
+       REG(a1, STRPTR                          *item),
+       REG(a2, struct lvGetItem        *lvgi));
+ULONG HOOKCALL StringDrawItem (
+       REG(a0, struct Hook                     *hook),
+       REG(a1, STRPTR                           str),
+       REG(a2, struct lvDrawItem       *lvdi));
+ULONG HOOKCALL ImageDrawItem (
+       REG(a0, struct Hook                     *hook),
+       REG(a1, struct Image            *img),
+       REG(a2, struct lvDrawItem       *lvdi));
+
+
+
+APTR HOOKCALL ListGetItem (
+       REG(a0, struct Hook                     *hook),
+       REG(a1, struct Node                     *node),
+       REG(a2, struct lvGetItem        *lvg))
+{
+       ULONG i;
+
+       ASSERT_VALIDNO0(lvg)
+
+       node = ((struct List *)(lvg->lvgi_Items))->lh_Head;
+
+       /* Warning: no sanity check is made against
+        * list being shorter than expected!
+        */
+       for (i = 0; i < lvg->lvgi_Number; i++)
+       {
+               ASSERT_VALIDNO0(node)
+               node = node->ln_Succ;
+       }
+
+       return (APTR)node;
+}
+
+
+
+APTR HOOKCALL ListGetNext (
+       REG(a0, struct Hook                     *hook),
+       REG(a1, struct Node                     *node),
+       REG(a2, struct lvGetItem        *lvg))
+{
+       ASSERT_VALIDNO0(node)
+       ASSERT_VALIDNO0(lvg)
+
+       return (APTR)(node->ln_Succ->ln_Succ ? node->ln_Succ : NULL);
+}
+
+
+
+APTR HOOKCALL ListGetPrev (
+       REG(a0, struct Hook                     *hook),
+       REG(a1, struct Node                     *node),
+       REG(a2, struct lvGetItem        *lvg))
+{
+       ASSERT_VALIDNO0(node)
+       ASSERT_VALIDNO0(lvg)
+
+       return (APTR)(node->ln_Pred->ln_Pred ? node->ln_Pred : NULL);
+}
+
+
+
+ULONG HOOKCALL ListStringDrawItem (
+       REG(a0, struct Hook                     *hook),
+       REG(a1, struct Node                     *node),
+       REG(a2, struct lvDrawItem       *lvdi))
+{
+       ASSERT_VALIDNO0(node)
+       ASSERT_VALIDNO0(lvdi)
+
+       return StringDrawItem (hook, node->ln_Name, lvdi);
+}
+
+
+
+ULONG HOOKCALL ListImageDrawItem (
+       REG(a0, struct Hook                     *hook),
+       REG(a1, struct Node                     *node),
+       REG(a2, struct lvDrawItem       *lvdi))
+{
+       ASSERT_VALIDNO0(node)
+       ASSERT_VALIDNO0(lvdi)
+
+       return ImageDrawItem (hook, (struct Image *)node->ln_Name, lvdi);
+}
+
+
+
+APTR HOOKCALL ArrayGetItem (
+       REG(a0, struct Hook                     *hook),
+       REG(a1, STRPTR                          *item),
+       REG(a2, struct lvGetItem        *lvg))
+{
+       ASSERT_VALIDNO0(lvg)
+       ASSERT_VALIDNO0(lvg->lvgi_Items)
+
+       return (APTR)(((STRPTR *)lvg->lvgi_Items)[lvg->lvgi_Number]);
+}
+
+
+
+ULONG HOOKCALL StringDrawItem (
+       REG(a0, struct Hook                     *hook),
+       REG(a1, STRPTR                           str),
+       REG(a2, struct lvDrawItem       *lvdi))
+{
+       struct RastPort *rp;
+       ULONG len;
+
+       ASSERT_VALIDNO0(lvdi)
+       rp = lvdi->lvdi_RastPort;
+       ASSERT_VALIDNO0(rp)
+       ASSERT_VALID(str)
+
+       if (!str)
+               /* Move to the leftmost pixel of the rectangle
+                * to have the following RectFill() clear all the line
+                */
+               Move (rp, lvdi->lvdi_Bounds.MinX, 0);
+       else
+       {
+               struct TextExtent textent;
+
+               if (lvdi->lvdi_State == LVR_NORMAL)
+               {
+#ifndef OS30_ONLY
+                       if (GfxBase->LibNode.lib_Version < 39)
+                       {
+                               SetAPen (rp, lvdi->lvdi_DrawInfo->dri_Pens[TEXTPEN]);
+                               SetBPen (rp, lvdi->lvdi_DrawInfo->dri_Pens[BACKGROUNDPEN]);
+                               SetDrMd (rp, JAM2);
+                       }
+                       else
+#endif /* !OS30_ONLY */
+                       SetABPenDrMd (rp, lvdi->lvdi_DrawInfo->dri_Pens[TEXTPEN],
+                               lvdi->lvdi_DrawInfo->dri_Pens[BACKGROUNDPEN],
+                               JAM2);
+               }
+               else
+               {
+#ifndef OS30_ONLY
+                       if (GfxBase->LibNode.lib_Version < 39)
+                       {
+                               SetAPen (rp, lvdi->lvdi_DrawInfo->dri_Pens[FILLTEXTPEN]);
+                               SetBPen (rp, lvdi->lvdi_DrawInfo->dri_Pens[FILLPEN]);
+                               SetDrMd (rp, JAM2);
+                       }
+                       else
+#endif /* !OS30_ONLY */
+                               SetABPenDrMd (rp, lvdi->lvdi_DrawInfo->dri_Pens[FILLTEXTPEN],
+                                       lvdi->lvdi_DrawInfo->dri_Pens[FILLPEN],
+                                       JAM2);
+               }
+
+               Move (rp, lvdi->lvdi_Bounds.MinX, lvdi->lvdi_Bounds.MinY + rp->Font->tf_Baseline);
+
+               len = strlen (str);
+
+               if (!(lvdi->lvdi_Flags & LVF_CLIPPED))
+               {
+                       /* Calculate how much text will fit in the listview width */
+                       len = TextFit (rp, str, len, &textent, NULL, 1,
+                               lvdi->lvdi_Bounds.MaxX - lvdi->lvdi_Bounds.MinX + 1,
+                               lvdi->lvdi_Bounds.MaxY - lvdi->lvdi_Bounds.MinY + 1);
+               }
+
+               Text (rp, str, len);
+
+               /* Text() will move the pen X position to
+                * lvdi->lvdi_Bounds.MinX + textent.te_Width.
+                */
+       }
+
+       /* Now clear the rest of the row. rp->cp_x is updated by Text() to the
+        * next character to print.
+        */
+       SetAPen (rp, lvdi->lvdi_DrawInfo->dri_Pens[(lvdi->lvdi_State == LVR_NORMAL) ?
+               BACKGROUNDPEN : FILLPEN]);
+       RectFill (rp, rp->cp_x,
+               lvdi->lvdi_Bounds.MinY,
+               lvdi->lvdi_Bounds.MaxX,
+               lvdi->lvdi_Bounds.MaxY);
+
+
+       return LVCB_OK;
+}
+
+
+
+ULONG HOOKCALL ImageDrawItem (
+       REG(a0, struct Hook                     *hook),
+       REG(a1, struct Image            *img),
+       REG(a2, struct lvDrawItem       *lvdi))
+{
+       struct RastPort *rp;
+       UWORD left;
+
+       ASSERT_VALID(img)
+       ASSERT_VALIDNO0(lvdi)
+       rp = lvdi->lvdi_RastPort;
+       ASSERT_VALIDNO0(rp)
+
+       if (!img)
+               /* Move to the leftmost pixel of the item rectangle
+                * to have the following RectFill() clear all the line
+                */
+               left = lvdi->lvdi_Bounds.MinX;
+       else
+       {
+               DrawImageState (rp, img,
+                       lvdi->lvdi_Bounds.MinX, lvdi->lvdi_Bounds.MinY,
+                       lvdi->lvdi_State, lvdi->lvdi_DrawInfo);
+
+               left = lvdi->lvdi_Bounds.MinX + img->Width;
+       }
+
+       /* Now clear the rest of the row. rp->cp_x is updated by Text() to the
+        * next character to print.
+        */
+       SetAPen (rp, lvdi->lvdi_DrawInfo->dri_Pens[(lvdi->lvdi_State == LVR_NORMAL) ?
+               BACKGROUNDPEN : FILLPEN]);
+       RectFill (rp, left,
+               lvdi->lvdi_Bounds.MinY,
+               lvdi->lvdi_Bounds.MaxX,
+               lvdi->lvdi_Bounds.MaxY);
+
+       return LVCB_OK;
+}
diff --git a/SMakefile b/SMakefile
new file mode 100644 (file)
index 0000000..f985286
--- /dev/null
+++ b/SMakefile
@@ -0,0 +1,136 @@
+##
+##     $VER: LVDemo_Makefile 2.2 (14.9.97)
+##
+##     Copyright (C) 1996,97 by Bernardo Innocenti
+##
+##
+
+###########################################################
+# Name of the main executable
+###########################################################
+#
+PROJ = LVDemo
+
+
+###########################################################
+# Package configuration
+###########################################################
+#
+
+# set to OS30_ONLY to leave out support for old V37
+# set to ANY_OS to make an executable for V37 with V39 support
+#
+#OSVER = ANY_OS
+OSVER = OS30_ONLY
+
+# Cpu to compile for (eg: "68020").
+#
+#CPU = 68000
+CPU = 68020
+
+###########################################################
+# Object files in this project
+###########################################################
+#
+OBJS = startup_sc.o LVDemo.o ListViewHooks.o \
+ ListViewClass.o ListBoxClass.o ScrollButtonClass.o
+
+
+###########################################################
+# Make the project
+###########################################################
+#
+all: $(PROJ)
+
+
+###########################################################
+# Remove all targets and intermediate files
+###########################################################
+#
+clean:
+       -Delete $(PROJ) $(OBJS) $(PROJ).gst
+
+
+###########################################################
+# Compiler, linker and assembler flags
+###########################################################
+#
+# Note: Using the "STRINGSCONST" compiler option requires
+#       patched versions of the OS headers to work correctly
+#
+
+# Compiler flags for both release and debug versions
+#
+COMMON_CFLAGS = PARAMETERS=REGISTERS STRINGMERGE NOSTACKCHECK NOCHECKABORT \
+       NOICONS NOVERSION ERRORREXX NOLINK DATA=NEAR CODE=NEAR \
+       STRSECT=CODE STRINGSCONST GST $(PROJ).gst DEF=$(OSVER) CPU=$(CPU)
+
+# Compiler optimization flags
+#
+OPT_CFLAGS = OPTIMIZE OPTTIME OPTSCHEDULER OPTINLINELOCAL \
+       OPTRDEPTH=4 OPTDEPTH=4 OPTCOMP=8
+
+# Debug flags: don't optimize and include all symbols in debug hunks
+#
+DEBUG_CFLAGS = NOOPTIMIZE DEBUG=FULLFLUSH ONERROR=CONTINUE DEF DEBUG CODE=FAR
+
+# Use the utility.library for 32bit multiplication and division.
+#
+UTILLIB_LFLAGS = DEFINE __CXM33=__UCXM33 DEFINE __CXD33=__UCXD33 \
+       DEFINE __CXM22=__UCXM22 DEFINE __CXD22=__UCXD22
+
+
+# RELEASE version should be compiled with these flags
+#
+#CFLAGS = $(COMMON_CFLAGS) $(OPT_CFLAGS)
+#LFLAGS = NODEBUG SMALLCODE SMALLDATA NOALVS NOICONS $(UTILLIB_LFLAGS)
+#LIBS = LIB LIB:sc.lib
+
+
+# DEBUG version should be compiled with these flags
+#
+CFLAGS = $(COMMON_CFLAGS) $(DEBUG_CFLAGS)
+LFLAGS = ADDSYM SMALLCODE SMALLDATA BATCH NOALVS NOICONS $(UTILLIB_LFLAGS)
+LIBS = LIB LIB:debug.lib LIB:sc.lib LIB:small.lib
+
+
+###########################################################
+# 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.
+#
+# NOTE:        The GST file does not depend on ListViewClass.h because
+#      otherwise all objects would be remade whenever I slightly edit
+#      the header file.
+#
+
+$(PROJ).gst: GST.c
+       $(CC) FROM GST.c MAKEGST $(PROJ).gst NOOBJNAME $(CFLAGS) \
+        DEF=INTUI_V36_NAMES_ONLY DEF=__USE_SYSBASE \
+        DEF=CLIB_ALIB_PROTOS_H DEF=LV_GADTOOLS_STUFF
+
+###########################################################
+# Make the executable
+###########################################################
+#
+# NOTE: Using implicit make rule to compile C files:
+#      .c.o:
+#              $(CC) $(CFLAGS) $(*).c
+#
+# NOTE: Using implicit make rule to assemble startup_sc.s
+#
+
+$(PROJ): $(PROJ).gst $(OBJS)
+       $(LD) FROM $(OBJS) TO $(PROJ) $(LIBS) $(LFLAGS)
+
+
+###########################################################
+# Dependencies
+###########################################################
+#
+ListViewClass.o:               ListViewClass.c
+ListBoxClass.o:                        ListBoxClass.c
+ScrollButtonClass.o:   ScrollButtonClass.c
+LVDemo.o:                              LVDemo.c
diff --git a/ScrollButtonClass.c b/ScrollButtonClass.c
new file mode 100644 (file)
index 0000000..ebc0a77
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+**     ScrollButtonClass.c
+**
+**     Copyright (C) 1998 Bernardo Innocenti
+**
+**     Use 4 chars wide TABs to read this file
+**
+**     Subclass of the buttongclass specialized for scroll buttons
+*/
+
+#define USE_BUILTIN_MATH
+#define INTUI_V36_NAMES_ONLY
+#define __USE_SYSBASE
+#define  CLIB_ALIB_PROTOS_H            /* Avoid dupe defs of boopsi funcs */
+
+
+#include <intuition/gadgetclass.h>
+
+#include <proto/intuition.h>
+
+
+#ifdef __STORM__
+       #pragma header
+#endif
+
+#include "CompilerSpecific.h"
+#include "Debug.h"
+#include "BoopsiStubs.h"
+
+#include "ScrollButtonClass.h"
+
+
+
+
+/* Per object instance data */
+struct ScrollButtonData
+{
+       /* The number of ticks we still have to wait
+        * before sending any notification.
+        */
+       ULONG TickCounter;
+};
+
+
+
+/* Dispatcher function prototype (just to keep esigent compilers quiet) */
+
+static ULONG HOOKCALL ScrollButtonDispatcher (
+       REG(a0, Class *cl),
+       REG(a2, struct Gadget *g),
+       REG(a1, struct gpInput *gpi));
+
+static ULONG HOOKCALL ScrollButtonDispatcher (
+       REG(a0, Class *cl),
+       REG(a2, struct Gadget *g),
+       REG(a1, struct gpInput *gpi))
+
+/* ScrollButton Class Dispatcher entrypoint.
+ * Handle boopsi messages.
+ */
+{
+       struct ScrollButtonData *bd = (struct ScrollButtonData *) INST_DATA(cl, g);
+
+       switch (gpi->MethodID)
+       {
+               case GM_GOACTIVE:
+                       /* May define an attribute to make delay configurable */
+                       bd->TickCounter = 3;
+
+                       /* Notify our target that we have initially hit. */
+                       NotifyAttrs ((Object *)g, gpi->gpi_GInfo, 0,
+                               GA_ID,  g->GadgetID,
+                               TAG_DONE);
+
+                       /* Send more input */
+                       return GMR_MEACTIVE;
+
+               case GM_HANDLEINPUT:
+               {
+                       struct RastPort *rp;
+                       ULONG retval = GMR_MEACTIVE;
+                       UWORD selected = 0;
+
+                       /* This also works with classic (non-boopsi) images. */
+                       if (PointInImage ((gpi->gpi_Mouse.X << 16) + (gpi->gpi_Mouse.Y), g->GadgetRender))
+                       {
+                               /* We are hit */
+                               selected = GFLG_SELECTED;
+                       }
+
+                       if (gpi->gpi_IEvent->ie_Class == IECLASS_RAWMOUSE && gpi->gpi_IEvent->ie_Code == SELECTUP)
+                       {
+                               /* Gadgetup, time to go */
+                               retval = GMR_NOREUSE;
+                               /* Unselect the gadget on our way out... */
+                               selected = 0;
+                       }
+                       else if (gpi->gpi_IEvent->ie_Class == IECLASS_TIMER)
+                       {
+                               /* We got a tick.  Decrement counter, and if 0, send notify. */
+
+                               if (bd->TickCounter)
+                                       bd->TickCounter--;
+                               else if (selected)
+                                       /* Notify our target that we are still being hit */
+                                       NotifyAttrs ((Object *)g, gpi->gpi_GInfo, 0,
+                                               GA_ID,  g->GadgetID,
+                                               TAG_DONE);
+                       }
+
+                       if ((g->Flags & GFLG_SELECTED) != selected)
+                       {
+                               /* Update changes in gadget render */
+                               g->Flags ^= GFLG_SELECTED;
+                               if (rp = ObtainGIRPort (gpi->gpi_GInfo))
+                               {
+                                       DoMethod ((Object *) g, GM_RENDER, gpi->gpi_GInfo, rp, GREDRAW_UPDATE);
+                                       ReleaseGIRPort (rp);
+                               }
+                       }
+                       return retval;
+               }
+
+               default:
+                       /* Super class handles everything else */
+                       return (DoSuperMethodA (cl, (Object *)g, (Msg) gpi));
+       }
+}
+
+
+
+Class *MakeScrollButtonClass (void)
+{
+       Class *class;
+
+       if (class = MakeClass (NULL, BUTTONGCLASS, NULL, sizeof(struct ScrollButtonData), 0))
+               class->cl_Dispatcher.h_Entry = (ULONG (*)()) ScrollButtonDispatcher;
+
+       return class;
+}
+
+
+
+BOOL FreeScrollButtonClass (Class *cl)
+{
+       return (FreeClass (cl));
+}
diff --git a/ScrollButtonClass.h b/ScrollButtonClass.h
new file mode 100644 (file)
index 0000000..560fa52
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef SCROLLBUTTONCLASS_H
+#define SCROLLBUTTONCLASS_H
+/*
+**     ScrollButtonClass
+**
+**     This code is inspierd from ScrollerWindow 0.3
+**     Copyright © 1994 Christoph Feck, TowerSystems.
+**
+**     Subclass of buttongclass.  The ROM class has two problems, which make
+**     it not quite usable for scrollarrows.  The first problem is the missing
+**     delay.  Once the next INTUITICK gets sent by input.device, the ROM
+**     class already sends a notification.  The other problem is that it also
+**     notifies us, when the button finally gets released (which is necessary
+**     for command buttons).
+**
+**     We define a new class with the GM_GOACTIVE and GM_HANDLEINPUT method
+**     overloaded to work around these problems.
+*/
+
+
+#define SCROLLBUTTONCLASS      "scrollbuttonclass"
+#define SCROLLBUTTONVERS       1
+
+
+/* Functions to initialize and destroy the class */
+Class  *MakeScrollButtonClass  (void);
+BOOL    FreeScrollButtonClass  (Class *cl);
+
+
+
+/*****************/
+/* Class Methods */
+/*****************/
+
+/* This class does not define any new methods */
+
+/********************/
+/* Class Attributes */
+/********************/
+
+/* #define SBA_Dummy (TAG_USER | ('S'<<16) | ('B'<<8)) */
+
+/* This class does not define any new attributes */
+
+
+
+
+#endif /* !SCROLLBUTTONCLASS_H */
diff --git a/VectorGlyphIClass.h b/VectorGlyphIClass.h
new file mode 100644 (file)
index 0000000..3998396
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+**     VectorGlyphIClass.h
+**
+**     Copyright (C) 1995,96,97,98 by Bernardo Innocenti
+**
+**     "vectorglyphiclass" class, a vector image class built
+**     on top of the "imageclass", providing some useful
+**     glyphs for buttons.
+*/
+
+#define VECTORGLYPHCLASS "vectorglyphiclass"
+
+/* Values for the SYSIA_Which attribute */
+#define VG_PLAY                        0
+#define VG_STOP                        1
+#define VG_REW                 2
+#define VG_FWD                 3
+#define VG_PICK                        4
+#define VG_UPARROW             5
+#define VG_DOWNARROW   6
+#define VG_LEFTARROW   7
+#define VG_RIGHTARROW  8
+
+
+/* Number of glyphs offered by the vectorglyph.image class */
+#define VG_IMGCOUNT 9
diff --git a/startup_gcc.s b/startup_gcc.s
new file mode 100644 (file)
index 0000000..ca4a05d
--- /dev/null
@@ -0,0 +1,2 @@
+.text
+       jmp     __main
diff --git a/startup_sc.s b/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/startup_storm.s b/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