4 * Handling of drag operations in windows
6 * © 1994-1998 Straylight
9 /*----- Licensing note ----------------------------------------------------*
11 * This file is part of Straylight's Glass.
13 * Glass is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2, or (at your option)
18 * Glass is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with Glass. If not, write to the Free Software Foundation,
25 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 /*----- Header files ------------------------------------------------------*/
31 * ANSI standard headers
44 #include "steel/Steel.h"
46 #include "steel/akbd.h"
47 #include "steel/coords.h"
48 #include "steel/bbc.h"
66 /*----- Private variables -------------------------------------------------*/
68 static int window__dragType=-1; /* The drag type (one of the macros above */
69 static int window__dragX; /* Last x position in drag */
70 static int window__dragY; /* Last y position in drag */
71 static int window__startX; /* Drag start position */
72 static int window__startY; /* Drag start position */
73 static BOOL window__dragDraw; /* Do we update the drag drawing? */
74 static wimp_box window__dragBox; /* Bounding box for the icon drag */
75 static glass_windPointer *window__dragWindow; /* Window containing drag op */
77 /*----- Main code ---------------------------------------------------------*/
80 * void window__align(glass_windPointer *w,int *x,int *y)
83 * Aligns the given point to the *nearest* grid point. The point is
84 * fiddled in-situ. It does the Right Thing with negative points.
87 * glass_windPointer *w == the window we're using
88 * int *x == pass-by-reference x coord of point
89 * int *y == pass-by-reference y coord of point
92 void window__align(glass_windPointer *w,int *x,int *y)
107 * void window__getDragCoords(glass_windPointer *w,int *x,int *y)
110 * Gets the (window) coordinates for the drag op, taking everything into
111 * account, and scrolling the window if necessary.
114 * glass_windPointer *w == the window owning the drag
115 * int *x == where to put x coord
116 * int *y == where to put y coord
119 static void window__getDragCoords(glass_windPointer *w, int *x, int *y)
121 /* --- Re-engineered by [mdw] 3 February 1998 --- *
123 * This function is a mess. So's the rest of the program, but this is
124 * the function I particularly wanted to change today.
129 int type = window__dragType;
131 int xbudge = 0, ybudge = 0;
132 unsigned xbudged = ~0, ybudged = ~0;
135 /* --- Snap type flags --- */
146 /* --- A little fiddling with the window --- */
153 /* --- Find out where the mouse is at the moment --- */
155 wimpt_noerr(wimp_get_point_info(&m));
156 wimpt_noerr(wimp_get_wind_state(w->h, &s));
157 ox = s.o.box.x0 - s.o.x;
158 oy = s.o.box.y1 - s.o.y;
160 /* --- If it's outside the window, then scroll it so it isn't --- */
162 if ((d = m.x - s.o.box.x0) < 0)
164 else if ((d = m.x - s.o.box.x1) > 0)
166 if ((d = m.y - s.o.box.y0) < 0)
168 else if ((d = m.y - s.o.box.y1) > 0)
171 wimpt_noerr(wimp_open_wind(&s.o));
172 wimpt_noerr(wimp_get_wind_state(w->h, &s));
174 /* --- Convert the mouse coordinates to window-relative ones --- */
179 /* --- If the scroll position has changed, update window block --- */
181 if (s.o.x != w->def->desc.w.scx || s.o.y != w->def->desc.w.scy) {
182 w->def->desc.w.scx = s.o.x;
183 w->def->desc.w.scy = s.o.y;
184 if (!w->t->alts) /* Moving window alters template */
185 tfile_markAsAltered(w->t); /* But only if not already altered */
186 editWindow_windowMoved(w);
190 /* --- Some words on the snapping procedure --- *
192 * There are several stages of snapping, with different priorities.
193 * The top priority is the pixel grid; nothing can interfere with that.
194 * Next comes the `constrain' key: if Control is pressed, the dragged
195 * object will only move either horizontally or vertically but not both.
196 * After this comes snapping to guides: an object near a guideline will
197 * snap so that either an edge or centreline is over the guide. Last,
198 * but not least, comes the good old-fashioned gridsnap.
201 /* --- Sort out funny drag types --- */
204 case window__GRABICON:
209 case window__TOPLEFT:
210 case window__TOPRIGHT:
211 case window__BOTTOMLEFT:
212 case window__BOTTOMRIGHT:
214 if (gPrefs_current()->sEdgeHandles)
215 snaps &= ~SNAP_CONSTRAIN;
221 snaps = SNAP_ALL & ~SNAP_CONSTRAIN;
228 /* --- Some simplification --- */
230 mx = m.x - window__startX;
231 my = m.y - window__startY;
233 /* --- Stage one: constrain --- *
235 * If the constrain key is set then I look to see whether the mouse
236 * has moved more horizontally or vertically since the drag commenced,
237 * and constrain the drag in the appropriate direction.
240 if ((snaps & SNAP_CONSTRAIN) && akbd_pollctl()) {
241 if (abs(mx) > abs(my)) {
243 ybudged = SNAP_CONSTRAIN;
246 xbudged = SNAP_CONSTRAIN;
250 /* --- Stage two: guidelines --- *
252 * Work out the bounding box of the dragged object. If a moving edge is
253 * near a guide, or the whole object is moving and the centre is near a
254 * guide, then snap the object against the guide. Which guide is chosen
255 * and which edge gets snapped is determined by proximity, and is decided
256 * independently in the x and y directions. (In olden times, there were
257 * priorities for various edges and lower-numbered guides `won'. This is
261 if (snaps & SNAP_GUIDES) {
262 int gxdist = window__SNAPDIST;
263 int gydist = window__SNAPDIST;
267 /* --- Transform the bounding box according to drag type --- */
269 box = window__dragBox;
270 if (type & window__TOP)
272 if (type & window__BOTTOM)
274 if (type & window__LEFT)
276 if (type & window__RIGHT)
279 /* --- Unknown bit of bodging removed temporarily --- *
281 * This bodge should have been done in the grab-icon code.
285 if (window__dragType == window__GRABICON) {
286 box.x1 -= wimpt_dx();
287 box.y1 -= wimpt_dy();
291 /* --- Now nip off through the guides array --- */
293 for (i = 0; i < glass_GUIDELIMIT; i++) {
295 /* --- Make sure this guide is sensible --- */
297 if (!w->guide[i].active)
300 /* --- Handle horizontal guides --- */
302 if ((type & (window__TOP | window__BOTTOM)) &&
303 ybudged >= SNAP_GUIDES &&
306 /* --- Catch the top edge --- */
308 if (type & window__TOP) {
309 d = w->guide[i].coord - box.y1; dabs = abs(d);
310 if (dabs <= gydist) {
313 ybudged = SNAP_CONSTRAIN;
317 /* --- Catch the bottom edge --- */
319 if (type & window__BOTTOM) {
320 d = w->guide[i].coord - box.y0; dabs = abs(d);
321 if (dabs <= gydist) {
324 ybudged = SNAP_CONSTRAIN;
328 /* --- Catch the centre --- */
330 if (type == window__MAIN) {
331 d = w->guide[i].coord - (box.y1 + box.y0) / 2; dabs = abs(d);
332 if (dabs <= gydist) {
335 ybudged = SNAP_CONSTRAIN;
340 /* --- Handle vertical guides --- */
342 if ((type & (window__LEFT | window__RIGHT)) &&
343 xbudged >= SNAP_GUIDES &&
344 !w->guide[i].horiz) {
346 /* --- Catch the left edge --- */
348 if (type & window__LEFT) {
349 d = w->guide[i].coord - box.x0; dabs = abs(d);
350 if (dabs <= gxdist) {
353 xbudged = SNAP_CONSTRAIN;
357 /* --- Catch the right edge --- */
359 if (type & window__RIGHT) {
360 d = w->guide[i].coord - box.x1; dabs = abs(d);
361 if (dabs <= gxdist) {
364 xbudged = SNAP_CONSTRAIN;
368 /* --- Catch the top edge --- */
370 if (type == window__MAIN) {
371 d = w->guide[i].coord - (box.x1 + box.x0) / 2; dabs = abs(d);
372 if (dabs <= gxdist) {
375 xbudged = SNAP_CONSTRAIN;
382 /* --- Stage three: snap to the grid --- */
384 if ((snaps & SNAP_GRID) && w->gridLock) {
385 int tx = mx + xbudge, ty = my + ybudge;
386 window__align(w, &tx, &ty);
387 if (xbudged >= SNAP_GRID) {
391 if (ybudged >= SNAP_GRID) {
397 /* --- Stage four: snap to the pixels --- */
399 if (snaps & SNAP_PIXEL) {
400 xbudge &= ~(wimpt_dx() - 1);
401 ybudge &= ~(wimpt_dy() - 1);
404 /* --- Return the totally mangled coordinates --- */
411 * void window__finishDrag(void)
414 * Terminates the drag operation apropriately, releasing handlers, memory
418 static void window__dragIdles(void *handle);
420 static void window__finishDrag(void)
422 glass_windPointer *w=window__dragWindow;
431 /* --- We've stopped dragging -- release the idle events --- */
433 win_removeIdleClaimer(window__dragIdles,0);
435 /* --- If there's no destination window, give up now --- */
437 if (!window__dragWindow)
441 window__setPtrShape(-2);
445 /* --- Find out where the drag ended --- */
447 window__getDragCoords(w,&x,&y);
449 /* --- Undraw the drag rectangle(s) --- */
451 if (!window__dragDraw) /* Don't try and remove if not yet plotted! */
453 wimpt_noerr(wimp_get_wind_state(w->h,&s));
456 r.box.x1=r.box.x0+s.o.box.x1-s.o.box.x0;
458 r.box.y0=r.box.y1+s.o.box.y0-s.o.box.y1;
459 wimpt_noerr(wimp_update_wind(&r,&more));
463 window__redrawDragBox(w,&r,window__dragX,window__dragY);
464 wimpt_noerr(wimp_get_rectangle(&r,&more));
468 /* --- Now work out what we actually have to do --- */
470 switch (window__dragType)
473 box.x0=(window__startX < x) ? window__startX : x;
474 box.x1=(window__startX > x) ? window__startX : x;
475 box.y0=(window__startY < y) ? window__startY : y;
476 box.y1=(window__startY > y) ? window__startY : y;
477 for (i=0;i<w->def->desc.w.nicons;i++)
479 window_boundingBox(w,i,&ibx);
480 if (coords_boxesoverlap(&box,&ibx))
481 window__select(w,i,TRUE);
483 window__updateMenu(w);
485 case window__HORGUIDE:
486 for (i=0;i<glass_GUIDELIMIT;i++)
488 if (w->guide[i].active &&
489 w->guide[i].selected &&
492 window__redrawGuide(w,i);
493 w->guide[i].coord+=y-window__startY;
494 window__redrawGuide(w,i);
498 case window__VERGUIDE:
499 for (i=0;i<glass_GUIDELIMIT;i++)
501 if (w->guide[i].active &&
502 w->guide[i].selected &&
505 window__redrawGuide(w,i);
506 w->guide[i].coord+=x-window__startX;
507 window__redrawGuide(w,i);
511 case window__GRABICON:
512 box=window__qGrabbedIcon()->box;
513 box.y1+=y-window__startY;
514 box.y0+=y-window__startY;
515 box.x0+=x-window__startX;
516 box.x1+=x-window__startX;
517 window__doGrabIcon(&box,window__dragWindow);
520 for (i=0;i<w->def->desc.w.nicons;i++)
522 if (w->def->i[i].selected)
524 window_redrawIcon(w,i);
525 if (window__dragType & window__TOP)
526 w->def->i[i].i.box.y1+=y-window__startY;
527 if (window__dragType & window__BOTTOM)
528 w->def->i[i].i.box.y0+=y-window__startY;
529 if (window__dragType & window__LEFT)
530 w->def->i[i].i.box.x0+=x-window__startX;
531 if (window__dragType & window__RIGHT)
532 w->def->i[i].i.box.x1+=x-window__startX;
533 if (window__dragType & window__TOP)
535 if (w->def->i[i].i.box.y1<w->def->i[i].i.box.y0)
536 w->def->i[i].i.box.y1=w->def->i[i].i.box.y0;
538 if (window__dragType & window__BOTTOM)
540 if (w->def->i[i].i.box.y0>w->def->i[i].i.box.y1)
541 w->def->i[i].i.box.y0=w->def->i[i].i.box.y1;
543 if (window__dragType & window__LEFT)
545 if (w->def->i[i].i.box.x0>w->def->i[i].i.box.x1)
546 w->def->i[i].i.box.x0=w->def->i[i].i.box.x1;
548 if (window__dragType & window__RIGHT)
550 if (w->def->i[i].i.box.x1<w->def->i[i].i.box.x0)
551 w->def->i[i].i.box.x1=w->def->i[i].i.box.x0;
553 window_redrawIcon(w,i);
554 tfile_markAsAltered(w->t);
555 editIcon_iconMoved(w,i);
561 window__setPtrShape(-2);
565 * void window__dragIdles(void *handle)
568 * Redraws the window during a drag operation
571 * void *handle == pointer to the window owning the drag
574 static void window__dragIdles(void *handle)
576 glass_windPointer *w=window__dragWindow;
584 wimpt_noerr(wimp_get_point_info(&m));
587 window__finishDrag();
592 wimpt_noerr(wimp_get_wind_state(w->h,&s));
595 r.box.x1=r.box.x0+s.o.box.x1-s.o.box.x0;
597 r.box.y0=r.box.y1+s.o.box.y0-s.o.box.y1;
600 if (!window__dragDraw)
602 wimpt_noerr(wimp_update_wind(&r,&more));
606 window__redrawDragBox(w,&r,window__dragX,window__dragY);
607 wimpt_noerr(wimp_get_rectangle(&r,&more));
610 win_removeIdleClaimer(window__dragIdles,0);
614 window__getDragCoords(w,&x,&y);
615 moved=(x!=window__dragX || y!=window__dragY);
616 wimpt_noerr(wimp_update_wind(&r,&more));
617 if (!moved && !window__dragDraw)
623 window__setXORColour(w,window__DRAGCOL);
624 if (!window__dragDraw && moved)
627 window__redrawDragBox(w,&r,window__dragX,window__dragY);
629 window__redrawDragBox(w,&r,x,y);
632 window__redrawDragBox(w,&r,x,y);
633 wimpt_noerr(wimp_get_rectangle(&r,&more));
635 window__dragDraw=FALSE;
641 * void window__startDrag(int type,
643 * glass_windPointer *w,
647 * Starts a drag operation in the specified window
650 * int type == what sort of drag this is
651 * wimp_box *box == the bounding box of the object we're dragging
652 * glass_windPointer *w == the window in which the drag is taking place
653 * int x,int y == the mouse position the drag will start from
656 void window__startDrag(int type,
658 glass_windPointer *w,
661 window__dragType=type;
662 window__dragBox=*box;
663 window__dragWindow=w;
664 window__dragX=window__startX=x;
665 window__dragY=window__startY=y;
666 window__dragDraw=TRUE;
667 win_addIdleClaimer(window__dragIdles,window__DRAGTIME,0);
671 * glass_windPointer *window__dragWind(void)
674 * Returns the current dragging window
677 glass_windPointer *window__dragWind(void)
679 return (window__dragWindow);
683 * void window__setDragWind(glass_windPointer *w)
686 * Claims the current drag operation for the given window
689 void window__setDragWind(glass_windPointer *w)
691 window__dragWindow=w;
695 * int window__qDragType(void)
698 * Returns the current drag type
701 int window__qDragType(void)
703 return (window__dragType);
707 * wimp_box window__qDragBox(void)
710 * Returns the bounding box of the things being dragged
713 wimp_box window__qDragBox(void)
715 return (window__dragBox);
719 * void window__dragCoords(int *x,int *y)
722 * Returns the current drag position
725 void window__dragCoords(int *x,int *y)
732 * void window__dragStart(int *x,int *y)
735 * Returns the initial drag position
738 void window__dragStart(int *x,int *y)