Initial revision
[ssr] / StraySrc / Glass / !Glass / c / wIcons
1 /*
2 * wIcons.c
3 *
4 * Manipulation of icons within template 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/interface.h"
47 #include "steel/sculptrix.h"
48 #include "steel/flex.h"
49 #include "steel/bbc.h"
50 #include "steel/font.h"
51
52 /*
53 * Glass headers
54 */
55
56 #include "gStruct.h"
57 #include "gMenus.h"
58 #include "gIcons.h"
59
60 #include "glass.h"
61 #include "gPrefs.h"
62 #include "tfile.h"
63 #include "window.h"
64 #include "_window.h"
65 #include "editIcon.h"
66 #include "indir.h"
67 #include "iconData.h"
68 #include "tearEdit.h"
69
70 /*----- Main code ---------------------------------------------------------*/
71
72 /*
73 * void window__bound(wimp_icon *i,wimp_box *box,BOOL force)
74 *
75 * Use
76 * Works out the bounding box (including 3D border) of the given pseudoicon.
77 *
78 * Parameters
79 * wimp_icon *i == pointer to an icon definition
80 * wimp_box *box == where to put the result
81 * BOOL force == force reading of the border, even if disabled in prefs
82 */
83
84 void window__bound(wimp_icon *i,wimp_box *box,BOOL force)
85 {
86 os_regset r;
87 wimp_icon j;
88
89 /* --- Get the answer from Sculptrix --- */
90
91 j=*i;
92 if (gPrefs_current()->sDispBorders &&
93 (force || gPrefs_current()->sIncBorder) &&
94 sculptrix_boundingBox(&j))
95 {
96 *box=j.box;
97 return;
98 }
99
100 /* --- Ask Interface if there's a bounding box at all --- */
101
102 if (gPrefs_current()->iDispBorders &&
103 (force || gPrefs_current()->iIncBorder))
104 {
105 r.r[1]=(int)&j;
106 wimpt_noerr(os_swix(XInterface_BoundingBox,&r));
107 *box=j.box;
108 }
109 else
110 *box=i->box;
111
112 /* --- Now ask WimpExtension --- */
113
114 if (gPrefs_current()->wDispBorders &&
115 (force || gPrefs_current()->wIncBorder))
116 {
117 r.r[0]=2;
118 r.r[1]=(int)i;
119 wimpt_noerr(os_swix(XWimpExt_BorderOp,&r));
120 box->x0=min2(box->x0,r.r[2]);
121 box->y0=min2(box->y0,r.r[3]);
122 box->x1=max2(box->x1,r.r[4]);
123 box->y1=max2(box->y1,r.r[5]);
124 }
125 }
126
127 /*
128 * void window__removeTrailingDeleted(glass_windPointer *w)
129 *
130 * Use
131 * Removes trailing deleted icons from a window (i.e. ones that can be
132 * safely deleted properly without messing up icon numbers).
133 *
134 * Parameters
135 * glass_windPointer *w == the window to blitz
136 */
137
138 void window__removeTrailingDeleted(glass_windPointer *w)
139 {
140 int i=w->def->desc.w.nicons-1;
141 int dead=0;
142 while (w->def->i[i].i.flags & wimp_IDELETED)
143 {
144 i--; /* Now points to top undeleted icon */
145 dead++;
146 }
147 flex_extend((flex_ptr)&w->def,
148 sizeof(glass_window)+
149 i*sizeof(glass_iconDescription));
150 w->size-=dead*sizeof(glass_iconDescription);
151 w->def->desc.w.nicons-=dead;
152 }
153
154 /*
155 * int window__createIcon(glass_windPointer *w)
156 *
157 * Use
158 * Creates a slot for an icon in the window specified, according to current
159 * preferences. The contents of the icon array are unspecified (and
160 * probably not too useful).
161 *
162 * Parameters
163 * glass_windPointer *w == the window to create the icon in
164 *
165 * Returns
166 * Icon number that can be used, or -1 if the operation failed.
167 */
168
169 int window__createIcon(glass_windPointer *w)
170 {
171 int i;
172
173 /* --- Find a space to create the icon --- *
174 *
175 * If we're using up deleted spaces, then loop through to find the lowest
176 * free one.
177 */
178
179 if (!gPrefs_current()->mCreateTop)
180 {
181 for (i=0;i<w->def->desc.w.nicons;i++)
182 {
183 if (w->def->i[i].i.flags & wimp_IDELETED)
184 {
185 w->def->i[i].selected=FALSE;
186 w->def->i[i].edit=0;
187 return (i);
188 }
189 }
190 }
191 else
192 i=w->def->desc.w.nicons;
193
194 /* --- We need to add a new icon on the end --- */
195
196 if (!flex_extend((flex_ptr)&w->def,
197 sizeof(glass_window)+
198 i*sizeof(glass_iconDescription)))
199 {
200 werr(FALSE,msgs_lookup("wdNEMCI"));
201 return (-1);
202 }
203 w->def->desc.w.nicons++;
204 w->size+=sizeof(glass_iconDescription);
205 w->def->i[i].selected=FALSE;
206 w->def->i[i].copied=FALSE;
207 w->def->i[i].edit=0;
208 return (i);
209 }
210
211 /*
212 * void window__renumber(glass_windPointer *w,BOOL renum)
213 *
214 * Use
215 * Sets the renumber flag of the given window to the given state.
216 * Everything is set up properly according to the new state.
217 *
218 * Parameters
219 * glass_windPointer *w == the window to renumber
220 * BOOL renum == the new state of the flag
221 */
222
223 void window__renumber(glass_windPointer *w,BOOL renum)
224 {
225 int i;
226
227 /* --- Make sure we've got something to do --- */
228
229 if (renum==w->renumber)
230 return;
231
232 /* --- Unselect all the guidelines --- */
233
234 for (i=0;i<glass_GUIDELIMIT;i++)
235 {
236 if (w->guide[i].selected)
237 {
238 w->guide[i].selected=FALSE;
239 window__redrawGuide(w,i);
240 }
241 }
242
243 /* --- Now change the selection boxes for all the icons --- *
244 *
245 * For each one, we undraw the old box, change the renumber state, and
246 * draw the new one. Klugey, but it works.
247 */
248
249 for (i=0;i<w->def->desc.w.nicons;i++)
250 {
251 if (w->def->i[i].selected)
252 {
253 window__select(w,i,FALSE);
254 w->renumber=!w->renumber;
255 window__select(w,i,TRUE);
256 w->renumber=!w->renumber;
257 }
258 }
259
260 /* --- Update the window state information, and the info bar --- */
261
262 w->renumber=!w->renumber;
263 window__toggleRenumber(w);
264 }
265
266 /*
267 * void window__copyIcons(glass_windPointer *w)
268 *
269 * Use
270 * Copies the selected icons into the given window. If the icons are
271 * already in the given window, they are duplicated and offset by a small
272 * quantity. If the icons are in a different window, then they are
273 * centred over the current visible area.
274 *
275 * Parameters
276 * glass_windPointer *w == the window to put the icons
277 */
278
279 void window__copyIcons(glass_windPointer *w)
280 {
281 int xoff;
282 int yoff;
283 wimp_box bound;
284 BOOL started;
285 int i;
286 wimp_wstate s;
287 int ni;
288 BOOL fonterr=FALSE;
289 glass_windPointer *wso=window_selectionOwner();
290
291 /* --- Work out where to put the selected icons --- *
292 *
293 * If we're copying to the same window, we just displace them a bit. If
294 * we're copying between windows, we need to centre them in the new
295 * window.
296 */
297
298 if (w==wso)
299 {
300 xoff=w->gridx;
301 yoff=-w->gridy;
302 }
303 else
304 {
305 started=FALSE;
306
307 /* --- Calculate the bounding box of the selection --- */
308
309 for (i=0;i<wso->def->desc.w.nicons;i++)
310 {
311 if (wso->def->i[i].selected)
312 {
313 if (started)
314 {
315 if (wso->def->i[i].i.box.x0<bound.x0)
316 bound.x0=wso->def->i[i].i.box.x0;
317 if (wso->def->i[i].i.box.x1>bound.x1)
318 bound.x1=wso->def->i[i].i.box.x1;
319 if (wso->def->i[i].i.box.y0<bound.y0)
320 bound.y0=wso->def->i[i].i.box.y0;
321 if (wso->def->i[i].i.box.y1>bound.y1)
322 bound.y1=wso->def->i[i].i.box.y1;
323 }
324 else
325 {
326 bound=wso->def->i[i].i.box;
327 started=TRUE;
328 }
329 }
330 }
331
332 /* --- Work out how to centre the icons --- */
333
334 wimpt_noerr(wimp_get_wind_state(w->h,&s));
335 xoff=(s.o.x+(s.o.box.x1-s.o.box.x0)/2-(bound.x1-bound.x0)/2);
336 yoff=(s.o.y-(s.o.box.y1-s.o.box.y0)/2-(bound.y1-bound.y0)/2);
337 xoff-=bound.x0;
338 yoff-=bound.y0;
339
340 /* --- If there's a grid lock, keep the same grid alignment --- */
341
342 if (w->gridLock)
343 window__align(w,&xoff,&yoff);
344
345 /* --- Also retain the pixel alignment --- */
346
347 xoff&=~(wimpt_dx()-1);
348 yoff&=~(wimpt_dy()-1);
349 }
350
351 /* --- We've altered the template file --- */
352
353 tfile_markAsAltered(w->t);
354
355 /* --- Now actually do the copy --- */
356
357 for (i=0;i<wso->def->desc.w.nicons;i++)
358 {
359 if (wso->def->i[i].selected)
360 {
361
362 /* --- Find out there to put the copy --- */
363
364 ni=window__createIcon(w);
365 if (ni==-1)
366 return;
367
368 /* --- Reposition the copied icon nicely --- */
369
370 w->def->i[ni].i=wso->def->i[i].i;
371 w->def->i[ni].i.box.x0+=xoff;
372 w->def->i[ni].i.box.x1+=xoff;
373 w->def->i[ni].i.box.y0+=yoff;
374 w->def->i[ni].i.box.y1+=yoff;
375
376 /* --- Set up the copied icon's font information --- */
377
378 if (!iconData_handleFont(w,&w->def->i[ni].i.flags) && !fonterr)
379 {
380 werr(FALSE,msgs_lookup("wdFERCPY"));
381 fonterr=TRUE;
382 }
383
384 /* --- Set up the copied icon's indirected data nicely --- */
385
386 if (!iconData_processIcon(w,ni,0,TRUE,0))
387 {
388 werr(FALSE,msgs_lookup("wdNEMCP"));
389 w->def->i[ni].i.flags&=~wimp_INDIRECT;
390 window_deleteIcon(w,ni);
391 return;
392 }
393 window_redrawIcon(w,ni);
394 w->def->i[ni].copied=TRUE;
395 }
396 }
397
398 /* --- Now move the selection across, and select the copies --- */
399
400 for (i=0;i<wso->def->desc.w.nicons;i++)
401 window__select(wso,i,FALSE);
402 for (i=0;i<w->def->desc.w.nicons;i++)
403 {
404 if (w->def->i[i].copied)
405 window__select(w,i,TRUE);
406 w->def->i[i].copied=FALSE;
407 }
408 window__gainSelection(w);
409 }
410
411 /*
412 * void window__nudgeIcons(glass_windPointer *w,wimp_box *nudge)
413 *
414 * Use
415 * Nudges the selected icons in the specified window, by adding the box
416 * given to each icon's bounding box. The nudge box is multiplied either by
417 * the current grid size (if grid lock is enabled) or by the pixel size (if
418 * it isn't) before addition. A nudge is considered to be a single
419 * alteration for the purposes of autosave-counting. If no icons are moved
420 * then the selected guidelines are moved instead.
421 *
422 * Parameters
423 * glass_windPointer *w == the window containing the icons to nudge
424 * wimp_box *nudge == the box to add to the coordinates of the icons
425 */
426
427 void window__nudgeIcons(glass_windPointer *w,wimp_box *nudge)
428 {
429 int i;
430 wimp_box b;
431 wimp_box *bp;
432 BOOL doneOne=FALSE;
433
434 /* --- Make sure we're not dragging something --- */
435
436 if (window__qDragType()!=-1)
437 return;
438
439 /* --- Multiply up the nudge rectangle --- */
440
441 if (w->gridLock)
442 {
443 nudge->x0*=w->gridx;
444 nudge->y0*=w->gridy;
445 nudge->x1*=w->gridx;
446 nudge->y1*=w->gridy;
447 }
448 else
449 {
450 nudge->x0*=wimpt_dx();
451 nudge->y0*=wimpt_dy();
452 nudge->x1*=wimpt_dx();
453 nudge->y1*=wimpt_dy();
454 }
455
456 /* --- Nudge the selected icons --- *
457 *
458 * Remember to make sure that we don't turn the icon inside-out.
459 */
460
461 for (i=0;i<w->def->desc.w.nicons;i++)
462 {
463 if (w->def->i[i].selected)
464 {
465 doneOne=TRUE;
466 window_boundingBox(w,i,&b);
467 bp=&w->def->i[i].i.box;
468 if (bp->x0+nudge->x0<=bp->x1+nudge->x1)
469 b.x0+=nudge->x0;
470 if (bp->x1+nudge->x1>=bp->x0+nudge->x0)
471 b.x1+=nudge->x1;
472 if (bp->y0+nudge->y0<=bp->y1+nudge->y1)
473 b.y0+=nudge->y0;
474 if (bp->y1+nudge->y1>=bp->y0+nudge->y0)
475 b.y1+=nudge->y1;
476 window_setBox(w,i,&b);
477 }
478 }
479
480 /* --- If no icons moved, nudge the guidelines --- *
481 *
482 * Only move guidelines if the box is sensible -- i.e. we're not just
483 * moving an edge, we're moving the whole box either horizontally or
484 * vertically
485 */
486
487 if (!doneOne && nudge->x0==nudge->x1 && nudge->y0==nudge->y1)
488 {
489 for (i=0;i<glass_GUIDELIMIT;i++)
490 {
491 if (w->guide[i].selected)
492 {
493 doneOne=TRUE;
494 window__redrawGuide(w,i);
495 if (w->guide[i].horiz)
496 w->guide[i].coord+=nudge->y0;
497 else
498 w->guide[i].coord+=nudge->x0;
499 window__redrawGuide(w,i);
500 }
501 }
502 }
503
504 /* --- If we did something, bump the alteration count --- */
505
506 if (!doneOne)
507 bbc_vdu(7);
508 else
509 tfile_markAsAltered(w->t);
510 }
511
512 /*
513 * void window_boundingBox(glass_windPointer *w,int icon,wimp_box *box)
514 *
515 * Use
516 * Gets the bounding box of the icon given and returns it in the block
517 * pointed to by box
518 *
519 * Parameters
520 * glass_windPointer *w == the window containing the icon
521 * int icon == the icon to 'boxise'
522 * wimp_box *box == where to put the result
523 */
524
525 void window_boundingBox(glass_windPointer *w,int icon,wimp_box *box)
526 {
527 window__bound(&w->def->i[icon].i,box,FALSE);
528 }
529
530 /*
531 * void window_setBox(glass_windPointer *w,int icon,wimp_box *box)
532 *
533 * Use
534 * Sets the icon bounding box to the box given, taking into account
535 * Interface borders and so on.
536 *
537 * Parameters
538 * glass_windPointer *w == the window containing the icon
539 * int icon == the icon number
540 * wimp_box *box == the new box for the icon
541 */
542
543 void window_setBox(glass_windPointer *w,int icon,wimp_box *box)
544 {
545 wimp_box bound;
546 window_boundingBox(w,icon,&bound);
547 window_redrawIcon(w,icon);
548 w->def->i[icon].i.box.x0+=box->x0-bound.x0;
549 w->def->i[icon].i.box.x1+=box->x1-bound.x1;
550 w->def->i[icon].i.box.y0+=box->y0-bound.y0;
551 w->def->i[icon].i.box.y1+=box->y1-bound.y1;
552 window_redrawIcon(w,icon);
553 editIcon_iconMoved(w,icon);
554 }
555
556 /*
557 * void window_deleteIcon(glass_windPointer *w,int icon)
558 *
559 * Use
560 * Deletes the icon specified, good an' proper.
561 *
562 * Parameters
563 * glass_windPointer *w == the scene of the crime
564 * int icon == the victim...
565 */
566
567 void window_deleteIcon(glass_windPointer *w,int icon)
568 {
569 int fhand;
570 editIcon_close(w,icon);
571
572 if (w->def->i[icon].selected)
573 w->selno--;
574 if (w==window_selectionOwner() && icon==window__selectedIcon())
575 window__setSelectedIcon(-1);
576
577 window_redrawIcon(w,icon);
578 if (w->def->i[icon].i.flags & wimp_IFONT)
579 {
580 fhand=(w->def->i[icon].i.flags>>24) & 0xff;
581 wimpt_noerr(font_lose(fhand));
582 w->fonts[fhand]--;
583 }
584 if (w->def->i[icon].i.flags & wimp_INDIRECT)
585 {
586 w->size-=w->def->i[icon].i.data.indirecttext.bufflen;
587 indir_free(w->def->i[icon].i.data.indirecttext.buffer);
588 if (w->def->i[icon].i.flags & wimp_ITEXT &&
589 w->def->i[icon].i.data.indirecttext.validstring!=(char *)-1)
590 {
591 utils_ctermToNterm(w->def->i[icon].i.data.indirecttext.validstring);
592 w->size-=strlen(w->def->i[icon].i.data.indirecttext.validstring)+1;
593 indir_free(w->def->i[icon].i.data.indirecttext.validstring);
594 }
595 }
596
597 w->def->i[icon].i.flags=wimp_IDELETED; /* This is a late icon */
598 w->def->i[icon].selected=FALSE;
599
600 if (gPrefs_current()->mDeleteRenum)
601 window_renumber(w,icon,w->def->desc.w.nicons-1);
602 tfile_markAsAltered(w->t);
603 window__removeTrailingDeleted(w);
604 }
605
606 /*
607 * void window_renumber(glass_windPointer *w,int icon,int new)
608 *
609 * Use
610 * Renumbers an icon, by removing it from the array, shuffling others out
611 * the way, and the putting it in its new position (i.e. it's an insert
612 * renumber, not a swap renumber like the old version - which wasn't
613 * terribly useful...)
614 *
615 * Parameters
616 * glass_windPointer *w == the window containing the icon
617 * int icon == the icon to renumber
618 * int new == the new number to give it
619 *
620 * Returns
621 * TRUE if successful
622 */
623
624 BOOL window_renumber(glass_windPointer *w,int icon,int new)
625 {
626 glass_iconDescription idef=w->def->i[icon];
627 int i;
628 int si=window__selectedIcon();
629
630 if (new<0 || new>=w->def->desc.w.nicons)
631 {
632 note(msgs_lookup("wdIIRN"));
633 return (FALSE);
634 }
635 if (new==icon)
636 return (TRUE);
637 editIcon_renumber(w,icon,new);
638
639 if (new<icon)
640 {
641 if (w==window_selectionOwner() && si!=-1)
642 {
643 if (si==icon)
644 window__renumberSelectedIcon(new);
645 else if (si>=new && si<icon)
646 window__renumberSelectedIcon(si+1);
647 }
648 for (i=icon;i>new;i--)
649 {
650 editIcon_renumber(w,i-1,i);
651 w->def->i[i]=w->def->i[i-1];
652 }
653 }
654 else
655 {
656 if (w==window_selectionOwner() && si!=-1)
657 {
658 if (si==icon)
659 window__renumberSelectedIcon(new);
660 else if (si<=new && si>icon)
661 window__renumberSelectedIcon(si-1);
662 }
663 for (i=icon;i<new;i++)
664 {
665 editIcon_renumber(w,i+1,i);
666 w->def->i[i]=w->def->i[i+1];
667 }
668 }
669 w->def->i[new]=idef;
670 window_redrawIcon(w,new);
671 return (TRUE);
672 }