Initial commit
[amiga/OpenBoopsi.git] / gadgets / ListBox / ListBoxClass.c
1 /*
2 **      $Id: ListBoxClass.c,v 1.3 1999/01/30 13:25:03 bernie Exp $
3 **
4 **      Copyright (C) 1997,98,99 Bernardo Innocenti (<bernardo.innocenti@usa.net>)
5 **      All rights reserved.
6 **
7 **      Use 4 chars wide TABs to read this file
8 **
9 **      GadTools-like `boopsi' ListView group class
10 */
11
12 #define USE_BUILTIN_MATH
13 #define INTUI_V36_NAMES_ONLY
14 #define INTUITION_IOBSOLETE_H
15 #define __USE_SYSBASE
16 #define  CLIB_ALIB_PROTOS_H             /* Avoid dupe defines of boopsi funcs */
17
18 #include <exec/types.h>
19 #include <exec/memory.h>
20 #include <intuition/intuition.h>
21 #include <intuition/intuitionbase.h>
22 #include <intuition/classes.h>
23 #include <intuition/gadgetclass.h>
24 #include <intuition/icclass.h>
25 #include <intuition/imageclass.h>
26
27 #include <proto/exec.h>
28 #include <proto/intuition.h>
29 #include <proto/utility.h>
30
31 #ifdef __STORM__
32         #pragma header
33 #endif
34
35 #define LV_GADTOOLS_STUFF
36 #include <gadgets/ListBoxClass.h>
37
38 #include <CompilerSpecific.h>
39 #include <DebugMacros.h>
40 #include <BoopsiStubs.h>
41 #include <BoopsiLib.h>
42
43
44
45 /* Sliders dimensions
46  */
47 #define VSLIDER_WIDTH   18
48 #define HSLIDER_HEIGHT  18
49
50
51 /* Per-object instance data */
52 struct LBData
53 {
54         /* struct Gadget *ThisGadget; (not used) */
55
56         /* Group children */
57         Object *ListView;
58         Object *HSlider;
59         Object *VSlider;
60         Object *LVToVSliderIC;
61         Object *LVToHSliderIC;
62
63         Object *Model;                  /* The ic object that makes our children talk to each other */
64         Object *Frame;                  /* The frame to put around the child objects */
65
66         /* Frame size */
67         LONG    FrameWidth, FrameHeight;
68
69         /* These two have the same meaning, but we keep both updated
70          * because the Rectangle structure (MinX, MinY, MaxX, MaxY)
71          * is more handy in some cases, while the IBox structure
72          * (Left/Top/Width/Height) is best for other cases.
73          */
74         struct IBox              GBox;
75         struct Rectangle GRect;
76 };
77
78
79 /* Global class data */
80 struct LBClassData
81 {
82         Class                           *SliderBarGClass;
83         struct ClassLibrary     *SliderBarBase;
84 };
85
86
87
88 #define SLIDERBARGCLASS_PTR ( ((struct LBClassData *)(cl->cl_UserData))->SliderBarGClass )
89
90
91
92 extern Class *ListViewClass;
93
94
95 /* Local function prototypes */
96 static void             LB_GMLayout             (Class *cl, struct Gadget *g, struct gpLayout *msg);
97 static ULONG    LB_OMSet                (Class *cl, struct Gadget *g, struct opUpdate *msg);
98 static ULONG    LB_OMGet                (Class *cl, struct Gadget *g, struct opGet *msg);
99 static ULONG    LB_OMNew                (Class *cl, struct Gadget *g, struct opSet *msg);
100 static void             LB_OMDispose    (Class *cl, struct Gadget *g, Msg msg);
101
102 static void             CreateVSlider   (Class *cl, struct Gadget *g, struct LBData *lb, struct DrawInfo *dri);
103 static void             CreateHSlider   (Class *cl, struct Gadget *g, struct LBData *lb, struct DrawInfo *dri);
104 static void             DeleteVSlider   (struct Gadget *g, struct LBData *lb);
105 static void             DeleteHSlider   (struct Gadget *g, struct LBData *lb);
106
107
108
109
110 /* Attribute translations for object interconnections */
111
112 static LONG MapLVToHSlider[] =
113 {
114         LVA_PixelLeft,          PGA_Top,
115         LVA_PixelWidth,         PGA_Total,
116         LVA_PixelHVisible,      PGA_Visible,
117         TAG_DONE
118 };
119
120 static LONG MapHSliderToLV[] =
121 {
122         PGA_Top,                        LVA_PixelLeft,
123         TAG_DONE
124 };
125
126 static LONG MapLVToVSlider[] =
127 {
128         LVA_PixelTop,           PGA_Top,
129         LVA_PixelHeight,        PGA_Total,
130         LVA_PixelVVisible,      PGA_Visible,
131         TAG_DONE
132 };
133
134 static LONG MapVSliderToLV[] =
135 {
136         PGA_Top,        LVA_PixelTop,
137         TAG_DONE
138 };
139
140
141
142 static ULONG HOOKCALL LBDispatcher(
143         REG(a0, Class *cl),
144         REG(a2, struct Gadget *g),
145         REG(a1, Msg msg))
146 {
147         ASSERT_VALID_PTR(cl)
148         ASSERT_VALID_PTR(g)
149         ASSERT_VALID_PTR(msg)
150
151         switch (msg->MethodID)
152         {
153                 case GM_LAYOUT:
154                         /* This method is only supported on V39 and above */
155                         LB_GMLayout(cl, g, (struct gpLayout *)msg);
156                         return TRUE;
157
158                 case OM_SET:
159                 case OM_UPDATE:
160                         return LB_OMSet(cl, g, (struct opUpdate *)msg);
161
162                 case OM_GET:
163                         return LB_OMGet(cl, g, (struct opGet *)msg);
164
165                 case OM_NEW:
166                         return LB_OMNew(cl, g, (struct opSet *)msg);
167
168                 case OM_DISPOSE:
169                         LB_OMDispose(cl, g, msg);
170                         return TRUE;
171
172                 default:
173                         /* Unsupported method: let our superclass's dispatcher take
174                          * a look at it. This includes all gadget methods sent
175                          * by Intuition: GM_RENDER, GM_HANDLEINPUT, GM_GOACTIVE and
176                          * GM_GOINACTIVE. These methods are automatically forwarded
177                          * to our child gadgets by the groupgclass.
178                          */
179                         DB2 (DBPRINTF("ListBoxClass: passing unknown method 0x%lx to superclass\n", msg->MethodID);)
180                         return DoSuperMethodA (cl, (Object *)g, msg);
181         }
182 }
183
184
185
186 static void LB_GMLayout(Class *cl, struct Gadget *g, struct gpLayout *msg)
187 {
188         struct LBData *lb = (struct LBData *) INST_DATA(cl, (Object *)g);
189
190         DB(DBPRINTF("ListBoxClass: GM_LAYOUT\n");)
191         ASSERT_VALID_PTR(lb)
192
193
194         /* Collect new gadget size */
195         GetGadgetBox(msg->gpl_GInfo, (struct ExtGadget *)g, &lv->GBox);
196         IBoxToRect(&lv->GBox, &lv->GRect);
197
198
199         /* Size our children accordingly */
200         SetAttrs(lb->ListView,
201                 GA_Left,        lb->GBox.Left + lb->FrameWidth / 2,
202                 GA_Top,         lb->GBox.Top + lb->FrameHeight / 2,
203                 GA_Width,       lb->GBox.Width - lb->FrameWidth - VSLIDER_WIDTH,
204                 GA_Height,      lb->GBox.Height - lb->FrameHeight - HSLIDER_HEIGHT,
205                 TAG_DONE);
206
207         if (lb->VSlider)
208                 SetAttrs(lb->VSlider,
209                         GA_Left,        lb->GRect.MaxX - VSLIDER_WIDTH + 1,
210                         GA_Top,         lb->GBox.Top,
211                         GA_Width,       VSLIDER_WIDTH,
212                         GA_Height,      lb->GBox.Height,
213                         TAG_DONE);
214
215
216         /* NOTE: it seems that the groupgclass does not forward GM_LAYOUT
217          * to its children, so we must handle this here.
218          */
219         DoMethodA(lb->ListView, (Msg)msg);
220         if (lb->VSlider)        DoMethodA(lb->VSlider, (Msg)msg);
221 }
222
223
224
225 static ULONG LB_OMSet(Class *cl, struct Gadget *g, struct opUpdate *msg)
226 {
227         struct LBData *lb = (struct LBData *) INST_DATA(cl, (Object *)g);
228
229         DB2(DBPRINTF("ListBoxClass: OM_SET\n");)
230         ASSERT_VALID_PTR(lb)
231
232         if (lb->ListView)
233                 /* Forward attributes to our listview */
234                 DoMethodA (lb->ListView, (Msg)msg);
235
236         /* Also forward to our superclass */
237         return DoSuperMethodA (cl, (Object *)g, (Msg) msg);
238 }
239
240
241
242 static ULONG LB_OMGet(Class *cl, struct Gadget *g, struct opGet *msg)
243 {
244         struct LBData *lb = (struct LBData *) INST_DATA(cl, (Object *)g);
245
246         DB2(DBPRINTF("ListBoxClass: OM_GET\n");)
247         ASSERT_VALID_PTR(lb)
248
249         /* Forward this method to our listview */
250         return DoMethodA(lb->ListView, (Msg)msg);
251 }
252
253
254
255 static ULONG LB_OMNew(Class *cl, struct Gadget *g, struct opSet *msg)
256 {
257         struct LBData   *lb;
258         struct DrawInfo *dri;
259
260
261         DB2(DBPRINTF("ListBoxClass: OM_NEW\n");)
262
263         if (g = (struct Gadget *)DoSuperMethodA(cl, (Object *)g, (Msg)msg))
264         {
265                 /* Set the GMORE_SCROLLRASTER flag */
266                 if (g->Flags & GFLG_EXTENDED)
267                 {
268                         DB(DBPRINTF("ListBoxClass: OM_NEW: Setting GMORE_SCROLLRASTER\n");)
269                         ((struct ExtGadget *)g)->MoreFlags |= GMORE_SCROLLRASTER;
270                 }
271
272                 lb = (struct LBData *) INST_DATA(cl, (Object *)g);
273                 ASSERT_VALID_PTR(lb)
274
275
276                 /* Clear the object instance */
277                 memset(lb, 0, sizeof (struct LBData));
278
279
280                 /* Store a pointer to this object (a Gadget) in the class private
281                  * instance. This way we can avoid passing it along to all functions.
282                  */
283                 /* lb->ThisGadget = g; (not used) */
284
285
286                 /* May be NULL */
287                 dri = (struct DrawInfo *) GetTagData(GA_DrawInfo, NULL, msg->ops_AttrList);
288
289
290                 /* Create a frame to put around us */
291                 if (lb->Frame = NewObject(NULL, FRAMEICLASS,
292                         IA_EdgesOnly,   TRUE,
293                         IA_FrameType,   FRAME_BUTTON,
294                         TAG_DONE))
295                 {
296                         struct IBox FrameBox, ContentsBox = { 0, 0, 0, 0 };
297
298                         /* Ask the frame about its nominal frame width and height */
299                         DoMethod((Object *)lb->Frame, IM_FRAMEBOX, &ContentsBox, &FrameBox, dri, 0);
300
301                         /* Remember it later */
302                         lb->FrameWidth = FrameBox.Width;
303                         lb->FrameHeight = FrameBox.Height;
304                 }
305
306                 /* Create a model object. This will be the core of the boopsi attributes
307                  * network used by the sub-objects to talk each other. We pass our
308                  * initalization tags to the model so it will pick up the correct
309                  * ICA_TARGET and ICA_MAP, if specified with NewObject().
310                  */
311                 if (lb->Model = NewObjectA(NULL, MODELCLASS, NULL))
312                 {
313                         /* Create the ListView and pass all creation time attributes to it.
314                          * Note that any GA_#? attributes are also passed to the listview,
315                          * so it will have the same size of its container.
316                          */
317                         if (lb->ListView = NewObject(ListViewClass, NULL,
318                                 GA_Image,               lb->Frame,
319                                 TAG_MORE,               msg->ops_AttrList))
320                         {
321                                 /* From now on, the groupgclass will dispose this object for us */
322                                 DoMethod((Object *)g, OM_ADDMEMBER, lb->ListView);
323
324                                 /* Connect Model to ListView */
325                                 {
326                                         APTR icobject;
327
328                                         if (icobject = NewObject(NULL, ICCLASS,
329                                                 ICA_TARGET,     lb->ListView,
330                                                 TAG_DONE))
331                                                 if (!DoMethod(lb->Model, OM_ADDMEMBER, icobject))
332                                                         DisposeObject(icobject);
333                                 }
334
335                                 /* Connect ListView to Model */
336                                 SetAttrs(lb->ListView,
337                                         ICA_TARGET, lb->Model,
338                                         TAG_DONE);
339
340                                 /* Add sliders */
341                                 CreateVSlider(cl, g, lb, dri);
342                                 CreateHSlider(cl, g, lb, dri);
343
344                                 /* Set the gadget width and height because the groupgclass
345                                  * always forces them to 0 on creation.
346                                  */
347                                 {
348                                         struct TagItem *tag;
349
350                                         if (tag = FindTagItem(GA_RelWidth, msg->ops_AttrList))
351                                                 SetAttrs(g,
352                                                         GA_RelWidth, tag->ti_Data,
353                                                         TAG_DONE);
354                                         else
355                                                 SetAttrs (g,
356                                                         GA_Width, GetTagData(GA_Width, g->Width, msg->ops_AttrList),
357                                                         TAG_DONE);
358
359                                         if (tag = FindTagItem(GA_RelHeight, msg->ops_AttrList))
360                                                 SetAttrs(g,
361                                                         GA_RelHeight, tag->ti_Data,
362                                                         TAG_DONE);
363                                         else
364                                                 SetAttrs(g,
365                                                         GA_Height, GetTagData (GA_Height, g->Height, msg->ops_AttrList),
366                                                         TAG_DONE);
367
368                                         DB2(DBPRINTF("ListBoxClass: OM_NEW: size set to L=%ld T=%ld W=%ld H=%ld\n",
369                                                 g->LeftEdge, g->TopEdge, g->Width, g->Height);)
370                                 }
371
372                                 /* TODO: Handle creation-time attributes */
373
374                                 return (ULONG)g;        /* Return newly created istance */
375                         }
376                 }
377
378
379                 /* Dispose object without disturbing the dispatchers of our sub-classes, if any */
380                 CoerceMethod(cl, (Object *)g, OM_DISPOSE);
381         }
382
383         return 0;       /* Fail */
384 }
385
386
387
388 static void LB_OMDispose(Class *cl, struct Gadget *g, Msg msg)
389 {
390         struct LBData   *lb = (struct LBData *) INST_DATA(cl, (Object *)g);
391
392         DB(DBPRINTF("ListBoxClass: OM_DISPOSE\n");)
393         ASSERT_VALID_PTR(lb)
394
395
396         DeleteHSlider(g, lb);
397         DeleteVSlider(g, lb);
398
399         /* Dispose the child objects that aren't freed automatically by the modelclass.
400          * Note that here we are disposing the Frame before we dispose the gadgets that
401          * may be still using it. Everything will go well unless those classes will
402          * access the frame within their OM_DISPOSE methods (very silly).
403          */
404         DisposeObject(lb->Model);
405         DisposeObject(lb->Frame);
406
407         /* Our superclass will cleanup everything else now */
408         DoSuperMethodA(cl, (Object *)g, (Msg) msg);
409
410         /* From now on, our instance data is no longer available */
411 }
412
413
414
415 static void CreateVSlider(Class *cl, struct Gadget *g, struct LBData *lb, struct DrawInfo *dri)
416 {
417         if (lb->VSlider = NewObject(SLIDERBARGCLASS_PTR, NULL,
418                 GA_ID,                  g->GadgetID,    /* Same as our ID */
419                 GA_DrawInfo,    dri,
420                 GA_LabelImage,  lb->Frame,
421                 PGA_Freedom,    FREEVERT,
422                 PGA_NewLook,    TRUE,
423                 PGA_Borderless, TRUE,
424                 LAYOUTA_Orientation, LORIENT_VERT,
425                 ICA_TARGET,             lb->Model,
426                 ICA_MAP,                MapVSliderToLV,
427                 TAG_DONE))
428         {
429                 /* From now on, the groupgclass will dispose this object for us */
430                 DoMethod((Object *)g, OM_ADDMEMBER, lb->VSlider);
431
432                 /* Connect Model to ListView */
433                 SetAttrs(lb->Model,
434                         ICA_TARGET, lb->ListView,
435                         TAG_DONE);
436
437                 /* Connect VSlider to Model */
438                 if (lb->LVToVSliderIC = NewObject(NULL, ICCLASS,
439                         ICA_TARGET,     lb->VSlider,
440                         ICA_MAP,        MapLVToVSlider,
441                         TAG_DONE))
442                         if (!DoMethod(lb->Model, OM_ADDMEMBER, lb->LVToVSliderIC))
443                                 DisposeObject(lb->LVToVSliderIC);
444         }
445 }
446
447
448
449 static void CreateHSlider(Class *cl, struct Gadget *g, struct LBData *lb, struct DrawInfo *dri)
450 {
451         /* TODO */
452 }
453
454
455 static void DeleteVSlider(struct Gadget *g, struct LBData *lb)
456 {
457         if (lb->LVToVSliderIC)
458         {
459                 ASSERT_VALID_PTR(lb->LVToVSliderIC)
460                 ASSERT_VALID_PTR(lb->Model)
461
462                 DoMethod (lb->Model, OM_REMMEMBER, lb->LVToVSliderIC);
463                 DisposeObject (lb->LVToVSliderIC);
464                 lb->LVToVSliderIC = NULL;
465         }
466
467         if (lb->VSlider)
468         {
469                 ASSERT_VALID_PTR(lb->VSlider)
470
471                 DoMethod ((Object *)g, OM_REMMEMBER, lb->VSlider);
472                 DisposeObject (lb->VSlider);
473                 lb->VSlider = NULL;
474         }
475 }
476
477
478
479 static void DeleteHSlider(struct Gadget *g, struct LBData *lb)
480 {
481         /* TODO */
482 }
483
484
485
486 Class *MakeListBoxClass(void)
487 {
488         Class *class;
489         struct LBClassData *classdata;
490
491         if (class = MakeClass(NULL, GROUPGCLASS, NULL, sizeof (struct LBData), 0))
492         {
493                 class->cl_Dispatcher.h_Entry = (ULONG (*)()) LBDispatcher;
494
495                 /* Allocate storage for global class data */
496                 if (classdata = AllocMem(sizeof (struct LBClassData), MEMF_PUBLIC | MEMF_CLEAR))
497                 {
498                         class->cl_UserData = (ULONG) classdata;
499
500                         if (classdata->SliderBarBase = (struct ClassLibrary *)
501                                 OpenLibrary("gadgets/sliderbar.gadget", 0))
502                         {
503                                 classdata->SliderBarGClass = classdata->SliderBarBase->cl_Class;
504                                 ASSERT_VALID_PTR(classdata->SliderBarGClass);
505
506                                 return class;
507                         }
508                 }
509
510                 FreeListBoxClass(class);
511         }
512
513         return NULL;
514 }
515
516
517
518 BOOL FreeListBoxClass(Class *class)
519 {
520         struct LBClassData *classdata;
521
522         if (class)
523         {
524                 ASSERT_VALID_PTR(class)
525
526                 classdata = (struct LBClassData *)class->cl_UserData;
527
528                 /* Try to remove the class */
529                 if (FreeClass(class))
530                 {
531                         if (classdata)
532                         {
533                                 ASSERT_VALID_PTR(classdata)
534                                 ASSERT_VALID_PTR_OR_NULL(classdata->SliderBarBase)
535
536                                 /* Cleanup global class data */
537
538                                 /* NULL is safe in CloseLibrary() since V36 */
539                                 CloseLibrary((struct Library *)classdata->SliderBarBase);
540                                 FreeMem(classdata, sizeof (struct LBClassData));
541                         }
542                         return TRUE;
543                 }
544                 return FALSE;
545         }
546         return TRUE;
547 }