Initial commit
[amiga/BoopsiListView.git] / ListBoxClass.c
1 /*
2 **      ListBoxClass.c
3 **
4 **      Copyright (C) 1997,98 Bernardo Innocenti
5 **
6 **      Use 4 chars wide TABs to read this file
7 **
8 **      GadTools-like `boopsi' ListView group class
9 */
10
11 #define USE_BUILTIN_MATH
12 #define INTUI_V36_NAMES_ONLY
13 #define __USE_SYSBASE
14 #define  CLIB_ALIB_PROTOS_H             /* Avoid dupe defs of boopsi funcs */
15
16 #include <exec/types.h>
17 #include <exec/memory.h>
18 #include <intuition/intuition.h>
19 #include <intuition/intuitionbase.h>
20 #include <intuition/classes.h>
21 #include <intuition/gadgetclass.h>
22 #include <intuition/icclass.h>
23 #include <intuition/imageclass.h>
24
25 #include <proto/exec.h>
26 #include <proto/intuition.h>
27 #include <proto/utility.h>
28
29 #ifdef __STORM__
30         #pragma header
31 #endif
32
33 #include "CompilerSpecific.h"
34 #include "Debug.h"
35 #include "BoopsiStubs.h"
36
37 #define LV_GADTOOLS_STUFF
38 #include "ScrollButtonClass.h"
39 #include "ListViewClass.h"
40 #include "VectorGlyphIClass.h"
41 #include "ListBoxClass.h"
42
43
44
45 #define VSLIDER_WIDTH   14
46 #define HSLIDER_HEIGHT  14
47
48
49 /* Per-object instance data */
50 struct LBData
51 {
52         /* struct Gadget *ThisGadget; (not used) */
53
54         /* Group children */
55         Object *ListView;
56         Object *HSlider;
57         Object *VSlider;
58         Object *UpButton;
59         Object *DownButton;
60         Object *LeftButton;
61         Object *RightButton;
62         Object *LVToVSliderIC;
63         Object *LVToHSliderIC;
64
65         Object *Model;  /* The ic object that makes our children talk to each other */
66         Object *Frame;  /* The frame to put around the listbox object */
67
68         struct Image *UpImage;
69         struct Image *DownImage;
70         struct Image *LeftImage;
71         struct Image *RightImage;
72
73         /* Frame size */
74         LONG    FrameWidth, FrameHeight;
75
76         /* These two have the same meaning, but we keep both updated
77          * because the Rectangle structure (MinX, MinY, MaxX, MaxY)
78          * is more handy in some cases, while the IBox structure
79          * (Left/Top/Width/Height) is best for other cases.
80          */
81         struct IBox              GBox;
82         struct Rectangle GRect;
83 };
84
85
86 /* Global class data */
87 struct LBClassData
88 {
89         Class           *ScrollButtonClass;
90         struct Library  *VectorGlyphBase;
91 };
92
93
94 extern Class *ListViewClass;
95
96
97 /* Local function prototypes */
98 static void             LB_GMRender             (Class *cl, struct Gadget *g, struct gpRender *msg);
99 static void             LB_GMLayout             (Class *cl, struct Gadget *g, struct gpLayout *msg);
100 static ULONG    LB_OMSet                (Class *cl, struct Gadget *g, struct opUpdate *msg);
101 static ULONG    LB_OMGet                (Class *cl, struct Gadget *g, struct opGet *msg);
102 static ULONG    LB_OMNew                (Class *cl, struct Gadget *g, struct opSet *msg);
103 static void             LB_OMDispose    (Class *cl, struct Gadget *g, Msg msg);
104
105 static void             CreateVSlider   (Class *cl, struct Gadget *g, struct LBData *lb, struct DrawInfo *dri);
106 static void             CreateHSlider   (Class *cl, struct Gadget *g, struct LBData *lb, struct DrawInfo *dri);
107 static void             DeleteVSlider   (struct Gadget *g, struct LBData *lb);
108 static void             DeleteHSlider   (struct Gadget *g, struct LBData *lb);
109
110 INLINE void             GetGadgetBox    (struct GadgetInfo *ginfo, struct ExtGadget *g, struct IBox *box, struct Rectangle *rect);
111
112
113
114 /* Attribute translations for object interconnections */
115
116 static LONG MapLVToHSlider[] =
117 {
118         LVA_PixelLeft,          PGA_Top,
119         LVA_PixelWidth,         PGA_Total,
120         LVA_PixelHVisible,      PGA_Visible,
121         TAG_DONE
122 };
123
124 static LONG MapHSliderToLV[] =
125 {
126         PGA_Top,                        LVA_PixelLeft,
127         TAG_DONE
128 };
129
130 /*
131 static LONG MapLVToVSlider[] =
132 {
133         LVA_Top,                        PGA_Top,
134         LVA_Total,                      PGA_Total,
135         LVA_Visible,            PGA_Visible,
136         TAG_DONE
137 };
138 */
139
140 static LONG MapLVToVSlider[] =
141 {
142         LVA_PixelTop,           PGA_Top,
143         LVA_PixelHeight,        PGA_Total,
144         LVA_PixelVVisible,      PGA_Visible,
145         TAG_DONE
146 };
147
148
149 /*
150 static LONG MapVSliderToLV[] =
151 {
152         PGA_Top,        LVA_Top,
153         TAG_DONE
154 };
155 */
156
157 static LONG MapVSliderToLV[] =
158 {
159         PGA_Top,        LVA_PixelTop,
160         TAG_DONE
161 };
162
163
164
165 static LONG MapUpButtonToLV[] =
166 {
167         GA_ID,          LVA_MoveUp,
168         TAG_DONE
169 };
170
171 static LONG MapDownButtonToLV[] =
172 {
173         GA_ID,          LVA_MoveDown,
174         TAG_DONE
175 };
176
177 static LONG MapLeftButtonToLV[] =
178 {
179         GA_ID,          LVA_MoveLeft,
180         TAG_DONE
181 };
182
183 static LONG MapRightButtonToLV[] =
184 {
185         GA_ID,          LVA_MoveRight,
186         TAG_DONE
187 };
188
189
190
191 static ULONG HOOKCALL LBDispatcher (
192         REG(a0, Class *cl),
193         REG(a2, struct Gadget *g),
194         REG(a1, Msg msg))
195 {
196         ASSERT_VALIDNO0(cl)
197         ASSERT_VALIDNO0(g)
198         ASSERT_VALIDNO0(msg)
199
200         switch (msg->MethodID)
201         {
202                 case GM_RENDER:
203                         LB_GMRender (cl, g, (struct gpRender *)msg);
204                         return TRUE;
205
206                 case GM_LAYOUT:
207                         /* This method is only supported on V39 and above */
208                         LB_GMLayout (cl, g, (struct gpLayout *)msg);
209                         return TRUE;
210
211                 case OM_SET:
212                 case OM_UPDATE:
213                         return LB_OMSet (cl, g, (struct opUpdate *)msg);
214
215                 case OM_GET:
216                         return LB_OMGet (cl, g, (struct opGet *)msg);
217
218                 case OM_NEW:
219                         return LB_OMNew (cl, g, (struct opSet *)msg);
220
221                 case OM_DISPOSE:
222                         LB_OMDispose (cl, g, msg);
223                         return TRUE;
224
225                 default:
226                         /* Unsupported method: let our superclass's dispatcher take
227                          * a look at it. This includes all gadget methods sent
228                          * by Intuition: GM_RENDER, GM_HANDLEINPUT, GM_GOACTIVE and
229                          * GM_GOINACTIVE. These methods are automatically forwarded
230                          * to our child gadgets by the groupgclass.
231                          */
232                         /* DB(kprintf("ListBoxClass: passing unknown method 0x%lx to superclass...\n", msg->MethodID);)
233                          */
234                         return DoSuperMethodA (cl, (Object *)g, msg);
235         }
236 }
237
238
239
240 static void LB_GMRender (Class *cl, struct Gadget *g, struct gpRender *msg)
241 {
242         struct LBData           *lb = INST_DATA (cl, g);
243
244         ASSERT_VALIDNO0(lb)
245
246
247 #ifndef OS30_ONLY
248         /* Pre-V39 Intuition won't call our GM_LAYOUT method, so we must
249          * always call it before redrawing the gadget.
250          */
251         if ((IntuitionBase->LibNode.lib_Version < 39) &&
252                 (msg->gpr_Redraw == GREDRAW_REDRAW))
253                 LB_GMLayout (cl, g, (struct gpLayout *)msg);
254 #endif /* !OS30_ONLY */
255
256
257         /* The groupgclass does not render its imagery,
258          * it only calls GM_RENDER for all its children
259          */
260         if (msg->gpr_Redraw == GREDRAW_REDRAW)
261         {
262                 DB (kprintf ("ListBoxClass: GM_RENDER: msg->gpr_Redraw = GREDRAW_REDRAW\n");)
263
264                 DoMethod (lb->Frame, IM_DRAWFRAME,
265                         msg->gpr_RPort,                                                         /* imp_RPort            */
266                         (lb->GBox.Left << 16) | (lb->GBox.Top),         /* imp_Offset           */
267                         IDS_NORMAL,                                                                     /* imp_State            */
268                         msg->gpr_GInfo->gi_DrInfo,                                      /* imp_DrInfo           */
269                         (lb->GBox.Width << 16) | (lb->GBox.Height));/* imp_Dimensions   */
270         }
271
272         /* Forward message to superclass so it will call it on all our children */
273         DoSuperMethodA (cl, (Object *)g, (Msg)msg);
274 }
275
276
277
278 static void LB_GMLayout (Class *cl, struct Gadget *g, struct gpLayout *msg)
279 {
280         struct LBData   *lb = (struct LBData *) INST_DATA (cl, (Object *)g);
281
282         DB (kprintf ("ListBoxClass: GM_LAYOUT\n");)
283         ASSERT_VALIDNO0(lb)
284
285
286         /* Collect new group size */
287         GetGadgetBox (msg->gpl_GInfo, (struct ExtGadget *)g, &lb->GBox, &lb->GRect);
288
289         /* Size our children accordingly */
290         SetAttrs (lb->ListView,
291                 // msg->gpl_GInfo->gi_Window, msg->gpl_GInfo->gi_Requester,
292                 GA_Left,        lb->GBox.Left + 2,                                              // +lb->FrameWidth,
293                 GA_Top,         lb->GBox.Top + 2,                                               // +lb->FrameHeight,
294                 GA_Width,       lb->GBox.Width - lb->FrameWidth - VSLIDER_WIDTH,
295                 GA_Height,      lb->GBox.Height - HSLIDER_HEIGHT,               // -lb->FrameHeight,
296                 TAG_DONE);
297
298         SetAttrs (lb->VSlider,
299                 // msg->gpl_GInfo->gi_Window, msg->gpl_GInfo->gi_Requester,
300                 GA_Left,        lb->GRect.MaxX - 2 - VSLIDER_WIDTH,             // lb->FrameWidth,
301                 GA_Top,         lb->GBox.Top + 2,                                               // lb->FrameHeight,
302                 GA_Width,       VSLIDER_WIDTH,
303                 GA_Height,      lb->GBox.Height - HSLIDER_HEIGHT * 3,   // g->Height - 10,
304                 TAG_DONE);
305
306         SetAttrs (lb->UpButton,
307                 GA_Left,        lb->GRect.MaxX - 2 - VSLIDER_WIDTH,             // lb->FrameWidth,
308                 GA_Top,         lb->GRect.MaxY - 2 - HSLIDER_HEIGHT * 2,// lb->FrameHeight,
309                 GA_Width,       VSLIDER_WIDTH,
310                 GA_Height,      HSLIDER_HEIGHT,
311                 TAG_DONE);
312
313         DB (kprintf ("ListBoxClass: GM_LAYOUT: upbutton left: %ld, upbutton top: %ld\n",
314                 lb->GRect.MaxX - 2 - VSLIDER_WIDTH, lb->GRect.MaxY - 2 - HSLIDER_HEIGHT * 2);)
315
316
317         SetAttrs (lb->DownButton,
318                 GA_Left,        lb->GRect.MaxX - 2 - VSLIDER_WIDTH,             // lb->FrameWidth,
319                 GA_Top,         lb->GRect.MaxY - 2 - HSLIDER_HEIGHT,    // lb->FrameHeight,
320                 GA_Width,       VSLIDER_WIDTH,
321                 GA_Height,      HSLIDER_HEIGHT,
322                 TAG_DONE);
323
324
325         /* NOTE: it seems that the groupgclass does not forward GM_LAYOUT
326          * to its children, so we must handle this here.
327          */
328
329         /* Forward GM_LAYOUT to embedded listview */
330         DoMethodA (lb->ListView, (Msg)msg);
331 }
332
333
334
335 static ULONG LB_OMSet (Class *cl, struct Gadget *g, struct opUpdate *msg)
336 {
337         struct LBData   *lb = (struct LBData *) INST_DATA (cl, (Object *)g);
338
339         DB (kprintf ("ListBoxClass: OM_SET\n");)
340         ASSERT_VALIDNO0(lb)
341
342         if (lb->ListView)
343                 /* Forward attributes to our listview */
344                 DoMethodA (lb->ListView, (Msg)msg);
345
346         /* Also forward to our superclass */
347         return DoSuperMethodA (cl, (Object *)g, (Msg) msg);
348 }
349
350
351
352 static ULONG LB_OMGet (Class *cl, struct Gadget *g, struct opGet *msg)
353 {
354         struct LBData   *lb = (struct LBData *) INST_DATA (cl, (Object *)g);
355
356         DB (kprintf ("ListBoxClass: OM_GET\n");)
357         ASSERT_VALIDNO0(lb)
358
359         /* Forward this method to our listview */
360         return DoMethodA (lb->ListView, (Msg)msg);
361 }
362
363
364
365 static ULONG LB_OMNew (Class *cl, struct Gadget *g, struct opSet *msg)
366 {
367         struct LBData   *lb;
368         struct DrawInfo *dri;
369
370
371         DB (kprintf ("ListBoxClass: OM_NEW:\n");)
372
373         if (g = (struct Gadget *)DoSuperMethodA (cl, (Object *)g, (Msg)msg))
374         {
375                 /* Set the GMORE_SCROLLRASTER flag */
376                 if (g->Flags & GFLG_EXTENDED)
377                 {
378                         DB (kprintf ("ListBoxClass: OM_NEW: Setting GMORE_SCROLLRASTER\n");)
379                         ((struct ExtGadget *)g)->MoreFlags |= GMORE_SCROLLRASTER;
380                 }
381
382                 lb = (struct LBData *) INST_DATA (cl, (Object *)g);
383                 ASSERT_VALIDNO0(lb)
384
385
386                 /* Clear the object instance */
387                 memset (lb, 0, sizeof (struct LBData));
388
389
390                 /* Store a pointer to this object (a Gadget) in the class private
391                  * instance. This way we can avoid passing it to all functions.
392                  */
393                 /* lb->ThisGadget = g; (not used) */
394
395
396                 /* May be NULL */
397                 dri = (struct DrawInfo *) GetTagData (GA_DrawInfo, NULL, msg->ops_AttrList);
398
399
400                 /* Create a model object. This will be the core of the boopsi attributes
401                  * network used by the sub-objects to talk each other. We pass our
402                  * initalization tags to the model so it will pick up the correct
403                  * ICA_TARGET and ICA_MAP, if specified with NewObject().
404                  */
405                 if (lb->Model = NewObjectA (NULL, MODELCLASS, NULL))
406                 {
407                         /* Create the ListView and pass all creation time attributes to it.
408                          * Note that any GA_#? attributes are also passed to the listview,
409                          * so it will have the same size of its container.
410                          */
411                         if (lb->ListView = NewObjectA (ListViewClass, NULL, msg->ops_AttrList))
412                         {
413                                 /* From now no, the groupgclass will dispose this object for us */
414                                 DoMethod ((Object *)g, OM_ADDMEMBER, lb->ListView);
415
416                                 //SetAttrs (lb->Model,
417                                 //      ICA_TARGET, lb->ListView,
418                                 //      TAG_DONE);
419
420                                 /* Connect Model to ListView */
421                                 {
422                                         APTR icobject;
423
424                                         if (icobject = NewObject (NULL, ICCLASS,
425                                                 ICA_TARGET,     lb->ListView,
426                                                 TAG_DONE))
427                                                 if (!DoMethod (lb->Model, OM_ADDMEMBER, icobject))
428                                                         DisposeObject (icobject);
429                                 }
430
431                                 /* Connect ListView to Model */
432                                 SetAttrs (lb->ListView,
433                                         ICA_TARGET, lb->Model,
434                                         TAG_DONE);
435
436                                 /* Add sliders */
437                                 CreateVSlider (cl, g, lb, dri);
438                                 CreateHSlider (cl, g, lb, dri);
439
440
441                                 /* Create a frame to put around us */
442
443                                 if (lb->Frame = NewObject (NULL, FRAMEICLASS,
444                                         IA_EdgesOnly,   TRUE,
445                                         IA_FrameType,   FRAME_BUTTON,
446                                         TAG_DONE))
447                                 {
448                                         struct IBox FrameBox, ContentsBox = { 0, 0, 0, 0 };
449
450                                         /* Ask the frame about its nominal frame width and height */
451                                         DoMethod ((Object *)lb->Frame, IM_FRAMEBOX, &ContentsBox, &FrameBox, dri, 0);
452
453                                         /* Remember it later */
454                                         lb->FrameWidth = FrameBox.Width;
455                                         lb->FrameHeight = FrameBox.Height;
456                                 }
457
458
459                                 /* Set the gadget width and height because the groupgclass
460                                  * always forces them to 0 on creation.
461                                  */
462                                 {
463                                         struct TagItem *tag;
464
465                                         if (tag = FindTagItem (GA_RelWidth, msg->ops_AttrList))
466                                                 SetAttrs (g,
467                                                         GA_RelWidth, tag->ti_Data,
468                                                         TAG_DONE);
469                                         else
470                                                 SetAttrs (g,
471                                                         GA_Width, GetTagData (GA_Width, g->Width, msg->ops_AttrList),
472                                                         TAG_DONE);
473
474                                         if (tag = FindTagItem (GA_RelHeight, msg->ops_AttrList))
475                                                 SetAttrs (g,
476                                                         GA_RelHeight, tag->ti_Data,
477                                                         TAG_DONE);
478                                         else
479                                                 SetAttrs (g,
480                                                         GA_Height, GetTagData (GA_Height, g->Height, msg->ops_AttrList),
481                                                         TAG_DONE);
482
483                                         DB (kprintf ("ListBoxClass: OM_NEW: set size to L=%ld T=%ld W=%ld H=%ld\n",
484                                                 g->LeftEdge, g->TopEdge, g->Width, g->Height);)
485                                 }
486
487
488                                 /* TODO: Handle creation-time attributes */
489
490                                 return (ULONG)g;        /* Return newly created istance */
491                         }
492                 }
493
494
495                 /* Dispose object without disturbing the dispatchers of our sub-classes, if any */
496                 CoerceMethod (cl, (Object *)g, OM_DISPOSE);
497         }
498
499         return 0;       /* Fail */
500 }
501
502
503
504 static void LB_OMDispose (Class *cl, struct Gadget *g, Msg msg)
505 {
506         struct LBData   *lb = (struct LBData *) INST_DATA (cl, (Object *)g);
507
508         ASSERT_VALIDNO0(lb)
509         DB (kprintf ("ListBoxClass: OM_DISPOSE\n");)
510
511         DeleteHSlider(g, lb);
512         DeleteVSlider(g, lb);
513
514         /* Dispose our subobjects which are not freed automatically */
515         DisposeObject (lb->Frame);
516         DisposeObject (lb->Model);
517
518         /* Our superclass will cleanup everything else now */
519         DoSuperMethodA (cl, (Object *)g, (Msg) msg);
520
521         /* From now on, our instance data is no longer available */
522 }
523
524
525
526 #define SCROLLBUTTON_CLASS_PTR  ( ((struct LBClassData *)(cl->cl_UserData))->ScrollButtonClass )
527
528
529 static void CreateVSlider (Class *cl, struct Gadget *g, struct LBData *lb, struct DrawInfo *dri)
530 {
531         if (lb->VSlider = NewObject (NULL, PROPGCLASS,
532                 GA_ID,                  g->GadgetID,    /* Same as our ID */
533                 GA_DrawInfo,    dri,
534                 PGA_Freedom,    FREEVERT,
535                 PGA_NewLook,    TRUE,
536                 PGA_Borderless, TRUE,
537                 ICA_TARGET,             lb->Model,
538                 ICA_MAP,                MapVSliderToLV,
539                 TAG_DONE))
540         {
541                 /* From now on, the groupgclass will dispose this object for us */
542                 DoMethod ((Object *)g, OM_ADDMEMBER, lb->VSlider);
543
544                 /* Connect Model to ListView */
545                 SetAttrs (lb->Model,
546                         ICA_TARGET, lb->ListView,
547                         TAG_DONE);
548
549                 /* Connect VSlider to Model */
550                 if (lb->LVToVSliderIC = NewObject (NULL, ICCLASS,
551                         ICA_TARGET,     lb->VSlider,
552                         ICA_MAP,        MapLVToVSlider,
553                         TAG_DONE))
554                         if (!DoMethod (lb->Model, OM_ADDMEMBER, lb->LVToVSliderIC))
555                                 DisposeObject (lb->LVToVSliderIC);
556         }
557
558         /* We won't bother checking for failure because the image is
559          * not life-critical in our object
560          */
561         lb->UpImage = NewObject (NULL, SYSICLASS,
562                 SYSIA_Which,    UPIMAGE,
563                 SYSIA_DrawInfo, dri,
564                 TAG_DONE);
565
566         if (lb->UpButton = NewObject (SCROLLBUTTON_CLASS_PTR, NULL,
567                 GA_ID,                  g->GadgetID,
568                 GA_DrawInfo,    dri,
569                 GA_Image,               lb->UpImage,
570                 ICA_TARGET,             lb->ListView,
571                 ICA_MAP,                MapUpButtonToLV,
572                 TAG_DONE))
573         {
574                 /* From now on, the groupgclass will dispose this object for us */
575                 DoMethod ((Object *)g, OM_ADDMEMBER, lb->UpButton);
576         }
577
578         /* We won't bother checking for failure because the image is
579          * not life-critical in our object
580          */
581         lb->DownImage = NewObject (NULL, SYSICLASS,
582                 SYSIA_Which,    DOWNIMAGE,
583                 SYSIA_DrawInfo, dri,
584                 TAG_DONE);
585
586         if (lb->DownButton = NewObject (SCROLLBUTTON_CLASS_PTR, NULL,
587                 GA_ID,                  g->GadgetID,
588                 GA_DrawInfo,    dri,
589                 GA_Image,               lb->DownImage,
590                 ICA_TARGET,             lb->ListView,
591                 ICA_MAP,                MapDownButtonToLV,
592                 TAG_DONE))
593         {
594                 /* From now on, the groupgclass will dispose this object for us */
595                 DoMethod ((Object *)g, OM_ADDMEMBER, lb->DownButton);
596         }
597 }
598
599
600
601 static void CreateHSlider (Class *cl, struct Gadget *g, struct LBData *lb, struct DrawInfo *dri)
602 {
603         /* TODO */
604 }
605
606
607 static void DeleteVSlider (struct Gadget *g, struct LBData *lb)
608 {
609         if (lb->DownButton)
610         {
611                 ASSERT_VALID(lb->DownButton)
612
613                 DoMethod ((Object *)g, OM_REMMEMBER, lb->DownButton);
614                 DisposeObject (lb->DownButton);
615                 lb->DownButton = NULL;
616         }
617
618         if (lb->DownImage)
619         {
620                 ASSERT_VALID(lb->DownImage)
621
622                 DisposeObject (lb->DownImage);
623                 lb->DownImage = NULL;
624         }
625
626         if (lb->UpButton)
627         {
628                 ASSERT_VALID(lb->UpButton)
629
630                 DoMethod ((Object *)g, OM_REMMEMBER, lb->UpButton);
631                 DisposeObject (lb->UpButton);
632                 lb->UpButton = NULL;
633         }
634
635         if (lb->UpImage)
636         {
637                 ASSERT_VALID(lb->UpImage)
638
639                 DisposeObject (lb->UpImage);
640                 lb->UpImage = NULL;
641         }
642
643         if (lb->LVToVSliderIC)
644         {
645                 ASSERT_VALID(lb->LVToVSliderIC)
646                 ASSERT_VALID(lb->Model)
647
648                 DoMethod (lb->Model, OM_REMMEMBER, lb->LVToVSliderIC);
649                 DisposeObject (lb->LVToVSliderIC);
650                 lb->LVToVSliderIC = NULL;
651         }
652
653         if (lb->VSlider)
654         {
655                 ASSERT_VALID(lb->VSlider)
656
657                 DoMethod ((Object *)g, OM_REMMEMBER, lb->VSlider);
658                 DisposeObject (lb->VSlider);
659                 lb->VSlider = NULL;
660         }
661 }
662
663
664
665 static void DeleteHSlider (struct Gadget *g, struct LBData *lb)
666 {
667         /* TODO */
668 }
669
670
671
672 INLINE void GetGadgetBox (struct GadgetInfo *ginfo, struct ExtGadget *g, struct IBox *box, struct Rectangle *rect)
673
674 /* Gets the actual IBox where a gadget exists in a window.
675  * The special cases it handles are all the REL#? (relative positioning flags).
676  *
677  * This function returns the gadget size in both the provided IBox and
678  * Rectangle structures, computing the values from the coordinates of the
679  * gadget and the window where it lives.
680  */
681 {
682         ASSERT_VALIDNO0(g)
683         ASSERT_VALIDNO0(ginfo)
684         ASSERT_VALIDNO0(box)
685         ASSERT_VALIDNO0(rect)
686
687         DB (if (g->Flags & GFLG_EXTENDED)
688                 kprintf ("ListBoxClass: GetGadgetBox(): GFLG_EXTENDED is set\n");)
689
690         DB (if ((g->Flags & GFLG_EXTENDED) && (g->MoreFlags & GMORE_BOUNDS))
691                 kprintf ("ListBoxClass: GetGadgetBox(): Gadget has valid bounds\n");)
692
693         box->Left = g->LeftEdge;
694         if (g->Flags & GFLG_RELRIGHT)
695                 box->Left += ginfo->gi_Domain.Width - 1;
696
697         box->Top = g->TopEdge;
698         if (g->Flags & GFLG_RELBOTTOM)
699                 box->Top += ginfo->gi_Domain.Height - 1;
700
701         box->Width = g->Width;
702         if (g->Flags & GFLG_RELWIDTH)
703                 box->Width += ginfo->gi_Domain.Width;
704
705         box->Height = g->Height;
706         if (g->Flags & GFLG_RELHEIGHT)
707                 box->Height += ginfo->gi_Domain.Height;
708
709         /* Convert IBox to Rectangle coordinates system */
710         rect->MinX = box->Left;
711         rect->MinY = box->Top;
712         rect->MaxX = box->Left + box->Width - 1;
713         rect->MaxY = box->Top + box->Height - 1;
714
715
716         DB (kprintf ("ListBoxClass: GetGadgetBox(): Left = %ld, Top = %ld, Width = %ld, Height = %ld\n",
717                 box->Left, box->Top, box->Width, box->Height);)
718 }
719
720
721
722 Class *MakeListBoxClass (void)
723 {
724         Class *class;
725         struct LBClassData *classdata;
726
727         if (class = MakeClass (NULL, GROUPGCLASS, NULL, sizeof (struct LBData), 0))
728         {
729                 class->cl_Dispatcher.h_Entry = (ULONG (*)()) LBDispatcher;
730
731                 /* Allocate storage for global class data */
732                 if (classdata = AllocMem (sizeof (struct LBClassData), MEMF_PUBLIC | MEMF_CLEAR))
733                 {
734                         class->cl_UserData = (ULONG) classdata;
735
736                         classdata->ScrollButtonClass = MakeScrollButtonClass();
737                         classdata->VectorGlyphBase = OpenLibrary ("images/vectorglyph.image", 0);
738                         return class;
739                 }
740
741                 FreeListBoxClass (class);
742         }
743
744         return NULL;
745 }
746
747
748
749 void FreeListBoxClass (Class *class)
750 {
751         struct LBClassData *classdata;
752
753         if (class)
754         {
755                 ASSERT_VALID(class)
756                 if (classdata = (struct LBClassData *)class->cl_UserData)
757                 {
758                         ASSERT_VALID(classdata)
759                         ASSERT_VALID(classdata->ScrollButtonClass)
760                         ASSERT_VALID(classdata->VectorGlyphBase)
761
762                         /* Cleanup global class data */
763                         CloseLibrary (classdata->VectorGlyphBase); /* NULL is safe since V36 */
764                         FreeScrollButtonClass (classdata->ScrollButtonClass);
765                         FreeMem (classdata, sizeof (struct LBClassData));
766                 }
767
768                 FreeClass (class);
769         }
770 }