Initial commit
[amiga/OpenBoopsi.git] / gadgets / ListView / LVDemo.c
1 /*
2 **      $Id: LVDemo.c,v 1.3 2000/01/12 21:17:25 bernie Exp $
3 **
4 **      Introduction
5 **      ============
6 **
7 **      This program demonstrates how to use the `boopsi' ListView gadget.
8 **
9 **      The source code shows how to create a resizable window with sliders
10 **      and how to write a custom `boopsi' class on top of the gadgetclass.
11 **
12 **
13 **      Compiling
14 **      =========
15 **
16 **      This project can be compiled with SAS/C 6.58 or better and
17 **      GeekGadget's GCC 2.95.2 or better.
18 **      You get the smallest executable with SAS/C. GCC will give
19 **      you quite a lot of warnings with the tag calls. Don't worry about them.
20 **
21 **
22 **      History
23 **      =======
24 **
25 **      0.1 (23.6.96)   First try
26 **      1.0 (21.1.97)   First alpha release
27 **      1.1 (24.5.97)   Lotsa bugs fixed, implemented LVA_DoubleClick
28 **      1.2 (31.8.97)   Lotsa bugs fixed, implemented LVA_Clipped
29 **                                      Fixed memory leak with the test list
30 **      1.3 (5.9.97)    Improved initial sliders notification
31 **                                      Added multiple selection (LVA_DoMultiselect)
32 **                                      Fixed scrolling problems in some unusual conditions
33 **      1.4     (8.9.97)        Sets GMORE_SCROLLRASTER in OM_NEW
34 **                                      Multiple demo windows showing the features of the class
35 **      1.5 (14.9.97)   Added LVA_ItemSpacing
36 **                                      Finally fixed the LVA_Clipped mode!
37 **
38 **      1.6 (2.10.97)   Added StormC support
39 **                                      Implemented pixel-wise vertical scrolling for clipped mode
40 **                                      Reworked the class istance data and some code
41 **
42 **      1.7 (15.12.98)  Added ListBoxClass
43 **                                      Moved ScrollButtonClass in its own file
44 **                                      Removed function OpenClass()
45 **                                      Updated to latest version of VectorGlyphIClass
46 **
47 **
48 **      Known Bugs
49 **      ==========
50 **
51 **              - This code has never been tested on V37.
52 **
53 **              - Middle mouse button scrolling does not work because
54 **                of a bug in input.device V40.
55 **
56 **
57 **      Copyright Notice
58 **      ================
59 **
60 **      Copyright © 1996,97,98,00 by Bernardo Innocenti <bernie@cosmos.it>.
61 **      Freely Distributable, as long as source code, documentation and
62 **      executable are kept together. Permission is granted to release
63 **      modified versions of this program as long as all existing copyright
64 **      notices are left intact.
65 **
66 */
67
68 #define USE_BUILTIN_MATH
69 #define INTUI_V36_NAMES_ONLY
70 #define __USE_SYSBASE
71 #define  CLIB_ALIB_PROTOS_H     /* Avoid including this header file because of
72                                                          * conflicting definitions in BoopsiStubs.h
73                                                          */
74 #include <exec/types.h>
75 #include <exec/memory.h>
76 #include <exec/execbase.h>
77
78 #include <dos/dos.h>
79
80 #include <intuition/intuition.h>
81 #include <intuition/intuitionbase.h>
82 #include <intuition/screens.h>
83 #include <intuition/classes.h>
84 #include <intuition/gadgetclass.h>
85 #include <intuition/imageclass.h>
86 #include <intuition/icclass.h>
87 #include <devices/timer.h>
88
89 #include <proto/exec.h>
90 #include <proto/intuition.h>
91 #include <proto/dos.h>
92 #include <proto/utility.h>
93 #include <proto/graphics.h>
94 #include <proto/diskfont.h>
95
96 #ifdef __STORM__
97         #pragma header
98 #endif
99
100 #include <gadgets/ScrollButtonClass.h>
101 #include <gadgets/ListViewClass.h>
102 //#include <gadgets/ListBoxClass.h>
103 #include <images/VectorGlyphIClass.h>
104
105 #include "CompilerSpecific.h"
106 #include "DebugMacros.h"
107 #include "BoopsiStubs.h"
108 #include "ListMacros.h"
109
110
111 /* Local function prototypes */
112
113 LONG SAVEDS                                      main                           (void);
114 static struct MsgPort           *OpenDemoWindows        (struct List *winlist);
115 static void                                      CloseDemoWindows       (struct List *winlist);
116 static struct LVHandle          *OpenLVWin                      (CONST_STRPTR pubscreen,
117         struct MsgPort *winport, CONST_STRPTR title, ULONG mode, BOOL useListBox,
118         ULONG left, ULONG top, ULONG width, ULONG height, ULONG moreTags, ...);
119 static void                                      CloseLVWin                     (struct LVHandle *lvhandle);
120 static struct Gadget            *CreateLVGadgets        (struct LVHandle *lvhandle,
121         struct TagItem *moreTags);
122 static void                                      DisposeGadgets         (struct LVHandle *lvhandle);
123 static void                                      CreateItems            (struct LVHandle *lvhandle);
124 static void                                      CreateImages           (struct DrawInfo *dri);
125 static void                                      FreeImages                     (void);
126
127
128 /* Width and height for the demo vector images */
129 #define IMAGES_WIDTH    56
130 #define IMAGES_HEIGHT   48
131
132
133 /* Gadgets IDs */
134 enum
135 {
136         GAD_LV, GAD_VSLIDER, GAD_HSLIDER,
137         GAD_UPBUTTON, GAD_DOWNBUTTON, GAD_LEFTBUTTON, GAD_RIGHTBUTTON,
138         GAD_COUNT
139 };
140
141
142 /* Images IDs */
143 enum
144 {
145         IMG_UP, IMG_DOWN, IMG_LEFT, IMG_RIGHT, IMG_COUNT
146 };
147
148
149
150
151 /* This structure describes an open ListView window */
152 struct LVHandle
153 {
154         struct MinNode   Link;                  /* Link LVHandle in a list of all windows       */
155         struct Window   *Win;                   /* Pointer to our window                                        */
156         struct Screen   *Scr;                   /* The screen we are opening our windows on     */
157         struct DrawInfo *DrawInfo;              /* DrawInfo for this screen                             */
158         ULONG                    Mode;                  /* ListView operating mode                                      */
159         APTR                     Items;                 /* Items attached to the ListView                       */
160         ULONG                    Total;                 /* Number of items or -1 if unknown                     */
161         struct Gadget   *Gad[GAD_COUNT];/* All our gadgets                                                      */
162         APTR                     Model;                 /* Make boopsi gadgets talk to each other       */
163         struct List              TestList;              /* Items list for LVA_#?List modes                      */
164         ULONG                   *SelectArray;   /* Array for storing multiple selections        */
165         BOOL                     UseListBox;    /* If TRUE, window uses the ListBox class       */
166 };
167
168
169
170 /* Version tag */
171
172 UBYTE versiontag[] = "$VER: ListViewDemo 1.7 (15.12.98) by Bernardo Innocenti"
173         " (compiled with " _COMPILED_WITH ")";
174
175
176
177 /* Workaround a bug in StormC header file <proto/utility.h> */
178
179 #ifdef __STORM__
180         #define UTILITYBASETYPE struct Library
181 #else
182         #define UTILITYBASETYPE struct UtilityBase
183 #endif
184
185 /* Library bases */
186 struct ExecBase                         *SysBase;
187 UTILITYBASETYPE                         *UtilityBase;
188 struct IntuitionBase            *IntuitionBase;
189 struct GfxBase                          *GfxBase;
190 struct Library                          *LayersBase;
191 struct Library                          *DiskfontBase;
192
193
194 /* Our private `boopsi' classes */
195 Class                                           *ListViewClass;
196 //static Class                          *ListBoxClass;
197 static Class                            *ScrollButtonClass;
198
199
200 /* `boopsi' images for all windows
201  *
202  * These variables must be NULL at startup time. We are not
203  * going to explicitly initialize them because otherwise
204  * Storm C 2.0 would generate a C++-style constructor to do it :-).
205  * LoasSeg() will clear the BSS data section for us, so these
206  * variables are guaranteed to be NULL anyway.
207  */
208 static struct Image             *Img[IMG_COUNT];
209 static ULONG                     ImgWidth[IMG_COUNT];
210 static ULONG                     ImgHeight[IMG_COUNT];
211 static struct TextFont  *CustomFont;
212
213
214
215 /* Attribute translations for object interconnections */
216
217 static LONG MapLVToHSlider[] =
218 {
219         LVA_PixelLeft,          PGA_Top,
220         LVA_PixelWidth,         PGA_Total,
221         LVA_PixelHVisible,      PGA_Visible,
222         TAG_DONE
223 };
224
225 static LONG MapHSliderToLV[] =
226 {
227         PGA_Top,                        LVA_PixelLeft,
228         TAG_DONE
229 };
230
231 /*
232 static LONG MapLVToVSlider[] =
233 {
234         LVA_Top,                        PGA_Top,
235         LVA_Total,                      PGA_Total,
236         LVA_Visible,            PGA_Visible,
237         TAG_DONE
238 };
239 */
240
241 static LONG MapLVToVSlider[] =
242 {
243         LVA_PixelTop,           PGA_Top,
244         LVA_PixelHeight,        PGA_Total,
245         LVA_PixelVVisible,      PGA_Visible,
246         TAG_DONE
247 };
248
249
250 /*
251 static LONG MapVSliderToLV[] =
252 {
253         PGA_Top,        LVA_Top,
254         TAG_DONE
255 };
256 */
257
258 static LONG MapVSliderToLV[] =
259 {
260         PGA_Top,        LVA_PixelTop,
261         TAG_DONE
262 };
263
264
265
266 static LONG MapUpButtonToLV[] =
267 {
268         GA_ID,          LVA_MoveUp,
269         TAG_DONE
270 };
271
272 static LONG MapDownButtonToLV[] =
273 {
274         GA_ID,          LVA_MoveDown,
275         TAG_DONE
276 };
277
278 static LONG MapLeftButtonToLV[] =
279 {
280         GA_ID,          LVA_MoveLeft,
281         TAG_DONE
282 };
283
284 static LONG MapRightButtonToLV[] =
285 {
286         GA_ID,          LVA_MoveRight,
287         TAG_DONE
288 };
289
290
291
292 /* Test Strings */
293
294 /* StormC does not see that the expression "versiontag + 6" is constant
295  * and generates an initializer for it. The following definition
296  * works around this problem.
297  */
298 #ifdef __STORM__
299         #define VERSIONTAG "ListViewDemo 1.7 by Bernardo Innocenti (compiled with " _COMPILED_WITH ")"
300 #else
301         #define VERSIONTAG versiontag + 6
302 #endif
303
304 static STRPTR TestStrings[] =
305 {
306         VERSIONTAG,
307         NULL,
308         "This `boopsi' ListView class supports all the features",
309         "of the Gadtools LISTVIEW_KIND, plus more stuff:",
310         NULL,
311         " + Easy to use (almost a drop-in replacement for LISTVIEW_KIND)",
312         " + Can be resized and supports GREL_#? flags",
313         " + Multiple selection of items",
314         " + Notifies your `boopsi' sliders",
315         " + Multiple columns (TODO)",
316         " + Redraws quickly without clearing (which is good for solid window sizing)",
317         " + Horizontal scrolling (TODO)",
318         " + Items with `boopsi' images",
319         " + Using arrays instead of exec lists",
320         " + You can use `boopsi' label images instead of plain text",
321         " + You can use your own custom rendering hook",
322         " + You can use your own item item-retriving callback hook",
323         " + List title (TODO)",
324         " + Full Keyboard control (all control, alt and shift key combinations supported)",
325         " + Asynchronous scrolling with inertia (TODO)",
326         " + OS 3.0 optimized (V39-only version also available)",
327         " + RTG friendly and optimized (no planar stuff in chunky bitmaps)",
328         " + Small code! (<10K)",
329         " + Written in C to be highly portable across compilers and CPUs",
330         " + Full commented source code included",
331         " + Source code compiles with SAS/C, StormC and GCC",
332         " + Subclasses can be easlily derived from the base listview class",
333         NULL,
334         "Please send comments to <bernardo.innocenti@usa.net>."
335 };
336
337 #define TESTSTRINGS_CNT (sizeof (TestStrings) / sizeof (CONST_STRPTR))
338
339
340
341 LONG SAVEDS _main(void)
342
343 /* Main program entry point.  When linking without startup code, this
344  * must be the first function in the first object module listed on the
345  * linker command line.  We also need to initialize SysBase and open
346  * all needed libraries manually.
347  */
348 {
349         struct MinList   winlist;
350         struct MsgPort  *winport;
351         struct Library  *VectorGlyphBase = NULL;
352         LONG                     sigwait, sigrcvd;
353         LONG                     retval = RETURN_FAIL;  /* = RETURN_FAIL */
354         BOOL                     quit   = FALSE;
355
356
357         /* Initialize SysBase */
358         SysBase = *((struct ExecBase **)4UL);
359
360         /* Open system libraries */
361
362         if ((UtilityBase = (UTILITYBASETYPE *) OpenLibrary("utility.library", 37L)) &&
363                 (IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 39L)) &&
364                 (GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 39L)) &&
365                 (LayersBase = OpenLibrary("layers.library", 39L)))
366         {
367                 if ((ListViewClass = MakeListViewClass()) &&
368 //                      (ListBoxClass = MakeListBoxClass()) &&
369                         (ScrollButtonClass = MakeScrollButtonClass()) &&
370                         (VectorGlyphBase = OpenLibrary("images/vectorglyph.image", 0)))
371                 {
372                         NEWLIST((struct List *)&winlist);
373
374                         if (winport = OpenDemoWindows((struct List *)&winlist))
375                         {
376                                 /* Pre-calculate the signal mask for Wait() */
377                                 sigwait = (1 << winport->mp_SigBit) |
378                                         SIGBREAKF_CTRL_C;
379
380                                 /* Now for the main loop.  As you can see, it is really
381                                  * very compact.  That's the magic of boopsi! :-)
382                                  */
383                                 while (!quit)
384                                 {
385                                         /* Sleep until something interesting occurs */
386                                         sigrcvd = Wait(sigwait);
387
388                                         /* Now handle received signals */
389
390                                         /* Break signal? */
391                                         if (sigrcvd & SIGBREAKF_CTRL_C)
392                                                 quit = TRUE;
393
394                                         /* IDCMP message? */
395                                         if (sigrcvd & (1 << winport->mp_SigBit))
396                                         {
397                                                 struct IntuiMessage     *msg;
398
399                                                 while (msg = (struct IntuiMessage *) GetMsg(winport))
400                                                 {
401                                                         switch (msg->Class)
402                                                         {
403                                                                 case IDCMP_CLOSEWINDOW:
404                                                                         quit = TRUE;
405                                                                         break;
406
407                                                                 default:
408                                                                         break;
409                                                         }
410                                                         ReplyMsg((struct Message *) msg);
411                                                 }
412                                         }
413                                 } /* End while (!quit) */
414
415                                 retval = 0;     /* RETURN_OK */
416
417                                 CloseDemoWindows((struct List *)&winlist);
418                         }
419
420                         FreeImages ();
421                 }
422
423                 /* These cannot fail. Passing NULL is ok. */
424                 CloseLibrary((struct Library *)VectorGlyphBase);
425                 FreeScrollButtonClass(ScrollButtonClass);
426 //              FreeListBoxClass(ListBoxClass);
427                 FreeListViewClass(ListViewClass);
428         }
429
430         /* Passing NULL to CloseLibrary() was illegal in pre-V37 Exec.
431          * To avoid crashing when someone attempts to run this program
432          * on an old OS, we need to test the first library base we tried
433          * to open.
434          */
435         if (UtilityBase)
436         {
437                 CloseLibrary((struct Library *)LayersBase);
438                 CloseLibrary((struct Library *)GfxBase);
439                 CloseLibrary((struct Library *)IntuitionBase);
440                 CloseLibrary((struct Library *)UtilityBase);
441         }
442
443         return retval;
444 }
445
446
447
448 static struct MsgPort *OpenDemoWindows(struct List *winlist)
449 {
450         struct LVHandle *lvhandle;
451         struct MsgPort  *winport;
452
453         if (DiskfontBase = OpenLibrary("diskfont.library", 0L))
454         {
455                 static struct TextAttr attr =
456                 {
457                         "times.font",
458                         24,
459                         FSB_ITALIC,
460                         0
461                 };
462
463                 CustomFont = OpenDiskFont(&attr);
464                 CloseLibrary(DiskfontBase);
465         }
466
467         /* Setup windows shared Message Port */
468         if (winport = CreateMsgPort())
469         {
470 /*              if (lvhandle = OpenLVWin(NULL, winport,
471                         "ListBox test",
472                         LVA_StringList,         TRUE, 600, 20, 320, 128,
473                         TAG_DONE))
474                         ADDTAIL(winlist, (struct Node *)lvhandle);
475 */
476 /*              if (lvhandle = OpenLVWin(NULL, winport,
477                         "LVA_TextFont = times/24/italic, LVA_Clipped = TRUE",
478                         LVA_StringList,         FALSE, 320, 320, 320, 64,
479                         LVA_TextFont,           CustomFont,
480                         LVA_Clipped,            TRUE,
481                         TAG_DONE))
482                         ADDTAIL(winlist, (struct Node *)lvhandle);
483 */
484 /*              if (lvhandle = OpenLVWin(NULL, winport,
485                         "LAYOUTA_Spacing = 4",
486                         LVA_StringList,         FALSE, 256, 256, 320, 64,
487                         LAYOUTA_Spacing,        4,
488                         TAG_DONE))
489                         ADDTAIL(winlist, (struct Node *)lvhandle);
490 */
491 /*              if (lvhandle = OpenLVWin(NULL, winport,
492                         "GA_ReadOnly = TRUE; LVA_Selected = 3",
493                         LVA_StringList, FALSE, 192, 192, 320, 64,
494                         GA_ReadOnly,            TRUE,
495                         LVA_Selected,           3,
496                         TAG_DONE))
497                         ADDTAIL(winlist, (struct Node *)lvhandle);
498 */
499                 if (lvhandle = OpenLVWin(NULL, winport,
500                         "Single selection image list, LVA_Clipped = TRUE",
501                         LVA_ImageList, FALSE, 128, 128, 320, 128,
502                         LVA_ItemHeight,         IMAGES_HEIGHT,
503                         LVA_Clipped,            TRUE,
504                         TAG_DONE))
505                         ADDTAIL(winlist, (struct Node *)lvhandle);
506 /*
507                 if (lvhandle = OpenLVWin(NULL, winport,
508                         "LVA_DoMultiSelect = TRUE; LVA_StringArray",
509                         LVA_StringArray,        FALSE, 64, 64, 320, 128,
510                         LVA_DoMultiSelect,      TRUE,
511                         TAG_DONE))
512                         ADDTAIL(winlist, (struct Node *)lvhandle);
513 */
514 /*              if (lvhandle = OpenLVWin(NULL, winport,
515                         "Plain, single selection string list",
516                         LVA_StringList,         FALSE, 0, 20, 320, 128,
517                         TAG_DONE))
518                         ADDTAIL(winlist, (struct Node *)lvhandle);
519 */
520                 /* Abort only if no windows could be opened */
521                 if (IsListEmpty(winlist))
522                 {
523                         DeleteMsgPort(winport);
524                         CloseFont (CustomFont);
525                         return NULL;
526                 }
527         }
528
529         return winport;
530 }
531
532
533
534 static void CloseDemoWindows(struct List *winlist)
535 {
536         struct MsgPort  *winport = NULL;
537         struct LVHandle *lvhandle;
538
539         while (lvhandle = (struct LVHandle *) REMHEAD(winlist))
540         {
541                 /* Safe way to close a shared IDCMP port window */
542
543                 Forbid();
544                 {
545                         struct Node *succ;
546                         struct Message *msg;
547
548                         winport = lvhandle->Win->UserPort;
549                         msg = (struct Message *) winport->mp_MsgList.lh_Head;
550
551                         /* Now remove any pending message from the shared IDCMP port */
552                         while (succ = msg->mn_Node.ln_Succ)
553                         {
554                                 /* Since we are closing all our windows at once,
555                                  * we don't need to check to which window this
556                                  * message was addressed.
557                                  */
558                                 REMOVE((struct Node *)msg);
559                                 ReplyMsg (msg);
560                                 msg = (struct Message *) succ;
561                         }
562
563                         /* Keep intuition from freeing our port... */
564                         lvhandle->Win->UserPort = NULL;
565
566                         /* ...and from sending us any more messages. */
567                         ModifyIDCMP(lvhandle->Win, 0L);
568                 }
569                 Permit();
570
571                 CloseLVWin(lvhandle);
572         }
573
574         DeleteMsgPort(winport); /* NULL is ok */
575
576         if (CustomFont)
577                 CloseFont(CustomFont);
578 }
579
580
581
582
583 static struct LVHandle *OpenLVWin(CONST_STRPTR pubscreen,
584         struct MsgPort *winport, CONST_STRPTR title, ULONG mode, BOOL useListBox,
585         ULONG left, ULONG top, ULONG width, ULONG height, ULONG moreTags, ...)
586 {
587         struct LVHandle *lvhandle;
588         struct Gadget   *glist;
589
590         if (lvhandle = AllocMem(sizeof (struct LVHandle), MEMF_ANY | MEMF_CLEAR))
591         {
592                 if (lvhandle->Scr = LockPubScreen(pubscreen))
593                 {
594                         /* GetScreenDrawInfo() never fails */
595                         lvhandle->DrawInfo = GetScreenDrawInfo(lvhandle->Scr);
596
597                         /* Set listview operating mode and ListBox flag */
598                         lvhandle->Mode                  = mode;
599                         lvhandle->UseListBox    = useListBox;
600
601                         CreateItems (lvhandle);
602
603                         if (glist = CreateLVGadgets(lvhandle, (struct TagItem *)&moreTags))
604                         {
605                                 if (lvhandle->Win = OpenWindowTags(NULL,
606                                         WA_Top,                         top,
607                                         WA_Left,                        left,
608                                         WA_InnerWidth,          width,
609                                         WA_InnerHeight,         height,
610                                         WA_PubScreen,           lvhandle->Scr,
611                                         WA_Gadgets,                     glist,
612                                         WA_Title,                       title,
613                                         /* Since we are going to redraw the whole window anyway,
614                                          * we can disable backfilling to avoid flashes while
615                                          * resizing or revealing the window.
616                                          */
617                                         WA_BackFill,            useListBox ? NULL : LAYERS_NOBACKFILL,
618                                         WA_ScreenTitle,         versiontag + 6,
619                                         WA_Flags,                       WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_SIZEGADGET | WFLG_SIZEBRIGHT | WFLG_SIZEBBOTTOM |
620                                                                                 WFLG_CLOSEGADGET | WFLG_SIMPLE_REFRESH | WFLG_NOCAREREFRESH,
621                                         WA_PubScreenFallBack, TRUE,
622                                         WA_AutoAdjust,          TRUE,
623                                         WA_MinWidth,            64,
624                                         WA_MinHeight,           64,
625                                         WA_MaxWidth,            -1,
626                                         WA_MaxHeight,           -1,
627                                         TAG_DONE))
628                                 {
629                                         lvhandle->Win->UserPort = winport;
630                                         ModifyIDCMP(lvhandle->Win, IDCMP_CLOSEWINDOW);
631
632                                         /* We need to keep our screen locked all the time
633                                          * because we want to free the associated DrawInfo
634                                          * *after* the window has been closed and
635                                          * FreeScreenDrawInfo() wants a pointer to a *valid*
636                                          * Screen.
637                                          */
638                                         return lvhandle;
639                                 }
640
641                                 DisposeGadgets(lvhandle);
642                         }
643
644                         FreeScreenDrawInfo(lvhandle->Scr, lvhandle->DrawInfo);
645                         /* lvhandle->DrawInfo = NULL */
646
647                         UnlockPubScreen(NULL, lvhandle->Scr);
648                 }
649                 FreeMem(lvhandle, sizeof(struct LVHandle));
650         }
651         return NULL;
652 }
653
654
655
656 static void CloseLVWin(struct LVHandle *lvhandle)
657 {
658         /* Close our window. No need to reply queued messages,
659          * Intuition is clever enough to care about this for us.
660          */
661         CloseWindow(lvhandle->Win);
662         DisposeGadgets(lvhandle);
663         UnlockPubScreen(NULL, lvhandle->Scr);
664
665         FreeVec(lvhandle->SelectArray); /* NULL is ok */
666
667         if ((lvhandle->Mode == LVA_StringList) || (lvhandle->Mode == LVA_ImageList))
668         {
669                 struct Node *node;
670
671                 /* Free the test list */
672                 while (node = REMHEAD (&lvhandle->TestList))
673                 {
674                         if (lvhandle->Mode == LVA_ImageList)
675                                 DisposeObject ((Object *)node->ln_Name);
676                         FreeMem (node, sizeof (struct Node));
677                 }
678         }
679
680         FreeMem (lvhandle, sizeof (struct LVHandle));
681 }
682
683
684
685 /*      Diagram of object interconnections for the ListView window
686  *      ==========================================================
687  *
688  *                 ScrollButtonClass objects
689  *      +----------+ +------------+ +------------+ +-------------+
690  *      | UpButton | | DownButton | | LeftButton | | RightButton |
691  *      +----------+ +------------+ +------------+ +-------------+
692  *       | GA_ID =     | GA_ID =       | GA_ID =       | GA_ID =
693  *       | LVA_MoveUp  | LVA_MoveDown  | LVA_MoveLeft  | LVA_MoveRight
694  *       |             |               |               |
695  *       |  +----------+               |               |
696  *       |  |  +-----------------------+               |
697  *       |  |  |  +------------------------------------+
698  *       |  |  |  |        propgclass object     icclass object
699  *       |  |  |  |          +-----------+      +--------------+
700  *       |  |  |  |          |  HSlider  |<-----| PIPToHSlider |
701  *       |  |  |  |          +-----------+      +--------------+
702  *       |  |  |  |     PGA_Top =  |                 ^ LVA_Top  = PGA_Top
703  *       |  |  |  |     LVA_Left   |                 | LVA_Visible = PGA_Visible
704  *       |  |  |  |                |                 |
705  *       V  V  V  V                V                 |
706  *      +-----------+         ***********            |
707  *      |           |-------->*         *------------+
708  *      | ListView  |         *  Model  *
709  *      |           |<--------*         *------------+
710  *      +-----------+         ***********            |
711  *                                 ^                 |
712  *                     PGA_Top =   |                 |
713  *                     LVA_Top     |                 V  icclass object
714  *                           +-----------+      +--------------+
715  *                           |  VSlider  |<-----| PIPToVSlider |
716  *                           +-----------+      +--------------+
717  *                         propgclass object     LVA_Top     = PGA_Top
718  *                                               LVA_Visible = PGA_Visible
719  */
720
721 static struct Gadget *CreateLVGadgets(struct LVHandle *lvhandle,
722         struct TagItem *moreTags)
723 {
724         struct Screen   *scr = lvhandle->Scr;
725         ULONG SizeWidth = 18, SizeHeight = 11;  /* Default size */
726         struct Image    *SizeImage;
727
728         /* Create a size image to get its... uhm... size */
729         if (SizeImage = NewObject(NULL, SYSICLASS,
730                 SYSIA_Which,    SIZEIMAGE,
731                 SYSIA_DrawInfo, lvhandle->DrawInfo,
732                 TAG_DONE))
733         {
734                 /* Get size gadget geometry */
735                 GetAttr(IA_Width, SizeImage, &SizeWidth);
736                 GetAttr(IA_Height, SizeImage, &SizeHeight);
737
738                 /* And then get rid of it... */
739                 DisposeObject(SizeImage);
740         }
741
742
743 /*      if (lvhandle->UseListBox)
744         {
745                 lvhandle->Gad[GAD_LV] = NewObject(ListBoxClass, NULL,
746                         GA_ID,                          GAD_LV,
747                         GA_Left,                        scr->WBorLeft,
748                         GA_Top,                         scr->WBorTop + scr->Font->ta_YSize + 1,
749                         GA_RelWidth,            - SizeWidth - scr->WBorLeft,
750                         GA_RelHeight,           - (scr->WBorTop + scr->Font->ta_YSize + SizeHeight + 1),
751                         GA_DrawInfo,            lvhandle->DrawInfo,
752                         lvhandle->Mode,         lvhandle->Items,
753                         LVA_Total,                      lvhandle->Total,
754                         LVA_SelectArray,        lvhandle->SelectArray,
755                         TAG_MORE,                       moreTags);
756
757                 return lvhandle->Gad[GAD_LV];
758         }
759 */
760
761         /* Code to create normal ListView class and its gadgets */
762
763
764         /* No need to check this: in case of failure we would just
765          * get no images in the scroll buttons, but we can still try
766          * to open our window anyway.
767          */
768         CreateImages(lvhandle->DrawInfo);
769
770         if (lvhandle->Model = NewObjectA(NULL, MODELCLASS, NULL))
771                 if (lvhandle->Gad[GAD_LV] = NewObject(ListViewClass, NULL,
772                         GA_ID,                          GAD_LV,
773                         GA_Left,                        scr->WBorLeft,
774                         GA_Top,                         scr->WBorTop + scr->Font->ta_YSize + 1,
775                         GA_RelWidth,            - SizeWidth - scr->WBorLeft,
776                         GA_RelHeight,           - (scr->WBorTop + scr->Font->ta_YSize + SizeHeight + 1),
777                         GA_DrawInfo,            lvhandle->DrawInfo,
778                         ICA_TARGET,                     lvhandle->Model,
779                         lvhandle->Mode,         lvhandle->Items,
780                         LVA_Total,                      lvhandle->Total,
781                         LVA_SelectArray,        lvhandle->SelectArray,
782                         TAG_MORE,                       moreTags))
783                         if (lvhandle->Gad[GAD_VSLIDER] = NewObject(NULL, PROPGCLASS,
784                                 GA_ID,                  GAD_VSLIDER,
785                                 GA_Previous,    lvhandle->Gad[GAD_LV],
786                                 GA_RelRight,    - SizeWidth + 5,
787                                 GA_Top,                 scr->WBorTop + scr->Font->ta_YSize + 2,
788                                 GA_Width,               SizeWidth - 8,
789                                 GA_RelHeight,   - (scr->WBorTop + scr->Font->ta_YSize +
790                                                                 SizeHeight + ImgHeight[IMG_DOWN] + ImgHeight[IMG_UP] + 4),
791                                 GA_RightBorder, TRUE,
792                                 GA_DrawInfo,    lvhandle->DrawInfo,
793                                 PGA_Freedom,    FREEVERT,
794                                 PGA_Borderless, ((lvhandle->DrawInfo->dri_Flags & DRIF_NEWLOOK) &&
795                                                                  (lvhandle->DrawInfo->dri_Depth != 1)),
796                                 PGA_NewLook,    TRUE,
797                                 ICA_TARGET,             lvhandle->Model,
798                                 ICA_MAP,                MapVSliderToLV,
799                                 TAG_DONE))
800                                 if (lvhandle->Gad[GAD_HSLIDER] = NewObject(NULL, PROPGCLASS,
801                                         GA_ID,                  GAD_HSLIDER,
802                                         GA_Previous,    lvhandle->Gad[GAD_VSLIDER],
803                                         GA_RelBottom,   - SizeHeight + ((SizeHeight > 15) ? 4 : 3),
804                                         GA_Left,                scr->WBorLeft,
805                                         GA_Height,              SizeHeight - ((SizeHeight > 15)  ? 6 : 4),
806                                         GA_RelWidth,    - (SizeWidth + ImgWidth[IMG_RIGHT] + ImgWidth[IMG_LEFT] + scr->WBorLeft + 2),
807                                         GA_BottomBorder,TRUE,
808                                         GA_DrawInfo,    lvhandle->DrawInfo,
809                                         PGA_Freedom,    FREEHORIZ,
810                                         PGA_Borderless, ((lvhandle->DrawInfo->dri_Flags & DRIF_NEWLOOK) &&
811                                                                          (lvhandle->DrawInfo->dri_Depth != 1)),
812                                         PGA_NewLook,    TRUE,
813                                         ICA_TARGET,             lvhandle->Model,
814                                         ICA_MAP,                MapHSliderToLV,
815                                         TAG_DONE))
816                                         if (lvhandle->Gad[GAD_UPBUTTON] = NewObject(ScrollButtonClass, NULL,
817                                                 GA_ID,                  GAD_UPBUTTON,
818                                                 GA_Previous,    lvhandle->Gad[GAD_HSLIDER],
819                                                 GA_RelBottom,   - SizeHeight - ImgHeight[IMG_DOWN] - ImgHeight[IMG_UP] + 1,
820                                                 GA_RelRight,    - ImgWidth[IMG_DOWN] + 1,
821                                                 GA_RightBorder, TRUE,
822                                                 GA_DrawInfo,    lvhandle->DrawInfo,
823                                                 GA_Image,               Img[IMG_UP],
824                                                 ICA_TARGET,             lvhandle->Gad[GAD_LV],
825                                                 ICA_MAP,                MapUpButtonToLV,
826                                                 TAG_DONE))
827                                                 if (lvhandle->Gad[GAD_DOWNBUTTON] = NewObject(ScrollButtonClass, NULL,
828                                                         GA_ID,                  GAD_DOWNBUTTON,
829                                                         GA_Previous,    lvhandle->Gad[GAD_UPBUTTON],
830                                                         GA_RelBottom,   - SizeHeight - ImgHeight[IMG_DOWN] + 1,
831                                                         GA_RelRight,    - ImgWidth[IMG_DOWN] + 1,
832                                                         GA_RightBorder, TRUE,
833                                                         GA_DrawInfo,    lvhandle->DrawInfo,
834                                                         GA_Image,               Img[IMG_DOWN],
835                                                         ICA_TARGET,             lvhandle->Gad[GAD_LV],
836                                                         ICA_MAP,                MapDownButtonToLV,
837                                                         TAG_DONE))
838                                                         if (lvhandle->Gad[GAD_LEFTBUTTON] = NewObject(ScrollButtonClass, NULL,
839                                                                 GA_ID,                  GAD_LEFTBUTTON,
840                                                                 GA_Previous,    lvhandle->Gad[GAD_DOWNBUTTON],
841                                                                 GA_RelBottom,   - ImgHeight[IMG_LEFT] + 1,
842                                                                 GA_RelRight,    - SizeWidth - ImgWidth[IMG_RIGHT] - ImgWidth[IMG_LEFT] + 1,
843                                                                 GA_BottomBorder,TRUE,
844                                                                 GA_DrawInfo,    lvhandle->DrawInfo,
845                                                                 GA_Image,               Img[IMG_LEFT],
846                                                                 ICA_TARGET,             lvhandle->Gad[GAD_LV],
847                                                                 ICA_MAP,                MapLeftButtonToLV,
848                                                                 TAG_DONE))
849                                                                 if (lvhandle->Gad[GAD_RIGHTBUTTON] = NewObject(ScrollButtonClass, NULL,
850                                                                         GA_ID,                  GAD_RIGHTBUTTON,
851                                                                         GA_Previous,    lvhandle->Gad[GAD_LEFTBUTTON],
852                                                                         GA_RelBottom,   - ImgHeight[IMG_RIGHT] + 1,
853                                                                         GA_RelRight,    - SizeWidth - ImgWidth[IMG_RIGHT] + 1,
854                                                                         GA_BottomBorder,TRUE,
855                                                                         GA_DrawInfo,    lvhandle->DrawInfo,
856                                                                         GA_Image,               Img[IMG_RIGHT],
857                                                                         ICA_TARGET,             lvhandle->Gad[GAD_LV],
858                                                                         ICA_MAP,                MapRightButtonToLV,
859                                                                         TAG_DONE))
860                                                                         {
861                                                                                 APTR icobject;
862
863                                                                                 /* Connect VSlider to Model */
864
865                                                                                 if (icobject = NewObject(NULL, ICCLASS,
866                                                                                         ICA_TARGET,     lvhandle->Gad[GAD_VSLIDER],
867                                                                                         ICA_MAP,        MapLVToVSlider,
868                                                                                         TAG_DONE))
869                                                                                         if (!DoMethod(lvhandle->Model, OM_ADDMEMBER, icobject))
870                                                                                                 DisposeObject (icobject);
871
872                                                                                 /* Connect HSlider to Model */
873
874                                                                                 if (icobject = NewObject(NULL, ICCLASS,
875                                                                                         ICA_TARGET,     lvhandle->Gad[GAD_HSLIDER],
876                                                                                         ICA_MAP,        MapLVToHSlider,
877                                                                                         TAG_DONE))
878                                                                                         if (!DoMethod(lvhandle->Model, OM_ADDMEMBER, icobject))
879                                                                                                 DisposeObject (icobject);
880
881                                                                                 /* Connect Model to ListView */
882
883                                                                                 SetAttrs(lvhandle->Model,
884                                                                                         ICA_TARGET, lvhandle->Gad[GAD_LV],
885                                                                                         TAG_DONE);
886
887                                                                                 return lvhandle->Gad[GAD_LV];
888                                                                         }
889         DisposeGadgets(lvhandle);
890
891         return NULL;
892 }
893
894
895
896 static void DisposeGadgets(struct LVHandle *lvhandle)
897 {
898         ULONG i;
899
900         for (i = 0; i < GAD_COUNT; i++)
901         {
902                 DisposeObject(lvhandle->Gad[i]);
903                 /* lvhandle->Gad[i] = NULL; */
904         }
905
906         /* Freeing the Model will also free its two targets */
907         DisposeObject(lvhandle->Model);
908         /* lvhandle->Model = NULL */
909 }
910
911
912
913 static void CreateItems(struct LVHandle *lvhandle)
914 {
915         if ((lvhandle->Mode == LVA_StringList) || (lvhandle->Mode == LVA_ImageList))
916         {
917                 struct Node             *node;
918                 ULONG                    i, cnt;
919
920
921                 if (lvhandle->Mode == LVA_StringList)
922                         cnt = TESTSTRINGS_CNT;
923                 else /* LVA_ImageList */
924                         cnt = VG_IMGCOUNT * 8;
925
926
927                 /* Build a list of nodes to test the list */
928
929                 NEWLIST(&lvhandle->TestList);
930
931                 for (i = 0; i < cnt; i++)
932                 {
933                         if (node = AllocMem(sizeof (struct Node), MEMF_PUBLIC))
934                         {
935                                 if (lvhandle->Mode == LVA_StringList)
936                                         node->ln_Name = TestStrings[i];
937                                 else
938                                         node->ln_Name = (STRPTR) NewObject(NULL, VECTORGLYPHCLASS,
939                                                 SYSIA_Which,    i % VG_IMGCOUNT,
940                                                 SYSIA_DrawInfo, lvhandle->DrawInfo,
941                                                 IA_Width,               IMAGES_WIDTH,
942                                                 IA_Height,              IMAGES_HEIGHT,
943                                                 TAG_DONE);
944
945                                 /* Unselect all items */
946                                 node->ln_Type = 0;
947
948                                 ADDTAIL (&lvhandle->TestList, node);
949
950                                 lvhandle->Total++;
951                         }
952                 }
953
954                 lvhandle->Items = &lvhandle->TestList;
955         }
956         else if (lvhandle->Mode == LVA_StringArray)
957         {
958                 lvhandle->Items = TestStrings;
959                 lvhandle->Total = TESTSTRINGS_CNT;
960                 lvhandle->SelectArray = AllocVec (TESTSTRINGS_CNT * sizeof (ULONG),
961                         MEMF_CLEAR | MEMF_PUBLIC);
962         }
963         else /* (lvhandle->Mode == LVA_ImageArray) */
964         {
965                 lvhandle->Items = NULL; /* No items     */
966                 lvhandle->Total = -1;   /* Unknown      */
967         }
968 }
969
970
971
972 static void CreateImages(struct DrawInfo *dri)
973
974 /* Create 4 arrow images for the window scroll buttons.
975  *
976  * Why bother checking for failure? The arrow images are not
977  * life critical in our program...
978  */
979 {
980         static ULONG imagetypes[IMG_COUNT] = { UPIMAGE, DOWNIMAGE, LEFTIMAGE, RIGHTIMAGE };
981         ULONG i;
982
983         for (i = 0; i < IMG_COUNT; i++)
984                 if (!Img[i])
985                         if (Img[i] = (struct Image *)NewObject (NULL, SYSICLASS,
986                                 SYSIA_Which,    imagetypes[i],
987                                 SYSIA_DrawInfo, dri,
988                                 TAG_DONE))
989                         {
990                                 /* Ask image width and height */
991                                 GetAttr(IA_Width, Img[i], &ImgWidth[i]);
992                                 GetAttr(IA_Height, Img[i], &ImgHeight[i]);
993                         }
994 }
995
996
997
998 static void FreeImages (void)
999 {
1000         ULONG i;
1001
1002         for (i = 0; i < IMG_COUNT; i++)
1003                 DisposeObject((APTR)Img[i]);    /* DisposeObject(NULL) is safe */
1004 }