From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from localhost by hawkwind.utcs.toronto.edu with SMTP id <2738>; Wed, 9 Dec 1992 16:34:40 -0500 To: sam-fans Subject: new menuhit.c Date: Wed, 9 Dec 1992 16:34:38 -0500 From: Chris Siebenmann Message-Id: <92Dec9.163440est.2738@hawkwind.utcs.toronto.edu> Apparently I forgot to point this out to the list. Some time back, Mike Haertel (mike@mystix.cs.uoregon.edu) posted a reimplementation of libXg/menuhit.c that did scrolling menus to comp.editors. Since the article has no doubt long since expired (posted 25 Nov), I've attached it here. I've been running this code for some time without problems. - cks /* Article 7064 of comp.editors: Path: utcsri!rpi!usenet.coe.montana.edu!ogicse!cs.uoregon.edu!mystix.cs.uoregon.edu!mike From: mike@mystix.cs.uoregon.edu (Michael John Haertel) Newsgroups: comp.editors Subject: Scrolling menus for Sam Summary: use at your own risk Message-ID: <1992Nov25.081302.26508@cs.uoregon.edu> Date: 25 Nov 92 08:13:02 GMT Article-I.D.: cs.1992Nov25.081302.26508 Sender: news@cs.uoregon.edu (Netnews Owner) Organization: University of Oregon Computer and Information Sciences Dept. Lines: 188 One day in a particularly large source directory I typed "sam *.[ch]", and the resulting menu of buffers exceeded what could fit vertically in my screen. So I decided to implement scrolling menus. Enclosed is a replacement for "menuhit.c" in the libXg/ subdirectory of the Sam distribution. It is a from-scratch implementation by me rather than being a modification of the old menuhit.c. It is a two hour hack so don't expect anything polished. Use it at your own risk... Mike */ #include "libg.h" enum { Scrollbar = 15, /* width of scrollbar in pixels */ Scrollthresh = 12, /* #items before we introduce a scrollbar. */ Border = 1, /* thickness of border on menu */ Inset = 2, /* inset of actual menu */ Vskip = 1, /* space between lines */ }; static char * genitem(Menu *menu, int i) { if (menu->item) return menu->item[i]; else return (*menu->gen)(i); } static Rectangle adjrect(Rectangle r) { if (r.max.x > screen.r.max.x) r = rsubp(r, Pt(r.max.x - screen.r.max.x, 0)); if (r.max.y > screen.r.max.y) r = rsubp(r, Pt(0, r.max.y - screen.r.max.y)); if (r.min.x < screen.r.min.x) r = raddp(r, Pt(screen.r.min.x - r.min.x, 0)); if (r.min.y < screen.r.min.y) r = raddp(r, Pt(0, screen.r.min.y - r.min.y)); return r; } static void highlight(Rectangle menur, int imin, int item) { if (item == -1) return; menur.min.y += (item - imin) * (font->height + Vskip); menur.max.y = menur.min.y + font->height; bitblt(&screen, menur.min, &screen, menur, ~D); } static void drawbar(Rectangle r, int imin, int imax, int nitems, Fcode f) { int h, b, e; Rectangle dorect; if (nitems == 0) return; h = r.max.y - r.min.y; b = (int) ((float) imin / nitems * h); e = (int) ((float) imax / nitems * h); dorect.min.x = r.min.x; dorect.max.x = r.max.x; dorect.min.y = r.min.y + b; dorect.max.y = r.min.y + e; bitblt(&screen, dorect.min, &screen, dorect, f); } static void draw(Menu *m, Rectangle r, Rectangle scrollr, int imin, int imax, int nitems) { char *s; Point p; Rectangle liner; int i; if (Dx(scrollr) != 0) bitblt(&screen, scrollr.min, &screen, Rpt(scrollr.min, r.max), Zero); else bitblt(&screen, r.min, &screen, r, Zero); if (Dx(scrollr) != 0) { bitblt(&screen, scrollr.min, &screen, scrollr, Zero); drawbar(scrollr, imin, imax, nitems, ~D); } liner.min = r.min; liner.max = add(r.min, Pt(Dx(r), font->height)); p = Pt(0, font->height + Vskip); for (i = imin; i < imax; ++i) { s = genitem(m, i); string(&screen, add(liner.min, Pt(Dx(liner) / 2 - strwidth(font, s) / 2, 0)), font, s, F); liner = raddp(liner, p); } } int menuhit(int but, Mouse *mouse, Menu *menu) { Rectangle totalr, menur, scrollr, itemr; int n, w, maxw, imin, imax, newimin, newimax, lasthit, hit; char *s; Bitmap *saveb; Point p; maxw = 0; for (n = 0; genitem(menu, n); ++n) { w = strwidth(font, genitem(menu, n)); if (w > maxw) maxw = w; } if (n == 0) return -1; /* maybe should wait for mouse released? */ lasthit = menu->lasthit; if (lasthit < 0 || lasthit >= n) lasthit = 0; imin = 0; imax = n > Scrollthresh ? Scrollthresh : n; if (imax <= lasthit) imin = lasthit - Scrollthresh / 2, imax = imin + Scrollthresh; if (imax > n) imin -= imax - n, imax -= imax - n; totalr.min = Pt(0,0); totalr.max.x = maxw + (n > Scrollthresh ? Scrollbar : 0) + 2 * Inset; totalr.max.y = (imax - imin) * (font->height + Vskip) - Vskip + 2 * Inset; p.x = (n > Scrollthresh ? Scrollbar : 0) + maxw / 2 + Inset; p.y = (lasthit - imin) * (font->height + Vskip) + (font->height + Vskip) / 2 + Inset; totalr = raddp(totalr, sub(mouse->xy, p)); totalr = adjrect(totalr); menur = inset(totalr, Inset); if (n > Scrollthresh) { scrollr = menur; scrollr.max.x = menur.min.x + Scrollbar - 1; menur.min.x += Scrollbar; } else scrollr.max.x = scrollr.min.x; saveb = balloc(totalr, screen.ldepth); if (!saveb) saveb = &screen; bitblt(saveb, saveb->r.min, &screen, totalr, S); border(&screen, totalr, Border, F); border(&screen, inset(totalr, Border), Inset - Border, Zero); draw(menu, menur, scrollr, imin, imax, n); cursorset(add(totalr.min, p)); highlight(menur, imin, lasthit); for (;;) { *mouse = emouse(); if ((mouse->buttons & (1 << but - 1)) == 0) break; if (ptinrect(mouse->xy, menur)) hit = (mouse->xy.y - menur.min.y) / (font->height + Vskip) + imin; else hit = -1; if (hit != lasthit) { highlight(menur, imin, lasthit); if (hit == imin && imin > 0) { --imin, --imax; draw(menu, menur, scrollr, imin, imax, n); cursorset(add(mouse->xy, Pt(0, font->height + Vskip))); } else if (hit == imax - 1 && imax < n) { ++imin, ++imax; draw(menu, menur, scrollr, imin, imax, n); cursorset(sub(mouse->xy, Pt(0, font->height + Vskip))); } highlight(menur, imin, hit); } if (ptinrect(mouse->xy, scrollr)) { newimin = (float) (mouse->xy.y - scrollr.min.y) / Dy(scrollr) * n; newimax = newimin + Scrollthresh; while (newimax > n) --newimin, --newimax; if (imin != newimin) { imin = newimin; imax = newimax; draw(menu, menur, scrollr, imin, imax, n); } } lasthit = hit; } if (lasthit != -1) menu->lasthit = lasthit; bitblt(&screen, totalr.min, saveb, totalr, S); bfree(saveb); return lasthit; }