Initial commit
[amiga/OpenBoopsi.git] / gadgets / PIPWin / PIPClass.c
1 /*
2 **      PIPClass.c
3 **
4 **      Copyright (C) 1996,97 Bernardo Innocenti
5 **
6 **      Picture In Picture gadget class
7 */
8
9 #define INTUI_V36_NAMES_ONLY
10
11 #include <exec/types.h>
12 #include <exec/libraries.h>
13 #include <intuition/intuition.h>
14 #include <intuition/intuitionbase.h>
15 #include <intuition/screens.h>
16 #include <intuition/classes.h>
17 #include <intuition/gadgetclass.h>
18
19 #include <proto/intuition.h>
20 #include <proto/graphics.h>
21 #include <proto/utility.h>
22
23 #include <CompilerSpecific.h>
24 #include <DebugMacros.h>
25 #include <BoopsiStubs.h>
26
27 #include "PIPClass.h"
28
29
30
31 /* PIP gadget private instance data */
32
33 struct PIPData
34 {
35         struct Screen   *Scr;                   /* Snoop this screen...                         */
36         struct BitMap   *BitMap;                /* ...or this BitMap                            */
37         WORD                    Width,  Height; /* Size of snooped object                       */
38         WORD                    OffX,   OffY;   /* Current XY offset of the view        */
39         WORD                    StartX, StartY; /* Start coords for mouse dragging      */
40         WORD                    Dragging;               /* TRUE if dragging with mouse          */
41         WORD                    Dummy;                  /* Keep data longword aligned           */
42         struct IBox             GBox;                   /* Real gadget size                                     */
43 };
44
45
46
47 /* Local function prototypes */
48
49 static void             PIP_GMRender            (Class *cl, struct Gadget *g, struct gpRender *msg);
50 static ULONG    PIP_GMHandleInput       (Class *cl, struct Gadget *g, struct gpInput *msg);
51 static void             PIP_GMGoInactive        (Class *cl, struct Gadget *g, struct gpGoInactive *msg);
52 static void             PIP_GMLayout            (Class *cl, struct Gadget *g, struct gpLayout *msg);
53 static ULONG    PIP_OMNew                       (Class *cl, struct Gadget *g, struct opSet *msg);
54 static ULONG    PIP_OMSet                       (Class *cl, struct Gadget *g, struct opUpdate *msg);
55 static ULONG    PIP_OMGet                       (Class *cl, struct Gadget *g, struct opGet *msg);
56 static ULONG    PIP_PIPMRefresh         (Class *cl, struct Gadget *g, struct pippRefresh *msg);
57 static void             RestrictXY                      (struct PIPData *pip);
58 static void             GetGadgetBox            (struct GadgetInfo *ginfo, struct Gadget *g, struct IBox *rect);
59
60
61
62 static ULONG HOOKCALL PIPDispatcher(
63         REG(a0, Class *cl),
64         REG(a2, struct Gadget *g),
65         REG(a1, Msg msg))
66 {
67         switch (msg->MethodID)
68         {
69                 case GM_RENDER:
70                         PIP_GMRender(cl, g, (struct gpRender *)msg);
71                         return TRUE;
72
73                 case GM_GOACTIVE:
74                 case GM_HANDLEINPUT:
75                         return PIP_GMHandleInput(cl, g, (struct gpInput *)msg);
76
77                 case GM_GOINACTIVE:
78                         PIP_GMGoInactive(cl, g, (struct gpGoInactive *)msg);
79                         return TRUE;
80
81                 case GM_LAYOUT:
82                         /* This method is only supported on V39 and above */
83                         PIP_GMLayout(cl, g, (struct gpLayout *)msg);
84                         return TRUE;
85
86                 case OM_NEW:
87                         return PIP_OMNew(cl, g, (struct opSet *)msg);
88
89                 /* We don't need to override OM_DISPOSE */
90
91                 case OM_SET:
92                 case OM_UPDATE:
93                         return PIP_OMSet(cl, g, (struct opUpdate *)msg);
94
95                 case OM_GET:
96                         return PIP_OMGet(cl, g, (struct opGet *)msg);
97
98                 case PIPM_REFRESH:
99                         return PIP_PIPMRefresh(cl, g, (struct pippRefresh *)msg);
100
101                 default:
102                         /* Unsupported method: let our superclass's
103                          * dispatcher take a look at it.
104                          */
105                         return DoSuperMethodA(cl, (Object *)g, msg);
106         }
107 }
108
109
110
111 static void PIP_GMRender(Class *cl, struct Gadget *g, struct gpRender *msg)
112 {
113         struct PIPData  *pip = INST_DATA(cl, g);
114         struct BitMap   *bitmap;
115
116
117         if (pip->Scr)
118                 bitmap = pip->Scr->RastPort.BitMap;     /* Get screen bitmap */
119         else if (pip->BitMap)
120                 bitmap = pip->BitMap;                           /* Use provided bitmap */
121         else
122                 bitmap = NULL;                                          /* Do nothing otherwise */
123
124
125         switch (msg->gpr_Redraw)
126         {
127                 case GREDRAW_REDRAW:
128
129                         if (!bitmap || (bitmap->Depth < msg->gpr_RPort->BitMap->Depth))
130                         {
131                                 /* Clearing all our visible area is needed because
132                                  * BltBitMapRastPort() will not clear the bitplanes beyond
133                                  * the last bitplane in the source bitmap.
134                                  *
135                                  * NOTE: The pen number really needs to be 0, not BACKGROUNDPEN!
136                                  */
137                                 SetAPen(msg->gpr_RPort, 0);
138                                 RectFill(msg->gpr_RPort,
139                                         pip->GBox.Left, pip->GBox.Top,
140                                         pip->GBox.Left + pip->GBox.Width, pip->GBox.Top + pip->GBox.Height);
141                         }
142
143                         /* NOTE: I'm falling through here! */
144
145                 case GREDRAW_UPDATE:
146
147                         if (bitmap)
148                         {
149                                 /* Scaling, remapping and other similar features could be added here */
150
151                                 /* Update gadget display */
152                                 BltBitMapRastPort(bitmap, pip->OffX, pip->OffY,
153                                         msg->gpr_RPort, pip->GBox.Left, pip->GBox.Top,
154                                         min (pip->GBox.Width, pip->Width), min (pip->GBox.Height, pip->Height),
155                                         0x0C0);
156                         }
157                         /* NOTE: I'm falling through here! */
158
159                 default:
160                         break;
161         }
162 }
163
164
165
166 static ULONG PIP_GMHandleInput(Class *cl, struct Gadget *g, struct gpInput *msg)
167 {
168         struct PIPData          *pip = INST_DATA (cl, g);
169         struct InputEvent       *ie = msg->gpi_IEvent;
170
171
172         /* Handle GM_GOACTIVE */
173         if (msg->MethodID == GM_GOACTIVE)
174         {
175                 if (!pip->Scr && !pip->BitMap)
176                         return GMR_NOREUSE;
177
178                 g->Flags |= GFLG_SELECTED;
179
180                 /* Do not process InputEvent when the gadget has been
181                  * activated by ActivateGadget().
182                  */
183                 if (!ie)
184                         return GMR_MEACTIVE;
185
186                 /* Note: The input event that triggered the gadget
187                  * activation (usually a mouse click) should be passed
188                  * to the GM_HANDLEINPUT method, so we fall down to it.
189                  */
190         }
191
192         /* Handle GM_HANDLEINPUT */
193         switch (ie->ie_Class)
194         {
195                 case IECLASS_RAWMOUSE:
196                 {
197                         switch (ie->ie_Code)
198                         {
199                                 case MENUDOWN:
200                                         /* Deactivate gadget on menu button press */
201                                         return GMR_REUSE;
202
203                                 case SELECTDOWN:
204
205                                         /* Check click outside gadget box */
206
207                                         if ((msg->gpi_Mouse.X < 0) ||
208                                                 (msg->gpi_Mouse.X >= pip->GBox.Width) ||
209                                                 (msg->gpi_Mouse.Y < 0) ||
210                                                 (msg->gpi_Mouse.Y >= pip->GBox.Height))
211                                                 return GMR_REUSE;
212
213                                         /* Store current mouse coordinates for mouse dragging */
214                                         pip->StartX = pip->OffX + msg->gpi_Mouse.X;
215                                         pip->StartY = pip->OffY + msg->gpi_Mouse.Y;
216                                         pip->Dragging = TRUE;
217
218                                         break;
219
220                                 case SELECTUP:
221
222                                         /* Stop mouse dragging mode */
223                                         pip->Dragging = FALSE;
224
225                                         /* Send one final notification to our targets */
226                                         UpdateAttrs((Object *)g, msg->gpi_GInfo, 0,
227                                                 PIPA_OffX,                      pip->StartX - msg->gpi_Mouse.X,
228                                                 PIPA_OffY,                      pip->StartY - msg->gpi_Mouse.Y,
229                                                 TAG_DONE);
230
231                                         break;
232
233
234                                 default: /* Mouse just moved */
235
236                                         /* Call our OM_UPDATE method to change the object attributes.
237                                          * This will also send notification to targets and
238                                          * update the contents of the gadget.
239                                          */
240                                         if (pip->Dragging)
241                                                 UpdateAttrs((Object *)g, msg->gpi_GInfo, OPUF_INTERIM,
242                                                         PIPA_OffX,                      pip->StartX - msg->gpi_Mouse.X,
243                                                         PIPA_OffY,                      pip->StartY - msg->gpi_Mouse.Y,
244                                                         GA_ID,                          g->GadgetID,
245                                                         TAG_DONE);
246                         }
247                         return GMR_MEACTIVE;
248                 }
249
250                 case IECLASS_RAWKEY:
251                 {
252                         LONG tags[3];
253                         WORD pos;
254
255                         switch (ie->ie_Code)
256                         {
257                                 case CURSORUP:
258                                         if (ie->ie_Qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
259                                                 pos = 0;
260                                         else if (ie->ie_Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
261                                                 pos = pip->OffY - pip->GBox.Height + 1;
262                                         else if (ie->ie_Qualifier & IEQUALIFIER_CONTROL)
263                                                 pos = pip->OffY - 8;
264                                         else
265                                                 pos = pip->OffY - 1;
266
267
268                                         tags[0] = PIPA_OffY;
269                                         tags[1] = pos;
270                                         tags[2] = TAG_DONE;
271                                         break;
272
273                                 case CURSORDOWN:
274                                         if (ie->ie_Qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
275                                                 pos = pip->Height - pip->GBox.Height;
276                                         else if (ie->ie_Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
277                                                 pos = pip->OffY + pip->GBox.Height - 1;
278                                         else if (ie->ie_Qualifier & IEQUALIFIER_CONTROL)
279                                                 pos = pip->OffY + 8;
280                                         else
281                                                 pos = pip->OffY + 1;
282
283                                         tags[0] = PIPA_OffY;
284                                         tags[1] = pos;
285                                         tags[2] = TAG_DONE;
286                                         break;
287
288                                 case CURSORLEFT:
289                                         if (ie->ie_Qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
290                                                 pos = 0;
291                                         else if (ie->ie_Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
292                                                 pos = pip->OffX - pip->GBox.Width + 1;
293                                         else if (ie->ie_Qualifier & IEQUALIFIER_CONTROL)
294                                                 pos = pip->OffX - 8;
295                                         else
296                                                 pos = pip->OffX - 1;
297
298                                         tags[0] = PIPA_OffX;
299                                         tags[1] = pos;
300                                         tags[2] = TAG_DONE;
301                                         break;
302
303                                 case CURSORRIGHT:
304                                         if (ie->ie_Qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
305                                                 pos = pip->Width - pip->GBox.Width;
306                                         else if (ie->ie_Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
307                                                 pos = pip->OffX + pip->GBox.Height - 1;
308                                         else if (ie->ie_Qualifier & IEQUALIFIER_CONTROL)
309                                                 pos = pip->OffX + 8;
310                                         else
311                                                 pos = pip->OffX + 1;
312
313                                         tags[0] = PIPA_OffX;
314                                         tags[1] = pos;
315                                         tags[2] = TAG_DONE;
316                                         break;
317
318                                 default:
319                                         tags[0] = TAG_DONE;
320                         }
321
322                         if (tags[0] != TAG_DONE)
323                                 DoMethod((Object *)g, OM_UPDATE, tags, msg->gpi_GInfo, OPUF_INTERIM);
324
325                         return GMR_MEACTIVE;
326                 }
327
328                 default:
329                         return GMR_MEACTIVE;
330         }
331 }
332
333
334
335 static void PIP_GMGoInactive(Class *cl, struct Gadget *g, struct gpGoInactive *msg)
336 {
337         struct PIPData *pip = INST_DATA (cl, g);
338
339         pip->Dragging = FALSE;
340         g->Flags &= ~GFLG_SELECTED;
341 }
342
343
344
345 static void PIP_GMLayout(Class *cl, struct Gadget *g, struct gpLayout *msg)
346 {
347         struct PIPData *pip = INST_DATA(cl, g);
348
349         GetGadgetBox(msg->gpl_GInfo, g, &pip->GBox);
350         RestrictXY(pip);
351
352         /* Notify our targets about it */
353         NotifyAttrs((Object *)g, msg->gpl_GInfo, 0,
354                 PIPA_OffX,                      pip->OffX,
355                 PIPA_OffY,                      pip->OffY,
356                 PIPA_Width,                     pip->Width,
357                 PIPA_Height,            pip->Height,
358                 PIPA_DisplayWidth,      pip->GBox.Width,
359                 PIPA_DisplayHeight,     pip->GBox.Height,
360                 GA_ID,                          g->GadgetID,
361                 TAG_DONE);
362 }
363
364
365
366 static ULONG PIP_OMNew(Class *cl, struct Gadget *g, struct opSet *msg)
367 {
368         ULONG result;
369
370         if (result = DoSuperMethodA(cl, (Object *)g, (Msg)msg))
371         {
372                 struct PIPData *pip = (struct PIPData *) INST_DATA(cl, (Object *)result);
373
374                 /* Read creation time attributes */
375                 pip->Scr        = (struct Screen *) GetTagData(PIPA_Screen,     NULL,   msg->ops_AttrList);
376                 pip->BitMap     = (struct BitMap *) GetTagData(PIPA_BitMap,     NULL,   msg->ops_AttrList);
377                 pip->OffX       = (WORD)                        GetTagData(PIPA_OffX,   0,              msg->ops_AttrList);
378                 pip->OffY       = (WORD)                        GetTagData(PIPA_OffY,   0,              msg->ops_AttrList);
379                 pip->Dragging = FALSE;
380         }
381         return result;
382 }
383
384
385
386 static ULONG PIP_OMSet(Class *cl, struct Gadget *g, struct opUpdate *msg)
387 {
388         struct PIPData  *pip = (struct PIPData *) INST_DATA(cl, g);
389         struct TagItem  *ti,
390                                         *tstate = msg->opu_AttrList;
391         ULONG   result = FALSE;
392         BOOL    do_super_method = FALSE,
393                         render                  = FALSE,
394                         notify                  = FALSE;
395
396         while (ti = NextTagItem(&tstate))
397                 switch (ti->ti_Tag)
398                 {
399                         case PIPA_Screen:
400                                 pip->BitMap     = NULL;
401
402                                 if (pip->Scr = (struct Screen *)ti->ti_Data)
403                                 {
404                                         pip->Width      = pip->Scr->Width;
405                                         pip->Height     = pip->Scr->Height;
406                                 }
407                                 else
408                                         pip->Width = pip->Height = 0;
409
410                                 RestrictXY(pip);
411
412                                 break;
413
414                         case PIPA_BitMap:
415                                 pip->Scr = NULL;
416                                 if (pip->BitMap = (struct BitMap *)ti->ti_Data)
417                                 {
418                                         pip->Width      = pip->BitMap->BytesPerRow << 3;
419                                         pip->Height     = pip->BitMap->Rows;
420                                 }
421                                 else
422                                         pip->Width = pip->Height = 0;
423
424                                 RestrictXY(pip);
425
426                                 render = TRUE;
427                                 notify = TRUE;
428
429                                 break;
430
431                         case PIPA_OffX:
432                                 if (pip->OffX != ti->ti_Data)
433                                 {
434                                         WORD newx = (WORD)ti->ti_Data;
435
436                                         /* Restrict offset to valid limits */
437                                         if (newx + pip->GBox.Width > pip->Width)
438                                                 newx = pip->Width - pip->GBox.Width;
439                                         if (newx < 0)
440                                                 newx = 0;
441
442                                         if (newx != pip->OffX)
443                                         {
444                                                 pip->OffX = newx;
445                                                 render = TRUE;
446                                                 notify = TRUE;
447                                         }
448                                 }
449                                 break;
450
451                         case PIPA_OffY:
452                                 if (pip->OffY != ti->ti_Data)
453                                 {
454                                         WORD newy = (WORD)ti->ti_Data;
455
456                                         /* Restrict offset to valid limits */
457                                         if (newy + pip->GBox.Height > pip->Height)
458                                                 newy = pip->Height - pip->GBox.Height;
459                                         if (newy < 0)
460                                                 newy = 0;
461
462                                         if (newy != pip->OffY)
463                                         {
464                                                 pip->OffY = newy;
465                                                 render = TRUE;
466                                                 notify = TRUE;
467                                         }
468                                 }
469                                 break;
470
471                         case PIPA_MoveUp:
472                                 if (pip->OffY)
473                                 {
474                                         if (pip->OffY > 8)
475                                                 pip->OffY -= 8;
476                                         else
477                                                 pip->OffY = 0;
478
479                                         render = TRUE;
480                                         notify = TRUE;
481                                 }
482                                 break;
483
484                         case PIPA_MoveDown:
485                                 if (pip->OffY < pip->Height - pip->GBox.Height)
486                                 {
487                                         if (pip->OffY + pip->GBox.Height < pip->Height - 8)
488                                                 pip->OffY += 8;
489                                         else
490                                                 pip->OffY = pip->Height - pip->GBox.Height;
491
492                                         render = TRUE;
493                                         notify = TRUE;
494                                 }
495                                 break;
496
497                         case PIPA_MoveLeft:
498                                 if (pip->OffX)
499                                 {
500                                         if (pip->OffX > 8)
501                                                 pip->OffX -= 8;
502                                         else
503                                                 pip->OffX = 0;
504
505                                         render = TRUE;
506                                         notify = TRUE;
507                                 }
508                                 break;
509
510                         case PIPA_MoveRight:
511                                 if (pip->OffX < pip->Width - pip->GBox.Width)
512                                 {
513                                         if (pip->OffX + pip->GBox.Width < pip->Width - 8)
514                                                 pip->OffX += 8;
515                                         else
516                                                 pip->OffX = pip->Width - pip->GBox.Width;
517
518                                         render = TRUE;
519                                         notify = TRUE;
520                                 }
521                                 break;
522
523                         default:
524                                 /* This little optimization avoids forwarding the
525                                  * OM_SET method to our superclass when there are
526                                  * no unknown tags.
527                                  */
528                                 do_super_method = TRUE;
529                                 break;
530                 }
531
532
533         /* Forward method to our superclass dispatcher, only when needed */
534
535         if (do_super_method)
536                 result = (DoSuperMethodA(cl, (Object *)g, (Msg) msg));
537
538
539         /* Update gadget imagery, only when needed */
540
541         if (render && msg->opu_GInfo)
542         {
543                 struct RastPort *rp;
544
545                 if (rp = ObtainGIRPort(msg->opu_GInfo))
546                 {
547                         DoMethod((Object *)g,
548                                 GM_RENDER, msg->opu_GInfo, rp, GREDRAW_UPDATE);
549                         ReleaseGIRPort(rp);
550                         result = TRUE;
551                 }
552         }
553
554
555         /* Notify our targets about all changed attributes, only when needed */
556
557         if (notify)
558                 NotifyAttrs((Object *)g, msg->opu_GInfo,
559                         (msg->MethodID == OM_UPDATE) ? msg->opu_Flags : 0,
560                         PIPA_OffX,                      pip->OffX,
561                         PIPA_OffY,                      pip->OffY,
562                         GA_ID,                          g->GadgetID,
563                         TAG_DONE);
564
565         return result;
566 }
567
568
569
570 static ULONG PIP_OMGet(Class *cl, struct Gadget *g, struct opGet *msg)
571 {
572         struct PIPData *pip = INST_DATA (cl, g);
573
574         switch (msg->opg_AttrID)
575         {
576                 case PIPA_Screen:
577                         *(msg->opg_Storage) = (ULONG) pip->Scr;
578                         return TRUE;
579
580                 case PIPA_BitMap:
581                         *(msg->opg_Storage) = (ULONG) pip->BitMap;
582                         return TRUE;
583
584                 case PIPA_OffX:
585                         *(msg->opg_Storage) = (ULONG) pip->OffX;
586                         return TRUE;
587
588                 case PIPA_OffY:
589                         *(msg->opg_Storage) = (ULONG) pip->OffY;
590                         return TRUE;
591
592                 case PIPA_Width:
593                         *(msg->opg_Storage) = (ULONG) pip->Width;
594                         return TRUE;
595
596                 case PIPA_Height:
597                         *(msg->opg_Storage) = (ULONG) pip->Height;
598                         return TRUE;
599
600                 default:
601                         return DoSuperMethodA(cl, (Object *)g, (Msg) msg);
602         }
603 }
604
605
606
607 static ULONG PIP_PIPMRefresh(Class *cl, struct Gadget *g, struct pippRefresh *msg)
608 {
609         struct RastPort *rp;
610
611         if (msg->pipp_GInfo && (rp = ObtainGIRPort(msg->pipp_GInfo)))
612         {
613                 /* Call our GM_RENDER method */
614
615                 DoMethod((Object *)g, GM_RENDER, msg->pipp_GInfo, rp, GREDRAW_UPDATE);
616                 ReleaseGIRPort(rp);
617                 return TRUE;
618         }
619
620         return FALSE;
621 }
622
623
624
625 static void RestrictXY(struct PIPData *pip)
626
627 /* Restrict XOff and YOff inside bitmap limits */
628 {
629         if (pip->OffY + pip->GBox.Height > pip->Height)
630                 pip->OffY = pip->Height - pip->GBox.Height;
631         if (pip->OffY < 0) pip->OffY = 0;
632
633         if (pip->OffX + pip->GBox.Width > pip->Width)
634                 pip->OffX = pip->Width - pip->GBox.Width;
635         if (pip->OffX < 0) pip->OffX = 0;
636 }
637
638
639
640 static void GetGadgetBox(struct GadgetInfo *ginfo, struct Gadget *g, struct IBox *rect)
641
642 /* This function gets the actual IBox where a gadget exists
643  * in a window.  The special cases it handles are all the REL#?
644  * (relative positioning flags).
645  */
646 {
647         rect->Left = g->LeftEdge;
648         if (g->Flags & GFLG_RELRIGHT) rect->Left += ginfo->gi_Domain.Width - 1;
649
650         rect->Top = g->TopEdge;
651         if (g->Flags & GFLG_RELBOTTOM) rect->Top += ginfo->gi_Domain.Height - 1;
652
653         rect->Width = g->Width;
654         if (g->Flags & GFLG_RELWIDTH) rect->Width += ginfo->gi_Domain.Width;
655
656         rect->Height = g->Height;
657         if (g->Flags & GFLG_RELHEIGHT) rect->Height += ginfo->gi_Domain.Height;
658 }
659
660
661
662 Class *MakePIPClass(void)
663 {
664         Class *PIPClass;
665
666         if (PIPClass = MakeClass(NULL, GADGETCLASS, NULL, sizeof (struct PIPData), 0))
667                 PIPClass->cl_Dispatcher.h_Entry = (ULONG (*)()) PIPDispatcher;
668
669         return PIPClass;
670 }
671
672
673
674 void FreePIPClass(Class *PIPClass)
675 {
676         FreeClass(PIPClass);
677 }