/* * wDragging.c * * Handling of drag operations in windows * * © 1994-1998 Straylight */ /*----- Licensing note ----------------------------------------------------* * * This file is part of Straylight's Glass. * * Glass 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. * * Glass 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 Glass. If not, write to the Free Software Foundation, * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*----- Header files ------------------------------------------------------*/ /* * ANSI standard headers */ #include #include #include /* * Steel headers */ #define _STDAPP #define _LOWLVL #include "steel/Steel.h" #include "steel/akbd.h" #include "steel/coords.h" #include "steel/bbc.h" /* * Glass headers */ #include "gStruct.h" #include "gMenus.h" #include "gIcons.h" #include "glass.h" #include "gPrefs.h" #include "tfile.h" #include "window.h" #include "_window.h" #include "editIcon.h" #include "editWin.h" /*----- Private variables -------------------------------------------------*/ static int window__dragType=-1; /* The drag type (one of the macros above */ static int window__dragX; /* Last x position in drag */ static int window__dragY; /* Last y position in drag */ static int window__startX; /* Drag start position */ static int window__startY; /* Drag start position */ static BOOL window__dragDraw; /* Do we update the drag drawing? */ static wimp_box window__dragBox; /* Bounding box for the icon drag */ static glass_windPointer *window__dragWindow; /* Window containing drag op */ /*----- Main code ---------------------------------------------------------*/ /* * void window__align(glass_windPointer *w,int *x,int *y) * * Use * Aligns the given point to the *nearest* grid point. The point is * fiddled in-situ. It does the Right Thing with negative points. * * Parameters * glass_windPointer *w == the window we're using * int *x == pass-by-reference x coord of point * int *y == pass-by-reference y coord of point */ void window__align(glass_windPointer *w,int *x,int *y) { if (*x<0) *x-=w->gridx/2; else *x+=w->gridx/2; if (*y<0) *y-=w->gridy/2; else *y+=w->gridy/2; *x-=*x % w->gridx; *y-=*y % w->gridy; } /* * void window__getDragCoords(glass_windPointer *w,int *x,int *y) * * Use * Gets the (window) coordinates for the drag op, taking everything into * account, and scrolling the window if necessary. * * Parameters * glass_windPointer *w == the window owning the drag * int *x == where to put x coord * int *y == where to put y coord */ static void window__getDragCoords(glass_windPointer *w, int *x, int *y) { /* --- Re-engineered by [mdw] 3 February 1998 --- * * * This function is a mess. So's the rest of the program, but this is * the function I particularly wanted to change today. */ wimp_mousestr m; wimp_box box; int type = window__dragType; int mx, my; int xbudge = 0, ybudge = 0; unsigned xbudged = ~0, ybudged = ~0; unsigned snaps; /* --- Snap type flags --- */ enum { SNAP_CONSTRAIN = 1, SNAP_GUIDES = 2, SNAP_GRID = 4, SNAP_PIXEL = 8, SNAP_NONE = 0, SNAP_ALL = 0x0F }; /* --- A little fiddling with the window --- */ { wimp_wstate s; int ox, oy; int d; /* --- Find out where the mouse is at the moment --- */ wimpt_noerr(wimp_get_point_info(&m)); wimpt_noerr(wimp_get_wind_state(w->h, &s)); ox = s.o.box.x0 - s.o.x; oy = s.o.box.y1 - s.o.y; /* --- If it's outside the window, then scroll it so it isn't --- */ if ((d = m.x - s.o.box.x0) < 0) s.o.x += d; else if ((d = m.x - s.o.box.x1) > 0) s.o.x += d; if ((d = m.y - s.o.box.y0) < 0) s.o.y += d; else if ((d = m.y - s.o.box.y1) > 0) s.o.y += d; wimpt_noerr(wimp_open_wind(&s.o)); wimpt_noerr(wimp_get_wind_state(w->h, &s)); /* --- Convert the mouse coordinates to window-relative ones --- */ m.x -= ox; m.y -= oy; /* --- If the scroll position has changed, update window block --- */ if (s.o.x != w->def->desc.w.scx || s.o.y != w->def->desc.w.scy) { w->def->desc.w.scx = s.o.x; w->def->desc.w.scy = s.o.y; if (!w->t->alts) /* Moving window alters template */ tfile_markAsAltered(w->t); /* But only if not already altered */ editWindow_windowMoved(w); } } /* --- Some words on the snapping procedure --- * * * There are several stages of snapping, with different priorities. * The top priority is the pixel grid; nothing can interfere with that. * Next comes the `constrain' key: if Control is pressed, the dragged * object will only move either horizontally or vertically but not both. * After this comes snapping to guides: an object near a guideline will * snap so that either an edge or centreline is over the guide. Last, * but not least, comes the good old-fashioned gridsnap. */ /* --- Sort out funny drag types --- */ switch (type) { case window__GRABICON: type = window__MAIN; case window__MAIN: snaps = SNAP_ALL; break; case window__TOPLEFT: case window__TOPRIGHT: case window__BOTTOMLEFT: case window__BOTTOMRIGHT: snaps = SNAP_ALL; if (gPrefs_current()->sEdgeHandles) snaps &= ~SNAP_CONSTRAIN; break; case window__TOP: case window__BOTTOM: case window__LEFT: case window__RIGHT: snaps = SNAP_ALL & ~SNAP_CONSTRAIN; break; default: snaps = SNAP_NONE; break; } /* --- Some simplification --- */ mx = m.x - window__startX; my = m.y - window__startY; /* --- Stage one: constrain --- * * * If the constrain key is set then I look to see whether the mouse * has moved more horizontally or vertically since the drag commenced, * and constrain the drag in the appropriate direction. */ if ((snaps & SNAP_CONSTRAIN) && akbd_pollctl()) { if (abs(mx) > abs(my)) { ybudge = -my; ybudged = SNAP_CONSTRAIN; } else { xbudge = -mx; xbudged = SNAP_CONSTRAIN; } } /* --- Stage two: guidelines --- * * * Work out the bounding box of the dragged object. If a moving edge is * near a guide, or the whole object is moving and the centre is near a * guide, then snap the object against the guide. Which guide is chosen * and which edge gets snapped is determined by proximity, and is decided * independently in the x and y directions. (In olden times, there were * priorities for various edges and lower-numbered guides `won'. This is * bad.) */ if (snaps & SNAP_GUIDES) { int gxdist = window__SNAPDIST; int gydist = window__SNAPDIST; int i; int d, dabs; /* --- Transform the bounding box according to drag type --- */ box = window__dragBox; if (type & window__TOP) box.y1 += my; if (type & window__BOTTOM) box.y0 += my; if (type & window__LEFT) box.x0 += mx; if (type & window__RIGHT) box.x1 += mx; /* --- Unknown bit of bodging removed temporarily --- * * * This bodge should have been done in the grab-icon code. */ #ifdef notdef if (window__dragType == window__GRABICON) { box.x1 -= wimpt_dx(); box.y1 -= wimpt_dy(); } #endif /* --- Now nip off through the guides array --- */ for (i = 0; i < glass_GUIDELIMIT; i++) { /* --- Make sure this guide is sensible --- */ if (!w->guide[i].active) continue; /* --- Handle horizontal guides --- */ if ((type & (window__TOP | window__BOTTOM)) && ybudged >= SNAP_GUIDES && w->guide[i].horiz) { /* --- Catch the top edge --- */ if (type & window__TOP) { d = w->guide[i].coord - box.y1; dabs = abs(d); if (dabs <= gydist) { gydist = dabs; ybudge = d; ybudged = SNAP_CONSTRAIN; } } /* --- Catch the bottom edge --- */ if (type & window__BOTTOM) { d = w->guide[i].coord - box.y0; dabs = abs(d); if (dabs <= gydist) { gydist = dabs; ybudge = d; ybudged = SNAP_CONSTRAIN; } } /* --- Catch the centre --- */ if (type == window__MAIN) { d = w->guide[i].coord - (box.y1 + box.y0) / 2; dabs = abs(d); if (dabs <= gydist) { gydist = dabs; ybudge = d; ybudged = SNAP_CONSTRAIN; } } } /* --- Handle vertical guides --- */ if ((type & (window__LEFT | window__RIGHT)) && xbudged >= SNAP_GUIDES && !w->guide[i].horiz) { /* --- Catch the left edge --- */ if (type & window__LEFT) { d = w->guide[i].coord - box.x0; dabs = abs(d); if (dabs <= gxdist) { gxdist = dabs; xbudge = d; xbudged = SNAP_CONSTRAIN; } } /* --- Catch the right edge --- */ if (type & window__RIGHT) { d = w->guide[i].coord - box.x1; dabs = abs(d); if (dabs <= gxdist) { gxdist = dabs; xbudge = d; xbudged = SNAP_CONSTRAIN; } } /* --- Catch the top edge --- */ if (type == window__MAIN) { d = w->guide[i].coord - (box.x1 + box.x0) / 2; dabs = abs(d); if (dabs <= gxdist) { gxdist = dabs; xbudge = d; xbudged = SNAP_CONSTRAIN; } } } } } /* --- Stage three: snap to the grid --- */ if ((snaps & SNAP_GRID) && w->gridLock) { int tx = mx + xbudge, ty = my + ybudge; window__align(w, &tx, &ty); if (xbudged >= SNAP_GRID) { xbudge = tx - mx; xbudged = SNAP_GRID; } if (ybudged >= SNAP_GRID) { ybudge = ty - my; ybudged = SNAP_GRID; } } /* --- Stage four: snap to the pixels --- */ if (snaps & SNAP_PIXEL) { xbudge &= ~(wimpt_dx() - 1); ybudge &= ~(wimpt_dy() - 1); } /* --- Return the totally mangled coordinates --- */ *x = m.x + xbudge; *y = m.y + ybudge; } /* * void window__finishDrag(void) * * Use * Terminates the drag operation apropriately, releasing handlers, memory * etc. */ static void window__dragIdles(void *handle); static void window__finishDrag(void) { glass_windPointer *w=window__dragWindow; wimp_redrawstr r; BOOL more; int x,y; int i; wimp_wstate s; wimp_box box; wimp_box ibx; /* --- We've stopped dragging -- release the idle events --- */ win_removeIdleClaimer(window__dragIdles,0); /* --- If there's no destination window, give up now --- */ if (!window__dragWindow) { bbc_vdu(7); window__dragType=-1; window__setPtrShape(-2); return; } /* --- Find out where the drag ended --- */ window__getDragCoords(w,&x,&y); /* --- Undraw the drag rectangle(s) --- */ if (!window__dragDraw) /* Don't try and remove if not yet plotted! */ { wimpt_noerr(wimp_get_wind_state(w->h,&s)); r.w=w->h; r.box.x0=s.o.x; r.box.x1=r.box.x0+s.o.box.x1-s.o.box.x0; r.box.y1=s.o.y; r.box.y0=r.box.y1+s.o.box.y0-s.o.box.y1; wimpt_noerr(wimp_update_wind(&r,&more)); while (more) { window__rotate(0); window__redrawDragBox(w,&r,window__dragX,window__dragY); wimpt_noerr(wimp_get_rectangle(&r,&more)); } } /* --- Now work out what we actually have to do --- */ switch (window__dragType) { case window__SELECT: box.x0=(window__startX < x) ? window__startX : x; box.x1=(window__startX > x) ? window__startX : x; box.y0=(window__startY < y) ? window__startY : y; box.y1=(window__startY > y) ? window__startY : y; for (i=0;idef->desc.w.nicons;i++) { window_boundingBox(w,i,&ibx); if (coords_boxesoverlap(&box,&ibx)) window__select(w,i,TRUE); } window__updateMenu(w); break; case window__HORGUIDE: for (i=0;iguide[i].active && w->guide[i].selected && w->guide[i].horiz) { window__redrawGuide(w,i); w->guide[i].coord+=y-window__startY; window__redrawGuide(w,i); } } break; case window__VERGUIDE: for (i=0;iguide[i].active && w->guide[i].selected && !w->guide[i].horiz) { window__redrawGuide(w,i); w->guide[i].coord+=x-window__startX; window__redrawGuide(w,i); } } break; case window__GRABICON: box=window__qGrabbedIcon()->box; box.y1+=y-window__startY; box.y0+=y-window__startY; box.x0+=x-window__startX; box.x1+=x-window__startX; window__doGrabIcon(&box,window__dragWindow); break; default: for (i=0;idef->desc.w.nicons;i++) { if (w->def->i[i].selected) { window_redrawIcon(w,i); if (window__dragType & window__TOP) w->def->i[i].i.box.y1+=y-window__startY; if (window__dragType & window__BOTTOM) w->def->i[i].i.box.y0+=y-window__startY; if (window__dragType & window__LEFT) w->def->i[i].i.box.x0+=x-window__startX; if (window__dragType & window__RIGHT) w->def->i[i].i.box.x1+=x-window__startX; if (window__dragType & window__TOP) { if (w->def->i[i].i.box.y1def->i[i].i.box.y0) w->def->i[i].i.box.y1=w->def->i[i].i.box.y0; } if (window__dragType & window__BOTTOM) { if (w->def->i[i].i.box.y0>w->def->i[i].i.box.y1) w->def->i[i].i.box.y0=w->def->i[i].i.box.y1; } if (window__dragType & window__LEFT) { if (w->def->i[i].i.box.x0>w->def->i[i].i.box.x1) w->def->i[i].i.box.x0=w->def->i[i].i.box.x1; } if (window__dragType & window__RIGHT) { if (w->def->i[i].i.box.x1def->i[i].i.box.x0) w->def->i[i].i.box.x1=w->def->i[i].i.box.x0; } window_redrawIcon(w,i); tfile_markAsAltered(w->t); editIcon_iconMoved(w,i); } } break; } window__dragType=-1; window__setPtrShape(-2); } /* * void window__dragIdles(void *handle) * * Use * Redraws the window during a drag operation * * Parameters * void *handle == pointer to the window owning the drag */ static void window__dragIdles(void *handle) { glass_windPointer *w=window__dragWindow; int x,y; wimp_redrawstr r; BOOL more; wimp_wstate s; BOOL moved; wimp_mousestr m; unused(handle); wimpt_noerr(wimp_get_point_info(&m)); if (!m.bbits) { window__finishDrag(); return; } if (!w) return; wimpt_noerr(wimp_get_wind_state(w->h,&s)); r.w=w->h; r.box.x0=s.o.x; r.box.x1=r.box.x0+s.o.box.x1-s.o.box.x0; r.box.y1=s.o.y; r.box.y0=r.box.y1+s.o.box.y0-s.o.box.y1; if (bbc_inkey(-113)) { if (!window__dragDraw) { wimpt_noerr(wimp_update_wind(&r,&more)); while (more) { window__rotate(0); window__redrawDragBox(w,&r,window__dragX,window__dragY); wimpt_noerr(wimp_get_rectangle(&r,&more)); } } win_removeIdleClaimer(window__dragIdles,0); window__dragType=-1; return; } window__getDragCoords(w,&x,&y); moved=(x!=window__dragX || y!=window__dragY); wimpt_noerr(wimp_update_wind(&r,&more)); if (!moved && !window__dragDraw) window__setDash(); else window__rotate(+2); while (more) { window__setXORColour(w,window__DRAGCOL); if (!window__dragDraw && moved) { window__rotate(-2); window__redrawDragBox(w,&r,window__dragX,window__dragY); window__rotate(+2); window__redrawDragBox(w,&r,x,y); } else window__redrawDragBox(w,&r,x,y); wimpt_noerr(wimp_get_rectangle(&r,&more)); } window__dragDraw=FALSE; window__dragX=x; window__dragY=y; } /* * void window__startDrag(int type, * wimp_box *box, * glass_windPointer *w, * int x,int y) * * Use * Starts a drag operation in the specified window * * Parameters * int type == what sort of drag this is * wimp_box *box == the bounding box of the object we're dragging * glass_windPointer *w == the window in which the drag is taking place * int x,int y == the mouse position the drag will start from */ void window__startDrag(int type, wimp_box *box, glass_windPointer *w, int x,int y) { window__dragType=type; window__dragBox=*box; window__dragWindow=w; window__dragX=window__startX=x; window__dragY=window__startY=y; window__dragDraw=TRUE; win_addIdleClaimer(window__dragIdles,window__DRAGTIME,0); } /* * glass_windPointer *window__dragWind(void) * * Use * Returns the current dragging window */ glass_windPointer *window__dragWind(void) { return (window__dragWindow); } /* * void window__setDragWind(glass_windPointer *w) * * Use * Claims the current drag operation for the given window */ void window__setDragWind(glass_windPointer *w) { window__dragWindow=w; } /* * int window__qDragType(void) * * Use * Returns the current drag type */ int window__qDragType(void) { return (window__dragType); } /* * wimp_box window__qDragBox(void) * * Use * Returns the bounding box of the things being dragged */ wimp_box window__qDragBox(void) { return (window__dragBox); } /* * void window__dragCoords(int *x,int *y) * * Use * Returns the current drag position */ void window__dragCoords(int *x,int *y) { *x=window__dragX; *y=window__dragY; } /* * void window__dragStart(int *x,int *y) * * Use * Returns the initial drag position */ void window__dragStart(int *x,int *y) { *x=window__startX; *y=window__startY; }