2 // pm.c - popupmenu.library
4 // popupmenu.library and the PopupMenu package is
5 // Copyright ©1996 - 2002 Henrik Isaksson
6 // All Rights Reserved.
17 // struct PM_Root *p = NULL;
21 // Interface to the shadow handling algortihms.
22 // This will add the current menu rect to the menu topology map, that
23 // keeps track of the depth arrengement of the menus. This way shadows
24 // can be mapped to the height differences of various parts of the menu
25 // structure so that the size of each shadow segment correlates to the
26 // distance between the background and the menu that cast the shadow.
27 // Each shadow rectangle computed, is added to a list of shadowed
28 // regions that will prevent shadows cast by submenus to overlap any of
29 // the shadows previoulsy drawn.
30 // When all this is done, the shadow is finally rendered by calls to
31 // PM_DrawShadow(), for each rectangular segment of the shadow cast by
35 void PM_HandleShadow(struct PM_Window
*a
)
40 int depth
=a
->MenuLevel
;
41 int l
=a
->Wnd
->LeftEdge
, t
=a
->Wnd
->TopEdge
;
45 if(!a
->Shadowmap
|| !a
->Topographic
)
46 return; // Shadows not used
48 if((delta
=PM_InitShadowList())) {
51 // The three following calls will create shadows for a lightsource
52 // positioned somewhere near the top left corner of the screen.
66 PM_MapShadow(a
->Topographic
, delta
,
68 depth
, PMSHADOW_HORIZ
|PMSHADOW_TOP
);
71 PM_MapShadow(a
->Topographic
, delta
,
73 depth
, PMSHADOW_VERT
|PMSHADOW_LEFT
);
76 PM_MapShadow(a
->Topographic
, delta
,
78 depth
, PMSHADOW_HORIZ
|PMSHADOW_VERT
);
81 // The list "delta" represents the additional shadows necessary to
82 // achieve the desired result.
85 PM_AddShadow(a
->Shadowmap
, delta
);
87 PM_SubMenuRect(a
->Shadowmap
, l
, t
, w
, h
);
89 PM_AddTopographicRegion(a
->Topographic
,
90 l
, t
, l
+w
, h
+t
, depth
+1);
92 worknode
= (PMSR
*)(delta
->mlh_Head
);
93 while((nextnode
= (PMSR
*)PM_NextNode(worknode
))) {
94 PM_DrawShadow(a
, worknode
->Left
-a
->Wnd
->LeftEdge
,
95 worknode
->Top
-a
->Wnd
->TopEdge
,
96 worknode
->Right
-a
->Wnd
->LeftEdge
-1,
97 worknode
->Bottom
-a
->Wnd
->TopEdge
-1);
101 PM_FreeShadowList(delta
);
106 void PM_RenderMenu(struct PM_Window
*a
, BOOL MenuDisable
, BOOL refresh
)
108 if(a
->te
.RPort
&& !refresh
) {
109 /* Off screen buffer present - draw into it instead */
110 /* (Unless it's a menu refresh) */
111 a
->RPort
= a
->te
.RPort
;
114 SetDrMd(a
->RPort
, JAM1
);
117 // Clear the background
119 PM_DrawBg(a
, 0, 0, a
->Width
-1, a
->Height
-1);
121 // Draw a frame around the menu
123 if(a
->p
->PullDown
&& a
->FirstTime
)
124 PM_DrawBox(a
, 0, 0, a
->Width
-1, a
->Height
-1, SHINE(a
->p
), SHADOW(a
->p
));
126 PM_DrawPrefBox(a
->p
, a
, 0, 0, a
->Width
-1, a
->Height
-1);
128 /*******************************************************
131 struct Hook *menurenderhook = NULL;
132 GetGUIAttrs(NULL, a->p->DrawInfo, GUIA_MenuRenderHook, &menurenderhook, TAG_DONE); // V50
134 struct MenuRenderMsg rendermsg;
135 rendermsg.mrm_MethodID = MR_MENUPANEL;
136 rendermsg.mrm_RastPort = a->RPort;
137 rendermsg.mrm_DrawInfo = a->p->DrawInfo;
138 rendermsg.mrm_Bounds = ;
139 rendermsg.mrm_State = 0;
140 rendermsg.mrm_Window = a->Wnd;
141 rendermsg.mrm_Flags = MRF_POPUP; // | MRF_TRANSPARENT
142 CallHook(menurenderhook, (Object *)&rendermsg);
146 *******************************************************/
151 SetFont(a
->RPort
, a
->p
->MenuFont
);
155 PM_NewDrawItem(a
, &a
->PM
, FALSE
, MenuDisable
);
157 // Dither the menu, if disabled and if Old Style is selected
159 if(MenuDisable
&& PM_Prefs
->pmp_SeparatorBar
)
160 PM_Ghost(a
, 0, 0, a
->Width
, a
->Height
, SHADOW(a
->p
));
162 /* Back to on-screen rendering */
163 a
->RPort
= a
->Wnd
->RPort
;
166 void SelectItem(struct PM_Window
*c
, BOOL mdis
)
168 if(!(c
->Selected
->Flags
& NPM_DISABLED
) && !mdis
) {
169 if(c
->Selected
->Flags
& NPM_CHECKIT
) {
170 if(c
->Selected
->Flags
& NPM_CHECKED
) {
171 if(!(c
->Selected
->Flags
& NPM_NOTOGGLE
)) {
172 if(c
->Selected
->Exclude
) {
173 PM_AlterState(c
->p
->PM
, c
->Selected
->Exclude
, PMACT_DESELECT
);
175 c
->Selected
->Flags
&=~NPM_CHECKED
;
176 c
->Selected
->Flags
|=NPM_ISSELECTED
;
177 if(c
->Selected
->AutoSetPtr
) *c
->Selected
->AutoSetPtr
=FALSE
;
178 if(c
->Selected
->Exclude
) {
179 PM_RenderMenu(c
, FALSE
, TRUE
);
181 PM_NewDrawItem(c
, c
->Selected
, TRUE
, FALSE
);
185 if(c
->Selected
->Exclude
) {
186 PM_AlterState(c
->p
->PM
, c
->Selected
->Exclude
, PMACT_SELECT
);
188 c
->Selected
->Flags
|=NPM_CHECKED
|NPM_ISSELECTED
;
189 if(c
->Selected
->AutoSetPtr
) *c
->Selected
->AutoSetPtr
=TRUE
;
190 if(c
->Selected
->Exclude
) {
191 PM_RenderMenu(c
, FALSE
, TRUE
);
193 PM_NewDrawItem(c
, c
->Selected
, TRUE
, FALSE
);
197 if(c
->Selected
->Flags
& NPM_ISSELECTED
) c
->Selected
->Flags
&=~NPM_ISSELECTED
;
198 else c
->Selected
->Flags
|=NPM_ISSELECTED
;
199 PM_NewDrawItem(c
, c
->Selected
, TRUE
, FALSE
);
202 c
->p
->ReturnCode
=c
->Selected
->UserData
;
203 c
->p
->ReturnID
=c
->Selected
->ID
;
210 // Called when a mouse button changes state.
212 BOOL
PM_MBHit(struct PM_Window
*c
, struct PM_InpMsg
*msg
, BOOL mdis
)
216 if(PM_Prefs
->pmp_Sticky
) {
217 if(((msg
->Code
&IECODE_LBUTTON
) || (msg
->Code
&IECODE_RBUTTON
))) {
218 if((msg
->Code
&IECODE_UP_PREFIX
) &&
219 ((msg
->Qual
& (IEQUALIFIER_LSHIFT
|IEQUALIFIER_RSHIFT
)) ||
220 (msg
->Qual
& (IEQUALIFIER_LEFTBUTTON
|IEQUALIFIER_RBUTTON
)))) {
221 if(c
->p
->DoMultiSel
) {
223 c
->p
->DoneMulti
=TRUE
;
228 if(!(msg
->Code
&IECODE_UP_PREFIX
)) {
233 if(!c
->p
->DoneMulti
) {
236 if(c
->Prev
) c
->Prev
->Running
=0L;
247 if(((msg
->Code
&IECODE_LBUTTON
) || (msg
->Code
&IECODE_RBUTTON
))) {
248 if((msg
->Code
&IECODE_UP_PREFIX
) &&
249 ((msg
->Qual
& (IEQUALIFIER_LSHIFT
|IEQUALIFIER_RSHIFT
)) ||
250 (msg
->Qual
& (IEQUALIFIER_LEFTBUTTON
|IEQUALIFIER_RBUTTON
)))) {
251 if(c
->p
->DoMultiSel
) {
253 c
->p
->DoneMulti
=TRUE
;
258 if(msg
->Code
&IECODE_UP_PREFIX
) {
259 if(!c
->p
->DoneMulti
) {
262 if(c
->Prev
) c
->Prev
->Running
=0L;
271 if(whattodo
& 1) { // Select an item
277 if(whattodo
==1) { // Close if multiselect not requested
278 if(c
->Prev
) c
->Prev
->Running
=0L;
282 if(whattodo
==0) return FALSE
;
287 struct PopupMenu
*PM_InsideItemBox(struct PM_Window
*c
, struct PopupMenu
*pm
, ULONG mx
, ULONG my
)
292 if(pm
->Sub
&& (pm
->Flags
&NPM_GROUP
)) {
293 tmppm
=PM_InsideItemBox(c
, pm
->Sub
, mx
, my
);
294 if(tmppm
) return tmppm
;
295 } else if(!(pm
->Flags
&NPM_NOSELECT
)) {
296 if(c
->p
->PullDown
&& c
->FirstTime
) {
297 if(my
>=c
->Wnd
->TopEdge
&&
298 my
<=c
->Wnd
->TopEdge
+pm
->Top
+pm
->Height
&&
299 mx
>=c
->Wnd
->LeftEdge
+pm
->Left
&&
300 mx
<=c
->Wnd
->LeftEdge
+pm
->Left
+pm
->Width
) {
304 if(my
>=c
->Wnd
->TopEdge
+pm
->Top
&&
305 my
<=c
->Wnd
->TopEdge
+pm
->Top
+pm
->Height
&&
306 mx
>=c
->Wnd
->LeftEdge
+pm
->Left
&&
307 mx
<=c
->Wnd
->LeftEdge
+pm
->Left
+pm
->Width
) {
321 // Redraws the previously selected item.
323 void PM_RedrawPrevSel(struct PM_Window
*c
)
326 PM_NewDrawItem(c
, c
->PrevSel
, FALSE
, FALSE
);
333 // Called on mouse moves
335 BOOL
PM_MMove(struct PM_Window
*c
, struct PM_InpMsg
*msg
,
336 ULONG wleft
, ULONG wtop
, ULONG mx
, ULONG my
, BOOL MenuDisable
)
338 if(c
->Running
&& my
>=c
->Wnd
->TopEdge
&& my
<=c
->Wnd
->TopEdge
+c
->Height
&& mx
>=c
->Wnd
->LeftEdge
&& mx
<=c
->Wnd
->LeftEdge
+c
->Width
) {
339 c
->PrevSel
=c
->Selected
;
341 c
->Selected
=PM_InsideItemBox(c
, &c
->PM
, mx
, my
);
343 if((c
->PrevSel
!=c
->Selected
)) {
346 if(!MenuDisable
) PM_RedrawPrevSel(c
);
350 if(!MenuDisable
) PM_NewDrawItem(c
, c
->Selected
, TRUE
, MenuDisable
);
355 if(!MenuDisable
) PM_NewDrawItem(c
, c
->Selected
, FALSE
, MenuDisable
);
364 // PM_OpenPopupWindow
366 // 1) Opens a window at the appropriate position
367 // 2) If the window has been opened before, it will be resized to fit
368 // changes in menu size
369 // 3) Renders the menu.
370 // 4) Performs transition effects (TE).
371 // 5) Renders shadows.
373 BOOL
PM_OpenPopupWindow(struct PM_Window
*a
)
375 ULONG shadows
= TRUE
;
378 if( IntuitionBase
->LibNode
.lib_Version
>= 50 ) {
379 GetGUIAttrs(NULL
, a
->p
->DrawInfo
, GUIA_MenuDropShadows
, &shadows
, TAG_DONE
); // V50 !!
386 a
->p
->ShadowHeight
=0;
391 // The first menu may be affected by some special parameters
392 // set through PM_OpenPopupMenu()
393 if(a
->p
->MenuWidth
>a
->Width
) a
->Width
=a
->p
->MenuWidth
;
394 if(a
->p
->MenuHeight
>a
->Height
) a
->Height
=a
->p
->MenuHeight
;
395 if(a
->p
->MenuRight
) a
->Wnd
->MouseX
=a
->p
->MenuRight
-a
->Width
;
396 if(a
->p
->MenuBottom
) a
->Wnd
->MouseY
=a
->p
->MenuBottom
-a
->Height
;
397 if(a
->p
->MenuCenter
) {
398 a
->Wnd
->MouseX
=(a
->p
->RootWnd
->WScreen
->Width
/2)-a
->Width
/2;
399 a
->Wnd
->MouseY
=(a
->p
->RootWnd
->WScreen
->Height
/2)-a
->Height
/2;
403 if(a
->FirstTime
&& a
->p
->PullDown
) {
404 // A pulldown menu will be horizontally laid out
406 PM_OpenWindow(a
, a
->MenuX
, a
->MenuY
, a
->Width
+1, a
->Height
+1, a
->p
->RootWnd
->WScreen
);
408 PM_ResizeWindow(a
, a
->MenuX
, a
->MenuY
, a
->Width
+1, a
->Height
+1);
416 if(mx
+a
->Width
> a
->p
->RootWnd
->WScreen
->Width
) {
417 // If we're too close to the right edge, open menus to the left hereafter.
418 a
->ReverseDirection
=TRUE
;
420 if(a
->AltXPos
-a
->Width
< 0) {
421 // If we're too close to the left edge, open menus to the right (default).
422 a
->ReverseDirection
=FALSE
;
425 if(!a
->ReverseDirection
) {
426 PM_OpenWindow(a
, mx
, my
, a
->Width
+(a
->p
->ShadowWidth
+a
->MenuLevel
*2)+1, a
->Height
+(a
->p
->ShadowHeight
+a
->MenuLevel
*2)+1, a
->p
->RootWnd
->WScreen
);
428 PM_OpenWindow(a
, a
->AltXPos
-a
->Width
, my
, a
->Width
+(a
->p
->ShadowWidth
+a
->MenuLevel
*2)+1, a
->Height
+(a
->p
->ShadowHeight
+a
->MenuLevel
*2)+1, a
->p
->RootWnd
->WScreen
);
431 PM_ResizeWindow(a
, SCREENMOUSEPOS(a
->p
), a
->Width
+1, a
->Height
+1);
436 PM_RenderMenu(a
, a
->MenuDisabled
, FALSE
);
439 // If there is a TE (transition effects) bitmap.
441 // Currently, only an "animation" effect is implemented.
448 h
= a
->Wnd
->Height
-1;
450 if(!(a
->p
->PullDown
&& a
->FirstTime
)) {
451 w
-=a
->p
->ShadowWidth
+a
->MenuLevel
*2;
452 h
-=a
->p
->ShadowHeight
+a
->MenuLevel
*2;
460 BltBitMap(a
->te
.BMap
, 0, 0, a
->Wnd
->WScreen
->RastPort
.BitMap
, a
->Wnd
->LeftEdge
, a
->Wnd
->TopEdge
, dw
*i
, dh
*i
, 0xc0, 0xff, 0L);
467 BltBitMap(a
->te
.BMap
, 0, 0, a
->Wnd
->WScreen
->RastPort
.BitMap
, a
->Wnd
->LeftEdge
, a
->Wnd
->TopEdge
, w
, h
, 0xc0, 0xff, 0L);
474 if(!(a
->p
->PullDown
&& a
->FirstTime
)) {
484 struct PM_Window
*PM_SetupSubWindow(struct PM_Window
*parent
, struct PM_Root
*p
, struct PopupMenu
*pm
)
486 struct PM_Window
*newwin
;
488 newwin
=PM_Mem_Alloc(sizeof(struct PM_Window
));
491 parent
->WasSelected
= parent
->Selected
;
492 newwin
->ReverseDirection
= parent
->ReverseDirection
;
493 newwin
->Topographic
= PM_CopyList(parent
->Topographic
);
494 newwin
->Shadowmap
= PM_CopyList(parent
->Shadowmap
);
496 if((parent
->Selected
->Flags
& NPM_DISABLED
) || parent
->MenuDisabled
)
497 newwin
->MenuDisabled
= TRUE
;
499 newwin
->ReverseDirection
= 0;
500 if(PM_Prefs
->pmp_Flags
&1) {
501 newwin
->Topographic
= PM_InitTopographicList();
502 newwin
->Shadowmap
= PM_InitShadowList();
503 if(newwin
->Topographic
)
504 PM_AddTopographicRegion(newwin
->Topographic
, 0, 0, 5000, 5000, 0);
509 newwin
->PM
.Flags
= NPM_NOSELECT
|NPM_GROUP
;
510 newwin
->PM
.Layout
= PML_Vertical
;
511 newwin
->PM
.Image
= NULL
;
513 newwin
->Running
= TRUE
;
514 newwin
->Prev
= parent
;
518 if(p
->PullDown
&& parent
->FirstTime
) {
519 newwin
->MenuX
= parent
->WasSelected
->Left
+ parent
->Wnd
->LeftEdge
- 1;
520 newwin
->MenuY
= parent
->Height
+ 1 + parent
->Wnd
->TopEdge
- 1;
521 newwin
->AltXPos
= parent
->WasSelected
->Left
+ parent
->Wnd
->LeftEdge
;
523 newwin
->MenuX
= (int)(parent
->WasSelected
->Width
- parent
->WasSelected
->Width
/ 3) + parent
->Wnd
->LeftEdge
+ parent
->WasSelected
->Left
;
525 if(newwin
->MenuX
& 1) // Prevents interference in dithered shadows
528 newwin
->MenuY
= YAPosSelBar(parent
, parent
->WasSelected
) - 5 + parent
->Wnd
->TopEdge
;
529 newwin
->AltXPos
= (int)(parent
->WasSelected
->Width
/ 3) + parent
->Wnd
->LeftEdge
+ parent
->WasSelected
->Left
;
532 newwin
->MenuLevel
= parent
->MenuLevel
+ 1;
533 if(newwin
->MenuLevel
>4) newwin
->MenuLevel
= 4;
535 if(parent
->SubMenuParent
->SubConstruct
) {
536 IPTR p
= (IPTR
)parent
;
537 newwin
->PM
.Sub
= (struct PopupMenu
*)CallHookA(parent
->SubMenuParent
->SubConstruct
, (Object
*)parent
->Selected
, &p
);
540 newwin
->AltXPos
= newwin
->MenuX
= p
->Scr
->MouseX
;
541 newwin
->MenuY
= p
->Scr
->MouseY
;
542 newwin
->MenuLevel
= 0;
543 newwin
->FirstTime
= TRUE
;
550 // Constructor hook cancelled the operation, we must tidy up a bit...
552 PM_FreeSubWindow(parent
, newwin
);
557 void PM_FreeSubWindow(struct PM_Window
*parent
, struct PM_Window
*window
)
561 if(parent
->SubMenuParent
->SubDestruct
) {
562 CallHookA(parent
->SubMenuParent
->SubDestruct
, (Object
*)parent
->SubMenuParent
, NULL
);
566 if(window
->Topographic
)
567 PM_FreeTopographicList(window
->Topographic
);
568 if(window
->Shadowmap
)
569 PM_FreeShadowList(window
->Shadowmap
);
571 PM_CloseWindow(window
);
575 parent
->SubMenuToOpen
= 0L;
578 void PM_SelectNext(struct PM_Window
*a
)
581 a
->Selected
=PM_FindNextSelectable(a
, a
->PM
.Sub
, &found
);
582 if(a
->Selected
==NULL
) a
->Selected
=PM_FindFirstSelectable(a
->PM
.Sub
);
585 void PM_SelectPrev(struct PM_Window
*a
)
588 a
->Selected
=PM_FindPrevSelectable(a
, a
->PM
.Sub
, &found
);
590 a
->Selected
=PM_FindLastSelectable(a
->PM
.Sub
);
593 APTR
PM_DoPopup(struct PM_Window
*a
)
595 if(PM_OpenPopupWindow(a
)) {
599 while(a
->Running
&& (a
->p
->TimeOut
<2)) {
600 struct PM_InpMsg
*msg
;
603 s
=Wait(1L << a
->p
->pmh
->port
->mp_SigBit
| 1L << a
->p
->tport
->mp_SigBit
);
605 if(s
& (1L << a
->p
->tport
->mp_SigBit
)) {
606 if(GetMsg(a
->p
->tport
)) {
607 a
->p
->treq
->tr_node
.io_Command
= TR_ADDREQUEST
;
608 a
->p
->treq
->tr_time
.tv_secs
=0;
609 a
->p
->treq
->tr_time
.tv_micro
=200000;
610 SendIO((struct IORequest
*)a
->p
->treq
);
617 if(s
& (1L << a
->p
->pmh
->port
->mp_SigBit
)) {
618 while((msg
=(struct PM_InpMsg
*)GetMsg(a
->p
->pmh
->port
))) {
620 case PM_MSG_RAWMOUSE
:
622 if(msg
->Code
!=IECODE_NOBUTTON
) {
623 PM_MBHit(a
, msg
, a
->MenuDisabled
);
626 if(!PM_MMove(a
, msg
, 0, 0, a
->Wnd
->WScreen
->MouseX
, a
->Wnd
->WScreen
->MouseY
, a
->MenuDisabled
)) {
627 if(PM_InsideWindows(a
->Wnd
->WScreen
->MouseX
, a
->Wnd
->WScreen
->MouseY
, a
)) {
629 if(a
->Prev
) a
->Prev
->Running
=TRUE
;
635 if(a
->p
->TimeOut
>0) a
->p
->TimeOut
--;
636 if(msg
->Kind
==PM_MSG_TIMER
|| a
->p
->Subtimer
==0) {
638 if(a
->Selected
&& (a
->p
->Subtimer
> PM_Prefs
->pmp_SubMenuDelay
) && !keymode
) {
639 if(a
->Selected
->Sub
|| a
->Selected
->SubConstruct
) {
640 a
->SubMenuToOpen
=a
->Selected
->Sub
;
641 a
->SubMenuParent
=a
->Selected
;
643 halt
=TRUE
; // stop before we get too many timer msgs
651 a
->PrevSel
=a
->Selected
;
654 if(a
->Selected
) PM_NewDrawItem(a
, a
->Selected
, TRUE
, FALSE
);
659 a
->PrevSel
=a
->Selected
;
662 if(a
->Selected
) PM_NewDrawItem(a
, a
->Selected
, TRUE
, FALSE
);
665 case PM_MSG_MULTISELECT
:
668 if(!a
->Selected
->Sub
&& !a
->Selected
->SubConstruct
) {
669 SelectItem(a
, FALSE
);
670 if(msg
->Kind
==PM_MSG_SELECT
) {
672 if(a
->Prev
) a
->Prev
->Running
=FALSE
;
678 if(a
->Selected
->Sub
|| a
->Selected
->SubConstruct
) {
679 a
->SubMenuToOpen
=a
->Selected
->Sub
;
680 a
->SubMenuParent
=a
->Selected
;
682 halt
=TRUE
; // stop before we get too many timer msgs
687 case PM_MSG_CLOSESUB
:
689 if(a
->Prev
) a
->Prev
->Running
=TRUE
;
693 case PM_MSG_TERMINATE
:
704 if(a
->SubMenuToOpen
) {
707 if((UBYTE
*)tsk
->tc_SPReg
<((UBYTE
*)tsk
->tc_SPLower
)+1024) {
710 } else if((a
->NextWindow
= PM_SetupSubWindow(a
, p
, a
->SubMenuToOpen
))) {
712 /* TODO: Need to check this code to avoid global p */
713 } else if((a
->NextWindow
= PM_SetupSubWindow(a
, a
->p
, a
->SubMenuToOpen
))) {
715 PM_DoPopup(a
->NextWindow
);
717 if(a
->Prev
) a
->Prev
->Running
= FALSE
;
720 if(!PM_MMove(a
, 0, 0, 0, a
->Wnd
->WScreen
->MouseX
, a
->Wnd
->WScreen
->MouseY
, a
->MenuDisabled
)) {
721 if(PM_InsideWindows(a
->Wnd
->WScreen
->MouseX
, a
->Wnd
->WScreen
->MouseY
, a
)) {
723 if(a
->Prev
) a
->Prev
->Running
= TRUE
;
728 PM_FreeSubWindow(a
, a
->NextWindow
);
729 a
->NextWindow
= NULL
;
731 a
->SubMenuToOpen
=NULL
;
741 // Popup a "hint" window, and close it at first mouse move
743 APTR
PM_DoHint(struct PM_Window
*a
)
745 if(PM_OpenPopupWindow(a
)) {
746 while(a
->Running
&& (a
->p
->TimeOut
<2)) {
747 struct PM_InpMsg
*msg
;
750 s
=Wait(1L << a
->p
->pmh
->port
->mp_SigBit
| 1L << a
->p
->tport
->mp_SigBit
);
752 if(s
& (1L << a
->p
->tport
->mp_SigBit
)) {
753 if(GetMsg(a
->p
->tport
)) {
754 a
->p
->treq
->tr_node
.io_Command
= TR_ADDREQUEST
;
755 a
->p
->treq
->tr_time
.tv_secs
=0;
756 a
->p
->treq
->tr_time
.tv_micro
=200000;
757 SendIO((struct IORequest
*)a
->p
->treq
);
762 if(s
& (1L << a
->p
->pmh
->port
->mp_SigBit
)) {
763 while((msg
=(struct PM_InpMsg
*)GetMsg(a
->p
->pmh
->port
))) {
766 if(a
->p
->TimeOut
>0) a
->p
->TimeOut
--;
781 APTR __saveds ASM
PM_OBSOLETEFilterIMsgA()
787 // Filter the IntuiMessage structure for command key sequences, and
788 // mouse events for intuition menu replacement mode.
790 APTR __saveds ASM
PM_FilterIMsgA(register __a0
struct Window
*w
GNUCREG(a0
),
791 register __a1
struct PopupMenu
*pm
GNUCREG(a1
),
792 register __a2
struct IntuiMessage
*im
GNUCREG(a2
),
793 register __a3
struct TagItem
*tags
GNUCREG(a3
))
795 struct TagItem
*tstate
;
797 struct Hook
*MenuHandler
=NULL
;
798 BOOL autpd
= FALSE
, rawkey
= FALSE
;
804 while((tag
=NextTagItem(&tstate
))) {
805 switch(tag
->ti_Tag
) {
807 MenuHandler
=(struct Hook
*)tag
->ti_Data
;
809 case PM_AutoPullDown
:
813 rawkey
= (BOOL
)tag
->ti_Data
;
818 if(im
->Class
==IDCMP_MOUSEBUTTONS
) {
820 if(im
->Code
==MENUDOWN
|| im
->Code
==MENUUP
) {
821 struct TagItem opmtags
[6];
823 opmtags
[0].ti_Tag
=PM_Menu
; opmtags
[0].ti_Data
=(IPTR
)pm
;
824 opmtags
[1].ti_Tag
=PM_Code
; opmtags
[1].ti_Data
=im
->Code
;
825 opmtags
[2].ti_Tag
=PM_PullDown
; opmtags
[2].ti_Data
=TRUE
;
827 opmtags
[3].ti_Tag
=PM_MenuHandler
; opmtags
[3].ti_Data
=(IPTR
)MenuHandler
;
829 opmtags
[3].ti_Tag
=PM_Dummy
; opmtags
[3].ti_Data
=0;
831 opmtags
[4].ti_Tag
=TAG_MORE
; opmtags
[4].ti_Data
=(IPTR
)tags
;
832 opmtags
[5].ti_Tag
=TAG_DONE
; opmtags
[5].ti_Data
=0;
834 return PM_OpenPopupMenuA(w
, opmtags
);
837 } else if(im
->Class
==IDCMP_VANILLAKEY
&& !rawkey
) {
838 if(im
->Qualifier
&IEQUALIFIER_RCOMMAND
) {
841 p
=PM_FindItemCommKey(pm
, im
->Code
);
843 if(!(p
->Flags
& NPM_DISABLED
)) {
844 if(p
->Flags
& NPM_CHECKIT
) {
845 if(p
->Flags
& NPM_CHECKED
) {
846 p
->Flags
&=~NPM_CHECKED
;
848 PM_AlterState(pm
, p
->Exclude
, PMACT_DESELECT
);
849 if(p
->AutoSetPtr
) *p
->AutoSetPtr
=FALSE
;
852 PM_AlterState(pm
, p
->Exclude
, PMACT_SELECT
);
853 p
->Flags
|=NPM_CHECKED
;
854 if(p
->AutoSetPtr
) *p
->AutoSetPtr
=TRUE
;
857 if(MenuHandler
) CallHookA(MenuHandler
, (Object
*)p
, NULL
);
862 } else if(im
->Class
==IDCMP_RAWKEY
&& rawkey
) {
863 if(im
->Qualifier
&IEQUALIFIER_RCOMMAND
) {
866 p
=PM_FindItemCommKey(pm
, im
->Code
);
868 if(!(p
->Flags
& NPM_DISABLED
)) {
869 if(p
->Flags
& NPM_CHECKIT
) {
870 if(p
->Flags
& NPM_CHECKED
) {
871 p
->Flags
&=~NPM_CHECKED
;
873 PM_AlterState(pm
, p
->Exclude
, PMACT_DESELECT
);
874 if(p
->AutoSetPtr
) *p
->AutoSetPtr
=FALSE
;
877 PM_AlterState(pm
, p
->Exclude
, PMACT_SELECT
);
878 p
->Flags
|=NPM_CHECKED
;
879 if(p
->AutoSetPtr
) *p
->AutoSetPtr
=TRUE
;
882 if(MenuHandler
) CallHookA(MenuHandler
, (Object
*)p
, NULL
);
892 // Call MenuHandler for each selected item
894 void PM_DoSelected(struct PM_Root
*p
, struct PopupMenu
*pm
)
896 struct PopupMenu
*z
=pm
;
899 if(z
->Flags
&NPM_ISSELECTED
) {
901 if(p
->DoMultiSel
) CallHookA(p
->MenuHandler
, (Object
*)z
, &v
);
902 z
->Flags
&=~NPM_ISSELECTED
;
903 if(z
->Flags
&NPM_CHECKED
)
904 z
->Flags
|=NPM_INITIAL_CHECKED
;
906 z
->Flags
&=~NPM_INITIAL_CHECKED
;
908 if(z
->Flags
&NPM_CHECKED
)
909 z
->Flags
|=NPM_INITIAL_CHECKED
;
911 z
->Flags
&=~NPM_INITIAL_CHECKED
;
913 if(z
->Sub
) PM_DoSelected(p
, z
->Sub
);
918 /// PM_OpenPopupMenuA
919 struct Window FakeWnd
;
921 APTR __saveds ASM
PM_OpenPopupMenuA(register __a1
struct Window
*prevwnd
GNUCREG(a1
),
922 register __a2
struct TagItem
*tags
GNUCREG(a2
))
925 BOOL shut_down
= FALSE
;
926 struct TagItem
*tstate
;
929 /* TODO: Check this code that is trying to get rid of global p */
937 FakeWnd
.WScreen
=IntuitionBase
->ActiveScreen
;
938 FakeWnd
.RPort
=&FakeWnd
.WScreen
->RastPort
;
941 p
=PM_AllocPMRoot(prevwnd
);
943 p
->RootWnd
= prevwnd
;
944 p
->Scr
= prevwnd
->WScreen
;
946 p
->pmh
=PM_InstallHandler(127);
950 PM_Prefs_Load(PMP_PATH
);
952 p
->RootMenu
= PM_SetupSubWindow(NULL
, p
, NULL
);
953 p
->RButton
=TRUE
; // default
956 while((tag
= NextTagItem(&tstate
)) && !shut_down
) {
957 switch(tag
->ti_Tag
) {
959 p
->MenuFont
=(struct TextFont
*)tag
->ti_Data
;
962 p
->LocaleHook
=(struct Hook
*)tag
->ti_Data
;
965 if(tag
->ti_Data
& IECODE_RBUTTON
) p
->RButton
=TRUE
;
966 if(tag
->ti_Data
& IECODE_LBUTTON
) p
->LButton
=TRUE
;
967 if((tag
->ti_Data
& IECODE_UP_PREFIX
)) {
972 if(tag
->ti_Data
) p
->LButton
= TRUE
;
975 case PM_CenterScreen
:
976 p
->MenuCenter
= tag
->ti_Data
;
979 p
->MenuRight
= tag
->ti_Data
+ prevwnd
->LeftEdge
;
982 p
->MenuBottom
= tag
->ti_Data
+ prevwnd
->TopEdge
;
985 p
->MenuWidth
= tag
->ti_Data
;
988 p
->MenuHeight
= tag
->ti_Data
;
991 p
->RootMenu
->MenuX
= tag
->ti_Data
+(tag
->ti_Data
&1L?1:0)+prevwnd
->LeftEdge
;
994 p
->RootMenu
->MenuY
= tag
->ti_Data
+prevwnd
->TopEdge
;
998 p
->PM
= (struct PopupMenu
*)tag
->ti_Data
;
1002 if(PM_Prefs
->pmp_PulldownPos
== PMP_PD_MOUSE
) { // Popup pulldowns?
1003 p
->PullDown
= tag
->ti_Data
;
1004 } /*else if(PM_Prefs->Popup == 2) { // pos dependent
1005 if(PM_Prefs->WinBar) {
1006 if(prevwnd->LeftEdge<prevwnd->WScreen->MouseX &&
1007 prevwnd->TopEdge<prevwnd->WScreen->MouseY &&
1008 prevwnd->LeftEdge+prevwnd->Width>prevwnd->WScreen->MouseX &&
1009 prevwnd->TopEdge+prevwnd->BorderTop>prevwnd->WScreen->MouseY) {
1010 p->PullDown = tag->ti_Data;
1012 } else if(prevwnd->WScreen->MouseY < prevwnd->WScreen->BarHeight) {
1013 p->PullDown = tag->ti_Data;
1017 if(p
->PullDown
) { // Put pulldowns at screen top-left
1018 p
->RootMenu
->MenuX
= 0;
1019 p
->RootMenu
->MenuY
= 0;
1020 p
->RootMenu
->MenuLevel
= -1; // Right shadow-size
1021 p
->RootMenu
->PM
.Layout
= PML_Horizontal
;
1023 p
->RootMenu
->PM
.Layout
=PML_Vertical
;
1026 case PM_MenuHandler
:
1027 p
->MenuHandler
= (struct Hook
*)tag
->ti_Data
;
1028 p
->DoMultiSel
= TRUE
;
1038 switch(PM_Prefs
->pmp_MenuBorder
) {
1040 p
->BorderWidth
=p
->BorderHeight
=1;
1043 case THICK_BUTTON_FRAME
:
1045 p
->BorderWidth
=p
->BorderHeight
=2;
1052 p
->BorderWidth
=p
->BorderHeight
=4;
1057 if(!p
->MenuFont
) p
->MenuFont
=p
->DrawInfo
->dri_Font
;
1060 if(!p
->MenuFont
) p
->MenuFont
=(struct TextFont
*)prevwnd
->WScreen
->Font
;
1062 p
->RootMenu
->PM
.Sub
= p
->PM
;
1064 PM_Image_Allocate(p
);
1067 p
->treq
=EZCreateTimer(UNIT_VBLANK
);
1069 p
->treq
=EZCreateTimer(0);
1072 p
->treq
->tr_time
.tv_secs
=0;
1073 p
->treq
->tr_time
.tv_micro
=200000;
1075 p
->tport
=p
->treq
->tr_node
.io_Message
.mn_ReplyPort
;
1077 p
->treq
->tr_node
.io_Command
=TR_ADDREQUEST
;
1078 SendIO((struct IORequest
*)p
->treq
);
1082 PM_DoHint(p
->RootMenu
);
1084 PM_DoPopup(p
->RootMenu
);
1087 AbortIO((struct IORequest
*)p
->treq
);
1088 WaitIO((struct IORequest
*)p
->treq
);
1089 EZDeleteTimer(p
->treq
);
1092 } /* if(!shut_down) */
1094 if(p
->DrawInfo
) FreeScreenDrawInfo(prevwnd
->WScreen
, p
->DrawInfo
);
1096 ret
= p
->ReturnCode
;
1100 PM_FreeSubWindow(NULL
, p
->RootMenu
);
1101 PM_RemoveHandler(p
->pmh
);
1104 PM_DoSelected(p
, p
->PM
);
1109 } else DisplayBeep(NULL
);
1115 /// PM_InsertMenuItemA
1117 // Function to add an item to a menu
1120 LONG __saveds ASM
PM_InsertMenuItemA(register __a0
struct PopupMenu
*base
GNUCREG(a0
),
1121 register __a1
struct TagItem
*tags
GNUCREG(a1
))
1123 struct TagItem
*tag
;
1124 struct TagItem
*tstate
;
1126 ULONG method
=0; // insertion method
1127 struct PopupMenu
*pointer
= NULL
, *pm
;
1132 while((tag
=NextTagItem(&tstate
))) {
1133 switch(tag
->ti_Tag
) {
1134 case PM_Insert_Before
:
1135 case PM_Insert_After
:
1136 case PM_Insert_First
:
1137 case PM_Insert_Last
:
1138 case PM_InsertSub_Last
:
1139 case PM_InsertSub_First
:
1140 case PM_InsertSub_Sorted
:
1142 pointer
=(struct PopupMenu
*)tag
->ti_Data
;
1144 case PM_Insert_AfterID
:
1146 pointer
=PM_FindID(base
, tag
->ti_Data
);
1148 case PM_Insert_BeforeID
:
1150 pointer
=(struct PopupMenu
*)PM_FindBeforeID(base
, tag
->ti_Data
);
1152 case PM_Insert_Item
:
1153 if(tag
->ti_Data
&& pointer
) {
1156 case PM_Insert_Last
:
1157 pm
=PM_FindLast(base
);
1158 if(pm
) pm
->Next
=(struct PopupMenu
*)tag
->ti_Data
;
1160 case PM_Insert_First
: // Relies on first itm being hdr (invis itm)
1162 base
->Next
=(struct PopupMenu
*)tag
->ti_Data
;
1163 base
->Next
->Next
=pm
;
1165 case PM_Insert_Before
:
1166 pm
=PM_FindBefore(base
, pointer
);
1168 pm
->Next
=(struct PopupMenu
*)tag
->ti_Data
;
1169 pm
->Next
->Next
=pointer
;
1172 case PM_Insert_BeforeID
:
1173 case PM_Insert_After
:
1174 case PM_Insert_AfterID
:
1176 pointer
->Next
=(struct PopupMenu
*)tag
->ti_Data
;
1177 pointer
->Next
->Next
=pm
;
1179 case PM_InsertSub_Last
:
1180 pm
=PM_FindLast(pointer
);
1181 if(pm
) pm
->Next
=(struct PopupMenu
*)tag
->ti_Data
;
1183 case PM_InsertSub_First
:
1185 pointer
->Sub
=(struct PopupMenu
*)tag
->ti_Data
;
1186 pointer
->Sub
->Next
=pm
;
1188 case PM_InsertSub_Sorted
:
1189 pm
=PM_FindSortedInsertPoint(base
, pointer
);
1191 pm
->Next
=(struct PopupMenu
*)tag
->ti_Data
;
1192 pm
->Next
->Next
=pointer
;
1207 /// PM_RemoveMenuItem
1209 // Function to remove an item
1212 struct PopupMenu
* __saveds ASM
PM_RemoveMenuItem(register __a0
struct PopupMenu
*base
GNUCREG(a0
),
1213 register __a1
struct PopupMenu
*item
GNUCREG(a1
))
1215 struct PopupMenu
*pm
, *prev
=NULL
;
1222 prev
->Next
=item
->Next
;
1238 BOOL __saveds ASM
PM_AbortHook(register __a0 APTR handle
GNUCREG(a0
))
1240 struct PM_Window
*a
=(struct PM_Window
*)handle
;
1241 int mx
=a
->Wnd
->WScreen
->MouseX
-a
->Wnd
->LeftEdge
;
1242 int my
=a
->Wnd
->WScreen
->MouseY
-a
->Wnd
->TopEdge
;
1244 if(mx
>=a
->Selected
->Left
&& mx
<=a
->Selected
->Left
+a
->Selected
->Width
&&
1245 my
>=a
->Selected
->Top
&& my
<=a
->Selected
->Top
+a
->Selected
->Height
) {
1248 if(a
->Selected
->Sub
) {
1250 a
->NextWindow
->PM
.Sub
=a
->Selected
->Sub
;
1251 CurrentTime(&a
->p
->Secs
, &m
);
1252 if(m
-a
->p
->Micros
>60000) /* Update only every 60th millisecond */
1253 PM_OpenPopupWindow(a
->NextWindow
);
1269 #include "popupmenu_libdefs.h"
1270 #define _LibID VERSION_STRING
1272 extern char _LibID
[];
1275 STRPTR ASM __saveds
PM_GetVersion(void)
1277 strcpy(version
, _LibID
);
1279 if(CyberGfx
) PM_StrCat(version
, "\nGraphics card environment found and utilized.");
1280 if(V40Gfx
) PM_StrCat(version
, "\nOS3.1 graphics.library found and utilized.");
1286 LONG ASM __saveds
PM_LayoutMenuA(register __a0
struct Window
*w
GNUCREG(a0
),
1287 register __a1
struct PopupMenu
*pm
GNUCREG(a1
),
1288 register __a2
struct TagItem
*tags
GNUCREG(a2
))
1293 LONG ASM __saveds
PM_RESERVED1()