/* PopMenu code by Helene (Lee) Taran, from the Splines program ** from Fish disk 97 (which replace disk 57). */ #include #include "popmenu.h" /* For some reason, someone forgot to include NewLayerInfo() in functions.h... */ struct Layer_Info *NewLayerInfo(); struct Layer_Info *PopUp_LayerInfo; struct Font *menufont; /* User should call Init_MenuPackage() before trying to pop up any * menus. Will return TRUE if the package has been initialized correctly. * Do not attempt to use this package if this initialization routine * returns FALSE. */ int Init_MenuPackage() { static struct TextAttr MenuFont = {(UBYTE *)"topaz.font",8,0,0}; if (!(menufont = OpenFont(&MenuFont))) return (0); if (!(PopUp_LayerInfo = (struct Layer_Info *)NewLayerInfo())) return (0); return(1); } /* The user should call Close_MenuPackage before exiting their program * to ensure that the menu package releases any memory that it * has allocated for its own use. */ Close_MenuPackage() { CloseFont(menufont); if (PopUp_LayerInfo != NULL) DisposeLayerInfo(PopUp_LayerInfo); } int Init_PopUp_Menu (menu) struct PopUp_Menu *menu; { struct PopUp_Item *item; int longest = 0; int total_height = 0; int i; if (menu == NULL) return(0); item = menu->first_item; while (item != NULL) { /* note: bad things may happen if you allocate an empty string for * your item text. */ item->width = MAX(item->width, FONT_WIDTH * strlen(item->text)); item->left = MAX(0,item->left); longest = MAX(longest, (item->width + item->left)); item->height = MAX(item->height, FONT_HEIGHT); item->top = MAX(0,item->top); total_height += (item->height + item->top); /* re-initialize item-top to be the top relative to the menu top */ item->top = (total_height - item->height); item = item->next; } menu->height = total_height+1; menu->width = MAX(menu->width, longest); /* initialize a bitmap the same size as the required menu image */ /* initialize a raster port to facillitate drawing into the bitmap */ InitBitMap(&(menu->bitmap), (long)menu->depth,(long)menu->width,(long)menu->height); InitRastPort(&(menu->rp)); SetFont(&(menu->rp),menufont); for (i = 0; i < menu->depth; i++) if (!(menu->bitmap.Planes[i] = (PLANEPTR) AllocRaster((long)menu->width, (long)menu->height))) { Dispose_PopUp(menu); return(0); } menu->rp.BitMap = &(menu->bitmap); menu->cr.BitMap = &(menu->bitmap); /* draw the menu outline and background color into its own bitmap */ SetDrMd(&(menu->rp),JAM1); SetAPen(&(menu->rp),(long)menu->area_color); SetOPen(&(menu->rp),(long)menu->outline_color); RectFill(&(menu->rp),0L,0L,menu->width-1L,menu->height-1L); BNDRYOFF(&(menu->rp)); /* turn off the raster port outlining */ item = menu->first_item; /* Now, draw each menu item */ while (item != NULL) { SetAPen(&(menu->rp),(long)item->color); Move(&(menu->rp),(long)item->left,(long)item->top + FONT_BASELINE); Text(&(menu->rp),item->text,(long)strlen(item->text)); item = item->next; } return(1); } /* SelectItem : activates the item located at . Assumes the * is currently active and thus, menu->left and menu->top should represent * the menu's current top,left corner. If there is an item located * at within the menu, then it is made 'active' ...this means * that the item becomes menu->active_item and is highlighted. The * old menu->active_item, if any, is deselected. Note: if you're using * a GIMMEZEROZERO window you'll have to fiddle with the x,y coordinates. */ SelectItem(menu,Window) struct PopUp_Menu *menu; struct Window *Window; { struct PopUp_Item *item; SHORT x = Window->MouseX + Window->LeftEdge; SHORT y = Window->MouseY + Window->TopEdge; item = menu->first_item; x -= menu->left; y -= menu->top; if ((x >= 0) && (x < menu->width) && (y >= 0) && (y < menu->height)) while (item != NULL) { if ((y >= item->top -1) && (y < item->top -1 + item->height)) break; /* we found an active item {yeah yeah...bad style, i know...} */ else item = item->next; } else item = NULL; /* mouse isn't on menu */ if (menu->active_item != item) { /* deselect previously active item */ ComplementItem(menu,menu->active_item,Window); ComplementItem(menu,item,Window); } menu->active_item = item; } /* ComplementItem : complements the given menu item area. Assumes that * the is currently being displayed and thus,the current menu->left * and menu->top represent the current coordinates of the menu's top,left * corner. Assumes the is an popup_item that belongs to * and that its left/top offsets have been initialized. */ ComplementItem(menu,item,Window) struct PopUp_Menu *menu; struct PopUp_Item *item; struct Window *Window; { BYTE old_mode = Window->WScreen->RastPort.DrawMode; if (item == NULL) return; /* do nothing */ SetDrMd(&(Window->WScreen->RastPort), COMPLEMENT); RectFill(&(Window->WScreen->RastPort), (long)menu->left, menu->top + item->top - 1L, menu->left + menu->width -1L, menu->top + item->top + item->height - 2L); SetDrMd(&(Window->WScreen->RastPort), (long)old_mode); } /* PopUp : this is one of those 'do everything' type of functions. It * displays the in the window given its upper left hand * coordinates . It tracks the user's mouse movements, * highlighting the currently active item until the user deselects * the menu by releasing the mouse's select button, at which time, * this function will remove the menu from the display. Returns the * selection_id of the item that was active when the user releases * the select button. Returns 0 if nothing was selected. * Assumes that the has been initialized by a call to * Init_PopUp_Menu and that the user has requested ReportMouse for the * Window in question (ReportMouse makes sure that the Window's MouseX and * MouseY coordinates are kept up to date). */ int PopUp(menu,Window) struct PopUp_Menu *menu; struct Window *Window; { ULONG oldflags; SHORT left, top; left = Window->MouseX; top = Window->MouseY; if (!Inside_Window(left,top,Window)) return(OUTSIDE_WINDOW); if ((menu->width <= Window->Width) && (left + menu->width > Window->Width)) left += Window->Width - (left + menu->width); if ((menu->height <= Window->Height) && (top + menu->height > Window->Height)) top += Window->Height - (top + menu->height); LockLayers(PopUp_LayerInfo); /* SwapBits...() requires that the clipping rectangle's bounds are given * with respect to the screen's 0,0 coordinate but mouse movements * are reported to the Window and are given in terms of the window's * coordinate system so...sLeft and sTop represent the adjustment of * the Window's coordinates to match the screen's coordinates. * Note: if you're using a GIMMEZEROZERO window, you'll have to muck * with the left,top coordinates to get the complementing to work. */ left = (left + Window->LeftEdge) & 0xfff0; top = top + Window->TopEdge; menu->cr.bounds.MinX = left; menu->cr.bounds.MinY = top; menu->cr.bounds.MaxX = left + menu->width -1; menu->cr.bounds.MaxY = top + menu->height -1; SwapBitsRastPortClipRect(Window->RPort,&(menu->cr)); menu->left = left; menu->top = top; SelectItem(menu,Window); /* **oldflags = Window->IDCMPFlags; **ModifyIDCMP(Window,oldflags | MOUSEMOVE); */ ReportMouse (TRUE, Window); /* Start telling us about mouse loc */ while (1) /* wait for the user to select an item and deselect the menu */ { struct IntuiMessage msg, *message; int MouseMoved = FALSE; Wait(1L << Window->UserPort->mp_SigBit); while (message = (struct IntuiMessage *)GetMsg(Window->UserPort)) { msg = *message; /* make a backup copy of message */ ReplyMsg(message); /* reply immediately to prevent deadlock */ if (msg.Class == MOUSEMOVE) /* just keep track of the fact that the mouse moved */ MouseMoved = TRUE; else if ((msg.Class == MOUSEBUTTONS) && (msg.Code == menu->deactivate)) { /* user has deselected the menu */ ReportMouse (FALSE, Window); SelectItem(menu,Window); SwapBitsRastPortClipRect(Window->RPort,&(menu->cr)); UnlockLayers(PopUp_LayerInfo); if (menu->active_item != NULL) return(menu->active_item->selection_id); else return(NOITEM_SELECTED); } /* end of deselection routine */ } /* * at this point the message queue should be empty so the mouse * has stopped for the moment. */ if (MouseMoved) SelectItem(menu,Window); } } Dispose_PopUp(menu) struct PopUp_Menu *menu; { int i; for (i = 0; i < menu->depth; i++) if (menu->bitmap.Planes[i] != NULL) FreeRaster(menu->bitmap.Planes[i], (long)menu->width, (long)menu->height); }