Initial revision
[ssr] / StraySrc / Glass / !Glass / c / wDragging
1 /*
2 * wDragging.c
3 *
4 * Handling of drag operations in windows
5 *
6 * © 1994-1998 Straylight
7 */
8
9 /*----- Licensing note ----------------------------------------------------*
10 *
11 * This file is part of Straylight's Glass.
12 *
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)
16 * any later version.
17 *
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.
22 *
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.
26 */
27
28 /*----- Header files ------------------------------------------------------*/
29
30 /*
31 * ANSI standard headers
32 */
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37
38 /*
39 * Steel headers
40 */
41
42 #define _STDAPP
43 #define _LOWLVL
44 #include "steel/Steel.h"
45
46 #include "steel/akbd.h"
47 #include "steel/coords.h"
48 #include "steel/bbc.h"
49
50 /*
51 * Glass headers
52 */
53
54 #include "gStruct.h"
55 #include "gMenus.h"
56 #include "gIcons.h"
57
58 #include "glass.h"
59 #include "gPrefs.h"
60 #include "tfile.h"
61 #include "window.h"
62 #include "_window.h"
63 #include "editIcon.h"
64 #include "editWin.h"
65
66 /*----- Private variables -------------------------------------------------*/
67
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 */
76
77 /*----- Main code ---------------------------------------------------------*/
78
79 /*
80 * void window__align(glass_windPointer *w,int *x,int *y)
81 *
82 * Use
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.
85 *
86 * Parameters
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
90 */
91
92 void window__align(glass_windPointer *w,int *x,int *y)
93 {
94 if (*x<0)
95 *x-=w->gridx/2;
96 else
97 *x+=w->gridx/2;
98 if (*y<0)
99 *y-=w->gridy/2;
100 else
101 *y+=w->gridy/2;
102 *x-=*x % w->gridx;
103 *y-=*y % w->gridy;
104 }
105
106 /*
107 * void window__getDragCoords(glass_windPointer *w,int *x,int *y)
108 *
109 * Use
110 * Gets the (window) coordinates for the drag op, taking everything into
111 * account, and scrolling the window if necessary.
112 *
113 * Parameters
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
117 */
118
119 static void window__getDragCoords(glass_windPointer *w, int *x, int *y)
120 {
121 /* --- Re-engineered by [mdw] 3 February 1998 --- *
122 *
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.
125 */
126
127 wimp_mousestr m;
128 wimp_box box;
129 int type = window__dragType;
130 int mx, my;
131 int xbudge = 0, ybudge = 0;
132 unsigned xbudged = ~0, ybudged = ~0;
133 unsigned snaps;
134
135 /* --- Snap type flags --- */
136
137 enum {
138 SNAP_CONSTRAIN = 1,
139 SNAP_GUIDES = 2,
140 SNAP_GRID = 4,
141 SNAP_PIXEL = 8,
142 SNAP_NONE = 0,
143 SNAP_ALL = 0x0F
144 };
145
146 /* --- A little fiddling with the window --- */
147
148 {
149 wimp_wstate s;
150 int ox, oy;
151 int d;
152
153 /* --- Find out where the mouse is at the moment --- */
154
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;
159
160 /* --- If it's outside the window, then scroll it so it isn't --- */
161
162 if ((d = m.x - s.o.box.x0) < 0)
163 s.o.x += d;
164 else if ((d = m.x - s.o.box.x1) > 0)
165 s.o.x += d;
166 if ((d = m.y - s.o.box.y0) < 0)
167 s.o.y += d;
168 else if ((d = m.y - s.o.box.y1) > 0)
169 s.o.y += d;
170
171 wimpt_noerr(wimp_open_wind(&s.o));
172 wimpt_noerr(wimp_get_wind_state(w->h, &s));
173
174 /* --- Convert the mouse coordinates to window-relative ones --- */
175
176 m.x -= ox;
177 m.y -= oy;
178
179 /* --- If the scroll position has changed, update window block --- */
180
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);
187 }
188 }
189
190 /* --- Some words on the snapping procedure --- *
191 *
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.
199 */
200
201 /* --- Sort out funny drag types --- */
202
203 switch (type) {
204 case window__GRABICON:
205 type = window__MAIN;
206 case window__MAIN:
207 snaps = SNAP_ALL;
208 break;
209 case window__TOPLEFT:
210 case window__TOPRIGHT:
211 case window__BOTTOMLEFT:
212 case window__BOTTOMRIGHT:
213 snaps = SNAP_ALL;
214 if (gPrefs_current()->sEdgeHandles)
215 snaps &= ~SNAP_CONSTRAIN;
216 break;
217 case window__TOP:
218 case window__BOTTOM:
219 case window__LEFT:
220 case window__RIGHT:
221 snaps = SNAP_ALL & ~SNAP_CONSTRAIN;
222 break;
223 default:
224 snaps = SNAP_NONE;
225 break;
226 }
227
228 /* --- Some simplification --- */
229
230 mx = m.x - window__startX;
231 my = m.y - window__startY;
232
233 /* --- Stage one: constrain --- *
234 *
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.
238 */
239
240 if ((snaps & SNAP_CONSTRAIN) && akbd_pollctl()) {
241 if (abs(mx) > abs(my)) {
242 ybudge = -my;
243 ybudged = SNAP_CONSTRAIN;
244 } else {
245 xbudge = -mx;
246 xbudged = SNAP_CONSTRAIN;
247 }
248 }
249
250 /* --- Stage two: guidelines --- *
251 *
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
258 * bad.)
259 */
260
261 if (snaps & SNAP_GUIDES) {
262 int gxdist = window__SNAPDIST;
263 int gydist = window__SNAPDIST;
264 int i;
265 int d, dabs;
266
267 /* --- Transform the bounding box according to drag type --- */
268
269 box = window__dragBox;
270 if (type & window__TOP)
271 box.y1 += my;
272 if (type & window__BOTTOM)
273 box.y0 += my;
274 if (type & window__LEFT)
275 box.x0 += mx;
276 if (type & window__RIGHT)
277 box.x1 += mx;
278
279 /* --- Unknown bit of bodging removed temporarily --- *
280 *
281 * This bodge should have been done in the grab-icon code.
282 */
283
284 #ifdef notdef
285 if (window__dragType == window__GRABICON) {
286 box.x1 -= wimpt_dx();
287 box.y1 -= wimpt_dy();
288 }
289 #endif
290
291 /* --- Now nip off through the guides array --- */
292
293 for (i = 0; i < glass_GUIDELIMIT; i++) {
294
295 /* --- Make sure this guide is sensible --- */
296
297 if (!w->guide[i].active)
298 continue;
299
300 /* --- Handle horizontal guides --- */
301
302 if ((type & (window__TOP | window__BOTTOM)) &&
303 ybudged >= SNAP_GUIDES &&
304 w->guide[i].horiz) {
305
306 /* --- Catch the top edge --- */
307
308 if (type & window__TOP) {
309 d = w->guide[i].coord - box.y1; dabs = abs(d);
310 if (dabs <= gydist) {
311 gydist = dabs;
312 ybudge = d;
313 ybudged = SNAP_CONSTRAIN;
314 }
315 }
316
317 /* --- Catch the bottom edge --- */
318
319 if (type & window__BOTTOM) {
320 d = w->guide[i].coord - box.y0; dabs = abs(d);
321 if (dabs <= gydist) {
322 gydist = dabs;
323 ybudge = d;
324 ybudged = SNAP_CONSTRAIN;
325 }
326 }
327
328 /* --- Catch the centre --- */
329
330 if (type == window__MAIN) {
331 d = w->guide[i].coord - (box.y1 + box.y0) / 2; dabs = abs(d);
332 if (dabs <= gydist) {
333 gydist = dabs;
334 ybudge = d;
335 ybudged = SNAP_CONSTRAIN;
336 }
337 }
338 }
339
340 /* --- Handle vertical guides --- */
341
342 if ((type & (window__LEFT | window__RIGHT)) &&
343 xbudged >= SNAP_GUIDES &&
344 !w->guide[i].horiz) {
345
346 /* --- Catch the left edge --- */
347
348 if (type & window__LEFT) {
349 d = w->guide[i].coord - box.x0; dabs = abs(d);
350 if (dabs <= gxdist) {
351 gxdist = dabs;
352 xbudge = d;
353 xbudged = SNAP_CONSTRAIN;
354 }
355 }
356
357 /* --- Catch the right edge --- */
358
359 if (type & window__RIGHT) {
360 d = w->guide[i].coord - box.x1; dabs = abs(d);
361 if (dabs <= gxdist) {
362 gxdist = dabs;
363 xbudge = d;
364 xbudged = SNAP_CONSTRAIN;
365 }
366 }
367
368 /* --- Catch the top edge --- */
369
370 if (type == window__MAIN) {
371 d = w->guide[i].coord - (box.x1 + box.x0) / 2; dabs = abs(d);
372 if (dabs <= gxdist) {
373 gxdist = dabs;
374 xbudge = d;
375 xbudged = SNAP_CONSTRAIN;
376 }
377 }
378 }
379 }
380 }
381
382 /* --- Stage three: snap to the grid --- */
383
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) {
388 xbudge = tx - mx;
389 xbudged = SNAP_GRID;
390 }
391 if (ybudged >= SNAP_GRID) {
392 ybudge = ty - my;
393 ybudged = SNAP_GRID;
394 }
395 }
396
397 /* --- Stage four: snap to the pixels --- */
398
399 if (snaps & SNAP_PIXEL) {
400 xbudge &= ~(wimpt_dx() - 1);
401 ybudge &= ~(wimpt_dy() - 1);
402 }
403
404 /* --- Return the totally mangled coordinates --- */
405
406 *x = m.x + xbudge;
407 *y = m.y + ybudge;
408 }
409
410 /*
411 * void window__finishDrag(void)
412 *
413 * Use
414 * Terminates the drag operation apropriately, releasing handlers, memory
415 * etc.
416 */
417
418 static void window__dragIdles(void *handle);
419
420 static void window__finishDrag(void)
421 {
422 glass_windPointer *w=window__dragWindow;
423 wimp_redrawstr r;
424 BOOL more;
425 int x,y;
426 int i;
427 wimp_wstate s;
428 wimp_box box;
429 wimp_box ibx;
430
431 /* --- We've stopped dragging -- release the idle events --- */
432
433 win_removeIdleClaimer(window__dragIdles,0);
434
435 /* --- If there's no destination window, give up now --- */
436
437 if (!window__dragWindow)
438 {
439 bbc_vdu(7);
440 window__dragType=-1;
441 window__setPtrShape(-2);
442 return;
443 }
444
445 /* --- Find out where the drag ended --- */
446
447 window__getDragCoords(w,&x,&y);
448
449 /* --- Undraw the drag rectangle(s) --- */
450
451 if (!window__dragDraw) /* Don't try and remove if not yet plotted! */
452 {
453 wimpt_noerr(wimp_get_wind_state(w->h,&s));
454 r.w=w->h;
455 r.box.x0=s.o.x;
456 r.box.x1=r.box.x0+s.o.box.x1-s.o.box.x0;
457 r.box.y1=s.o.y;
458 r.box.y0=r.box.y1+s.o.box.y0-s.o.box.y1;
459 wimpt_noerr(wimp_update_wind(&r,&more));
460 while (more)
461 {
462 window__rotate(0);
463 window__redrawDragBox(w,&r,window__dragX,window__dragY);
464 wimpt_noerr(wimp_get_rectangle(&r,&more));
465 }
466 }
467
468 /* --- Now work out what we actually have to do --- */
469
470 switch (window__dragType)
471 {
472 case window__SELECT:
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++)
478 {
479 window_boundingBox(w,i,&ibx);
480 if (coords_boxesoverlap(&box,&ibx))
481 window__select(w,i,TRUE);
482 }
483 window__updateMenu(w);
484 break;
485 case window__HORGUIDE:
486 for (i=0;i<glass_GUIDELIMIT;i++)
487 {
488 if (w->guide[i].active &&
489 w->guide[i].selected &&
490 w->guide[i].horiz)
491 {
492 window__redrawGuide(w,i);
493 w->guide[i].coord+=y-window__startY;
494 window__redrawGuide(w,i);
495 }
496 }
497 break;
498 case window__VERGUIDE:
499 for (i=0;i<glass_GUIDELIMIT;i++)
500 {
501 if (w->guide[i].active &&
502 w->guide[i].selected &&
503 !w->guide[i].horiz)
504 {
505 window__redrawGuide(w,i);
506 w->guide[i].coord+=x-window__startX;
507 window__redrawGuide(w,i);
508 }
509 }
510 break;
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);
518 break;
519 default:
520 for (i=0;i<w->def->desc.w.nicons;i++)
521 {
522 if (w->def->i[i].selected)
523 {
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)
534 {
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;
537 }
538 if (window__dragType & window__BOTTOM)
539 {
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;
542 }
543 if (window__dragType & window__LEFT)
544 {
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;
547 }
548 if (window__dragType & window__RIGHT)
549 {
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;
552 }
553 window_redrawIcon(w,i);
554 tfile_markAsAltered(w->t);
555 editIcon_iconMoved(w,i);
556 }
557 }
558 break;
559 }
560 window__dragType=-1;
561 window__setPtrShape(-2);
562 }
563
564 /*
565 * void window__dragIdles(void *handle)
566 *
567 * Use
568 * Redraws the window during a drag operation
569 *
570 * Parameters
571 * void *handle == pointer to the window owning the drag
572 */
573
574 static void window__dragIdles(void *handle)
575 {
576 glass_windPointer *w=window__dragWindow;
577 int x,y;
578 wimp_redrawstr r;
579 BOOL more;
580 wimp_wstate s;
581 BOOL moved;
582 wimp_mousestr m;
583 unused(handle);
584 wimpt_noerr(wimp_get_point_info(&m));
585 if (!m.bbits)
586 {
587 window__finishDrag();
588 return;
589 }
590 if (!w)
591 return;
592 wimpt_noerr(wimp_get_wind_state(w->h,&s));
593 r.w=w->h;
594 r.box.x0=s.o.x;
595 r.box.x1=r.box.x0+s.o.box.x1-s.o.box.x0;
596 r.box.y1=s.o.y;
597 r.box.y0=r.box.y1+s.o.box.y0-s.o.box.y1;
598 if (bbc_inkey(-113))
599 {
600 if (!window__dragDraw)
601 {
602 wimpt_noerr(wimp_update_wind(&r,&more));
603 while (more)
604 {
605 window__rotate(0);
606 window__redrawDragBox(w,&r,window__dragX,window__dragY);
607 wimpt_noerr(wimp_get_rectangle(&r,&more));
608 }
609 }
610 win_removeIdleClaimer(window__dragIdles,0);
611 window__dragType=-1;
612 return;
613 }
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)
618 window__setDash();
619 else
620 window__rotate(+2);
621 while (more)
622 {
623 window__setXORColour(w,window__DRAGCOL);
624 if (!window__dragDraw && moved)
625 {
626 window__rotate(-2);
627 window__redrawDragBox(w,&r,window__dragX,window__dragY);
628 window__rotate(+2);
629 window__redrawDragBox(w,&r,x,y);
630 }
631 else
632 window__redrawDragBox(w,&r,x,y);
633 wimpt_noerr(wimp_get_rectangle(&r,&more));
634 }
635 window__dragDraw=FALSE;
636 window__dragX=x;
637 window__dragY=y;
638 }
639
640 /*
641 * void window__startDrag(int type,
642 * wimp_box *box,
643 * glass_windPointer *w,
644 * int x,int y)
645 *
646 * Use
647 * Starts a drag operation in the specified window
648 *
649 * Parameters
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
654 */
655
656 void window__startDrag(int type,
657 wimp_box *box,
658 glass_windPointer *w,
659 int x,int y)
660 {
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);
668 }
669
670 /*
671 * glass_windPointer *window__dragWind(void)
672 *
673 * Use
674 * Returns the current dragging window
675 */
676
677 glass_windPointer *window__dragWind(void)
678 {
679 return (window__dragWindow);
680 }
681
682 /*
683 * void window__setDragWind(glass_windPointer *w)
684 *
685 * Use
686 * Claims the current drag operation for the given window
687 */
688
689 void window__setDragWind(glass_windPointer *w)
690 {
691 window__dragWindow=w;
692 }
693
694 /*
695 * int window__qDragType(void)
696 *
697 * Use
698 * Returns the current drag type
699 */
700
701 int window__qDragType(void)
702 {
703 return (window__dragType);
704 }
705
706 /*
707 * wimp_box window__qDragBox(void)
708 *
709 * Use
710 * Returns the bounding box of the things being dragged
711 */
712
713 wimp_box window__qDragBox(void)
714 {
715 return (window__dragBox);
716 }
717
718 /*
719 * void window__dragCoords(int *x,int *y)
720 *
721 * Use
722 * Returns the current drag position
723 */
724
725 void window__dragCoords(int *x,int *y)
726 {
727 *x=window__dragX;
728 *y=window__dragY;
729 }
730
731 /*
732 * void window__dragStart(int *x,int *y)
733 *
734 * Use
735 * Returns the initial drag position
736 */
737
738 void window__dragStart(int *x,int *y)
739 {
740 *x=window__startX;
741 *y=window__startY;
742 }