chiark / gitweb /
Initial revision
[ssr] / StraySrc / Glass / !Glass / c / gSprite
1 /*
2  * gSprite.c
3  *
4  * Handling of template file sprite windows and areas
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 <string.h>
35 #include <ctype.h>
36
37 /*
38  * Steel headers
39  */
40
41 #define _STDAPP
42 #define _XFER
43 #define _LOWLVL
44 #include "steel/Steel.h"
45
46 #include "steel/viewer.h"
47 #include "steel/flex.h"
48 #include "steel/akbd.h"
49 #include "steel/bbc.h"
50 #include "steel/choices.h"
51 #include "steel/colourtran.h"
52
53 /*
54  * Glass headers
55  */
56
57 #include "gStruct.h"
58 #include "gMenus.h"
59 #include "gIcons.h"
60
61 #include "glass.h"
62 #include "intMsgs.h"
63 #include "gSprite.h"
64 #include "gPrefs.h"
65 #include "indir.h"
66 #include "window.h"
67 #include "tfile.h"
68
69 /*----- Macros ------------------------------------------------------------*/
70
71 #define gSprite__FILEHEIGHT 872 /* Height to open first sprite window     */
72 #define gSprite__FILEX 220      /* x position of sprite windows           */
73 #define gSprite__FILETOP 920    /* Height after files wrap around         */
74 #define gSprite__CHUNK 2048     /* Size in bytes to allocate areas by     */
75 #define gSprite__WINDHEIGHT 852 /* Height for individual sprite window    */
76 #define gSprite__WINDX 240      /* x position of individual sprite window */
77 #define gSprite__WINDTOP 900    /* Height after wrap around               */
78 #define gSprite__WINDMINX 340   /* Minimum width of a sprite window       */
79 #define gSprite__WINDMINY 150   /* Minimum height of a sprite window      */
80 #define gSprite__BOXX 200       /* Size of the box for sprite display (x) */
81 #define gSprite__BOXY 148       /* Size of the box for sprite display (y) */
82
83 /*----- Type definitions --------------------------------------------------*/
84
85 typedef struct
86 {
87   glass_tfile *t;                /* Template file owner                    */
88   int serial;                    /* Order the icons were created in        */
89   char name[15];                 /* Name of the sprite                     */
90   viewer_icon i;                 /* My viewer icon                         */
91   wimp_w w;                      /* My window handle                       */
92 }
93 gSprite__data;
94
95 /*----- Static variables --------------------------------------------------*/
96
97 static int gSprite__fileHeight=gSprite__FILEHEIGHT;
98                                  /* Height to open next sprite window      */
99 static int gSprite__windHeight=gSprite__WINDHEIGHT;
100
101 static int gSprite__size;       /* Count up sprite sizes                   */
102 static void **gSprite__flex;    /* The flex block for saving selections    */
103 static sprite_area *gSprite__default;   /* Default sprite file             */
104 static int gSprite__serial;     /* Monotonic time counter                  */
105 static glass_tfile *gSprite__selOwner; /* Which window owns selection?     */
106
107 /*----- Support routines --------------------------------------------------*/
108
109 /*
110  * BOOL gSprite__getMemory(glass_tfile *t,int size)
111  *
112  * Use
113  *  Ensures that the specified quantity of memory is available in the sprite
114  *  area given.  If the memory is not there, it is allocated and the pointer
115  *  is placed in the appropriate place.  Messages are sent round as
116  *  applicable.
117  *
118  * Parameters
119  *  glass_tfile *t == the template file to alllcate memory in
120  *  int size == the quantity of memory required to add to the area
121  *
122  * Returns
123  *  TRUE if the operation succeeded, or FALSE for not enough memory available
124  */
125
126 static BOOL gSprite__getMemory(glass_tfile *t,int size)
127 {
128   int aligned;
129   sprite_area *s;
130   if (t->s->freeoff+size<t->s->size) /* Check that the memory is there     */
131     return (TRUE);
132   aligned=(t->s->freeoff+size+gSprite__CHUNK-1) & ~(gSprite__CHUNK-1);
133                                  /* Qunatise by the chunk size             */
134   if (s=indir_realloc(t->s,aligned),!s) /* Allocate memory required        */
135     return (FALSE);              /* Say we failed if we did                */
136   s->size=aligned;               /* Insert the new sprite area size        */
137   t->s=s;                        /* And change the pointers to the new one */
138   return (TRUE);
139 }
140
141 /*
142  * void gSprite__minimise(glass_tfile *t)
143  *
144  * Use
145  *  Attempts to minimise the space taken up by template file t's sprite area
146  *  if wasteful operations took upn too much memory, or sprites have been
147  *  deleted.  Ironically, this requires memory...  However, failure to
148  *  allocate sufficient memory is ignored.
149  *
150  * Parameters
151  *  glass_tfile *t == the template file to minimise
152  */
153
154 static void gSprite__minimise(glass_tfile *t)
155 {
156   sprite_area *s;
157   int aligned=(t->s->freeoff+gSprite__CHUNK-1) & ~(gSprite__CHUNK-1);
158                                  /* Get the size the area should be        */
159   if (t->s->size==aligned)
160     return;
161   if (s=indir_realloc(t->s,aligned),!s)
162     return;
163   s->size=aligned;
164   t->s=s;
165 }
166
167 /*
168  * int gSprite__compare(void *a,void *b)
169  *
170  * Use
171  *  Compares creation times of two icons
172  *
173  * Parameters
174  *  both pointers to data attached to icons
175  */
176
177 static int gSprite__compare(void *a,void *b)
178 {
179   gSprite__data *x=a;
180   gSprite__data *y=b;
181   return (x->serial-y->serial);
182 }
183
184 /*
185  * void gSprite__gainSelection(glass_tfile *t)
186  *
187  * Use
188  *  Hands the current sprite selection and the input focus to the specified
189  *  template file sprite viewer.
190  *
191  *  Note that the selection model here is more complex than it is in window,
192  *  since the focus tfile may not coincide with the selection owner (since
193  *  menu clicks move the selection owner, but not the focus).
194  *
195  * Parameters
196  *  glass_tfile *t == the sprite viewer to which we give the selection
197  */
198
199 static void gSprite__gainSelection(glass_tfile *t)
200 {
201   wimp_caretstr c;
202   if (t!=gSprite__selOwner)
203   {
204     if (gSprite__selOwner)
205       viewer_selectAll(gSprite__selOwner->vs,FALSE);
206     gSprite__selOwner=t;
207   }
208   if (t)
209   {
210     c.w=viewer_syshandle(t->vs);
211     c.i=-1;
212     c.x=-250;
213     c.y=0;
214     c.index=-1;
215     c.height=0x02000000;
216     wimpt_noerr(wimp_set_caret_pos(&c));
217   }
218 }
219
220 /*
221  * void gSprite__closeWindows(void *handle)
222  *
223  * Use
224  *  Destroys any windows open when the template file dies
225  *
226  * Parameters
227  *  void *handle == pointer to window data
228  */
229
230 static void gSprite__closeWindows(void *handle)
231 {
232   gSprite__data *d=handle;
233   if (d->w)
234   {
235     win_register_event_handler(d->w,0,0);
236     wimpt_noerr(wimp_delete_wind(d->w));
237     win_activedec();
238   }
239   mem_free(d);
240 }
241
242 /*
243  * void gSprite__sizes(viewer_icon i,void *handle)
244  *
245  * Use
246  *  Adds the size of the given sprite to the total in gSprite__size.
247  *
248  * Parameters
249  *  viewer_icon i == the icon number of the sprite (ignored)
250  *  void *handle == pointer to sprite data
251  */
252
253 static void gSprite__sizes(viewer_icon i,void *handle)
254 {
255   sprite_id sid;
256   sprite_header *hdr;
257   gSprite__data *d=handle;
258   unused(i);
259   sid.s.name=d->name;
260   sid.tag=0;
261   wimpt_noerr(sprite_select_rp(d->t->s,&sid,(sprite_ptr *)&hdr));
262   gSprite__size+=hdr->next;
263 }
264
265 /*
266  * char *gSprite__lower(char *s)
267  *
268  * Use
269  *  Translates the string given to lower case
270  *
271  * Parameters
272  *  char *s == the string to convert
273  *
274  * Returns
275  *  A pointer to the string
276  */
277
278 static char *gSprite__lower(char *s)
279 {
280   char *p=s;
281   while (*p)
282   {
283     *p=tolower(*p);
284     p++;
285   }
286   return (s);
287 }
288
289 /*
290  * BOOL gSprite__saveArea(void *handle,char *filename)
291  *
292  * Use
293  *  Saves a sprite area to disk
294  *
295  * Parameters
296  *  char *filename == the filename to save to
297  *  void *handle == pointer to owning template file
298  *
299  * Returns
300  *  TRUE for success
301  */
302
303 static BOOL gSprite__saveArea(char *filename,void *handle)
304 {
305   glass_tfile *t=handle;
306   if (gPrefs_current()->cSave &&
307       saveas_file_is_safe() &&
308       res_fileExists(filename))
309   {
310     if (!warning(msgs_lookup("spSOFP"),msgs_lookup("spSOF"),filename))
311       return (FALSE);
312   }
313   if (utils_complain(sprite_area_save(t->s,filename),msgs_lookup("spESS")))
314     return (FALSE);
315   else
316     return (TRUE);
317 }
318
319 /*
320  * BOOL gSprite__sendArea(void *handle,int *maxbuf)
321  *
322  * Use
323  *  Sends a sprite area via RAM transfer
324  *
325  * Parameters
326  *  void *handle == pointer to owning template file
327  *  int *maxbuf == pointer to buffer size of other application
328  */
329
330 static BOOL gSprite__sendArea(void *handle,int *maxbuf)
331 {
332   glass_tfile *t=handle;
333   return (xfersend_sendBlock(&t->s->number,t->s->freeoff-4,maxbuf));
334 }
335
336 /*
337  * void gSprite__addToSelSave(viewer_icon i,void *handle)
338  *
339  * Use
340  *  Adds the given sprite to the sprite area held in gSprite__flex
341  *
342  * Parameters
343  *  viewer_icon i == the icon number of the sprite to add (ignored)
344  *  void *handle == pointer to sprite data
345  */
346
347 static void gSprite__addToSelSave(viewer_icon i,void *handle)
348 {
349   gSprite__data *d=handle;
350   sprite_header *hdr;
351   sprite_id sid;
352   void *p=charptr(*gSprite__flex,0)+
353           _ptr(sprite_area,*gSprite__flex,0)->freeoff;
354   unused(i);
355   sid.s.name=d->name;
356   sid.tag=0;
357   wimpt_noerr(sprite_select_rp(d->t->s,&sid,(sprite_ptr *)&hdr));
358   memcpy(p,hdr,hdr->next);
359   _ptr(sprite_area,*gSprite__flex,0)->freeoff+=hdr->next;
360 }
361
362 /*
363  * BOOL gSprite__setupSelSave(glass_tfile *t,void **p)
364  *
365  * Use
366  *  Sets up a sprite area to save in a flex block anchored on p.  The flex
367  *  block is allocaed here.  The caller must free the block itself.
368  *
369  * Parameters
370  *  glass_tfile *t == the template file owning the selection
371  *  void **p == pointer to a flex anchor
372  *
373  * Returns
374  *  TRUE if successful
375  */
376
377 static BOOL gSprite__setupSelSave(glass_tfile *t,void **p)
378 {
379   if (!flex_alloc(p,gSprite__size+16))
380   {
381     werr(FALSE,msgs_lookup("spNEMSLSV"));
382     return (FALSE);
383   }
384   gSprite__flex=p;
385   _ptr(sprite_area,*p,0)->size=gSprite__size+16;
386   _ptr(sprite_area,*p,0)->number=viewer_selected(t->vs);
387   _ptr(sprite_area,*p,0)->freeoff=16;
388   _ptr(sprite_area,*p,0)->sproff=16;
389   viewer_doForIcons(t->vs,TRUE,gSprite__addToSelSave);
390   return (TRUE);
391 }
392
393 /*
394  * BOOL gSprite__saveSelection(char *filename,void *handle)
395  *
396  * Use
397  *  Saves a selection of sprite to a file
398  *
399  * Parameters
400  *  char *filename == the file to save to
401  *  void *handle == pointer to the owning template file
402  *
403  * Returns
404  *  TRUE for success
405  */
406
407 static BOOL gSprite__saveSelection(char *filename,void *handle)
408 {
409   glass_tfile *t=handle;
410   void *p;
411   if (gPrefs_current()->cSave &&
412       saveas_file_is_safe() &&
413       res_fileExists(filename))
414   {
415     if (!warning(msgs_lookup("spSOFP"),msgs_lookup("spSOF"),filename))
416       return (FALSE);
417   }
418   if (!gSprite__setupSelSave(t,&p))
419     return (FALSE);
420   if (utils_complain(sprite_area_save((sprite_area *)p,filename),
421                      msgs_lookup("spESS")))
422   {
423     flex_free(&p);
424     return (FALSE);
425   }
426   else
427   {
428     flex_free(&p);
429     return (TRUE);
430   }
431 }
432
433 /*
434  * BOOL gSprite__sendSelection(void *handle,int *maxbuf)
435  *
436  * Use
437  *  Sends a selection of sprite to another application in memory
438  *
439  * Parameters
440  *  void *handle == pointer to the owning template file
441  *  int *maxbuf == the size of the receiving application's buffer
442  *
443  * Returns
444  *  TRUE for success
445  */
446
447 static BOOL gSprite__sendSelection(void *handle,int *maxbuf)
448 {
449   glass_tfile *t=handle;
450   void *p;
451   if (!gSprite__setupSelSave(t,&p))
452     return (FALSE);
453   if (!xfersend_sendBlock((char *)p+4,gSprite__size+12,maxbuf))
454   {
455     flex_free(&p);
456     return (FALSE);
457   }
458   else
459   {
460     flex_free(&p);
461     return (TRUE);
462   }
463 }
464
465 /*
466  * BOOL gSprite__saveGrabbed(void *handle,char *filename)
467  *
468  * Use
469  *  Saves a sprite area to disk
470  *
471  * Parameters
472  *  char *filename == the filename to save to
473  *  void *handle == pointer-to-pointer to sprite area
474  *
475  * Returns
476  *  TRUE for success
477  */
478
479 static BOOL gSprite__saveGrabbed(char *filename,void *handle)
480 {
481   sprite_area **s=handle;
482   if (gPrefs_current()->cSave &&
483       saveas_file_is_safe() &&
484       res_fileExists(filename))
485   {
486     if (!warning(msgs_lookup("spSOFP"),msgs_lookup("spSOF"),filename))
487       return (FALSE);
488   }
489   if (utils_complain(sprite_area_save(*s,filename),msgs_lookup("spESS")))
490     return (FALSE);
491   else
492     return (TRUE);
493 }
494
495 /*
496  * BOOL gSprite__sendGrabbed(void *handle,int *maxbuf)
497  *
498  * Use
499  *  Sends a sprite area via RAM transfer
500  *
501  * Parameters
502  *  void *handle == pointer-to-pointer to sprite area
503  *  int *maxbuf == pointer to buffer size of other application
504  */
505
506 static BOOL gSprite__sendGrabbed(void *handle,int *maxbuf)
507 {
508   sprite_area **s=handle;
509   return (xfersend_sendBlock(&(*s)->number,(*s)->freeoff-4,maxbuf));
510 }
511
512 /*
513  * void gSprite__grab(wimp_mousestr *m,void *handle)
514  *
515  * Use
516  *  Grabs a sprite area from a given window
517  *
518  * Parameters
519  *  wimp_mousestr *m == info about the window/icon the pointer is over
520  *  void *handle ==  a pointer (ignored)
521  */
522
523 static void gSprite__grab(wimp_mousestr *m,void *handle)
524 {
525   wimp_winfo *w;
526   os_regset r;
527   void *p;
528   void *q;
529   sprite_area s;
530   wimp_msgstr msg;
531   unused(handle);
532   switch (m->w)
533   {
534     case -1:
535     case -2:
536       werr(FALSE,msgs_lookup("spCGS"));
537       return;
538   }
539   if (wimpt_getVersion()>=300)   /* RISC OS 3 allows 'safe' GetWindowInfos */
540     w=mem_alloc(sizeof(wimp_winfo));
541   else
542     w=mem_alloc(sizeof(wimp_winfo)+200*sizeof(wimp_icon));
543   if (!w)
544   {
545     werr(FALSE,msgs_lookup("spNEMGS"));
546     return;
547   }
548   w->w=m->w;
549   r.r[1]=(int)w;
550   if (wimpt_getVersion()>=300)
551     r.r[1]+=1;                   /* RO3 - only window header, not icons    */
552   wimpt_noerr(os_swix(XWimp_GetWindowInfo,&r)); /* Get the information     */
553   q=w->info.spritearea;          /* Get the window's sprite area           */
554   mem_free(w);                   /* No longer needed                       */
555   if ((int)q<0x8000)
556   {
557     note(msgs_lookup("spNAUSA"));
558     return;
559   }
560   else if ((int)q>=0x01800000)
561   {
562     saveas(msgs_lookup("spSVSPR"),
563            msgs_lookup("spGRB"),
564            0xff9,
565            s.freeoff,
566            gSprite__saveGrabbed,
567            gSprite__sendGrabbed,
568            0,
569            &q);
570   }
571   else
572   {
573     msg.hdr.size=20;
574     msg.hdr.your_ref=0;
575     r.r[0]=19;
576     r.r[1]=(int)&msg;
577     r.r[2]=m->w;
578     r.r[3]=m->i;
579     wimpt_noerr(os_swix(XWimp_SendMessage,&r)); /* Find the task's handle  */
580     wimpt_noerr(wimp_transferblock(r.r[2],
581                                    q,
582                                    wimpt_task(),
583                                    (char *)&s,
584                                    sizeof(sprite_area)));
585     if (!flex_alloc(&p,s.freeoff))
586     {
587       werr(FALSE,msgs_lookup("spNEMGS"));
588       return;
589     }
590     wimpt_noerr(wimp_transferblock(r.r[2],q,wimpt_task(),p,s.freeoff));
591     saveas(msgs_lookup("spSVSPR"),
592            msgs_lookup("spGRB"),
593            0xff9,
594            s.freeoff,
595            gSprite__saveGrabbed,
596            gSprite__sendGrabbed,
597            0,
598            &p);
599     flex_free(&p);
600   }
601 }
602
603 /*
604  * BOOL gSprite__copy(char *buff,void *handle)
605  *
606  * Use
607  *  Copies a sprite, ensuring that the name hasn't been duplicated etc.
608  *
609  * Parameters
610  *  char *buff == pointer to the new name
611  *  void *handle == pointer to data for the sprite
612  *
613  * Returns
614  *  TRUE if everything went OK
615  */
616
617 static BOOL gSprite__copy(char *buff,void *handle)
618 {
619   gSprite__data *d=handle;
620   gSprite__data *new;
621   sprite_id sid;
622   sprite_header *hdr;
623   viewer_icon i=viewer_findIcon(d->t->vs,buff);
624   sid.s.name=d->name;
625   sid.tag=0;
626   gSprite__lower(buff);
627   if (i==viewer_NOICON || i==d->i)
628   {
629     mem_useUser(indir_alloc,indir_free);
630     if (new=mem_alloc(sizeof(gSprite__data)),!new)
631     {
632       werr(FALSE,msgs_lookup("spNEMCPY"));
633       mem_useMalloc();
634       return (FALSE);
635     }
636     wimpt_noerr(sprite_select_rp(d->t->s,&sid,(sprite_ptr *)&hdr));
637     if (!gSprite__getMemory(d->t,hdr->next+50))
638     {
639       mem_free(new);
640       mem_useMalloc();
641       werr(FALSE,msgs_lookup("spNEMCPY"));
642       return (FALSE);
643     }
644     wimpt_noerr(sprite_copy(d->t->s,&sid,buff));
645     gSprite__minimise(d->t);
646     strcpy(new->name,buff);
647     new->serial=gSprite__serial++;
648     new->i=viewer_addIcon(d->t->vs,buff,buff,TRUE,new);
649     viewer_setFiletype(new->i,0xff9);
650     mem_useMalloc();
651     new->w=0;
652     new->t=d->t;
653     if (!dbox_wasAdjustClick())
654       viewer_clickSelect(d->t->vs,viewer_NOICON,wimp_BMID);
655     intMsgs_send(glass_SPRITECHANGE,d->t);
656     return (TRUE);
657   }
658   note(msgs_lookup("spNAE"),
659        viewer_textOfIcon(i));
660   return (FALSE);
661
662 }
663
664 /*
665  * BOOL gSprite__rename(char *buff,void *handle)
666  *
667  * Use
668  *  Renames a sprite, ensuring that the name hasn't been duplicated etc.
669  *
670  * Parameters
671  *  char *buff == pointer to the new name
672  *  void *handle == pointer to data for the sprite
673  *
674  * Returns
675  *  TRUE if everything went OK
676  */
677
678 static BOOL gSprite__rename(char *buff,void *handle)
679 {
680   gSprite__data *d=handle;
681   sprite_id sid;
682   viewer_icon i=viewer_findIcon(d->t->vs,buff);
683   sid.s.name=d->name;
684   sid.tag=0;
685   gSprite__lower(buff);
686   if (i==viewer_NOICON || i==d->i)
687   {
688     mem_useUser(indir_alloc,indir_free);
689     i=viewer_addIcon(d->t->vs,buff,buff,TRUE,d);
690     mem_useMalloc();
691     if (!i)
692       return (FALSE);
693     viewer_setFiletype(i,0xff9);
694     wimpt_noerr(sprite_rename(d->t->s,&sid,buff));
695     viewer_removeIcon(d->i);
696     d->i=i;
697     strcpy(d->name,buff);
698     intMsgs_send(glass_SPRITECHANGE,d->t);
699     if (!dbox_wasAdjustClick())
700       viewer_clickSelect(d->t->vs,viewer_NOICON,wimp_BMID);
701     event_clear_current_menu();
702     return (TRUE);
703   }
704   note(msgs_lookup("spNAE"),
705        viewer_textOfIcon(i));
706   return (FALSE);
707 }
708
709 /*
710  * void gSprite__delSprites(viewer_icon i,void *handle)
711  *
712  * Use
713  *  Deletes the given sprite.
714  *
715  * Parameters
716  *  viewer_icon i == the icon handle that it is
717  *  void *handle == the sprite data pointer
718  */
719
720 static void gSprite__delSprites(viewer_icon i,void *handle)
721 {
722   sprite_id sid;
723   gSprite__data *d=handle;
724   unused(i);
725   sid.s.name=d->name;
726   sid.tag=0;
727   wimpt_noerr(sprite_delete(d->t->s,&sid));
728   viewer_removeIcon(d->i);
729   if (d->w)
730   {
731     win_register_event_handler(d->w,0,0);
732     win_activedec();
733     wimpt_noerr(wimp_delete_wind(d->w));
734   }
735   mem_free(d);
736 }
737
738 /*
739  * void gSprite__createWindow(viewer_icon i,void *handle)
740  *
741  * Use
742  *  Opens a window to display a given sprite.
743  *
744  * Parameters
745  *  viewer_icon i == the icon representing the sprite
746  *  void *handle == the information to add to the window
747  */
748
749 static void gSprite__windowHandler(wimp_eventstr *e,void *handle);
750
751 static void gSprite__createWindow(viewer_icon i,void *handle)
752 {
753   wimp_wstate s;
754   gSprite__data *d=handle;
755   wimp_wind w=
756   {
757     0,0,0,0, 0,0, -1,
758     wimp_WMOVEABLE |
759       wimp_WNEW |
760       wimp_WTITLE |
761       wimp_WTOGGLE |
762       wimp_WVSCR |
763       wimp_WHSCR |
764       wimp_WSIZE |
765       wimp_WBACK |
766       wimp_WQUIT,
767     7,2,7,1,3,1,12,0,
768     0,0,gSprite__WINDMINX,gSprite__WINDMINY,
769     wimp_ITEXT | wimp_IHCENTRE,
770     0,
771     0,
772     0x00010001,
773     "",
774     0,
775   };
776   sprite_id sid;
777   sprite_info info;
778   unused(i);
779   if (!d->w)
780   {
781     strcpy(w.title.text,d->name);
782     sid.s.name=d->name;
783     sid.tag=0;
784     wimpt_noerr(sprite_readsize(d->t->s,&sid,&info));
785     info.width <<= bbc_modevar(info.mode,bbc_XEigFactor);
786     info.height <<= bbc_modevar(info.mode,bbc_YEigFactor);
787     if (info.width>gSprite__WINDMINX)
788       w.ex.x1=info.width;
789     if (info.height>gSprite__WINDMINY)
790       w.ex.y1=info.height;
791     w.box.x0=gSprite__WINDX;
792     w.box.x1=gSprite__WINDX+w.ex.x1;
793     w.box.y1=gSprite__windHeight;
794     w.box.y0=gSprite__windHeight-w.ex.y1;
795     if (w.ex.x1>500)
796       w.box.x1=gSprite__WINDX+500;
797     if (w.ex.y1>500)
798       w.box.y0=gSprite__windHeight-500;
799     if (wimpt_complain(wimp_create_wind(&w,&d->w)))
800       return;
801     win_register_event_handler(d->w,gSprite__windowHandler,d);
802     win_activeinc();
803     gSprite__windHeight-=48;
804     if (gSprite__windHeight<612)
805       gSprite__windHeight=gSprite__WINDTOP;
806   }
807   wimpt_noerr(wimp_get_wind_state(d->w,&s));
808   wimpt_noerr(wimp_open_wind(&s.o));
809 }
810
811 /*----- Event handlers ----------------------------------------------------*/
812
813 /*
814  * BOOL tfile__dragUnknowns(wimp_eventstr *e,void *handle)
815  *
816  * Use
817  *  Handles unknown events during the period of dragging viewer icons
818  *  around.  It will respond to the following drags:
819  *
820  *  A drag to a blank area of icon bar will open the windows.
821  *
822  *  Otherwise, a selection save will be started, and the windows packaged
823  *  off to another application.
824  *
825  * Parameters
826  *  wimp_eventstr *e == the event to look at
827  *  void *handle == the template file whose sprite area we're dragging from
828  *
829  * Returns
830  *  TRUE if the drag has been processed
831  */
832
833 static BOOL gSprite__dragUnknowns(wimp_eventstr *e,void *handle)
834 {
835   glass_tfile *t=handle;
836   wimp_mousestr m;
837   BOOL handled=FALSE;
838   switch (e->e)
839   {
840     case wimp_EUSERDRAG:
841       handled=TRUE;
842       win_remove_unknown_event_processor(gSprite__dragUnknowns,t);
843       wimpt_noerr(wimp_get_point_info(&m));
844       if (m.w==viewer_syshandle(t->vs))
845         break;
846       if (m.w==-2 && m.i==-1)
847         viewer_doForIcons(t->vs,TRUE,gSprite__createWindow);
848       else
849       {
850           gSprite__size=0;
851           viewer_doForIcons(t->vs,TRUE,gSprite__sizes);
852           wimpt_fake_event(e);          /* Fool xfersend to send data      */
853           xfersend(0xFF9,
854                    msgs_lookup("spSEL"),
855                    gSprite__size+16,
856                    gSprite__saveSelection,
857                    gSprite__sendSelection,
858                    0,
859                    e,
860                    t);
861       }
862       viewer_selectAll(t->vs,FALSE);
863       break;
864   }
865   return (handled);
866 }
867
868 /*
869  * void gSprite__viewerRedraw(viewer_icon i,
870  *                             wimp_redrawstr *r,
871  *                             wimp_box *box,
872  *                             char *text,
873  *                             char *sprite,
874  *                             BOOL selected,
875  *                             void *handle)
876  *
877  * Use
878  *  Redraws an icon in the sprite viewer.
879  *
880  * Parameters
881  *  viewer_icon i == the icon I'm redrawing
882  *  wimp_redrawstr *r == the redraw structure
883  *  wimp_box *box == the box to fit it in
884  *  char *text == the text to draw
885  *  char *sprite == the sprite to draw (ignored)
886  *  BOOL selected == whether the icon is selected
887  *  void *handle == pointer to the template file
888  */
889
890 static void gSprite__viewerRedraw(viewer_icon i,
891                                    wimp_redrawstr *r,
892                                    wimp_box *box,
893                                    char *text,
894                                    char *sprite,
895                                    BOOL selected,
896                                    void *handle)
897 {
898   int ox=r->box.x0-r->scx;
899   int oy=r->box.y1-r->scy;
900   sprite_id sid;
901   sprite_header *hdr;
902   sprite_pixtrans colbuff[256];  /* Colour translation table               */
903   sprite_factors zoom;           /* Zoomage table                          */
904   sprite_info info;              /* Info about the sprite                  */
905   glass_tfile *t=handle;
906   int newy;                      /* Bodged height of the sprite            */
907   int xoff,yoff;                 /* x and y offsets to display sprite      */
908   wimp_icon spname;              /* Virtual icon for sprite name           */
909   unused(i);
910   unused(sprite);
911
912   spname.box.x0=box->x0;
913   spname.box.x1=box->x1;
914   spname.box.y0=box->y0;
915   spname.box.y1=box->y0+40;
916   spname.flags=0x1700010b+(selected<<21);
917   spname.data.indirecttext.buffer=text;
918   spname.data.indirecttext.validstring="Sblank";
919   wimpt_noerr(wimp_ploticon(&spname));
920
921   sid.s.name=text;
922   sid.tag=0;
923   wimpt_noerr(sprite_select_rp(t->s,&sid,(sprite_ptr *)&hdr));
924   sid.s.addr=hdr;
925   sid.tag=sprite_id_addr;
926   wimpt_noerr(sprite_readsize(t->s,&sid,&info));
927   if (bbc_modevar(info.mode,bbc_NColour)==63) /* 256 colour mode - handle  */
928     colourtran_select_table(info.mode,0,-1,(wimp_paletteword *)-1,colbuff);
929   else
930     wimpt_noerr(wimp_readpixtrans(t->s,&sid,&zoom,colbuff));
931   zoom.xmag=zoom.ymag=zoom.xdiv=zoom.ydiv=1;
932   info.width <<= bbc_modevar(info.mode,bbc_XEigFactor); /* Now OS units    */
933   info.height <<= bbc_modevar(info.mode,bbc_YEigFactor);
934
935   /* --- Bodge the multipliers to fit the sprite in --- */
936
937   newy=info.height;
938   if (info.width>gSprite__BOXX)
939   {
940     zoom.xmag*=gSprite__BOXX;
941     zoom.ymag*=gSprite__BOXX;
942     zoom.xdiv*=info.width;
943     zoom.ydiv*=info.width;
944     newy=(newy*gSprite__BOXX)/info.width+1;
945   }
946
947   if (newy>gSprite__BOXY)
948   {
949     zoom.xmag*=gSprite__BOXY;
950     zoom.ymag*=gSprite__BOXY;
951     zoom.xdiv*=newy;
952     zoom.ydiv*=newy;
953   }
954
955   info.width=info.width*zoom.xmag/zoom.xdiv;
956   info.height=info.height*zoom.ymag/zoom.ydiv;
957   xoff=(gSprite__BOXX-info.width)/2;
958   yoff=(gSprite__BOXY-info.height)/2+48;
959   zoom.xmag <<= bbc_modevar(info.mode,bbc_XEigFactor);
960   zoom.ymag <<= bbc_modevar(info.mode,bbc_YEigFactor);
961   zoom.xdiv <<= bbc_vduvar(bbc_XEigFactor);
962   zoom.ydiv <<= bbc_vduvar(bbc_YEigFactor);
963
964   wimpt_noerr(sprite_put_scaled(t->s,
965                                 &sid,
966                                 8,
967                                 ox+box->x0+xoff,
968                                 oy+box->y0+yoff,
969                                 &zoom,
970                                 colbuff));
971 }
972
973 /*
974  * void gSprite__windowHandler(wimp_eventstr *e,void *handle)
975  *
976  * Use
977  *  Handles events for individual sprite windows
978  *
979  * Parameters
980  *  wimp_eventstr *e == the event to handle
981  *  void *handle == pointer to data structure
982  */
983
984 static void gSprite__windowHandler(wimp_eventstr *e,void *handle)
985 {
986   gSprite__data *d=handle;
987   wimp_redrawstr r;
988   int ox,oy;
989   BOOL more;
990   sprite_id sid;
991   sprite_factors zoom;
992   sprite_pixtrans colbuff[256];
993   sprite_header *hdr;
994   sprite_info info;
995
996   switch (e->e)
997   {
998     case wimp_EREDRAW:
999       r.w=d->w;
1000       wimpt_noerr(wimp_redraw_wind(&r,&more));
1001       ox=r.box.x0-r.scx;
1002       oy=r.box.y1-r.scy;
1003       while (more)
1004       {
1005         sid.s.name=d->name;
1006         sid.tag=0;
1007         wimpt_noerr(sprite_select_rp(d->t->s,&sid,(sprite_ptr *)&hdr));
1008         sid.s.addr=hdr;
1009         sid.tag=sprite_id_addr;
1010         wimpt_noerr(sprite_readsize(d->t->s,&sid,&info));
1011         if (bbc_modevar(info.mode,bbc_NColour)==63)
1012         {
1013           colourtran_select_table(info.mode,
1014                                   0,
1015                                   -1,
1016                                   (wimp_paletteword *)-1,
1017                                   colbuff);
1018         }
1019         else
1020           wimpt_noerr(wimp_readpixtrans(d->t->s,&sid,&zoom,colbuff));
1021         zoom.xmag=zoom.ymag=zoom.xdiv=zoom.ydiv=1;
1022         info.width <<= bbc_modevar(info.mode,bbc_XEigFactor);
1023         info.height <<= bbc_modevar(info.mode,bbc_YEigFactor);
1024
1025         info.width=info.width*zoom.xmag/zoom.xdiv;
1026         info.height=info.height*zoom.ymag/zoom.ydiv;
1027         zoom.xmag <<= bbc_modevar(info.mode,bbc_XEigFactor);
1028         zoom.ymag <<= bbc_modevar(info.mode,bbc_YEigFactor);
1029         zoom.xdiv <<= bbc_vduvar(bbc_XEigFactor);
1030         zoom.ydiv <<= bbc_vduvar(bbc_YEigFactor);
1031
1032         wimpt_noerr(sprite_put_scaled(d->t->s,&sid,8,ox,oy,&zoom,colbuff));
1033
1034         wimpt_noerr(wimp_get_rectangle(&r,&more));
1035       }
1036       break;
1037     case wimp_EOPEN:
1038       wimpt_noerr(wimp_open_wind(&e->data.o));
1039       break;
1040     case wimp_ECLOSE:
1041       win_register_event_handler(d->w,0,0);
1042       win_activedec();
1043       wimpt_noerr(wimp_delete_wind(d->w));
1044       d->w=0;
1045       break;
1046     case wimp_ESEND:
1047     case wimp_ESENDWANTACK:
1048       switch (e->data.msg.hdr.action)
1049       {
1050         case wimp_MHELPREQUEST:
1051           help_startHelp();
1052           help_addLine(msgs_lookup("sphSPDW"),d->name);
1053           help_endHelp();
1054           break;
1055       }
1056       break;
1057   }
1058 }
1059
1060 /*
1061  * void gSprite__menuHelp(int hit[],void *handle)
1062  *
1063  * Use
1064  *  Responds to help requests for the sprite viewer menu
1065  *
1066  * Parameters
1067  *  int hit[] == array of menu selections
1068  *  void *handle == pointer to owning template file (unused)
1069  */
1070
1071 static void gSprite__menuHelp(int hit[],void *handle)
1072 {
1073   unused(handle);
1074   help_startHelp();
1075   help_readFromMenu("spmhSPM",hit);
1076   help_endHelp();
1077 }
1078
1079 /*
1080  * void gSprite__menuHandler(int hit[],void *handle)
1081  *
1082  * Use
1083  *  Responds to menu selections from the sprite viewer menu
1084  *
1085  * Parameters
1086  *  int hit[] == array of menu selections
1087  *  void *handle == pointer to owning template file
1088  */
1089
1090 static void gSprite__menuHandler(int hit[],void *handle)
1091 {
1092   glass_tfile *t=handle;
1093   wimp_mousestr m;
1094   char buff[50];
1095   dbox d;
1096   sprite_id sid;
1097   sprite_info info;
1098   sprite_header *hdr;
1099   switch (hit[0])
1100   {
1101     case 0:
1102       viewer_clickSelect(t->vs,viewer_NOICON,wimp_BMID);
1103       break;
1104     case glass_SPINFO:
1105        if (d=dbox_create("sprFileInfo"),d)
1106        {
1107          dbox_setfield(d,glass_SAOWNER,"%.%s",t->filename);
1108          dbox_setfield(d,
1109                        glass_SASIZE,
1110                        "%s",
1111                        utils_cvtSize(t->s->freeoff-4));
1112          dbox_setfield(d,glass_SASPRITES,"%i",t->s->number);
1113          mbox(d,"sphSAINF");
1114        }
1115        break;
1116     case glass_SPSEL:
1117       switch (hit[1])
1118       {
1119         case glass_SPSELINFO:
1120           if (viewer_selected(t->vs)==1)
1121           {
1122             if (d=dbox_create("spriteInfo"),d)
1123             {
1124               sid.s.name=((gSprite__data *)
1125                        viewer_iconHandle(viewer_firstSelected(t->vs)))->name;
1126               sid.tag=0;
1127               wimpt_noerr(sprite_readsize(t->s,&sid,&info));
1128               wimpt_noerr(sprite_select_rp(t->s,&sid,(sprite_ptr *)&hdr));
1129               dbox_setfield(d,glass_SPNAME,"%s",sid.s.name);
1130               dbox_setfield(d,glass_SPMODE,"%i",info.mode);
1131               dbox_setfield(d,glass_SPWIDTH,"%i",info.width);
1132               dbox_setfield(d,glass_SPHEIGHT,"%i",info.height);
1133               dbox_setfield(d,
1134                             glass_SPSIZE,
1135                             "%s",
1136                             utils_cvtSize(hdr->next+16));
1137               dbox_setfield(d,
1138                             glass_SPMASK,
1139                             "%s",
1140                             msgs_lookup(info.mask ? "yes" : "no"));
1141               dbox_setfield(d,
1142                             glass_SPPALETTE,
1143                             "%s",
1144                             msgs_lookup(hdr->image!=0x2c ? "yes" : "no"));
1145               mbox(d,"sphSPINF");
1146             }
1147           }
1148           else
1149           {
1150             if (d=dbox_create("sprSelInfo"),d)
1151             {
1152               dbox_setfield(d,glass_SSNUM,"%i",viewer_selected(t->vs));
1153               gSprite__size=0;
1154               viewer_doForIcons(t->vs,TRUE,gSprite__sizes);
1155               dbox_setfield(d,
1156                             glass_SPSIZE,
1157                             "%s",
1158                             utils_cvtSize(gSprite__size+16));
1159               mbox(d,"sphSSINF");
1160             }
1161           }
1162           break;
1163         case glass_SPSELCOPY:
1164           writable
1165           (
1166             msgs_lookup("spCOPY"),
1167             viewer_textOfIcon(viewer_firstSelected(t->vs)),
1168             buff,
1169             gSprite__copy,
1170             viewer_iconHandle(viewer_firstSelected(t->vs))
1171           );
1172           break;
1173         case glass_SPSELRENAME:
1174           writable(msgs_lookup("spREN"),
1175                    viewer_textOfIcon(viewer_firstSelected(t->vs)),
1176                    buff,
1177                    gSprite__rename,
1178                    viewer_iconHandle(viewer_firstSelected(t->vs)));
1179           break;
1180         case glass_SPSELSAVE:
1181           gSprite__size=0;
1182           viewer_doForIcons(t->vs,TRUE,gSprite__sizes);
1183           saveas(msgs_lookup("spSVSEL"),
1184                  msgs_lookup("spSEL"),
1185                  0xff9,
1186                  gSprite__size+16,
1187                  gSprite__saveSelection,
1188                  gSprite__sendSelection,
1189                  0,
1190                  t);
1191           break;
1192         case glass_SPSELDELETE:
1193           viewer_doForIcons(t->vs,TRUE,gSprite__delSprites);
1194           gSprite__minimise(t);
1195           intMsgs_send(glass_SPRITECHANGE,t);
1196           break;
1197       }
1198       break;
1199     case glass_SPSELALL:
1200       viewer_selectAll(t->vs,TRUE);
1201       break;
1202     case glass_SPCLRSEL:
1203       viewer_selectAll(t->vs,FALSE);
1204       break;
1205     case glass_SPSAVE:
1206       saveas(msgs_lookup("spSVSPR"),
1207              "Sprites",
1208              0xff9,
1209              t->s->freeoff-4,
1210              gSprite__saveArea,
1211              gSprite__sendArea,
1212              0,
1213              t);
1214       break;
1215     case glass_SPGRAB:
1216       window_grab(gSprite__grab,0);
1217       break;
1218   }
1219   if (wimpt_last_event()->e==wimp_EMENU)
1220   {
1221     wimpt_noerr(wimp_get_point_info(&m));
1222     if (m.bbits!=wimp_BRIGHT)
1223       viewer_clickSelect(t->vs,viewer_NOICON,wimp_BMID);
1224   }
1225 }
1226
1227 /*
1228  * menu gSprite__menuMaker(void *handle)
1229  *
1230  * Use
1231  *  Creates a menu for the sprite file viewer
1232  *
1233  * Parameters
1234  *  void *handle == pointer to owning template file
1235  *
1236  * Returns
1237  *  Pointer to the menu it has set up
1238  */
1239
1240 static menu gSprite__menuMaker(void *handle)
1241 {
1242   static menu m;                 /* The main menu pointer                  */
1243   static menu sprsm;             /* Submenu for sprite options             */
1244   static char sprName[50];       /* Buffer for sprite name                 */
1245   glass_tfile *t=handle;
1246   if (!m)                        /* Do we have to create the menu?         */
1247   {
1248     m=menu_new("Sprites",msgs_lookup("spM"));
1249     sprsm=menu_new("_",msgs_lookup("spSS"));
1250     menu_submenu(m,glass_SPSEL,sprsm);
1251     menu_redirectItem(m,glass_SPSEL,sprName,50,0);
1252   }
1253   menu_minWidth(m,0);
1254   viewer_setupMenu(t->vs,
1255                    msgs_lookup("spSPR"),
1256                    m,
1257                    glass_SPSEL,
1258                    sprName);
1259   switch (viewer_selected(t->vs))
1260   {
1261     case 0:
1262       menu_setflags(m,glass_SPCLRSEL,FALSE,TRUE);
1263       menu_setflags(m,glass_SPSELALL,FALSE,!viewer_icons(t->vs));
1264       menu_settitle(sprsm,msgs_lookup("spSPR"));
1265       menu_setflags(sprsm,glass_SPSELINFO,FALSE,TRUE);
1266       menu_setflags(sprsm,glass_SPSELCOPY,FALSE,TRUE);
1267       menu_setflags(sprsm,glass_SPSELRENAME,FALSE,TRUE);
1268       menu_setflags(sprsm,glass_SPSELSAVE,FALSE,TRUE);
1269       menu_setflags(sprsm,glass_SPSELDELETE,FALSE,TRUE);
1270       break;
1271     case 1:
1272       menu_setflags(m,glass_SPCLRSEL,FALSE,FALSE);
1273       menu_setflags(m,glass_SPSELALL,FALSE,FALSE);
1274       menu_settitle(sprsm,msgs_lookup("spSPR"));
1275       menu_setflags(sprsm,glass_SPSELINFO,FALSE,FALSE);
1276       menu_setflags(sprsm,glass_SPSELCOPY,FALSE,FALSE);
1277       menu_setflags(sprsm,glass_SPSELRENAME,FALSE,FALSE);
1278       menu_setflags(sprsm,glass_SPSELSAVE,FALSE,FALSE);
1279       menu_setflags(sprsm,glass_SPSELDELETE,FALSE,FALSE);
1280       break;
1281     default:
1282       menu_setflags(m,glass_SPCLRSEL,FALSE,FALSE);
1283       menu_setflags(m,glass_SPSELALL,FALSE,FALSE);
1284       menu_settitle(sprsm,msgs_lookup("spSEL"));
1285       menu_setflags(sprsm,glass_SPSELINFO,FALSE,FALSE);
1286       menu_setflags(sprsm,glass_SPSELCOPY,FALSE,TRUE);
1287       menu_setflags(sprsm,glass_SPSELRENAME,FALSE,TRUE);
1288       menu_setflags(sprsm,glass_SPSELSAVE,FALSE,FALSE);
1289       menu_setflags(sprsm,glass_SPSELDELETE,FALSE,FALSE);
1290       break;
1291   }
1292   menu_setflags(m,glass_SPGRAB,FALSE,window_grabbing());
1293   return (m);
1294 }
1295
1296 /*
1297  * void gSprite__simMenu(glass_tfile *t,int hit1,int hit2)
1298  *
1299  * Use
1300  *  Simulates a menu hit on a template file window
1301  *
1302  * Parameters
1303  *  glass_tfile *t == the template file the event is destined for
1304  *  int hit1 == the main menu entry number
1305  *  int hit2 == the submenu entry number
1306  */
1307
1308 static void gSprite__simMenu(glass_tfile *t,int hit1,int hit2)
1309 {
1310   wimp_menustr *m=menu_syshandle(gSprite__menuMaker(t));
1311   wimp_menuitem *i=(wimp_menuitem *)(m+1)+(hit1-1);
1312   int mnu[3];
1313   mnu[0]=hit1;
1314   mnu[1]=hit2;
1315   mnu[2]=0;
1316
1317   if ((int)i->submenu==-1 || hit2==0)
1318   {
1319     if (i->iconflags & wimp_INOSELECT)
1320     {
1321       bbc_vdu(7);
1322       return;
1323     }
1324     else
1325       mnu[1]=0;
1326   }
1327   else
1328   {
1329     i=(wimp_menuitem *)(i->submenu+1)+(hit2-1);
1330     if (i->iconflags & wimp_INOSELECT)
1331     {
1332       bbc_vdu(7);
1333       return;
1334     }
1335   }
1336   gSprite__menuHandler(mnu,t);
1337 }
1338
1339 /*
1340  * BOOL gSprite__viewerRaw(viewer v,wimp_eventstr *e,void *handle)
1341  *
1342  * Use
1343  *  Handles raw events destined for the viewer window, and picks out
1344  *  interesting ones.
1345  *
1346  * Parameters
1347  *  viewer v == the viewer its going for
1348  *  wimp_eventstr *e == what it is
1349  *  void *handle == pointer to owning template file
1350  *
1351  * Returns
1352  *  TRUE if the event was worth waiting for
1353  */
1354
1355 static BOOL gSprite__viewerRaw(viewer v,wimp_eventstr *e,void *handle)
1356 {
1357   glass_tfile *t=handle;
1358   char *filename;
1359   int filetype;
1360   int estsize;
1361   void *p;
1362   BOOL handled=FALSE;
1363   unused(v);
1364   switch (e->e)
1365   {
1366     case wimp_EKEY:
1367       switch (e->data.key.chcode)
1368       {
1369         case akbd_Fn+1+akbd_Sh: /* sF1 */
1370           gSprite__simMenu(t,glass_SPINFO,0);
1371           handled=TRUE;
1372           break;
1373         case 1:                 /* ^A */
1374           gSprite__simMenu(t,glass_SPSELALL,0);
1375           handled=TRUE;
1376           break;
1377         case 26:                /* ^Z */
1378           gSprite__simMenu(t,glass_SPCLRSEL,0);
1379           handled=TRUE;
1380           break;
1381         case akbd_Fn+3:         /* F3 */
1382           gSprite__simMenu(t,glass_SPSAVE,0);
1383           handled=TRUE;
1384           break;
1385         case 7:                 /* ^G */
1386           gSprite__simMenu(t,glass_SPGRAB,0);
1387           handled=TRUE;
1388           break;
1389
1390         case akbd_Fn+1+akbd_Ctl:/* ^F1 */
1391           gSprite__simMenu(t,glass_SPSEL,glass_SPSELINFO);
1392           handled=TRUE;
1393           break;
1394         case 3:                 /* ^C */
1395           gSprite__simMenu(t,glass_SPSEL,glass_SPSELCOPY);
1396           handled=TRUE;
1397           break;
1398         case 18:                /* ^R */
1399           gSprite__simMenu(t,glass_SPSEL,glass_SPSELRENAME);
1400           handled=TRUE;
1401           break;
1402         case akbd_Fn+3+akbd_Sh: /* sF3 */
1403           gSprite__simMenu(t,glass_SPSEL,glass_SPSELSAVE);
1404           handled=TRUE;
1405           break;
1406         case 24:                /* ^X */
1407           gSprite__simMenu(t,glass_SPSEL,glass_SPSELDELETE);
1408           handled=TRUE;
1409           break;
1410
1411         case akbd_Fn+2+akbd_Ctl:/* ^F2 */
1412           viewer_hide(v);
1413           handled=TRUE;
1414           break;
1415       }
1416       break;
1417
1418     case wimp_ESEND:
1419     case wimp_ESENDWANTACK:
1420       switch (e->data.msg.hdr.action)
1421       {
1422         case wimp_MDATASAVE:
1423           filetype=xferrecv_checkimport(&estsize);
1424           switch (filetype)
1425           {
1426             case 0xff9:
1427               if (xferrecv_returnImportedBlock(&p)!=-1)
1428               {
1429                 gSprite_mergeFromMemory(t,&p);
1430                 flex_free(&p);
1431               }
1432               break;
1433           }
1434           handled=TRUE;
1435           break;
1436         case wimp_MDATALOAD:
1437           filetype=xferrecv_checkinsert(&filename);
1438           switch (filetype)
1439           {
1440             case 0xff9:
1441               gSprite_mergeFromFile(t,filename);
1442               xferrecv_insertfileok();
1443               break;
1444           }
1445           handled=TRUE;
1446           break;
1447       }
1448       break;
1449   }
1450   return (handled);
1451 }
1452
1453 /*
1454  * void gSprite__viewerHandler(viewer v,
1455  *                              viewer_icon i,
1456  *                              wimp_bbits b,
1457  *                              void *vhandle,
1458  *                              void *ihandle)
1459  *
1460  * Use
1461  *  Handles events in a sprite viewer
1462  *
1463  * Parameters
1464  *  viewer v == the handle for the viewer that got the event
1465  *  viewer_icon i == the icon handle that was 'evented', or an event code
1466  *  wimp_bbits b == the mouse button status, if relevant
1467  *  void *vhandle == pointer to the owning template file
1468  *  void *ihandle == an unused pointer
1469  */
1470
1471 static void gSprite__viewerHandler(viewer v,
1472                                     viewer_icon i,
1473                                     wimp_bbits b,
1474                                     void *vhandle,
1475                                     void *ihandle)
1476 {
1477   glass_tfile *t=vhandle;
1478   unused(ihandle);
1479   switch ((int)i)
1480   {
1481     case (int)viewer_CLOSE:
1482       viewer_hide(v);
1483       break;
1484     case (int)viewer_HELP:
1485       help_startHelp();
1486       help_addLine(msgs_lookup("sphMVW"));
1487       help_endHelp();
1488       break;
1489     default:
1490       if (b!=wimp_BMID)
1491       {
1492         gSprite__gainSelection(t);
1493         viewer_clickSelect(v,i,b);
1494       }
1495       switch (b)
1496       {
1497         case wimp_BMID:
1498           if (t==gSprite__selOwner ||
1499               !gSprite__selOwner ||
1500               !viewer_selected(gSprite__selOwner->vs))
1501           {
1502             gSprite__selOwner=t;
1503             viewer_clickSelect(v,i,b);
1504           }
1505           menu_make(gSprite__menuMaker,
1506                     gSprite__menuHandler,
1507                     gSprite__menuHelp,
1508                     t);
1509           break;
1510         case wimp_BLEFT:
1511         case wimp_BRIGHT:
1512           if (i!=viewer_NOICON)
1513           {
1514             gSprite__createWindow(i,viewer_iconHandle(i));
1515             viewer_selectIcon(i,FALSE);
1516           }
1517           break;
1518         case wimp_BDRAGLEFT:
1519         case wimp_BDRAGRIGHT:
1520           if (i!=viewer_NOICON)
1521           {
1522             tfile_dragSelected(i,b,"spackage");
1523             win_add_unknown_event_processor(gSprite__dragUnknowns,t);
1524           }
1525           break;
1526       }
1527       break;
1528   }
1529 }
1530
1531 /*----- External routines -------------------------------------------------*/
1532
1533 /*
1534  * void gSprite_kill(glass_tfile *t)
1535  *
1536  * Use
1537  *  Closes the sprite viewer and frees the sprite area
1538  *
1539  * Parameters
1540  *  glass_tfile *t == the template file that's closing
1541  */
1542
1543 void gSprite_kill(glass_tfile *t)
1544 {
1545   if (t->vs)
1546     viewer_delete(t->vs,gSprite__closeWindows);
1547   indir_free(t->s);
1548 }
1549
1550 /*
1551  * void gSprite_display(glass_tfile *t)
1552  *
1553  * Use
1554  *  Displays the sprite viewer for the specified template file.
1555  *
1556  * glass_tfile *t == the template file whose sprites we want to see
1557  */
1558
1559 void gSprite_display(glass_tfile *t)
1560 {
1561   viewer_selectAll(t->vs,FALSE);
1562   viewer_display(t->vs);
1563 }
1564
1565 /*
1566  * void gSprite_mergeFromMemory(glass_tfile *t,void **p)
1567  *
1568  * Use
1569  *  Merges a sprite file which is stored in memory.  This is so I can do
1570  *  in-memory transfer of sprites.
1571  *
1572  * Parameters
1573  *  glass_tfile *t == the template file owner of the sprite area
1574  *  void **p == the flex block stroing the sprite file
1575  */
1576
1577 void gSprite_mergeFromMemory(glass_tfile *t,void **p)
1578 {
1579   int sprites=_ptr(sprite_area,*p,-4)->number;
1580   int i;
1581   int h=_ptr(sprite_area,*p,-4)->sproff-4;
1582   int length;
1583   sprite_id sid;
1584   viewer_icon icn;
1585   char buff[15];
1586   gSprite__data *d;
1587   if (!gSprite__getMemory(t,_ptr(sprite_area,*p,-4)->freeoff))
1588   {
1589     werr(FALSE,msgs_lookup("spNEMM"));
1590     return;
1591   }
1592   for (i=1;i<=sprites;i++)
1593   {
1594     length=_ptr(sprite_header,*p,h)->next;
1595     buff[12]=0;
1596     memcpy(buff,_ptr(sprite_header,*p,h)->name,12);
1597     sid.s.name=buff;
1598     sid.tag=0;
1599
1600     /* --- Slight problem --- *
1601      *
1602      * viewer is deadly cunning, because it uses a nice case-insensitive
1603      * string-compare for all this, which handles annoying things like
1604      * accents.  Unfortunately, the RISC OS sprite system isn't as cunning,
1605      * and so, when viewer says that `Élite' and `élite' are the same,
1606      * RISC OS moans that it can't find  the sprite when you delete it.
1607      *
1608      * Hence we place the sprite deletion in there as a check that it really
1609      * does exist in there.  Note that it only deletes the sprite if it
1610      * found the icon.
1611      */
1612
1613     if (icn=viewer_findIcon(t->vs,sid.s.name),
1614           icn && !sprite_delete(t->s,&sid))
1615     {
1616       d=viewer_iconHandle(icn);
1617       if (d->w)
1618       {
1619         win_register_event_handler(d->w,0,0);
1620         wimpt_noerr(wimp_delete_wind(d->w));
1621         d->w=0;
1622       }
1623       mem_free(d);
1624       viewer_removeIcon(icn);
1625     }
1626     mem_useUser(indir_alloc,indir_free);
1627     if (d=mem_alloc(sizeof(gSprite__data)),!d)
1628     {
1629       mem_useMalloc();
1630       werr(FALSE,msgs_lookup("spNEMM"));
1631       return;
1632     }
1633     strcpy(d->name,sid.s.name);
1634     d->t=t;
1635     d->w=0;
1636     d->serial=gSprite__serial++;
1637     if (d->i=viewer_addIcon(t->vs,sid.s.name,sid.s.name,TRUE,d),!d->i)
1638     {
1639       mem_useMalloc();
1640       mem_free(d);
1641       return;
1642     }
1643     mem_useMalloc();
1644     viewer_setFiletype(d->i,0xff9);
1645     memcpy(((char *)t->s)+t->s->freeoff,_ptr(sprite_header,*p,h),length);
1646     t->s->freeoff+=length;
1647     t->s->number++;
1648     h+=length;
1649   }
1650   gSprite__minimise(t);
1651   intMsgs_send(glass_SPRITECHANGE,t);
1652 }
1653
1654 /*
1655  * void gSprite_mergeFromFile(glass_tfile *t,char *name)
1656  *
1657  * Use
1658  *  Merges the given file into the sprite area specified.
1659  *
1660  * Parameters
1661  *  glass_tfile *t == the template file that we're going to load for
1662  *  char *name == the name of the file to load
1663  */
1664
1665 void gSprite_mergeFromFile(glass_tfile *t,char *name)
1666 {
1667   os_filestr f;                  /* Going to make some OS_File calls       */
1668   void *p;                       /* p will point to the file in memory     */
1669   f.action=17;                   /* Read catalogue information for file    */
1670   f.name=name;                   /* Point to file name to find out about   */
1671   if (utils_complain(os_file(&f),msgs_lookup("spEMF")))
1672     return;
1673   if (!flex_alloc(&p,f.start))
1674   {
1675     werr(FALSE,msgs_lookup("spNEMM"));
1676     return;
1677   }
1678   f.action=16;                   /* Load the file                          */
1679   f.loadaddr=(int)p;             /* Where to load the file                 */
1680   f.execaddr=0;                  /* Load it there!!!                       */
1681   if (utils_complain(os_file(&f),msgs_lookup("spEMF")))
1682   {
1683     flex_free(&p);
1684     return;
1685   }
1686   gSprite_mergeFromMemory(t,&p);   /* Now do the merge                    */
1687   flex_free(&p);
1688 }
1689
1690 /*
1691  * void gSprite_new(glass_tfile *t)
1692  *
1693  * Use
1694  *  Creates a sprite file for the given template file.  Initially, the file
1695  *  is blank.  On failure, an error is generated and sprite area 1 (WIMP
1696  *  pool) is used instead.  Note that at present, this section uses indir
1697  *  for allocation of sprite areas.
1698  *
1699  * Parameters
1700  *  glass_tfile *t == the file to use
1701  */
1702
1703 void gSprite_new(glass_tfile *t)
1704 {
1705   char buff[256];
1706   void *def;
1707   if (t->s=indir_alloc(gSprite__CHUNK),!t->s)
1708   {
1709     werr(FALSE,msgs_lookup("spNEMC"));
1710     t->s=(sprite_area *)1;       /* Set the WIMP sprite area               */
1711     t->vs=0;                     /* Tell the world something's wrong       */
1712     return;
1713   }
1714   sprintf(buff,msgs_lookup("spVT"),t->filename);
1715   mem_useUser(indir_alloc,indir_free);
1716   t->vs=viewer_create(gSprite__FILEX,
1717                       gSprite__fileHeight,
1718                       gSprite__BOXX,
1719                       gSprite__BOXY+48,
1720                       resspr_area(),
1721                       buff,
1722                       msgs_lookup("spBANR"));
1723   mem_useMalloc();
1724   if (!t->vs)
1725     return;
1726   viewer_setCompare(t->vs,gSprite__compare);
1727   gSprite__fileHeight-=48;
1728   if (gSprite__fileHeight<632)
1729     gSprite__fileHeight=gSprite__FILETOP;
1730   viewer_eventHandler(t->vs,gSprite__viewerHandler,t);
1731   viewer_rawEventHandler(t->vs,gSprite__viewerRaw,t);
1732   viewer_redrawHandler(t->vs,gSprite__viewerRedraw,t);
1733   t->s->size=gSprite__CHUNK;
1734   t->s->number=0;
1735   t->s->sproff=16;
1736   t->s->freeoff=16;
1737   if (gPrefs_current()->sLoadDef && gSprite__default)
1738   {
1739     def=&gSprite__default->number;
1740     gSprite_mergeFromMemory(t,&def);
1741   }
1742 }
1743
1744 /*
1745  * sprite_area *gSprite_area(void)
1746  *
1747  * Use
1748  *  Returns the address of the Glass default sprite file, or 1 for the
1749  *  WIMP sprite area if no default sprites are loaded
1750  */
1751
1752 sprite_area *gSprite_area(void)
1753 {
1754   return (gSprite__default ? gSprite__default : (sprite_area *)1);
1755 }
1756
1757 /*
1758  * void gSprite_init(void)
1759  *
1760  * Use
1761  *  Loads the Glass default sprite area into memory.
1762  */
1763
1764 void gSprite_init(void)
1765 {
1766   os_filestr f;                  /* Going to make some OS_File calls       */
1767   f.action=17;                   /* Read catalogue information for file    */
1768   f.name=choices_name("Defaults.Sprites",FALSE);
1769   if (utils_complain(os_file(&f),msgs_lookup("spELD")))
1770     return;
1771   if (f.action==0)
1772     return;
1773   if (gSprite__default=mem_alloc(f.start+4),!gSprite__default)
1774   {
1775     werr(FALSE,msgs_lookup("spNEMLD"));
1776     return;
1777   }
1778   gSprite__default->size=f.start+4;
1779   gSprite__default->number=0;
1780   gSprite__default->sproff=16;
1781   gSprite__default->freeoff=16;
1782   if (utils_complain(sprite_area_load(gSprite__default,
1783                      choices_name("Defaults.Sprites",FALSE)),
1784       msgs_lookup("spELD")))
1785     gSprite__default=0;
1786 }