/* * Copyright 1992 Eric R. Smith. All rights reserved. * Redistribution is permitted only if the distribution * is not for profit, and only if all documentation * (including, in particular, the file "copying") * is included in the distribution in unmodified form. * THIS PROGRAM COMES WITH ABSOLUTELY NO WARRANTY, NOT * EVEN THE IMPLIED WARRANTIES OF MERCHANTIBILITY OR * FITNESS FOR A PARTICULAR PURPOSE. USE AT YOUR OWN * RISK. */ #include #include #include #include #include #include "xgem.h" #include "twdefs.h" #define FM_COPY 3 /* given a mouse x and y coordinate * (msx, msy) and the x, y coordinate * of a menu bar, find which of the * bar's menus should be displayed * (if any), On return, the (x,y) * coordinate at which the drop-down * should be placed is put in *xp and * *yp */ /* if we're *way* outside of the menu area, * make the menu go away */ #define GOAWAY ((MENU *)-1L) static MENU * whichmenu(bar, barx, bary, msx, msy, xp, yp) MENU *bar; int barx, bary, msx, msy; int *xp, *yp; { MENU *m; int xpixel; int leftx; if (msx < barx || msy < bary) return GOAWAY; if (msy > bary+gl_hbox) return 0; xpixel = barx; m = bar; while(m) { leftx = xpixel; xpixel += gl_wchar * (strlen(m->title)+2); if (msx < xpixel) { *xp = leftx+1; *yp = bary+gl_hbox; return m; } m = m->next; } return 0; } /* * create an object for a drop down menu, with * upper corner (x, y) */ static OBJECT * makemenu(m, x, y) MENU *m; int x, y; { extern int entrylen __PROTO(( ENTRY * )); extern char *entrystr __PROTO(( ENTRY *, int )); OBJECT *popobj; ENTRY *e; int numobjects, wide, i; numobjects = 1; wide = 0; for (e = m->contents; e; e = e->next) { numobjects++; if ( (i = entrylen(e)) > wide) wide = i; } popobj = malloc(numobjects * sizeof(OBJECT)); if (!popobj) return 0; /* first, create the box around the menu */ popobj[0].ob_next = -1; popobj[0].ob_head = 1; popobj[0].ob_tail = numobjects - 1; popobj[0].ob_type = G_BOX; popobj[0].ob_flags = LASTOB; popobj[0].ob_state = NORMAL; popobj[0].ob_spec = 0x00ff1100L; popobj[0].ob_x = x/gl_wchar; popobj[0].ob_y = y/gl_hchar; popobj[0].ob_width = wide; popobj[0].ob_height = numobjects - 1; i = 1; for (e = m->contents; e; e = e->next) { popobj[i].ob_next = (e->next) ? i+1 : 0; popobj[i].ob_head = popobj[i].ob_tail = -1; popobj[i].ob_type = G_STRING; popobj[i].ob_flags = NONE; popobj[i].ob_state = e->state; popobj[i].ob_spec = (long)entrystr(e, wide); if (!popobj[i].ob_spec) return 0; popobj[i].ob_x = 0; popobj[i].ob_y = i - 1; popobj[i].ob_width = wide; popobj[i].ob_height = 1; i++; } popobj[i-1].ob_flags = LASTOB; /* now, fix up the object tree */ for (i = 0; i < numobjects; i++) rsrc_obfix(popobj, i); popobj[0].ob_x = x; popobj[0].ob_y = y; return popobj; } static void freeobj(obj) OBJECT *obj; { int i; for (i = 1; i; i++) { free((void *)obj[i].ob_spec); if (obj[i].ob_flags & LASTOB) break; } free(obj); } #define screen_planes gl_screenplanes static MFDB scr_mfdb; static void savearea(mfdb, x, y, w, h) MFDB *mfdb; int x, y, w, h; { int blitrec[8]; blitrec[0] = x; blitrec[1] = y; blitrec[2] = x + w - 1; blitrec[3] = y + h - 1; blitrec[4] = 0; blitrec[5] = 0; blitrec[6] = w - 1; blitrec[7] = h - 1; /* mfdb->fd_addr is assumed to be set already */ mfdb->fd_w = w; mfdb->fd_h = h; mfdb->fd_wdwidth = ((w+15)/16); mfdb->fd_stand = 0; mfdb->fd_nplanes = screen_planes; hide_mouse(); set_clip(x, y, w, h); vro_cpyfm(vdi_handle, FM_COPY, blitrec, &scr_mfdb, mfdb); show_mouse(); } static void restorearea(mfdb, x, y, w, h) MFDB *mfdb; int x, y, w, h; { int blitrec[8]; blitrec[0] = 0; blitrec[1] = 0; blitrec[2] = w-1; blitrec[3] = h-1; blitrec[4] = x; blitrec[5] = y; blitrec[6] = x+w-1; blitrec[7] = y+h-1; hide_mouse(); set_clip(x, y, w, h); vro_cpyfm(vdi_handle, FM_COPY, blitrec, mfdb, &scr_mfdb); show_mouse(); } /* * deal with user interactions with a drop-down menu bar * (x,y) is the upper left hand corner of the region * containing the bar */ void dropdown_menu(bar, x, y) MENU *bar; int x,y; { OBJECT *popobj = 0; MENU *m, *lastm, *tmpm; ENTRY *e; int i; int w, h; int event, msx, msy, mbutton, mbreturn, keycode, dummy; int curobj = -1; int newobj; unsigned lowbyte, hibyte; int barx, bary; long blitbuf; MFDB blit_mfdb; int newx, newy; int drawn = 0; int winhandle; int winowner; wind_update(BEG_MCTRL); /* check to see that we have the right top window */ wind_get(0, WF_TOP, &winhandle, &winowner, &dummy, &dummy); if (!gl_topwin || gl_topwin->wi_handle != winhandle) { wind_update(END_MCTRL); return; } barx = x; bary = y; msx = msy = 0; event = evnt_multi(MU_M1, 2, 0x0001, 0x0001, 1, msx, msy, 1, 1, 0, 0, 0, 0, 0, 0L, 0L, &msx, &msy, &mbutton, &dummy, &keycode, &mbreturn); wind_get(0, WF_SCREEN, &hibyte, &lowbyte, &dummy, &dummy); blitbuf = ((long)hibyte << 16L) | lowbyte; blit_mfdb.fd_addr = blitbuf; /* * find which (if any) menu should be displayed */ m = whichmenu(bar, barx, bary, msx, msy, &newx, &newy); if (!m || m == GOAWAY) { wind_update(END_MCTRL); return; } lastm = 0; w = h = 0; /* display the tree and interact with it */ for(;;) { if (m != lastm) { if (popobj) { freeobj(popobj); popobj = 0; } if (drawn) { restorearea(&blit_mfdb, x, y, w, h); drawn = 0; } if (!m || (m == GOAWAY)) { break; } lastm = m; x = newx; y = newy; /* create a new object tree */ popobj = makemenu(m, x, y); if (!popobj) { break; } /* "-1" takes care of the bordering box */ x = popobj[0].ob_x - 1; y = popobj[0].ob_y - 1; if (x >= xdesk+wdesk) x = xdesk+wdesk-1; if (y >= ydesk+hdesk) { break; }; w = popobj[0].ob_width + 2; h = popobj[0].ob_height + 2; if (x + w > xdesk + wdesk) { w = xdesk + wdesk - x; } if (y + h > ydesk + hdesk) h = ydesk + hdesk - y; /* save the area we're about to draw over */ savearea(&blit_mfdb, x, y, w, h); /* now actually draw the object */ objc_draw(popobj, 0, 1, x, y, w, h); drawn = 1; } event = evnt_multi(MU_BUTTON|MU_M1|MU_KEYBD, 2, 0x0001, 0x0001, 1, msx, msy, 1, 1, 0, 0, 0, 0, 0, 0L, 0L, &msx, &msy, &mbutton, &dummy, &keycode, &mbreturn); if (event & MU_M1) { if (msx >= x && msx <= x+w && msy >= y && msy <= y+h) { newobj = objc_find(popobj, 0, 1, msx, msy); } else { newobj = -1; tmpm = whichmenu(bar, barx, bary, msx, msy, &newx, &newy); if (tmpm) m = tmpm; else if (msy - y > h + gl_hbox) m = GOAWAY; else if (msx - x > w + gl_wbox) m = GOAWAY; } if (curobj > 0 && curobj != newobj) { objc_change(popobj, curobj, 0, x, y, w, h, NORMAL, 1); } curobj = newobj; if (curobj > 0 && popobj[curobj].ob_state != DISABLED) { objc_change(popobj, curobj, 0,x,y,w,h, SELECTED, 1); } else { curobj = -1; } } if (event & MU_KEYBD) { lowbyte = keycode & 0x00ff; i = 1; for (e = m->contents; e; e = e->next) { if ((lowbyte && e->keycode == lowbyte) || e->keycode == keycode) { curobj = i; break; } else i++; } } if (event & MU_BUTTON) { break; } } /* free memory allocated by `entrystr' */ if (popobj) { freeobj(popobj); } if (drawn) restorearea(&blit_mfdb, x, y, w, h); wind_update(END_MCTRL); if (curobj > 0) { for (e = m->contents; e; e = e->next) { --curobj; if (curobj == 0) { (*e->func)(e->arg); break; } } } } char * menustr(bar) MENU *bar; { static char buf[80]; char *s = buf; buf[0] = 0; while(bar) { strcat(s, " "); strcat(s, bar->title); strcat(s, " "); bar = bar->next; } return buf[0] ? buf : 0; }