muimaster.library: Area class will not eat wheel movement messages
[AROS.git] / workbench / libs / popupmenu / pm.c
bloba9c5854ed9f1826a11a2590fa3ee5b1a4199f8c6
1 //
2 // pm.c - popupmenu.library
3 //
4 // popupmenu.library and the PopupMenu package is
5 // Copyright ©1996 - 2002 Henrik Isaksson
6 // All Rights Reserved.
7 //
9 #include "pmpriv.h"
11 #include "pminput.h"
13 #ifndef __AROS__
14 #include "newgui.h"
15 #endif
17 // struct PM_Root *p = NULL;
19 // PM_HandleShadow
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
32 // this menu.
35 void PM_HandleShadow(struct PM_Window *a)
37 PMSR *worknode;
38 PMSR *nextnode;
39 PMSRList *delta;
40 int depth=a->MenuLevel;
41 int l=a->Wnd->LeftEdge, t=a->Wnd->TopEdge;
42 int w=a->Width;
43 int h=a->Height;
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.
54 // +----------+
55 // | |--+
56 // | | |
57 // | |1 |
58 // | | |
59 // | | |
60 // +-+--------+--+
61 // | 2 |3 |
62 // +--------+--+
65 // Shadow 1
66 PM_MapShadow(a->Topographic, delta,
67 l+w, t, l+w, t+h,
68 depth, PMSHADOW_HORIZ|PMSHADOW_TOP);
70 // Shadow 2
71 PM_MapShadow(a->Topographic, delta,
72 l, t+h, l+w, t+h,
73 depth, PMSHADOW_VERT|PMSHADOW_LEFT);
75 // Shadow 3
76 PM_MapShadow(a->Topographic, delta,
77 l+w, t+h, l+w, t+h,
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);
98 worknode=nextnode;
101 PM_FreeShadowList(delta);
105 // PM_RenderMenu
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));
125 else
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
133 if(menurenderhook) {
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);
143 } else {
146 *******************************************************/
149 // Set Menu Font
151 SetFont(a->RPort, a->p->MenuFont);
153 // Draw the items
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);
180 } else {
181 PM_NewDrawItem(c, c->Selected, TRUE, FALSE);
184 } else {
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);
192 } else {
193 PM_NewDrawItem(c, c->Selected, TRUE, FALSE);
196 } else {
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;
208 // PM_MBHit
210 // Called when a mouse button changes state.
212 BOOL PM_MBHit(struct PM_Window *c, struct PM_InpMsg *msg, BOOL mdis)
214 UBYTE whattodo=0;
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) {
222 whattodo=3;
223 c->p->DoneMulti=TRUE;
224 } else {
225 return FALSE;
227 } else {
228 if(!(msg->Code&IECODE_UP_PREFIX)) {
229 c->StickyFlag=TRUE;
230 return FALSE;
231 } else {
232 if(c->StickyFlag) {
233 if(!c->p->DoneMulti) {
234 whattodo=1;
235 } else {
236 if(c->Prev) c->Prev->Running=0L;
237 c->Running=0L;
238 return TRUE;
243 } else {
244 return FALSE;
246 } else {
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) {
252 whattodo=3;
253 c->p->DoneMulti=TRUE;
254 } else {
255 return FALSE;
257 } else {
258 if(msg->Code&IECODE_UP_PREFIX) {
259 if(!c->p->DoneMulti) {
260 whattodo=1;
261 } else {
262 if(c->Prev) c->Prev->Running=0L;
263 c->Running=0L;
264 return TRUE;
268 } else return FALSE;
271 if(whattodo & 1) { // Select an item
272 if(c->Selected) {
273 SelectItem(c, mdis);
277 if(whattodo==1) { // Close if multiselect not requested
278 if(c->Prev) c->Prev->Running=0L;
279 c->Running=0L;
282 if(whattodo==0) return FALSE;
283 else return TRUE;
286 // PM_InsideItemBox
287 struct PopupMenu *PM_InsideItemBox(struct PM_Window *c, struct PopupMenu *pm, ULONG mx, ULONG my)
289 PopupMenu *tmppm;
291 while(pm) {
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) {
301 return pm;
303 } else {
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) {
308 return pm;
312 pm=pm->Next;
315 return NULL;
319 // PM_RedrawPrevSel
321 // Redraws the previously selected item.
323 void PM_RedrawPrevSel(struct PM_Window *c)
325 if(c->PrevSel) {
326 PM_NewDrawItem(c, c->PrevSel, FALSE, FALSE);
331 // PM_MMove
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)) {
344 c->p->Subtimer=0;
345 if(c->PrevSel) {
346 if(!MenuDisable) PM_RedrawPrevSel(c);
347 c->PrevSel=0L;
349 if(c->Selected) {
350 if(!MenuDisable) PM_NewDrawItem(c, c->Selected, TRUE, MenuDisable);
353 } else {
354 if(c->Selected) {
355 if(!MenuDisable) PM_NewDrawItem(c, c->Selected, FALSE, MenuDisable);
356 c->Selected=0L;
358 return FALSE;
360 return TRUE;
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;
377 #ifndef __AROS__
378 if( IntuitionBase->LibNode.lib_Version >= 50 ) {
379 GetGUIAttrs(NULL, a->p->DrawInfo, GUIA_MenuDropShadows, &shadows, TAG_DONE); // V50 !!
381 #endif
383 PM_LayoutMenu(a);
385 if(!shadows) {
386 a->p->ShadowHeight=0;
387 a->p->ShadowWidth=0;
390 if(a->FirstTime) {
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
405 if(!a->Wnd)
406 PM_OpenWindow(a, a->MenuX, a->MenuY, a->Width+1, a->Height+1, a->p->RootWnd->WScreen);
407 else
408 PM_ResizeWindow(a, a->MenuX, a->MenuY, a->Width+1, a->Height+1);
409 } else {
410 if(!a->Wnd) {
411 WORD mx, my;
413 mx = a->MenuX;
414 my = a->MenuY;
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);
427 } else {
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);
430 } else {
431 PM_ResizeWindow(a, SCREENMOUSEPOS(a->p), a->Width+1, a->Height+1);
435 if(a->Wnd) {
436 PM_RenderMenu(a, a->MenuDisabled, FALSE);
438 if(a->te.BMap) {
439 // If there is a TE (transition effects) bitmap.
441 // Currently, only an "animation" effect is implemented.
443 int i;
444 int w, h;
445 int dw, dh;
447 w = a->Wnd->Width-1;
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;
455 dw = w/10;
456 dh = h/10;
458 for(i=1;i<11;i++) {
459 //Forbid();
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);
461 WaitBlit();
462 //Permit();
463 WaitTOF();
466 //Forbid();
467 BltBitMap(a->te.BMap, 0, 0, a->Wnd->WScreen->RastPort.BitMap, a->Wnd->LeftEdge, a->Wnd->TopEdge, w, h, 0xc0, 0xff, 0L);
468 WaitBlit();
469 //Permit();
472 // Handle shadows
473 if(shadows) {
474 if(!(a->p->PullDown && a->FirstTime)) {
475 PM_HandleShadow(a);
479 return TRUE;
481 return FALSE;
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));
489 if(newwin) {
490 if(parent) {
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;
498 } else {
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);
508 newwin->PM.Sub = pm;
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;
515 newwin->p = p;
517 if(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;
522 } else {
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
526 newwin->MenuX += 1;
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);
539 } else {
540 newwin->AltXPos = newwin->MenuX = p->Scr->MouseX;
541 newwin->MenuY = p->Scr->MouseY;
542 newwin->MenuLevel = 0;
543 newwin->FirstTime = TRUE;
544 return newwin;
547 if(newwin->PM.Sub)
548 return newwin;
550 // Constructor hook cancelled the operation, we must tidy up a bit...
552 PM_FreeSubWindow(parent, newwin);
554 return NULL;
557 void PM_FreeSubWindow(struct PM_Window *parent, struct PM_Window *window)
559 if(window) {
560 if(parent) {
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);
572 PM_Mem_Free(window);
574 if(parent)
575 parent->SubMenuToOpen = 0L;
578 void PM_SelectNext(struct PM_Window *a)
580 BOOL found=FALSE;
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)
587 BOOL found=FALSE;
588 a->Selected=PM_FindPrevSelectable(a, a->PM.Sub, &found);
589 if(!a->Selected)
590 a->Selected=PM_FindLastSelectable(a->PM.Sub);
593 APTR PM_DoPopup(struct PM_Window *a)
595 if(PM_OpenPopupWindow(a)) {
596 BOOL halt;
597 BOOL keymode=FALSE;
599 while(a->Running && (a->p->TimeOut<2)) {
600 struct PM_InpMsg *msg;
601 ULONG s;
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);
611 a->p->TimeOut++;
615 halt=FALSE;
617 if(s & (1L << a->p->pmh->port->mp_SigBit)) {
618 while((msg=(struct PM_InpMsg *)GetMsg(a->p->pmh->port))) {
619 switch(msg->Kind) {
620 case PM_MSG_RAWMOUSE:
621 keymode=FALSE;
622 if(msg->Code!=IECODE_NOBUTTON) {
623 PM_MBHit(a, msg, a->MenuDisabled);
624 break;
625 } else {
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)) {
628 a->Running=0L;
629 if(a->Prev) a->Prev->Running=TRUE;
634 case PM_MSG_TIMER:
635 if(a->p->TimeOut>0) a->p->TimeOut--;
636 if(msg->Kind==PM_MSG_TIMER || a->p->Subtimer==0) {
637 a->p->Subtimer++;
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
647 break;
649 case PM_MSG_DOWN:
650 keymode=TRUE;
651 a->PrevSel=a->Selected;
652 PM_RedrawPrevSel(a);
653 PM_SelectNext(a);
654 if(a->Selected) PM_NewDrawItem(a, a->Selected, TRUE, FALSE);
655 break;
657 case PM_MSG_UP:
658 keymode=TRUE;
659 a->PrevSel=a->Selected;
660 PM_RedrawPrevSel(a);
661 PM_SelectPrev(a);
662 if(a->Selected) PM_NewDrawItem(a, a->Selected, TRUE, FALSE);
663 break;
665 case PM_MSG_MULTISELECT:
666 case PM_MSG_SELECT:
667 if(a->Selected) {
668 if(!a->Selected->Sub && !a->Selected->SubConstruct) {
669 SelectItem(a, FALSE);
670 if(msg->Kind==PM_MSG_SELECT) {
671 a->Running=0;
672 if(a->Prev) a->Prev->Running=FALSE;
676 case PM_MSG_OPENSUB:
677 if(a->Selected) {
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
685 break;
687 case PM_MSG_CLOSESUB:
688 a->Running=0;
689 if(a->Prev) a->Prev->Running=TRUE;
690 halt=TRUE;
691 break;
693 case PM_MSG_TERMINATE:
694 a->Running=0L;
695 halt=TRUE;
696 break;
698 PM_Mem_Free(msg);
700 if(halt) break;
704 if(a->SubMenuToOpen) {
705 struct Task *tsk;
706 tsk=FindTask(NULL);
707 if((UBYTE *)tsk->tc_SPReg<((UBYTE *)tsk->tc_SPLower)+1024) {
708 DisplayBeep(NULL);
709 #if 0
710 } else if((a->NextWindow = PM_SetupSubWindow(a, p, a->SubMenuToOpen))) {
711 #else
712 /* TODO: Need to check this code to avoid global p */
713 } else if((a->NextWindow = PM_SetupSubWindow(a, a->p, a->SubMenuToOpen))) {
714 #endif
715 PM_DoPopup(a->NextWindow);
717 if(a->Prev) a->Prev->Running = FALSE;
719 if(!keymode) {
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)) {
722 a->Running = 0L;
723 if(a->Prev) a->Prev->Running = TRUE;
728 PM_FreeSubWindow(a, a->NextWindow);
729 a->NextWindow = NULL;
731 a->SubMenuToOpen=NULL;
734 PM_CloseWindow(a);
737 return 0L;
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;
748 ULONG s;
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);
758 a->p->TimeOut++;
762 if(s & (1L << a->p->pmh->port->mp_SigBit)) {
763 while((msg=(struct PM_InpMsg *)GetMsg(a->p->pmh->port))) {
764 switch(msg->Kind) {
765 case PM_MSG_TIMER:
766 if(a->p->TimeOut>0) a->p->TimeOut--;
767 break;
768 default:
769 a->Running=0L;
771 PM_Mem_Free(msg);
776 PM_CloseWindow(a);
778 return 0L;
781 APTR __saveds ASM PM_OBSOLETEFilterIMsgA()
783 return NULL;
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;
796 struct TagItem *tag;
797 struct Hook *MenuHandler=NULL;
798 BOOL autpd = FALSE, rawkey = FALSE;
800 if(!pm) return 0L;
801 if(!im) return 0L;
803 tstate = tags;
804 while((tag=NextTagItem(&tstate))) {
805 switch(tag->ti_Tag) {
806 case PM_MenuHandler:
807 MenuHandler=(struct Hook *)tag->ti_Data;
808 break;
809 case PM_AutoPullDown:
810 autpd=TRUE;
811 break;
812 case PM_RawKey:
813 rawkey = (BOOL)tag->ti_Data;
814 break;
818 if(im->Class==IDCMP_MOUSEBUTTONS) {
819 if(autpd) {
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;
826 if(MenuHandler) {
827 opmtags[3].ti_Tag=PM_MenuHandler; opmtags[3].ti_Data=(IPTR)MenuHandler;
828 } else {
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) {
839 struct PopupMenu *p;
841 p=PM_FindItemCommKey(pm, im->Code);
842 if(p) {
843 if(!(p->Flags & NPM_DISABLED)) {
844 if(p->Flags & NPM_CHECKIT) {
845 if(p->Flags & NPM_CHECKED) {
846 p->Flags&=~NPM_CHECKED;
847 if(p->Exclude)
848 PM_AlterState(pm, p->Exclude, PMACT_DESELECT);
849 if(p->AutoSetPtr) *p->AutoSetPtr=FALSE;
850 } else {
851 if(p->Exclude)
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);
858 return p->UserData;
862 } else if(im->Class==IDCMP_RAWKEY && rawkey) {
863 if(im->Qualifier&IEQUALIFIER_RCOMMAND) {
864 struct PopupMenu *p;
866 p=PM_FindItemCommKey(pm, im->Code);
867 if(p) {
868 if(!(p->Flags & NPM_DISABLED)) {
869 if(p->Flags & NPM_CHECKIT) {
870 if(p->Flags & NPM_CHECKED) {
871 p->Flags&=~NPM_CHECKED;
872 if(p->Exclude)
873 PM_AlterState(pm, p->Exclude, PMACT_DESELECT);
874 if(p->AutoSetPtr) *p->AutoSetPtr=FALSE;
875 } else {
876 if(p->Exclude)
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);
883 return p->UserData;
888 return NULL;
892 // Call MenuHandler for each selected item
894 void PM_DoSelected(struct PM_Root *p, struct PopupMenu *pm)
896 struct PopupMenu *z=pm;
898 while(z) {
899 if(z->Flags&NPM_ISSELECTED) {
900 IPTR v = 0;
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;
905 else
906 z->Flags&=~NPM_INITIAL_CHECKED;
908 if(z->Flags&NPM_CHECKED)
909 z->Flags|=NPM_INITIAL_CHECKED;
910 else
911 z->Flags&=~NPM_INITIAL_CHECKED;
913 if(z->Sub) PM_DoSelected(p, z->Sub);
914 z=z->Next;
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))
924 APTR ret = 0L;
925 BOOL shut_down = FALSE;
926 struct TagItem *tstate;
927 struct TagItem *tag;
928 #if 1
929 /* TODO: Check this code that is trying to get rid of global p */
930 struct PM_Root *p;
931 #endif
933 if(!prevwnd) {
934 prevwnd=&FakeWnd;
935 FakeWnd.LeftEdge=0;
936 FakeWnd.TopEdge=0;
937 FakeWnd.WScreen=IntuitionBase->ActiveScreen;
938 FakeWnd.RPort=&FakeWnd.WScreen->RastPort;
941 p=PM_AllocPMRoot(prevwnd);
942 if(p) {
943 p->RootWnd = prevwnd;
944 p->Scr = prevwnd->WScreen;
946 p->pmh=PM_InstallHandler(127);
948 if(p->pmh) {
950 PM_Prefs_Load(PMP_PATH);
952 p->RootMenu = PM_SetupSubWindow(NULL, p, NULL);
953 p->RButton=TRUE; // default
955 tstate = tags;
956 while((tag = NextTagItem(&tstate)) && !shut_down) {
957 switch(tag->ti_Tag) {
958 case PM_ForceFont:
959 p->MenuFont=(struct TextFont *)tag->ti_Data;
960 break;
961 case PM_LocaleHook:
962 p->LocaleHook=(struct Hook *)tag->ti_Data;
963 break;
964 case PM_Code:
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)) {
968 shut_down=TRUE;
970 break;
971 case PM_UseLMB:
972 if(tag->ti_Data) p->LButton = TRUE;
973 p->RButton = FALSE;
974 break;
975 case PM_CenterScreen:
976 p->MenuCenter = tag->ti_Data;
977 break;
978 case PM_Right:
979 p->MenuRight = tag->ti_Data + prevwnd->LeftEdge;
980 break;
981 case PM_Bottom:
982 p->MenuBottom = tag->ti_Data + prevwnd->TopEdge;
983 break;
984 case PM_MinWidth:
985 p->MenuWidth = tag->ti_Data;
986 break;
987 case PM_MinHeight:
988 p->MenuHeight = tag->ti_Data;
989 break;
990 case PM_Left:
991 p->RootMenu->MenuX = tag->ti_Data+(tag->ti_Data&1L?1:0)+prevwnd->LeftEdge;
992 break;
993 case PM_Top:
994 p->RootMenu->MenuY = tag->ti_Data+prevwnd->TopEdge;
995 break;
996 case PM_Menu:
997 if(tag->ti_Data) {
998 p->PM = (struct PopupMenu *)tag->ti_Data;
1000 break;
1001 case PM_PullDown:
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;
1022 } else {
1023 p->RootMenu->PM.Layout=PML_Vertical;
1025 break;
1026 case PM_MenuHandler:
1027 p->MenuHandler = (struct Hook *)tag->ti_Data;
1028 p->DoMultiSel = TRUE;
1029 break;
1030 case PM_HintBox:
1031 p->HintBox = TRUE;
1032 break;
1036 if(!shut_down) {
1038 switch(PM_Prefs->pmp_MenuBorder) {
1039 case BUTTON_FRAME:
1040 p->BorderWidth=p->BorderHeight=1;
1041 break;
1042 case MAGIC_FRAME:
1043 case THICK_BUTTON_FRAME:
1044 case DOUBLE_FRAME:
1045 p->BorderWidth=p->BorderHeight=2;
1046 break;
1047 case INTUI_FRAME:
1048 p->BorderWidth=2;
1049 p->BorderHeight=1;
1050 break;
1051 case DROPBOX_FRAME:
1052 p->BorderWidth=p->BorderHeight=4;
1053 break;
1056 if(p->DrawInfo) {
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);
1066 #ifdef __AROS__
1067 p->treq=EZCreateTimer(UNIT_VBLANK);
1068 #else
1069 p->treq=EZCreateTimer(0);
1070 #endif
1071 if(p->treq) {
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);
1081 if(p->HintBox)
1082 PM_DoHint(p->RootMenu);
1083 else
1084 PM_DoPopup(p->RootMenu);
1086 if(p->treq) {
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;
1098 Delay(2);
1100 PM_FreeSubWindow(NULL, p->RootMenu);
1101 PM_RemoveHandler(p->pmh);
1104 PM_DoSelected(p, p->PM);
1105 PM_Image_Free(p);
1106 PM_Mem_Free(p);
1108 return ret;
1109 } else DisplayBeep(NULL);
1111 return 0L;
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;
1125 LONG count=0;
1126 ULONG method=0; // insertion method
1127 struct PopupMenu *pointer = NULL, *pm;
1129 if(!base) return 0;
1131 tstate = tags;
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:
1141 method=tag->ti_Tag;
1142 pointer=(struct PopupMenu *)tag->ti_Data;
1143 break;
1144 case PM_Insert_AfterID:
1145 method=tag->ti_Tag;
1146 pointer=PM_FindID(base, tag->ti_Data);
1147 break;
1148 case PM_Insert_BeforeID:
1149 method=tag->ti_Tag;
1150 pointer=(struct PopupMenu *)PM_FindBeforeID(base, tag->ti_Data);
1151 break;
1152 case PM_Insert_Item:
1153 if(tag->ti_Data && pointer) {
1155 switch(method) {
1156 case PM_Insert_Last:
1157 pm=PM_FindLast(base);
1158 if(pm) pm->Next=(struct PopupMenu *)tag->ti_Data;
1159 break;
1160 case PM_Insert_First: // Relies on first itm being hdr (invis itm)
1161 pm=base->Next;
1162 base->Next=(struct PopupMenu *)tag->ti_Data;
1163 base->Next->Next=pm;
1164 break;
1165 case PM_Insert_Before:
1166 pm=PM_FindBefore(base, pointer);
1167 if(pm) {
1168 pm->Next=(struct PopupMenu *)tag->ti_Data;
1169 pm->Next->Next=pointer;
1171 break;
1172 case PM_Insert_BeforeID:
1173 case PM_Insert_After:
1174 case PM_Insert_AfterID:
1175 pm=pointer->Next;
1176 pointer->Next=(struct PopupMenu *)tag->ti_Data;
1177 pointer->Next->Next=pm;
1178 break;
1179 case PM_InsertSub_Last:
1180 pm=PM_FindLast(pointer);
1181 if(pm) pm->Next=(struct PopupMenu *)tag->ti_Data;
1182 break;
1183 case PM_InsertSub_First:
1184 pm=pointer->Sub;
1185 pointer->Sub=(struct PopupMenu *)tag->ti_Data;
1186 pointer->Sub->Next=pm;
1187 break;
1188 case PM_InsertSub_Sorted:
1189 pm=PM_FindSortedInsertPoint(base, pointer);
1190 if(pm) {
1191 pm->Next=(struct PopupMenu *)tag->ti_Data;
1192 pm->Next->Next=pointer;
1194 break;
1197 count++;
1199 break;
1203 return count;
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;
1217 pm=base;
1219 if(pm) do {
1220 if(pm==item) {
1221 if(prev) {
1222 prev->Next=item->Next;
1223 item->Next=NULL;
1224 return item;
1225 } else {
1226 return NULL;
1229 prev=pm;
1230 pm=pm->Next;
1231 } while(pm);
1233 return NULL;
1237 /// PM_AbortHook
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) {
1247 if(a->NextWindow) {
1248 if(a->Selected->Sub) {
1249 ULONG m;
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);
1254 a->p->Micros=m;
1258 return FALSE;
1260 return TRUE;
1264 /// PM_GetVersion
1266 char version[256];
1268 #ifdef __AROS__
1269 #include "popupmenu_libdefs.h"
1270 #define _LibID VERSION_STRING
1271 #else
1272 extern char _LibID[];
1273 #endif
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.");
1282 return version;
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))
1290 return 0;
1293 LONG ASM __saveds PM_RESERVED1()
1295 return 0;