/*************************************************************************** * popmenu_pak.c -general-purpose dynamic Pop-up Menu routines to help make* * programming alot easier. * * (c) 1990 VIDEOWORKS Computer Applications * * All rights reserved. * * 129 Orchard Avenue, Rocky Mount, VA 24151 * * (703) 483-8219 / 489-3863 * * * * Designed and Developed by Paul T. Miller * * * * Program Name: N/A * * Version: 1 * * Revision: 0 * *-------------------------------------------------------------------------* * File: (popmenu_pak.c) dynamic window-relative pop-up menu routines * *-------------------------------------------------------------------------* * Modification History * * Date Author Comment * * -------- ------ ------- * * 03-29-90 PTM Created. Initialization/Setup * 04-01-90 PTM PMenu drawing/handling. RastPort FONT characteristics * 05-07-90 PTM Modify PMenu box expansion to fit in window * 05-09-90 PTM Move itemtext positioning to drawmenu() * ***************************************************************************/ #include "popmenu_pak.h" #ifndef GRAPHICS_GFXMACROS_H #include #endif /* Here are some constants for itemtext placement and item sizes */ #define PMENU_WOFF 16 /* sixteen pixels wider than longest text */ #define PITEM_VOFF 2 /* extra pixels for item (+font height) */ #define PITEM_YOFF 2 /* vertical index into item for text */ #define PITEM_XOFF 2 /* horizontal index into item for text */ struct PMenu *BuildPMenu(item, flags, inum, title, id) struct PMenuItem *item; USHORT flags; SHORT inum; UBYTE *title; USHORT id; { struct PMenu *pmenu; struct IntuiText *itext = NULL; struct Border *border; struct Gadget *gad; /* Allocate memory for menu, gadget, gadget text, and border */ pmenu = (struct PMenu *)AllocMem(sizeof(struct PMenu), MEMF_CLEAR); if (!pmenu) return(NULL); gad = (struct Gadget *)AllocMem(sizeof(struct Gadget), MEMF_CLEAR); if (!gad) { FreeMem(pmenu, sizeof(struct PMenu)); return(NULL); } border = (struct Border *)AllocMem(sizeof(struct Border), MEMF_CLEAR); if (!border) { FreeMem(gad, sizeof(struct Gadget)); FreeMem(pmenu, sizeof(struct PMenu)); return(NULL); } itext = (struct IntuiText *)AllocMem(sizeof(struct IntuiText), MEMF_CLEAR); if (!itext) { FreeMem(border, sizeof(struct Border)); FreeMem(gad, sizeof(struct Gadget)); FreeMem(pmenu, sizeof(struct PMenu)); return(NULL); } if (flags & SHADOWED) border->Count = 8; else border->Count = 5; border->XY = (SHORT *)AllocMem(sizeof(SHORT)*16, MEMF_CLEAR); border->FrontPen = 1; border->BackPen = 0; border->DrawMode = JAM1; pmenu->FirstPItem = item; pmenu->Flags = flags; pmenu->MenuName = (UBYTE *)AllocMem(80, MEMF_CLEAR); if (title) { strcpy(pmenu->MenuName, title); pmenu->Flags |= TITLED; /* title supplied by user */ } if (pmenu->FirstPItem) { if (inum < 1) inum = 1; pmenu->ActiveItem = GetPItem(pmenu, inum); pmenu->Flags |= PREDEFINED; } itext->FrontPen = 1; /* Build text for user-controlled menu title */ itext->BackPen = 0; itext->DrawMode = JAM1; itext->IText = pmenu->MenuName; gad->NextGadget = NULL; gad->GadgetType = BOOLGADGET; gad->Activation = GADGIMMEDIATE | FOLLOWMOUSE; gad->Flags = GADGHCOMP; gad->GadgetRender = (APTR)border; gad->SelectRender = NULL; gad->MutualExclude = NULL; gad->SpecialInfo = NULL; gad->GadgetText = itext; gad->GadgetID = id; gad->UserData = (APTR)pmenu; pmenu->PMenuGadget = gad; return(pmenu); } void SetPMenuColor(pmenu, frontpen, backpen) struct PMenu *pmenu; USHORT frontpen, backpen; { struct Border *border; struct IntuiText *itext; if (pmenu) { itext = (struct IntuiText *)pmenu->PMenuGadget->GadgetText; border = (struct Border *)pmenu->PMenuGadget->GadgetRender; if (itext) { itext->FrontPen = frontpen; itext->BackPen = backpen; } if (border) { border->FrontPen = frontpen; border->BackPen = backpen; } } } InitPMenu(window, pmenu, x, y) struct Window *window; struct PMenu *pmenu; SHORT x, y; { struct RastPort *rp = window->RPort; struct Gadget *gad; ULONG flags; if (!window || !pmenu) return(NULL); flags = window->IDCMPFlags; ModifyIDCMP(window, flags | GADGETUP); pmenu->PMenuWindow = window; /* no user-set width, so find width/height of activation box */ if (pmenu->Width == 0) pmenu->Width = (SHORT)find_maxpitemwidth(pmenu) + PMENU_WOFF; if (pmenu->Height == 0) pmenu->Height = rp->TxHeight+PITEM_VOFF; /* find out where in the window to render the window */ if (x == -1) pmenu->LeftEdge = window->Width - pmenu->Width - 5; else if (x < -1) pmenu->LeftEdge = window->Width + x; else pmenu->LeftEdge = x; if (y == -1) pmenu->TopEdge = window->Height - pmenu->Height - 5; else if (y < -1) pmenu->TopEdge = window->Height + y; else pmenu->TopEdge = y; /* set up activation gadget attributes */ gad = pmenu->PMenuGadget; gad->LeftEdge = pmenu->LeftEdge; gad->TopEdge = pmenu->TopEdge; gad->Width = pmenu->Width; gad->Height = pmenu->Height; calc_border(pmenu); pmenu->Flags |= MENUENABLED; pmenu->Flags |= MENU_INIT; AddGadget(window, pmenu->PMenuGadget, -1); RefreshPMenu(pmenu); return(1); } find_maxpitemwidth(pmenu) struct PMenu *pmenu; { int length, max = 0; struct PMenuItem *item; for (item = pmenu->FirstPItem; item; item = item->NextPItem) { if (item->Flags & ITEMTEXT) { length = IntuiTextLength((struct IntuiText *)item->ItemFill); if (length > max) max = length; } } return(max); } void calc_border(pmenu) struct PMenu *pmenu; { struct Border *b; SHORT *dat; if (pmenu) { b = (struct Border *)pmenu->PMenuGadget->GadgetRender; dat = b->XY; if (pmenu->Flags & SHADOWED) { b->Count = 8; dat[0] = dat[2] = pmenu->Width+1; dat[1] = dat[4] = 1; dat[3] = dat[5] = pmenu->Height+1; dat[6] = dat[11] = dat[12] = dat[13] = dat[14] = 0; dat[7] = dat[9] = dat[15] = pmenu->Height; dat[8] = dat[10] = pmenu->Width; } else { b->Count = 5; dat[0] = dat[1] = dat[3] = dat[6] = dat[8] = dat[9] = 0; dat[2] = dat[4] = pmenu->Width; dat[5] = dat[7] = pmenu->Height; } } } void RefreshPMenu(pmenu) struct PMenu *pmenu; { struct IntuiText *itext; struct PMenuItem *item; int x, y, w, h; UBYTE bpen; bpen = pmenu->PMenuGadget->GadgetText->BackPen; if (pmenu && (pmenu->Flags & MENU_INIT)) { if (pmenu->Flags & TITLED) pmenu->PMenuGadget->GadgetText->IText = pmenu->MenuName; else { item = pmenu->ActiveItem; if (item->Flags & ITEMTEXT) { itext = (struct IntuiText *)pmenu->ActiveItem->ItemFill; pmenu->PMenuGadget->GadgetText->IText = itext->IText; } } itext = pmenu->PMenuGadget->GadgetText; itext->LeftEdge = (pmenu->Width - IntuiTextLength(itext))/2; itext->TopEdge = PITEM_YOFF; x = pmenu->LeftEdge; y = pmenu->TopEdge; w = pmenu->Width; h = pmenu->Height; SetDrMd(pmenu->PMenuWindow->RPort, JAM1); SetAPen(pmenu->PMenuWindow->RPort, bpen); RectFill(pmenu->PMenuWindow->RPort, x, y, x+w, y+h); RefreshGList(pmenu->PMenuGadget, pmenu->PMenuWindow, NULL, 1); } } void RemovePMenu(pmenu) struct PMenu *pmenu; { if (pmenu && pmenu->Flags & MENU_INIT) { pmenu->Flags &= ~MENU_INIT; pmenu->Flags &= ~MENUENABLED; RemoveGadget(pmenu->PMenuWindow, pmenu->PMenuGadget); } } void FreePMenu(pmenu) struct PMenu *pmenu; { struct Border *border; if (pmenu) { RemovePMenu(pmenu); FreeMem(pmenu->PMenuGadget->GadgetText, sizeof(struct IntuiText)); FreeMem(pmenu->MenuName, 80); border = (struct Border *)pmenu->PMenuGadget->GadgetRender; if (border) { FreeMem(border->XY, sizeof(SHORT) * 16); FreeMem(border, sizeof(struct Border)); } FreeMem(pmenu->PMenuGadget, sizeof(struct Gadget)); FreeMem(pmenu, sizeof(struct PMenu)); } } void SetActiveItem(pmenu, inum) struct PMenu *pmenu; SHORT inum; { struct PMenuItem *item; if (inum < 1) return; if (pmenu && (pmenu->Flags & COMMPMENU)) { item = GetPItem(pmenu, inum); if (item) { pmenu->ActiveItem = item; RefreshPMenu(pmenu); } } } struct PMenuItem *GetPItem(pmenu, inum) struct PMenu *pmenu; SHORT inum; { struct PMenuItem *item; UCOUNT i = 1; for (item = pmenu->FirstPItem; item; item = item->NextPItem, i++) if (i == inum) return(item); return(NULL); } SHORT GetPItemNum(pmenu, item) struct PMenu *pmenu; struct PMenuItem *item; { struct PMenuItem *pitem; SHORT i = 0; if (item == NULL) return(0); if (pmenu->FirstPItem == item) return(1); for (pitem = pmenu->FirstPItem; pitem; pitem = pitem->NextPItem, i++) if (pitem == item) return((SHORT)(i+1)); return(0); } CountPMenuItems(pmenu) struct PMenu *pmenu; { struct PMenuItem *item; UCOUNT i; if (pmenu->FirstPItem == NULL) return(0); for (i = 0, item = pmenu->FirstPItem; item; item = item->NextPItem, i++) ; return(i+1); } /* Sets the menu activation box of a popup menu to specified text: Only if an initial constant title was supplied with BuildPMenu() */ void SetPMenuText(pmenu, text) struct PMenu *pmenu; UBYTE *text; { if (pmenu) if (pmenu->Flags & TITLED) { strcpy(pmenu->MenuName, text); RefreshPMenu(pmenu); } } USHORT HandlePMenu(pmenu) struct PMenu *pmenu; { ULONG oldflags; struct Window *win; struct BitMap *sbm; struct PMenuItem *item; struct IntuiMessage *message; ULONG class; USHORT code, inum; SHORT mx, my; SHORT lnum = 0, mousemoved = 0; if (!pmenu) return(NULL); calc_menusize(pmenu); sbm = savebackground(pmenu); drawpmenu(pmenu); win = pmenu->PMenuWindow; oldflags = win->IDCMPFlags; ReportMouse(win, TRUE); ModifyIDCMP(win, oldflags | MOUSEMOVE | MOUSEBUTTONS); lnum = GetPItemNum(pmenu, pmenu->ActiveItem); complement_item(pmenu, lnum); while (1) { WaitPort(win->UserPort); while (message = (struct IntuiMessage *)GetMsg(win->UserPort)) { class = message->Class; code = message->Code; mx = message->MouseX; my = message->MouseY; ReplyMsg(message); switch (class) { case MOUSEMOVE: mousemoved = 1; break; case MOUSEBUTTONS: switch (code) { case SELECTUP: item = getmenuitem(pmenu, mx, my); restorebackground(pmenu, sbm); ModifyIDCMP(win, oldflags); inum = GetPItemNum(pmenu, item); if (!(pmenu->Flags & TITLED)) SetActiveItem(pmenu, inum); return(inum); break; } break; } } if (mousemoved) { mousemoved = 0; handle_items(pmenu, mx, my, &lnum); } } } struct PMenuItem *getmenuitem(pmenu, mx, my) struct PMenu *pmenu; SHORT mx, my; { struct PMenuItem *item; SHORT x, y, x2, y2; USHORT num; if (!pmenu) return(NULL); x = pmenu->JazzX; y = pmenu->JazzY; x2 = pmenu->JazzX + pmenu->BeatX; y2 = pmenu->JazzY + pmenu->BeatY; if (mx > x && my > y && mx < x2 && my < y2) { num = (my - y - PITEM_YOFF) / pmenu->Height; item = GetPItem(pmenu, (SHORT)(num+1)); if (item->Flags & ITEMENABLED) return(item); } return(NULL); } void calc_menusize(pmenu) struct PMenu *pmenu; { UCOUNT num; num = CountPMenuItems(pmenu); if (pmenu->Flags & TITLED) /* titled, so find width of largest item */ pmenu->BeatX = find_maxpitemwidth(pmenu) + PMENU_WOFF; else pmenu->BeatX = pmenu->Width; /* otherwise width/height of menu box */ pmenu->BeatY = pmenu->Height * (num-1) + PITEM_VOFF-1; if (pmenu->Flags & SHADOWED) { pmenu->BeatX++; pmenu->BeatY++; } pmenu->JazzX = pmenu->LeftEdge; /* upper-left corner of expanded */ pmenu->JazzY = pmenu->TopEdge; /* shift menu into window if it extends out right side */ if (pmenu->JazzX + pmenu->BeatX > pmenu->PMenuWindow->Width) pmenu->JazzX = pmenu->LeftEdge + pmenu->Width - pmenu->BeatX + 1; if (pmenu->Flags & COMMPMENU) { num = GetPItemNum(pmenu, pmenu->ActiveItem); pmenu->JazzY -= ((num-1) * pmenu->Height); while (pmenu->JazzY <= 0) pmenu->JazzY += pmenu->Height; } } struct BitMap *savebackground(pmenu) struct PMenu *pmenu; { int x, y, w, h; struct Window *win; struct BitMap *bm; win = pmenu->PMenuWindow; x = win->LeftEdge + pmenu->JazzX; y = win->TopEdge + pmenu->JazzY; w = pmenu->BeatX + 1; h = pmenu->BeatY + 1; bm = allocbitmap((USHORT)w, (USHORT)h, (UBYTE)win->RPort->BitMap->Depth); if (bm) BltBitMap(win->RPort->BitMap, x, y, bm, 0, 0, w, h, 0xc0, 0xff, NULL); return(bm); } void restorebackground(pmenu, bm) struct PMenu *pmenu; struct BitMap *bm; { int x, y, w, h; struct Window *win; if (!pmenu) return; win = pmenu->PMenuWindow; x = win->LeftEdge + pmenu->JazzX; y = win->TopEdge + pmenu->JazzY; w = pmenu->BeatX + 1; h = pmenu->BeatY + 1; if (bm) { BltBitMap(bm, 0, 0, win->RPort->BitMap, x, y, w, h, 0xc0, 0xff, NULL); freebitmap(bm); } pmenu->Flags &= ~MIDRAWN; } void drawpmenu(pmenu) struct PMenu *pmenu; { int x, y, w, h, i; struct RastPort *rp; struct PMenuItem *item; struct IntuiText *itext; UBYTE fpen, bpen; rp = pmenu->PMenuWindow->RPort; SetDrMd(rp, JAM1); fpen = pmenu->PMenuGadget->GadgetText->FrontPen; bpen = pmenu->PMenuGadget->GadgetText->BackPen; x = pmenu->JazzX; y = pmenu->JazzY; w = pmenu->BeatX; h = pmenu->BeatY; if (pmenu->Flags & SHADOWED) { w--; h--; } /* draw the menu outline */ SetAPen(rp, fpen); RectFill(rp, x, y, x+w, y+h); SetAPen(rp, bpen); RectFill(rp, x+1, y+1, x+w-2, y+h-2); SetAPen(rp, fpen); if (pmenu->Flags & SHADOWED) { Move(rp, x+w+1, y+1); Draw(rp, x+w+1, y+h+1); Draw(rp, x+1, y+h+1); } for (i = 0, item = pmenu->FirstPItem; item; item = item->NextPItem, i++) { if (item->Flags & ITEMTEXT) { itext = (struct IntuiText *)item->ItemFill; itext->TopEdge = PITEM_YOFF; /* position item text */ if (item->Flags & CENTERED) itext->LeftEdge = (w - IntuiTextLength(itext))/2; else itext->LeftEdge = PITEM_XOFF; SetAPen(rp, itext->FrontPen); Move(rp, x+itext->LeftEdge, y + itext->TopEdge + (i * pmenu->Height) + rp->TxBaseline); Text(rp, itext->IText, strlen(itext->IText)); } } pmenu->Flags |= MIDRAWN; } void handle_items(pmenu, mx, my, lnum) struct PMenu *pmenu; SHORT mx, my, *lnum; { SHORT x, y, x2, y2; USHORT num; if (!pmenu) return; x = pmenu->JazzX; y = pmenu->JazzY; x2 = pmenu->JazzX + pmenu->BeatX - 1; y2 = pmenu->JazzY + pmenu->BeatY - 1; if (pmenu->Flags & SHADOWED) { x2--; y2--; } if (mx > x && my > y && mx < x2 && my < y2) { num = ((my - y - PITEM_VOFF) / pmenu->Height) + 1; if (num != *lnum) { if (*lnum != 0) complement_item(pmenu, *lnum); complement_item(pmenu, num); } *lnum = num; return; } if (*lnum != 0) complement_item(pmenu, *lnum); *lnum = 0; } void complement_item(pmenu, num) struct PMenu *pmenu; SHORT num; { int x, y, w, h; struct PMenuItem *item; struct RastPort *rp = pmenu->PMenuWindow->RPort; BYTE old_mode = rp->DrawMode; item = GetPItem(pmenu, num); if (pmenu == NULL || item == NULL) return; if (item->Flags & ITEMENABLED) { x = pmenu->JazzX+1; y = pmenu->JazzY + (num-1)*pmenu->Height+1; w = pmenu->BeatX-4; h = pmenu->Height-1; SetDrMd(rp, COMPLEMENT); SetAPen(rp, 1); RectFill(rp, x, y, x+w, y+h); SetDrMd(rp, old_mode); } } struct BitMap *allocbitmap(width, height, depth) USHORT width, height; UBYTE depth; { struct BitMap *bm; register int i; bm = (struct BitMap *)AllocMem(sizeof(struct BitMap), MEMF_CLEAR); if (bm) { InitBitMap(bm, (long)depth, (long)width, (long)height); for (i = 0; i < depth; i++) { bm->Planes[i] = (PLANEPTR)AllocRaster(width, height); if (!bm->Planes[i]) { freebitmap(bm); return(NULL); } } } return(bm); } void freebitmap(bm) struct BitMap *bm; { register int i; if (bm) { for (i = 0; i < bm->Depth; i++) if (bm->Planes[i]) FreeMem(bm->Planes[i], (long)(bm->BytesPerRow * bm->Rows)); FreeMem(bm, sizeof(struct BitMap)); } }