Initial commit
[amiga/OpenBoopsi.git] / gadgets / ScrollButton / ScrollButtonClass.c
1 /*
2 **      $Id: ScrollButtonClass.c,v 1.1 1999/02/07 14:24:43 bernie Exp $
3 **
4 **      Copyright (C) 1998,99 Bernardo Innocenti
5 **
6 **      Use 4 chars wide TABs to read this file
7 **
8 **      The ScrollButtonClass is a simple subclass of the buttongclass
9 **      specialized for scroll buttons. See ScrollButtonClass.h for a detailed
10 **      description.
11 */
12
13 #define USE_BUILTIN_MATH
14 #define INTUI_V36_NAMES_ONLY
15 #define __USE_SYSBASE
16 #define  CLIB_ALIB_PROTOS_H             /* Avoid dupe defs of boopsi funcs */
17
18
19 #include <intuition/gadgetclass.h>
20 #include <intuition/imageclass.h>
21 #include <proto/exec.h>
22 #include <proto/intuition.h>
23 #include <proto/utility.h>
24
25 #include <gadgets/ScrollButtonClass.h>
26
27 #include <CompilerSpecific.h>
28 #include <DebugMacros.h>
29 #include <DiagnosticMacros.h>
30 #include <BoopsiStubs.h>
31 #include <BoopsiLib.h>
32
33 #ifdef __STORM__
34         #pragma header
35 #endif
36
37
38
39 /* Per object instance data */
40 struct ScrollButtonData
41 {
42         /* The number of ticks we still have to wait
43          * before sending any notification.
44          */
45         ULONG TickCounter;
46
47         struct IBox GBox;               /* Our gadget bounds            */
48         struct IBox FrameBox;   /* Frame size and position      */
49
50         BOOL LayoutDone;                /* Need to recalculate above info? */
51 };
52
53
54
55 /* Local function prototypes */
56
57 static ULONG    SB_GMGoActive   (Class *cl, struct Gadget *g, struct gpInput *gpi);
58 static ULONG    SB_GMHandleInput(Class *cl, struct Gadget *g, struct gpInput *gpi);
59 static void             SB_GMRender             (Class *cl, struct Gadget *g, struct gpRender *gpr);
60 static void             SB_GMLayout             (Class *cl, struct Gadget *g, struct gpLayout *gpl);
61 static ULONG    SB_OMSet                (Class *cl, struct Gadget *g, struct opUpdate *msg);
62
63 static ULONG HOOKCALL ScrollButtonDispatcher(
64         REG(a0, Class *cl),
65         REG(a2, struct Gadget *g),
66         REG(a1, Msg msg));
67
68
69
70 #if (CLASS_FLAVOUR & FLAVOUR_CLASSLIB)
71
72         /* Class library support functions */
73         struct ClassLibrary     *       HOOKCALL _UserLibInit           (REG(a6, struct ClassLibrary *mybase));
74         struct ClassLibrary     *       HOOKCALL _UserLibCleanup        (REG(a6, struct ClassLibrary *mybase));
75         Class *                                 HOOKCALL _GetEngine                     (REG(a6, struct ClassLibrary *mybase));
76
77         /* Library data */
78         const UBYTE LibName[] = "scrollbutton.gadget";
79         const UBYTE LibVer[] = { '$', 'V', 'E', 'R', ':', ' ' };
80         const UBYTE LibId[] = "scrollbutton.gadget 1.0 (19.3.2000) © 1997-2000 Bernardo Innocenti\n";
81
82         /* Workaround a bug in StormC header file <proto/utility.h> */
83         #ifdef __STORM__
84                 #define UTILITYBASETYPE struct Library
85         #else
86                 #define UTILITYBASETYPE struct UtilityBase
87         #endif
88
89         /* Library bases */
90         struct ExecBase                 *SysBase                = NULL;
91         struct IntuitionBase    *IntuitionBase  = NULL;
92         UTILITYBASETYPE                 *UtilityBase    = NULL;
93 #endif
94
95
96
97 static ULONG SB_GMGoActive(Class *cl, struct Gadget *g, struct gpInput *gpi)
98 {
99         struct ScrollButtonData *bd = (struct ScrollButtonData *) INST_DATA(cl, g);
100
101         DB2(DBPRINTF("ScrollButton: GM_GOACTIVE\n");)
102         ASSERT_VALID_PTR(bd)
103
104         /* May define an attribute to make delay configurable */
105         bd->TickCounter = 3;
106
107         /* Notify our target that we have initially hit. */
108         NotifyAttrs((Object *)g, gpi->gpi_GInfo, 0,
109                 GA_ID,  g->GadgetID,
110                 TAG_DONE);
111
112         /* Send more input */
113         return GMR_MEACTIVE;
114 }
115
116
117
118 static ULONG SB_GMHandleInput(Class *cl, struct Gadget *g, struct gpInput *gpi)
119 {
120         struct ScrollButtonData *bd = (struct ScrollButtonData *) INST_DATA(cl, g);
121         struct RastPort *rp;
122         ULONG retval = GMR_MEACTIVE;
123         UWORD selected = 0;
124
125         DB2 (DBPRINTF ("ScrollButton: GM_HANDLEINPUT\n");)
126         ASSERT_VALID_PTR(bd)
127
128         /* This also works with classic (non-boopsi) images. */
129         if (PointInImage((gpi->gpi_Mouse.X << 16) + (gpi->gpi_Mouse.Y), g->GadgetRender))
130         {
131                 /* We are hit */
132                 selected = GFLG_SELECTED;
133         }
134
135         if (gpi->gpi_IEvent->ie_Class == IECLASS_RAWMOUSE && gpi->gpi_IEvent->ie_Code == SELECTUP)
136         {
137                 /* Gadgetup, time to go */
138                 retval = GMR_NOREUSE;
139                 /* Unselect the gadget on our way out... */
140                 selected = 0;
141         }
142         else if (gpi->gpi_IEvent->ie_Class == IECLASS_TIMER)
143         {
144                 /* We got a tick. Decrement counter, and if 0, send notify. */
145
146                 if (bd->TickCounter)
147                         bd->TickCounter--;
148                 else if (selected)
149                         /* Notify our target that we are still being hit */
150                         NotifyAttrs ((Object *)g, gpi->gpi_GInfo, 0,
151                                 GA_ID,  g->GadgetID,
152                                 TAG_DONE);
153         }
154
155         if ((g->Flags & GFLG_SELECTED) != selected)
156         {
157                 /* Update changes in gadget render */
158                 g->Flags ^= GFLG_SELECTED;
159                 if (rp = ObtainGIRPort (gpi->gpi_GInfo))
160                 {
161                         DoMethod((Object *) g, GM_RENDER, gpi->gpi_GInfo, rp, GREDRAW_UPDATE);
162                         ReleaseGIRPort(rp);
163                 }
164         }
165         return retval;
166 }
167
168
169
170 static void SB_GMRender(Class *cl, struct Gadget *g, struct gpRender *gpr)
171 {
172         struct ScrollButtonData *bd = (struct ScrollButtonData *) INST_DATA(cl, g);
173         struct impDraw impdraw;
174
175         DB2(DBPRINTF("ScrollButton: GM_RENDER\n");)
176         ASSERT_VALID_PTR(bd)
177
178         if (!bd->LayoutDone)
179                 SB_GMLayout(cl, g, (struct gpLayout *)gpr);
180
181         impdraw.MethodID        = IM_DRAWFRAME;
182         impdraw.imp_RPort       = gpr->gpr_RPort;
183         impdraw.imp_DrInfo      = gpr->gpr_GInfo->gi_DrInfo;
184
185         /* Check if we are living inside an inactive window border */
186         if ((g->Activation & (GACT_RIGHTBORDER | GACT_LEFTBORDER | GACT_TOPBORDER | GACT_BOTTOMBORDER)) &&
187                 gpr->gpr_GInfo->gi_Window &&
188                 (!(gpr->gpr_GInfo->gi_Window->Flags & WFLG_WINDOWACTIVE)))
189         {
190                 impdraw.imp_State = (g->Flags & GFLG_SELECTED) ?
191                         IDS_INACTIVESELECTED :
192                         ((g->Flags & GFLG_DISABLED) ? IDS_INACTIVEDISABLED : IDS_INACTIVENORMAL );
193         }
194         else
195         {
196                 impdraw.imp_State = (g->Flags & GFLG_SELECTED) ?
197                         ((g->Flags & GFLG_DISABLED) ? IDS_SELECTEDDISABLED : IDS_SELECTED ) :
198                         ((g->Flags & GFLG_DISABLED) ? IDS_DISABLED : IDS_NORMAL );
199         }
200
201         /* Check whether our label image is valid and can be sent a boopsi message */
202         if ((g->Flags & GFLG_LABELIMAGE) && g->GadgetText &&
203                 (((struct Image *)g->GadgetText)->Depth == CUSTOMIMAGEDEPTH))
204         {
205                 impdraw.imp_Offset.X    = bd->GBox.Left - bd->FrameBox.Left;
206                 impdraw.imp_Offset.Y    = bd->GBox.Top - bd->FrameBox.Top;
207                 impdraw.imp_Dimensions.Width    = bd->GBox.Width - bd->FrameBox.Width;
208                 impdraw.imp_Dimensions.Height   = bd->GBox.Height - bd->FrameBox.Height;
209
210                 DoMethodA((Object *)g->GadgetText, (Msg)&impdraw);
211         }
212
213         /* Check whether our framing image is valid and can be sent a boopsi message */
214         if ((g->Flags & GFLG_GADGIMAGE) && g->GadgetRender &&
215                 (((struct Image *)g->GadgetRender)->Depth == CUSTOMIMAGEDEPTH))
216         {
217                 impdraw.imp_Offset.X    = bd->GBox.Left;
218                 impdraw.imp_Offset.Y    = bd->GBox.Top;
219                 impdraw.imp_Dimensions.Width    = bd->GBox.Width;
220                 impdraw.imp_Dimensions.Height   = bd->GBox.Height;
221
222                 DoMethodA((Object *)g->GadgetRender, (Msg)&impdraw);
223         }
224 }
225
226
227
228 static void SB_GMLayout(Class *cl, struct Gadget *g, struct gpLayout *gpl)
229 {
230         struct ScrollButtonData *bd = (struct ScrollButtonData *) INST_DATA(cl, g);
231
232         DB2(DBPRINTF("ScrollButton: GM_LAYOUT\n");)
233         ASSERT_VALID_PTR(bd)
234
235         /* Compute new button size */
236         GetGadgetBox(gpl->gpl_GInfo, g, &bd->GBox);
237
238         /* Check if our framing image is valid and can be sent a boopsi message */
239         if ((g->Flags & GFLG_GADGIMAGE) && g->GadgetRender &&
240                 (((struct Image *)g->GadgetRender)->Depth == CUSTOMIMAGEDEPTH))
241         {
242                 struct IBox fakecontents = { 0, 0, 0, 0 };
243
244                 /* Ask the frame what is its size */
245                 DoMethod((Object *)g->GadgetRender, IM_FRAMEBOX,
246                         &fakecontents, &bd->FrameBox, gpl->gpl_GInfo->gi_DrInfo, 0);
247         }
248
249         bd->LayoutDone = TRUE;
250 }
251
252
253
254 static ULONG SB_OMSet (Class *cl, struct Gadget *g, struct opUpdate *msg)
255 {
256         struct ScrollButtonData *bd = (struct ScrollButtonData *) INST_DATA(cl, g);
257         struct TagItem  *ti, *tstate = msg->opu_AttrList;
258
259         DB1(DBPRINTF("ScrollButton: OM_SET\n");)
260         ASSERT_VALID_PTR(bd)
261         ASSERT_VALID_PTR(tstate)
262
263         while (ti = NextTagItem(&tstate))
264         {
265                 /* Check for any of GA_Width, GA_RelWidth, GA_Height, GA_RelHeight.
266                  * This test assumes that the values of these tags are consecutive
267                  * and will break whenever their definitions are changed.
268                  */
269                 if ((ti->ti_Tag >= GA_Width) && (ti->ti_Tag <= GA_RelHeight))
270                         bd->LayoutDone = FALSE;
271         }
272
273         /* The superclass handles everything else */
274         return (DoSuperMethodA(cl, (Object *)g, (Msg) msg));
275 }
276
277
278
279 static ULONG HOOKCALL ScrollButtonDispatcher(
280         REG(a0, Class *cl),
281         REG(a2, struct Gadget *g),
282         REG(a1, Msg msg))
283
284 /* ScrollButton Class Dispatcher entrypoint.
285  * Handle boopsi messages.
286  */
287 {
288         switch (msg->MethodID)
289         {
290                 case GM_GOACTIVE:
291                         return SB_GMGoActive(cl, g, (struct gpInput *)msg);
292
293                 case GM_HANDLEINPUT:
294                         return SB_GMHandleInput(cl, g, (struct gpInput *)msg);
295
296                 case GM_RENDER:
297                         SB_GMRender(cl, g, (struct gpRender *)msg);
298                         return TRUE;
299
300                 case GM_LAYOUT:
301                         SB_GMLayout(cl, g, (struct gpLayout *)msg);
302                         return TRUE;
303
304                 case OM_SET:
305                 case OM_UPDATE:
306                         return SB_OMSet(cl, g, (struct opUpdate *)msg);
307
308                 default:
309                         /* Super class handles everything else */
310                         return (DoSuperMethodA(cl, (Object *)g, msg));
311         }
312 }
313
314
315
316 Class *MakeScrollButtonClass (void)
317 {
318         Class *class;
319
320         if (class = MakeClass(NULL, BUTTONGCLASS, NULL, sizeof(struct ScrollButtonData), 0))
321                 class->cl_Dispatcher.h_Entry = (ULONG (*)()) ScrollButtonDispatcher;
322
323         return class;
324 }
325
326 Class *MakeFrScrollButtonClass (void)
327 {
328         Class *class;
329
330         if (class = MakeClass (NULL, FRBUTTONCLASS, NULL, sizeof(struct ScrollButtonData), 0))
331                 class->cl_Dispatcher.h_Entry = (ULONG (*)()) ScrollButtonDispatcher;
332
333         return class;
334 }
335
336 BOOL FreeScrollButtonClass (Class *cl)
337 {
338         return (FreeClass(cl));
339 }
340
341
342
343 #if (CLASS_FLAVOUR & FLAVOUR_CLASSLIB)
344 /*
345  * Class library support functions
346  */
347
348 struct ClassLibrary * HOOKCALL _UserLibInit(REG(a6, struct ClassLibrary *mybase))
349 {
350         /* Initialize SysBase */
351         SysBase = *((struct ExecBase **)4UL);
352
353         if (!((UtilityBase = (UTILITYBASETYPE *) OpenLibrary("utility.library", 39)) &&
354                 (IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 39))))
355         {
356                 _UserLibCleanup(mybase);
357                 return NULL;
358         }
359
360         if (!(mybase->cl_Class = MakeScrollButtonClass()))
361         {
362                 _UserLibCleanup(mybase);
363                 return NULL;
364         }
365
366         AddClass(mybase->cl_Class);
367         return mybase;
368 }
369
370 struct ClassLibrary * HOOKCALL _UserLibCleanup(REG(a6, struct ClassLibrary *mybase))
371 {
372         if (mybase->cl_Class)
373                 if (!FreeScrollButtonClass(mybase->cl_Class))
374                         return NULL;
375
376         CloseLibrary((struct Library *)IntuitionBase);
377         CloseLibrary((struct Library *)UtilityBase);
378
379         return mybase;
380 }
381
382 Class * HOOKCALL _GetEngine(REG(a6, struct ClassLibrary *mybase))
383 {
384         return mybase->cl_Class;
385 }
386
387 #endif /* (CLASS_FLAVOUR & FLAVOUR_CLASSLIB) */