4 ** Copyright (C) 1996,97 Bernardo Innocenti
6 ** Picture In Picture gadget class
9 #define INTUI_V36_NAMES_ONLY
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>
19 #include <proto/intuition.h>
20 #include <proto/graphics.h>
21 #include <proto/utility.h>
23 #include <CompilerSpecific.h>
24 #include <DebugMacros.h>
25 #include <BoopsiStubs.h>
31 /* PIP gadget private instance data */
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 */
47 /* Local function prototypes */
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);
62 static ULONG HOOKCALL PIPDispatcher(
64 REG(a2, struct Gadget *g),
67 switch (msg->MethodID)
70 PIP_GMRender(cl, g, (struct gpRender *)msg);
75 return PIP_GMHandleInput(cl, g, (struct gpInput *)msg);
78 PIP_GMGoInactive(cl, g, (struct gpGoInactive *)msg);
82 /* This method is only supported on V39 and above */
83 PIP_GMLayout(cl, g, (struct gpLayout *)msg);
87 return PIP_OMNew(cl, g, (struct opSet *)msg);
89 /* We don't need to override OM_DISPOSE */
93 return PIP_OMSet(cl, g, (struct opUpdate *)msg);
96 return PIP_OMGet(cl, g, (struct opGet *)msg);
99 return PIP_PIPMRefresh(cl, g, (struct pippRefresh *)msg);
102 /* Unsupported method: let our superclass's
103 * dispatcher take a look at it.
105 return DoSuperMethodA(cl, (Object *)g, msg);
111 static void PIP_GMRender(Class *cl, struct Gadget *g, struct gpRender *msg)
113 struct PIPData *pip = INST_DATA(cl, g);
114 struct BitMap *bitmap;
118 bitmap = pip->Scr->RastPort.BitMap; /* Get screen bitmap */
119 else if (pip->BitMap)
120 bitmap = pip->BitMap; /* Use provided bitmap */
122 bitmap = NULL; /* Do nothing otherwise */
125 switch (msg->gpr_Redraw)
129 if (!bitmap || (bitmap->Depth < msg->gpr_RPort->BitMap->Depth))
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.
135 * NOTE: The pen number really needs to be 0, not BACKGROUNDPEN!
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);
143 /* NOTE: I'm falling through here! */
149 /* Scaling, remapping and other similar features could be added here */
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),
157 /* NOTE: I'm falling through here! */
166 static ULONG PIP_GMHandleInput(Class *cl, struct Gadget *g, struct gpInput *msg)
168 struct PIPData *pip = INST_DATA (cl, g);
169 struct InputEvent *ie = msg->gpi_IEvent;
172 /* Handle GM_GOACTIVE */
173 if (msg->MethodID == GM_GOACTIVE)
175 if (!pip->Scr && !pip->BitMap)
178 g->Flags |= GFLG_SELECTED;
180 /* Do not process InputEvent when the gadget has been
181 * activated by ActivateGadget().
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.
192 /* Handle GM_HANDLEINPUT */
193 switch (ie->ie_Class)
195 case IECLASS_RAWMOUSE:
200 /* Deactivate gadget on menu button press */
205 /* Check click outside gadget box */
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))
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;
222 /* Stop mouse dragging mode */
223 pip->Dragging = FALSE;
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,
234 default: /* Mouse just moved */
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.
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,
258 if (ie->ie_Qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
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)
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)
289 if (ie->ie_Qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
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)
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)
322 if (tags[0] != TAG_DONE)
323 DoMethod((Object *)g, OM_UPDATE, tags, msg->gpi_GInfo, OPUF_INTERIM);
335 static void PIP_GMGoInactive(Class *cl, struct Gadget *g, struct gpGoInactive *msg)
337 struct PIPData *pip = INST_DATA (cl, g);
339 pip->Dragging = FALSE;
340 g->Flags &= ~GFLG_SELECTED;
345 static void PIP_GMLayout(Class *cl, struct Gadget *g, struct gpLayout *msg)
347 struct PIPData *pip = INST_DATA(cl, g);
349 GetGadgetBox(msg->gpl_GInfo, g, &pip->GBox);
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,
366 static ULONG PIP_OMNew(Class *cl, struct Gadget *g, struct opSet *msg)
370 if (result = DoSuperMethodA(cl, (Object *)g, (Msg)msg))
372 struct PIPData *pip = (struct PIPData *) INST_DATA(cl, (Object *)result);
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;
386 static ULONG PIP_OMSet(Class *cl, struct Gadget *g, struct opUpdate *msg)
388 struct PIPData *pip = (struct PIPData *) INST_DATA(cl, g);
390 *tstate = msg->opu_AttrList;
391 ULONG result = FALSE;
392 BOOL do_super_method = FALSE,
396 while (ti = NextTagItem(&tstate))
402 if (pip->Scr = (struct Screen *)ti->ti_Data)
404 pip->Width = pip->Scr->Width;
405 pip->Height = pip->Scr->Height;
408 pip->Width = pip->Height = 0;
416 if (pip->BitMap = (struct BitMap *)ti->ti_Data)
418 pip->Width = pip->BitMap->BytesPerRow << 3;
419 pip->Height = pip->BitMap->Rows;
422 pip->Width = pip->Height = 0;
432 if (pip->OffX != ti->ti_Data)
434 WORD newx = (WORD)ti->ti_Data;
436 /* Restrict offset to valid limits */
437 if (newx + pip->GBox.Width > pip->Width)
438 newx = pip->Width - pip->GBox.Width;
442 if (newx != pip->OffX)
452 if (pip->OffY != ti->ti_Data)
454 WORD newy = (WORD)ti->ti_Data;
456 /* Restrict offset to valid limits */
457 if (newy + pip->GBox.Height > pip->Height)
458 newy = pip->Height - pip->GBox.Height;
462 if (newy != pip->OffY)
485 if (pip->OffY < pip->Height - pip->GBox.Height)
487 if (pip->OffY + pip->GBox.Height < pip->Height - 8)
490 pip->OffY = pip->Height - pip->GBox.Height;
511 if (pip->OffX < pip->Width - pip->GBox.Width)
513 if (pip->OffX + pip->GBox.Width < pip->Width - 8)
516 pip->OffX = pip->Width - pip->GBox.Width;
524 /* This little optimization avoids forwarding the
525 * OM_SET method to our superclass when there are
528 do_super_method = TRUE;
533 /* Forward method to our superclass dispatcher, only when needed */
536 result = (DoSuperMethodA(cl, (Object *)g, (Msg) msg));
539 /* Update gadget imagery, only when needed */
541 if (render && msg->opu_GInfo)
545 if (rp = ObtainGIRPort(msg->opu_GInfo))
547 DoMethod((Object *)g,
548 GM_RENDER, msg->opu_GInfo, rp, GREDRAW_UPDATE);
555 /* Notify our targets about all changed attributes, only when needed */
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,
570 static ULONG PIP_OMGet(Class *cl, struct Gadget *g, struct opGet *msg)
572 struct PIPData *pip = INST_DATA (cl, g);
574 switch (msg->opg_AttrID)
577 *(msg->opg_Storage) = (ULONG) pip->Scr;
581 *(msg->opg_Storage) = (ULONG) pip->BitMap;
585 *(msg->opg_Storage) = (ULONG) pip->OffX;
589 *(msg->opg_Storage) = (ULONG) pip->OffY;
593 *(msg->opg_Storage) = (ULONG) pip->Width;
597 *(msg->opg_Storage) = (ULONG) pip->Height;
601 return DoSuperMethodA(cl, (Object *)g, (Msg) msg);
607 static ULONG PIP_PIPMRefresh(Class *cl, struct Gadget *g, struct pippRefresh *msg)
611 if (msg->pipp_GInfo && (rp = ObtainGIRPort(msg->pipp_GInfo)))
613 /* Call our GM_RENDER method */
615 DoMethod((Object *)g, GM_RENDER, msg->pipp_GInfo, rp, GREDRAW_UPDATE);
625 static void RestrictXY(struct PIPData *pip)
627 /* Restrict XOff and YOff inside bitmap limits */
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;
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;
640 static void GetGadgetBox(struct GadgetInfo *ginfo, struct Gadget *g, struct IBox *rect)
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).
647 rect->Left = g->LeftEdge;
648 if (g->Flags & GFLG_RELRIGHT) rect->Left += ginfo->gi_Domain.Width - 1;
650 rect->Top = g->TopEdge;
651 if (g->Flags & GFLG_RELBOTTOM) rect->Top += ginfo->gi_Domain.Height - 1;
653 rect->Width = g->Width;
654 if (g->Flags & GFLG_RELWIDTH) rect->Width += ginfo->gi_Domain.Width;
656 rect->Height = g->Height;
657 if (g->Flags & GFLG_RELHEIGHT) rect->Height += ginfo->gi_Domain.Height;
662 Class *MakePIPClass(void)
666 if (PIPClass = MakeClass(NULL, GADGETCLASS, NULL, sizeof (struct PIPData), 0))
667 PIPClass->cl_Dispatcher.h_Entry = (ULONG (*)()) PIPDispatcher;
674 void FreePIPClass(Class *PIPClass)