2 ** $Id: SmartGroupGClass.c,v 1.1 1999/02/07 14:24:44 bernie Exp $
4 ** Copyright (C) 1999 Bernardo Innocenti
5 ** All rights reserved.
7 ** Use 4 chars wide TABs to read this file
9 ** "smartgroupgclass", a subclass of the gadgetclass which replaces
10 ** and enhances the builtin groupgclass.
11 ** See the header file "SmartGroupGClass.h" for class documentation.
14 /* Definitions for system headers */
15 #define USE_BUILTIN_MATH
16 #define INTUI_V36_NAMES_ONLY
17 #define INTUITION_IOBSOLETE_H
19 #define CLIB_ALIB_PROTOS_H /* Avoid dupe defines of boopsi funcs */
21 #include <exec/types.h>
22 #include <exec/memory.h>
23 #include <utility/tagitem.h>
24 #include <intuition/intuition.h>
25 #include <intuition/classes.h>
26 #include <intuition/classusr.h>
27 #include <intuition/gadgetclass.h>
29 #include <proto/exec.h>
30 #include <proto/intuition.h>
31 #include <proto/utility.h>
37 #include <gadgets/SmartGroupGClass.h>
39 #include <CompilerSpecific.h>
40 #include <DebugMacros.h>
41 #include <ListMacros.h>
42 #include <BoopsiStubs.h>
43 #include <BoopsiLib.h>
46 /* Local function prototypes */
48 static ULONG HOOKCALL SmartGroupDispatcher (REG(a0, Class *cl), REG(a2, Object *o), REG(a1, Msg msg));
50 static ULONG ASMCALL SG_GMHitTest (REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct gpHitTest *msg));
51 static void ASMCALL SG_GMRender (REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct gpRender *msg));
52 static ULONG ASMCALL SG_GMGoActive (REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct gpInput *msg));
53 static ULONG ASMCALL SG_GMHandleInput(REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct gpInput *msg));
54 static void ASMCALL SG_GMGoInactive (REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct gpGoInactive *msg));
55 static ULONG ASMCALL SG_GMHelpTest (REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct gpHitTest *msg));
56 static void ASMCALL SG_GMLayout (REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct gpLayout *msg));
57 static ULONG ASMCALL SG_GMDomain (REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct gpDomain *msg));
58 static ULONG ASMCALL SG_OMNew (REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct opSet *msg));
59 static void ASMCALL SG_OMDispose (REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, Msg msg));
60 //static ULONG ASMCALL SG_OMSet (REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct opUpdate *msg));
61 static void ASMCALL SG_OMAddMember (REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct opMember *msg));
62 static void ASMCALL SG_OMRemMember (REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct opMember *msg));
64 /* Per-object instance data */
68 struct MinList ChildList;
70 struct ExtGadget * ActiveChild;
73 /* Group orientation: can be LORIENT_VERT or LORIENT_HORIZ */
79 #if (CLASS_FLAVOUR & FLAVOUR_CLASSLIB)
81 /* Class library support functions */
82 struct ClassLibrary * HOOKCALL _UserLibInit (REG(a6, struct ClassLibrary *mybase));
83 struct ClassLibrary * HOOKCALL _UserLibCleanup (REG(a6, struct ClassLibrary *mybase));
84 Class * HOOKCALL _GetEngine (REG(a6, struct ClassLibrary *mybase));
87 const UBYTE LibName[] = "sliderbar.gadget";
88 const UBYTE LibVer[] = { '$', 'V', 'E', 'R', ':', ' ' };
89 const UBYTE LibId[] = "smartgroup.gadget 1.0 (6.1.99) © 1999 Bernardo Innocenti\n";
91 /* Workaround a bug in StormC header file <proto/utility.h> */
93 #define UTILITYBASETYPE struct Library
95 #define UTILITYBASETYPE struct UtilityBase
99 static struct ExecBase *SysBase = NULL;
100 static struct IntuitionBase *IntuitionBase = NULL;
101 static UTILITYBASETYPE *UtilityBase = NULL;
107 static ULONG ASMCALL SG_GMHitTest (REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct gpHitTest *msg))
109 struct GroupData *gd = INST_DATA (cl, g);
110 struct ExtGadget *child;
114 DB2 (DBPRINTF ("SmartGroupGClass: GM_HITTEST\n");)
117 object_state = (APTR)gd->ChildList.mlh_Head;
118 ASSERT_VALID_PTR(object_state)
120 x = msg->gpht_Mouse.X + gd->GBox.Left;
121 y = msg->gpht_Mouse.Y + gd->GBox.Top;
123 /* Search over all our children */
124 while (child = (struct ExtGadget *)NextObject(&object_state))
126 ASSERT_VALID_PTR(child)
128 /* Check whether the mouse coordinates are inside
129 * the gadget rectangle of our child
131 if ((x >= child->LeftEdge) &&
132 (x < child->LeftEdge + child->Width) &&
133 (y >= child->TopEdge) &&
134 (y < child->TopEdge + child->Height))
136 struct gpHitTest childmsg;
138 childmsg.MethodID = GM_HITTEST;
139 childmsg.gpht_GInfo = msg->gpht_GInfo;
140 childmsg.gpht_Mouse.X = x - child->LeftEdge;
141 childmsg.gpht_Mouse.Y = y - child->TopEdge;
143 if (DoMethodA ((Object *)child, (Msg)&childmsg) == GMR_GADGETHIT)
145 gd->ActiveChild = child;
146 return GMR_GADGETHIT;
156 static void ASMCALL SG_GMRender(REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct gpRender *msg))
158 struct GroupData *gd = INST_DATA (cl, g);
162 DB2 (DBPRINTF ("SmartGroupGClass: GM_RENDER\n");)
165 object_state = (APTR)gd->ChildList.mlh_Head;
166 ASSERT_VALID_PTR(object_state)
168 /* The groupgclass disposes all its children automatically */
169 while (o = NextObject(&object_state))
172 DoMethodA (o, (Msg)msg);
178 static ULONG ASMCALL SG_GMGoActive (REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct gpInput *msg))
180 struct GroupData *gd = INST_DATA (cl, g);
182 DB2 (DBPRINTF ("SmartGroupGClass: GM_GOACTIVE\n");)
184 ASSERT_VALID_PTR(gd->ActiveChild)
188 struct gpInput childmsg;
190 childmsg.MethodID = GM_GOACTIVE;
191 childmsg.gpi_GInfo = msg->gpi_GInfo;
192 childmsg.gpi_IEvent = msg->gpi_IEvent;
193 childmsg.gpi_Termination= msg->gpi_Termination;
194 childmsg.gpi_Mouse.X = msg->gpi_Mouse.X + gd->GBox.Left
195 - gd->ActiveChild->LeftEdge;
196 childmsg.gpi_Mouse.Y = msg->gpi_Mouse.Y + gd->GBox.Top
197 - gd->ActiveChild->TopEdge;
198 childmsg.gpi_TabletData = msg->gpi_TabletData;
200 return (DoMethodA ((Object *)gd->ActiveChild, (Msg)&childmsg));
203 gd->ActiveChild = NULL;
209 static ULONG ASMCALL SG_GMHandleInput (REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct gpInput *msg))
211 struct GroupData *gd = INST_DATA (cl, g);
213 DB2 (DBPRINTF ("SmartGroupGClass: GM_HANDLEINPUT\n");)
215 ASSERT_VALID_PTR(gd->ActiveChild)
219 struct gpInput childmsg;
221 childmsg.MethodID = GM_HANDLEINPUT;
222 childmsg.gpi_GInfo = msg->gpi_GInfo;
223 childmsg.gpi_IEvent = msg->gpi_IEvent;
224 childmsg.gpi_Termination= msg->gpi_Termination;
225 childmsg.gpi_Mouse.X = msg->gpi_Mouse.X + gd->GBox.Left
226 - gd->ActiveChild->LeftEdge;
227 childmsg.gpi_Mouse.Y = msg->gpi_Mouse.Y + gd->GBox.Top
228 - gd->ActiveChild->TopEdge;
229 childmsg.gpi_TabletData = msg->gpi_TabletData;
231 DB2 (DBPRINTF ("SmartGroupGClass: Translating coords: X=%ld, Y=%ld\n",
232 childmsg.gpi_Mouse.X, childmsg.gpi_Mouse.Y);)
234 return DoMethodA ((Object *)gd->ActiveChild, (Msg)&childmsg);
242 static void ASMCALL SG_GMGoInactive (REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct gpGoInactive *msg))
244 struct GroupData *gd = INST_DATA (cl, g);
246 DB2 (DBPRINTF ("SmartGroupGClass: GM_GOINACTIVE\n");)
248 ASSERT_VALID_PTR(gd->ActiveChild)
252 DoMethodA ((Object *)gd->ActiveChild, (Msg)msg);
253 gd->ActiveChild = NULL;
259 static ULONG ASMCALL SG_GMHelpTest (REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct gpHitTest *msg))
261 struct GroupData *gd = INST_DATA (cl, g);
262 struct ExtGadget *child;
266 DB1 (DBPRINTF ("SmartGroupGClass: GM_HELPTEST\n");)
269 object_state = (APTR)gd->ChildList.mlh_Head;
270 ASSERT_VALID_PTR(object_state)
273 if ((g->Flags & GFLG_EXTENDED) && (g->MoreFlags & GMORE_GADGETHELP))
275 x = msg->gpht_Mouse.X + gd->GBox.Left;
276 y = msg->gpht_Mouse.Y + gd->GBox.Top;
278 /* Search over all our children */
279 while (child = (struct ExtGadget *)NextObject(&object_state))
281 ASSERT_VALID_PTR(child)
283 /* Limit to gadgets that understand GM_HELPTEST */
284 if ((child->Flags & GFLG_EXTENDED) && (child->MoreFlags & GMORE_GADGETHELP))
286 if (child->MoreFlags & GMORE_BOUNDS)
288 /* Check whether the mouse coordinates are inside
289 * the gadget rectangle of our child
291 if ((x >= child->BoundsLeftEdge) &&
292 (x < child->BoundsLeftEdge + child->BoundsWidth) &&
293 (y >= child->BoundsTopEdge) &&
294 (y < child->BoundsTopEdge + child->BoundsHeight))
296 struct gpHitTest childmsg;
298 childmsg.MethodID = GM_HELPTEST;
299 childmsg.gpht_GInfo = msg->gpht_GInfo;
300 childmsg.gpht_Mouse.X = x - child->BoundsLeftEdge;
301 childmsg.gpht_Mouse.Y = y - child->BoundsTopEdge;
303 if (DoMethodA ((Object *)child, (Msg)&childmsg) == GMR_HELPHIT)
305 gd->ActiveChild = child;
310 else /* No valid bounds, use gadget select box */
312 /* Check whether the mouse coordinates are inside
313 * the gadget rectangle of our child
315 if ((x >= child->LeftEdge) &&
316 (x < child->LeftEdge + child->Width) &&
317 (y >= child->TopEdge) &&
318 (y < child->TopEdge + child->Height))
320 struct gpHitTest childmsg;
322 childmsg.MethodID = GM_HELPTEST;
323 childmsg.gpht_GInfo = msg->gpht_GInfo;
324 childmsg.gpht_Mouse.X = x - child->LeftEdge;
325 childmsg.gpht_Mouse.Y = y - child->TopEdge;
327 if (DoMethodA ((Object *)child, (Msg)&childmsg) == GMR_HELPHIT)
329 gd->ActiveChild = child;
338 return GMR_NOHELPHIT;
343 static void ASMCALL SG_GMLayout(REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct gpLayout *msg))
345 struct GroupData *gd = (struct GroupData *) INST_DATA (cl, (Object *)g);
346 struct ExtGadget *child;
349 DB2 (DBPRINTF ("SmartGroupGClass: GM_LAYOUT\n");)
352 /* Collect new group size */
353 GetGadgetBounds(msg->gpl_GInfo, g, &gd->GBox);
355 /* Forward GM_LAYOUT to our children that need it */
357 object_state = (APTR)gd->ChildList.mlh_Head;
358 ASSERT_VALID_PTR(object_state)
360 while (child = (struct ExtGadget *)NextObject(&object_state))
362 ASSERT_VALID_PTR(child)
371 DB2 (DBPRINTF ("SmartGroupGClass: Forwarding GM_LAYOUT to child\n");)
372 DoMethodA ((Object *)child, (Msg)msg);
379 static ULONG ASMCALL SG_GMDomain (REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct gpDomain *msg))
381 DB1 (DBPRINTF ("SmartGroupGClass: GM_DOMAIN\n");)
389 static ULONG ASMCALL SG_OMNew (REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct opSet *msg))
391 struct GroupData *gd;
392 struct TagItem *ti, *tstate = msg->ops_AttrList;
394 DB1 (DBPRINTF ("SmartGroupGClass: OM_NEW\n");)
396 if (g = (struct ExtGadget *)DoSuperMethodA (cl, (Object *)g, (Msg)msg))
398 gd = (struct GroupData *) INST_DATA (cl, (Object *)g);
401 DB (if (g->Flags & GFLG_EXTENDED)
402 DBPRINTF ("SmartGroupGClass: Wow, we are an ExtGadget!\n");)
404 /* Initialize object instance data */
405 NEWLIST((struct List *)&gd->ChildList);
406 gd->ActiveChild = NULL;
407 gd->ActiveChild = NULL;
409 gd->Orientation = LORIENT_NONE;
411 while (ti = NextTagItem (&tstate))
416 struct opMember opam;
420 /* Commit suicide without disturbing the
421 * dispatchers of our subclasses
423 SG_OMDispose (cl, g, NULL);
427 ASSERT_VALID_PTR(ti->ti_Data)
428 opam.opam_Object = (Object *)ti->ti_Data;
429 SG_OMAddMember (cl, g, &opam);
433 case LAYOUTA_Orientation:
434 gd->Orientation = ti->ti_Data;
441 /* Intuition sends GM_LAYOUT messages only to gadgets with
442 * relative postion. Since we need to recalculate the
443 * positions of our children when the window resizes, we
444 * set the GFLG_RELSPECIAL property if to be notified in all cases.
446 g->Flags |= GFLG_RELSPECIAL;
454 static void ASMCALL SG_OMDispose (REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, Msg msg))
456 struct GroupData *gd = (struct GroupData *) INST_DATA (cl, (Object *)g);
460 DB1 (DBPRINTF ("SmartGroupGClass: OM_DISPOSE\n");)
463 object_state = (APTR)gd->ChildList.mlh_Head;
464 ASSERT_VALID_PTR(object_state)
466 /* The groupgclass disposes all its children automatically */
467 while (o = NextObject(&object_state))
470 DoMethod (o, OM_REMOVE);
474 /* Our superclass will cleanup everything else now */
475 DoSuperMethodA (cl, (Object *)g, msg);
477 /* From now on, our instance data is no longer available */
482 static ULONG ASMCALL SG_OMSet (REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct opUpdate *msg))
484 struct GroupData *gd = INST_DATA (cl, g);
485 struct TagItem *ti, *tstate = msg->opu_AttrList;
487 DB1 (DBPRINTF ("SmartGroupGClass: OM_SET\n");)
490 ASSERT_VALID_PTR_OR_NULL(tstate)
492 DB2 (DBPRINTF ((msg->MethodID == OM_SET) ?
493 "SliderBarGClass: OM_SET\n" :
494 "SliderBarGClass: OM_UPDATE\n");)
497 while (ti = NextTagItem (&tstate))
513 static void ASMCALL SG_OMAddMember (REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct opMember *msg))
515 struct GroupData *gd = INST_DATA (cl, g);
517 DB1 (DBPRINTF ("SmartGroupGClass: OM_ADDMEMBER\n");)
521 ASSERT_VALID_PTR(msg->opam_Object)
523 /* TODO: check for GMORE_SCROLLRASTER ? */
525 DoMethod (msg->opam_Object, OM_ADDTAIL, &gd->ChildList);
531 static void ASMCALL SG_OMRemMember (REG(a0, Class *cl), REG(a2, struct ExtGadget *g), REG(a1, struct opMember *msg))
533 struct GroupData *gd = INST_DATA (cl, g);
535 DB1 (DBPRINTF ("SmartGroupGClass: OM_REMMEBRER\n");)
538 DoMethod (msg->opam_Object, OM_REMOVE);
540 if (msg->opam_Object == (Object *)gd->ActiveChild)
541 gd->ActiveChild = NULL;
543 /* TODO: resize & redraw group */
548 static ULONG HOOKCALL SmartGroupDispatcher (
553 /* SmartGroup class dispatcher - Handles all boopsi messages. */
555 static const HookPtr GMHooks[] =
557 /* GM_HITTEST */ (HookPtr)SG_GMHitTest,
558 /* GM_RENDER */ (HookPtr)SG_GMRender,
559 /* GM_GOACTIVE */ (HookPtr)SG_GMGoActive,
560 /* GM_HANDLEINPUT */ (HookPtr)SG_GMHandleInput,
561 /* GM_GOINACTIVE */ (HookPtr)SG_GMGoInactive,
562 /* GM_HELPTEST */ (HookPtr)SG_GMHelpTest,
563 /* GM_LAYOUT */ (HookPtr)SG_GMLayout,
564 /* GM_DOMAIN */ (HookPtr)SG_GMDomain
567 static const HookPtr OMHooks[] =
569 /* OM_NEW */ (HookPtr)SG_OMNew,
570 /* OM_DISPOSE */ (HookPtr)SG_OMDispose,
571 /* OM_SET */ (HookPtr)NULL,
572 /* OM_GET */ (HookPtr)NULL,
573 /* OM_ADDTAIL */ (HookPtr)NULL,
574 /* OM_REMOVE */ (HookPtr)NULL,
575 /* OM_NOTIFY */ (HookPtr)NULL,
576 /* OM_UPDATE */ (HookPtr)NULL,
577 /* OM_ADDMEMBER */ (HookPtr)SG_OMAddMember,
578 /* OM_REMMEMBER */ (HookPtr)SG_OMRemMember
585 ASSERT_VALID_PTR(msg)
587 /* Optimized boopsi class dispatcher.
589 * This class overrides almost all gadgetclass and rootclass messages.
590 * The usual switch() statement on msg->MessageID would be quite inefficient.
591 * Most compilers aren't smart enough to implement it with two jump tables
592 * as we did here. This code relies on the values assigned to the method IDs
593 * in the headers to remain constant. It will break otherwise.
599 return GMHooks[id](cl, o, msg);
603 if (id <= (OM_REMMEMBER - OM_NEW))
605 return OMHooks[id](cl, o, msg);
607 return DoSuperMethodA (cl, o, msg);
612 Class *MakeSmartGroupGClass (void)
616 if (class = MakeClass (
617 #if (CLASS_FLAVOUR & FLAVOUR_PUBCLASS)
622 GADGETCLASS, NULL, sizeof (struct GroupData), 0))
624 class->cl_Dispatcher.h_Entry = (ULONG (*)()) SmartGroupDispatcher;
631 BOOL FreeSmartGroupGClass (Class *class)
635 ASSERT_VALID_PTR(class)
637 /* Try to remove the class */
638 if (FreeClass (class))
648 #if (CLASS_FLAVOUR & FLAVOUR_CLASSLIB)
650 * Class library support functions
653 struct ClassLibrary * HOOKCALL _UserLibInit (REG(a6, struct ClassLibrary *mybase))
655 SysBase = *((struct ExecBase **)4); /* Initialize SysBase */
657 IntuitionBase = (struct IntuitionBase *) OpenLibrary ("intuition.library", 39);
658 UtilityBase = (UTILITYBASETYPE *) OpenLibrary ("utility.library", 39);
660 if (!(IntuitionBase && UtilityBase))
662 _UserLibCleanup (mybase);
666 if (mybase->cl_Class = MakeSmartGroupGClass ())
672 _UserLibCleanup (mybase);
677 struct ClassLibrary * HOOKCALL _UserLibCleanup (REG(a6, struct ClassLibrary *mybase))
679 if (mybase->cl_Class)
680 if (!FreeSliderBarGClass (mybase->cl_Class))
683 CloseLibrary ((struct Library *)UtilityBase);
684 CloseLibrary ((struct Library *)IntuitionBase);
689 Class * HOOKCALL _GetEngine (REG(a6, struct ClassLibrary *mybase))
691 return (mybase->cl_Class);
694 #endif /* (CLASS_FLAVOUR & FLAVOUR_CLASSLIB) */