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