--- /dev/null
+#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 */
--- /dev/null
+#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 */
--- /dev/null
+#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 */
--- /dev/null
+##
+## $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
--- /dev/null
+/*
+** 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"
--- /dev/null
+/* $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 */
+}
--- /dev/null
+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)
--- /dev/null
+/*
+** 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);
+ }
+}
--- /dev/null
+#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 */
--- /dev/null
+#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 */
--- /dev/null
+/* 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);
+}
--- /dev/null
+#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 */
--- /dev/null
+/*
+** 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;
+}
--- /dev/null
+##
+## $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
--- /dev/null
+/*
+** 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));
+}
--- /dev/null
+#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 */
--- /dev/null
+/*
+** 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
--- /dev/null
+.text
+ jmp __main
--- /dev/null
+
+ XREF @_main
+
+ SECTION CODE,code
+
+_start:
+ jmp @_main
+
+ END
--- /dev/null
+
+ XREF __main
+
+ SECTION CODE,code
+
+_start:
+ bra __main
+
+ END