Initial commit.
[amiga/xmodule.git] / CustomClasses.c
1 /*
2 **      CustomClasses.c
3 **
4 **      Copyright (C) 1995,96,97 Bernardo Innocenti
5 **
6 **      Special custom BOOPSI classes.
7 */
8
9 #include <exec/memory.h>
10 #include <utility/tagitem.h>
11 #include <graphics/gfxbase.h>
12 #include <devices/inputevent.h>
13
14 #include <intuition/intuition.h>
15 #include <intuition/classes.h>
16 #include <intuition/classusr.h>
17 #include <intuition/gadgetclass.h>
18 #include <intuition/icclass.h>
19 #include <intuition/imageclass.h>
20
21 #include <proto/exec.h>
22 #include <proto/graphics.h>
23 #include <proto/intuition.h>
24 #include <proto/utility.h>
25
26 #include "CustomClasses.h"
27 #include "XModulePriv.h"
28 #include "Gui.h"
29
30 /* Per object instance data */
31 struct ScrollButtonData
32 {
33         /* The number of ticks we still have to wait
34          * before sending any notification.
35          */
36         ULONG TickCounter;
37 };
38
39
40
41 /* Function prototypes */
42
43 static LIBCALL ULONG ScrollButtonDispatcher (
44         REG(a0, Class *cl),
45         REG(a2, struct Gadget *g),
46         REG(a1, struct gpInput *gpi));
47
48 static ULONG HOOKCALL VImageDispatcher (
49         REG(a0, Class *cl),
50         REG(a2, struct Image *im),
51         REG(a1, struct opSet *ops));
52
53 static void NotifyAttrChanges   (Object *o, struct GadgetInfo *gi, ULONG flags, Tag attr1, ...);
54 static void DrawPlayImage               (struct RastPort *rp, UWORD width, UWORD heigth);
55 static void DrawStopImage               (struct RastPort *rp, UWORD width, UWORD heigth);
56 static void DrawForwardImage    (struct RastPort *rp, UWORD width, UWORD heigth);
57 static void DrawRewindImage             (struct RastPort *rp, UWORD width, UWORD heigth);
58 static void DrawPickImage               (struct RastPort *rp, UWORD width, UWORD height);
59 static void DrawVImage                  (struct impDraw *imp, struct Image *im, struct BitMap *bm);
60
61
62
63 /* tagcall stub for OM_NOTIFY */
64 static void NotifyAttrChanges (Object *o, struct GadgetInfo *gi, ULONG flags, Tag attr1, ...)
65 {
66         DoSuperMethod (OCLASS(o), o, OM_NOTIFY, &attr1, gi, flags);
67 }
68
69
70 /*
71 **      ScrollButtonClass
72 **
73 **      Parts of the code have been inspired by ScrollerWindow 0.3 demo
74 **      Copyright © 1994 Christoph Feck, TowerSystems.
75 **
76 **      Subclass of buttongclass.  The ROM class has two problems, which make
77 **      it not quite usable for scrollarrows.  The first problem is the missing
78 **      delay.  Once the next INTUITICK gets send by input.device, the ROM
79 **      class already sends a notification.  The other problem is that it also
80 **      notifies us, when the button finally gets released (which is necessary
81 **      for command buttons).
82 **
83 **      We define a new class with the GM_GOACTIVE and GM_HANDLEINPUT method
84 **      overloaded to work around these problems.
85 */
86
87 static LIBCALL ULONG ScrollButtonDispatcher (
88         REG(a0, Class *cl),
89         REG(a2, struct Gadget *g),
90         REG(a1, struct gpInput *gpi))
91
92 /* ScrollButton Class Dispatcher entrypoint.
93  * Handle BOOPSI messages.
94  */
95 {
96         struct ScrollButtonData *bd = (struct ScrollButtonData *) INST_DATA(cl, g);
97
98         switch (gpi->MethodID)
99         {
100                 case GM_GOACTIVE:
101                         /* May define an attribute to make delay configurable */
102                         bd->TickCounter = 3;
103
104                         /* Notify our target that we have initially hit. */
105                         NotifyAttrChanges ((Object *)g, gpi->gpi_GInfo, 0,
106                                 GA_ID, g->GadgetID,
107                                 TAG_DONE);
108
109                         /* Send more input */
110                         return GMR_MEACTIVE;
111
112                 case GM_HANDLEINPUT:
113                 {
114                         struct RastPort *rp;
115                         ULONG retval = GMR_MEACTIVE;
116                         UWORD selected = 0;
117
118                         /* This also works with classic (non-boopsi) images. */
119                         if (PointInImage ((gpi->gpi_Mouse.X << 16) + (gpi->gpi_Mouse.Y), g->GadgetRender))
120                         {
121                                 /* We are hit */
122                                 selected = GFLG_SELECTED;
123                         }
124
125                         if (gpi->gpi_IEvent->ie_Class == IECLASS_RAWMOUSE && gpi->gpi_IEvent->ie_Code == SELECTUP)
126                         {
127                                 /* Gadgetup, time to go */
128                                 retval = GMR_NOREUSE;
129                                 /* Unselect the gadget on our way out... */
130                                 selected = 0;
131                         }
132                         else if (gpi->gpi_IEvent->ie_Class == IECLASS_TIMER)
133                         {
134                                 /* We got a tick.  Decrement counter, and if 0, send notify. */
135
136                                 if (bd->TickCounter) bd->TickCounter--;
137                                 else if (selected)
138                                 {
139                                         NotifyAttrChanges ((Object *) g, gpi->gpi_GInfo, 0,
140                                                 GA_ID, g->GadgetID,
141                                                 TAG_DONE);
142                                 }
143                         }
144
145                         if ((g->Flags & GFLG_SELECTED) != selected)
146                         {
147                                 /* Update changes in gadget render */
148                                 g->Flags ^= GFLG_SELECTED;
149                                 if (rp = ObtainGIRPort (gpi->gpi_GInfo))
150                                 {
151                                         DoMethod ((Object *) g, GM_RENDER, gpi->gpi_GInfo, rp, GREDRAW_UPDATE);
152                                         ReleaseGIRPort (rp);
153                                 }
154                         }
155                         return retval;
156                 }
157
158                 default:
159                         /* Super class handles everything else */
160                         return (DoSuperMethodA (cl, (Object *)g, (Msg) gpi));
161         }
162 }
163
164
165
166 GLOBALCALL Class *InitScrollButtonClass (void)
167 {
168         Class *class;
169
170         if (class = MakeClass (NULL, BUTTONGCLASS, NULL, sizeof(struct ScrollButtonData), 0))
171                 class->cl_Dispatcher.h_Entry = (ULONG (*)()) ScrollButtonDispatcher;
172
173         return class;
174 }
175
176
177
178 GLOBALCALL BOOL FreeScrollButtonClass (Class *cl)
179 {
180         return (FreeClass (cl));
181 }
182
183
184
185 static ULONG HOOKCALL VImageDispatcher (
186         REG(a0, Class *cl),
187         REG(a2, struct Image *im),
188         REG(a1, struct opSet *ops))
189
190 /* VImage Class Dispatcher entrypoint.
191  * Handle BOOPSI messages.
192  */
193 {
194         switch (ops->MethodID)
195         {
196                 case OM_NEW:
197
198                         /* Create the image structure */
199                         if (im = (struct Image *)DoSuperMethodA (cl, (Object *)im, (Msg) ops))
200                         {
201                                 ULONG                    which;
202                                 struct RastPort  rp;
203                         /*      struct DrawInfo *dri = (struct DrawInfo *)GetTagData (GA_DrawInfo, NULL, ops->ops_AttrList);
204                         */
205                                 which = GetTagData (SYSIA_Which, 0, ops->ops_AttrList);
206
207                                 InitRastPort (&rp);
208
209 #ifndef OS30_ONLY
210                                 if (GfxBase->LibNode.lib_Version >= 39)
211 #endif /* !OS30_ONLY */
212                                         rp.BitMap = AllocBitMap (im->Width, im->Height, 1, BMF_CLEAR, NULL);
213 #ifndef OS30_ONLY
214                                 else
215                                 {
216                                         if (rp.BitMap = AllocMem (sizeof (struct BitMap), MEMF_PUBLIC))
217                                         {
218                                                 InitBitMap (rp.BitMap, 1, im->Width, im->Height);
219                                                 if (!(rp.BitMap->Planes[0] = AllocMem (RASSIZE(im->Width, im->Height), MEMF_CHIP | MEMF_CLEAR)))
220                                                 {
221                                                         FreeMem (rp.BitMap, sizeof (struct BitMap));
222                                                         rp.BitMap = NULL;
223                                                 }
224                                         }
225                                 }
226 #endif /* !OS30_ONLY */
227
228                                 if (rp.BitMap)
229                                 {
230                                         PLANEPTR                planeptr;
231                                         struct TmpRas   tmpras;
232                                         struct AreaInfo areainfo;
233                                         WORD                    areabuffer[(5 * 10 + 1) / 2];
234
235                                         if (planeptr = AllocRaster (im->Width, im->Height))
236                                         {
237                                                 InitTmpRas (&tmpras, planeptr, RASSIZE(im->Width, im->Height));
238                                                 InitArea (&areainfo, areabuffer, 10);
239                                                 SetAPen (&rp, 1);
240                                                 rp.TmpRas = &tmpras;
241                                                 rp.AreaInfo = &areainfo;
242
243                                                 switch (which)
244                                                 {
245                                                         case IM_PLAY:
246                                                                 DrawPlayImage (&rp, im->Width, im->Height);
247                                                                 break;
248
249                                                         case IM_STOP:
250                                                                 DrawStopImage (&rp, im->Width, im->Height);
251                                                                 break;
252
253                                                         case IM_FWD:
254                                                                 DrawForwardImage (&rp, im->Width, im->Height);
255                                                                 break;
256
257                                                         case IM_REW:
258                                                                 DrawRewindImage (&rp, im->Width, im->Height);
259                                                                 break;
260
261                                                         case IM_PICK:
262                                                                 DrawPickImage (&rp, im->Width, im->Height);
263                                                                 break;
264                                                 }
265
266                                                 /* Just to be sure... */
267                                                 rp.TmpRas = NULL;
268                                                 rp.AreaInfo = NULL;
269
270                                                 FreeRaster (planeptr, im->Width, im->Height);
271                                         }
272
273                                         /* This way our image will complement better */
274                                         rp.BitMap->Planes[1] = (UBYTE *)0;
275                                         rp.BitMap->Depth = 2;
276
277
278                                         /* Failing to allocate the TmpRas will cause the
279                                          * image to be blank, but no error will be
280                                          * reported.
281                                          */
282
283                                         /* Store the BitMap pointer here for later usage */
284                                         im->ImageData = (UWORD *)rp.BitMap;
285
286                                         return (ULONG)im;       /* Return new image object */
287                                 }
288
289                                 DisposeObject (im);
290                         }
291
292                         return NULL;
293
294                 case IM_DRAW:
295                 case IM_DRAWFRAME:
296                         DrawVImage ((struct impDraw *)ops, im, (struct BitMap *)im->ImageData);
297                         break;
298
299                 case OM_DISPOSE:
300
301                         /* Restore original depth! */
302                         ((struct BitMap *)im->ImageData)->Depth = 1;
303
304 #ifndef OS30_ONLY
305                         if (GfxBase->LibNode.lib_Version >= 39)
306 #endif /* !OS30_ONLY */
307                                 FreeBitMap ((struct BitMap *)im->ImageData);
308 #ifndef OS30_ONLY
309                         else
310                         {
311                                 FreeMem (((struct BitMap *)im->ImageData)->Planes[0], RASSIZE(im->Width, im->Height));
312                                 FreeMem (((struct BitMap *)im->ImageData), sizeof (struct BitMap));
313                         }
314 #endif /* !OS30_ONLY */
315
316                         /* Now let our superclass free it's istance */
317                         /* Note: I'm falling through here! */
318
319                 default:
320                         /* Our Super class handles everything else */
321                         return (DoSuperMethodA (cl, (Object *)im, (Msg) ops));
322         }
323 }
324
325
326
327 GLOBALCALL Class *InitVImageClass (void)
328 {
329         Class *class;
330
331         if (class = MakeClass (NULL, IMAGECLASS, NULL, 0, 0))
332                 class->cl_Dispatcher.h_Entry = (ULONG (*)()) VImageDispatcher;
333
334         return class;
335 }
336
337
338
339 GLOBALCALL BOOL FreeVImageClass (Class *cl)
340 {
341         return (FreeClass (cl));
342 }
343
344
345
346 static void DrawPlayImage (struct RastPort *rp, UWORD width, UWORD height)
347 {
348         UWORD   ymin = height / 4,
349                         ymax = (height * 3) / 4,
350                         ymid;
351
352         ymin -= (ymax - ymin) & 1;      /* Force odd heigth for better arrow aspect */
353         ymid = (ymin + ymax) / 2;
354
355         RectFill (rp, 1, ymin, (width / 4) - 1, ymax);
356
357         AreaMove (rp, width / 3, ymin);
358         AreaDraw (rp, width - 2, ymid);
359         AreaDraw (rp, width / 3, ymax);
360
361         AreaEnd (rp);
362 }
363
364
365
366 static void DrawStopImage (struct RastPort *rp, UWORD width, UWORD height)
367 {
368         RectFill (rp, width / 4, height / 4, (width * 3) / 4, (height * 3) / 4);
369 }
370
371
372
373 static void DrawForwardImage (struct RastPort *rp, UWORD width, UWORD height)
374 {
375         UWORD   ymin = height / 4,
376                         ymax = (height * 3) / 4,
377                         ymid;
378
379         ymin -= (ymax - ymin) & 1;      /* Force odd heigth for better arrow aspect */
380         ymid = (ymin + ymax) / 2;
381
382         AreaMove (rp, 1, ymin);
383         AreaDraw (rp, width / 2, ymid);
384         AreaDraw (rp, 1, ymax);
385
386         AreaMove (rp, width / 2, ymin);
387         AreaDraw (rp, width - 2, ymid);
388         AreaDraw (rp, width / 2, ymax);
389
390         AreaEnd (rp);
391 }
392
393
394
395 static void DrawRewindImage (struct RastPort *rp, UWORD width, UWORD height)
396 {
397         UWORD   ymin = height / 4,
398                         ymax = (height * 3) / 4,
399                         ymid;
400
401         ymin -= (ymax - ymin) & 1;      /* Force odd heigth for better arrow aspect */
402         ymid = (ymin + ymax) / 2;
403
404         AreaMove (rp, width - 2, ymin);
405         AreaDraw (rp, width / 2, ymid);
406         AreaDraw (rp, width - 2, ymax);
407
408         AreaMove (rp, width / 2 - 1, ymin);
409         AreaDraw (rp, 1, ymid);
410         AreaDraw (rp, width / 2 - 1, ymax);
411
412         AreaEnd (rp);
413 }
414
415
416 static void DrawPickImage (struct RastPort *rp, UWORD width, UWORD height)
417 /*
418  *      arrowxmin
419  *      | tailxmin
420  *      | | tailxmax
421  *      | | |
422  *      | v v
423  *      | ###<----tailymin
424  *      v ###
425  *      #######<--arrowymin
426  *       #####
427  *        ###
428  *         #<-----arrowymax
429  *      #######<--arrowymax+1
430  */
431 {
432         UWORD   tailymin        = height / 6,
433                         tailxmin        = (width * 2) / 5,
434                         tailxmax        = (width * 3) / 5,
435                         arrowymin       = (height * 2) / 5,
436                         arrowymax       = (height * 4) / 5,
437                         arrowxmin       = width / 5,
438                         arrowxmax       = (width * 4) / 5;
439
440         AreaMove (rp, tailxmin, tailymin);
441         AreaDraw (rp, tailxmax, tailymin);
442         AreaDraw (rp, tailxmax, arrowymin);
443         AreaDraw (rp, arrowxmax, arrowymin);
444         AreaDraw (rp, (arrowxmin + arrowxmax) / 2, arrowymax);
445         AreaDraw (rp, arrowxmin, arrowymin);
446         AreaDraw (rp, tailxmin, arrowymin);
447         AreaEnd (rp);
448
449         if (arrowymax < height - 1) arrowymax++;
450
451         Move (rp, arrowxmin, arrowymax);
452         Draw (rp, arrowxmax, arrowymax);
453 }
454
455
456
457 static void DrawVImage (struct impDraw *imp, struct Image *im, struct BitMap *bm)
458 {
459         if (bm)
460                 BltBitMapRastPort (bm, 0, 0, imp->imp_RPort,
461                         imp->imp_Offset.X, imp->imp_Offset.Y, im->Width, im->Height,
462                         (imp->imp_State == IDS_SELECTED) ? 0x030 : 0x0C0);
463 }
464
465
466
467 GLOBALCALL struct Gadget *CreateUpButton (ULONG id, struct Gadget *target, LONG *map, LONG Place)
468 {
469         struct Gadget   *UpButton;
470         struct Image    *UpImage;
471
472         if (!(UpImage = NewImageObject (UPIMAGE)))
473                 return NULL;
474
475         if (!(UpButton = (struct Gadget *)NewObject (ScrollButtonClass, NULL,
476                 GA_ID,                          id,
477                 GA_RelBottom,           - (UpImage->Height * 2) - SizeHeight + 1,
478                 (Place == SCROLLERPLACE_LEFT) ? GA_Left : GA_RelRight,
479                         (Place == SCROLLERPLACE_LEFT) ? 0 : (- SizeWidth + 1),
480                 /* ^--- perhaps using UpImage->Width would be better... */
481
482                 (Place == SCROLLERPLACE_LEFT) ? GA_LeftBorder : GA_RightBorder, TRUE,
483
484                 /* No need for GA_Width/Height.  buttongclass is smart :) */
485                 GA_Image,                       UpImage,
486                 ICA_TARGET,                     target,
487                 ICA_MAP,                        map,
488                 TAG_DONE)))
489                 DisposeObject (UpImage);
490
491         return UpButton;
492 }
493
494
495
496 GLOBALCALL struct Gadget *CreateDnButton (ULONG id, struct Gadget *target, LONG *map, LONG Place)
497 {
498         struct Gadget   *DnButton;
499         struct Image    *DnImage;
500
501
502         if (!(DnImage = NewImageObject (DOWNIMAGE)))
503                 return NULL;
504
505         if (!(DnButton = (struct Gadget *)NewObject (ScrollButtonClass, NULL,
506                 GA_ID,                          id,
507                 GA_RelBottom,           - DnImage->Height - SizeHeight + 1,
508                 (Place == SCROLLERPLACE_LEFT) ? GA_Left : GA_RelRight,
509                         (Place == SCROLLERPLACE_LEFT) ? 0 : (- SizeWidth + 1),
510                 (Place == SCROLLERPLACE_LEFT) ? GA_LeftBorder : GA_RightBorder, TRUE,
511                 /* No need for GA_Width/Height.  buttongclass is smart :) */
512                 GA_Image,                       DnImage,
513                 ICA_TARGET,                     target,
514                 ICA_MAP,                        map,
515                 TAG_DONE)))
516                 DisposeObject (DnImage);
517
518         return DnButton;
519 }
520
521
522
523 GLOBALCALL struct Gadget *CreateSxButton (ULONG id, struct Gadget *target, LONG *map)
524 {
525         struct Gadget   *SxButton;
526         struct Image    *SxImage;
527
528
529         if (!(SxImage = NewImageObject (LEFTIMAGE)))
530                 return NULL;
531
532         if (!(SxButton = (struct Gadget *)NewObject (ScrollButtonClass, NULL,
533                 GA_ID,                          id,
534                 GA_RelBottom,           - SxImage->Height + 1,
535                 GA_RelRight,            - SizeWidth - (SxImage->Width * 2) + 1,
536                 GA_BottomBorder,        TRUE,
537                 /* No need for GA_Width/Height.  buttongclass is smart :) */
538                 GA_Image,                       SxImage,
539                 ICA_TARGET,                     target,
540                 ICA_MAP,                        map,
541                 TAG_DONE)))
542                 DisposeObject (SxImage);
543
544         return SxButton;
545 }
546
547
548
549 GLOBALCALL struct Gadget *CreateDxButton (ULONG id, struct Gadget *target, LONG *map)
550 {
551         struct Gadget   *DxButton;
552         struct Image    *DxImage;
553
554
555         if (!(DxImage = NewImageObject (RIGHTIMAGE)))
556                 return NULL;
557
558         if (!(DxButton = (struct Gadget *)NewObject (ScrollButtonClass, NULL,
559                 GA_ID,                          id,
560                 GA_RelBottom,           - DxImage->Height + 1,
561                 GA_RelRight,            - SizeWidth - DxImage->Width + 1,
562                 GA_BottomBorder,        TRUE,
563                 /* No need for GA_Width/Height.  buttongclass is smart :) */
564                 GA_Image,                       DxImage,
565                 ICA_TARGET,                     target,
566                 ICA_MAP,                        map,
567                 TAG_DONE)))
568                 DisposeObject (DxImage);
569
570         return DxButton;
571 }
572
573
574
575 GLOBALCALL struct Gadget *CreateVSlider (ULONG id, struct Gadget *target, LONG *map, LONG ButtonsSpacing, LONG Place)
576 {
577         UWORD horizpos;
578
579         horizpos = (Place == SCROLLERPLACE_LEFT) ? Scr->WBorLeft : (- SizeWidth + 5);
580
581         return ((struct Gadget *)NewObject (NULL, PROPGCLASS,
582                 GA_ID,                          id,
583                 GA_Top,                         OffY + 1,
584                 (Place == SCROLLERPLACE_LEFT) ? GA_Left : GA_RelRight, horizpos,
585                 GA_Width,                       SizeWidth - 8,
586                 GA_RelHeight,           - OffY - Scr->WBorBottom - SizeHeight
587                                                         - ButtonsSpacing,
588                 (Place == SCROLLERPLACE_LEFT) ? GA_LeftBorder : GA_RightBorder, TRUE,
589
590                 PGA_NewLook,            TRUE,
591
592                 /* Borderless sliders do only look good with newlook screens */
593                 PGA_Borderless,         ((DrawInfo->dri_Flags & DRIF_NEWLOOK) && DrawInfo->dri_Depth != 1),
594
595                 ICA_TARGET,                     target,
596                 ICA_MAP,                        map,
597
598                 TAG_DONE));
599 }
600
601
602
603 GLOBALCALL struct Gadget *CreateHSlider (ULONG id, struct Gadget *target, LONG *map, LONG ButtonsSpacing)
604 {
605         struct Gadget   *HSlider;
606
607         if (!(HSlider = (struct Gadget *)NewObject (NULL, PROPGCLASS,
608                 GA_ID,                          id,
609                 GA_Left,                        OffX - 1,
610                 GA_RelBottom,           - SizeHeight + ((SizeHeight > 15) ? 4 : 3),
611                 GA_RelWidth,            - OffX - SizeWidth
612                                                         - ButtonsSpacing,
613                 GA_Height,                      SizeHeight - ((SizeHeight > 15)  ? 6 : 4),
614                 GA_BottomBorder,        TRUE,
615
616                 PGA_NewLook,            TRUE,
617                 PGA_Borderless,         ((DrawInfo->dri_Flags & DRIF_NEWLOOK) && DrawInfo->dri_Depth != 1),
618                 PGA_Freedom,            FREEHORIZ,
619
620                 ICA_TARGET,                     target,
621                 ICA_MAP,                        map,
622
623                 TAG_DONE)))
624                 return NULL;
625         return HSlider;
626 }