chiark / gitweb /
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 }