/* * tearoff.c * * Straylight TMS Segment * Nice tearoff menu system - impressive even if I do say so myself * * © 1995-1998 Straylight */ /*----- Licensing note ----------------------------------------------------* * * This file is part of Straylight's Steel library. * * Steel is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * Steel is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Steel. If not, write to the Free Software Foundation, * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define dbox__INTERNALS #define _CORE #define _STDAPP #include #include #include "steel.h" #include "string.h" #include "os.h" #include "bbc.h" #include "xtearoff.h" #include "font.h" #include "swis.h" #include "alarm.h" #include "tearSupt/tearSupt.h" /*----- Global defintions -------------------------------------------------*/ static int tearoff__x, tearoff__y; /* Position to open next menu */ static tearoff tearoff__prevLevel = NULL, /* Most recent parent */ tearoff__currentMenu = NULL, /* Current Transient menu */ tearoff__originatingTearoff = NULL, /* Most recent tornoff parent */ tearoff__tornoffList = NULL, /* List of torn off menus */ tearoff__oldHandle = NULL; /* Idle event handle */ static wimp_w tearoff__currentDbox=0; /* Current dbox opened */ static BOOL tearoff__subMenuPending=FALSE, riscos3, /* We are on risos3 */ tearoff__menuActive=TRUE; /* Are menus active */ static int tearoff__alarmTime, /* Time alarm was set */ tearoff__alarmTime2; /* Time 2nd alarm was set */ static tearoff__alarm tearoff__alm; /*----- Icon handles for the tear bar -------------------------------------*/ #define tearIcon__tear 0 #define tearIcon__close 0 #define tearIcon__fold 1 /*----- Function prototypes for cyclic definitions ------------------------*/ static void tearoff__doIdles(void *handle); static void tearoff__windowHandler(wimp_eventstr *e, void *handle); /*----- Tearoff code - Not for the casual observer!! ----------------------*/ static void tearoff_closeMenus(void) { tearSupport_switch(1); wimp_create_menu((wimp_menustr*)-1,0,0); tearSupport_switch(0); } /*----- Window creation routines ----------------------------------------- */ /* --- Create the tear icon in the given window --- */ static void tearoff__createTearIcon(tearoff t /* was wimp_w w [mdw] */) { wimp_w w=t->w; /* [mdw] */ wimp_icreate ict; wimp_i i; #ifdef notdef /* [mdw] */ ict.i.box.x0 = 4; ict.i.box.x1 = 68; ict.i.box.y1 = -4; ict.i.box.y0 = -20; #else /* [mdw] */ ict.i.box.x0 = 0; ict.i.box.x1 = t->width; ict.i.box.y1 = 0; ict.i.box.y0 = -24; #endif /* [mdw] */ ict.i.flags = wimp_IVCENTRE | wimp_IFILLED | wimp_IBTYPE * wimp_BCLICKDEBOUNCE | wimp_ISPRITE | wimp_INDIRECT | (0x30000000) /* [mdw] */; ict.i.data.indirectsprite.name = "tear"; ict.i.data.indirectsprite.spritearea = resspr_area(); ict.i.data.indirectsprite.nameisname = 4; ict.w = w; wimpt_noerr(wimp_create_icon(&ict, &i)); } /* --- Create the close/fold icons in the menu */ static void tearoff__createTornIcons(tearoff t) { wimp_icreate ict; wimp_i i; #ifdef notdef /* [mdw] */ ict.i.box.x0 = 4; ict.i.box.x1 = 70; ict.i.box.y1 = -4; ict.i.box.y0 = -20; #else /* [mdw] */ ict.i.box.x0 = 0; ict.i.box.x1 = (t->width/2)-2; ict.i.box.y1 = 0; ict.i.box.y0 = -24; #endif /* [mdw] */ ict.i.flags = wimp_IVCENTRE | wimp_IFILLED | wimp_IBTYPE * wimp_BCLICKDEBOUNCE | wimp_ISPRITE | wimp_INDIRECT | (0x30000000) /* [mdw] */; ict.i.data.indirectsprite.name = "close"; ict.i.data.indirectsprite.spritearea = resspr_area(); ict.i.data.indirectsprite.nameisname = 4; ict.w = t->w; wimpt_noerr(wimp_create_icon(&ict, &i)); /* wimp_set_icon_state(t->w, i, 0, 0); [mdw] */ /* Now create the fold icon */ #ifdef notdef /* [mdw] */ ict.i.box.x1 = t->width - 4; ict.i.box.x0 = ict.i.box.x1 - 60; ict.i.box.y1 = -4; ict.i.box.y0 = -20; #else /* [mdw] */ ict.i.box.x0 = (t->width/2)+2; ict.i.box.x1 = t->width; ict.i.box.y1 = 0; ict.i.box.y0 = -24; #endif /* [mdw] */ ict.i.flags = wimp_IVCENTRE | wimp_IFILLED | wimp_IBTYPE * wimp_BCLICKDEBOUNCE | wimp_ISPRITE | wimp_INDIRECT | wimp_IRJUST | /* [mdw] */ (0x30000000) /* [mdw] */; ict.i.data.indirectsprite.name = "fold"; ict.i.data.indirectsprite.spritearea = resspr_area(); ict.i.data.indirectsprite.nameisname = 4; ict.w = t->w; wimpt_noerr(wimp_create_icon(&ict, &i)); /* wimp_set_icon_state(t->w, i, 0, 0); [mdw] */ { /* --- start [mdw] --- */ wimp_redrawstr r; int more; r.w=t->w; r.box.x0=0; r.box.x1=t->width; r.box.y0=-24; r.box.y1=0; wimpt_noerr(wimp_update_wind(&r,&more)); while (more) { wimp_setcolour(0); bbc_rectanglefill(r.box.x0-r.scx+t->width/2-2, r.box.y1-r.scy-24, 4,24); wimpt_noerr(wimp_get_rectangle(&r,&more)); } /* --- end [mdw] --- */ } } /* --- Called to calculate the width of a menu */ void tearoff_calculateMenuWidth(tearoff t) { tearoff__item *i; int cnt; int max,w; i=(tearoff__item *)(t+1); max=0; for (cnt=1;cnt<=t->numberOfItems;cnt++,i++) { w=wimpt_stringWidth(i->text); if (w>max) max=w; } t->width=max; i=(tearoff__item *)(t+1); max=0; for (cnt=1;cnt<=t->numberOfItems;cnt++,i++) { if (i->keys) { w=wimpt_stringWidth(i->keys); if (w>max) max=w; } } if (max) max+=16; t->width+=48+16+max; t->keyWidth=max; w=wimpt_stringWidth(t->menuTitle); if (t->widthwidth=w; if (t->width<150 && t->tearoff) t->width=150; } /* --- Creates a window for a menu --- */ static wimp_w tearoff__createWindow(tearoff t) { wimp_wind wind; wimp_icreate ict; wimp_i i; wimp_w w; int extra = (t->tearoff)?tearoff__HEIGHT:0, h; int scy=(bbc_vduvar(bbc_YWindLimit)+1)<width; wind.ex.y0 = -t->numberOfItems * 44 - extra - t->dotted; wind.ex.y1 = 0; h = wind.ex.y1-wind.ex.y0; /* Height */ wind.box.x0 = 0; wind.box.x1 = t->width; if (t->maxHeight && h > t->maxHeight) wind.box.y0 = -t->maxHeight; else wind.box.y0 = -h; wind.box.y1 = 0; wind.scx = 0; wind.scy = 0; wind.minsize = 0; wind.behind = -1; wind.flags = wimp_WMOVEABLE | wimp_WTITLE | wimp_WNEW; t->scrollBar=FALSE; if (!t->folded && (t->maxHeight && h > t->maxHeight || (h+50)>scy)) { wind.flags |= wimp_WVSCR; t->scrollBar=TRUE; } wind.titleflags = wimp_ITEXT | wimp_IHCENTRE | wimp_IHCENTRE | wimp_INDIRECT; wind.workflags = wimp_IBTYPE * wimp_BCLICKDEBOUNCE; wind.colours[wimp_WCTITLEFORE] = 7; wind.colours[wimp_WCTITLEBACK] = 2; wind.colours[wimp_WCWKAREAFORE] = 7; wind.colours[wimp_WCWKAREABACK] = 0; wind.colours[wimp_WCSCROLLOUTER] = 3; wind.colours[wimp_WCSCROLLINNER] = 1; wind.colours[wimp_WCTITLEHI] = 12; wind.colours[wimp_WCRESERVED] = 0; wind.spritearea = (void *)1; wind.title.indirecttext.buffer = t->menuTitle; wind.title.indirecttext.validstring = 0; wind.title.indirecttext.bufflen = strlen(t->menuTitle + 1); wind.nicons = 0; if (wimp_create_wind(&wind, &w)) return -1; t->w=w; /* Create tearoff icon if needed */ if (t->tearoff) { #ifdef notdef /* [mdw] */ /* First the blank bar */ ict.i.box.x0 = 0; ict.i.box.x1 = t->width; ict.i.box.y1 = 0; ict.i.box.y0 = -tearoff__HEIGHT; ict.i.flags = wimp_IVCENTRE | wimp_IFILLED | wimp_IBTYPE * wimp_BIGNORE | wimp_IBACKCOL * 3 | wimp_IFORECOL * 7; ict.i.flags |= wimp_ITEXT; strcpy(ict.i.data.text, ""); ict.w = w; wimpt_noerr(wimp_create_icon(&ict, &i)); #endif /* [mdw] */ /* Now the icons */ if (!t->tornoff) tearoff__createTearIcon(t /* was `w' [mdw] */); else tearoff__createTornIcons(t); } return w; } /*----- Functions to register with dbox -----------------------------------*/ /* --- Registered with dbox so that it can find out where to open dbox --- */ static void tearoff__finder(int *x,int *y) { *x=tearoff__x; *y=tearoff__y; } /* --- Registered with dbox also, so that it can tell me to open a dbox */ static void tearoff__openDbox(wimp_openstr *o) { if (tearoff__subMenuPending) { tearoff__currentDbox = o->w; wimp_open_wind(o); if (!tearoff__currentMenu) tearSupport_opened(wimpt_task()); } } /* --- Registered with dbox so that it knows whether to open the next dbox using above registered function */ static BOOL tearoff__wasSubmenu(void) { return tearoff__subMenuPending; } /*----- The tearoff menu handling routines - Yuk --------------------------*/ /* --- Used to either highlight or unhighlight a menu item. The current status of the icon is checked and no change is made if is doesn't need to be. */ void tearoff_highlightItem(int s, tearoff t, BOOL select) { wimp_icon textIcon,keyIcon; tearoff__item *item; wimp_redrawstr r; int more; if (!s) return; if (!select && s!=t->selected) return; item = (tearoff__item *)(t+1); item+=(s-1); if (item->shaded) return; r.w=t->w; r.box.x1=t->width-24; r.box.x0=24; r.box.y0=item->y; r.box.y1=item->y+44; textIcon.box.y0=item->y; textIcon.box.y1=item->y+44; textIcon.box.x0 = 24; textIcon.box.x1 = 24 + t->width - 48; textIcon.flags = wimp_ITEXT | wimp_IVCENTRE | wimp_IFILLED | wimp_INDIRECT; if (select) textIcon.flags |= wimp_IBACKCOL * 7 | wimp_IFORECOL * 0; else textIcon.flags |= wimp_IBACKCOL * 0 | wimp_IFORECOL * 7; if (item->shaded) textIcon.flags |= wimp_INOSELECT; textIcon.data.indirecttext.buffer = item->text; textIcon.data.indirecttext.validstring = ""; textIcon.data.indirecttext.bufflen = strlen(item->text) + 1; if (item->keys) { keyIcon.box.x1=t->width-24; keyIcon.box.x0=keyIcon.box.x1-t->keyWidth; keyIcon.box.y0=textIcon.box.y0; keyIcon.box.y1=textIcon.box.y1; keyIcon.flags=wimp_ITEXT | wimp_IFILLED | wimp_INDIRECT | wimp_IRJUST; if (select) keyIcon.flags |= wimp_IBACKCOL * 7 | wimp_IFORECOL * 0; else keyIcon.flags |= wimp_IBACKCOL * 0 | wimp_IFORECOL * 7; if (item->shaded) keyIcon.flags |= wimp_INOSELECT; keyIcon.data.indirecttext.buffer=item->keys; keyIcon.data.indirecttext.validstring=""; keyIcon.data.indirecttext.bufflen=strlen(item->keys)+1; } wimp_update_wind(&r,&more); while(more) { wimp_ploticon(&textIcon); if (item->keys) wimp_ploticon(&keyIcon); wimp_get_rectangle(&r,&more); } } /* --- Called to deselect the currently highlighted item in a menu. Here, a selected item is one that has been set up by going over a submenu arrow. */ static void tearoff__deselect(tearoff t) { if (!t) return; tearoff_highlightItem(t->selected, t, FALSE); t->warned = FALSE; t->selected = 0; } /* --- Clean up after pointer has lewft a window etc --- */ static void tearoff__cleanUp(void) { win_removeIdleClaimer(tearoff__doIdles, tearoff__oldHandle); alarm_remove(tearoff__alarmTime,0); alarm_remove(tearoff__alarmTime2,0); tearoff__alm.item=-1; tearoff__menuActive=TRUE; tearoff__oldHandle = NULL; } /* --- Closes the menu passed, and all sub menus off of it. If the menu passed is top of the current transient menu, then TearoffSupport is told to stop giving me button pressed/menu created messages. */ static void tearoff__closeMenuStructure(tearoff t,tearoff nowOn) { tearoff p; /* Close dbox if opened */ if (tearoff__currentDbox) { wimp_close_wind(tearoff__currentDbox); tearoff__currentDbox=0; if (tearoff__prevLevel) { if (tearoff__prevLevel != nowOn) { tearoff__deselect(tearoff__prevLevel); tearoff__prevLevel->selected = FALSE; } tearoff__prevLevel->warned = FALSE; tearoff__prevLevel->sub = NULL; } if (!tearoff__currentMenu) tearSupport_closed(); } if (!t) return; if (t->prev != NULL) { tearoff__prevLevel = NULL; tearoff__originatingTearoff = NULL; if (t->prev->tornoff) { if (t->prev != nowOn) tearoff__deselect(t->prev); else t->prev->warned = FALSE; } } if (t == tearoff__currentMenu) { tearoff__currentMenu = NULL; tearSupport_closed(); /* Stop TearoffSupport Sending messages */ } while (t) { p = t; if (t->tornoff) return; /* Dont' close a torn off menu */ wimpt_noerr(wimp_close_wind(t->w)); wimpt_noerr(wimp_delete_wind(t->w)); win_register_event_handler(t->w, (win_event_handler)0, 0); if (t->warned) p = t->sub; else p = NULL; if (tearoff__oldHandle == t) tearoff__cleanUp(); t->warned = FALSE; t->selected = FALSE; t->prev = FALSE; t->open = FALSE; t->sub = NULL; t->w=0; (t->selectProc)(tearoff_CLOSE, NULL, t->userHandle); t = p; } } /* --- Called to shade a sub menu arrow --- */ #ifdef notdef /* [mdw] */ static void tearoff__shadeSub(tearoff t,int i,BOOL shade) { tearoff__item *item; wimp_redrawstr r; wimp_icon icon; int more; if (!t) return; item=(tearoff__item *)(t+1); item+=(i-1); item->subShaded=shade; if (t->open) { r.w=t->w; r.box.x1=icon.box.x1=t->width; r.box.x0=icon.box.x0=t->width-24; r.box.y0=icon.box.y0=item->y; r.box.y1=icon.box.y1=item->y+44; icon.flags = wimp_IVCENTRE | wimp_IHCENTRE | wimp_IFILLED | wimp_IBACKCOL * 0 | wimp_IFORECOL * 7; if (item->shaded || item->subShaded) icon.flags |= wimp_INOSELECT; if (!riscos3) { icon.flags |= wimp_ITEXT; strcpy(icon.data.text, "\x89"); } else { icon.flags |= wimp_ISPRITE | wimp_ITEXT | wimp_INDIRECT; icon.data.indirecttext.validstring = "s\x89"; icon.data.indirecttext.bufflen = 1; icon.data.indirecttext.buffer = ""; } wimp_update_wind(&r,&more); while(more) { wimp_ploticon(&icon); wimp_get_rectangle(&r,&more); } } } #else #define tearoff__shadeSub(x,y,z) ((void)0) #endif /* --- Called to 'tear' off the given menu --- */ static void tearoff__tearMenu(tearoff t, BOOL close) { wimp_dragstr d; if (t->tornoff) return; t->tornoff = TRUE; tearoff__deselect(t->prev); /* Do a window move drag */ d.window = t->w; d.type = wimp_MOVE_WIND; wimp_drag_box(&d); /* Add it to the torn off list */ t->nextTornoff = tearoff__tornoffList; tearoff__tornoffList = t; if (tearoff__currentMenu==t) { tearoff__currentMenu=NULL; tearSupport_closed(); } /* Close the current menu structure if dragged with select */ if (close) tearoff__closeMenuStructure(tearoff__currentMenu,NULL); /* Shade the sub menu arrow of the previous menu */ if (t->prev) tearoff__shadeSub(t->prev,t->fromItem,TRUE); /* Now we need to alter the tearoff icons at the top of the menu */ wimp_delete_icon(t->w, tearIcon__tear); /* Delete tear icon */ /* Create a close icon - this should be given the same icon number as the tear icon had */ tearoff__createTornIcons(t); } /* --- Used to 'fold up' the given menu */ static void tearoff__foldMenu(tearoff t, BOOL close) { wimp_wstate state; wimp_mousestr m; BOOL sameMenu=FALSE; if (!t) return; if (close) tearoff__closeMenuStructure(tearoff__currentMenu,NULL); /* Close any transient submenus opened from this menu */ if (t->warned) { tearoff__closeMenuStructure(t->sub,NULL); tearoff__deselect(t); } /* Re-open the menu at the right size */ wimp_get_wind_state(t->w, &state); wimp_get_point_info(&m); if (m.w==t->w) sameMenu=TRUE; t->folded = !t->folded; if (t->folded) { if (t->scrollBar) { wimpt_noerr(wimp_close_wind(t->w)); wimpt_noerr(wimp_delete_wind(t->w)); win_register_event_handler(t->w, (win_event_handler)0, 0); if (tearoff__oldHandle==t) tearoff__cleanUp(); tearoff__createWindow(t); state.o.w=t->w; state.o.box.y0=state.o.box.y1-tearoff__HEIGHT; win_register_event_handler(t->w, tearoff__windowHandler, t); wimp_open_wind(&(state.o)); if (sameMenu && m.i>=-1) { win_addIdleClaimer(tearoff__doIdles, 0, t); tearoff__oldHandle = t; } } else { state.o.box.y0=state.o.box.y1-tearoff__HEIGHT; wimp_open_wind(&(state.o)); } } else { int scy=(bbc_vduvar(bbc_YWindLimit)+1)<numberOfItems*44)+t->dotted+tearoff__HEIGHT; if (t->maxHeight && h>t->maxHeight || (h+50)>scy) { wimpt_noerr(wimp_close_wind(t->w)); wimpt_noerr(wimp_delete_wind(t->w)); win_register_event_handler(t->w, (win_event_handler)0, 0); if (tearoff__oldHandle==t) tearoff__cleanUp(); tearoff__createWindow(t); state.o.w=t->w; if (t->maxHeight) state.o.box.y0=state.o.box.y1-t->maxHeight; else state.o.box.y0=state.o.box.y1-tearoff__HEIGHT- (t->numberOfItems*44)-t->dotted; win_register_event_handler(t->w, tearoff__windowHandler, t); win_adjustBox(&state.o); wimp_open_wind(&state.o); if (sameMenu && m.i>=-1) { win_addIdleClaimer(tearoff__doIdles, 0, t); tearoff__oldHandle = t; } } else { state.o.box.y0=state.o.box.y1-tearoff__HEIGHT- (t->numberOfItems*44)-t->dotted; win_adjustBox(&state.o); wimp_open_wind(&(state.o)); } } } /* --- Used to 'un-tear' the given menu - this will be called if it is re-opened as a tranisent of another menu. It is also called in the routine to close a torn off menu */ static wimp_w tearoff__unTearMenu(tearoff t, BOOL update) { wimp_redrawstr r; int more; tearoff ptr = tearoff__tornoffList, prev = NULL; /* Go through the 'tornoff list', if the menu is found then remove it, untear it, and return it's window handle */ while (ptr) { if (ptr == t) { /* remove it from list */ if (!prev) tearoff__tornoffList = t->nextTornoff; else prev->nextTornoff = t->nextTornoff; t->tornoff = FALSE; /* Close any transient menu opened from it */ if (t->warned) { tearoff__closeMenuStructure(t->sub,NULL); tearoff__deselect(t); } /* Unshade the sub menu pointer for the previous menu */ tearoff__shadeSub(t->prev,t->fromItem,FALSE); /* Unfold it if it needs to be */ if (t->folded) tearoff__foldMenu(t, FALSE); /* Update if it is being moved - rather than just closed */ if (update) { wimp_delete_icon(t->w, tearIcon__close); wimp_delete_icon(t->w, tearIcon__fold); /* Replace the tear icon */ tearoff__createTearIcon(t /* was t->w [mdw] */); /* Now redraw top of menu */ r.w = t->w; r.box.x0 = 0; r.box.x1 = t->width; r.box.y1 = 0; r.box.y0 = -tearoff__HEIGHT; wimp_update_wind(&r, &more); while (more) { wimp_get_rectangle(&r, &more); } } return t->w; } prev = ptr; ptr = ptr->nextTornoff; } return (wimp_w)-1; } /* --- Close a torn off menu --- */ static void tearoff__closeTornoff(tearoff t, BOOL close) { if (!t) return; tearoff__unTearMenu(t,FALSE); tearoff__closeMenuStructure(t,NULL); if (tearoff__oldHandle == t) tearoff__cleanUp(); if (close) tearoff__closeMenuStructure(tearoff__currentMenu,NULL); /* Let the user know about it being close */ (t->selectProc)(tearoff_CLOSE,t->selected,t->userHandle); } /* --- Called to automatically open a sub menu --- */ static void tearoff__alarmHandler2(int called_at,void *handle) { tearoff__menuActive=TRUE; } static void tearoff__alarmHandler(int called_at,void *handle) { wimp_wstate state; tearoff t=tearoff__alm.t; tearoff__item *item; int delay,dummy; item=(tearoff__item *)(t+1); item+=(tearoff__alm.item-1); wimp_get_wind_state(t->w, &state); tearoff__x = state.o.box.x1-24; tearoff__y = state.o.box.y1 + tearoff__y; tearoff__prevLevel=t; t->warned = TRUE; if (t->tornoff) tearoff__originatingTearoff = t; /* No current submenu opened */ t->sub = NULL; /* Set up the second alarm */ os_swi3r(XOS_Byte,161,23,0,&dummy,&dummy,&delay); if (delay) { tearoff__menuActive=FALSE; tearoff__alarmTime2=alarm_timenow()+delay*10; alarm_set(tearoff__alarmTime2,tearoff__alarmHandler2,0); } /* If item->subMenu is not FALSE, it contains a menu to be opened automatically */ if (item->subMenu) { tearoff__subMenuPending = TRUE; tearoff_displayMenu(item->sub,0); tearoff__subMenuPending = FALSE; } /* Does the user want a tearoff_SUBMENU message? Note that he can have one even if the menu is opened automatically - unlike menu.c */ if (item->subMenuWarning) { tearoff__subMenuPending = TRUE; (t->selectProc)(tearoff_SUBMENU,t->selected,t->userHandle); tearoff__subMenuPending = FALSE; } } /* --- The biggy - all button events are returned here for dealing with - alter at your own risk. */ static void tearoff__buttons(tearoff t, wimp_mousestr *m) { int itm=0, cnt; BOOL subPointer,justClickShadedArrow=FALSE; wimp_wstate state; tearoff__item *item = (tearoff__item *)(t + 1), *selItem; /* First we deal with any type if button event - including the 'always' type. Don't bother doing anything if it is folded though */ if (!tearoff__menuActive) return; selItem=(tearoff__item *)(t+1); if (t->selected) selItem+=(t->selected-1); if (!t->folded) { int cnt,dummy,delay; BOOL wasDbox=!!tearoff__currentDbox; itm=0; subPointer=FALSE; tearoff__y=0; wimp_get_wind_state(t->w, &state); m->y = m->y+state.o.y-state.o.box.y1; m->x = m->x+state.o.x-state.o.box.x0; for (cnt = 1; cnt <= t->numberOfItems; cnt++, item++) { if (m->y >= item->y && m->y < item->y+44) { itm = cnt; if (m->x >= t->width - 24) subPointer = TRUE; tearoff__y = item->y+44; break; } } item=(tearoff__item *)(t+1); item+=(itm-1); if ((subPointer && (item->subMenu || item->subMenuWarning) && (t->selected != itm)) || (!(subPointer && (item->subMenu || item->subMenuWarning)) && itm!=tearoff__alm.item) && m->i<0) { /* We get here if we are over a sub menu arrow AND we weren't over it before, OR we are not over a sub menu arrow at all AND we are not over an icon. */ /* First close the RISCOS menu using tearoffSupport */ tearoff_closeMenus(); /* If this menu is torn off, then close any transient menu opened at the moment */ if (t->tornoff) tearoff__closeMenuStructure(tearoff__currentMenu, t); /* If there is already a menu opened from this menu, then close it. NB This may already have been closed by the line above - but no harm done. */ if (t->warned) { tearoff__closeMenuStructure(t->sub,t); t->sub = NULL; } /* Horrid hack because I can't do co-routines */ if (wasDbox) { wimp_eventstr e; e.e=wimp_ENULL; wimpt_fake_event(&e); return; } if (t->selected!=itm) { if (t->selected && !selItem->shaded || itm == 0) tearoff_highlightItem(t->selected, t, FALSE); if (itm) tearoff_highlightItem(itm, t, TRUE); t->selected=itm; alarm_remove(tearoff__alarmTime,0); alarm_remove(tearoff__alarmTime2,0); tearoff__alm.item=-1; } if (tearoff__alm.item==-1) { if (riscos3 && itm && (item->subMenu || item->subMenuWarning)) { os_swi3r(XOS_Byte,161,197,0,&dummy,&dummy,&delay); if (delay & 0x80) { os_swi3r(XOS_Byte,161,23,0,&dummy,&dummy,&delay); if (delay) { tearoff__alm.t=t; tearoff__alm.item=itm; tearoff__alarmTime=alarm_timenow()+delay*10; alarm_set(tearoff__alarmTime,tearoff__alarmHandler,0); } } } } /* Correct some variables */ t->warned = FALSE; tearoff__prevLevel = NULL; tearoff__originatingTearoff = NULL; } /* Did we go over the 'icon bar' ? */ if (m->i>=0) { if (t->selected) tearoff_highlightItem(t->selected, t, FALSE); t->selected = 0; if (t->warned) tearoff__closeMenuStructure(t->sub,NULL); t->warned = FALSE; tearoff__alm.item=0; tearoff__prevLevel = NULL; tearoff__originatingTearoff = NULL; } /* We are over an arrow icon */ if (m->i<0 && subPointer && (item->subMenu || item->subMenuWarning) /* && !item->subShaded || (item->subShaded && subPointer && m->bbits) [mdw] */) { /* Get position at which to open next menu */ wimp_get_wind_state(m->w, &state); tearoff__x = state.o.box.x1+2; tearoff__y = state.o.box.y1 + tearoff__y; if (!t->warned) /* This menu isn't opened yet, and/or submenu message hasn't been sent */ { if (item->subShaded && subPointer && m->bbits) justClickShadedArrow=TRUE; tearoff__prevLevel=t; t->warned = TRUE; if (t->tornoff) tearoff__originatingTearoff = t; /* No current submenu opened */ t->sub = NULL; /* If item->subMenu is not FALSE, it contains a menu to be opened automatically */ if (item->subMenu) { tearoff__subMenuPending = TRUE; tearoff_displayMenu(item->sub,0); tearoff__subMenuPending = FALSE; } /* Does the user want a tearoff_SUBMENU message? Note that he can have one even if the menu is opened automatically - unlike menu.c */ if (item->subMenuWarning) { tearoff__subMenuPending = TRUE; (t->selectProc)(tearoff_SUBMENU,t->selected,t->userHandle); tearoff__subMenuPending = FALSE; } } else { /* Since we are here, we know that the menu is already opened, if there is one that is */ if (t->sub) { /* Close any submenus off of the submenu */ if (t->sub->warned) { tearoff__closeMenuStructure(t->sub->sub,NULL); tearoff__deselect(t->sub); } /* And reposition submenu */ wimp_get_wind_state(t->sub->w, &state); state.o.box.x0 = tearoff__x; state.o.box.x1 = tearoff__x + t->sub->width; if (t->sub->tearoff) tearoff__y += tearoff__HEIGHT; state.o.box.y0 = tearoff__y - (state.o.box.y1 - state.o.box.y0); state.o.box.y1 = tearoff__y; state.o.behind=-1; /* Mangle positional data so that it fits on the screen */ win_adjustBox(&(state.o)); /* Top and left to go here */ wimp_open_wind(&state.o); } } } } /* Now we need to deal with actual button presses! */ if (m->bbits && !justClickShadedArrow) { int dummy; /* Was click on tearoff bar */ if (m->i>=0) { if (m->i == tearIcon__tear && !t->tornoff) tearoff__tearMenu(t, m->bbits & 6); else if (m->i == tearIcon__close && t->tornoff) tearoff__closeTornoff(t, m->bbits & 6); else if (m->i == tearIcon__fold && t->tornoff) tearoff__foldMenu(t, m->bbits & 6); } /* No, so select item */ else { int temp=t->selected; /* Don't select a shaded item :-) * (but do close the menu, if we need to [mdw]) */ if (item->shaded) { if (m->bbits & 6) tearoff__closeMenuStructure(tearoff__currentMenu,NULL); return; } /* Flash icon in a similar way to the wimp - cunning */ for (cnt = 1; cnt <= 3; cnt++) { os_byte(19,&dummy,&dummy); /* Wait for Vsync */ os_byte(19,&dummy,&dummy); t->selected=itm; tearoff_highlightItem(itm, t, FALSE); os_byte(19,&dummy,&dummy); os_byte(19,&dummy,&dummy); t->selected=-1; tearoff_highlightItem(itm, t, TRUE); } t->selected=temp; /* Close menu structure if select used */ if (m->bbits & 6) tearoff__closeMenuStructure(tearoff__currentMenu,NULL); /* Tell user about click */ (t->selectProc)(tearoff_SELECTION,itm,t->userHandle); } } return; } /* --- Called on Null events --- */ static void tearoff__doIdles(void *handle) { wimp_mousestr m; tearoff t = (tearoff)handle; handle = handle; wimp_get_point_info(&m); m.bbits=0; tearoff__buttons(t, &m); } /* --- Routine to program the VDU dash pattern --- */ static void tearoff__makeDashPattern(int ptn) { int byte1,byte2; byte1=242; byte2=8; wimpt_noerr(os_byte(163,&byte1,&byte2)); /* Dot-dash length */ bbc_vduq(23,6,ptn,ptn,ptn,ptn,ptn,ptn,ptn,ptn); } /* --- Menu redraw routine --- */ void tearoff__doRedraw(tearoff t, wimp_redrawstr *r) { tearoff__item *i; wimp_icon icon; int c; i = (tearoff__item *)(t+1); wimp_setcolour(7); /* Go through each item, and draw the dotted lines where appropriate */ for (c = 1; c <= t->numberOfItems; c++) { /* Select icon */ icon.box.y1=i->y+44; icon.box.y0=i->y; if (i->selType != tearoff_NONE) { icon.box.x0=0; icon.box.x1=24; icon.flags=wimp_IVCENTRE | wimp_IHCENTRE | wimp_IFILLED | wimp_IBACKCOL * 0 | wimp_IFORECOL * 7; if (i->shaded) icon.flags |= wimp_INOSELECT; if (!riscos3 && i->selType == tearoff_TICKED) { icon.flags |= wimp_ITEXT; strcpy(icon.data.text, "\x80"); } if (riscos3 && i->selType == tearoff_TICKED) { icon.flags |= wimp_ISPRITE | wimp_INDIRECT; icon.data.indirectsprite.name = "\x80"; icon.data.indirectsprite.spritearea = (sprite_area *)1; icon.data.indirectsprite.nameisname = 4; } if (i->selType == tearoff_RADIOED) { icon.flags |= wimp_ITEXT; strcpy(icon.data.text, "\x8F"); } wimpt_noerr(wimp_ploticon(&icon)); } /* Main text part */ icon.box.x0 = 24; icon.box.x1 = 24 + t->width - 48; if (i->keys) icon.box.x1-=16*(strlen(i->keys) + 1); icon.flags = wimp_ITEXT | wimp_IVCENTRE | wimp_IFILLED | wimp_INDIRECT; if (t->selected == c && !i->shaded) icon.flags |= wimp_IBACKCOL * 7 | wimp_IFORECOL * 0; else icon.flags |= wimp_IBACKCOL * 0 | wimp_IFORECOL * 7; if (i->shaded) icon.flags |= wimp_INOSELECT; icon.data.indirecttext.buffer = i->text; icon.data.indirecttext.validstring = ""; icon.data.indirecttext.bufflen = strlen(i->text) + 1; wimpt_noerr(wimp_ploticon(&icon)); if (i->keys) { /* Print the control key short cut - right justified of course! */ icon.box.x0=t->width-24-t->keyWidth; icon.box.x1=t->width-24; icon.flags=wimp_ITEXT | wimp_IFILLED | wimp_INDIRECT | wimp_IRJUST; if (t->selected==c && !i->shaded) icon.flags |= wimp_IBACKCOL * 7 | wimp_IFORECOL * 0; else icon.flags |= wimp_IBACKCOL * 0 | wimp_IFORECOL * 7; if (i->shaded) icon.flags |= wimp_INOSELECT; icon.data.indirecttext.buffer=i->keys; icon.data.indirecttext.validstring=""; icon.data.indirecttext.bufflen=strlen(i->keys)+1; wimpt_noerr(wimp_ploticon(&icon)); } /* Sub Arrow */ if (i->subMenu || i->subMenuWarning) { icon.box.x0 = t->width - 24; icon.box.x1 = t->width; icon.flags = wimp_IVCENTRE | wimp_IHCENTRE | wimp_IFILLED | wimp_IBACKCOL * 0 | wimp_IFORECOL * 7; if (i->shaded /* || i->subShaded [mdw] */) icon.flags |= wimp_INOSELECT; if (!riscos3) { icon.flags |= wimp_ITEXT; strcpy(icon.data.text, "\x89"); } else { icon.flags |= wimp_ISPRITE | wimp_ITEXT | wimp_INDIRECT; icon.data.indirecttext.validstring = "s\x89"; icon.data.indirecttext.bufflen = 1; icon.data.indirecttext.buffer = ""; } wimpt_noerr(wimp_ploticon(&icon)); } if (i->dotted) { bbc_move(r->box.x0,r->box.y1+i->y-12-r->scy); /* Plot the dotted line */ tearoff__makeDashPattern(0xf0); wimp_setcolour(7); bbc_plot(bbc_DottedBoth+bbc_DrawRelFore,t->width,0); } i++; } } /* --- Perform the redraw loop --- */ static void tearoff__redrawMenu(tearoff t) { wimp_redrawstr r; int more; r.w = t->w; wimp_redraw_wind(&r, &more); while (more) { tearoff__doRedraw(t, &r); wimp_get_rectangle(&r, &more); } } /* --- Called to re-open the window - used when the window flags have changed, and for things such as titles changing */ void tearoff_rebuildMenu(tearoff t) { wimp_wstate state; BOOL hadIdle=FALSE; wimp_get_wind_state(t->w, &state); if (tearoff__oldHandle==t) { tearoff__cleanUp(); hadIdle=TRUE; } wimpt_noerr(wimp_close_wind(t->w)); wimpt_noerr(wimp_delete_wind(t->w)); win_register_event_handler(t->w, (win_event_handler)0, 0); tearoff__createWindow(t); state.o.w=t->w; state.o.box.x1=state.o.box.x0+t->width; if (t->maxHeight) state.o.box.y0=state.o.box.y1-t->maxHeight; else state.o.box.y0=state.o.box.y1-tearoff__HEIGHT- (t->numberOfItems*44)-t->dotted; wimp_open_wind(&(state.o)); win_register_event_handler(t->w, tearoff__windowHandler, t); if (hadIdle) { win_addIdleClaimer(tearoff__doIdles,0,t); tearoff__oldHandle=t; } } /* --- Adds/Removes a scrollbar from a menu --- */ static void tearoff__checkForScrollBar(tearoff t) { int h; wimp_wstate state; int scy=(bbc_vduvar(bbc_YWindLimit)+1)<folded) return; /* Don't do anything to a folded menu */ h = t->numberOfItems * 44 + t->dotted; if (t->tearoff) h += tearoff__HEIGHT; h += 50; /* Leave room for title bar */ if (t->scrollBar && h <= scy && (!t->maxHeight || (t->maxHeight && h < t->maxHeight))) { /* remove scroll bar */ wimp_get_wind_state(t->w, &state); wimpt_noerr(wimp_close_wind(t->w)); wimpt_noerr(wimp_delete_wind(t->w)); win_register_event_handler(t->w, (win_event_handler)0, 0); tearoff__createWindow(t); state.o.w=t->w; state.o.box.y1= state.o.box.y0+(t->numberOfItems*44+t->dotted)+ ((t->tearoff)?tearoff__HEIGHT:0); win_register_event_handler(t->w, tearoff__windowHandler, t); wimp_open_wind(&(state.o)); return; } if ((!t->scrollBar && h > scy) || wimpt_justChangedMode()) { /* Add scroll bar */ wimp_get_wind_state(t->w, &state); wimpt_noerr(wimp_close_wind(t->w)); wimpt_noerr(wimp_delete_wind(t->w)); win_register_event_handler(t->w, (win_event_handler)0, 0); tearoff__createWindow(t); state.o.w=t->w; state.o.box.x1=state.o.box.x0+t->width; wimp_open_wind(&(state.o)); win_register_event_handler(t->w, tearoff__windowHandler, t); return; } } /* --- Window handler for my psuedo-menus --- */ static void tearoff__windowHandler(wimp_eventstr *e, void *handle) { tearoff t = (tearoff)handle; wimp_mousestr m; BOOL ptrOverThisWindow=FALSE; switch (e->e) { case wimp_EOPEN: if (wimpt_justChangedMode()) { wimp_get_point_info(&m); if (m.w==t->w) ptrOverThisWindow=TRUE; tearoff__checkForScrollBar(t); if (ptrOverThisWindow && m.i>=-1 && !tearoff__oldHandle) { win_addIdleClaimer(tearoff__doIdles, 0, t); tearoff__oldHandle = t; } } else wimpt_noerr(wimp_open_wind(&e->data.o)); break; case wimp_EREDRAW: tearoff__redrawMenu(t); break; case wimp_EBUT: tearoff__buttons(t,&e->data.but.m); break; case wimp_EPTRENTER: if (tearoff__oldHandle) tearoff__cleanUp(); win_addIdleClaimer(tearoff__doIdles, 0, t); tearoff__oldHandle = t; break; case wimp_EPTRLEAVE: if (tearoff__oldHandle) tearoff__cleanUp(); if (!t->warned && t->selected) { tearoff_highlightItem(t->selected, t, FALSE); t->selected=0; } break; case wimp_ESEND: case wimp_ESENDWANTACK: switch (e->data.msg.hdr.action) { case wimp_MHELPREQUEST: { wimp_wstate state; int x,y,cnt; tearoff__item *item; if (e->data.msg.data.helprequest.m.i>=0) { help_startHelp(); if (e->data.msg.data.helprequest.m.i==1 && !t->tornoff) help_addLine("Drag to tear off this submenu"); if (e->data.msg.data.helprequest.m.i==1 && t->tornoff) help_addLine("Click to close this menu"); if (e->data.msg.data.helprequest.m.i==2) { if (t->folded) help_addLine("Click to un-fold this menu"); else help_addLine("Click to fold this menu"); } help_endHelp(); return; } wimp_get_wind_state(t->w,&state); x=e->data.msg.data.helprequest.m.x+state.o.x-state.o.box.x0; y=e->data.msg.data.helprequest.m.y+state.o.y-state.o.box.y1; item=(tearoff__item *)(t+1); for (cnt = 1; cnt <= t->numberOfItems; cnt++, item++) { if (y >= item->y && y < item->y+44) { (t->selectProc)(tearoff_HELP,cnt,t->userHandle); return; } } } } default: break; } } /* --- Called to create a menu or a sub menu --- */ BOOL tearoff_displayMenu(tearoff t1,void *handle) { wimp_w w; wimp_mousestr m; wimp_wstate state; int height; wimp_get_point_info(&m); /* If the window is not already torn off, then create the window for it */ if (w = tearoff__unTearMenu(t1, TRUE), w == -1) { if (!t1->open) { w = tearoff__createWindow(t1); if (w == -1) return FALSE; win_register_event_handler(w, tearoff__windowHandler, t1); } else w=t1->w; } if (handle) t1->userHandle=handle; wimp_get_wind_state(w, &state); height = state.o.box.y1 - state.o.box.y0; state.o.behind=-1; if (t1->tearoff) tearoff__y += tearoff__HEIGHT; if (!tearoff__subMenuPending || !tearoff__currentMenu) { /* Here it must be a new menu, not a sub menu of a transient menu */ tearoff__closeMenuStructure(tearoff__currentMenu,NULL); t1->prev = NULL; if (tearoff__originatingTearoff && tearoff__subMenuPending) { /* It is being opened from a torn off menu */ t1->prev = tearoff__originatingTearoff; state.o.box.x0 = tearoff__x; state.o.box.x1 = tearoff__x + t1->width; state.o.box.y0 = tearoff__y - height; state.o.box.y1 = tearoff__y; tearoff__originatingTearoff->sub=t1; t1->fromItem=t1->prev->selected; } else { /* Open over the pointer */ state.o.box.x0 = m.x-64; state.o.box.x1 = state.o.box.x0 + t1->width; state.o.box.y0 = m.y - height; state.o.box.y1 = m.y; } tearoff__currentMenu = t1; /* Tell TearoffSupport to send me messages */ tearSupport_opened(wimpt_task()); } else { /* It is a sub menu of a transient menu */ t1->prev = tearoff__prevLevel; state.o.box.x0 = tearoff__x; state.o.box.x1 = tearoff__x + t1->width; state.o.box.y0 = tearoff__y - height; state.o.box.y1 = tearoff__y; tearoff__prevLevel->sub=t1; t1->fromItem=t1->prev->selected; } t1->open=TRUE; win_adjustBox(&(state.o)); wimp_open_wind(&(state.o)); return TRUE; } void tearoff_displayAt(tearoff t,void *handle,int x,int y) { wimp_w w; wimp_wstate state; int height; /* If the window is not already torn off, then create the window for it */ if (w = tearoff__unTearMenu(t, TRUE), w == -1) { if (!t->w) { w = tearoff__createWindow(t); if (w == -1) return; win_register_event_handler(w, tearoff__windowHandler, t); } else w=t->w; } if (handle) t->userHandle=handle; tearoff__closeMenuStructure(tearoff__currentMenu,NULL); t->prev = NULL; wimp_get_wind_state(w, &state); height = state.o.box.y1 - state.o.box.y0; state.o.box.x0 = x; state.o.box.x1 = x + t->width; state.o.box.y0 = y - height; state.o.box.y1 = y; tearoff__currentMenu = t; tearSupport_opened(wimpt_task()); t->open=TRUE; win_adjustBox(&(state.o)); wimp_open_wind(&(state.o)); } int tearoff_height(tearoff t) { int height=0; height+=t->numberOfItems*44+t->dotted; if (t->tearoff) height+=tearoff__HEIGHT; return height; } /* --- Used to receive messages from TearoffSupport */ static BOOL unknown_event(wimp_eventstr *e, void *handle) { win_event_handler eh = 0; handle = handle; switch(e->e) { case wimp_ESEND: case wimp_ESENDWANTACK: switch (e->data.msg.hdr.action) { case wimp_MMODECHANGE: if (tearoff__oldHandle) tearoff__cleanUp(); return FALSE; case 0x4a340: /* Button pressed message - close any transient menus iff button click was not in one of my menus */ if (e->data.msg.data.helprequest.m.w == tearoff__currentDbox) return TRUE; win_read_eventhandler(e->data.msg.data.helprequest.m.w, &eh, &handle); if (eh != tearoff__windowHandler) tearoff__closeMenuStructure(tearoff__currentMenu,NULL); return TRUE; case 0x4a341: /* A close menu message - close menus regardless. eg. escape pressed or wimp menu opened */ tearoff__closeMenuStructure(tearoff__currentMenu,NULL); return TRUE; } } return FALSE; } /* --- Some Functions the give functionality to user of library */ void tearoff_closeMenu(tearoff t) { if (t->open) { if (!t->tornoff) tearoff__closeMenuStructure(t,NULL); else tearoff__closeTornoff(t,FALSE); } } static void tearoff__defaultmidb(wimp_w w, int made, void *handlea, void *handleb, void *handlec) { if (w==win_ICONBAR) { wimp_mousestr m; wimp_get_point_info(&m); tearoff_displayAt((tearoff)handlea, 0, m.x-64, 96+tearoff_height((tearoff)handlea)); } else tearoff_displayMenu((tearoff)handlea,0); } BOOL tearoff_attachMenu(wimp_w w,tearoff t,void *handle) { if (handle) t->userHandle=handle; return (event_attachMidbHandler(w, tearoff__defaultmidb, TRUE, (void *)t, NULL, NULL )); } void tearoff_init(void) { static BOOL initialised=FALSE; if (initialised) return; tearSupport_init(); riscos3=(wimpt_getVersion()>=300); win_add_unknown_event_processor(unknown_event, 0); dbox__rms(tearoff__finder,tearoff__wasSubmenu,tearoff__openDbox); alarm_init(); tearoff__alm.item=-1; initialised=TRUE; }