4 ** Copyright (C) 1997,98 Bernardo Innocenti
6 ** Use 4 chars wide TABs to read this file
8 ** GadTools-like `boopsi' ListView group class
11 #define USE_BUILTIN_MATH
12 #define INTUI_V36_NAMES_ONLY
14 #define CLIB_ALIB_PROTOS_H /* Avoid dupe defs of boopsi funcs */
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>
25 #include <proto/exec.h>
26 #include <proto/intuition.h>
27 #include <proto/utility.h>
33 #include "CompilerSpecific.h"
35 #include "BoopsiStubs.h"
37 #define LV_GADTOOLS_STUFF
38 #include "ScrollButtonClass.h"
39 #include "ListViewClass.h"
40 #include "VectorGlyphIClass.h"
41 #include "ListBoxClass.h"
45 #define VSLIDER_WIDTH 14
46 #define HSLIDER_HEIGHT 14
49 /* Per-object instance data */
52 /* struct Gadget *ThisGadget; (not used) */
62 Object *LVToVSliderIC;
63 Object *LVToHSliderIC;
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 */
68 struct Image *UpImage;
69 struct Image *DownImage;
70 struct Image *LeftImage;
71 struct Image *RightImage;
74 LONG FrameWidth, FrameHeight;
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.
82 struct Rectangle GRect;
86 /* Global class data */
89 Class *ScrollButtonClass;
90 struct Library *VectorGlyphBase;
94 extern Class *ListViewClass;
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);
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);
110 INLINE void GetGadgetBox (struct GadgetInfo *ginfo, struct ExtGadget *g, struct IBox *box, struct Rectangle *rect);
114 /* Attribute translations for object interconnections */
116 static LONG MapLVToHSlider[] =
118 LVA_PixelLeft, PGA_Top,
119 LVA_PixelWidth, PGA_Total,
120 LVA_PixelHVisible, PGA_Visible,
124 static LONG MapHSliderToLV[] =
126 PGA_Top, LVA_PixelLeft,
131 static LONG MapLVToVSlider[] =
134 LVA_Total, PGA_Total,
135 LVA_Visible, PGA_Visible,
140 static LONG MapLVToVSlider[] =
142 LVA_PixelTop, PGA_Top,
143 LVA_PixelHeight, PGA_Total,
144 LVA_PixelVVisible, PGA_Visible,
150 static LONG MapVSliderToLV[] =
157 static LONG MapVSliderToLV[] =
159 PGA_Top, LVA_PixelTop,
165 static LONG MapUpButtonToLV[] =
171 static LONG MapDownButtonToLV[] =
177 static LONG MapLeftButtonToLV[] =
183 static LONG MapRightButtonToLV[] =
185 GA_ID, LVA_MoveRight,
191 static ULONG HOOKCALL LBDispatcher (
193 REG(a2, struct Gadget *g),
200 switch (msg->MethodID)
203 LB_GMRender (cl, g, (struct gpRender *)msg);
207 /* This method is only supported on V39 and above */
208 LB_GMLayout (cl, g, (struct gpLayout *)msg);
213 return LB_OMSet (cl, g, (struct opUpdate *)msg);
216 return LB_OMGet (cl, g, (struct opGet *)msg);
219 return LB_OMNew (cl, g, (struct opSet *)msg);
222 LB_OMDispose (cl, g, msg);
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.
232 /* DB(kprintf("ListBoxClass: passing unknown method 0x%lx to superclass...\n", msg->MethodID);)
234 return DoSuperMethodA (cl, (Object *)g, msg);
240 static void LB_GMRender (Class *cl, struct Gadget *g, struct gpRender *msg)
242 struct LBData *lb = INST_DATA (cl, g);
248 /* Pre-V39 Intuition won't call our GM_LAYOUT method, so we must
249 * always call it before redrawing the gadget.
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 */
257 /* The groupgclass does not render its imagery,
258 * it only calls GM_RENDER for all its children
260 if (msg->gpr_Redraw == GREDRAW_REDRAW)
262 DB (kprintf ("ListBoxClass: GM_RENDER: msg->gpr_Redraw = GREDRAW_REDRAW\n");)
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 */
272 /* Forward message to superclass so it will call it on all our children */
273 DoSuperMethodA (cl, (Object *)g, (Msg)msg);
278 static void LB_GMLayout (Class *cl, struct Gadget *g, struct gpLayout *msg)
280 struct LBData *lb = (struct LBData *) INST_DATA (cl, (Object *)g);
282 DB (kprintf ("ListBoxClass: GM_LAYOUT\n");)
286 /* Collect new group size */
287 GetGadgetBox (msg->gpl_GInfo, (struct ExtGadget *)g, &lb->GBox, &lb->GRect);
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,
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,
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,
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);)
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,
325 /* NOTE: it seems that the groupgclass does not forward GM_LAYOUT
326 * to its children, so we must handle this here.
329 /* Forward GM_LAYOUT to embedded listview */
330 DoMethodA (lb->ListView, (Msg)msg);
335 static ULONG LB_OMSet (Class *cl, struct Gadget *g, struct opUpdate *msg)
337 struct LBData *lb = (struct LBData *) INST_DATA (cl, (Object *)g);
339 DB (kprintf ("ListBoxClass: OM_SET\n");)
343 /* Forward attributes to our listview */
344 DoMethodA (lb->ListView, (Msg)msg);
346 /* Also forward to our superclass */
347 return DoSuperMethodA (cl, (Object *)g, (Msg) msg);
352 static ULONG LB_OMGet (Class *cl, struct Gadget *g, struct opGet *msg)
354 struct LBData *lb = (struct LBData *) INST_DATA (cl, (Object *)g);
356 DB (kprintf ("ListBoxClass: OM_GET\n");)
359 /* Forward this method to our listview */
360 return DoMethodA (lb->ListView, (Msg)msg);
365 static ULONG LB_OMNew (Class *cl, struct Gadget *g, struct opSet *msg)
368 struct DrawInfo *dri;
371 DB (kprintf ("ListBoxClass: OM_NEW:\n");)
373 if (g = (struct Gadget *)DoSuperMethodA (cl, (Object *)g, (Msg)msg))
375 /* Set the GMORE_SCROLLRASTER flag */
376 if (g->Flags & GFLG_EXTENDED)
378 DB (kprintf ("ListBoxClass: OM_NEW: Setting GMORE_SCROLLRASTER\n");)
379 ((struct ExtGadget *)g)->MoreFlags |= GMORE_SCROLLRASTER;
382 lb = (struct LBData *) INST_DATA (cl, (Object *)g);
386 /* Clear the object instance */
387 memset (lb, 0, sizeof (struct LBData));
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.
393 /* lb->ThisGadget = g; (not used) */
397 dri = (struct DrawInfo *) GetTagData (GA_DrawInfo, NULL, msg->ops_AttrList);
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().
405 if (lb->Model = NewObjectA (NULL, MODELCLASS, NULL))
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.
411 if (lb->ListView = NewObjectA (ListViewClass, NULL, msg->ops_AttrList))
413 /* From now no, the groupgclass will dispose this object for us */
414 DoMethod ((Object *)g, OM_ADDMEMBER, lb->ListView);
416 //SetAttrs (lb->Model,
417 // ICA_TARGET, lb->ListView,
420 /* Connect Model to ListView */
424 if (icobject = NewObject (NULL, ICCLASS,
425 ICA_TARGET, lb->ListView,
427 if (!DoMethod (lb->Model, OM_ADDMEMBER, icobject))
428 DisposeObject (icobject);
431 /* Connect ListView to Model */
432 SetAttrs (lb->ListView,
433 ICA_TARGET, lb->Model,
437 CreateVSlider (cl, g, lb, dri);
438 CreateHSlider (cl, g, lb, dri);
441 /* Create a frame to put around us */
443 if (lb->Frame = NewObject (NULL, FRAMEICLASS,
445 IA_FrameType, FRAME_BUTTON,
448 struct IBox FrameBox, ContentsBox = { 0, 0, 0, 0 };
450 /* Ask the frame about its nominal frame width and height */
451 DoMethod ((Object *)lb->Frame, IM_FRAMEBOX, &ContentsBox, &FrameBox, dri, 0);
453 /* Remember it later */
454 lb->FrameWidth = FrameBox.Width;
455 lb->FrameHeight = FrameBox.Height;
459 /* Set the gadget width and height because the groupgclass
460 * always forces them to 0 on creation.
465 if (tag = FindTagItem (GA_RelWidth, msg->ops_AttrList))
467 GA_RelWidth, tag->ti_Data,
471 GA_Width, GetTagData (GA_Width, g->Width, msg->ops_AttrList),
474 if (tag = FindTagItem (GA_RelHeight, msg->ops_AttrList))
476 GA_RelHeight, tag->ti_Data,
480 GA_Height, GetTagData (GA_Height, g->Height, msg->ops_AttrList),
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);)
488 /* TODO: Handle creation-time attributes */
490 return (ULONG)g; /* Return newly created istance */
495 /* Dispose object without disturbing the dispatchers of our sub-classes, if any */
496 CoerceMethod (cl, (Object *)g, OM_DISPOSE);
504 static void LB_OMDispose (Class *cl, struct Gadget *g, Msg msg)
506 struct LBData *lb = (struct LBData *) INST_DATA (cl, (Object *)g);
509 DB (kprintf ("ListBoxClass: OM_DISPOSE\n");)
511 DeleteHSlider(g, lb);
512 DeleteVSlider(g, lb);
514 /* Dispose our subobjects which are not freed automatically */
515 DisposeObject (lb->Frame);
516 DisposeObject (lb->Model);
518 /* Our superclass will cleanup everything else now */
519 DoSuperMethodA (cl, (Object *)g, (Msg) msg);
521 /* From now on, our instance data is no longer available */
526 #define SCROLLBUTTON_CLASS_PTR ( ((struct LBClassData *)(cl->cl_UserData))->ScrollButtonClass )
529 static void CreateVSlider (Class *cl, struct Gadget *g, struct LBData *lb, struct DrawInfo *dri)
531 if (lb->VSlider = NewObject (NULL, PROPGCLASS,
532 GA_ID, g->GadgetID, /* Same as our ID */
534 PGA_Freedom, FREEVERT,
536 PGA_Borderless, TRUE,
537 ICA_TARGET, lb->Model,
538 ICA_MAP, MapVSliderToLV,
541 /* From now on, the groupgclass will dispose this object for us */
542 DoMethod ((Object *)g, OM_ADDMEMBER, lb->VSlider);
544 /* Connect Model to ListView */
546 ICA_TARGET, lb->ListView,
549 /* Connect VSlider to Model */
550 if (lb->LVToVSliderIC = NewObject (NULL, ICCLASS,
551 ICA_TARGET, lb->VSlider,
552 ICA_MAP, MapLVToVSlider,
554 if (!DoMethod (lb->Model, OM_ADDMEMBER, lb->LVToVSliderIC))
555 DisposeObject (lb->LVToVSliderIC);
558 /* We won't bother checking for failure because the image is
559 * not life-critical in our object
561 lb->UpImage = NewObject (NULL, SYSICLASS,
562 SYSIA_Which, UPIMAGE,
566 if (lb->UpButton = NewObject (SCROLLBUTTON_CLASS_PTR, NULL,
569 GA_Image, lb->UpImage,
570 ICA_TARGET, lb->ListView,
571 ICA_MAP, MapUpButtonToLV,
574 /* From now on, the groupgclass will dispose this object for us */
575 DoMethod ((Object *)g, OM_ADDMEMBER, lb->UpButton);
578 /* We won't bother checking for failure because the image is
579 * not life-critical in our object
581 lb->DownImage = NewObject (NULL, SYSICLASS,
582 SYSIA_Which, DOWNIMAGE,
586 if (lb->DownButton = NewObject (SCROLLBUTTON_CLASS_PTR, NULL,
589 GA_Image, lb->DownImage,
590 ICA_TARGET, lb->ListView,
591 ICA_MAP, MapDownButtonToLV,
594 /* From now on, the groupgclass will dispose this object for us */
595 DoMethod ((Object *)g, OM_ADDMEMBER, lb->DownButton);
601 static void CreateHSlider (Class *cl, struct Gadget *g, struct LBData *lb, struct DrawInfo *dri)
607 static void DeleteVSlider (struct Gadget *g, struct LBData *lb)
611 ASSERT_VALID(lb->DownButton)
613 DoMethod ((Object *)g, OM_REMMEMBER, lb->DownButton);
614 DisposeObject (lb->DownButton);
615 lb->DownButton = NULL;
620 ASSERT_VALID(lb->DownImage)
622 DisposeObject (lb->DownImage);
623 lb->DownImage = NULL;
628 ASSERT_VALID(lb->UpButton)
630 DoMethod ((Object *)g, OM_REMMEMBER, lb->UpButton);
631 DisposeObject (lb->UpButton);
637 ASSERT_VALID(lb->UpImage)
639 DisposeObject (lb->UpImage);
643 if (lb->LVToVSliderIC)
645 ASSERT_VALID(lb->LVToVSliderIC)
646 ASSERT_VALID(lb->Model)
648 DoMethod (lb->Model, OM_REMMEMBER, lb->LVToVSliderIC);
649 DisposeObject (lb->LVToVSliderIC);
650 lb->LVToVSliderIC = NULL;
655 ASSERT_VALID(lb->VSlider)
657 DoMethod ((Object *)g, OM_REMMEMBER, lb->VSlider);
658 DisposeObject (lb->VSlider);
665 static void DeleteHSlider (struct Gadget *g, struct LBData *lb)
672 INLINE void GetGadgetBox (struct GadgetInfo *ginfo, struct ExtGadget *g, struct IBox *box, struct Rectangle *rect)
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).
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.
683 ASSERT_VALIDNO0(ginfo)
685 ASSERT_VALIDNO0(rect)
687 DB (if (g->Flags & GFLG_EXTENDED)
688 kprintf ("ListBoxClass: GetGadgetBox(): GFLG_EXTENDED is set\n");)
690 DB (if ((g->Flags & GFLG_EXTENDED) && (g->MoreFlags & GMORE_BOUNDS))
691 kprintf ("ListBoxClass: GetGadgetBox(): Gadget has valid bounds\n");)
693 box->Left = g->LeftEdge;
694 if (g->Flags & GFLG_RELRIGHT)
695 box->Left += ginfo->gi_Domain.Width - 1;
697 box->Top = g->TopEdge;
698 if (g->Flags & GFLG_RELBOTTOM)
699 box->Top += ginfo->gi_Domain.Height - 1;
701 box->Width = g->Width;
702 if (g->Flags & GFLG_RELWIDTH)
703 box->Width += ginfo->gi_Domain.Width;
705 box->Height = g->Height;
706 if (g->Flags & GFLG_RELHEIGHT)
707 box->Height += ginfo->gi_Domain.Height;
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;
716 DB (kprintf ("ListBoxClass: GetGadgetBox(): Left = %ld, Top = %ld, Width = %ld, Height = %ld\n",
717 box->Left, box->Top, box->Width, box->Height);)
722 Class *MakeListBoxClass (void)
725 struct LBClassData *classdata;
727 if (class = MakeClass (NULL, GROUPGCLASS, NULL, sizeof (struct LBData), 0))
729 class->cl_Dispatcher.h_Entry = (ULONG (*)()) LBDispatcher;
731 /* Allocate storage for global class data */
732 if (classdata = AllocMem (sizeof (struct LBClassData), MEMF_PUBLIC | MEMF_CLEAR))
734 class->cl_UserData = (ULONG) classdata;
736 classdata->ScrollButtonClass = MakeScrollButtonClass();
737 classdata->VectorGlyphBase = OpenLibrary ("images/vectorglyph.image", 0);
741 FreeListBoxClass (class);
749 void FreeListBoxClass (Class *class)
751 struct LBClassData *classdata;
756 if (classdata = (struct LBClassData *)class->cl_UserData)
758 ASSERT_VALID(classdata)
759 ASSERT_VALID(classdata->ScrollButtonClass)
760 ASSERT_VALID(classdata->VectorGlyphBase)
762 /* Cleanup global class data */
763 CloseLibrary (classdata->VectorGlyphBase); /* NULL is safe since V36 */
764 FreeScrollButtonClass (classdata->ScrollButtonClass);
765 FreeMem (classdata, sizeof (struct LBClassData));