From 06ee78d1a44acc2d40194554674af6bacaff012b Mon Sep 17 00:00:00 2001 From: Bernie Innocenti Date: Fri, 11 Mar 2011 01:02:27 -0500 Subject: [PATCH 1/1] Initial commit --- BoopsiStubs.h | 181 ++++ CompilerSpecific.h | 304 ++++++ Debug.h | 103 ++ GNUmakefile | 92 ++ GST.c | 45 + LVDemo.c | 1043 +++++++++++++++++++++ "LVDemo.\266" | 147 +++ ListBoxClass.c | 770 +++++++++++++++ ListBoxClass.h | 36 + ListMacros.h | 98 ++ ListViewClass.c | 2181 +++++++++++++++++++++++++++++++++++++++++++ ListViewClass.h | 429 +++++++++ ListViewHooks.c | 293 ++++++ SMakefile | 136 +++ ScrollButtonClass.c | 147 +++ ScrollButtonClass.h | 48 + VectorGlyphIClass.h | 26 + startup_gcc.s | 2 + startup_sc.s | 9 + startup_storm.s | 9 + 20 files changed, 6099 insertions(+) create mode 100644 BoopsiStubs.h create mode 100644 CompilerSpecific.h create mode 100644 Debug.h create mode 100644 GNUmakefile create mode 100644 GST.c create mode 100644 LVDemo.c create mode 100644 "LVDemo.\266" create mode 100644 ListBoxClass.c create mode 100644 ListBoxClass.h create mode 100644 ListMacros.h create mode 100644 ListViewClass.c create mode 100644 ListViewClass.h create mode 100644 ListViewHooks.c create mode 100644 SMakefile create mode 100644 ScrollButtonClass.c create mode 100644 ScrollButtonClass.h create mode 100644 VectorGlyphIClass.h create mode 100644 startup_gcc.s create mode 100644 startup_sc.s create mode 100644 startup_storm.s diff --git a/BoopsiStubs.h b/BoopsiStubs.h new file mode 100644 index 0000000..a8202c7 --- /dev/null +++ b/BoopsiStubs.h @@ -0,0 +1,181 @@ +#ifndef BOOPSISTUBS_H +#define BOOPSISTUBS_H +/* +** $VER: BoopsiStubs.h 1.2 (1.9.97) +** +** Copyright (C) 1997 Bernardo Innocenti. All rights reserved. +** +** Use 4 chars wide TABs to read this file +** +** Using these inline versions of the amiga.lib boopsi support functions +** results in faster and smaller code against their linked library +** counterparts. When debug is active, these functions will also +** validate the parameters you pass in. +** +*/ + +#ifndef COMPILERSPECIFIC_H +#include "CompilerSpecific.h" +#endif /* COMPILERSPECIFIC_H */ + +#ifndef DEBUG_H +#include "Debug.h" +#endif /* DEBUG_H */ + +/* This definition will prevent the redefinition of the following stubs with + * their amiga.lib equivalents. This only works if you are using a patched + * version of + */ +#define USE_BOOPSI_STUBS + + + +/* the _HookPtr type is a shortcut for a pointer to a hook function */ + +typedef ASMCALL ULONG (*_HookPtr) + (REG(a0, Class *), REG(a2, Object *), REG(a1, APTR)); + +INLINE ULONG CoerceMethodA (struct IClass *cl, Object *o, Msg message) +{ + ASSERT_VALIDNO0(cl) + ASSERT_VALID(o) + + return ((_HookPtr)cl->cl_Dispatcher.h_Entry) ((APTR)cl, (APTR)o, (APTR)message); +} + +INLINE ULONG DoSuperMethodA (struct IClass *cl, Object *o, Msg message) +{ + ASSERT_VALIDNO0(cl) + ASSERT_VALID(o) + + cl = cl->cl_Super; + return ((_HookPtr)cl->cl_Dispatcher.h_Entry) ((APTR)cl, (APTR)o, (APTR)message); +} + +INLINE ULONG DoMethodA (Object *o, Msg message) +{ + Class *cl; + ASSERT_VALIDNO0(o) + cl = OCLASS (o); + ASSERT_VALIDNO0(cl) + + return ((_HookPtr)cl->cl_Dispatcher.h_Entry) ((APTR)cl, (APTR)o, (APTR)message); +} + + + +/* Var-args versions of the above functions. SAS/C is clever enough to inline these, + * while gcc and egcs refuse to inline a function with '...' (yikes!). The GCC + * versions of these functions are macro blocks similar to those used in the + * inline/#?.h headers. + */ +#if defined(__SASC) || defined (__STORM__) + + INLINE ULONG CoerceMethod (struct IClass *cl, Object *o, ULONG MethodID, ...) + { + ASSERT_VALIDNO0(cl) + ASSERT_VALID(o) + + return ((_HookPtr)cl->cl_Dispatcher.h_Entry) ((APTR)cl, (APTR)o, (APTR)&MethodID); + } + + INLINE ULONG DoSuperMethod (struct IClass *cl, Object *o, ULONG MethodID, ...) + { + ASSERT_VALIDNO0(cl) + ASSERT_VALID(o) + + cl = cl->cl_Super; + return ((_HookPtr)cl->cl_Dispatcher.h_Entry) ((APTR)cl, (APTR)o, (APTR)&MethodID); + } + + INLINE ULONG DoMethod (Object *o, ULONG MethodID, ...) + { + Class *cl; + + ASSERT_VALIDNO0(o) + cl = OCLASS (o); + ASSERT_VALIDNO0(cl) + + return ((_HookPtr)cl->cl_Dispatcher.h_Entry) ((APTR)cl, (APTR)o, (APTR)&MethodID); + } + + /* varargs stub for the OM_NOTIFY method */ + INLINE void NotifyAttrs (Object *o, struct GadgetInfo *gi, ULONG flags, Tag attr1, ...) + { + ASSERT_VALIDNO0(o) + ASSERT_VALID(gi) + + DoMethod (o, OM_NOTIFY, &attr1, gi, flags); + } + + /* varargs stub for the OM_UPDATE method */ + INLINE void UpdateAttrs (Object *o, struct GadgetInfo *gi, ULONG flags, Tag attr1, ...) + { + ASSERT_VALIDNO0(o) + ASSERT_VALID(gi) + + DoMethod (o, OM_UPDATE, &attr1, gi, flags); + } + +#elif defined(__GNUC__) + + #define CoerceMethod(cl, o, msg...) \ + ({ \ + ULONG _msg[] = { msg }; \ + ASSERT_VALIDNO0(cl) \ + ASSERT_VALID(o) \ + ((_HookPtr)cl->cl_Dispatcher.h_Entry) ((APTR)cl, (APTR)o, (APTR)_msg); \ + }) + + #define DoSuperMethod(cl, o, msg...) \ + ({ \ + Class *_cl; \ + ULONG _msg[] = { msg }; \ + ASSERT_VALID(o) \ + ASSERT_VALIDNO0(cl) \ + _cl = cl = cl->cl_Super; \ + ASSERT_VALIDNO0(_cl) \ + ((_HookPtr)_cl->cl_Dispatcher.h_Entry) ((APTR)_cl, (APTR)o, (APTR)_msg); \ + }) + + #define DoMethod(o, msg...) \ + ({ \ + Class *_cl; \ + ULONG _msg[] = { msg }; \ + ASSERT_VALIDNO0(o) \ + _cl = OCLASS(o); \ + ASSERT_VALIDNO0(_cl) \ + ((_HookPtr)_cl->cl_Dispatcher.h_Entry) ((APTR)_cl, (APTR)o, (APTR)_msg); \ + }) + + /* Var-args stub for the OM_NOTIFY method */ + #define NotifyAttrs(o, gi, flags, attrs...) \ + ({ \ + Class *_cl; \ + ULONG _attrs[] = { attrs }; \ + ULONG _msg[] = { OM_NOTIFY, (ULONG)_attrs, (ULONG)gi, flags }; \ + ASSERT_VALIDNO0(o) \ + _cl = OCLASS(o); \ + ASSERT_VALIDNO0(_cl) \ + ASSERT_VALID(gi) \ + ((_HookPtr)_cl->cl_Dispatcher.h_Entry) ((APTR)_cl, (APTR)o, (APTR)_msg); \ + }) + + /* Var-args stub for the OM_UPDATE method */ + #define UpdateAttrs(o, gi, flags, attrs...) \ + ({ \ + Class *_cl; \ + ULONG _attrs[] = { attrs }; \ + ULONG _msg[] = { OM_UPDATE, (ULONG)_attrs, (ULONG)gi, flags }; \ + ASSERT_VALIDNO0(o) \ + _cl = OCLASS(o); \ + ASSERT_VALIDNO0(_cl) \ + ASSERT_VALID(gi) \ + ((_HookPtr)_cl->cl_Dispatcher.h_Entry) ((APTR)_cl, (APTR)o, (APTR)_msg); \ + }) +#endif + +/* Nobody else needs this anymore... */ +#undef _HookPtr + +#endif /* !BOOPSISTUBS_H */ diff --git a/CompilerSpecific.h b/CompilerSpecific.h new file mode 100644 index 0000000..6041ece --- /dev/null +++ b/CompilerSpecific.h @@ -0,0 +1,304 @@ +#ifndef COMPILERSPECIFIC_H +#define COMPILERSPECIFIC_H +/* +** $VER: CompilerSpecific.h 2.3 (26.10.97) +** +** Copyright (C) 1997 Bernardo Innocenti. All rights reserved. +** +** Compiler specific definitions is here. You can add support +** for other compilers in this header. Please return any changes +** you make to me, so I can add them to my personal copy of this file. +** +** Here is a short description of the macros defined below: +** +** LIBCALL +** Shared library entry point, with register args +** +** HOOKCALL +** Hook or boopsi dispatcher entry point with arguments +** passed in registers +** +** GLOBALCALL +** Attribute for functions to be exported to other modules for +** global access within the same executable file. +** Usually defined to "extern", but can be overridden for special +** needs, such as compiling all modules together in a single +** object module to optimize code better. +** +** XDEF +** Attribute for symbols to be exported to other modules for +** global access within the same executable file. +** Usually defined to an empty value. +** +** XREF +** Attribute for symbols to be imported from other modules +** within the same executable file. +** Usually defined to "extern". +** +** INLINE +** Please put function body inline to the calling code +** +** STDARGS +** Function uses standard C conventions for arguments +** +** ASMCALL +** Function takes arguments in the specified 68K registers +** +** REGCALL +** Function takes arguments in registers choosen by the compiler +** +** CONSTCALL +** Function does not modify any global variable +** +** FORMATCALL(archetype,string_index,first_to_check) +** Function uses printf or scanf-like formatting +** +** SAVEDS +** Function needs to reload context for small data model +** +** INTERRUPT +** Function will be called from within an interrupt +** +** NORETURN +** Function does never return +** +** ALIGNED +** Variable must be aligned to longword boundaries +** +** CHIP +** Variable must be stored in CHIP RAM +** +** REG(reg,arg) +** Put argument in 68K register +** +** min(a,b) +** Return the minimum between and +** +** max(a,b) +** Return the maximum between and +** +** abs(a) +** Return the absolute value of +** +** _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 +#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 + #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 +#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 + + #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 . + * Passing "const char *" parameters will only work if the OS protos are + * patched accordingly, otherwise you will get a lot of compiler warnings + * for const to volatile conversions. + * + * Using "const" where it is appropriate helps the compiler optimizing + * code better, so this mess is probably worth it. I wish one day the OS + * headers will come with support for the const keyword. + */ +#ifndef _CONST_STRPTR_DEFINED +typedef char *CONST_STRPTR; +#endif + + +/* Special function attributes */ + +#define LIBCALL ASMCALL SAVEDS +#define HOOKCALL ASMCALL SAVEDS +#ifdef __cplusplus + #define GLOBALCALL extern "C" +#else + #define GLOBALCALL +#endif + +/* special variable attributes */ +#define XDEF +#define XREF extern + + +/* AROS Compatibility: IPTR is a type which can store a pointer + * as well as a long integer. + */ +#ifndef IPTR +#define IPTR LONG +#endif /* IPTR */ + + +#endif /* !COMPILERSPECIFIC_H */ diff --git a/Debug.h b/Debug.h new file mode 100644 index 0000000..ba7b825 --- /dev/null +++ b/Debug.h @@ -0,0 +1,103 @@ +#ifndef DEBUG_H +#define DEBUG_H +/* +** $VER: Debug.h 2.2 (19.9.98) +** +** Copyright (C) 1995,96,97 Bernardo Innocenti. All rights reserved. +** +** Use 4 chars wide TABs to read this file +** +** Some handy debug macros which are automatically excluded when the +** DEBUG preprocessor sysmbol is not defined. To make debug executables, +** link with debug.lib or any module containing the kprintf() function. +** +** Here is a short description of the macros defined below: +** +** ILLEGAL +** Output an inline "ILLEGAL" 68K opcode, which will +** be interpreted as a breakpoint by most debuggers. +** +** DBPRINTF +** Output a formatted string to the debug console. This +** macro uses the debug.lib kprintf() function by default. +** +** ASSERT(x) +** Do nothing if the expression evalutates to a +** non-zero value, output a debug message otherwise. +** +** ASSERT_VALID(x) +** Checks if the expression 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 points to a valid +** memory location, and outputs a debug message +** otherwise. A NULL pointer is considered INVALID. +** +** DB(x) +** Compile the expression when making a debug +** executable, leave it out otherwise. +*/ + +#ifdef DEBUG + + /* Needed for TypeOfMem() */ + #ifndef PROTO_EXEC_H + #include + #endif /* PROTO_EXEC_H */ + + #if defined(__SASC) + + extern void __builtin_emit (int); + // #define ILLEGAL __builtin_emit(0x4AFC) + #define ILLEGAL 0 + STDARGS extern void kprintf (const char *, ...); + + #elif defined(__GNUC__) + + /* Currently, there is no kprintf() in libamiga.a */ + #define kprintf printf + + /* GCC doesn't accept asm statemnts in the middle of an + * expression such as `a ? b : asm("something")'. + */ + #define ILLEGAL illegal() + static inline int illegal(void) { asm ("illegal"); return 0; } + extern void STDARGS FORMATCALL(printf,1,2) kprintf (const char *, ...); + + #else + #error Please add compiler specific definitions for your compiler + #endif + + #if defined(__SASC) || defined (__GNUC__) + + /* common definitions for ASSERT and DB macros */ + + #define DBPRINTF kprintf + + #define ASSERT(x) ( (x) ? 0 : \ + ( DBPRINTF ("\x07%s, %ld: assertion failed: " #x "\n", \ + __FILE__, __LINE__) , ILLEGAL ) ); + + #define ASSERT_VALID(x) ( ((((APTR)(x)) == NULL) || \ + (((LONG)(x) > 1024) && TypeOfMem ((APTR)(x)))) ? 0 : \ + ( DBPRINTF ("\x07%s, %ld: bad address: " #x " = $%lx\n", \ + __FILE__, __LINE__, (APTR)(x)) , ILLEGAL ) ); + + #define ASSERT_VALIDNO0(x) ( (((LONG)(x) > 1024) && \ + TypeOfMem ((APTR)(x))) ? 0 : \ + ( DBPRINTF ("\x07%s, %ld: bad address: " #x " = $%lx\n", \ + __FILE__, __LINE__, (APTR)(x)) , ILLEGAL ) ); + + #define DB(x) x + #endif + +#else + #define ASSERT_VALID(x) + #define ASSERT_VALIDNO0(x) + #define ASSERT(x) + #define DB(x) +#endif /* DEBUG */ + +#endif /* !DEBUG_H */ diff --git a/GNUmakefile b/GNUmakefile new file mode 100644 index 0000000..9b8bd82 --- /dev/null +++ b/GNUmakefile @@ -0,0 +1,92 @@ +## +## $VER: LVDemo_Makefile 2.1 (5.9.97) +## +## Copyright (C) 1996,97 by Bernardo Innocenti +## +## Makefile for GCC +## + +########################################################### +# Name of the main executable +########################################################### +# +PROJ = LVDemo + + +########################################################### +# Package configuration +########################################################### +# + +# set to OS30_ONLY to leave out support for old V37 +# set to ANY_OS to make an executable for V37 with V39 support +# +OSVER = ANY_OS + +# cpu to compile for. +# +CPU = 68020 + + +########################################################### +# Object files in this project +########################################################### +# +OBJS = startup_gcc.o LVDemo.o ListViewHooks.o \ + ListViewClass.o ListBoxClass.o ScrollButtonClass.o + + +########################################################### +# Make the project +########################################################### +# +all: $(PROJ) + + +########################################################### +# Remove all targets and intermediate files +########################################################### +# +clean: + -Delete $(PROJ) $(OBJS) + + +########################################################### +# Dependences +########################################################### +# +LVDemo.c ListViewClass.c: ListViewClass.h + + +########################################################### +# GCC Release version should be compiled with these flags +########################################################### +# +CC = gcc +CFLAGS = -c -O0 -finline-functions -fno-implement-inlines \ + -m$(CPU) -msmall-code -mregparm -fomit-frame-pointer \ + -I/gg/include -I/include -Wunused -Wreturn-type -D$(OSVER) +LFLAGS = -s +LIBS = -noixemul -nostdlib + + +########################################################### +# GCC - Make the executable +########################################################### +# + +# Assemble startup code +# +startup_gcc.o: startup_gcc.s + $(AS) startup_gcc.s -o startup_gcc.o + +# Compile C sources and make the object files +# +.c.o: PIPClass.h + $(CC) $(*).c $(CFLAGS) + +# Link object files and make the executable +# +$(PROJ): $(OBJS) + $(CC) $(OBJS) -o $(PROJ) $(LFLAGS) $(LIBS) + @Protect $(PROJ) +e diff --git a/GST.c b/GST.c new file mode 100644 index 0000000..260f8e9 --- /dev/null +++ b/GST.c @@ -0,0 +1,45 @@ +/* +** GST.c +** +** Copyright (C) 1997 by Bernardo Innocenti +** +** This is a dummy source file used to make the Global Symbol Table +** for LVDemo. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "CompilerSpecific.h" +#include "Debug.h" + +/* Can't include these in GST because they are defining some inline functions + * #include "BoopsiStubs.h" + * #include "ListMacros.h" + */ + +#define LV_GADTOOLS_STUFF +#include "ScrollButtonClass.h" +#include "ListViewClass.h" +#include "VectorGlyphIClass.h" +#include "ListBoxClass.h" diff --git a/LVDemo.c b/LVDemo.c new file mode 100644 index 0000000..e6900e2 --- /dev/null +++ b/LVDemo.c @@ -0,0 +1,1043 @@ +/* $VER: ListViewDemo 1.6 (15.12.98) by Bernardo Innocenti +** +** +** Introduction +** ============ +** +** This program demonstrates how to use the `boopsi' ListView gadget. +** +** The source code shows how to create a resizable window with sliders +** and how to write a custom `boopsi' class on top of the gadgetclass. +** +** +** Compiling +** ========= +** +** This project can be compiled with SAS/C 6.58 or better and +** GeekGadget's GCC 2.7.2.1 or better. +** You get the smallest executable with SAS/C. GCC will give +** you quite a lot of warnings with the tag calls. Don't worry about them. +** +** +** History +** ======= +** +** 0.1 (23.6.96) First try +** 1.0 (21.1.97) First alpha release +** 1.1 (24.5.97) Lotsa bugs fixed, implemented LVA_DoubleClick +** 1.2 (31.8.97) Lotsa bugs fixed, implemented LVA_Clipped +** Fixed memory leak with the test list +** 1.3 (5.9.97) Improved initial sliders notification +** Added multiple selection (LVA_DoMultiselect) +** Fixed scrolling problems in some unusual conditions +** 1.4 (8.9.97) Sets GMORE_SCROLLRASTER in OM_NEW +** Multiple demo windows showing the features of the class +** 1.5 (14.9.97) Added LVA_ItemSpacing +** Finally fixed the LVA_Clipped mode! +** +** 1.6 (2.10.97) Added StormC support +** Implemented pixel-wise vertical scrolling for clipped mode +** Reworked the class istance data and some code +** +** 1.7 (15.12.98) Added ListBoxClass +** Moved ScrollButtonClass in its own file +** Removed function OpenClass() +** Updated to latest version of VectorGlyphIClass +** +** +** Known Bugs +** ========== +** +** - This code has never been tested on V37. +** +** - Middle mouse button scrolling does not work because +** of a bug in input.device V40. +** +** +** Copyright Notice +** ================ +** +** Copyright © 1996,97 by Bernardo Innocenti . +** 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 +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 */ + +#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 ." +}; + +#define TESTSTRINGS_CNT (sizeof (TestStrings) / sizeof (CONST_STRPTR)) + + + +LONG SAVEDS _main (void) + +/* Main program entry point. When linking without startup code, this + * must be the first function in the first object module listed on the + * linker command line. We also need to initialize SysBase and open + * all needed libraries manually. + */ +{ + struct MinList winlist; + struct MsgPort *winport; + struct Library *VectorGlyphBase = NULL; + LONG sigwait, sigrcvd; + LONG retval = RETURN_FAIL; /* = RETURN_FAIL */ + BOOL quit = FALSE; + + + /* Initialize SysBase */ + SysBase = *((struct ExecBase **)4UL); + + /* Open system libraries */ + + if ((UtilityBase = (UTILITYBASETYPE *) OpenLibrary ("utility.library", 37L)) && + (IntuitionBase = (struct IntuitionBase *)OpenLibrary ("intuition.library", WANTEDLIBVER)) && + (GfxBase = (struct GfxBase *)OpenLibrary ("graphics.library", WANTEDLIBVER)) && + (LayersBase = OpenLibrary ("layers.library", WANTEDLIBVER))) + { + if ((ListViewClass = MakeListViewClass ()) && + (ListBoxClass = MakeListBoxClass ()) && + (ScrollButtonClass = MakeScrollButtonClass ()) && + (VectorGlyphBase = OpenLibrary ("images/vectorglyph.image", 0))) + { + NEWLIST ((struct List *)&winlist); + + if (winport = OpenDemoWindows ((struct List *)&winlist)) + { + /* Pre-calculate the signal mask for Wait() */ + sigwait = (1 << winport->mp_SigBit) | + SIGBREAKF_CTRL_C; + + /* Now for the main loop. As you can see, it is really + * very compact. That's the magic of boopsi! :-) + */ + while (!quit) + { + /* Sleep until something interesting occurs */ + sigrcvd = Wait (sigwait); + + /* Now handle received signals */ + + /* Break signal? */ + if (sigrcvd & SIGBREAKF_CTRL_C) + quit = TRUE; + + /* IDCMP message? */ + if (sigrcvd & (1 << winport->mp_SigBit)) + { + struct IntuiMessage *msg; + + while (msg = (struct IntuiMessage *) GetMsg (winport)) + { + switch (msg->Class) + { + case IDCMP_CLOSEWINDOW: + quit = TRUE; + break; + + default: + break; + } + ReplyMsg ((struct Message *) msg); + } + } + } /* End while (!quit) */ + + retval = 0; /* RETURN_OK */ + + CloseDemoWindows ((struct List *)&winlist); + } + + FreeImages (); + } + + /* These cannot fail. Passing NULL is ok. */ + CloseLibrary ((struct Library *)VectorGlyphBase); + FreeScrollButtonClass (ScrollButtonClass); + FreeListBoxClass (ListBoxClass); + FreeListViewClass (ListViewClass); + } + + /* Passing NULL to CloseLibrary() was illegal in pre-V37 Exec. + * To avoid crashing when someone attempts to run this program + * on an old OS, we need to test the first library base we tried + * to open. + */ + if (UtilityBase) + { + CloseLibrary ((struct Library *)LayersBase); + CloseLibrary ((struct Library *)GfxBase); + CloseLibrary ((struct Library *)IntuitionBase); + CloseLibrary ((struct Library *)UtilityBase); + } + + return retval; +} + + + +static struct MsgPort *OpenDemoWindows (struct List *winlist) +{ + struct LVHandle *lvhandle; + struct MsgPort *winport; + + if (DiskfontBase = OpenLibrary ("diskfont.library", 0L)) + { + static struct TextAttr attr = + { + "times.font", + 24, + FSB_ITALIC, + 0 + }; + + CustomFont = OpenDiskFont (&attr); + CloseLibrary (DiskfontBase); + } + + /* Setup windows shared Message Port */ + if (winport = CreateMsgPort()) + { + if (lvhandle = OpenLVWin (NULL, winport, + "ListBox test", + LVA_StringList, TRUE, 600, 20, 320, 128, + TAG_DONE)) + ADDTAIL (winlist, (struct Node *)lvhandle); + + if (lvhandle = OpenLVWin (NULL, winport, + "LVA_TextFont = times/24/italic, LVA_Clipped = TRUE", + LVA_StringList, FALSE, 320, 320, 320, 64, + LVA_TextFont, CustomFont, + LVA_Clipped, TRUE, + TAG_DONE)) + ADDTAIL (winlist, (struct Node *)lvhandle); + + if (lvhandle = OpenLVWin (NULL, winport, + "LAYOUTA_Spacing = 4", + LVA_StringList, FALSE, 256, 256, 320, 64, + LAYOUTA_Spacing, 4, + TAG_DONE)) + ADDTAIL (winlist, (struct Node *)lvhandle); + + if (lvhandle = OpenLVWin (NULL, winport, + "GA_ReadOnly = TRUE; LVA_Selected = 3", + LVA_StringList, FALSE, 192, 192, 320, 64, + GA_ReadOnly, TRUE, + LVA_Selected, 3, + TAG_DONE)) + ADDTAIL (winlist, (struct Node *)lvhandle); + + if (lvhandle = OpenLVWin (NULL, winport, + "Single selection image list, LVA_Clipped = TRUE", + LVA_ImageList, FALSE, 128, 128, 320, 128, + LVA_ItemHeight, IMAGES_HEIGHT, + LVA_Clipped, TRUE, + TAG_DONE)) + ADDTAIL (winlist, (struct Node *)lvhandle); + + if (lvhandle = OpenLVWin (NULL, winport, + "LVA_DoMultiSelect = TRUE; LVA_StringArray", + LVA_StringArray, FALSE, 64, 64, 320, 128, + LVA_DoMultiSelect, TRUE, + TAG_DONE)) + ADDTAIL (winlist, (struct Node *)lvhandle); + + if (lvhandle = OpenLVWin (NULL, winport, + "Plain, single selection string list", + LVA_StringList, FALSE, 0, 20, 320, 128, + TAG_DONE)) + ADDTAIL (winlist, (struct Node *)lvhandle); + + /* Abort only if no windows could be opened */ + if (IsListEmpty (winlist)) + { + DeleteMsgPort (winport); + CloseFont (CustomFont); + return NULL; + } + } + + return winport; +} + + + +static void CloseDemoWindows (struct List *winlist) +{ + struct MsgPort *winport = NULL; + struct LVHandle *lvhandle; + + while (lvhandle = (struct LVHandle *) REMHEAD (winlist)) + { + /* Safe way to close a shared IDCMP port window */ + + Forbid(); + { + struct Node *succ; + struct Message *msg; + + winport = lvhandle->Win->UserPort; + msg = (struct Message *) winport->mp_MsgList.lh_Head; + + /* Now remove any pending message from the shared IDCMP port */ + while (succ = msg->mn_Node.ln_Succ) + { + /* Since we are closing all our windows at once, + * we don't need to check to which window this + * message was addressed. + */ + REMOVE ((struct Node *)msg); + ReplyMsg (msg); + msg = (struct Message *) succ; + } + + /* Keep intuition from freeing our port... */ + lvhandle->Win->UserPort = NULL; + + /* ...and from sending us any more messages. */ + ModifyIDCMP (lvhandle->Win, 0L); + } + Permit(); + + CloseLVWin (lvhandle); + } + + DeleteMsgPort (winport); /* NULL is ok */ + + if (CustomFont) + CloseFont (CustomFont); +} + + + +/* No-op backfilling hook. Since we are going to redraw the whole window + * anyway, we can disable backfilling. This avoids ugly flashing while + * resizing or revealing the window. + * + * This function does not need the __saveds attribute because it makes no + * references to external data. + */ + +#ifndef OS30_ONLY + +static ULONG BFHookFunc (void) +{ + return 1; /* Do nothing */ +} + +static struct Hook BFHook = +{ + NULL, NULL, + (ULONG(*)())BFHookFunc, +}; + +#endif /* !OS30_ONLY */ + + + +static struct LVHandle *OpenLVWin (CONST_STRPTR pubscreen, + struct MsgPort *winport, CONST_STRPTR title, ULONG mode, BOOL useListBox, + ULONG left, ULONG top, ULONG width, ULONG height, ULONG moreTags, ...) +{ + struct LVHandle *lvhandle; + struct Gadget *glist; + + if (lvhandle = AllocMem (sizeof (struct LVHandle), MEMF_ANY | MEMF_CLEAR)) + { + if (lvhandle->Scr = LockPubScreen (pubscreen)) + { + /* GetScreenDrawInfo() never fails */ + lvhandle->DrawInfo = GetScreenDrawInfo (lvhandle->Scr); + + /* Set listview operating mode and ListBox flag */ + lvhandle->Mode = mode; + lvhandle->UseListBox = useListBox; + + CreateItems (lvhandle); + + if (glist = CreateLVGadgets (lvhandle, (struct TagItem *)&moreTags)) + { + +/* I'm using this define because GCC does not support putting + * preprocessor directives (#ifdef/#endif) inside macro arguments. + */ +#ifndef OS30_ONLY + #define BACKFILL_TAG_VALUE ((IntuitionBase->LibNode.lib_Version < 39) ? &BFHook : LAYERS_NOBACKFILL) +#else + #define BACKFILL_TAG_VALUE LAYERS_NOBACKFILL +#endif /* !OS30_ONLY */ + + if (lvhandle->Win = OpenWindowTags (NULL, + WA_Top, top, + WA_Left, left, + WA_InnerWidth, width, + WA_InnerHeight, height, + WA_PubScreen, lvhandle->Scr, + WA_Gadgets, glist, + WA_Title, title, + WA_BackFill, useListBox ? NULL : BACKFILL_TAG_VALUE, + WA_ScreenTitle, versiontag + 6, + WA_Flags, WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_SIZEGADGET | WFLG_SIZEBRIGHT | WFLG_SIZEBBOTTOM | + WFLG_CLOSEGADGET | WFLG_SIMPLE_REFRESH | WFLG_NOCAREREFRESH, + WA_PubScreenFallBack, TRUE, + WA_AutoAdjust, TRUE, + WA_MinWidth, 64, + WA_MinHeight, 64, + WA_MaxWidth, -1, + WA_MaxHeight, -1, + TAG_DONE)) +#undef BACKFILL_TAG_VALUE + { + lvhandle->Win->UserPort = winport; + ModifyIDCMP (lvhandle->Win, IDCMP_CLOSEWINDOW); + + /* We need to keep our screen locked all the time + * because we want to free the associated DrawInfo + * *after* the window has been closed and + * FreeScreenDrawInfo() wants a pointer to a *valid* + * Screen. + */ + return lvhandle; + } + + DisposeGadgets (lvhandle); + } + + FreeScreenDrawInfo (lvhandle->Scr, lvhandle->DrawInfo); + /* lvhandle->DrawInfo = NULL */ + + UnlockPubScreen (NULL, lvhandle->Scr); + } + FreeMem (lvhandle, sizeof (struct LVHandle)); + } + return NULL; +} + + + +static void CloseLVWin (struct LVHandle *lvhandle) +{ + /* Close our window. No need to reply queued messages, + * Intuition is clever enough to care about this for us. + */ + CloseWindow (lvhandle->Win); + DisposeGadgets (lvhandle); + UnlockPubScreen (NULL, lvhandle->Scr); + + FreeVec (lvhandle->SelectArray); /* NULL is ok */ + + if ((lvhandle->Mode == LVA_StringList) || (lvhandle->Mode == LVA_ImageList)) + { + struct Node *node; + + /* Free the test list */ + while (node = REMHEAD (&lvhandle->TestList)) + { + if (lvhandle->Mode == LVA_ImageList) + DisposeObject ((Object *)node->ln_Name); + FreeMem (node, sizeof (struct Node)); + } + } + + FreeMem (lvhandle, sizeof (struct LVHandle)); +} + + + +/* Diagram of object interconnections for the ListView window + * ========================================================== + * + * ScrollButtonClass objects + * +----------+ +------------+ +------------+ +-------------+ + * | UpButton | | DownButton | | LeftButton | | RightButton | + * +----------+ +------------+ +------------+ +-------------+ + * | GA_ID = | GA_ID = | GA_ID = | GA_ID = + * | LVA_MoveUp | LVA_MoveDown | LVA_MoveLeft | LVA_MoveRight + * | | | | + * | +----------+ | | + * | | +-----------------------+ | + * | | | +------------------------------------+ + * | | | | propgclass object icclass object + * | | | | +-----------+ +--------------+ + * | | | | | HSlider |<-----| PIPToHSlider | + * | | | | +-----------+ +--------------+ + * | | | | PGA_Top = | ^ LVA_Top = PGA_Top + * | | | | LVA_Left | | LVA_Visible = PGA_Visible + * | | | | | | + * V V V V V | + * +-----------+ *********** | + * | |-------->* *------------+ + * | ListView | * Model * + * | |<--------* *------------+ + * +-----------+ *********** | + * ^ | + * PGA_Top = | | + * LVA_Top | V icclass object + * +-----------+ +--------------+ + * | VSlider |<-----| PIPToVSlider | + * +-----------+ +--------------+ + * propgclass object LVA_Top = PGA_Top + * LVA_Visible = PGA_Visible + */ + +static struct Gadget *CreateLVGadgets (struct LVHandle *lvhandle, + struct TagItem *moreTags) +{ + struct Screen *scr = lvhandle->Scr; + ULONG SizeWidth = 18, SizeHeight = 11; /* Default size */ + struct Image *SizeImage; + + /* Create a size image to get its... uhm... size */ + if (SizeImage = NewObject (NULL, SYSICLASS, + SYSIA_Which, SIZEIMAGE, + SYSIA_DrawInfo, lvhandle->DrawInfo, + TAG_DONE)) + { + /* Get size gadget geometry */ + GetAttr (IA_Width, SizeImage, &SizeWidth); + GetAttr (IA_Height, SizeImage, &SizeHeight); + + /* And then get rid of it... */ + DisposeObject (SizeImage); + } + + + if (lvhandle->UseListBox) + { + lvhandle->Gad[GAD_LV] = NewObject (ListBoxClass, NULL, + GA_ID, GAD_LV, + GA_Left, scr->WBorLeft, + GA_Top, scr->WBorTop + scr->Font->ta_YSize + 1, + GA_RelWidth, - SizeWidth - scr->WBorLeft, + GA_RelHeight, - (scr->WBorTop + scr->Font->ta_YSize + SizeHeight + 1), + GA_DrawInfo, lvhandle->DrawInfo, + lvhandle->Mode, lvhandle->Items, + LVA_Total, lvhandle->Total, + LVA_SelectArray, lvhandle->SelectArray, + TAG_MORE, moreTags); + + return lvhandle->Gad[GAD_LV]; + } + + /* Code to create normal ListView class and its gadgets */ + + + /* No need to check this: in case of failure we would just + * get no images in the scroll buttons, but we can still try + * to open our window. + */ + CreateImages (lvhandle->DrawInfo); + + if (lvhandle->Model = NewObjectA (NULL, MODELCLASS, NULL)) + if (lvhandle->Gad[GAD_LV] = NewObject (ListViewClass, NULL, + GA_ID, GAD_LV, + GA_Left, scr->WBorLeft, + GA_Top, scr->WBorTop + scr->Font->ta_YSize + 1, + GA_RelWidth, - SizeWidth - scr->WBorLeft, + GA_RelHeight, - (scr->WBorTop + scr->Font->ta_YSize + SizeHeight + 1), + GA_DrawInfo, lvhandle->DrawInfo, + ICA_TARGET, lvhandle->Model, + lvhandle->Mode, lvhandle->Items, + LVA_Total, lvhandle->Total, + LVA_SelectArray, lvhandle->SelectArray, + TAG_MORE, moreTags)) + if (lvhandle->Gad[GAD_VSLIDER] = NewObject (NULL, PROPGCLASS, + GA_ID, GAD_VSLIDER, + GA_Previous, lvhandle->Gad[GAD_LV], + GA_RelRight, - SizeWidth + 5, + GA_Top, scr->WBorTop + scr->Font->ta_YSize + 2, + GA_Width, SizeWidth - 8, + GA_RelHeight, - (scr->WBorTop + scr->Font->ta_YSize + + SizeHeight + ImgHeight[IMG_DOWN] + ImgHeight[IMG_UP] + 4), + GA_RightBorder, TRUE, + GA_DrawInfo, lvhandle->DrawInfo, + PGA_Freedom, FREEVERT, + PGA_Borderless, ((lvhandle->DrawInfo->dri_Flags & DRIF_NEWLOOK) && + (lvhandle->DrawInfo->dri_Depth != 1)), + PGA_NewLook, TRUE, + ICA_TARGET, lvhandle->Model, + ICA_MAP, MapVSliderToLV, + TAG_DONE)) + if (lvhandle->Gad[GAD_HSLIDER] = NewObject (NULL, PROPGCLASS, + GA_ID, GAD_HSLIDER, + GA_Previous, lvhandle->Gad[GAD_VSLIDER], + GA_RelBottom, - SizeHeight + ((SizeHeight > 15) ? 4 : 3), + GA_Left, scr->WBorLeft, + GA_Height, SizeHeight - ((SizeHeight > 15) ? 6 : 4), + GA_RelWidth, - (SizeWidth + ImgWidth[IMG_RIGHT] + ImgWidth[IMG_LEFT] + scr->WBorLeft + 2), + GA_BottomBorder,TRUE, + GA_DrawInfo, lvhandle->DrawInfo, + PGA_Freedom, FREEHORIZ, + PGA_Borderless, ((lvhandle->DrawInfo->dri_Flags & DRIF_NEWLOOK) && + (lvhandle->DrawInfo->dri_Depth != 1)), + PGA_NewLook, TRUE, + ICA_TARGET, lvhandle->Model, + ICA_MAP, MapHSliderToLV, + TAG_DONE)) + if (lvhandle->Gad[GAD_UPBUTTON] = NewObject (ScrollButtonClass, NULL, + GA_ID, GAD_UPBUTTON, + GA_Previous, lvhandle->Gad[GAD_HSLIDER], + GA_RelBottom, - SizeHeight - ImgHeight[IMG_DOWN] - ImgHeight[IMG_UP] + 1, + GA_RelRight, - ImgWidth[IMG_DOWN] + 1, + GA_RightBorder, TRUE, + GA_DrawInfo, lvhandle->DrawInfo, + GA_Image, Img[IMG_UP], + ICA_TARGET, lvhandle->Gad[GAD_LV], + ICA_MAP, MapUpButtonToLV, + TAG_DONE)) + if (lvhandle->Gad[GAD_DOWNBUTTON] = NewObject (ScrollButtonClass, NULL, + GA_ID, GAD_DOWNBUTTON, + GA_Previous, lvhandle->Gad[GAD_UPBUTTON], + GA_RelBottom, - SizeHeight - ImgHeight[IMG_DOWN] + 1, + GA_RelRight, - ImgWidth[IMG_DOWN] + 1, + GA_RightBorder, TRUE, + GA_DrawInfo, lvhandle->DrawInfo, + GA_Image, Img[IMG_DOWN], + ICA_TARGET, lvhandle->Gad[GAD_LV], + ICA_MAP, MapDownButtonToLV, + TAG_DONE)) + if (lvhandle->Gad[GAD_LEFTBUTTON] = NewObject (ScrollButtonClass, NULL, + GA_ID, GAD_LEFTBUTTON, + GA_Previous, lvhandle->Gad[GAD_DOWNBUTTON], + GA_RelBottom, - ImgHeight[IMG_LEFT] + 1, + GA_RelRight, - SizeWidth - ImgWidth[IMG_RIGHT] - ImgWidth[IMG_LEFT] + 1, + GA_BottomBorder,TRUE, + GA_DrawInfo, lvhandle->DrawInfo, + GA_Image, Img[IMG_LEFT], + ICA_TARGET, lvhandle->Gad[GAD_LV], + ICA_MAP, MapLeftButtonToLV, + TAG_DONE)) + if (lvhandle->Gad[GAD_RIGHTBUTTON] = NewObject (ScrollButtonClass, NULL, + GA_ID, GAD_RIGHTBUTTON, + GA_Previous, lvhandle->Gad[GAD_LEFTBUTTON], + GA_RelBottom, - ImgHeight[IMG_RIGHT] + 1, + GA_RelRight, - SizeWidth - ImgWidth[IMG_RIGHT] + 1, + GA_BottomBorder,TRUE, + GA_DrawInfo, lvhandle->DrawInfo, + GA_Image, Img[IMG_RIGHT], + ICA_TARGET, lvhandle->Gad[GAD_LV], + ICA_MAP, MapRightButtonToLV, + TAG_DONE)) + { + APTR icobject; + + /* Connect VSlider to Model */ + + if (icobject = NewObject (NULL, ICCLASS, + ICA_TARGET, lvhandle->Gad[GAD_VSLIDER], + ICA_MAP, MapLVToVSlider, + TAG_DONE)) + if (!DoMethod (lvhandle->Model, OM_ADDMEMBER, icobject)) + DisposeObject (icobject); + + /* Connect HSlider to Model */ + + if (icobject = NewObject (NULL, ICCLASS, + ICA_TARGET, lvhandle->Gad[GAD_HSLIDER], + ICA_MAP, MapLVToHSlider, + TAG_DONE)) + if (!DoMethod (lvhandle->Model, OM_ADDMEMBER, icobject)) + DisposeObject (icobject); + + /* Connect Model to ListView */ + + SetAttrs (lvhandle->Model, + ICA_TARGET, lvhandle->Gad[GAD_LV], + TAG_DONE); + + return lvhandle->Gad[GAD_LV]; + } + DisposeGadgets (lvhandle); + + return NULL; +} + + + +static void DisposeGadgets (struct LVHandle *lvhandle) +{ + ULONG i; + + for (i = 0; i < GAD_COUNT; i++) + { + DisposeObject (lvhandle->Gad[i]); + /* lvhandle->Gad[i] = NULL; */ + } + + /* Freeing the Model will also free its two targets */ + DisposeObject (lvhandle->Model); + /* lvhandle->Model = NULL */ +} + + + +static void CreateItems (struct LVHandle *lvhandle) +{ + if ((lvhandle->Mode == LVA_StringList) || (lvhandle->Mode == LVA_ImageList)) + { + struct Node *node; + ULONG i, cnt; + + + if (lvhandle->Mode == LVA_StringList) + cnt = TESTSTRINGS_CNT; + else /* LVA_ImageList */ + cnt = VG_IMGCOUNT * 8; + + + /* Build a list of nodes to test the list */ + + NEWLIST (&lvhandle->TestList); + + for (i = 0; i < cnt; i++) + { + if (node = AllocMem (sizeof (struct Node), MEMF_PUBLIC)) + { + if (lvhandle->Mode == LVA_StringList) + node->ln_Name = TestStrings[i]; + else + node->ln_Name = (STRPTR) NewObject (NULL, VECTORGLYPHCLASS, + SYSIA_Which, i % VG_IMGCOUNT, + SYSIA_DrawInfo, lvhandle->DrawInfo, + IA_Width, IMAGES_WIDTH, + IA_Height, IMAGES_HEIGHT, + TAG_DONE); + + /* Unselect all items */ + node->ln_Type = 0; + + ADDTAIL (&lvhandle->TestList, node); + + lvhandle->Total++; + } + } + + lvhandle->Items = &lvhandle->TestList; + } + else if (lvhandle->Mode == LVA_StringArray) + { + lvhandle->Items = TestStrings; + lvhandle->Total = TESTSTRINGS_CNT; + lvhandle->SelectArray = AllocVec (TESTSTRINGS_CNT * sizeof (ULONG), + MEMF_CLEAR | MEMF_PUBLIC); + } + else /* (lvhandle->Mode == LVA_ImageArray) */ + { + lvhandle->Items = NULL; /* No items */ + lvhandle->Total = -1; /* Unknown */ + } +} + + + +static void CreateImages (struct DrawInfo *dri) + +/* Create 4 arrow images for the window scroll buttons. + * + * Why bother checking for failure? The arrow images are not + * life critical in our program... + */ +{ + static ULONG imagetypes[IMG_COUNT] = { UPIMAGE, DOWNIMAGE, LEFTIMAGE, RIGHTIMAGE }; + ULONG i; + + for (i = 0; i < IMG_COUNT; i++) + if (!Img[i]) + if (Img[i] = (struct Image *)NewObject (NULL, SYSICLASS, + SYSIA_Which, imagetypes[i], + SYSIA_DrawInfo, dri, + TAG_DONE)) + { + /* Ask image width and height */ + GetAttr (IA_Width, Img[i], &ImgWidth[i]); + GetAttr (IA_Height, Img[i], &ImgHeight[i]); + } +} + + + +static void FreeImages (void) +{ + ULONG i; + + for (i = 0; i < IMG_COUNT; i++) + DisposeObject ((APTR)Img[i]); /* DisposeObject(NULL) is safe */ +} diff --git "a/LVDemo.\266" "b/LVDemo.\266" new file mode 100644 index 0000000..d3914ce --- /dev/null +++ "b/LVDemo.\266" @@ -0,0 +1,147 @@ +Storm Shell Project (0010) +Settings (Start) +C/C++ Environment +"StormC:include" +"Include:" +Includepath (End) +0 "LVDemo.StormHeader" 128 +0 "objects" +C/C++ Preprozessor +1 "_INLINE_INCLUDES" "" +Defines (End) +1 1 1 +C/C++ Options +0 1 1 0 0 0 0 0 0 1 1 0 1 +C/C++ Optimizer +9 +C/C++ Warnings +1 1 1 1 1 1 0 1 +Assembler +0 "" +0 0 +Linker +0 2 "startup_storm.o" 1 0 1 0 1 1 0 +"StormC:lib" 0 "StormC:lib/logfile" 0 1 1 +0 "_WizardSurface" +0 0 50 0 50 0 50 0 0 +0 0 0 0 0 0 0 0 +Run +16 "" "" "" 0 +"" + +1 "CON://400/180/Storm Console/AUTO/WAIT/SCREEN StormScreen" "RAM:Output" "RAM:Input" +1 0 +0 0 0 "" "" +Settings (End) +File +1 "LVDemo.c" +"LVDemo.c" +"CompilerSpecific.h" +"Debug.h" +"BoopsiStubs.h" +"ListMacros.h" +"ListViewClass.h" +"ListBoxClass.h" +"VectorGlyphIClass.h" +Storm Shell Project (Dependencies) +"LVDemo.o" "LVDemo.debug" +"" +"LVDemo.o" "LVDemo.debug" +File +1 "ListViewClass.c" +"ListViewClass.c" +"CompilerSpecific.h" +"Debug.h" +"BoopsiStubs.h" +"ListViewClass.h" +Storm Shell Project (Dependencies) +"ListViewClass.o" "ListViewClass.debug" +"" +"ListViewClass.o" "ListViewClass.debug" +File +1 "ListViewHooks.c" +"ListViewHooks.c" +"CompilerSpecific.h" +"Debug.h" +"ListViewClass.h" +Storm Shell Project (Dependencies) +"ListViewHooks.o" "ListViewHooks.debug" +"" +"ListViewHooks.o" "ListViewHooks.debug" +File +1 "ListBoxClass.c" +"ListBoxClass.c" +"CompilerSpecific.h" +"Debug.h" +"BoopsiStubs.h" +"ListViewClass.h" +"ListBoxClass.h" +Storm Shell Project (Dependencies) +"ListBoxClass.o" "ListBoxClass.debug" +"" +"ListBoxClass.o" "ListBoxClass.debug" +Section +1 1 100 +File +2 "Work:SC/src/Include/CompilerSpecific.h" +"Work:SC/src/Include/CompilerSpecific.h" +Storm Shell Project (Dependencies) +"" "" +"" +File +2 "Work:SC/src/Include/Debug.h" +"Work:SC/src/Include/Debug.h" +Storm Shell Project (Dependencies) +"" "" +"" +File +2 "Work:SC/src/Include/BoopsiStubs.h" +"Work:SC/src/Include/BoopsiStubs.h" +Storm Shell Project (Dependencies) +"" "" +"" +File +2 "ListViewClass.h" +"ListViewClass.h" +Storm Shell Project (Dependencies) +"" "" +"" +File +2 "ListBoxClass.h" +"ListBoxClass.h" +Storm Shell Project (Dependencies) +"" "" +"" +File +2 "Work:SC/src/Include/ListMacros.h" +"Work:SC/src/Include/ListMacros.h" +Storm Shell Project (Dependencies) +"" "" +"" +File +2 "VectorGlyphIClass.h" +"VectorGlyphIClass.h" +Storm Shell Project (Dependencies) +"" "" +"" +Section +2 1 95 +File +12 "startup_storm.s" +"startup_storm.s" +Storm Shell Project (Dependencies) +"startup_storm.o" "" +"" +"startup_storm.o" +Section +12 1 90 +File +10 "LVDemo" +"LVDemo" +Storm Shell Project (Dependencies) +"" "" +"" +"LVDemo.link" +Section +10 1 40 +Storm Shell Project (End) diff --git a/ListBoxClass.c b/ListBoxClass.c new file mode 100644 index 0000000..6c88ee3 --- /dev/null +++ b/ListBoxClass.c @@ -0,0 +1,770 @@ +/* +** ListBoxClass.c +** +** Copyright (C) 1997,98 Bernardo Innocenti +** +** Use 4 chars wide TABs to read this file +** +** GadTools-like `boopsi' ListView group class +*/ + +#define USE_BUILTIN_MATH +#define INTUI_V36_NAMES_ONLY +#define __USE_SYSBASE +#define CLIB_ALIB_PROTOS_H /* Avoid dupe defs of boopsi funcs */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef __STORM__ + #pragma header +#endif + +#include "CompilerSpecific.h" +#include "Debug.h" +#include "BoopsiStubs.h" + +#define LV_GADTOOLS_STUFF +#include "ScrollButtonClass.h" +#include "ListViewClass.h" +#include "VectorGlyphIClass.h" +#include "ListBoxClass.h" + + + +#define VSLIDER_WIDTH 14 +#define HSLIDER_HEIGHT 14 + + +/* Per-object instance data */ +struct LBData +{ + /* struct Gadget *ThisGadget; (not used) */ + + /* Group children */ + Object *ListView; + Object *HSlider; + Object *VSlider; + Object *UpButton; + Object *DownButton; + Object *LeftButton; + Object *RightButton; + Object *LVToVSliderIC; + Object *LVToHSliderIC; + + Object *Model; /* The ic object that makes our children talk to each other */ + Object *Frame; /* The frame to put around the listbox object */ + + struct Image *UpImage; + struct Image *DownImage; + struct Image *LeftImage; + struct Image *RightImage; + + /* Frame size */ + LONG FrameWidth, FrameHeight; + + /* These two have the same meaning, but we keep both updated + * because the Rectangle structure (MinX, MinY, MaxX, MaxY) + * is more handy in some cases, while the IBox structure + * (Left/Top/Width/Height) is best for other cases. + */ + struct IBox GBox; + struct Rectangle GRect; +}; + + +/* Global class data */ +struct LBClassData +{ + Class *ScrollButtonClass; + struct Library *VectorGlyphBase; +}; + + +extern Class *ListViewClass; + + +/* Local function prototypes */ +static void LB_GMRender (Class *cl, struct Gadget *g, struct gpRender *msg); +static void LB_GMLayout (Class *cl, struct Gadget *g, struct gpLayout *msg); +static ULONG LB_OMSet (Class *cl, struct Gadget *g, struct opUpdate *msg); +static ULONG LB_OMGet (Class *cl, struct Gadget *g, struct opGet *msg); +static ULONG LB_OMNew (Class *cl, struct Gadget *g, struct opSet *msg); +static void LB_OMDispose (Class *cl, struct Gadget *g, Msg msg); + +static void CreateVSlider (Class *cl, struct Gadget *g, struct LBData *lb, struct DrawInfo *dri); +static void CreateHSlider (Class *cl, struct Gadget *g, struct LBData *lb, struct DrawInfo *dri); +static void DeleteVSlider (struct Gadget *g, struct LBData *lb); +static void DeleteHSlider (struct Gadget *g, struct LBData *lb); + +INLINE void GetGadgetBox (struct GadgetInfo *ginfo, struct ExtGadget *g, struct IBox *box, struct Rectangle *rect); + + + +/* Attribute translations for object interconnections */ + +static LONG MapLVToHSlider[] = +{ + LVA_PixelLeft, PGA_Top, + LVA_PixelWidth, PGA_Total, + LVA_PixelHVisible, PGA_Visible, + TAG_DONE +}; + +static LONG MapHSliderToLV[] = +{ + PGA_Top, LVA_PixelLeft, + TAG_DONE +}; + +/* +static LONG MapLVToVSlider[] = +{ + LVA_Top, PGA_Top, + LVA_Total, PGA_Total, + LVA_Visible, PGA_Visible, + TAG_DONE +}; +*/ + +static LONG MapLVToVSlider[] = +{ + LVA_PixelTop, PGA_Top, + LVA_PixelHeight, PGA_Total, + LVA_PixelVVisible, PGA_Visible, + TAG_DONE +}; + + +/* +static LONG MapVSliderToLV[] = +{ + PGA_Top, LVA_Top, + TAG_DONE +}; +*/ + +static LONG MapVSliderToLV[] = +{ + PGA_Top, LVA_PixelTop, + TAG_DONE +}; + + + +static LONG MapUpButtonToLV[] = +{ + GA_ID, LVA_MoveUp, + TAG_DONE +}; + +static LONG MapDownButtonToLV[] = +{ + GA_ID, LVA_MoveDown, + TAG_DONE +}; + +static LONG MapLeftButtonToLV[] = +{ + GA_ID, LVA_MoveLeft, + TAG_DONE +}; + +static LONG MapRightButtonToLV[] = +{ + GA_ID, LVA_MoveRight, + TAG_DONE +}; + + + +static ULONG HOOKCALL LBDispatcher ( + REG(a0, Class *cl), + REG(a2, struct Gadget *g), + REG(a1, Msg msg)) +{ + ASSERT_VALIDNO0(cl) + ASSERT_VALIDNO0(g) + ASSERT_VALIDNO0(msg) + + switch (msg->MethodID) + { + case GM_RENDER: + LB_GMRender (cl, g, (struct gpRender *)msg); + return TRUE; + + case GM_LAYOUT: + /* This method is only supported on V39 and above */ + LB_GMLayout (cl, g, (struct gpLayout *)msg); + return TRUE; + + case OM_SET: + case OM_UPDATE: + return LB_OMSet (cl, g, (struct opUpdate *)msg); + + case OM_GET: + return LB_OMGet (cl, g, (struct opGet *)msg); + + case OM_NEW: + return LB_OMNew (cl, g, (struct opSet *)msg); + + case OM_DISPOSE: + LB_OMDispose (cl, g, msg); + return TRUE; + + default: + /* Unsupported method: let our superclass's dispatcher take + * a look at it. This includes all gadget methods sent + * by Intuition: GM_RENDER, GM_HANDLEINPUT, GM_GOACTIVE and + * GM_GOINACTIVE. These methods are automatically forwarded + * to our child gadgets by the groupgclass. + */ + /* DB(kprintf("ListBoxClass: passing unknown method 0x%lx to superclass...\n", msg->MethodID);) + */ + return DoSuperMethodA (cl, (Object *)g, msg); + } +} + + + +static void LB_GMRender (Class *cl, struct Gadget *g, struct gpRender *msg) +{ + struct LBData *lb = INST_DATA (cl, g); + + ASSERT_VALIDNO0(lb) + + +#ifndef OS30_ONLY + /* Pre-V39 Intuition won't call our GM_LAYOUT method, so we must + * always call it before redrawing the gadget. + */ + if ((IntuitionBase->LibNode.lib_Version < 39) && + (msg->gpr_Redraw == GREDRAW_REDRAW)) + LB_GMLayout (cl, g, (struct gpLayout *)msg); +#endif /* !OS30_ONLY */ + + + /* The groupgclass does not render its imagery, + * it only calls GM_RENDER for all its children + */ + if (msg->gpr_Redraw == GREDRAW_REDRAW) + { + DB (kprintf ("ListBoxClass: GM_RENDER: msg->gpr_Redraw = GREDRAW_REDRAW\n");) + + DoMethod (lb->Frame, IM_DRAWFRAME, + msg->gpr_RPort, /* imp_RPort */ + (lb->GBox.Left << 16) | (lb->GBox.Top), /* imp_Offset */ + IDS_NORMAL, /* imp_State */ + msg->gpr_GInfo->gi_DrInfo, /* imp_DrInfo */ + (lb->GBox.Width << 16) | (lb->GBox.Height));/* imp_Dimensions */ + } + + /* Forward message to superclass so it will call it on all our children */ + DoSuperMethodA (cl, (Object *)g, (Msg)msg); +} + + + +static void LB_GMLayout (Class *cl, struct Gadget *g, struct gpLayout *msg) +{ + struct LBData *lb = (struct LBData *) INST_DATA (cl, (Object *)g); + + DB (kprintf ("ListBoxClass: GM_LAYOUT\n");) + ASSERT_VALIDNO0(lb) + + + /* Collect new group size */ + GetGadgetBox (msg->gpl_GInfo, (struct ExtGadget *)g, &lb->GBox, &lb->GRect); + + /* Size our children accordingly */ + SetAttrs (lb->ListView, + // msg->gpl_GInfo->gi_Window, msg->gpl_GInfo->gi_Requester, + GA_Left, lb->GBox.Left + 2, // +lb->FrameWidth, + GA_Top, lb->GBox.Top + 2, // +lb->FrameHeight, + GA_Width, lb->GBox.Width - lb->FrameWidth - VSLIDER_WIDTH, + GA_Height, lb->GBox.Height - HSLIDER_HEIGHT, // -lb->FrameHeight, + TAG_DONE); + + SetAttrs (lb->VSlider, + // msg->gpl_GInfo->gi_Window, msg->gpl_GInfo->gi_Requester, + GA_Left, lb->GRect.MaxX - 2 - VSLIDER_WIDTH, // lb->FrameWidth, + GA_Top, lb->GBox.Top + 2, // lb->FrameHeight, + GA_Width, VSLIDER_WIDTH, + GA_Height, lb->GBox.Height - HSLIDER_HEIGHT * 3, // g->Height - 10, + TAG_DONE); + + SetAttrs (lb->UpButton, + GA_Left, lb->GRect.MaxX - 2 - VSLIDER_WIDTH, // lb->FrameWidth, + GA_Top, lb->GRect.MaxY - 2 - HSLIDER_HEIGHT * 2,// lb->FrameHeight, + GA_Width, VSLIDER_WIDTH, + GA_Height, HSLIDER_HEIGHT, + TAG_DONE); + + DB (kprintf ("ListBoxClass: GM_LAYOUT: upbutton left: %ld, upbutton top: %ld\n", + lb->GRect.MaxX - 2 - VSLIDER_WIDTH, lb->GRect.MaxY - 2 - HSLIDER_HEIGHT * 2);) + + + SetAttrs (lb->DownButton, + GA_Left, lb->GRect.MaxX - 2 - VSLIDER_WIDTH, // lb->FrameWidth, + GA_Top, lb->GRect.MaxY - 2 - HSLIDER_HEIGHT, // lb->FrameHeight, + GA_Width, VSLIDER_WIDTH, + GA_Height, HSLIDER_HEIGHT, + TAG_DONE); + + + /* NOTE: it seems that the groupgclass does not forward GM_LAYOUT + * to its children, so we must handle this here. + */ + + /* Forward GM_LAYOUT to embedded listview */ + DoMethodA (lb->ListView, (Msg)msg); +} + + + +static ULONG LB_OMSet (Class *cl, struct Gadget *g, struct opUpdate *msg) +{ + struct LBData *lb = (struct LBData *) INST_DATA (cl, (Object *)g); + + DB (kprintf ("ListBoxClass: OM_SET\n");) + ASSERT_VALIDNO0(lb) + + if (lb->ListView) + /* Forward attributes to our listview */ + DoMethodA (lb->ListView, (Msg)msg); + + /* Also forward to our superclass */ + return DoSuperMethodA (cl, (Object *)g, (Msg) msg); +} + + + +static ULONG LB_OMGet (Class *cl, struct Gadget *g, struct opGet *msg) +{ + struct LBData *lb = (struct LBData *) INST_DATA (cl, (Object *)g); + + DB (kprintf ("ListBoxClass: OM_GET\n");) + ASSERT_VALIDNO0(lb) + + /* Forward this method to our listview */ + return DoMethodA (lb->ListView, (Msg)msg); +} + + + +static ULONG LB_OMNew (Class *cl, struct Gadget *g, struct opSet *msg) +{ + struct LBData *lb; + struct DrawInfo *dri; + + + DB (kprintf ("ListBoxClass: OM_NEW:\n");) + + if (g = (struct Gadget *)DoSuperMethodA (cl, (Object *)g, (Msg)msg)) + { + /* Set the GMORE_SCROLLRASTER flag */ + if (g->Flags & GFLG_EXTENDED) + { + DB (kprintf ("ListBoxClass: OM_NEW: Setting GMORE_SCROLLRASTER\n");) + ((struct ExtGadget *)g)->MoreFlags |= GMORE_SCROLLRASTER; + } + + lb = (struct LBData *) INST_DATA (cl, (Object *)g); + ASSERT_VALIDNO0(lb) + + + /* Clear the object instance */ + memset (lb, 0, sizeof (struct LBData)); + + + /* Store a pointer to this object (a Gadget) in the class private + * instance. This way we can avoid passing it to all functions. + */ + /* lb->ThisGadget = g; (not used) */ + + + /* May be NULL */ + dri = (struct DrawInfo *) GetTagData (GA_DrawInfo, NULL, msg->ops_AttrList); + + + /* Create a model object. This will be the core of the boopsi attributes + * network used by the sub-objects to talk each other. We pass our + * initalization tags to the model so it will pick up the correct + * ICA_TARGET and ICA_MAP, if specified with NewObject(). + */ + if (lb->Model = NewObjectA (NULL, MODELCLASS, NULL)) + { + /* Create the ListView and pass all creation time attributes to it. + * Note that any GA_#? attributes are also passed to the listview, + * so it will have the same size of its container. + */ + if (lb->ListView = NewObjectA (ListViewClass, NULL, msg->ops_AttrList)) + { + /* From now no, the groupgclass will dispose this object for us */ + DoMethod ((Object *)g, OM_ADDMEMBER, lb->ListView); + + //SetAttrs (lb->Model, + // ICA_TARGET, lb->ListView, + // TAG_DONE); + + /* Connect Model to ListView */ + { + APTR icobject; + + if (icobject = NewObject (NULL, ICCLASS, + ICA_TARGET, lb->ListView, + TAG_DONE)) + if (!DoMethod (lb->Model, OM_ADDMEMBER, icobject)) + DisposeObject (icobject); + } + + /* Connect ListView to Model */ + SetAttrs (lb->ListView, + ICA_TARGET, lb->Model, + TAG_DONE); + + /* Add sliders */ + CreateVSlider (cl, g, lb, dri); + CreateHSlider (cl, g, lb, dri); + + + /* Create a frame to put around us */ + + if (lb->Frame = NewObject (NULL, FRAMEICLASS, + IA_EdgesOnly, TRUE, + IA_FrameType, FRAME_BUTTON, + TAG_DONE)) + { + struct IBox FrameBox, ContentsBox = { 0, 0, 0, 0 }; + + /* Ask the frame about its nominal frame width and height */ + DoMethod ((Object *)lb->Frame, IM_FRAMEBOX, &ContentsBox, &FrameBox, dri, 0); + + /* Remember it later */ + lb->FrameWidth = FrameBox.Width; + lb->FrameHeight = FrameBox.Height; + } + + + /* Set the gadget width and height because the groupgclass + * always forces them to 0 on creation. + */ + { + struct TagItem *tag; + + if (tag = FindTagItem (GA_RelWidth, msg->ops_AttrList)) + SetAttrs (g, + GA_RelWidth, tag->ti_Data, + TAG_DONE); + else + SetAttrs (g, + GA_Width, GetTagData (GA_Width, g->Width, msg->ops_AttrList), + TAG_DONE); + + if (tag = FindTagItem (GA_RelHeight, msg->ops_AttrList)) + SetAttrs (g, + GA_RelHeight, tag->ti_Data, + TAG_DONE); + else + SetAttrs (g, + GA_Height, GetTagData (GA_Height, g->Height, msg->ops_AttrList), + TAG_DONE); + + DB (kprintf ("ListBoxClass: OM_NEW: set size to L=%ld T=%ld W=%ld H=%ld\n", + g->LeftEdge, g->TopEdge, g->Width, g->Height);) + } + + + /* TODO: Handle creation-time attributes */ + + return (ULONG)g; /* Return newly created istance */ + } + } + + + /* Dispose object without disturbing the dispatchers of our sub-classes, if any */ + CoerceMethod (cl, (Object *)g, OM_DISPOSE); + } + + return 0; /* Fail */ +} + + + +static void LB_OMDispose (Class *cl, struct Gadget *g, Msg msg) +{ + struct LBData *lb = (struct LBData *) INST_DATA (cl, (Object *)g); + + ASSERT_VALIDNO0(lb) + DB (kprintf ("ListBoxClass: OM_DISPOSE\n");) + + DeleteHSlider(g, lb); + DeleteVSlider(g, lb); + + /* Dispose our subobjects which are not freed automatically */ + DisposeObject (lb->Frame); + DisposeObject (lb->Model); + + /* Our superclass will cleanup everything else now */ + DoSuperMethodA (cl, (Object *)g, (Msg) msg); + + /* From now on, our instance data is no longer available */ +} + + + +#define SCROLLBUTTON_CLASS_PTR ( ((struct LBClassData *)(cl->cl_UserData))->ScrollButtonClass ) + + +static void CreateVSlider (Class *cl, struct Gadget *g, struct LBData *lb, struct DrawInfo *dri) +{ + if (lb->VSlider = NewObject (NULL, PROPGCLASS, + GA_ID, g->GadgetID, /* Same as our ID */ + GA_DrawInfo, dri, + PGA_Freedom, FREEVERT, + PGA_NewLook, TRUE, + PGA_Borderless, TRUE, + ICA_TARGET, lb->Model, + ICA_MAP, MapVSliderToLV, + TAG_DONE)) + { + /* From now on, the groupgclass will dispose this object for us */ + DoMethod ((Object *)g, OM_ADDMEMBER, lb->VSlider); + + /* Connect Model to ListView */ + SetAttrs (lb->Model, + ICA_TARGET, lb->ListView, + TAG_DONE); + + /* Connect VSlider to Model */ + if (lb->LVToVSliderIC = NewObject (NULL, ICCLASS, + ICA_TARGET, lb->VSlider, + ICA_MAP, MapLVToVSlider, + TAG_DONE)) + if (!DoMethod (lb->Model, OM_ADDMEMBER, lb->LVToVSliderIC)) + DisposeObject (lb->LVToVSliderIC); + } + + /* We won't bother checking for failure because the image is + * not life-critical in our object + */ + lb->UpImage = NewObject (NULL, SYSICLASS, + SYSIA_Which, UPIMAGE, + SYSIA_DrawInfo, dri, + TAG_DONE); + + if (lb->UpButton = NewObject (SCROLLBUTTON_CLASS_PTR, NULL, + GA_ID, g->GadgetID, + GA_DrawInfo, dri, + GA_Image, lb->UpImage, + ICA_TARGET, lb->ListView, + ICA_MAP, MapUpButtonToLV, + TAG_DONE)) + { + /* From now on, the groupgclass will dispose this object for us */ + DoMethod ((Object *)g, OM_ADDMEMBER, lb->UpButton); + } + + /* We won't bother checking for failure because the image is + * not life-critical in our object + */ + lb->DownImage = NewObject (NULL, SYSICLASS, + SYSIA_Which, DOWNIMAGE, + SYSIA_DrawInfo, dri, + TAG_DONE); + + if (lb->DownButton = NewObject (SCROLLBUTTON_CLASS_PTR, NULL, + GA_ID, g->GadgetID, + GA_DrawInfo, dri, + GA_Image, lb->DownImage, + ICA_TARGET, lb->ListView, + ICA_MAP, MapDownButtonToLV, + TAG_DONE)) + { + /* From now on, the groupgclass will dispose this object for us */ + DoMethod ((Object *)g, OM_ADDMEMBER, lb->DownButton); + } +} + + + +static void CreateHSlider (Class *cl, struct Gadget *g, struct LBData *lb, struct DrawInfo *dri) +{ + /* TODO */ +} + + +static void DeleteVSlider (struct Gadget *g, struct LBData *lb) +{ + if (lb->DownButton) + { + ASSERT_VALID(lb->DownButton) + + DoMethod ((Object *)g, OM_REMMEMBER, lb->DownButton); + DisposeObject (lb->DownButton); + lb->DownButton = NULL; + } + + if (lb->DownImage) + { + ASSERT_VALID(lb->DownImage) + + DisposeObject (lb->DownImage); + lb->DownImage = NULL; + } + + if (lb->UpButton) + { + ASSERT_VALID(lb->UpButton) + + DoMethod ((Object *)g, OM_REMMEMBER, lb->UpButton); + DisposeObject (lb->UpButton); + lb->UpButton = NULL; + } + + if (lb->UpImage) + { + ASSERT_VALID(lb->UpImage) + + DisposeObject (lb->UpImage); + lb->UpImage = NULL; + } + + if (lb->LVToVSliderIC) + { + ASSERT_VALID(lb->LVToVSliderIC) + ASSERT_VALID(lb->Model) + + DoMethod (lb->Model, OM_REMMEMBER, lb->LVToVSliderIC); + DisposeObject (lb->LVToVSliderIC); + lb->LVToVSliderIC = NULL; + } + + if (lb->VSlider) + { + ASSERT_VALID(lb->VSlider) + + DoMethod ((Object *)g, OM_REMMEMBER, lb->VSlider); + DisposeObject (lb->VSlider); + lb->VSlider = NULL; + } +} + + + +static void DeleteHSlider (struct Gadget *g, struct LBData *lb) +{ + /* TODO */ +} + + + +INLINE void GetGadgetBox (struct GadgetInfo *ginfo, struct ExtGadget *g, struct IBox *box, struct Rectangle *rect) + +/* Gets the actual IBox where a gadget exists in a window. + * The special cases it handles are all the REL#? (relative positioning flags). + * + * This function returns the gadget size in both the provided IBox and + * Rectangle structures, computing the values from the coordinates of the + * gadget and the window where it lives. + */ +{ + ASSERT_VALIDNO0(g) + ASSERT_VALIDNO0(ginfo) + ASSERT_VALIDNO0(box) + ASSERT_VALIDNO0(rect) + + DB (if (g->Flags & GFLG_EXTENDED) + kprintf ("ListBoxClass: GetGadgetBox(): GFLG_EXTENDED is set\n");) + + DB (if ((g->Flags & GFLG_EXTENDED) && (g->MoreFlags & GMORE_BOUNDS)) + kprintf ("ListBoxClass: GetGadgetBox(): Gadget has valid bounds\n");) + + box->Left = g->LeftEdge; + if (g->Flags & GFLG_RELRIGHT) + box->Left += ginfo->gi_Domain.Width - 1; + + box->Top = g->TopEdge; + if (g->Flags & GFLG_RELBOTTOM) + box->Top += ginfo->gi_Domain.Height - 1; + + box->Width = g->Width; + if (g->Flags & GFLG_RELWIDTH) + box->Width += ginfo->gi_Domain.Width; + + box->Height = g->Height; + if (g->Flags & GFLG_RELHEIGHT) + box->Height += ginfo->gi_Domain.Height; + + /* Convert IBox to Rectangle coordinates system */ + rect->MinX = box->Left; + rect->MinY = box->Top; + rect->MaxX = box->Left + box->Width - 1; + rect->MaxY = box->Top + box->Height - 1; + + + DB (kprintf ("ListBoxClass: GetGadgetBox(): Left = %ld, Top = %ld, Width = %ld, Height = %ld\n", + box->Left, box->Top, box->Width, box->Height);) +} + + + +Class *MakeListBoxClass (void) +{ + Class *class; + struct LBClassData *classdata; + + if (class = MakeClass (NULL, GROUPGCLASS, NULL, sizeof (struct LBData), 0)) + { + class->cl_Dispatcher.h_Entry = (ULONG (*)()) LBDispatcher; + + /* Allocate storage for global class data */ + if (classdata = AllocMem (sizeof (struct LBClassData), MEMF_PUBLIC | MEMF_CLEAR)) + { + class->cl_UserData = (ULONG) classdata; + + classdata->ScrollButtonClass = MakeScrollButtonClass(); + classdata->VectorGlyphBase = OpenLibrary ("images/vectorglyph.image", 0); + return class; + } + + FreeListBoxClass (class); + } + + return NULL; +} + + + +void FreeListBoxClass (Class *class) +{ + struct LBClassData *classdata; + + if (class) + { + ASSERT_VALID(class) + if (classdata = (struct LBClassData *)class->cl_UserData) + { + ASSERT_VALID(classdata) + ASSERT_VALID(classdata->ScrollButtonClass) + ASSERT_VALID(classdata->VectorGlyphBase) + + /* Cleanup global class data */ + CloseLibrary (classdata->VectorGlyphBase); /* NULL is safe since V36 */ + FreeScrollButtonClass (classdata->ScrollButtonClass); + FreeMem (classdata, sizeof (struct LBClassData)); + } + + FreeClass (class); + } +} diff --git a/ListBoxClass.h b/ListBoxClass.h new file mode 100644 index 0000000..0d65bb8 --- /dev/null +++ b/ListBoxClass.h @@ -0,0 +1,36 @@ +#ifndef LISTBOXCLASS_H +#define LISTBOXCLASS_H +/* +** ListBoxClass.h +** +** Copyright (C) 1997 by Bernardo Innocenti +** +** ListBox class built on top of the "groupgclass". +** +*/ + +#define LISTBOXCLASS "listboxclass" +#define LISTBOXVERS 1 + + +Class *MakeListBoxClass (void); +void FreeListBoxClass (Class *ListViewClass); + + + +/*****************/ +/* Class Methods */ +/*****************/ + +/* This class does not define any new methods */ + +/********************/ +/* Class Attributes */ +/********************/ + +/* #define LBA_Dummy (TAG_USER | ('L'<<16) | ('B'<<8)) */ + +/* This class does not define any new attributes */ + + +#endif /* !LISTBOXCLASS_H */ diff --git a/ListMacros.h b/ListMacros.h new file mode 100644 index 0000000..4b7d808 --- /dev/null +++ b/ListMacros.h @@ -0,0 +1,98 @@ +#ifndef LISTMACROS_H +#define LISTMACROS_H +/* +** $VER: ListMacros.h 2.1 (1.9.97) +** +** Copyright (C) 1996,97 Bernardo Innocenti. All rights reserved. +** +** Use 4 chars wide TABs to read this source +** +** Some handy macros for list operations. Using these macros is faster +** than calling their exec.library equivalents, but they will eventually +** make your code a little bigger and are also subject to common macro +** side effects. +*/ + + +#define NEWLIST(l) ( (l)->lh_TailPred = (struct Node *)(l), \ + (l)->lh_Tail = 0, \ + (l)->lh_Head = (struct Node *)(&((l)->lh_Tail)) ) + +#define ADDHEAD(l,n) ( (n)->ln_Pred = (struct Node *)(l), \ + (n)->ln_Succ = (l)->lh_Head, \ + (l)->lh_Head->ln_Pred = (n), \ + (l)->lh_Head = (n) ) + +#define ADDTAIL(l,n) ( (n)->ln_Succ = (struct Node *)(&((l)->lh_Tail)), \ + (n)->ln_Pred = (l)->lh_TailPred, \ + (l)->lh_TailPred->ln_Succ = (n), \ + (l)->lh_TailPred = (n) ) + +#define REMOVE(n) ( (n)->ln_Succ->ln_Pred = (n)->ln_Pred, \ + (n)->ln_Pred->ln_Succ = (n)->ln_Succ ) + +#define GETHEAD(l) ( (l)->lh_Head->ln_Succ ? (l)->lh_Head : (struct Node *)NULL ) + +#define GETTAIL(l) ( (l)->lh_TailPred->ln_Succ ? (l)->lh_TailPred : (struct Node *)NULL ) + +#define GETSUCC(n) ( (n)->ln_Succ->ln_Succ ? (n)->ln_Succ : (struct Node *)NULL ) + +#define GETPRED(n) ( (n)->ln_Pred->ln_Pred ? (n)->ln_Pred : (struct Node *)NULL ) + + +#ifdef __GNUC__ + +#define REMHEAD(l) \ +({ \ + struct Node *n = (l)->lh_Head; \ + n->ln_Succ ? \ + (l)->lh_Head = n->ln_Succ, \ + (l)->lh_Head->ln_Pred = (struct Node *)(l), \ + n : \ + NULL; \ +}) + +#define REMTAIL(l) \ +({ \ + struct Node *n = (l)->lh_TailPred; \ + n->ln_Pred ? \ + (l)->lh_TailPred = n->ln_Pred, \ + (l)->lh_TailPred->ln_Succ = (struct Node *)(&((l)->lh_Tail)), \ + n : \ + NULL; \ +}) + + +#else + +/* These two can't be implemented as macros without the GCC ({...}) language extension */ + +INLINE struct Node *REMHEAD(struct List *l) +{ + struct Node *n = l->lh_Head; + + if (n->ln_Succ) + { + l->lh_Head = n->ln_Succ; + l->lh_Head->ln_Pred = (struct Node *)l; + return n; + } + return NULL; +} + +INLINE struct Node *REMTAIL(struct List *l) +{ + struct Node *n = l->lh_TailPred; + + if (n->ln_Pred) + { + l->lh_TailPred = n->ln_Pred; + l->lh_TailPred->ln_Succ = (struct Node *)(&(l->lh_Tail)); + return n; + } + return NULL; +} + +#endif + +#endif /* !LISTMACROS_H */ diff --git a/ListViewClass.c b/ListViewClass.c new file mode 100644 index 0000000..d4ceb17 --- /dev/null +++ b/ListViewClass.c @@ -0,0 +1,2181 @@ +/* ListViewClass.c +** +** Copyright (C) 1996,97 Bernardo Innocenti +** +** Use 4 chars wide TABs to read this file +** +** GadTools-like `boopsi' ListView gadget class +*/ + +#define USE_BUILTIN_MATH +#define INTUI_V36_NAMES_ONLY +#define __USE_SYSBASE +#define CLIB_ALIB_PROTOS_H /* Avoid dupe defs of boopsi funcs */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 */ + 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 to . No sanity checks are performed + * to ensure that all items between and 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 + */ + #define LayerCovered(l) \ + ((!(l)->ClipRect) || memcmp (&(l)->ClipRect->bounds, \ + &(l)->bounds, sizeof (struct Rectangle))) + #define LayerDamaged(l) \ + ((l)->DamageList && (l)->DamageList->RegionRectangle) + #define NeedZeroScrollRaster(l) (LayerCovered(l) || LayerDamaged(l)) + + + /* This will scroll the layer damage regions without actually + * scrolling the display, but only if our layer really needs it. + */ + if ((rp->Layer->Flags & LAYERSIMPLE) && NeedZeroScrollRaster (rp->Layer)) + { + UBYTE oldmask = rp->Mask; /* Would GetRPAttr() be better? */ + + DB (kprintf (" Calling ScrollRaster()\n");) +#ifdef OS30_ONLY + SetWriteMask (rp, 0); +#else + SafeSetWriteMask (rp, 0); +#endif /* OS30_ONLY */ + ScrollRaster (rp, 0, scroll_dy, + lv->GRect.MinX, lv->GRect.MinY, + lv->GRect.MaxX, + lv->GRect.MaxY); + +#ifdef OS30_ONLY + SetWriteMask (rp, oldmask); +#else + SafeSetWriteMask (rp, oldmask); +#endif /* OS30_ONLY */ + } + +#ifndef OS30_ONLY + if (GfxBase->LibNode.lib_Version >= 39) +#endif /* OS30_ONLY */ + /* Restore MaxPen in our RastPort */ + SetMaxPen (rp, -1); + } + + /* Update OldTop to the current Top item and + * OldPixelTop to the current PixelTop position. + */ + lv->OldTop = lv->Top; + lv->OldPixelTop = lv->PixelTop; + + break; + } + + default: + break; + } + + if (lv->ClipRegion) + { + ASSERT_VALIDNO0(oldregion) + /* Restore old clipping region in our layer */ + InstallClipRegion (rp->Layer, oldregion); + } + + if (oldfont) + SetFont (rp, oldfont); + } + else if (msg->gpr_Redraw == GREDRAW_REDRAW) + { + /* Clear all gadget contents */ + SetAPen (rp, msg->gpr_GInfo->gi_DrInfo->dri_Pens[BACKGROUNDPEN]); + RectFill (rp, lv->GRect.MinX, lv->GRect.MinY, lv->GRect.MaxX, lv->GRect.MaxY); + } +} + + + +static ULONG LV_GMGoActive (Class *cl, struct Gadget *g, struct gpInput *msg) +{ + struct LVData *lv = INST_DATA (cl, g); + + ASSERT_VALIDNO0(lv) + DB (kprintf ("GM_GOACTIVE: gpi_IEvent = $%lx\n", msg->gpi_IEvent);) + + + if (!lv->Items) + return GMR_NOREUSE; + + g->Flags |= GFLG_SELECTED; + + /* Do not process InputEvent when the gadget has been + * activated by ActivateGadget(). + */ + if (!msg->gpi_IEvent) + return GMR_MEACTIVE; + + /* Note: The input event that triggered the gadget + * activation (usually a mouse click) should be passed + * to the GM_HANDLEINPUT method, so we fall down to it. + */ + return LV_GMHandleInput (cl, g, msg); +} + + + +INLINE LONG ItemHit (struct LVData *lv, WORD x, WORD y) + +/* Determine which item has been hit with gadget relative + * coordinates x and y. + */ +{ + return ((y + lv->PixelTop) / (lv->ItemHeight + lv->Spacing)); +} + + + +static ULONG LV_GMHandleInput (Class *cl, struct Gadget *g, struct gpInput *msg) +{ + struct LVData *lv = INST_DATA (cl, g); + struct InputEvent *ie = msg->gpi_IEvent; + ULONG result = GMR_MEACTIVE; + + ASSERT_VALIDNO0(lv) +/* DB (kprintf ("GM_HANDLEINPUT: ie_Class = $%lx, ie->ie_Code = $%lx, " + "gpi_Mouse.X = %ld, gpi_Mouse.Y = %ld\n", + ie->ie_Class, ie->ie_Code, msg->gpi_Mouse.X, msg->gpi_Mouse.Y);) +*/ + switch (ie->ie_Class) + { + case IECLASS_RAWKEY: + { + LONG tags[5]; + LONG pos; + + switch (ie->ie_Code) + { + case CURSORUP: + if ((lv->Flags & LVF_READONLY) || (ie->ie_Qualifier & IEQUALIFIER_CONTROL)) + { + if (ie->ie_Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) + pos = lv->Top - lv->Visible / 2; + else + pos = lv->Top - 1; + + if (pos < 0) pos = 0; + + tags[0] = LVA_Top; + tags[1] = pos; + tags[2] = TAG_DONE; + } + else + { + if (ie->ie_Qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT)) + pos = 0; + else if (ie->ie_Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) + pos = lv->Selected - lv->Visible + 1; + else + pos = lv->Selected - 1; + + if (pos < 0) pos = 0; + + tags[0] = LVA_Selected; + tags[1] = pos; + tags[2] = LVA_MakeVisible; + tags[3] = pos; + tags[4] = TAG_DONE; + } + break; + + case CURSORDOWN: + if ((lv->Flags & LVF_READONLY) || (ie->ie_Qualifier & IEQUALIFIER_CONTROL)) + { + if (ie->ie_Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) + pos = lv->Top + lv->Visible / 2; + else + pos = lv->Top + 1; + + tags[0] = LVA_Top; + tags[1] = pos; + tags[2] = TAG_DONE; + } + else + { + if (ie->ie_Qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT)) + pos = lv->Total - 1; + else if (ie->ie_Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) + pos = lv->Selected + lv->Visible - 1; + else + pos = lv->Selected + 1; + + tags[0] = LVA_Selected; + tags[1] = pos; + tags[2] = LVA_MakeVisible; + tags[3] = pos; + tags[4] = TAG_DONE; + } + break; + + default: + tags[0] = TAG_DONE; + + } /* End switch (ie->ie_Code) */ + + if (tags[0] != TAG_DONE) + DoMethod ((Object *)g, OM_UPDATE, tags, msg->gpi_GInfo, + (ie->ie_Qualifier & IEQUALIFIERB_REPEAT) ? OPUF_INTERIM : 0); + + break; + } + + case IECLASS_RAWMOUSE: + { + LONG selected; + + switch (ie->ie_Code) + { + case SELECTDOWN: + + /* Check for click outside gadget box */ + + if ((msg->gpi_Mouse.X < 0) || + (msg->gpi_Mouse.X >= lv->GBox.Width) || + (msg->gpi_Mouse.Y < 0) || + (msg->gpi_Mouse.Y >= lv->GBox.Height)) + { + result = GMR_REUSE; + break; + } + + /* Start dragging mode */ + lv->Flags |= LVF_DRAGGING; + + if (lv->Flags & LVF_READONLY) + break; + + /* Select an item */ + selected = ItemHit (lv, msg->gpi_Mouse.X, msg->gpi_Mouse.Y); + + /* No action when selecting over blank space in the bottom */ + if ((selected < 0) || (selected >= lv->Total)) + break; + + /* Backup current selection for RMB undo */ + lv->BackupSelected = lv->Selected; + lv->BackupPixelTop = lv->PixelTop; + + if (selected == lv->Selected) + { + /* Check for double click */ + if (DoubleClick (lv->DoubleClickSecs, lv->DoubleClickMicros, + ie->ie_TimeStamp.tv_secs, ie->ie_TimeStamp.tv_micro)) + UpdateAttrs ((Object *)g, msg->gpi_GInfo, 0, + LVA_DoubleClick, selected, + TAG_DONE); + } + + if (lv->Flags & LVF_DOMULTISELECT) + /* Setup for multiple items drag selection */ + lv->DragSelect = IsItemSelected (lv, NULL, selected) ? + LVA_DeselectItem : LVA_SelectItem; + else if (g->Activation & GACT_TOGGLESELECT) + { + /* Setup for single item toggle */ + lv->DragSelect = LVA_Selected; + if (selected == lv->Selected) + selected = ~0; + } + else /* Single selection */ + /* Setup for single item drag selection */ + lv->DragSelect = LVA_Selected; + + UpdateAttrs ((Object *)g, msg->gpi_GInfo, 0, + lv->DragSelect, selected, + TAG_DONE); + + /* Save double click info */ + lv->DoubleClickSecs = ie->ie_TimeStamp.tv_secs; + lv->DoubleClickMicros = ie->ie_TimeStamp.tv_micro; + break; + + case MENUDOWN: + /* Undo selection & position when RMB is pressed */ + if (lv->Flags & (LVF_DRAGGING | LVF_SCROLLING)) + { + /* Stop dragging and scrolling modes */ + lv->Flags &= ~(LVF_DRAGGING | LVF_SCROLLING); + + if ((lv->BackupSelected != lv->Selected) || + (lv->BackupPixelTop != lv->PixelTop)) + { + UpdateAttrs ((Object *)g, msg->gpi_GInfo, 0, + (lv->Flags & LVF_READONLY) ? + TAG_IGNORE : LVA_Selected, lv->BackupSelected, + LVA_PixelTop, lv->BackupPixelTop, + TAG_DONE); + } + } + else + /* Deactivate gadget on menu button press */ + result = GMR_REUSE; + + break; + + case MIDDLEDOWN: + /* Argh, input.device never sends this event in V40! */ + DB (kprintf ("scrolling on\n");) + + /* Start MMB scrolling */ + lv->BackupPixelTop = lv->PixelTop; + lv->BackupSelected = lv->Selected; + lv->MiddleMouseY = msg->gpi_Mouse.Y; + lv->Flags |= LVF_DRAGGING; + break; + + case SELECTUP: + + /* Stop dragging mode */ + lv->Flags &= ~LVF_DRAGGING; + + if (g->Activation & GACT_RELVERIFY) + { + /* Send IDCMP_GADGETUP message to our parent window */ + msg->gpi_Termination = &lv->Selected; + result = GMR_NOREUSE | GMR_VERIFY; + } + break; + + case MIDDLEUP: + /* Argh, input.device never sends this event in V40! */ + DB (kprintf ("scrolling off\n");) + + /* Stop MMB scrolling */ + lv->Flags &= ~LVF_SCROLLING; + break; + + default: /* Mouse moved */ + + /* Holding LMB? */ + if (lv->Flags & LVF_DRAGGING) + { + /* Select an item */ + selected = ItemHit (lv, msg->gpi_Mouse.X, msg->gpi_Mouse.Y); + + /* Moved over another item inside the currently displayed list? */ + if ((selected != lv->Selected) && !(lv->Flags & LVF_READONLY) + && (selected >= lv->Top) && (selected < lv->Top + lv->Visible)) + { + /* Single selection */ + + /* Call our OM_UPDATE method to change the attributes. + * This will also send notification to targets and + * update the contents of the gadget. + */ + UpdateAttrs ((Object *)g, msg->gpi_GInfo, 0, + lv->DragSelect, selected, + TAG_DONE); + } + } + + /* Holding MMB? */ + if (lv->Flags & LVF_SCROLLING) + { + DB (kprintf (" scrolling\n");) + selected = (msg->gpi_Mouse.Y - lv->MiddleMouseY) + + lv->BackupPixelTop; + + UpdateAttrs ((Object *)g, msg->gpi_GInfo, 0, + LVA_PixelTop, selected < 0 ? 0 : selected, + TAG_DONE); + } + + } /* End switch (ie->ie_Code) */ + + break; + } + + case IECLASS_TIMER: + + /* Holding LMB? */ + if (lv->Flags & LVF_DRAGGING) + { + /* Mouse above the upper item? */ + if ((msg->gpi_Mouse.Y < 0) && lv->Top) + { + /* Scroll up */ + UpdateAttrs ((Object *)g, msg->gpi_GInfo, 0, + LVA_MoveUp, 1, + (lv->Flags & LVF_READONLY) ? TAG_IGNORE : LVA_Selected, lv->Top - 1, + TAG_DONE); + } + /* Mouse below the bottom item? */ + else if (msg->gpi_Mouse.Y / (lv->ItemHeight + lv->Spacing) >= lv->Visible) + { + /* Scroll down */ + UpdateAttrs ((Object *)g, msg->gpi_GInfo, 0, + LVA_MoveDown, 1, + (lv->Flags & LVF_READONLY) ? TAG_IGNORE : LVA_Selected, lv->Top + lv->Visible, + TAG_DONE); + } + } + break; + + default: + ; + + } /* End switch (ie->ie_Class) */ + + return result; +} + + + +static void LV_GMGoInactive (Class *cl, struct Gadget *g, struct gpGoInactive *msg) +{ + struct LVData *lv = INST_DATA (cl, g); + ASSERT_VALIDNO0(lv) + + DB (kprintf ("GM_GOINACTIVE\n");) + + /* Stop dragging and scrolling modes */ + lv->Flags &= ~(LVF_DRAGGING | LVF_SCROLLING); + + /* Mark gadget inactive */ + g->Flags &= ~GFLG_SELECTED; +} + + + +INLINE void GetGadgetBox (struct GadgetInfo *ginfo, struct ExtGadget *g, struct IBox *box, struct Rectangle *rect) + +/* Gets the actual IBox where a gadget exists in a window. + * The special cases it handles are all the REL#? (relative positioning flags). + * + * This function returns the gadget size in both the provided IBox and + * Rectangle structures, computing the values from the coordinates of the + * gadget and the window where it lives. + */ +{ + ASSERT_VALIDNO0(g) + ASSERT_VALIDNO0(ginfo) + ASSERT_VALIDNO0(box) + ASSERT_VALIDNO0(rect) + + DB (if ((g->Flags & GFLG_EXTENDED) && (g->MoreFlags & GMORE_BOUNDS)) + kprintf (" Gadget has valid bounds\n");) + + box->Left = g->LeftEdge; + if (g->Flags & GFLG_RELRIGHT) + box->Left += ginfo->gi_Domain.Width - 1; + + box->Top = g->TopEdge; + if (g->Flags & GFLG_RELBOTTOM) + box->Top += ginfo->gi_Domain.Height - 1; + + box->Width = g->Width; + if (g->Flags & GFLG_RELWIDTH) + box->Width += ginfo->gi_Domain.Width; + + box->Height = g->Height; + if (g->Flags & GFLG_RELHEIGHT) + box->Height += ginfo->gi_Domain.Height; + + /* Convert IBox to Rectangle coordinates system */ + rect->MinX = box->Left; + rect->MinY = box->Top; + rect->MaxX = box->Left + box->Width - 1; + rect->MaxY = box->Top + box->Height - 1; +} + + + +static void LV_GMLayout (Class *cl, struct Gadget *g, struct gpLayout *msg) +{ + struct LVData *lv = INST_DATA (cl, g); + LONG visible; + + DB (kprintf ("GM_LAYOUT\n");) + ASSERT_VALIDNO0(lv) + ASSERT_VALIDNO0(msg->gpl_GInfo) + ASSERT_VALIDNO0(msg->gpl_GInfo->gi_DrInfo) + ASSERT_VALIDNO0(msg->gpl_GInfo->gi_DrInfo->dri_Font) + + + /* We shouldn't draw inside the GM_LAYOUT method: the + * GM_REDRAW method will be called by Intuition shortly after. + */ + lv->Flags |= LVF_DONTDRAW; + + /* Collect new gadget size */ + GetGadgetBox (msg->gpl_GInfo, (struct ExtGadget *)g, &lv->GBox, &lv->GRect); + + /* Calculate clipping region for gadget LVA_Clipped mode */ + if (lv->ClipRegion) + { + /* Remove previous clipping rectangle, if any */ + ClearRegion (lv->ClipRegion); + + /* Install a clipping rectangle around the gadget box. + * We don't check for failure because we couldn't do + * anything otherwise. + */ + OrRectRegion (lv->ClipRegion, &lv->GRect); + } + + /* Setup Font if not yet done */ + if (!lv->Font) + { + lv->Font = msg->gpl_GInfo->gi_DrInfo->dri_Font; + if (!lv->ItemHeight) + lv->ItemHeight = lv->Font->tf_YSize; + } + + if (lv->ItemHeight) + { + if (lv->ClipRegion) + /* Allow displaying an incomplete item at the bottom of the listview, + * plus one incomplete item at the top. + */ + visible = (lv->GBox.Height + lv->ItemHeight + lv->Spacing - 1) / + (lv->ItemHeight + lv->Spacing); + else + /* get maximum number of items fitting in the listview height. + * Ignore spacing for the last visible item. + */ + visible = (lv->GBox.Height + lv->Spacing) / (lv->ItemHeight + lv->Spacing); + } + else + visible = 0; + + lv->MaxScroll = lv->GBox.Height / lv->ScrollRatio; + + + /* Send initial notification to our sliders, or update them to + * the new values. The slieders will get the correct size also + * in the special case where the list is attached at creation + * time and the sliders are attached later using a model object. + * + * The private class attribute LVA_Visible will handle everything for us. + */ + UpdateAttrs ((Object *)g, msg->gpl_GInfo, 0, + LVA_Visible, visible, + TAG_DONE); + + /* Re-enable drawing */ + lv->Flags &= ~LVF_DONTDRAW; +} + + + +static ULONG LV_OMSet (Class *cl, struct Gadget *g, struct opUpdate *msg) +{ + struct LVData *lv = INST_DATA (cl, g); + struct TagItem *ti, + *tstate = msg->opu_AttrList; + ULONG result; + UWORD action = 0; /* See flag definitions above */ + + ASSERT_VALIDNO0(lv) + ASSERT_VALID(tstate) + + DB (kprintf ((msg->MethodID == OM_SET) ? "OM_SET:\n" : "OM_UPDATE:\n");) + + + /* Definitions for the ations to be taken right after + * scanning the attributes list in OM_SET/OM_UPDATE. + * For speed reasons we pack them together in a single variable, + * so we can set and test multiple flags in once. + */ + #define LVF_DO_SUPER_METHOD (1<<0) + #define LVF_REDRAW (1<<1) + #define LVF_SCROLL (1<<2) + #define LVF_TOGGLESELECT (1<<3) + #define LVF_NOTIFY (1<<4) + #define LVF_NOTIFYALL (1<<5) + + + while (ti = NextTagItem (&tstate)) + switch (ti->ti_Tag) + { + case GA_ID: + DB (kprintf (" GA_ID, %ld\n", ti->ti_Data);) + + /* Avoid sending all taglists to our superclass because of GA_ID */ + g->GadgetID = ti->ti_Data; + break; + + case LVA_Selected: + DB (kprintf (" LVA_Selected, %ld\n", ti->ti_Data);) + + if (lv->Items) + { + LONG newselected = ti->ti_Data; + + if (newselected != ~0) + newselected = (newselected >= lv->Total) ? + (lv->Total - 1) : newselected; + + if (lv->Selected != newselected) + { + if (((lv->Selected >= lv->Top) && + (lv->Selected < lv->Top + lv->Visible)) || + ((newselected >= lv->Top) && + (newselected < lv->Top + lv->Visible))) + action |= LVF_TOGGLESELECT; + + lv->Selected = newselected; + + if (newselected == ~0) + lv->SelectedPtr = NULL; + else + lv->SelectedPtr = GetItem (lv, newselected); + + action |= LVF_NOTIFY; + } + } + break; + + case LVA_Top: + DB (kprintf (" LVA_Top, %ld\n", ti->ti_Data);) + + if ((lv->Top != ti->ti_Data) && lv->Items) + { + /* This will scroll the listview contents when needed */ + + lv->Top = ((ti->ti_Data + lv->Visible) >= lv->Total) ? + ((lv->Total <= lv->Visible) ? 0 : (lv->Total - lv->Visible)) + : ti->ti_Data; + lv->PixelTop = lv->Top * (lv->ItemHeight + lv->Spacing); + + /* TODO: optimize for some special cases: + * Top == oldtop + 1 and Top == oldtop - 1 + */ + lv->TopPtr = GetItem (lv, lv->Top); + action |= LVF_SCROLL | LVF_NOTIFY; + } + break; + + case LVA_Total: + DB (kprintf (" LVA_Total, %ld\n", ti->ti_Data);) + + /* We don't hhandle LVA_Total except when setting a new + * list or array of items. + */ + break; + + case LVA_SelectItem: + DB (kprintf (" LVA_SelectItem, %ld\n", ti->ti_Data);) + + /* Check LVA_MaxSelect */ + if (lv->SelectCount >= lv->MaxSelect) + DisplayBeep (msg->opu_GInfo ? msg->opu_GInfo->gi_Screen : NULL); + else if (lv->Items) + { + LONG newselected = (ti->ti_Data >= lv->Total) ? + (lv->Total - 1) : ti->ti_Data; + + if (((lv->Selected >= lv->Top) && + (lv->Selected < lv->Top + lv->Visible)) || + ((newselected >= lv->Top) && + (newselected < lv->Top + lv->Visible))) + action |= LVF_TOGGLESELECT; + + lv->Selected = newselected; + lv->SelectedPtr = GetItem (lv, newselected); + + if (!IsItemSelected (lv, lv->SelectedPtr, newselected)) + { + lv->SelectCount++; + + if (lv->SelectArray) + lv->SelectArray[newselected] = lv->SelectCount; + else if (lv->Flags & LVF_LIST) + ((struct Node *)lv->SelectedPtr)->ln_Type = lv->SelectCount; + } + action |= LVF_NOTIFY; + } + break; + + case LVA_DeselectItem: + DB (kprintf (" LVA_DeselectItem, %ld\n", ti->ti_Data);) + + if (lv->Items) + { + LONG newselected = (ti->ti_Data >= lv->Total) ? + (lv->Total - 1) : ti->ti_Data; + + if (((lv->Selected >= lv->Top) && + (lv->Selected < lv->Top + lv->Visible)) || + ((newselected >= lv->Top) && + (newselected < lv->Top + lv->Visible))) + action |= LVF_TOGGLESELECT; + + lv->Selected = newselected; + lv->SelectedPtr = GetItem (lv, newselected); + + if (IsItemSelected (lv, lv->SelectedPtr, newselected)) + { + lv->SelectCount--; + + if (lv->SelectArray) + lv->SelectArray[lv->Selected] = 0; + else if (lv->Flags & LVF_LIST) + ((struct Node *)lv->SelectedPtr)->ln_Type = 0; + + action |= LVF_NOTIFY; + } + } + break; + + case LVA_ToggleItem: + DB (kprintf (" LVA_ToggleItem, %ld\n", ti->ti_Data);) + + if (lv->Items) + { + LONG newselected = newselected = (ti->ti_Data >= lv->Total) ? + (lv->Total - 1) : ti->ti_Data; + + if (((lv->Selected >= lv->Top) && + (lv->Selected < lv->Top + lv->Visible)) || + ((newselected >= lv->Top) && + (newselected < lv->Top + lv->Visible))) + action |= LVF_TOGGLESELECT; + + lv->Selected = newselected; + lv->SelectedPtr = GetItem (lv, newselected); + + if (IsItemSelected (lv, lv->SelectedPtr, lv->Selected)) + { + /* Deselect */ + lv->SelectCount--; + + if (lv->SelectArray) + lv->SelectArray[lv->Selected] = 0; + else if (lv->Flags & LVF_LIST) + ((struct Node *)lv->SelectedPtr)->ln_Type = 0; + } + else + { + /* Check LVA_MaxSelect */ + if (lv->SelectCount >= lv->MaxSelect) + DisplayBeep (msg->opu_GInfo ? msg->opu_GInfo->gi_Screen : NULL); + else + { + /* Select */ + lv->SelectCount++; + + if (lv->SelectArray) + lv->SelectArray[lv->Selected] = lv->SelectCount; + else if (lv->Flags & LVF_LIST) + ((struct Node *)lv->SelectedPtr)->ln_Type = lv->SelectCount; + } + } + + action |= LVF_NOTIFY; + } + break; + + case LVA_ClearSelected: + DB (kprintf (" LVA_ClearSelected, %ld\n", ti->ti_Data);) + + if (lv->Items) + { + LONG newselected = ti->ti_Data; + LONG i; + + if (((lv->Selected >= lv->Top) && + (lv->Selected < lv->Top + lv->Visible)) || + ((newselected >= lv->Top) && + (newselected < lv->Top + lv->Visible))) + action |= LVF_TOGGLESELECT; + + lv->Selected = ~0; + lv->SelectedPtr = NULL; + lv->SelectCount = 0; + + + /* Clear the selections */ + + if (lv->SelectArray) + for (i = 0; i < lv->Total; i++) + lv->SelectArray[i] = 0; + else if (lv->Flags & LVF_LIST) + { + struct Node *node; + + for (node = ((struct List *)lv->Items)->lh_Head; + node = node->ln_Succ; + node->ln_Type = 0) + ASSERT_VALID(node); + } + + /* TODO: check if total redraw is really needed */ + action |= LVF_REDRAW | LVF_NOTIFY; + } + break; + + case LVA_MakeVisible: + { + LONG itemnum = ti->ti_Data; + + DB (kprintf (" LVA_MakeVisible, %ld\n", ti->ti_Data);) + + if (itemnum < 0) + itemnum = 0; + + if (itemnum >= lv->Total) + itemnum = lv->Total - 1; + + if (itemnum < lv->Top) + { + /* Scroll up */ + + lv->Top = itemnum; + lv->TopPtr = GetItem (lv, lv->Top); + action |= LVF_SCROLL | LVF_NOTIFY; + } + else if (itemnum >= lv->Top + lv->Visible) + { + /* Scroll down */ + + lv->Top = itemnum - lv->Visible + 1; + lv->TopPtr = GetItem (lv, lv->Top); + action |= LVF_SCROLL | LVF_NOTIFY; + } + break; + } + + case LVA_MoveUp: + DB (kprintf (" LVA_MoveUp, %ld\n", ti->ti_Data);) + + if ((lv->Top > 0) && lv->Items) + { + lv->Top--; + lv->TopPtr = GetPrev (lv, lv->TopPtr, lv->Top); + action |= LVF_SCROLL | LVF_NOTIFY; + } + break; + + case LVA_MoveDown: + DB (kprintf (" LVA_MoveDown, %ld\n", ti->ti_Data);) + + if ((lv->Top + lv->Visible < lv->Total) && lv->Items) + { + lv->Top++; + lv->TopPtr = GetNext (lv, lv->TopPtr, lv->Top); + action |= LVF_SCROLL | LVF_NOTIFY; + } + break; + + case LVA_MoveLeft: + DB (kprintf (" Unimplemented attr: LVA_MoveLeft\n");) + break; + + case LVA_MoveRight: + DB (kprintf (" Unimplemented attr: LVA_MoveRight\n");) + break; + + case LVA_StringList: + DB (kprintf (" LVA_StringList, $%lx\n", ti->ti_Data);) + + if (ti->ti_Data == ~0) + lv->Items = NULL; + else + { + ASSERT_VALID(ti->ti_Data) + + lv->Items = (void *) ti->ti_Data; + lv->GetItemFunc = ListGetItem; + lv->GetNextFunc = ListGetNext; + lv->GetPrevFunc = ListGetPrev; + lv->DrawItemFunc = ListStringDrawItem; + lv->Flags |= LVF_LIST; + + lv->Total = GetTagData (LVA_Total, ~0, msg->opu_AttrList); + if (lv->Total == ~0) + lv->Total = CountNodes (lv->Items); + + lv->SelectCount = CountSelections (lv); + + action |= LVF_REDRAW | LVF_NOTIFYALL; + } + break; + + case LVA_StringArray: + DB (kprintf (" LVA_StringArray, $%lx\n", ti->ti_Data);) + + if (ti->ti_Data == ~0) + lv->Items = NULL; + else + { + ASSERT_VALID(ti->ti_Data) + + lv->Items = (void *) ti->ti_Data; + lv->GetItemFunc = ArrayGetItem; + lv->GetNextFunc = ArrayGetItem; + lv->GetPrevFunc = ArrayGetItem; + lv->DrawItemFunc = StringDrawItem; + lv->Flags &= ~LVF_LIST; + + lv->Total = GetTagData (LVA_Total, ~0, msg->opu_AttrList); + if ((lv->Total == ~0) && lv->Items) + { + /* Count items */ + ULONG i = 0; + while (((APTR *)lv->Items)[i]) i++; + lv->Total = i; + } + + lv->SelectCount = CountSelections(lv); + + action |= LVF_REDRAW | LVF_NOTIFYALL; + } + break; + + case LVA_ImageList: + DB (kprintf (" LVA_ImageList, $%lx\n", ti->ti_Data);) + + if (ti->ti_Data == ~0) + lv->Items = NULL; + else + { + ASSERT_VALID(ti->ti_Data) + + lv->Items = (void *) ti->ti_Data; + lv->GetItemFunc = ListGetItem; + lv->GetNextFunc = ListGetNext; + lv->GetPrevFunc = ListGetPrev; + lv->DrawItemFunc = ListImageDrawItem; + lv->Flags |= LVF_LIST; + + lv->Total = GetTagData (LVA_Total, ~0, msg->opu_AttrList); + if (lv->Total == ~0) + lv->Total = CountNodes (lv->Items); + + lv->SelectCount = CountSelections(lv); + + action |= LVF_REDRAW | LVF_NOTIFYALL; + } + break; + + case LVA_ImageArray: + DB (kprintf (" LVA_ImageArray, $%lx\n", ti->ti_Data);) + + if (ti->ti_Data == ~0) + lv->Items = NULL; + else + { + ASSERT_VALID(ti->ti_Data) + + lv->Items = (void *) ti->ti_Data; + lv->GetItemFunc = ArrayGetItem; + lv->GetNextFunc = ArrayGetItem; + lv->GetPrevFunc = ArrayGetItem; + lv->DrawItemFunc = ImageDrawItem; + lv->Flags &= ~LVF_LIST; + + lv->Total = GetTagData (LVA_Total, ~0, msg->opu_AttrList); + if ((lv->Total == ~0) && lv->Items) + { + /* Count items */ + ULONG i = 0; + while (((APTR *)lv->Items)[i]) i++; + lv->Total = i; + } + + action |= LVF_REDRAW | LVF_NOTIFYALL; + } + break; + + case LVA_CustomList: + DB (kprintf (" LVA_CustomList, $%lx\n", ti->ti_Data);) + + if (ti->ti_Data == ~0) + lv->Items = NULL; + else + { + ASSERT_VALID(ti->ti_Data) + + lv->Items = (void *) ti->ti_Data; + lv->SelectCount = CountSelections (lv); + + action |= LVF_REDRAW | LVF_NOTIFYALL; + } + break; + + case LVA_Visible: + DB (kprintf (" LVA_Visible, %ld\n", ti->ti_Data);) + + /* This attribute can only be set internally, and will + * trigger a full slider notification. + */ + lv->Visible = ti->ti_Data; + action |= LVF_NOTIFYALL; + + + /* Also scroll the ListView if needed. */ + if (lv->ClipRegion) + { + LONG height = lv->Total * (lv->ItemHeight + lv->Spacing); + LONG newtop; + + if (lv->PixelTop + lv->GBox.Height >= height) + { + lv->PixelTop = height - lv->GBox.Height; + if (lv->PixelTop < 0) + lv->PixelTop = 0; + + newtop = lv->PixelTop / (lv->ItemHeight + lv->Spacing); + if (newtop != lv->Top) + { + lv->Top = newtop; + lv->TopPtr = GetItem (lv, newtop); + } + action |= LVF_SCROLL; + } + } + else if (lv->Top + lv->Visible >= lv->Total) + { + lv->Top = (lv->Total <= lv->Visible) ? 0 : (lv->Total - lv->Visible); + lv->TopPtr = GetItem (lv, lv->Top); + lv->PixelTop = lv->Top * (lv->ItemHeight + lv->Spacing); + action |= LVF_SCROLL; + } + break; + + case LVA_SelectArray: + DB (kprintf (" LVA_SelectArray, $%lx\n", ti->ti_Data);) + ASSERT_VALID(ti->ti_Data) + + lv->SelectArray = (ULONG *) ti->ti_Data; + lv->SelectCount = CountSelections (lv); + action |= LVF_REDRAW; + break; + + case LVA_MaxSelect: + DB (kprintf (" LVA_MaxSelect, %ld\n", ti->ti_Data);) + + lv->MaxSelect = ti->ti_Data; + /* NOTE: We are not checking lv->SelectCount */ + break; + + case LVA_PixelTop: /* Handle pixel-wise scrolling */ + DB (kprintf (" LVA_PixelTop, %ld\n", ti->ti_Data);) + + if (ti->ti_Data != lv->PixelTop && lv->Items && lv->ItemHeight) + { + LONG newtop; + + lv->PixelTop = ti->ti_Data; + action |= LVF_SCROLL; + + newtop = lv->PixelTop / (lv->ItemHeight + lv->Spacing); + newtop = ((newtop + lv->Visible) >= lv->Total) ? + ((lv->Total <= lv->Visible) ? 0 : (lv->Total - lv->Visible)) + : newtop; + + if (newtop != lv->Top) + { + /* TODO: optimize GetItem for some special cases: + * Top = oldtop + 1 and Top = oldtop - 1 + */ + lv->Top = newtop; + lv->TopPtr = GetItem (lv, newtop); + action |= LVF_NOTIFY | LVF_SCROLL; + } + } + break; + + case LVA_ScrollRatio: + DB (kprintf (" LVA_ScrollRatio, %ld\n", ti->ti_Data);) + ASSERT(ti->ti_Data != 0) + + lv->ScrollRatio = ti->ti_Data; + lv->MaxScroll = lv->GBox.Height / lv->ScrollRatio; + break; + + default: + DB (kprintf (" Passing unknown tag to superclass: $%lx, %ld\n", + ti->ti_Tag, ti->ti_Data);) + + /* This little optimization avoids forwarding the + * OM_SET method to our superclass then there are + * no unknown tags. + */ + action |= LVF_DO_SUPER_METHOD; + break; + } + + DB(kprintf (" TAG_DONE\n");) + + /* Forward method to our superclass dispatcher, only if needed */ + + if (action & LVF_DO_SUPER_METHOD) + result = (DoSuperMethodA (cl, (Object *)g, (Msg) msg)); + else + result = TRUE; + + + /* Update gadget imagery, only when needed */ + + if ((action & (LVF_REDRAW | LVF_SCROLL | LVF_TOGGLESELECT)) + && msg->opu_GInfo && !(lv->Flags & LVF_DONTDRAW)) + { + struct RastPort *rp; + + if (rp = ObtainGIRPort (msg->opu_GInfo)) + { + /* Just redraw everything */ + if (action & LVF_REDRAW) + DoMethod ((Object *)g, GM_RENDER, msg->opu_GInfo, rp, GREDRAW_REDRAW); + else + { + /* Both these may happen at the same time */ + + if (action & LVF_SCROLL) + DoMethod ((Object *)g, GM_RENDER, msg->opu_GInfo, rp, + GREDRAW_UPDATE); + + if (action & LVF_TOGGLESELECT) + DoMethod ((Object *)g, GM_RENDER, msg->opu_GInfo, rp, + GREDRAW_TOGGLE); + } + + ReleaseGIRPort (rp); + } + DB(else kprintf ("*** ObtainGIRPort() failed!\n");) + } + + + /* Notify our targets about changed attributes */ + + if (action & LVF_NOTIFYALL) + { + DB(kprintf("OM_NOTIFY: ALL\n");) + DB(kprintf(" LVA_Top, %ld\n", lv->Top);) + DB(kprintf(" LVA_Total, %ld\n", lv->Total);) + DB(kprintf(" LVA_Visible, %ld\n", lv->Visible);) + DB(kprintf(" LVA_Selected, %ld\n", lv->Selected);) + DB(kprintf(" LVA_PixelTop, %ld\n", lv->PixelTop);) + DB(kprintf(" LVA_PixelHeight, %ld\n", lv->Total * (lv->ItemHeight + lv->Spacing));) + DB(kprintf(" LVA_PixelVVisible, %ld\n", lv->GBox.Height);) + DB(kprintf(" TAG_DONE\n");) + + NotifyAttrs ((Object *)g, msg->opu_GInfo, + (msg->MethodID == OM_UPDATE) ? msg->opu_Flags : 0, + LVA_Top, lv->Top, + LVA_Total, lv->Total, + LVA_Visible, lv->Visible, + LVA_Selected, lv->Selected, + LVA_PixelTop, lv->PixelTop, + LVA_PixelHeight, lv->Total * (lv->ItemHeight + lv->Spacing), + LVA_PixelVVisible, lv->ClipRegion ? + lv->GBox.Height : + lv->Visible * (lv->ItemHeight + lv->Spacing), + GA_ID, g->GadgetID, + TAG_DONE); + } + else if (action & LVF_NOTIFY) + { + IPTR tags[9]; + int cnt = 0; + + if (action & LVF_SCROLL) + { + tags[0] = LVA_Top; tags[1] = lv->Top; + tags[2] = LVA_PixelTop; tags[3] = lv->Top * (lv->ItemHeight + lv->Spacing); + cnt = 4; + } + + if (action & LVF_TOGGLESELECT) + { + tags[cnt++] = LVA_Selected; tags[cnt++] = lv->Selected; + } + + tags[cnt++] = GA_ID; + tags[cnt++] = g->GadgetID; + tags[cnt] = TAG_DONE; + + DoMethod ((Object *)g, OM_NOTIFY, tags, msg->opu_GInfo, + (msg->MethodID == OM_UPDATE) ? msg->opu_Flags : 0); + } + + return result; +} + + + +static ULONG LV_OMGet (Class *cl, struct Gadget *g, struct opGet *msg) +{ + struct LVData *lv = INST_DATA (cl, g); + + ASSERT_VALIDNO0(lv) + ASSERT_VALIDNO0(msg->opg_Storage) + + DB (kprintf ("OM_GET\n");) + + + switch (msg->opg_AttrID) + { + case LVA_Selected: + *msg->opg_Storage = (ULONG) lv->Selected; + return TRUE; + + case LVA_Top: + *msg->opg_Storage = (ULONG) lv->Top; + return TRUE; + + case LVA_Total: + *msg->opg_Storage = (ULONG) lv->Total; + return TRUE; + + case LVA_StringList: + case LVA_StringArray: + case LVA_ImageList: + case LVA_ImageArray: + case LVA_CustomList: + *msg->opg_Storage = (ULONG) lv->Items; + return TRUE; + + case LVA_Visible: + *msg->opg_Storage = (ULONG) lv->Visible; + return TRUE; + + case LVA_SelectedPtr: + *msg->opg_Storage = (ULONG) lv->SelectedPtr; + return TRUE; + + case LVA_SelectArray: + *msg->opg_Storage = (ULONG) lv->SelectArray; + return TRUE; + + default: + return DoSuperMethodA (cl, (Object *)g, (Msg) msg); + } +} + + + +static ULONG LV_OMNew (Class *cl, struct Gadget *g, struct opSet *msg) +{ + struct LVData *lv; + struct TagItem *tag; + struct DrawInfo *drawinfo; + + + DB (kprintf ("OM_NEW\n");) + + if (g = (struct Gadget *)DoSuperMethodA (cl, (Object *)g, (Msg)msg)) + { + /* Set the GMORE_SCROLLRASTER flag */ + if (g->Flags & GFLG_EXTENDED) + { + DB (kprintf (" Setting GMORE_SCROLLRASTER\n");) + ((struct ExtGadget *)g)->MoreFlags |= GMORE_SCROLLRASTER; + } + + lv = (struct LVData *) INST_DATA (cl, (Object *)g); + ASSERT_VALIDNO0(lv) + + /* Handle creation-time attributes */ + + /* Map boolean attributes */ + { + static IPTR boolMap[] = + { + GA_ReadOnly, LVF_READONLY, + LVA_Clipped, LVF_CLIPPED, + LVA_ShowSelected, LVF_SHOWSELECTED, + LVA_DoMultiSelect, LVF_DOMULTISELECT, + TAG_DONE + }; + + lv->Flags = PackBoolTags ( + LVF_SHOWSELECTED, + msg->ops_AttrList, + (struct TagItem *)boolMap); + } + + + /* Select font to use when drawing the Listview labels */ + + /* First, try to get the font from our DrawInfo... */ + + if (drawinfo = (struct DrawInfo *) + GetTagData (GA_DrawInfo, NULL, msg->ops_AttrList)) + { + ASSERT_VALID(drawinfo) + lv->Font = drawinfo->dri_Font; + } + else + lv->Font = NULL; + + + /* ...then override it with LVA_TextFont */ + + if (tag = FindTagItem (LVA_TextFont, msg->ops_AttrList)) + { + if (tag->ti_Data) + { + lv->Font = (struct TextFont *)tag->ti_Data; + ASSERT_VALID(lv->Font) + } + } + else /* Otherwise, try GA_TextAttr */ + { + struct TextAttr *attr; + struct TextFont *font; + + if (attr = (struct TextAttr *)GetTagData (GA_TextAttr, + NULL, msg->ops_AttrList)) + { + if (font = OpenFont (attr)) + { + /* Must remember to close this font later */ + lv->Flags |= LVF_CLOSEFONT; + lv->Font = font; + } + } + } + + /* Calculate ItemHeight */ + + if (lv->Font) + /* Get height from font Y size */ + lv->ItemHeight = lv->Font->tf_YSize; + else + lv->ItemHeight = 0; + + lv->ItemHeight = GetTagData (LVA_ItemHeight, lv->ItemHeight, msg->ops_AttrList); + lv->Spacing = GetTagData (LAYOUTA_Spacing, 0, msg->ops_AttrList); + + if (tag = FindTagItem (LVA_MaxPen, msg->ops_AttrList)) + lv->MaxPen = tag->ti_Data; + else + { + if (drawinfo) + lv->MaxPen = max ( + max (drawinfo->dri_Pens[BACKGROUNDPEN], + drawinfo->dri_Pens[TEXTPEN]), + max (drawinfo->dri_Pens[FILLPEN], + drawinfo->dri_Pens[FILLTEXTPEN])); + else + lv->MaxPen = (ULONG)-1; + } + + + lv->Total = GetTagData (LVA_Total, ~0, msg->ops_AttrList); + + if (lv->Items = (APTR) GetTagData (LVA_StringList, NULL, msg->ops_AttrList)) + { + ASSERT_VALID(lv->Items) + lv->GetItemFunc = ListGetItem; + lv->GetNextFunc = ListGetNext; + lv->GetPrevFunc = ListGetPrev; + lv->DrawItemFunc = ListStringDrawItem; + lv->Flags |= LVF_LIST; + + if (lv->Total == ~0) + lv->Total = CountNodes (lv->Items); + } + else if (lv->Items = (APTR) GetTagData (LVA_StringArray, NULL, msg->ops_AttrList)) + { + ASSERT_VALID(lv->Items) + lv->GetItemFunc = ArrayGetItem; + lv->GetNextFunc = ArrayGetItem; + lv->GetPrevFunc = ArrayGetItem; + lv->DrawItemFunc = StringDrawItem; + + if (lv->Total == ~0) + { + /* Count items */ + ULONG i = 0; + while (((APTR *)lv->Items)[i]) i++; + lv->Total = i; + } + } + else if (lv->Items = (APTR) GetTagData (LVA_ImageList, NULL, msg->ops_AttrList)) + { + ASSERT_VALID(lv->Items) + lv->GetItemFunc = ListGetItem; + lv->GetNextFunc = ListGetNext; + lv->GetPrevFunc = ListGetPrev; + lv->DrawItemFunc = ListImageDrawItem; + lv->Flags |= LVF_LIST; + + if (lv->Total == ~0) + lv->Total = CountNodes (lv->Items); + } + else if (lv->Items = (APTR) GetTagData (LVA_ImageArray, NULL, msg->ops_AttrList)) + { + ASSERT_VALID(lv->Items) + lv->GetItemFunc = ArrayGetItem; + lv->GetNextFunc = ArrayGetItem; + lv->GetPrevFunc = ArrayGetItem; + lv->DrawItemFunc = ImageDrawItem; + + if (lv->Total == ~0) + { + /* Count items */ + ULONG i = 0; + while (((APTR *)lv->Items)[i]) i++; + lv->Total = i; + } + } + + lv->SelectArray = (ULONG *)GetTagData (LVA_SelectArray, NULL, msg->ops_AttrList); + lv->MaxSelect = GetTagData (LVA_MaxSelect, -1, msg->ops_AttrList); + lv->SelectCount = CountSelections(lv); + + if (lv->Visible = GetTagData (LVA_Visible, 0, msg->ops_AttrList)) + { + SetAttrs (g, + GA_Height, lv->Visible * (lv->ItemHeight + lv->Spacing), + TAG_DONE); + } + + /* Initialize Top and all related values */ + + lv->OldTop = lv->Top = GetTagData (LVA_MakeVisible, + GetTagData (LVA_Top, 0, msg->ops_AttrList), msg->ops_AttrList); + lv->OldPixelTop = lv->PixelTop = lv->Top * (lv->ItemHeight + lv->Spacing); + + if (lv->Items) + lv->TopPtr = GetItem (lv, lv->Top); + + lv->ScrollRatio = GetTagData (LVA_ScrollRatio, 2, msg->ops_AttrList); + ASSERT(lv->ScrollRatio != 0) + + if ((lv->OldSelected = + lv->Selected = GetTagData (LVA_Selected, ~0, msg->ops_AttrList)) != ~0) + lv->SelectedPtr = GetItem (lv, lv->Selected); + + if (lv->CallBack = (struct Hook *)GetTagData (LVA_CallBack, NULL, + msg->ops_AttrList)) + { + ASSERT_VALID(lv->CallBack->h_Entry) + lv->DrawItemFunc = (LVDrawHook *) lv->CallBack->h_Entry; + } + + if (lv->Flags & LVF_CLIPPED) + lv->ClipRegion = NewRegion (); + } + return (ULONG)g; +} + + + +static void LV_OMDispose (Class *cl, struct Gadget *g, Msg msg) +{ + struct LVData *lv; + + lv = (struct LVData *) INST_DATA (cl, (Object *)g); + + ASSERT_VALIDNO0(lv) + DB (kprintf ("OM_DISPOSE\n");) + + if (lv->ClipRegion) + DisposeRegion (lv->ClipRegion); + + if (lv->Flags & LVF_CLOSEFONT) + CloseFont (lv->Font); + + /* Our superclass will cleanup everything else now */ + DoSuperMethodA (cl, (Object *)g, (Msg) msg); + + /* From now on, our instance data is no longer available */ +} + + + +/* Misc support functions */ + +INLINE ULONG CountNodes (struct List *list) + +/* Return the number of nodes in a list */ +{ + struct Node *node; + ULONG count = 0; + + if (list) + { + ASSERT_VALID(list) + + for (node = list->lh_Head; node = node->ln_Succ; count++) + ASSERT_VALID(node); + } + + return count; +} + + + +static ULONG CountSelections (struct LVData *lv) + +/* Count the number of selections in a multiselect listview */ +{ + ULONG count = 0; + + ASSERT_VALIDNO0(lv) + + + if (lv->Flags & LVF_DOMULTISELECT) + { + if (lv->SelectArray) + { + int i; + + ASSERT_VALID(lv->SelectArray) + + for (i = 0; i < lv->Total; i++) + if (lv->SelectArray[i]) + count++; + } + else if ((lv->Flags & LVF_LIST) && lv->Items) + { + struct Node *node; + + ASSERT_VALID(lv->Items) + + for (node = ((struct List *)lv->Items)->lh_Head; node = node->ln_Succ; count++) + ASSERT_VALID(node); + } + } + + return count; +} + + + +INLINE ULONG IsItemSelected (struct LVData *lv, APTR item, ULONG num) + +/* Checks if the given item is selected */ +{ + ASSERT_VALIDNO0(lv) + ASSERT_VALID(item) + ASSERT(num >= 0) + ASSERT(num < lv->Total) + + + if (lv->Flags & LVF_DOMULTISELECT) + { + if (lv->SelectArray) + { + ASSERT(num < lv->Total) + + return lv->SelectArray[num]; + } + else if (lv->Flags & LVF_LIST) + { + if (!item) + item = GetItem (lv, num); + + ASSERT_VALIDNO0(item) + + return item ? (ULONG)(((struct Node *)item)->ln_Type) : 0; + } + + return 0; + } + else + return ((ULONG)(num == lv->Selected)); +} + + + +INLINE APTR GetItem (struct LVData *lv, ULONG num) + +/* Stub for LV_GETITEM hook method */ +{ + struct lvGetItem lvgi; + + + ASSERT_VALIDNO0(lv) + ASSERT_VALIDNO0(lv->Items) + ASSERT_VALIDNO0(lv->GetItemFunc) + ASSERT(num >= 0) + ASSERT(num < lv->Total) + + + lvgi.lvgi_MethodID = LV_GETITEM; + lvgi.lvgi_Number = num; + lvgi.lvgi_Items = lv->Items; + + return (lv->GetItemFunc (lv->CallBack, NULL, &lvgi)); +} + + + +INLINE APTR GetNext (struct LVData *lv, APTR item, ULONG num) + +/* Stub for LV_GETNEXT hook method */ +{ + struct lvGetItem lvgi; + + + ASSERT_VALIDNO0(lv) + ASSERT_VALIDNO0(lv->GetNextFunc) + ASSERT_VALID(item) + ASSERT(num >= 0) + ASSERT(num < lv->Total) + + + lvgi.lvgi_MethodID = LV_GETNEXT; + lvgi.lvgi_Number = num; + lvgi.lvgi_Items = lv->Items; + + return (lv->GetNextFunc (lv->CallBack, item, &lvgi)); +} + + + +INLINE APTR GetPrev (struct LVData *lv, APTR item, ULONG num) + +/* Stub for LV_GETPREV hook method */ +{ + struct lvGetItem lvgi; + + + ASSERT_VALIDNO0(lv) + ASSERT_VALIDNO0(lv->GetPrevFunc) + ASSERT_VALID(item) + ASSERT(num >= 0) + ASSERT(num < lv->Total) + + + lvgi.lvgi_MethodID = LV_GETPREV; + lvgi.lvgi_Number = num; + lvgi.lvgi_Items = lv->Items; + + return (lv->GetPrevFunc (lv->CallBack, item, &lvgi)); +} + + + +Class *MakeListViewClass (void) +{ + Class *LVClass; + + if (LVClass = MakeClass (NULL, GADGETCLASS, NULL, sizeof (struct LVData), 0)) + LVClass->cl_Dispatcher.h_Entry = (ULONG (*)()) LVDispatcher; + + return LVClass; +} + + + +void FreeListViewClass (Class *LVClass) +{ + ASSERT_VALID(LVClass) + FreeClass (LVClass); +} diff --git a/ListViewClass.h b/ListViewClass.h new file mode 100644 index 0000000..98f6b19 --- /dev/null +++ b/ListViewClass.h @@ -0,0 +1,429 @@ +#ifndef LISTVIEWCLASS_H +#define LISTVIEWCLASS_H +/* +** ListViewClass.h +** +** Copyright (C) 1996,97 by Bernardo Innocenti +** +** ListView class built on top of the "gadgetclass". +** +*/ + +#define LISTVIEWCLASS "listviewclass" +#define LISTVIEWVERS 1 + + +Class *MakeListViewClass (void); +void FreeListViewClass (Class *LVClass); + + + +/*****************/ +/* Class Methods */ +/*****************/ + +/* This class does not define any new methods */ + + + +/********************/ +/* Class Attributes */ +/********************/ + +#define LVA_Dummy (TAG_USER | ('L'<<16) | ('V'<<8)) + +#define LVA_Selected (LVA_Dummy+1) + /* (IGSNU) LONG - Selected item number. ~0 (-1) indicates that no + * item is selected. + */ + +#define LVA_Top (LVA_Dummy+2) + /* (IGSNU) LONG - Number of top displayed item. Default is 0. + */ + +#define LVA_Total (LVA_Dummy+3) + /* (IGSN) LONG - Total number of items in list. + * This attribute can be set when LVA_StringArray, LVA_ImageArray + * or LVA_CustomList is used. If you pass -1 or omit this tag, + * the ListView class will count the items in the array until it + * finds a NULL entry. If you know the number of nodes in your list, + * you will save some internal overhead by telling it to the + * ListView with the LVA_Total tag, expecially when using the + * LVA_StringList and LVA_ImageList modes. You must set LVA_Total + * each time you provide a new list or array, and in the same + * OM_SET call. + * Be careful: no checks are made on the values you are passing! + */ + +#define LVA_SelectItem (LVA_Dummy+4) + /* (SN) LONG - Add item specified by ti_Data to the + * selection list. + */ + +#define LVA_DeselectItem (LVA_Dummy+5) + /* (SN) LONG - Remove item specified by ti_Data from the + * selection list. + */ + +#define LVA_ToggleItem (LVA_Dummy+6) + /* (SN) LONG - Toggle item selection. + */ + +#define LVA_ClearSelected (LVA_Dummy+7) + /* (SN) LONG - Remove the selected state to AALL items. + */ + + +#define LVA_MakeVisible (LVA_Dummy+8) + /* (ISU) Make this item visible by doing the minimum required scrolling. + */ + +#define LVA_MoveUp (LVA_Dummy+9) +#define LVA_MoveDown (LVA_Dummy+10) +#define LVA_MoveLeft (LVA_Dummy+11) +#define LVA_MoveRight (LVA_Dummy+12) + /* (SU) Scroll the display up/down. left/right movement is not + * yet supported. + */ + +#define LVA_StringList (LVA_Dummy+13) +#define LVA_StringArray (LVA_Dummy+14) +#define LVA_ImageList (LVA_Dummy+15) +#define LVA_ImageArray (LVA_Dummy+16) +#define LVA_CustomList (LVA_Dummy+17) +#define LVA_Labels LVA_StringList + /* (ISG) List of items to display. All structures and strings + * are referenced, not copied, so they must stay in memory + * while the ListView gadget displays them. + * + * LVA_StringList (AKA LVA_Labels) passes a pointer to a + * List (or MinList) of Node structures. The strings + * pointed by ln_Name will be displayed as item labels. + * + * LVA_StringArray specifies a pointer to an array of STRPTR which + * will be used as item labels. The array must be NULL terminated + * unless the LVA_Count is set. + * + * LVA_ImageList passes a pointer to a List (or MinList) + * of Node structures. The ln_Name field of each Node structure + * must point to a normal Image structure or to an instance of + * the imageclass (or a subclass of it). + * + * LVA_ImageArray specifies a pointer to an array of pointers to + * Image structures or imageclass objects. The array must be NULL + * terminated unless the LVA_Count attribute is used. + * + * LVA_CustomList can be used to provide a data structure which + * is neither a list nor an array. Custom user functions will + * be called to retrieve the items. The data passed with this + * tag will be passed to the user functions. + * + * Setting one of these attributes to NULL causes the contents + * of the ListView gadget to be cleared. + * + * Setting one of these attributes to ~0 (-1) detaches the + * items from the ListView. You must detach your list before + * adding or removing items. This isn't required when using + * array oriented item lists. + */ + +#define LVA_Visible (LVA_Dummy+18) + /* (IGN) ULONG - Number of visible items. When this attribute is + * passed on intialization, the ListView gadget will resize + * itself to make the desired number of lines visible. this + * feature is incompatible with GA_RelHeight. LVA_Visible at + * creation time requires a valid DrawInfo passed with the + * GA_DrawInfo tag. + */ + +#define LVA_SelectedPtr (LVA_Dummy+19) + /* (GN) Selected item pointer. Will be NULL when no items are selected. + */ + +#define LVA_SelectArray (LVA_Dummy+20) + /* (ISGNU) ULONG * - This array of ULONGs is only used in + * LVA_#?Array modes, when multiple selection is active. It will + * hold the selection status of all items in the list. Each + * element will be 0 if the related item is unselected, and + * it will indicate the selection order when the item is + * selected. + */ + +#define LVA_CallBack (LVA_Dummy+21) + /* (I) struct Hook * - Callback hook for various listview operations. + * The call back hook is called with: + * A0 - struct Hook * + * A1 - struct LVDrawMsg * + * A2 - struct Node * + * The callback hook *must* check the lvdm_MethodID field of the + * message and only do processing if it is known. If any other + * value is passed, the callback hook must return LVCB_UNKNOWN. + */ + +#define LVA_ShowSelected (LVA_Dummy+23) + /* (I) BOOL - Enable highlighting selected items (default is TRUE). + * Note that this is different from the GadTools ListView default, + * which is not displaying the selected item. + */ + +#define LVA_Clipped (LVA_Dummy+24) + /* (I) BOOL - When this attribute is set, the ListView gadget Installs + * a ClipRegion in its Layer whenever it redraws its items. + * (defaults is FALSE). + */ + +#define LVA_DoMultiSelect (LVA_Dummy+25) + /* (I) BOOL - Allows picking multiple items from the list + * (default is FALSE). + * When MultiSelect mode is active and a List structure is used, + * the ListView gadget keeps track of which items are selected + * by setting/clearing the ln_Type field of the Node structure. + * When items are passed with an array, you must also provide + * a second array for storing selection information (LVA_SelectArray). + * When item number n is selected, then the n-th BOOL of this array + * is set with its selection order. + */ + +#define LVA_ItemHeight (LVA_Dummy+27) + /* (I) ULONG - Exact height of an item. Defaults to be the Y size + * of the font used to draw the text labels. The listview will ask + * the height to its Image items if not explicitly given. + */ + +#define LVA_MaxPen (LVA_Dummy+28) + /* (I) LONG - The maximum pen number used by rendering in a custom + * rendering callback hook. This is used to optimize the + * rendering and scrolling of the listview display (default is + * the maximum pen number used by all of TEXTPEN, BACKGROUNDPEN, + * FILLPEN, TEXTFILLPEN and BLOCKPEN). + */ + +#define LVA_TextFont (LVA_Dummy+29) + /* (I) struct TextFont * - Font to be used for rendering texts. + * Defaults to the default screen font. + */ + +#define LVA_DoubleClick (LVA_Dummy+30) + /* (N) ULONG - The item specified by ti_Data has been double clicked. + */ + +#define LVA_MaxSelect (LVA_Dummy+31) + /* (IS) ULONG - Maximum number of selected items to allow in multiselect + * mode. If you later set this tag with a more restrictive condition, the + * listview will NOT deselect any of the currently selected items to + * satisfy your request. Default is unlimited (~0). + */ + +#define LVA_PixelTop (LVA_Dummy+32) + /* (ISGNU) ULONG - Offset from top of list in pixel units. Useful for + * scrollers. + */ + +#define LVA_PixelHeight (LVA_Dummy+33) + /* (N) LONG - Total height of list in pixel units. Useful for scrollers. + */ + +#define LVA_PixelVVisible (LVA_Dummy+34) + /* (N) LONG - Number of vertical visible pixels. Useful for scrollers. + */ + +#define LVA_PixelLeft (LVA_Dummy+35) + /* (ISNU) LONG - Offset from left of list in pixel units. Useful for scrollers. + */ + +#define LVA_PixelWidth (LVA_Dummy+36) + /* (ISNG) LONG - Total width of list in pixel units. Useful for scrollers. + */ + +#define LVA_PixelHVisible (LVA_Dummy+37) + /* (N) Number of horizontal visible pixels. Useful for scrollers. + */ + +#define LVA_Title (LVA_Dummy+38) + /* (IS) Listview title item. This item will be drawn on the top line of + * the list and will not scroll. ti_Data points to an item in the same + * format of the items list (e.g.: If it is LVA_StringArray, then it will + * be a Node * with ln_Name pointing to a text string. The item will be + * passed to the custom item drawing hook. (TODO) + */ + +#define LVA_Columns (LVA_Dummy+39) + /* (I) (LONG) Number of columns to be displayed. Default is 1. If set + * to ~0, the listview will precheck all items to calculate this number + * automatically. (TODO) + */ + +#define LVA_ColumnSpacing (LVA_Dummy+40) + /* (I) ULONG - Spacing between columns in pixel units. + * Default is 4 pixels. (TODO) + */ + +#define LVA_ColumnWidths (LVA_Dummy+41) + /* (IGS) ULONG * - Points to an array of ULONGs containing the width of + * each column expressed in pixel units. A value of -1 causes the + * ListView to automatically calculate the width of the column, by + * asking the width to all items. (TODO) + */ + +#define LVA_ColumnSeparator (LVA_Dummy+42) + /* (I) UBYTE - When the listview is in multicolumn mode, the + * internal text label routines will scan the string for this + * character, as a separator for the column labels. This defaults + * to '\t', so a label for a three column list view would look + * like this: "One\tTwo\tThree". (TODO) + */ + +#define LVA_ResizeColumns (LVA_Dummy+43) + /* (IS) BOOL - Allows the user to resize the columns by dragging + * on the listview title line. (TODO) + */ + +#define LVA_SelectTick (LVA_Dummy+44) + /* (I) When user selects an item, show a checkmark on the left + * instead of rendering the item in selected state. (TODO) + */ + +#define LVA_ScrollInertia (LVA_Dummy+45) + /* (IS) ULONG - Sets the scrolling inertia of the listview. + * Defaults to 4 for LVA_Clipped mode, 0 for a non-clipped listview. + * (TODO) + */ + +#define LVA_ScrollRatio (LVA_Dummy+46) + /* (IS) ULONG - If you ask the listview to scroll more than + * LVA_Visible / LVA_ScrollRatio lines, all the listview contents + * will be redrawn instead. The minimum value of 1 will trigger a + * full redraw only when ALL the listview visible part is scrolled away. + * The default value is 2, which is a good compromise for items which + * can redraw themselves relatively quickly, such as simple text + * labels or bitmap images. + */ + +/* Public flags */ +#define LVB_READONLY 0 /* Do not allow item selection */ +#define LVB_CLIPPED 1 /* Clip item drawing inside their boxes */ +#define LVB_SHOWSELECTED 2 /* Hilights the selected item */ +#define LVB_DOMULTISELECT 3 /* Allows user to pick more than one item */ +#define LVB_SMOOTHSCROLLING 4 /* Scoll pixel by pixel */ +#define LVB_RESIZECOLUMNS 5 /* Allow user to resize the columns */ + +/* Internal flags - DO NOT USE */ +#define LVB_CLOSEFONT 27 /* Close the font when disposing the object */ +#define LVB_LIST 28 /* Using an exec List */ +#define LVB_DONTDRAW 29 /* Do not perform any drawing operations */ +#define LVB_SCROLLING 30 /* User scrolling with middle mouse button */ +#define LVB_DRAGGING 31 /* User dragging selection with LMB */ + + +#define LVF_READONLY (1< will be included too */ + +#ifdef LV_GADTOOLS_STUFF + +/* The different types of messages that a listview callback hook can see */ +#define LV_DRAW 0x202L /* draw yourself, with state */ + +/* Possible return values from a callback hook */ +#define LVCB_OK 0 /* callback understands this message type */ +#define LVCB_UNKNOWN 1 /* callback does not understand this message */ + +/* states for LVDrawMsg.lvdm_State */ +#define LVR_NORMAL 0 /* the usual */ +#define LVR_SELECTED 1 /* for selected gadgets */ +#define LVR_NORMALDISABLED 2 /* for disabled gadgets */ +#define LVR_SELECTEDDISABLED 8 /* disabled and selected */ + +#endif /* LV_GADTOOLS_STUFF */ + +#define LVR_TITLE 16 /* ListView title item */ + + +/* More callback hook methods */ + +#define LV_GETNEXT 0x203L /* gimme next item in list */ +#define LV_GETPREV 0x204L /* gimme previous item in list */ +#define LV_GETITEM 0x205L /* gimme item handle by number */ + +/* These two methods can be used to optimize listview rendering + * operations. You can safely assume that the rastport attributes + * you set inside LV_DRAWBEGIN will remain unchanged for all + * subsequent calls to LV_DRAW, until an LV_DRAWEND is issued. + * They do also provide a way to lock/unlock the list of items + * if the access to its item needs to be arbitrated by a semaphore. + */ +#define LV_DRAWBEGIN 0x206L /* prepare to draw items */ +#define LV_DRAWEND 0x207L /* items drawing completed */ + + + +/* More messages */ + +struct lvDrawItem +{ + ULONG lvdi_MethodID; /* LV_DRAW */ + ULONG lvdi_Current; /* Current item number */ + APTR lvdi_Items; /* Pointer to List, array, etc. */ + struct RastPort *lvdi_RastPort; /* where to render to */ + struct DrawInfo *lvdi_DrawInfo; /* useful to have around */ + struct Rectangle lvdi_Bounds; /* limits of where to render */ + ULONG lvdi_State; /* how to render */ + ULONG lvdi_Flags; /* Current LVF_#? flags */ +}; + +struct lvGetItem +{ + ULONG lvgi_MethodID; /* LV_GETITEM */ + ULONG lvgi_Number; /* Number of item to get */ + APTR lvgi_Items; /* Pointer to List, array, etc. */ +}; + +#define lvGetNext lvGetItem +#define lvGetPrev lvGetItem +#define lvDrawBegin lvGetItem /* lvgi_Number has no useful meaning */ +#define lvDrawEnd lvGetItem /* lvgi_Number has no useful meaning */ + +#endif /* !LISTVIEWCLASS_H */ diff --git a/ListViewHooks.c b/ListViewHooks.c new file mode 100644 index 0000000..9d65c93 --- /dev/null +++ b/ListViewHooks.c @@ -0,0 +1,293 @@ +/* +** ListViewHooks.c +** +** Copyright (C) 1996,97 Bernardo Innocenti +** +** Use 4 chars wide TABs to read this file +** +** Internal drawing and browsing hooks the listview class +*/ + +#define USE_BUILTIN_MATH +#define INTUI_V36_NAMES_ONLY +#define __USE_SYSBASE +#define CLIB_ALIB_PROTOS_H /* Avoid dupe defs of boopsi funcs */ + +#include +#include +#include +#include + +#include +#include + +#ifdef __STORM__ + #pragma header +#endif + +#include "CompilerSpecific.h" +#include "Debug.h" + +#define LV_GADTOOLS_STUFF +#include "ListViewClass.h" + + + +/* Definitions for builtin List hook */ + +APTR HOOKCALL ListGetItem ( + REG(a0, struct Hook *hook), + REG(a1, struct Node *node), + REG(a2, struct lvGetItem *lvgi)); +APTR HOOKCALL ListGetNext ( + REG(a0, struct Hook *hook), + REG(a1, struct Node *node), + REG(a2, struct lvGetNext *lvgn)); +APTR HOOKCALL ListGetPrev ( + REG(a0, struct Hook *hook), + REG(a1, struct Node *node), + REG(a2, struct lvGetPrev *lvgp)); +ULONG HOOKCALL ListStringDrawItem ( + REG(a0, struct Hook *hook), + REG(a1, struct Node *node), + REG(a2, struct lvDrawItem *lvdi)); +ULONG HOOKCALL ListImageDrawItem ( + REG(a0, struct Hook *hook), + REG(a1, struct Node *node), + REG(a2, struct lvDrawItem *lvdi)); + + +/* Definitions for builtin Array hook */ + +APTR HOOKCALL ArrayGetItem ( + REG(a0, struct Hook *hook), + REG(a1, STRPTR *item), + REG(a2, struct lvGetItem *lvgi)); +ULONG HOOKCALL StringDrawItem ( + REG(a0, struct Hook *hook), + REG(a1, STRPTR str), + REG(a2, struct lvDrawItem *lvdi)); +ULONG HOOKCALL ImageDrawItem ( + REG(a0, struct Hook *hook), + REG(a1, struct Image *img), + REG(a2, struct lvDrawItem *lvdi)); + + + +APTR HOOKCALL ListGetItem ( + REG(a0, struct Hook *hook), + REG(a1, struct Node *node), + REG(a2, struct lvGetItem *lvg)) +{ + ULONG i; + + ASSERT_VALIDNO0(lvg) + + node = ((struct List *)(lvg->lvgi_Items))->lh_Head; + + /* Warning: no sanity check is made against + * list being shorter than expected! + */ + for (i = 0; i < lvg->lvgi_Number; i++) + { + ASSERT_VALIDNO0(node) + node = node->ln_Succ; + } + + return (APTR)node; +} + + + +APTR HOOKCALL ListGetNext ( + REG(a0, struct Hook *hook), + REG(a1, struct Node *node), + REG(a2, struct lvGetItem *lvg)) +{ + ASSERT_VALIDNO0(node) + ASSERT_VALIDNO0(lvg) + + return (APTR)(node->ln_Succ->ln_Succ ? node->ln_Succ : NULL); +} + + + +APTR HOOKCALL ListGetPrev ( + REG(a0, struct Hook *hook), + REG(a1, struct Node *node), + REG(a2, struct lvGetItem *lvg)) +{ + ASSERT_VALIDNO0(node) + ASSERT_VALIDNO0(lvg) + + return (APTR)(node->ln_Pred->ln_Pred ? node->ln_Pred : NULL); +} + + + +ULONG HOOKCALL ListStringDrawItem ( + REG(a0, struct Hook *hook), + REG(a1, struct Node *node), + REG(a2, struct lvDrawItem *lvdi)) +{ + ASSERT_VALIDNO0(node) + ASSERT_VALIDNO0(lvdi) + + return StringDrawItem (hook, node->ln_Name, lvdi); +} + + + +ULONG HOOKCALL ListImageDrawItem ( + REG(a0, struct Hook *hook), + REG(a1, struct Node *node), + REG(a2, struct lvDrawItem *lvdi)) +{ + ASSERT_VALIDNO0(node) + ASSERT_VALIDNO0(lvdi) + + return ImageDrawItem (hook, (struct Image *)node->ln_Name, lvdi); +} + + + +APTR HOOKCALL ArrayGetItem ( + REG(a0, struct Hook *hook), + REG(a1, STRPTR *item), + REG(a2, struct lvGetItem *lvg)) +{ + ASSERT_VALIDNO0(lvg) + ASSERT_VALIDNO0(lvg->lvgi_Items) + + return (APTR)(((STRPTR *)lvg->lvgi_Items)[lvg->lvgi_Number]); +} + + + +ULONG HOOKCALL StringDrawItem ( + REG(a0, struct Hook *hook), + REG(a1, STRPTR str), + REG(a2, struct lvDrawItem *lvdi)) +{ + struct RastPort *rp; + ULONG len; + + ASSERT_VALIDNO0(lvdi) + rp = lvdi->lvdi_RastPort; + ASSERT_VALIDNO0(rp) + ASSERT_VALID(str) + + if (!str) + /* Move to the leftmost pixel of the rectangle + * to have the following RectFill() clear all the line + */ + Move (rp, lvdi->lvdi_Bounds.MinX, 0); + else + { + struct TextExtent textent; + + if (lvdi->lvdi_State == LVR_NORMAL) + { +#ifndef OS30_ONLY + if (GfxBase->LibNode.lib_Version < 39) + { + SetAPen (rp, lvdi->lvdi_DrawInfo->dri_Pens[TEXTPEN]); + SetBPen (rp, lvdi->lvdi_DrawInfo->dri_Pens[BACKGROUNDPEN]); + SetDrMd (rp, JAM2); + } + else +#endif /* !OS30_ONLY */ + SetABPenDrMd (rp, lvdi->lvdi_DrawInfo->dri_Pens[TEXTPEN], + lvdi->lvdi_DrawInfo->dri_Pens[BACKGROUNDPEN], + JAM2); + } + else + { +#ifndef OS30_ONLY + if (GfxBase->LibNode.lib_Version < 39) + { + SetAPen (rp, lvdi->lvdi_DrawInfo->dri_Pens[FILLTEXTPEN]); + SetBPen (rp, lvdi->lvdi_DrawInfo->dri_Pens[FILLPEN]); + SetDrMd (rp, JAM2); + } + else +#endif /* !OS30_ONLY */ + SetABPenDrMd (rp, lvdi->lvdi_DrawInfo->dri_Pens[FILLTEXTPEN], + lvdi->lvdi_DrawInfo->dri_Pens[FILLPEN], + JAM2); + } + + Move (rp, lvdi->lvdi_Bounds.MinX, lvdi->lvdi_Bounds.MinY + rp->Font->tf_Baseline); + + len = strlen (str); + + if (!(lvdi->lvdi_Flags & LVF_CLIPPED)) + { + /* Calculate how much text will fit in the listview width */ + len = TextFit (rp, str, len, &textent, NULL, 1, + lvdi->lvdi_Bounds.MaxX - lvdi->lvdi_Bounds.MinX + 1, + lvdi->lvdi_Bounds.MaxY - lvdi->lvdi_Bounds.MinY + 1); + } + + Text (rp, str, len); + + /* Text() will move the pen X position to + * lvdi->lvdi_Bounds.MinX + textent.te_Width. + */ + } + + /* Now clear the rest of the row. rp->cp_x is updated by Text() to the + * next character to print. + */ + SetAPen (rp, lvdi->lvdi_DrawInfo->dri_Pens[(lvdi->lvdi_State == LVR_NORMAL) ? + BACKGROUNDPEN : FILLPEN]); + RectFill (rp, rp->cp_x, + lvdi->lvdi_Bounds.MinY, + lvdi->lvdi_Bounds.MaxX, + lvdi->lvdi_Bounds.MaxY); + + + return LVCB_OK; +} + + + +ULONG HOOKCALL ImageDrawItem ( + REG(a0, struct Hook *hook), + REG(a1, struct Image *img), + REG(a2, struct lvDrawItem *lvdi)) +{ + struct RastPort *rp; + UWORD left; + + ASSERT_VALID(img) + ASSERT_VALIDNO0(lvdi) + rp = lvdi->lvdi_RastPort; + ASSERT_VALIDNO0(rp) + + if (!img) + /* Move to the leftmost pixel of the item rectangle + * to have the following RectFill() clear all the line + */ + left = lvdi->lvdi_Bounds.MinX; + else + { + DrawImageState (rp, img, + lvdi->lvdi_Bounds.MinX, lvdi->lvdi_Bounds.MinY, + lvdi->lvdi_State, lvdi->lvdi_DrawInfo); + + left = lvdi->lvdi_Bounds.MinX + img->Width; + } + + /* Now clear the rest of the row. rp->cp_x is updated by Text() to the + * next character to print. + */ + SetAPen (rp, lvdi->lvdi_DrawInfo->dri_Pens[(lvdi->lvdi_State == LVR_NORMAL) ? + BACKGROUNDPEN : FILLPEN]); + RectFill (rp, left, + lvdi->lvdi_Bounds.MinY, + lvdi->lvdi_Bounds.MaxX, + lvdi->lvdi_Bounds.MaxY); + + return LVCB_OK; +} diff --git a/SMakefile b/SMakefile new file mode 100644 index 0000000..f985286 --- /dev/null +++ b/SMakefile @@ -0,0 +1,136 @@ +## +## $VER: LVDemo_Makefile 2.2 (14.9.97) +## +## Copyright (C) 1996,97 by Bernardo Innocenti +## +## + +########################################################### +# Name of the main executable +########################################################### +# +PROJ = LVDemo + + +########################################################### +# Package configuration +########################################################### +# + +# set to OS30_ONLY to leave out support for old V37 +# set to ANY_OS to make an executable for V37 with V39 support +# +#OSVER = ANY_OS +OSVER = OS30_ONLY + +# Cpu to compile for (eg: "68020"). +# +#CPU = 68000 +CPU = 68020 + +########################################################### +# Object files in this project +########################################################### +# +OBJS = startup_sc.o LVDemo.o ListViewHooks.o \ + ListViewClass.o ListBoxClass.o ScrollButtonClass.o + + +########################################################### +# Make the project +########################################################### +# +all: $(PROJ) + + +########################################################### +# Remove all targets and intermediate files +########################################################### +# +clean: + -Delete $(PROJ) $(OBJS) $(PROJ).gst + + +########################################################### +# Compiler, linker and assembler flags +########################################################### +# +# Note: Using the "STRINGSCONST" compiler option requires +# patched versions of the OS headers to work correctly +# + +# Compiler flags for both release and debug versions +# +COMMON_CFLAGS = PARAMETERS=REGISTERS STRINGMERGE NOSTACKCHECK NOCHECKABORT \ + NOICONS NOVERSION ERRORREXX NOLINK DATA=NEAR CODE=NEAR \ + STRSECT=CODE STRINGSCONST GST $(PROJ).gst DEF=$(OSVER) CPU=$(CPU) + +# Compiler optimization flags +# +OPT_CFLAGS = OPTIMIZE OPTTIME OPTSCHEDULER OPTINLINELOCAL \ + OPTRDEPTH=4 OPTDEPTH=4 OPTCOMP=8 + +# Debug flags: don't optimize and include all symbols in debug hunks +# +DEBUG_CFLAGS = NOOPTIMIZE DEBUG=FULLFLUSH ONERROR=CONTINUE DEF DEBUG CODE=FAR + +# Use the utility.library for 32bit multiplication and division. +# +UTILLIB_LFLAGS = DEFINE __CXM33=__UCXM33 DEFINE __CXD33=__UCXD33 \ + DEFINE __CXM22=__UCXM22 DEFINE __CXD22=__UCXD22 + + +# RELEASE version should be compiled with these flags +# +#CFLAGS = $(COMMON_CFLAGS) $(OPT_CFLAGS) +#LFLAGS = NODEBUG SMALLCODE SMALLDATA NOALVS NOICONS $(UTILLIB_LFLAGS) +#LIBS = LIB LIB:sc.lib + + +# DEBUG version should be compiled with these flags +# +CFLAGS = $(COMMON_CFLAGS) $(DEBUG_CFLAGS) +LFLAGS = ADDSYM SMALLCODE SMALLDATA BATCH NOALVS NOICONS $(UTILLIB_LFLAGS) +LIBS = LIB LIB:debug.lib LIB:sc.lib LIB:small.lib + + +########################################################### +# Make Global Symbol Table to speed up compiling +########################################################### +# +# We must define some symbols here because defining them +# inside GST.c won't work as expected. +# +# NOTE: The GST file does not depend on ListViewClass.h because +# otherwise all objects would be remade whenever I slightly edit +# the header file. +# + +$(PROJ).gst: GST.c + $(CC) FROM GST.c MAKEGST $(PROJ).gst NOOBJNAME $(CFLAGS) \ + DEF=INTUI_V36_NAMES_ONLY DEF=__USE_SYSBASE \ + DEF=CLIB_ALIB_PROTOS_H DEF=LV_GADTOOLS_STUFF + +########################################################### +# Make the executable +########################################################### +# +# NOTE: Using implicit make rule to compile C files: +# .c.o: +# $(CC) $(CFLAGS) $(*).c +# +# NOTE: Using implicit make rule to assemble startup_sc.s +# + +$(PROJ): $(PROJ).gst $(OBJS) + $(LD) FROM $(OBJS) TO $(PROJ) $(LIBS) $(LFLAGS) + + +########################################################### +# Dependencies +########################################################### +# +ListViewClass.o: ListViewClass.c +ListBoxClass.o: ListBoxClass.c +ScrollButtonClass.o: ScrollButtonClass.c +LVDemo.o: LVDemo.c diff --git a/ScrollButtonClass.c b/ScrollButtonClass.c new file mode 100644 index 0000000..ebc0a77 --- /dev/null +++ b/ScrollButtonClass.c @@ -0,0 +1,147 @@ +/* +** ScrollButtonClass.c +** +** Copyright (C) 1998 Bernardo Innocenti +** +** Use 4 chars wide TABs to read this file +** +** Subclass of the buttongclass specialized for scroll buttons +*/ + +#define USE_BUILTIN_MATH +#define INTUI_V36_NAMES_ONLY +#define __USE_SYSBASE +#define CLIB_ALIB_PROTOS_H /* Avoid dupe defs of boopsi funcs */ + + +#include + +#include + + +#ifdef __STORM__ + #pragma header +#endif + +#include "CompilerSpecific.h" +#include "Debug.h" +#include "BoopsiStubs.h" + +#include "ScrollButtonClass.h" + + + + +/* Per object instance data */ +struct ScrollButtonData +{ + /* The number of ticks we still have to wait + * before sending any notification. + */ + ULONG TickCounter; +}; + + + +/* Dispatcher function prototype (just to keep esigent compilers quiet) */ + +static ULONG HOOKCALL ScrollButtonDispatcher ( + REG(a0, Class *cl), + REG(a2, struct Gadget *g), + REG(a1, struct gpInput *gpi)); + +static ULONG HOOKCALL ScrollButtonDispatcher ( + REG(a0, Class *cl), + REG(a2, struct Gadget *g), + REG(a1, struct gpInput *gpi)) + +/* ScrollButton Class Dispatcher entrypoint. + * Handle boopsi messages. + */ +{ + struct ScrollButtonData *bd = (struct ScrollButtonData *) INST_DATA(cl, g); + + switch (gpi->MethodID) + { + case GM_GOACTIVE: + /* May define an attribute to make delay configurable */ + bd->TickCounter = 3; + + /* Notify our target that we have initially hit. */ + NotifyAttrs ((Object *)g, gpi->gpi_GInfo, 0, + GA_ID, g->GadgetID, + TAG_DONE); + + /* Send more input */ + return GMR_MEACTIVE; + + case GM_HANDLEINPUT: + { + struct RastPort *rp; + ULONG retval = GMR_MEACTIVE; + UWORD selected = 0; + + /* This also works with classic (non-boopsi) images. */ + if (PointInImage ((gpi->gpi_Mouse.X << 16) + (gpi->gpi_Mouse.Y), g->GadgetRender)) + { + /* We are hit */ + selected = GFLG_SELECTED; + } + + if (gpi->gpi_IEvent->ie_Class == IECLASS_RAWMOUSE && gpi->gpi_IEvent->ie_Code == SELECTUP) + { + /* Gadgetup, time to go */ + retval = GMR_NOREUSE; + /* Unselect the gadget on our way out... */ + selected = 0; + } + else if (gpi->gpi_IEvent->ie_Class == IECLASS_TIMER) + { + /* We got a tick. Decrement counter, and if 0, send notify. */ + + if (bd->TickCounter) + bd->TickCounter--; + else if (selected) + /* Notify our target that we are still being hit */ + NotifyAttrs ((Object *)g, gpi->gpi_GInfo, 0, + GA_ID, g->GadgetID, + TAG_DONE); + } + + if ((g->Flags & GFLG_SELECTED) != selected) + { + /* Update changes in gadget render */ + g->Flags ^= GFLG_SELECTED; + if (rp = ObtainGIRPort (gpi->gpi_GInfo)) + { + DoMethod ((Object *) g, GM_RENDER, gpi->gpi_GInfo, rp, GREDRAW_UPDATE); + ReleaseGIRPort (rp); + } + } + return retval; + } + + default: + /* Super class handles everything else */ + return (DoSuperMethodA (cl, (Object *)g, (Msg) gpi)); + } +} + + + +Class *MakeScrollButtonClass (void) +{ + Class *class; + + if (class = MakeClass (NULL, BUTTONGCLASS, NULL, sizeof(struct ScrollButtonData), 0)) + class->cl_Dispatcher.h_Entry = (ULONG (*)()) ScrollButtonDispatcher; + + return class; +} + + + +BOOL FreeScrollButtonClass (Class *cl) +{ + return (FreeClass (cl)); +} diff --git a/ScrollButtonClass.h b/ScrollButtonClass.h new file mode 100644 index 0000000..560fa52 --- /dev/null +++ b/ScrollButtonClass.h @@ -0,0 +1,48 @@ +#ifndef SCROLLBUTTONCLASS_H +#define SCROLLBUTTONCLASS_H +/* +** ScrollButtonClass +** +** This code is inspierd from ScrollerWindow 0.3 +** Copyright © 1994 Christoph Feck, TowerSystems. +** +** Subclass of buttongclass. The ROM class has two problems, which make +** it not quite usable for scrollarrows. The first problem is the missing +** delay. Once the next INTUITICK gets sent by input.device, the ROM +** class already sends a notification. The other problem is that it also +** notifies us, when the button finally gets released (which is necessary +** for command buttons). +** +** We define a new class with the GM_GOACTIVE and GM_HANDLEINPUT method +** overloaded to work around these problems. +*/ + + +#define SCROLLBUTTONCLASS "scrollbuttonclass" +#define SCROLLBUTTONVERS 1 + + +/* Functions to initialize and destroy the class */ +Class *MakeScrollButtonClass (void); +BOOL FreeScrollButtonClass (Class *cl); + + + +/*****************/ +/* Class Methods */ +/*****************/ + +/* This class does not define any new methods */ + +/********************/ +/* Class Attributes */ +/********************/ + +/* #define SBA_Dummy (TAG_USER | ('S'<<16) | ('B'<<8)) */ + +/* This class does not define any new attributes */ + + + + +#endif /* !SCROLLBUTTONCLASS_H */ diff --git a/VectorGlyphIClass.h b/VectorGlyphIClass.h new file mode 100644 index 0000000..3998396 --- /dev/null +++ b/VectorGlyphIClass.h @@ -0,0 +1,26 @@ +/* +** VectorGlyphIClass.h +** +** Copyright (C) 1995,96,97,98 by Bernardo Innocenti +** +** "vectorglyphiclass" class, a vector image class built +** on top of the "imageclass", providing some useful +** glyphs for buttons. +*/ + +#define VECTORGLYPHCLASS "vectorglyphiclass" + +/* Values for the SYSIA_Which attribute */ +#define VG_PLAY 0 +#define VG_STOP 1 +#define VG_REW 2 +#define VG_FWD 3 +#define VG_PICK 4 +#define VG_UPARROW 5 +#define VG_DOWNARROW 6 +#define VG_LEFTARROW 7 +#define VG_RIGHTARROW 8 + + +/* Number of glyphs offered by the vectorglyph.image class */ +#define VG_IMGCOUNT 9 diff --git a/startup_gcc.s b/startup_gcc.s new file mode 100644 index 0000000..ca4a05d --- /dev/null +++ b/startup_gcc.s @@ -0,0 +1,2 @@ +.text + jmp __main diff --git a/startup_sc.s b/startup_sc.s new file mode 100644 index 0000000..a37c6b0 --- /dev/null +++ b/startup_sc.s @@ -0,0 +1,9 @@ + + XREF @_main + + SECTION CODE,code + +_start: + jmp @_main + + END diff --git a/startup_storm.s b/startup_storm.s new file mode 100644 index 0000000..912a205 --- /dev/null +++ b/startup_storm.s @@ -0,0 +1,9 @@ + + XREF __main + + SECTION CODE,code + +_start: + bra __main + + END -- 2.25.1