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 struct TagItem
*tag
, *tstate
;
227 struct PropGData
*data
;
228 struct BBox old_knobbox
;
231 BOOL set_flag
= FALSE
;
232 BOOL was_disabled
= FALSE
;
233 BOOL full_redraw
= FALSE
;
234 BOOL old_knobbox_ok
= FALSE
;
237 data
= INST_DATA(cl
, g
);
238 tstate
= msg
->ops_AttrList
;
240 was_disabled
= (g
->Flags
& GFLG_DISABLED
) ? TRUE
: FALSE
;
242 method
.MethodID
= OM_SET
;
243 method
.ops_AttrList
= msg
->ops_AttrList
;
244 method
.ops_GInfo
= msg
->ops_GInfo
;
245 retval
= DoSuperMethodA(cl
, (Object
*)g
, (Msg
)&method
);
247 if ( ((g
->Flags
& GFLG_DISABLED
) ? TRUE
: FALSE
) != was_disabled
) full_redraw
= TRUE
;
251 CalcBBox (msg
->ops_GInfo
->gi_Window
, msg
->ops_GInfo
->gi_Requester
, g
, &old_knobbox
);
252 old_knobbox_ok
= CalcKnobSize(g
, &old_knobbox
);
255 newtop
= data
->top
; /* !! */
257 /* Set to 1 to signal visual changes */
258 while ((tag
= NextTagItem(&tstate
)) != NULL
)
263 newtop
= (UWORD
)tag
->ti_Data
;
264 /* This will be poked into data->top later below, because the
265 value might have to be adjusted. It depends on PGA_Total
266 and PGA_Visible which might be also set later in the taglist */
272 data
->visible
= tag
->ti_Data
;
278 data
->total
= tag
->ti_Data
;
283 /* When one of the four next ones is set, what should then happen
284 with PGA_Top, Total and Visible ?
285 For example decreasing Body could mean both a decrease of
286 Visible or an increase in Total. Which of them it is,
287 we cannot know. So we say that the PGA_xxxPot/Body
288 attrs should not be used along with Top, Total and Visible. */
291 data
->propinfo
.HorizPot
= (UWORD
)tag
->ti_Data
;
296 data
->propinfo
.HorizBody
= (UWORD
)tag
->ti_Data
;
301 data
->propinfo
.VertPot
= (UWORD
)tag
->ti_Data
;
306 data
->propinfo
.VertBody
= (UWORD
)tag
->ti_Data
;
311 data
->propinfo
.Flags
&= ~(FREEHORIZ
|FREEVERT
);
312 data
->propinfo
.Flags
|= tag
->ti_Data
;
316 SETFLAG(data
->propinfo
.Flags
, tag
->ti_Data
, PROPNEWLOOK
);
320 SETFLAG(data
->propinfo
.Flags
, tag
->ti_Data
, PROPBORDERLESS
);
330 /* Convert GADGHBOX to GADGHCOMP */
331 if (tag
->ti_Data
& GFLG_GADGHBOX
)
334 tag
->ti_Data
&= ~GFLG_GADGHBOX
;
336 tag
->ti_Data
|= GFLG_GADGHCOMP
;
344 } /* switch (tag->ti_Tag) */
346 } /* while ((tag = NextTagItem(&tstate)) != NULL) */
348 /* Top, Visible or Total set? */
352 UWORD
*bodyptr
, *potptr
;
354 /* fix top value if necessary */
356 if (data
->total
> data
->visible
)
358 if (newtop
> (data
->total
- data
->visible
))
359 newtop
= data
->total
- data
->visible
;
366 if (data
->top
!= newtop
)
369 if (data
->flags
& PRIVFLAG_NICENOTIFY
)
371 NotifyTop(cl
, g
, msg
->ops_GInfo
, TRUE
);
375 if (data
->propinfo
.Flags
& FREEVERT
)
377 bodyptr
= &(data
->propinfo
.VertBody
);
378 potptr
= &(data
->propinfo
.VertPot
);
382 bodyptr
= &(data
->propinfo
.HorizBody
);
383 potptr
= &(data
->propinfo
.HorizPot
);
398 /* The two last tests below may be redundant */
399 if ((retval
|| full_redraw
) && (NULL
!= msg
->ops_GInfo
) &&
400 ((OCLASS(g
) == cl
) || (data
->flags
& PRIVFLAG_NICERENDER
)))
404 rp
= ObtainGIRPort(msg
->ops_GInfo
);
407 struct gpRender method
;
409 data
->old_knobbox
= old_knobbox_ok
? &old_knobbox
: 0;
411 method
.MethodID
= GM_RENDER
;
412 method
.gpr_GInfo
= msg
->ops_GInfo
;
413 method
.gpr_RPort
= rp
;
414 method
.gpr_Redraw
= full_redraw
? GREDRAW_REDRAW
: GREDRAW_UPDATE
;
416 DoMethodA((Object
*)g
, (Msg
)&method
);
420 if (!(data
->flags
& PRIVFLAG_NICERENDER
))
422 /* retval of 1 indicates that user needs to rerender gadget
423 manually, while 0 means he does not need to do so */
431 /****************************************************************************************/
433 IPTR
PropGClass__OM_NEW(Class
*cl
, Object
*o
, struct opSet
*msg
)
435 struct Gadget
*g
= (struct Gadget
*)DoSuperMethodA(cl
, o
, (Msg
)msg
);
437 D(bug("PropGClass: new %p\n", o
));
441 struct PropGData
*data
= INST_DATA(cl
, g
);
443 /* A hack to make the functions in propgadgets.c work
445 g
->SpecialInfo
= &(data
->propinfo
);
447 /* Set some default values in the propinfo structure */
449 The instance object is cleared memory!
450 memset(&(data->propinfo), 0, sizeof (struct PropInfo));
452 data
->propinfo
.Flags
= PROPNEWLOOK
|AUTOKNOB
|FREEVERT
;
453 data
->propinfo
.VertPot
= 0;
454 data
->propinfo
.VertBody
= MAXBODY
;
455 data
->propinfo
.HorizPot
= 0;
456 data
->propinfo
.HorizBody
= MAXBODY
;
457 data
->DisplayHook
= GetTagData(PGA_DisplayHook
, 0, msg
->ops_AttrList
);
462 if (GetTagData(PGA_NotifyBehaviour
, PG_BEHAVIOUR_COMPATIBLE
, msg
->ops_AttrList
) ==
465 data
->flags
|= PRIVFLAG_NICENOTIFY
;
468 if (GetTagData(PGA_RenderBehaviour
, PG_BEHAVIOUR_COMPATIBLE
, msg
->ops_AttrList
) ==
471 data
->flags
|= PRIVFLAG_NICERENDER
;
474 SetGadgetType(g
, GTYP_CUSTOMGADGET
);
476 /* Handle our special tags - overrides defaults */
477 PropGClass__OM_SET(cl
, g
, msg
);
479 } /* if (object created) */
484 /****************************************************************************************/
486 IPTR
PropGClass__OM_GET(Class
*cl
, struct Gadget
*g
,struct opGet
*msg
)
488 struct PropGData
*data
;
491 data
= INST_DATA(cl
, g
);
493 switch (msg
->opg_AttrID
)
496 *(msg
->opg_Storage
) = data
->top
;
500 *(msg
->opg_Storage
) = data
->total
;
503 case PGA_DisplayHook
:
504 *(msg
->opg_Storage
) = data
->DisplayHook
;
508 *(msg
->opg_Storage
) = data
->visible
;
512 *(msg
->opg_Storage
) = data
->propinfo
.HorizPot
;
516 *(msg
->opg_Storage
) = data
->propinfo
.HorizBody
;
520 *(msg
->opg_Storage
) = data
->propinfo
.VertPot
;
524 *(msg
->opg_Storage
) = data
->propinfo
.VertBody
;
528 *(msg
->opg_Storage
) = data
->propinfo
.Flags
& (FREEHORIZ
|FREEVERT
);
532 retval
= DoSuperMethodA(cl
, (Object
*)g
, (Msg
)msg
);
538 /****************************************************************************************/
540 IPTR
PropGClass__GM_GOACTIVE(Class
*cl
, struct Gadget
*g
, struct gpInput
*msg
)
542 IPTR retval
= GMR_NOREUSE
;
544 /* Was GOACTIVE caused by an input event ? */
547 struct PropGData
*data
= INST_DATA(cl
, g
);
549 /* Fake a standard intuition prop gadget.
550 Of course this is a hack. */
551 SetGadgetType(g
, GTYP_PROPGADGET
);
553 /* Handle SelectDown event */
557 msg
->gpi_GInfo
->gi_Window
,
558 msg
->gpi_GInfo
->gi_Requester
,
563 SetGadgetType(g
, GTYP_CUSTOMGADGET
);
565 if (!(data
->propinfo
.Flags
& KNOBHIT
))
567 /* If the knob was not hit, swallow hit-event.
568 (Gadget has allready been updated) */
570 /* Update PGA_Top. Final update. */
571 UpdateTop(cl
, g
, msg
->gpi_GInfo
, TRUE
);
573 *(msg
->gpi_Termination
) = data
->top
;
574 retval
= GMR_NOREUSE
|GMR_VERIFY
;
578 /* We must remember mousepos for use in GM_HANDLEINPUT */
579 data
->last_x
= msg
->gpi_Mouse
.X
;
580 data
->last_y
= msg
->gpi_Mouse
.Y
;
582 retval
= GMR_MEACTIVE
; /* Stay active */
584 /* enable buffering to speed up refresh */
585 ((struct IntWindow
*)(msg
->gpi_GInfo
->gi_Window
))->specialflags
|= SPFLAG_WANTBUFFER
;
588 ((struct IIHData
*)GetPrivIBase(IntuitionBase
)->InputHandler
->is_Data
)->PropTask
=
589 NewCreateTask(TASKTAG_CODETYPE
, CODETYPE_PPC
, TASKTAG_PC
, (ULONG
)PropRefreshTask
,
591 TASKTAG_PPC_ARG1
,(ULONG
)IntuitionBase
,
592 TASKTAG_PPC_ARG2
,(ULONG
)g
,
593 TASKTAG_PPC_ARG3
,(ULONG
)msg
->gpi_GInfo
->gi_Window
,
594 TASKTAG_PPC_ARG4
,(ULONG
)msg
->gpi_GInfo
->gi_Requester
,
598 } /* if not knob was hit */
600 } /* if gadget was activated by an input event */
605 /****************************************************************************************/
607 IPTR
PropGClass__GM_HANDLEINPUT(Class
*cl
, struct Gadget
*g
, struct gpInput
*msg
)
609 struct InputEvent
*ie
;
610 struct PropGData
*data
= INST_DATA(cl
, g
);
612 /* Default: stay active */
613 IPTR retval
= GMR_MEACTIVE
;
615 ie
= msg
->gpi_IEvent
;
616 if (ie
->ie_Class
== IECLASS_RAWMOUSE
)
620 case IECODE_NOBUTTON
:
621 if ((msg
->gpi_Mouse
.X
!= data
->last_x
) || (msg
->gpi_Mouse
.Y
!= data
->last_y
))
623 /* Fake a standard intuition prop gadget */
624 SetGadgetType(g
, GTYP_PROPGADGET
);
628 msg
->gpi_GInfo
->gi_Window
,
629 msg
->gpi_GInfo
->gi_Requester
,
630 msg
->gpi_Mouse
.X
, /* - data->last_x, */
631 msg
->gpi_Mouse
.Y
, /* - data->last_y, */
635 SetGadgetType(g
, GTYP_CUSTOMGADGET
);
637 data
->last_x
= msg
->gpi_Mouse
.X
;
638 data
->last_y
= msg
->gpi_Mouse
.Y
;
640 D(bug("PropGClass: Calling UpdateTop\n"));
642 /* Update PGA_Top. Interim update. */
643 UpdateTop(cl
, g
, msg
->gpi_GInfo
, FALSE
);
649 /* User has released the knob. Refresh knob */
652 if (((struct IIHData
*)GetPrivIBase(IntuitionBase
)->InputHandler
->is_Data
)->PropTask
)
654 ULONG oldSignals
= SetSignal(0,SIGF_INTUITION
);
655 struct Task
*task
= ((struct IIHData
*)GetPrivIBase(IntuitionBase
)->InputHandler
->is_Data
)->PropTask
;
659 ((struct IIHData
*)GetPrivIBase(IntuitionBase
)->InputHandler
->is_Data
)->PropTask
= 0;
662 if (task
) //small protection
664 Signal(task
,PSIG_DIE
);
666 signals
= Wait(SIGF_INTUITION
);
669 SetSignal(oldSignals
| (signals
& ~SIGF_INTUITION
),0xFFFFFFFF);
673 SetGadgetType(g
, GTYP_PROPGADGET
);
678 msg
->gpi_GInfo
->gi_Window
,
679 msg
->gpi_GInfo
->gi_Requester
,
683 SetGadgetType(g
, GTYP_CUSTOMGADGET
);
685 /* Update PGA_Top. Final update. */
686 UpdateTop(cl
, g
, msg
->gpi_GInfo
, TRUE
);
688 *(msg
->gpi_Termination
) = data
->top
;
689 retval
= GMR_NOREUSE
|GMR_VERIFY
;
692 } /* switch (ie->ie_Code) */
694 } /* if (ie->ie_Class == IECLASS_RAWMOUSE) */
699 /****************************************************************************************/
701 IPTR
PropGClass__GM_RENDER(Class
*cl
, struct Gadget
*g
, struct gpRender
*msg
)
703 struct PropGData
*data
= INST_DATA(cl
, g
);
705 DEBUG_PROP(dprintf("render_propgclass:\n"));
709 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
)
711 Signal(((struct IIHData
*)GetPrivIBase(IntuitionBase
)->InputHandler
->is_Data
)->PropTask
,PSIG_REFRESHALL
);
717 /* Fake a standard intuition prop gadget */
718 SetGadgetType(g
, GTYP_PROPGADGET
);
720 if ((msg
->gpr_Redraw
== GREDRAW_UPDATE
) && (data
->old_knobbox
))
722 struct BBox new_knobbox
;
724 CalcBBox (msg
->gpr_GInfo
->gi_Window
, msg
->gpr_GInfo
->gi_Requester
, g
, &new_knobbox
);
726 DEBUG_PROP(dprintf("render_propgclass: BBox Left %ld Top %ld Width %ld Height %ld\n",
730 new_knobbox
.Height
));
732 if (CalcKnobSize(g
, &new_knobbox
))
734 #if PROP_RENDER_OPTIMIZATION
735 RefreshPropGadgetKnob (g
, data
->old_knobbox
, &new_knobbox
, msg
->gpr_GInfo
->gi_Window
, msg
->gpr_GInfo
->gi_Requester
, IntuitionBase
);
739 CalcBBox (msg
->gpr_GInfo
->gi_Window
, msg
->gpr_GInfo
->gi_Requester
, g
, &gbox
);
740 RefreshPropGadgetKnob (g
, &gbox
, &new_knobbox
, msg
->gpr_GInfo
->gi_Window
, msg
->gpr_GInfo
->gi_Requester
, IntuitionBase
);
744 data
->old_knobbox
= 0;
748 /* Redraw the whole gadget */
752 msg
->gpr_GInfo
->gi_Window
,
753 msg
->gpr_GInfo
->gi_Requester
,
758 SetGadgetType(g
, GTYP_CUSTOMGADGET
);
764 /****************************************************************************************/
766 IPTR
PropGClass__GM_GOINACTIVE(Class
*cl
, struct Gadget
*g
, struct gpGoInactive
*msg
)
768 /* Gadget cancelled by intuition ? */
770 if (msg
->gpgi_Abort
== 1)
774 if (((struct IIHData
*)GetPrivIBase(IntuitionBase
)->InputHandler
->is_Data
)->PropTask
)
776 ULONG oldSignals
= SetSignal(0,SIGF_INTUITION
);
777 struct Task
*task
= ((struct IIHData
*)GetPrivIBase(IntuitionBase
)->InputHandler
->is_Data
)->PropTask
;
781 ((struct IIHData
*)GetPrivIBase(IntuitionBase
)->InputHandler
->is_Data
)->PropTask
= 0;
784 if (task
) //small protection
786 Signal(task
,PSIG_DIE
);
788 signals
= Wait(SIGF_INTUITION
);
791 SetSignal(oldSignals
| (signals
& ~SIGF_INTUITION
),0xFFFFFFFF);
795 SetGadgetType(g
, GTYP_PROPGADGET
);
800 msg
->gpgi_GInfo
->gi_Window
,
801 msg
->gpgi_GInfo
->gi_Requester
,
805 SetGadgetType(g
, GTYP_CUSTOMGADGET
);
807 ((struct IntWindow
*)(msg
->gpgi_GInfo
->gi_Window
))->specialflags
&= ~SPFLAG_WANTBUFFER
;
809 /* Update PGA_Top. Final update */
810 UpdateTop(cl
, g
, msg
->gpgi_GInfo
, TRUE
);
815 /****************************************************************************************/