/* * Buttons * provides handling for clever buttons and things. * * v. 1.00 (23 July 1993) * * © 1993-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. */ #include "wimp.h" #include "wimpt.h" #include "buttons.h" #include "bbc.h" #include "win.h" #include "werr.h" #include #include "colourtran.h" #include "msgs.h" #include "mem.h" #include "alarm.h" #include #include typedef struct { /* --- Information from the caller --- */ dbox d; dbox_field f; dbox wd; dbox_field wf; int diff; buttons_arrowProc p; void *handle; /* --- Other information about the arrow --- */ int rate; BOOL done; } buttons__arrowstr; /* * A structure that says everything necessary about a slider */ typedef struct { dbox d; wimp_i i; int max; int *val; int colour; BOOL isVertical; int slideMax; int iwidth; int o; buttons_sliderHandler proc; void *handle; BOOL dragOver; int lastLen; } buttons__sliderstr; static BOOL buttons__testMouse=TRUE; /* * void buttons_arrowClick * ( * dbox d, * dbox_field f, * int min, * int max, * int increment, * BOOL wrap * ) * * Use * Increases (or decreases) the value in a writable box. If the last event * was a click with the adjust button, the increment is made negative (i.e. * 1 becomes -1, and -1 becomes 1), following the standard convention. The * value will not be allowed to exceed the limits passed. If you don't * want limits, make max and min the same. Optionally, you can make the * field's value 'wrap around' if it goes out of range. * * Parameters * dbox d == the dbox handle * dbox_field f == the field number * int min == the lowest allowable value for the field * int max == the highest allowable value for the field * int increment == what to add to the value. This may be negative. * BOOL wrap == wrap around or not */ void buttons_arrowClick ( dbox d, dbox_field f, int min, int max, int increment, BOOL wrap ) { int value=dbox_getNumeric(d,f); if (buttons__testMouse && dbox_wasAdjustClick()) increment=-increment; value+=increment; if (max>min) { if (value>max) value=wrap ? min : max; if (valuemin,sa->max,diff,sa->wrap); } /* * void buttons__arrowAlarm(int at,void *handle) * * Use * Handles an alarm for the arrow handlers: we use alarms to handle the * auto-repeat on the arrows more easily. * * Parameters * int at == when this alarm was meant to happen * void *handle == information about the arrow handling */ static void buttons__arrowAlarm(int at,void *handle) { buttons__arrowstr *a=handle; int missed; /* --- Call the user function, and get a call-back --- * * * We resynch to the original time, to try to make up for any major * problems, in case the repeat rate is very slow. */ (a->p)(a->wd,a->wf,a->diff,a->handle); missed=(alarm_timenow()-at)/a->rate; alarm_set(at+(missed+1)*a->rate,buttons__arrowAlarm,a); } /* * BOOL buttons__arrowUnknowns(wimp_eventstr *e,void *handle) * * Use * Picks up unknown events for the duration of an arrow operation. It is * used to detect the raising of the mouse buttons by picking up the * WIMP drag event */ static BOOL buttons__arrowUnknowns(wimp_eventstr *e,void *handle) { BOOL handled=FALSE; buttons__arrowstr *a=handle; switch (e->e) { case wimp_EUSERDRAG: a->done=TRUE; win_remove_unknown_event_processor(buttons__arrowUnknowns,a); handled=TRUE; break; } return (handled); } /* * void buttons_arrow(dbox d, * dbox_field f, * dbox wd, * dbox_field wf, * buttons_arrowProc p, * int diff, * void *handle) * * Use * Handles a click on an arrow button. It constrains the mouse pointer * so it doesn't drift away annoyingly, and presses the icon in (by * selecting it, so set up the sprites properly) while it's being clicked, * with nary a flicker in sight. It simulates auto-repeat on the button, * so don't use real auto-repeat buttons. * * Parameters * dbox d,dbox_field f == the icon that was clicked * dbox wd,dbox_field wf == the (writable) icon that is passed to p * buttons_arrowProc p == a procedure to call for each `click' on the * button. It may call buttons_arrowClick to bump the field. If you pass * 0, a default function is called which just bumps the icon. handle must * point to a buttons_simpleArrow structure filled in correctly. * int diff == the difference to add to the icon (passed to p). The sign * is toggled if the click was with the adjust button. * void *handle == a handle to pass to p */ void buttons_arrow(dbox d, dbox_field f, dbox wd, dbox_field wf, buttons_arrowProc p, int diff, void *handle) { buttons__arrowstr ar; wimp_mousestr m; int rate=0,delay=0; int read_osbyte; wimp_dragstr drag; /* --- Handle adjust-clicks in time-honoured fashion --- */ if (dbox_wasAdjustClick()) diff=-diff; /* --- Start a drag to constrain the mouse --- */ wimpt_noerr(wimp_get_point_info(&m)); drag.window=dbox_syshandle(d); drag.type=wimp_USER_HIDDEN; drag.parent.x0=drag.parent.x1=m.x; drag.parent.y0=drag.parent.y1=m.y; wimpt_noerr(wimp_drag_box(&drag)); /* --- Read auto-repeat information --- */ read_osbyte=255; os_byte(196,&delay,&read_osbyte); read_osbyte=255; os_byte(197,&rate,&read_osbyte); /* --- Fill in the structure --- */ ar.d=d; ar.f=f; ar.wd=wd; ar.wf=wf; ar.p=p ? p : buttons__simpleArrows; ar.diff=diff; ar.handle=handle; ar.rate=rate; ar.done=FALSE; /* --- Perform an initial `click' --- */ dbox_selecticon(d,f,TRUE); buttons__testMouse=FALSE; ar.p(wd,wf,diff,handle); /* --- Set the alarm up to do the next one --- */ alarm_set(alarm_timenow()+delay,buttons__arrowAlarm,&ar); win_add_unknown_event_processor(buttons__arrowUnknowns,&ar); /* --- Wait until the user stops clicking --- */ while (!ar.done) event_process(); /* --- Tidy everything away again --- */ dbox_selecticon(d,f,FALSE); buttons__testMouse=TRUE; alarm_removeall(&ar); } /* * BOOL buttons_insertChar(dbox d,int chcode,char min,char max) * * Use * This function inserts the character specified into the writable field * containing the caret. Useful if you want to handle input yourself so * you can update things (such as sliders) in deending on the input. It * will insure that the character is within the limits given (invalid * limits will cause a default of 32-255 to be used). * * Parameters * dbox d == dbox handle * int chcode == the character, as returned from event_process(). * char min == minimum character allowable. * char max == maximum character allowable. * * Returns * Whether it inserted the character or not. */ BOOL buttons_insertChar(dbox d,int chcode,char min,char max) { wimp_caretstr c; char field[255]; int i; int len; if (min>=max) { min=32; max=255; } if (chcodemax) return (FALSE); wimpt_noerr(wimp_get_caret_pos(&c)); c.height=-1; dbox_getfield(d,c.i,field,254); len=strlen(field); for (i=len;i>=c.index;i--) field[i+1]=field[i]; field[c.index]=chcode; c.index++; dbox_setfield(d,c.i,field); wimpt_noerr(wimp_set_caret_pos(&c)); return (TRUE); } /* * void buttons__rect(int x1,int y1,int x2,int y2,int colour) * * Use * Gagdy veneer for bbc_rectanglefill * * Parameters * int x1 == left side * int y1 == bottom * int x2 == right side * int y2 == top * int colour == the colour to use */ static void buttons__rect(int x1,int y1,int x2,int y2,int colour) { int w,h; wimpt_noerr(wimp_setcolour(colour)); w=x2-x1; h=y2-y1; if (w && h) wimpt_noerr(bbc_rectanglefill(x1,y1,w,h)); } /* * void buttons__doRdrSlider * ( * wimp_w w, * wimp_icon *icn, * int slideLen, * int colour, * BOOL isVertical * ) * * Use * Redraws a slider with the specified length. * * Parameters * wimp_w w == the window handle * wimp_icon *icn == pointer to the icon definition * int slideLen == the length to draw the slider * int colour == colour of the slider * BOOL isVertical == whether the slider is vertical */ static void buttons__doRdrSlider ( wimp_w w, wimp_icon *icn, int slideLen, int colour, BOOL isVertical ) { wimp_wstate state; int bordCol; int backg; int ox,oy; wimpt_noerr(wimp_get_wind_state(w,&state)); bordCol=(((int)icn->flags)>>24) & 15; backg=(((int)icn->flags)>>28) & 15; ox=state.o.box.x0-state.o.x; oy=state.o.box.y1-state.o.y; if (isVertical) { buttons__rect ( icn->box.x0+ox , icn->box.y0+oy , icn->box.x1+ox , slideLen+icn->box.y0+oy , colour ); buttons__rect ( icn->box.x0+ox , icn->box.y0+oy+slideLen , icn->box.x1+ox , icn->box.y1+oy , backg ); wimpt_noerr(wimp_setcolour(bordCol)); wimpt_noerr(bbc_move(ox+icn->box.x0,oy+icn->box.y0+slideLen)); wimpt_noerr(bbc_draw(ox+icn->box.x1,oy+icn->box.y0+slideLen)); } else { buttons__rect ( icn->box.x0+ox , icn->box.y0+oy , slideLen+icn->box.x0+ox , icn->box.y1+oy , colour ); buttons__rect ( icn->box.x0+ox+slideLen , icn->box.y0+oy , icn->box.x1+ox , icn->box.y1+oy , backg ); wimpt_noerr(wimp_setcolour(bordCol)); wimpt_noerr(bbc_move(ox+icn->box.x0+slideLen,oy+icn->box.y0)); wimpt_noerr(bbc_draw(ox+icn->box.x0+slideLen,oy+icn->box.y1)); } } /* * void buttons_redrawSlider * ( * dbox d, * wimp_i i, * int max, * int val, * int colour, * BOOL isVertical * ) * * Use * Draws a slider in the specified icon * * Parameters * dbox d == dbox handle * wimp_i icon == the icon we're dealing with * int max == the maximum value you want * int val == the current value of the slider * int colour == the WIMP colour for the slider * BOOL isVertical == TRUE if the slider is vertical */ void buttons_redrawSlider ( dbox d, wimp_i i, int max, int val, int colour, BOOL isVertical ) { wimp_w w=dbox_syshandle(d); wimp_icon icn; int slideLen,slideMax; wimpt_noerr(wimp_get_icon_info(w,i,&icn)); icn.box.x0+=wimpt_dx(); icn.box.y0+=wimpt_dy(); icn.box.x1-=2*wimpt_dx(); icn.box.y1-=2*wimpt_dy(); slideMax=isVertical ? icn.box.y1-icn.box.y0 : icn.box.x1-icn.box.x0; slideLen=(val*slideMax)/max; if (slideLen>slideMax) slideLen=slideMax; buttons__doRdrSlider(w,&icn,slideLen,colour,isVertical); } /* * void buttons_updateSlider * ( * dbox d, * wimp_i i, * int max, * int val, * int colour, * BOOL isVertical * ) * * Use * Redraws a slider in the specified icon * * Parameters * dbox d == dbox handle * wimp_i icon == the icon we're dealing with * int max == the maximum value you want * int val == the current value of the slider * int colour == the WIMP colour for the slider * BOOL isVertical == TRUE if the slider is vertical */ void buttons_updateSlider ( dbox d, wimp_i i, int max, int val, int colour, BOOL isVertical ) { wimp_w w=dbox_syshandle(d); wimp_icon icn; wimp_redrawstr r; BOOL more; wimpt_noerr(wimp_get_icon_info(w,i,&icn)); r.w=w; r.box.x0=icn.box.x0; r.box.y0=icn.box.y0; r.box.x1=icn.box.x1; r.box.y1=icn.box.y1; wimpt_noerr(wimp_update_wind(&r,&more)); while (more) { buttons_redrawSlider(d,i,max,val,colour,isVertical); wimpt_noerr(wimp_get_rectangle(&r,&more)); } } /* * void buttons__doSlide(void *handle) * * Use * This is the routine wot keeps everything going during the drag. * * Parameters * void *handle == pointer to the buttons__sliderstr running the show */ static void buttons__doSlide(void *handle) { wimp_mousestr m; int nVal; buttons__sliderstr *s=(buttons__sliderstr *)handle; int dist,half; wimp_w w=dbox_syshandle(s->d); wimp_icon icn; wimp_redrawstr r; BOOL more; int len; wimpt_noerr(wimp_get_point_info(&m)); half=s->slideMax/s->max/2; len=s->isVertical ? m.y-s->o : m.x-s->o; dist=len+half; nVal=dist*s->max/s->slideMax; if ((s->slideMax)>=(s->max)*2*(s->isVertical ? wimpt_dy() : wimpt_dx())) len=nVal*s->slideMax/s->max; if (nVal>s->max) nVal=s->max; if (nVal<0) nVal=0; if (len!=s->lastLen) { *(s->val)=nVal; s->lastLen=len; wimpt_noerr(wimp_get_icon_info(w,s->i,&icn)); icn.box.x0+=wimpt_dx(); icn.box.y0+=wimpt_dy(); icn.box.x1-=wimpt_dx(); icn.box.y1-=wimpt_dy(); r.w=w; r.box.x0=icn.box.x0; r.box.y0=icn.box.y0; r.box.x1=icn.box.x1; r.box.y1=icn.box.y1; wimpt_noerr(wimp_update_wind(&r,&more)); icn.box.x1-=wimpt_dx(); icn.box.y1-=wimpt_dy(); while (more) { buttons__doRdrSlider(w,&icn,len,s->colour,s->isVertical); wimpt_noerr(wimp_get_rectangle(&r,&more)); } if (s->proc) (s->proc)(s->d,s->i,nVal,s->handle); } } /* * BOOL buttons__ukEvents(wimp_eventstr *e,void *handle) * * Use * This routine just grabs the drag event and closes off the drag if it's * over. * * Parameters * wimp_eventstr *e == a pointer to the event * void *handle == pointer to the current slider * * Returns * Whether it handled the event or not. */ static BOOL buttons__ukEvents(wimp_eventstr *e,void *handle) { BOOL handled=FALSE; buttons__sliderstr *s=(buttons__sliderstr *)handle; switch (e->e) { case wimp_EUSERDRAG: s->dragOver=TRUE; win_remove_idle_claimer(buttons__doSlide,handle); handled=TRUE; win_remove_unknown_event_processor(buttons__ukEvents,handle); mem_free(s); break; } return (handled); } /* * void buttons_slideSlider * ( * dbox d, * wimp_i i, * int max, * int *val, * int colour, * BOOL isVertical, * buttons_sliderHandler proc, * void *handle * ) * * Use * This routine just neatly handles the slider totally. Just pass it the * parameters it needs, and let it get on with it. * * Parameters * dbox d == the dbox * wimp_i i == the icon number * int max == the maximum slider value * int *val == a pointer to the slider value (updated as we go along) * int colour == the colour for the slider * BOOL isVertical == whether the slider is vertical * buttons_sliderHandler proc == a slider handler, which can update, say a * a writable field as the drag takes place * void *handle == a handle to pass the routine */ void buttons_slideSlider ( dbox d, wimp_i i, int max, int *val, int colour, BOOL isVertical, buttons_sliderHandler proc, void *handle ) { wimp_w w=dbox_syshandle(d); wimp_wstate state; wimp_icon icn; int ox,oy; buttons__sliderstr *s; wimp_dragstr drag; wimp_mousestr m; s=(buttons__sliderstr *)mem_alloc(sizeof(buttons__sliderstr)); if (!s) { werr(FALSE,msgs_lookup("buttonsNM:Not enough memory for slider.")); return; } wimpt_noerr(wimp_get_wind_state(w,&state)); wimpt_noerr(wimp_get_icon_info(w,i,&icn)); icn.box.x0+=wimpt_dx(); icn.box.y0+=wimpt_dy(); icn.box.x1-=2*wimpt_dx(); icn.box.y1-=2*wimpt_dy(); ox=state.o.box.x0-state.o.x; oy=state.o.box.y1-state.o.y; s->slideMax=isVertical ? icn.box.y1-icn.box.y0 : icn.box.x1-icn.box.x0; s->d=d; s->i=i; s->max=max; s->val=val; s->colour=colour; s->isVertical=isVertical; s->o=isVertical ? oy+icn.box.y0 : ox+icn.box.x0; s->iwidth=isVertical ? icn.box.x1-icn.box.x0 : icn.box.y1-icn.box.y0; s->proc=proc; s->handle=handle; s->dragOver=FALSE; s->lastLen=-1; wimpt_noerr(wimp_get_point_info(&m)); drag.window=0; drag.type=wimp_USER_HIDDEN; if (isVertical) { drag.parent.x0=m.x; drag.parent.y0=icn.box.y0+oy; drag.parent.x1=m.x; drag.parent.y1=icn.box.y1+oy; } else { drag.parent.x0=icn.box.x0+ox; drag.parent.y0=m.y; drag.parent.x1=icn.box.x1+ox; drag.parent.y1=m.y; } wimpt_noerr(wimp_drag_box(&drag)); win_add_unknown_event_processor(buttons__ukEvents,s); win_addIdleClaimer(buttons__doSlide,2,s); } /* * void buttons_redrawColourButton(dbox d,wimp_i i,buttons_colourstr *c) * * Use * Redraws a true-colour button. * * Parameters * dbox d == the dbox * wimp_i i == icon number * buttons_colourstr == the colour we're intersted in */ void buttons_redrawColourButton(dbox d,wimp_i i,wimp_paletteword c) { wimp_w w=dbox_syshandle(d); wimp_wstate state; wimp_icon icn; int ox,oy; int dummy; wimpt_noerr(wimp_get_wind_state(w,&state)); wimpt_noerr(wimp_get_icon_info(w,i,&icn)); ox=state.o.box.x0-state.o.x; oy=state.o.box.y1-state.o.y; if (wimpt_getVersion()>=300) colourtran_setGCOL(c,256,0,&dummy); /* Dither if you can */ else colourtran_setGCOL(c,0,0,&dummy); bbc_rectanglefill( ox+icn.box.x0 , oy+icn.box.y0 , icn.box.x1-icn.box.x0-1 , icn.box.y1-icn.box.y0-1 ); } /* * buttons_updateColourButton(dbox d,wimp_i i,wimp_paletteword c) * * Use * This routine redraws a colour button. * * Parameters * dbox d == the dbox * wimp_i == the icon number * wimp_paletteword c == the colour number */ void buttons_updateColourButton(dbox d,wimp_i i,wimp_paletteword c) { wimp_w w=dbox_syshandle(d); wimp_icon icn; wimp_redrawstr r; BOOL more; wimpt_noerr(wimp_get_icon_info(w,i,&icn)); r.w=w; r.box.x0=icn.box.x0; r.box.y0=icn.box.y0; r.box.x1=icn.box.x1; r.box.y1=icn.box.y1; wimpt_noerr(wimp_update_wind(&r,&more)); while (more) { buttons_redrawColourButton(d,i,c); wimpt_noerr(wimp_get_rectangle(&r,&more)); } }