2 Copyright 1995-2005, The AROS Development Team. All rights reserved.
3 Copyright 2001-2003, The MorphOS Development Team. All Rights Reserved.
7 #include <exec/types.h>
9 #include <proto/intuition.h>
10 #include <proto/graphics.h>
11 #include <proto/utility.h>
12 #include <intuition/intuition.h>
13 #include <intuition/classes.h>
14 #include <intuition/classusr.h>
15 #include <intuition/gadgetclass.h>
16 #include <intuition/cghooks.h>
17 #include <graphics/clip.h>
18 #include <aros/asmcall.h>
22 #include <aros/debug.h>
24 #include "intuition_intern.h"
25 #include "inputhandler.h"
26 #include "propgadgets.h"
28 #define DEBUG_PROP(x) ;
31 void PropRefreshTask(struct IntuitionBase
*IntuitionBase
,struct Gadget
*gadget
,struct Window
*window
,struct Requester
*requester
)
37 signals
= Wait(PSIG_REFRESHALL
|PSIG_DIE
);
39 //go_inactive refreshes the gadget on it's own, so we do not need to care about queued refreshes!
40 if (signals
& PSIG_DIE
)
43 ((struct IIHData
*)GetPrivIBase(IntuitionBase
)->InputHandler
->is_Data
)->PropTask
= 0;
45 Signal(((struct IIHData
*)GetPrivIBase(IntuitionBase
)->InputHandler
->is_Data
)->InputDeviceTask
,SIGF_INTUITION
);
49 if (signals
& PSIG_REFRESHALL
)
51 RefreshPropGadget (gadget
, window
, requester
, IntuitionBase
);
54 //no need to refresh the knob when whole gadget was refreshed already
55 //if ((signals & PSIG_REFRESHKNOB) && (!(signals & PSIG_REFRESHALL)))
63 /*****************************************************************************************
65 stegerg: Tested behaviour under AmigaOS:
68 renders itself during OM_SET(PGA_Top) : yes, method retval = 0
69 renders itself during OM_UPDATE(PGA_Top) : yes, method retval = 0
70 sends OM_NOTIFY during OM_SET(PGA_Top) : no
71 sends OM_NOTIFY during OM_UPDATE(PGA_Top): no
72 sends OM_NOTIFY when user drags knob : yes
74 -> only when user uses gadget, OM_NOTIFY is sent
76 propgclass subclass object:
77 renders itself during OM_SET(PGA_Top) : no, method retval = 1 [*]
78 renders itself during OM_UPDATE(PGA_Top) : no, method retval = 1 [*]
79 sends OM_NOTIFY during OM_SET(PGA_Top) : no
80 sends OM_NOTIFY during OM_UPDATE(PGA_Top): no
81 sends OM_NOTIFY when user drags knob : yes
83 Here [*], another weird behaviour is, that the ~internal (??) PGA_Top value
84 stays at the old value, so you can click at the knob where you see it
85 (although because of the real PGA_Top it could in reality be in some
86 completely different place) and use it. Only when the gadget is re-rendered
87 (completely?), or you click somewhere in the prop box to cause a ~one page knob
88 move, the ~internal PGA_Top gets updated to the real value. Note, that
89 GetAttr(PGA_Top) will always be correct, so when I say ~internal PGA_Top,
90 this must be some internal variable only for inputhandling of gadget!!
91 BTW: With PGA_Total (and most likely also PGA_Visible) it's exactly the
92 same: imagine before the OM_SET/OM_UPDATE the prop knob was 10 % of the
93 size of the prop box. And after the OM_SET/OM_UPDATE(PGA_Total) the prop knob
94 would be 50 % of the size of the prop box -> again - before a rerendering of
95 the gadget happens, or a jump-one-page click by user - the gadget behaves
96 like it was still having the old PGA_Total value (-> knob size == 10 % of
99 AROS propgclass at the moment does not copy this behaviour!!!!
101 *****************************************************************************************/
104 #define IntuitionBase ((struct IntuitionBase *)(cl->cl_UserData))
106 /****************************************************************************************/
108 #define SetGadgetType(gad, type) ((struct Gadget *)gad)->GadgetType &= ~GTYP_GTYPEMASK; \
109 ((struct Gadget *)gad)->GadgetType |= type;
111 #define PRIVFLAG_NICERENDER 1
112 #define PRIVFLAG_NICENOTIFY 2
114 /****************************************************************************************/
116 static VOID
FindScrollerValues(UWORD total
, UWORD visible
, UWORD top
,
117 WORD overlap
, UWORD
*body
, UWORD
*pot
)
122 hidden
= total
- visible
;
129 *body
= (hidden
> 0) ?
130 (UWORD
)(((ULONG
)(visible
- overlap
) * MAXBODY
) / (total
- overlap
)) :
133 *pot
= (hidden
> 0) ? (UWORD
)(((ULONG
) top
* MAXPOT
) / hidden
) : 0;
138 /****************************************************************************************/
140 static UWORD
FindScrollerTop(UWORD total
, UWORD visible
, UWORD pot
)
145 hidden
= total
- visible
;
149 top
= (((ULONG
) hidden
* pot
) + (MAXPOT
/ 2)) / MAXPOT
;
154 /****************************************************************************************/
156 static VOID
NotifyTop(Class
*cl
, struct Gadget
*g
, struct GadgetInfo
*gi
, BOOL final
)
158 struct PropGData
*data
= INST_DATA(cl
, g
);
159 struct opUpdate notifymsg
;
160 struct TagItem notifyattrs
[3];
162 D(bug("PropGClass: NotifyTop(top=%d, final=%d)\n",
165 notifyattrs
[0].ti_Tag
= PGA_Top
;
166 notifyattrs
[0].ti_Data
= data
->top
;
167 notifyattrs
[1].ti_Tag
= GA_ID
;
168 notifyattrs
[1].ti_Data
= g
->GadgetID
;
169 notifyattrs
[2].ti_Tag
= TAG_END
;
171 notifymsg
.MethodID
= OM_NOTIFY
;
172 notifymsg
.opu_AttrList
= notifyattrs
;
173 notifymsg
.opu_GInfo
= gi
;
174 notifymsg
.opu_Flags
= (final
!= FALSE
) ? 0 : OPUF_INTERIM
;
176 DoSuperMethodA(cl
, (Object
*)g
, (Msg
)¬ifymsg
);
181 /****************************************************************************************/
183 static VOID
UpdateTop(Class
*cl
, struct Gadget
*g
, struct GadgetInfo
*gi
, BOOL final
)
185 /* Updates the PGA_Top attribute accordin to the Bofy/Pot vars.
186 ** Also triggers notifcation if PGA_Top has changed.
189 struct PropGData
*data
= (struct PropGData
*)INST_DATA(cl
, g
);
192 D(bug("PropGClass: UpdateTop()\n"));
194 pot
= (data
->propinfo
.Flags
& FREEVERT
) ? data
->propinfo
.VertPot
:
195 data
->propinfo
.HorizPot
;
197 top
= FindScrollerTop(data
->total
, data
->visible
, pot
);
199 D(bug("PropGClass: Found scroller top: %d, old %d\n", top
, data
->top
));
201 /* PGA_Top changed by user ? */
202 if ((top
!= data
->top
) || final
)
204 D(bug("PropGClass: top != data->top, calling NotifyTop\n"));
208 NotifyTop(cl
, g
, gi
, final
);
214 /****************************************************************************************/
216 #define SETFLAG(flagvar, boolvar, flag) \
222 /****************************************************************************************/
224 IPTR
PropGClass__OM_SET(Class
*cl
, struct Gadget
*g
, struct opSet
*msg
)
226 const struct TagItem
*tstate
;
228 struct PropGData
*data
;
229 struct BBox old_knobbox
;
232 BOOL set_flag
= FALSE
;
233 BOOL was_disabled
= FALSE
;
234 BOOL full_redraw
= FALSE
;
235 BOOL old_knobbox_ok
= FALSE
;
238 data
= INST_DATA(cl
, g
);
239 tstate
= msg
->ops_AttrList
;
241 was_disabled
= (g
->Flags
& GFLG_DISABLED
) ? TRUE
: FALSE
;
243 method
.MethodID
= OM_SET
;
244 method
.ops_AttrList
= msg
->ops_AttrList
;
245 method
.ops_GInfo
= msg
->ops_GInfo
;
246 retval
= DoSuperMethodA(cl
, (Object
*)g
, (Msg
)&method
);
248 if ( ((g
->Flags
& GFLG_DISABLED
) ? TRUE
: FALSE
) != was_disabled
) full_redraw
= TRUE
;
252 CalcBBox (msg
->ops_GInfo
->gi_Window
, msg
->ops_GInfo
->gi_Requester
, g
, &old_knobbox
);
253 old_knobbox_ok
= CalcKnobSize(g
, &old_knobbox
);
256 newtop
= data
->top
; /* !! */
258 /* Set to 1 to signal visual changes */
259 while ((tag
= NextTagItem(&tstate
)) != NULL
)
264 newtop
= (UWORD
)tag
->ti_Data
;
265 /* This will be poked into data->top later below, because the
266 value might have to be adjusted. It depends on PGA_Total
267 and PGA_Visible which might be also set later in the taglist */
273 data
->visible
= tag
->ti_Data
;
279 data
->total
= tag
->ti_Data
;
284 /* When one of the four next ones is set, what should then happen
285 with PGA_Top, Total and Visible ?
286 For example decreasing Body could mean both a decrease of
287 Visible or an increase in Total. Which of them it is,
288 we cannot know. So we say that the PGA_xxxPot/Body
289 attrs should not be used along with Top, Total and Visible. */
292 data
->propinfo
.HorizPot
= (UWORD
)tag
->ti_Data
;
297 data
->propinfo
.HorizBody
= (UWORD
)tag
->ti_Data
;
302 data
->propinfo
.VertPot
= (UWORD
)tag
->ti_Data
;
307 data
->propinfo
.VertBody
= (UWORD
)tag
->ti_Data
;
312 data
->propinfo
.Flags
&= ~(FREEHORIZ
|FREEVERT
);
313 data
->propinfo
.Flags
|= tag
->ti_Data
;
317 SETFLAG(data
->propinfo
.Flags
, tag
->ti_Data
, PROPNEWLOOK
);
321 SETFLAG(data
->propinfo
.Flags
, tag
->ti_Data
, PROPBORDERLESS
);
331 /* Convert GADGHBOX to GADGHCOMP */
332 if (tag
->ti_Data
& GFLG_GADGHBOX
)
335 tag
->ti_Data
&= ~GFLG_GADGHBOX
;
337 tag
->ti_Data
|= GFLG_GADGHCOMP
;
345 } /* switch (tag->ti_Tag) */
347 } /* while ((tag = NextTagItem(&tstate)) != NULL) */
349 /* Top, Visible or Total set? */
353 UWORD
*bodyptr
, *potptr
;
355 /* fix top value if necessary */
357 if (data
->total
> data
->visible
)
359 if (newtop
> (data
->total
- data
->visible
))
360 newtop
= data
->total
- data
->visible
;
367 if (data
->top
!= newtop
)
370 if (data
->flags
& PRIVFLAG_NICENOTIFY
)
372 NotifyTop(cl
, g
, msg
->ops_GInfo
, TRUE
);
376 if (data
->propinfo
.Flags
& FREEVERT
)
378 bodyptr
= &(data
->propinfo
.VertBody
);
379 potptr
= &(data
->propinfo
.VertPot
);
383 bodyptr
= &(data
->propinfo
.HorizBody
);
384 potptr
= &(data
->propinfo
.HorizPot
);
399 /* The two last tests below may be redundant */
400 if ((retval
|| full_redraw
) && (NULL
!= msg
->ops_GInfo
) &&
401 ((OCLASS(g
) == cl
) || (data
->flags
& PRIVFLAG_NICERENDER
)))
405 rp
= ObtainGIRPort(msg
->ops_GInfo
);
408 struct gpRender method
;
410 data
->old_knobbox
= old_knobbox_ok
? &old_knobbox
: 0;
412 method
.MethodID
= GM_RENDER
;
413 method
.gpr_GInfo
= msg
->ops_GInfo
;
414 method
.gpr_RPort
= rp
;
415 method
.gpr_Redraw
= full_redraw
? GREDRAW_REDRAW
: GREDRAW_UPDATE
;
417 DoMethodA((Object
*)g
, (Msg
)&method
);
421 if (!(data
->flags
& PRIVFLAG_NICERENDER
))
423 /* retval of 1 indicates that user needs to rerender gadget
424 manually, while 0 means he does not need to do so */
432 /****************************************************************************************/
434 IPTR
PropGClass__OM_NEW(Class
*cl
, Object
*o
, struct opSet
*msg
)
436 struct Gadget
*g
= (struct Gadget
*)DoSuperMethodA(cl
, o
, (Msg
)msg
);
438 D(bug("PropGClass: new %p\n", o
));
442 struct PropGData
*data
= INST_DATA(cl
, g
);
444 /* A hack to make the functions in propgadgets.c work
446 g
->SpecialInfo
= &(data
->propinfo
);
448 /* Set some default values in the propinfo structure */
450 The instance object is cleared memory!
451 memset(&(data->propinfo), 0, sizeof (struct PropInfo));
453 data
->propinfo
.Flags
= PROPNEWLOOK
|AUTOKNOB
|FREEVERT
;
454 data
->propinfo
.VertPot
= 0;
455 data
->propinfo
.VertBody
= MAXBODY
;
456 data
->propinfo
.HorizPot
= 0;
457 data
->propinfo
.HorizBody
= MAXBODY
;
458 data
->DisplayHook
= GetTagData(PGA_DisplayHook
, 0, msg
->ops_AttrList
);
463 if (GetTagData(PGA_NotifyBehaviour
, PG_BEHAVIOUR_COMPATIBLE
, msg
->ops_AttrList
) ==
466 data
->flags
|= PRIVFLAG_NICENOTIFY
;
469 if (GetTagData(PGA_RenderBehaviour
, PG_BEHAVIOUR_COMPATIBLE
, msg
->ops_AttrList
) ==
472 data
->flags
|= PRIVFLAG_NICERENDER
;
475 SetGadgetType(g
, GTYP_CUSTOMGADGET
);
477 /* Handle our special tags - overrides defaults */
478 PropGClass__OM_SET(cl
, g
, msg
);
480 } /* if (object created) */
485 /****************************************************************************************/
487 IPTR
PropGClass__OM_GET(Class
*cl
, struct Gadget
*g
,struct opGet
*msg
)
489 struct PropGData
*data
;
492 data
= INST_DATA(cl
, g
);
494 switch (msg
->opg_AttrID
)
497 *(msg
->opg_Storage
) = data
->top
;
501 *(msg
->opg_Storage
) = data
->total
;
504 case PGA_DisplayHook
:
505 *(msg
->opg_Storage
) = (IPTR
)data
->DisplayHook
;
509 *(msg
->opg_Storage
) = data
->visible
;
513 *(msg
->opg_Storage
) = data
->propinfo
.HorizPot
;
517 *(msg
->opg_Storage
) = data
->propinfo
.HorizBody
;
521 *(msg
->opg_Storage
) = data
->propinfo
.VertPot
;
525 *(msg
->opg_Storage
) = data
->propinfo
.VertBody
;
529 *(msg
->opg_Storage
) = data
->propinfo
.Flags
& (FREEHORIZ
|FREEVERT
);
533 retval
= DoSuperMethodA(cl
, (Object
*)g
, (Msg
)msg
);
539 /****************************************************************************************/
541 IPTR
PropGClass__GM_GOACTIVE(Class
*cl
, struct Gadget
*g
, struct gpInput
*msg
)
543 IPTR retval
= GMR_NOREUSE
;
545 /* Was GOACTIVE caused by an input event ? */
548 struct PropGData
*data
= INST_DATA(cl
, g
);
550 /* Fake a standard intuition prop gadget.
551 Of course this is a hack. */
552 SetGadgetType(g
, GTYP_PROPGADGET
);
554 /* Handle SelectDown event */
558 msg
->gpi_GInfo
->gi_Window
,
559 msg
->gpi_GInfo
->gi_Requester
,
564 SetGadgetType(g
, GTYP_CUSTOMGADGET
);
566 if (!(data
->propinfo
.Flags
& KNOBHIT
))
568 /* If the knob was not hit, swallow hit-event.
569 (Gadget has allready been updated) */
571 /* Update PGA_Top. Final update. */
572 UpdateTop(cl
, g
, msg
->gpi_GInfo
, TRUE
);
574 *(msg
->gpi_Termination
) = data
->top
;
575 retval
= GMR_NOREUSE
|GMR_VERIFY
;
579 /* We must remember mousepos for use in GM_HANDLEINPUT */
580 data
->last_x
= msg
->gpi_Mouse
.X
;
581 data
->last_y
= msg
->gpi_Mouse
.Y
;
583 retval
= GMR_MEACTIVE
; /* Stay active */
585 /* enable buffering to speed up refresh */
586 ((struct IntWindow
*)(msg
->gpi_GInfo
->gi_Window
))->specialflags
|= SPFLAG_WANTBUFFER
;
589 ((struct IIHData
*)GetPrivIBase(IntuitionBase
)->InputHandler
->is_Data
)->PropTask
=
590 NewCreateTask(TASKTAG_CODETYPE
, CODETYPE_PPC
, TASKTAG_PC
, (ULONG
)PropRefreshTask
,
592 TASKTAG_PPC_ARG1
,(ULONG
)IntuitionBase
,
593 TASKTAG_PPC_ARG2
,(ULONG
)g
,
594 TASKTAG_PPC_ARG3
,(ULONG
)msg
->gpi_GInfo
->gi_Window
,
595 TASKTAG_PPC_ARG4
,(ULONG
)msg
->gpi_GInfo
->gi_Requester
,
599 } /* if not knob was hit */
601 } /* if gadget was activated by an input event */
606 /****************************************************************************************/
608 IPTR
PropGClass__GM_HANDLEINPUT(Class
*cl
, struct Gadget
*g
, struct gpInput
*msg
)
610 struct InputEvent
*ie
;
611 struct PropGData
*data
= INST_DATA(cl
, g
);
613 /* Default: stay active */
614 IPTR retval
= GMR_MEACTIVE
;
616 ie
= msg
->gpi_IEvent
;
617 if (ie
->ie_Class
== IECLASS_RAWMOUSE
)
621 case IECODE_NOBUTTON
:
622 if ((msg
->gpi_Mouse
.X
!= data
->last_x
) || (msg
->gpi_Mouse
.Y
!= data
->last_y
))
624 /* Fake a standard intuition prop gadget */
625 SetGadgetType(g
, GTYP_PROPGADGET
);
629 msg
->gpi_GInfo
->gi_Window
,
630 msg
->gpi_GInfo
->gi_Requester
,
631 msg
->gpi_Mouse
.X
, /* - data->last_x, */
632 msg
->gpi_Mouse
.Y
, /* - data->last_y, */
636 SetGadgetType(g
, GTYP_CUSTOMGADGET
);
638 data
->last_x
= msg
->gpi_Mouse
.X
;
639 data
->last_y
= msg
->gpi_Mouse
.Y
;
641 D(bug("PropGClass: Calling UpdateTop\n"));
643 /* Update PGA_Top. Interim update. */
644 UpdateTop(cl
, g
, msg
->gpi_GInfo
, FALSE
);
650 /* User has released the knob. Refresh knob */
653 if (((struct IIHData
*)GetPrivIBase(IntuitionBase
)->InputHandler
->is_Data
)->PropTask
)
655 ULONG oldSignals
= SetSignal(0,SIGF_INTUITION
);
656 struct Task
*task
= ((struct IIHData
*)GetPrivIBase(IntuitionBase
)->InputHandler
->is_Data
)->PropTask
;
660 ((struct IIHData
*)GetPrivIBase(IntuitionBase
)->InputHandler
->is_Data
)->PropTask
= 0;
663 if (task
) //small protection
665 Signal(task
,PSIG_DIE
);
667 signals
= Wait(SIGF_INTUITION
);
670 SetSignal(oldSignals
| (signals
& ~SIGF_INTUITION
),0xFFFFFFFF);
674 SetGadgetType(g
, GTYP_PROPGADGET
);
679 msg
->gpi_GInfo
->gi_Window
,
680 msg
->gpi_GInfo
->gi_Requester
,
684 SetGadgetType(g
, GTYP_CUSTOMGADGET
);
686 /* Update PGA_Top. Final update. */
687 UpdateTop(cl
, g
, msg
->gpi_GInfo
, TRUE
);
689 *(msg
->gpi_Termination
) = data
->top
;
690 retval
= GMR_NOREUSE
|GMR_VERIFY
;
693 } /* switch (ie->ie_Code) */
695 } /* if (ie->ie_Class == IECLASS_RAWMOUSE) */
700 /****************************************************************************************/
702 IPTR
PropGClass__GM_RENDER(Class
*cl
, struct Gadget
*g
, struct gpRender
*msg
)
704 struct PropGData
*data
= INST_DATA(cl
, g
);
706 DEBUG_PROP(dprintf("render_propgclass:\n"));
710 if ((g
== ((struct IIHData
*)GetPrivIBase(IntuitionBase
)->InputHandler
->is_Data
)->ActiveGadget
) && (FindTask(0) == ((struct IIHData
*)GetPrivIBase(IntuitionBase
)->InputHandler
->is_Data
)->InputDeviceTask
) && ((struct IIHData
*)GetPrivIBase(IntuitionBase
)->InputHandler
->is_Data
)->PropTask
)
712 Signal(((struct IIHData
*)GetPrivIBase(IntuitionBase
)->InputHandler
->is_Data
)->PropTask
,PSIG_REFRESHALL
);
718 /* Fake a standard intuition prop gadget */
719 SetGadgetType(g
, GTYP_PROPGADGET
);
721 if ((msg
->gpr_Redraw
== GREDRAW_UPDATE
) && (data
->old_knobbox
))
723 struct BBox new_knobbox
;
725 CalcBBox (msg
->gpr_GInfo
->gi_Window
, msg
->gpr_GInfo
->gi_Requester
, g
, &new_knobbox
);
727 DEBUG_PROP(dprintf("render_propgclass: BBox Left %ld Top %ld Width %ld Height %ld\n",
731 new_knobbox
.Height
));
733 if (CalcKnobSize(g
, &new_knobbox
))
735 #if PROP_RENDER_OPTIMIZATION
736 RefreshPropGadgetKnob (g
, data
->old_knobbox
, &new_knobbox
, msg
->gpr_GInfo
->gi_Window
, msg
->gpr_GInfo
->gi_Requester
, IntuitionBase
);
740 CalcBBox (msg
->gpr_GInfo
->gi_Window
, msg
->gpr_GInfo
->gi_Requester
, g
, &gbox
);
741 RefreshPropGadgetKnob (g
, &gbox
, &new_knobbox
, msg
->gpr_GInfo
->gi_Window
, msg
->gpr_GInfo
->gi_Requester
, IntuitionBase
);
745 data
->old_knobbox
= 0;
749 /* Redraw the whole gadget */
753 msg
->gpr_GInfo
->gi_Window
,
754 msg
->gpr_GInfo
->gi_Requester
,
759 SetGadgetType(g
, GTYP_CUSTOMGADGET
);
765 /****************************************************************************************/
767 IPTR
PropGClass__GM_GOINACTIVE(Class
*cl
, struct Gadget
*g
, struct gpGoInactive
*msg
)
769 /* Gadget cancelled by intuition ? */
771 if (msg
->gpgi_Abort
== 1)
775 if (((struct IIHData
*)GetPrivIBase(IntuitionBase
)->InputHandler
->is_Data
)->PropTask
)
777 ULONG oldSignals
= SetSignal(0,SIGF_INTUITION
);
778 struct Task
*task
= ((struct IIHData
*)GetPrivIBase(IntuitionBase
)->InputHandler
->is_Data
)->PropTask
;
782 ((struct IIHData
*)GetPrivIBase(IntuitionBase
)->InputHandler
->is_Data
)->PropTask
= 0;
785 if (task
) //small protection
787 Signal(task
,PSIG_DIE
);
789 signals
= Wait(SIGF_INTUITION
);
792 SetSignal(oldSignals
| (signals
& ~SIGF_INTUITION
),0xFFFFFFFF);
796 SetGadgetType(g
, GTYP_PROPGADGET
);
801 msg
->gpgi_GInfo
->gi_Window
,
802 msg
->gpgi_GInfo
->gi_Requester
,
806 SetGadgetType(g
, GTYP_CUSTOMGADGET
);
808 ((struct IntWindow
*)(msg
->gpgi_GInfo
->gi_Window
))->specialflags
&= ~SPFLAG_WANTBUFFER
;
810 /* Update PGA_Top. Final update */
811 UpdateTop(cl
, g
, msg
->gpgi_GInfo
, TRUE
);
816 /****************************************************************************************/