chiark / gitweb /
Initial revision
[ssr] / StraySrc / Libraries / Steel / c / viewer
1 /*
2  * viewer
3  *  Allows creation of Filer-like windows which rearrange themselves.
4  *
5  * v. 1.00 (13 August 1993)
6  *
7  * © 1993-1998 Straylight
8  */
9
10 /*----- Licensing note ----------------------------------------------------*
11  *
12  * This file is part of Straylight's Steel library.
13  *
14  * Steel is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2, or (at your option)
17  * any later version.
18  *
19  * Steel is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with Steel.  If not, write to the Free Software Foundation,
26  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 #include "wimp.h"
30 #include "wimpt.h"
31 #include "win.h"
32 #include "viewer.h"
33 #include "msgs.h"
34 #include "bbc.h"
35 #include "werr.h"
36 #include "os.h"
37 #include "xfersend.h"
38 #include "mem.h"
39 #include "coords.h"
40 #include "utils.h"
41 #include "help.h"
42 #include "fileicon.h"
43 #include "buffer.h"
44 #include "swis.h"
45 #include <stdlib.h>
46 #include <string.h>
47 #include <stdio.h>
48
49 #include "dll.h"
50
51 #ifndef _dll_NODLL
52   extern void _dllEntry(viewer__events)(wimp_eventstr *e,void *handle);
53 #endif
54
55 /*
56  * A structure of information about a viewer icon.
57  */
58
59 typedef struct viewer__iconstr
60 {
61   viewer_icon next;
62   viewer_icon last;
63   char *text;
64   char sprite[15];
65   void *handle;
66   int filetype;
67   BOOL selected;
68   viewer v;
69 }
70 viewer__iconstr;
71
72 /*
73  * A structure of information about a viewer window.
74  */
75
76 typedef struct viewer__viewerstr
77 {
78   wimp_w wind;
79   int icons;
80   viewer_eventhandler events;
81   void *closeHandle;
82   viewer_raweventhandler rawEvents;
83   void *rawHandle;
84   viewer_redrawhandler rdr;
85   void *rdrHandle;
86   viewer_compare cmp;
87   sprite_area *spr;
88   int width;
89   int height;
90   int across;
91   int down;
92   int selected;
93   char banner[50];
94   char title[256];
95   viewer_icon iconlist;
96   viewer_icon menuSel;
97   wimp_openstr oldSize;
98 }
99 viewer__viewerstr;
100
101 /*
102  * Some constants for the things
103  */
104
105 #define viewer__MINWIDTH 380
106 #define viewer__MINHEIGHT 160
107 #define viewer__GAP 16
108 #define viewer__BANNERHEIGHT 48
109
110 static viewer_saveproc viewer__usersave;
111 static viewer_sendproc viewer__usersend;
112 static viewer_printproc viewer__userprint;
113 static viewer_icon viewer__currentIcon;
114 static viewer viewer__currentViewer;
115 static int viewer__filetype;
116
117 #define max(x,y) ((x)>(y) ? (x) : (y))
118 #define min(x,y) ((x)<(y) ? (x) : (y))
119
120 /*
121  * void viewer__resize(viewer v)
122  *
123  * Use
124  *  Resizes the viewer nicely, altering the extent if necessary.
125  *
126  * Parameters
127  *  viewer v == the viewer handle
128  */
129
130 static void viewer__resize(viewer v)
131 {
132   wimp_wstate s;
133   int w;
134   int across;
135   int down;
136   int minw;
137   int scw;
138   int maxacc;
139   wimp_redrawstr r;
140   wimpt_noerr(wimp_get_wind_state(v->wind,&s));
141   w=s.o.box.x1-s.o.box.x0;
142   scw=wimpt_scwidth()-40;
143   across=w/(v->width+viewer__GAP);
144   maxacc=scw/(v->width+viewer__GAP);
145   if (across>v->icons)
146     across=v->icons;
147   if (maxacc>v->icons)
148     maxacc=v->icons;
149   if (across>maxacc)
150     across=maxacc;
151   if (across==0)
152     across=1;
153   down=v->icons/across;
154   if (v->icons%across!=0)
155     down++;
156   if (down==0)
157     down=1;
158   if (v->across==across && v->down==down && (s.flags & wimp_WOPEN))
159     return;
160   minw=wimpt_stringWidth(v->title)+164;
161   if (v->banner[0]!='\0')
162   {
163     int l=wimpt_stringWidth(v->banner)+64;
164     if (l>minw)
165       minw=l;
166   }
167   if (minw<viewer__MINWIDTH)
168     minw=viewer__MINWIDTH;
169   r.w=v->wind;
170   r.box.x0=0;
171   r.box.y0=-(v->height+viewer__GAP)*down;
172   if (v->banner[0]!='\0')
173     r.box.y0-=viewer__BANNERHEIGHT;
174   if (r.box.y0>-viewer__MINHEIGHT)
175     r.box.y0=-viewer__MINHEIGHT;
176   r.box.x1=(v->width+viewer__GAP)*maxacc;
177   if (r.box.x1<minw)
178     r.box.x1=minw;
179   r.box.y1=0;
180   wimpt_noerr(wimp_set_extent(&r));
181
182   /* --- Acorn-approved hack --- *
183    *
184    * We have problems with the Wimp fiddling our extent after we've set it,
185    * so we just redraw lots and lots.
186    */
187
188   r.box.x0=r.box.y0=-0xFFFFFF;
189   r.box.x1=r.box.y1=0xFFFFFF;
190   wimpt_noerr(wimp_force_redraw(&r));
191
192   if (s.flags & wimp_WOPEN)
193     wimpt_noerr(wimp_open_wind(&s.o));
194   v->across=across;
195   v->down=down;
196 }
197
198 /*
199  * void viewer__redrawicon(wimp_redrawstr *r,viewer_icon icn,wimp_box *box)
200  *
201  * Use
202  *  Redraws an icon in the box specified.
203  *
204  * Parameters
205  *  viewer_icon icn == the icon to redraw
206  *  wimp_box *box == the box to draw it in
207  */
208
209 static void viewer__redrawicon
210 (
211   wimp_redrawstr *r,
212   viewer_icon icn,
213   wimp_box *box
214 )
215 {
216   wimp_icon i;
217   char valid[256];
218   viewer v=icn->v;
219   if (v->rdr)
220   {
221     (v->rdr)(icn,r,box,icn->text,icn->sprite,icn->selected,v->rdrHandle);
222     return;
223   }
224   i.box=*box;
225   if (icn->text!=0 && icn->sprite[0]!='\0')
226   {
227     i.flags=wimp_ITEXT|wimp_ISPRITE|wimp_INDIRECT|wimp_IHCENTRE;
228     sprintf(valid,"S%s",icn->sprite);
229     i.data.indirecttext.buffer=icn->text;
230     i.data.indirecttext.validstring=valid;
231     i.data.indirecttext.bufflen=1;
232   }
233   else if (icn->text!=0)
234   {
235     i.flags=wimp_ITEXT|wimp_INDIRECT|wimp_IHCENTRE|wimp_IVCENTRE;
236     i.data.indirecttext.buffer=icn->text;
237     i.data.indirecttext.validstring=(char *)-1;
238     i.data.indirecttext.bufflen=1;
239   }
240   else
241   {
242     i.flags=wimp_ISPRITE|wimp_INDIRECT|wimp_IHCENTRE|wimp_IVCENTRE;
243     i.data.indirectsprite.name=icn->sprite;
244     i.data.indirectsprite.spritearea=v->spr;
245     i.data.indirectsprite.nameisname=TRUE;
246   }
247   i.flags|=0x17000000;
248   if (icn->selected)
249     i.flags|=wimp_ISELECTED;
250   wimpt_noerr(wimp_ploticon(&i));
251 }
252
253 /*
254  * void viewer_drawFileIcons
255  * (
256  *   viewer_icon icn,
257  *   wimp_redrawstr *r,
258  *   wimp_box *box,
259  *   char *text,
260  *   char *sprite,
261  *   BOOL selected,
262  *   void *handle
263  * )
264  *
265  * Use
266  *  Redraw handler which takes into account filetypes of icons.  The icons
267  *  automatically get new sprites if the sprites for their filetypes change.
268  *  For applications, the text is considered to be a filename.  Register
269  *  this function using viewer_redrawHandler().
270  *
271  * Parameters
272  *  viewer_icon icn == the icon to paint
273  *  wimp_redrawstr *r == information about this redraw
274  *  wimp_box *box == the box to draw the icon in
275  *  char *text == the text to display
276  *  char *sprite == the sprite to display
277  *  BOOL selected == is the icon selected?
278  *  void *handle == a caller defined handle (ignored)
279  */
280
281 void viewer_drawFileIcons
282 (
283   viewer_icon icn,
284   wimp_redrawstr *r,
285   wimp_box *box,
286   char *text,
287   char *sprite,
288   BOOL selected,
289   void *handle
290 )
291 {
292   wimp_icon i;
293   char valid[256];
294   handle=handle;
295   r=r;
296   i.box=*box;
297   i.flags=wimp_ITEXT|wimp_ISPRITE|wimp_INDIRECT|wimp_IHCENTRE;
298   if (icn->filetype!=-1)
299     sprintf(valid,"S%s",fileicon_spriteName(icn->filetype,text));
300   else
301     sprintf(valid,"S%s",sprite);
302   i.data.indirecttext.buffer=text;
303   i.data.indirecttext.validstring=valid;
304   i.data.indirecttext.bufflen=1;
305   i.flags|=0x17000000;
306   if (selected)
307     i.flags|=wimp_ISELECTED;
308   wimpt_noerr(wimp_ploticon(&i));
309 }
310
311 /*
312  * viewer__redraw(wimp_redrawstr *r,void *handle)
313  *
314  * Use
315  *  Redraws a viewer window
316  *
317  * Parameters
318  *  wimp_redrawstr *r == the bit to redraw
319  *  void *handle == the viewer
320  */
321
322 static void viewer__redraw(wimp_redrawstr *r,void *handle)
323 {
324   viewer v=(viewer)handle;
325   viewer_icon icn=v->iconlist;
326   int oy=r->box.y1-r->scy;
327   int x=viewer__GAP/2;
328   int y=-viewer__GAP/2;
329   wimp_box box;
330   coords_cvtstr c;
331   wimp_box g;
332   int cnt=0;
333   wimp_icon banicn={28,-48,1000,0,0x037000131};
334   c.box=r->box;
335   c.scx=r->scx;
336   c.scy=r->scy;
337   g=r->g;
338   coords_box_toworkarea(&g,&c);
339   wimp_setcolour(0x81);
340   bbc_clg();
341   if (v->banner[0]!='\0')
342   {
343     if (g.y1>-viewer__BANNERHEIGHT)
344     {
345       wimp_setcolour(3);
346       bbc_rectanglefill
347       (
348         r->g.x0,
349         oy-viewer__BANNERHEIGHT,
350         r->g.x1-r->g.x0,
351         viewer__BANNERHEIGHT
352       );
353       wimp_setcolour(7);
354       banicn.box.x1=100+wimpt_stringWidth(v->banner);
355       banicn.data.indirecttext.buffer=v->banner;
356       banicn.data.indirecttext.validstring=(char *)-1;
357       wimpt_noerr(wimp_ploticon(&banicn));
358     }
359     y-=viewer__BANNERHEIGHT;
360   }
361   while (icn)
362   {
363     box.x0=x;
364     box.x1=x+v->width;
365     box.y1=y;
366     box.y0=y-v->height;
367     if (coords_boxesoverlap(&box,&g))
368       viewer__redrawicon(r,icn,&box);
369     icn=icn->next;
370     if ((++cnt)==v->across)
371     {
372       x=viewer__GAP/2;
373       y-=viewer__GAP+v->height;
374       cnt=0;
375     }
376     else
377       x+=viewer__GAP+v->width;
378   }
379 }
380
381 /*
382  * void viewer__events(wimp_eventstr *e,void *handle)
383  *
384  * Use
385  *  Window event handler for the viewer.
386  *
387  * Parameters
388  *  wimp_eventstr *e == the wimp event structure
389  *  void *handle == my data (i.e. a 'this' pointer to my viewer__viewerstr)
390  */
391
392 _dll_static void viewer__events(wimp_eventstr *e,void *handle)
393 {
394   BOOL handled=FALSE;
395   viewer v=(viewer)handle;
396   wimp_wstate s;
397   if (v->rawEvents)
398     handled=(v->rawEvents)(v,e,v->rawHandle);
399   if (handled)
400     return;
401   switch (e->e)
402   {
403     case wimp_EREDRAW:
404       wimpt_redraw(viewer__redraw,v);
405       break;
406     case wimp_EOPEN:
407       if (wimpt_justChangedMode())
408         viewer_settitle(v,0);
409       wimpt_noerr(wimp_get_wind_state(v->wind,&s));
410       wimpt_noerr(wimp_open_wind(&e->data.o));
411       viewer__resize(v);
412       if (s.flags & wimp_WCLICK_TOGGLE)
413       {
414         if (s.flags & wimp_WFULL)
415         {
416           e->data.o.box=v->oldSize.box;
417           e->data.o.x=v->oldSize.x;
418           e->data.o.y=v->oldSize.y;
419           wimpt_noerr(wimp_open_wind(&e->data.o));
420         }
421         else
422           v->oldSize=s.o;
423       }
424       break;
425     case wimp_ECLOSE:
426       if (v->events)
427         (v->events)(v,viewer_CLOSE,(wimp_bbits)0,v->closeHandle,0);
428       break;
429     case wimp_EBUT:
430       {
431         viewer_icon i=viewer_iconFromCoords
432         (
433           v,
434           e->data.but.m.x,
435           e->data.but.m.y
436         );
437         if (v->events)
438         {
439           (v->events)
440           (
441             v,
442             i,
443             e->data.but.m.bbits,
444             v->closeHandle,
445             (i!=viewer_NOICON) ? i->handle : 0
446           );
447         }
448       }
449       break;
450     case wimp_EKEY:
451       wimpt_noerr(wimp_processkey(e->data.key.chcode));
452       break;
453     case wimp_ESEND:
454     case wimp_ESENDWANTACK:
455       switch (e->data.msg.hdr.action)
456       {
457         case wimp_MHELPREQUEST:
458           {
459             viewer_icon i=viewer_iconFromCoords
460             (
461               v,
462               e->data.msg.data.helprequest.m.x,
463               e->data.msg.data.helprequest.m.y
464             );
465             if (v->events)
466             {
467               (v->events)
468               (
469                 v,
470                 viewer_HELP,
471                 0,
472                 v->closeHandle,
473                 (i!=viewer_NOICON) ? i->handle : 0
474               );
475             }
476           }
477           break;
478       }
479       break;
480   }
481 }
482
483 /*
484  * viewer viewer_create
485  * (
486  *   int x,
487  *   int y,
488  *   int width,
489  *   int height,
490  *   sprite_area *spr,
491  *   char *title,
492  *   char *banner
493  * )
494  *
495  * Use
496  *  Creates a viewer window.  Note that viewer windows don't need templates,
497  *  and don't contain real wimp icons, just yer normal redrawn-by-
498  *  application things (which can be handled by a caller-defined function if
499  *  necessary).  The banner along the top is optional - if a null pointer is
500  *  passed, no banner is included.  The sprite area passed applies to all
501  *  the icons in the window, although if  you want to, you can use your own
502  *  redraw routine to handle different sprite areas for them.
503  *
504  * Parameters
505  *  int x,int y == the coordinates of the top-left corner of the window
506  *    (file windows for editors need good positioning here).
507  *  int width == the width of an icon
508  *  int height == the height of an icon
509  *  sprite_area *spr == the sprite area for the window
510  *  char *title == the title of the window
511  *  char *banner == the banner heading along the top (like 'Sprite file
512  *    window' or something)
513  *
514  * Returns
515  *  A handle to the viewer window.  As usual, a NULL pointer indicates
516  *   something went wrong.
517  */
518
519 viewer viewer_create
520 (
521   int x,
522   int y,
523   int width,
524   int height,
525   sprite_area *spr,
526   char *title,
527   char *banner
528 )
529 {
530   viewer v=(viewer)mem_alloc(sizeof(viewer__viewerstr));
531   wimp_wind w=
532   {
533     {0,0,0,0},
534     0,0,
535     -1,
536     (
537       wimp_WMOVEABLE |
538       wimp_WBACK |
539       wimp_WQUIT |
540       wimp_WTITLE |
541       wimp_WTOGGLE |
542       wimp_WVSCR |
543       wimp_WSIZE |
544       wimp_WNEW
545     ),
546     {7,2,7,255,3,1,12,0},
547     {0,0,0,0},
548     wimp_ITEXT|wimp_IHCENTRE|wimp_INDIRECT,
549     wimp_IBTYPE*wimp_BCLICKDRAGDOUBLE,
550     0,
551     1,
552     "Yay",
553     0
554   };
555   w.box.x0=x;
556   w.box.x1=x+900-(900%(width+viewer__GAP));
557   if (w.box.x1==x)
558     w.box.x1=x+width+viewer__GAP;
559   w.box.y0=y-500+(500%(height+viewer__GAP));
560   if (w.box.y0==y)
561     w.box.y0=y-height+viewer__GAP;
562   w.box.y1=y;
563   w.spritearea=(void *)spr;
564   if (!v)
565   {
566     werr(FALSE,msgs_lookup("viewerNEM:Not enough room to create viewer."));
567     return (0);
568   }
569   w.title.indirecttext.buffer=v->title;
570   w.title.indirecttext.validstring=(char *)-1;
571   w.title.indirecttext.bufflen=256;
572   strcpy(v->title,title);
573   if (banner)
574   {
575     strcpy(v->banner,banner);
576     w.box.y0-=viewer__BANNERHEIGHT;
577   }
578   else
579     v->banner[0]='\0';
580   v->icons=0;
581   v->events=0;
582   v->rawEvents=0;
583   v->rdr=0;
584   v->cmp=0;
585   v->iconlist=0;
586   v->width=width;
587   v->height=height;
588   v->spr=spr;
589   v->across=-1;
590   v->selected=0;
591   v->menuSel=viewer_NOICON;
592   v->oldSize.box=w.box;
593   v->oldSize.x=w.scx;
594   v->oldSize.y=w.scy;
595   if (wimp_create_wind(&w,&v->wind))
596   {
597     werr
598     (
599       FALSE,
600       msgs_lookup("viewerTMW:Too many windows - "
601                                             "%s could not create viewer."),
602       wimpt_programname()
603     );
604     mem_free(v);
605     return (0);
606   }
607   win_register_event_handler(v->wind,_dllEntry(viewer__events),v);
608   win_activeinc();
609   return (v);
610 }
611
612 /*
613  * void viewer_display(viewer v)
614  *
615  * Use
616  *  Displays a viewer on the screen.
617  *
618  * Parameters
619  *  viewer v == the viewer handle
620  */
621
622 void viewer_display(viewer v)
623 {
624   wimp_wstate state;
625   viewer__resize(v);
626   wimpt_noerr(wimp_get_wind_state(v->wind,&state));
627   state.o.behind=-1;
628   wimpt_noerr(wimp_open_wind(&state.o));
629 }
630
631 /*
632  * void viewer_hide(viewer v)
633  *
634  * Use
635  *  Hides an open viewer.
636  *
637  * Parameters
638  *  viewer v == the handle
639  */
640
641 void viewer_hide(viewer v)
642 {
643   wimpt_noerr(wimp_close_wind(v->wind));
644 }
645
646 /*
647  * void viewer_delete(viewer v,void (*freeProc)(void *handle))
648  *
649  * Use
650  *  Zaps a viewer and everything in it.  The function you pass to this
651  *  routine is called with every icon handle the viewer has associated with
652  *  it, so you don't need to link all the structures together - I've already
653  *  done that here!
654  *
655  * Parameters
656  *  viewer v == the viewer to destroy
657  *  void (*freeProc)(void *handle) == a function to free one of the caller-
658  *    defined viewer icon structures.
659  */
660
661 void viewer_delete(viewer v,void (*freeProc)(void *handle))
662 {
663   viewer_icon icn=v->iconlist;
664   viewer_icon i;
665   while (icn)
666   {
667     if (icn->handle && freeProc)
668       freeProc(icn->handle);
669     i=icn;
670     icn=i->next;
671     mem_free(i->text);
672     mem_free(i);
673   }
674   win_register_event_handler(v->wind,0,0);
675   win_activedec();
676   wimpt_noerr(wimp_delete_wind(v->wind));
677   mem_free(v);
678 }
679
680 /*
681  * void viewer_eventHandler(viewer v,viewer_eventhandler proc,void *handle)
682  *
683  * Use
684  *  Attaches an event handler to the viewer.  The handle passed to this
685  *  function is only used for close events, so you can take appropriate
686  *  action at the other end.  Otherwise, the handle for the icon concerned
687  *  is used.  Suggested code:
688  *
689  * void user_viewer(viewer v,viewer_icon i,wimp_bbits b,void *handle)
690  * {
691  *   user_fileStructure *file=(user_fileStructure *)handle;
692  *   user_itemStructure *item=(user_itemStructure *)handle;
693  *   switch ((int)i)
694  *   {
695  *     case (int)viewer_CLOSE:
696  *       ... use 'file' for this bit of code ...
697  *       break;
698  *     case viewer_NOICON:
699  *       ... use 'file' for this bit as well ...
700  *       break;
701  *     default:
702  *       ... use 'item' for this bit of code ...
703  *       break;
704  *   }
705  * }
706  *
707  * Parameters
708  *  viewer v == the viewer to attach the handler to
709  *  viewer_eventhandler proc == the event handler
710  *  void *handle == the handle
711  */
712
713 void viewer_eventHandler(viewer v,viewer_eventhandler proc,void *handle)
714 {
715   v->events=proc;
716   v->closeHandle=handle;
717 }
718
719 /*
720  * void viewer_rawEventHandler
721  * (
722  *   viewer v,
723  *   viewer_raweventhandler proc,
724  *   void *handle
725  * )
726  *
727  * Use
728  *  Attaches a raw event handler to a viewer.  Same as always, this one.
729  *
730  * Parameters
731  *  viewer v == the viewer handle
732  *  viewer_raweventhandler proc == the handler routine
733  *  void *handle == the handle for the user's data
734  */
735
736 void viewer_rawEventHandler
737 (
738   viewer v,
739   viewer_raweventhandler proc,
740   void *handle
741 )
742 {
743   v->rawEvents=proc;
744   v->rawHandle=handle;
745 }
746
747 /*
748  * void viewer_redrawHandler(viewer v,viewer_redrawhandler proc,void *handle)
749  *
750  * Use
751  *  Adds in a user-defined routine for redrawing icons in the window.
752  *
753  * Parameters
754  *  viewer v == the viewer handle
755  *  viewer_redrawhandler proc == the routine for doing the redraw
756  *  void *handle == a handle to be passed to the routine (a sprite area or
757  *    something)
758  */
759
760 void viewer_redrawHandler(viewer v,viewer_redrawhandler proc,void *handle)
761 {
762   v->rdr=proc;
763   v->rdrHandle=handle;
764 }
765
766 /*
767  * void viewer_setIconSize(viewer v,int width,int height)
768  *
769  * Use
770  *  Sets a new icon size for the viewer.  This would normally be
771  *  accompanied by a chnge in redraw handler.  It would be used e.g. when
772  *  using a menu option giving a choice between 'Large icons' and 'Small
773  *  icons'.
774  *
775  * Parameters
776  *  viewer v == the viewer which is to receive this change
777  *  int width == the new width
778  *  int height == the new height
779  */
780
781 void viewer_setIconSize(viewer v,int width,int height)
782 {
783   v->width=width;
784   v->height=height;
785   v->across=-1;
786   viewer__resize(v);
787 }
788
789 /*
790  * void viewer_setCompare(viewer v,viewer_compare cmp)
791  *
792  * Use
793  *  Registers a compare function for the viewer specified.  The function
794  *  is passed the caller-defined handles of two icons.  The function must
795  *  return <0 if a<b, >0 if a>b, or ==0 if a==b (like strcmp does).  The
796  *  viewer's icons are then resorted.  Pass 0 to use the default sorting
797  *  system (a caseless compare on the icon text)
798  *
799  *  The resorting algorithm is the link mergesort, originally implemented
800  *  in the Sapphire linklist manager.
801  *
802  * Parameters
803  *  viewer v == the viewer we're going to set the comparer up for
804  *  viewer_compare cmp == the function to do comparing
805  */
806
807 void viewer_setCompare(viewer v,viewer_compare cmp)
808 {
809   v->cmp=cmp;
810   if (!v->icons)
811     return;
812   v->across=-1;
813   viewer__resize(v);
814
815   /* --- Now we have to resort the list --- *
816    *
817    * This code is pretty much based on the mergesort code printed in
818    * Sedgewick's Algorithms.
819    */
820
821   #define next(p) ((p) ? (p)->next : (p))
822
823   {
824     viewer_icon a,b,c,t,todo,x,y;
825     int i,n,more;
826
827     n=1;
828     c=v->iconlist;
829
830     do
831     {
832       todo=v->iconlist;
833       more=FALSE;
834       c=(viewer_icon)&v->iconlist;
835       while (todo)
836       {
837         t=todo;
838         a=t;
839         if (a!=v->iconlist)
840           more=TRUE;
841         for (i=1;i<n;i++)
842           t=next(t);
843         b=next(t);
844         x=b;
845         t=b;
846         for (i=1;i<n;i++)
847           t=next(t);
848         t=next(t);
849         y=t;
850         todo=t;
851
852         /* --- Now we have to do the actual merge --- *
853          *
854          * This is made a little bit more difficult since we have null-
855          * terminated lists rather than a weird node which points to itself.
856          */
857
858         while (a!=x || b!=y)
859         {
860           if (a==x)
861             i=1;
862           else if (b==y)
863             i=-1;
864           else if (v->cmp)
865             i=(v->cmp)(a->handle,b->handle);
866           else
867             i=utils_caselessCmp(a->text,b->text);
868
869           if (i<=0)
870           {
871             c->next=a;
872             c=a;
873             a=next(a);
874           }
875           else
876           {
877             c->next=b;
878             c=b;
879             b=next(b);
880           }
881         }
882
883       }
884       c->next=0;
885       n*=2;
886     }
887     while (more);
888   }
889
890   #undef next
891
892   /* --- Now we have to sort out the back links --- */
893
894   {
895     viewer_icon a=v->iconlist;
896
897     a->last=0;
898     while (a->next)
899     {
900       a->next->last=a;
901       a=a->next;
902     }
903   }
904 }
905
906 /*
907  * viewer_icon viewer_addIcon
908  * (
909  *   viewer v,
910  *   char *text,
911  *   char *sprite,
912  *   BOOL inOrder,
913  *   void *handle
914  * )
915  *
916  * Use
917  *  Adds a new icon to a viewer window.
918  *
919  * Parameters
920  *  viewer v == the handle of the viewer to use.
921  *  char *text == the text to put under the icon (may be null)
922  *  char *sprite == the sprite to use (may be null)
923  *  BOOL inOrder == whether you want the icons sorted into order according
924  *   to the text fields
925  *  void *handle == the handle you want to pass to the event handler routine
926  *
927  * Returns
928  *  A handle to the icon.  If this is NULL, something went majorly wrong
929  *  (sorry, John)
930  */
931
932 viewer_icon viewer_addIcon
933 (
934   viewer v,
935   char *text,
936   char *sprite,
937   BOOL inOrder,
938   void *handle
939 )
940 {
941   viewer_icon icn=(viewer_icon)&(v->iconlist);
942   viewer_icon new=(viewer_icon)mem_alloc(sizeof(viewer__iconstr));
943   if (!new)
944   {
945     werr(FALSE,msgs_lookup("viewerCCI:Not enough room to create new icon."));
946     return (0);
947   }
948   if (text)
949   {
950     if (new->text=mem_alloc(strlen(text)+1),!new->text)
951     {
952       mem_free(new);
953       werr
954       (
955         FALSE,
956         msgs_lookup("viewerCCI:Not enough room to create new icon.")
957       );
958       return (0);
959     }
960     strcpy(new->text,text);
961   }
962   else
963     new->text=0;
964   if (sprite)
965     strcpy(new->sprite,sprite);
966   else
967     new->sprite[0]='\0';
968   while (icn->next)
969   {
970     if (v->cmp)
971     {
972       if ((v->cmp)(icn->next->handle,handle)>0 && inOrder==TRUE)
973         break;
974     }
975     else
976     {
977       if (utils_caselessCmp(icn->next->text,text)>0 && inOrder==TRUE)
978         break;
979     }
980     icn=icn->next;
981   }
982   new->next=icn->next;
983   new->last=icn;
984   icn->next=new;
985   if (new->next)
986     new->next->last=new;
987   new->handle=handle;
988   new->v=v;
989   new->selected=FALSE;
990   new->filetype=-1;
991   v->icons++;
992   v->across=-1;
993   viewer__resize(v);
994   return (new);
995 }
996
997 /*
998  * void viewer_setFiletype(viewer_icon i,int type)
999  *
1000  * Use
1001  *  Sets the filetype of the icon - useful for bins and things.  This
1002  *  filetype overrides the setting given to viewer_exportSelected().  It can
1003  *  also be used to display the correct icon in the viewer by changing the
1004  *  redraw handler to viewer_drawFileIcons().
1005  *
1006  * Parameters
1007  *  viewer_icon i == the icon to change
1008  *  int type == the filetype to give it (any valid WIMP filetype will do)
1009  */
1010
1011 void viewer_setFiletype(viewer_icon i,int type)
1012 {
1013   i->filetype=type;
1014 }
1015
1016 /*
1017  * int viewer_readFiletype(viewer_icon i)
1018  *
1019  * Use
1020  *  Returns the filetype attached to an icon.
1021  *
1022  * Parameters
1023  *  viewer_icon i == the icon handle
1024  *
1025  * Returns
1026  *  The type attached using viewer_setFiletype(), or -1 if none.
1027  */
1028
1029 int viewer_readFiletype(viewer_icon i)
1030 {
1031   return (i->filetype);
1032 }
1033
1034 /*
1035  * viewer_icon viewer_findIcon(viewer v,char *text)
1036  *
1037  * Use
1038  *  Searches through a viewer to find an icon with the same text as 'text'.
1039  *  The search is case-insensitive.
1040  *
1041  * Parameters
1042  *  viewer v == the viewer handle
1043  *  char *text == text to search for
1044  *
1045  * Returns
1046  *  The icon handle, or viewer_NOICON if unsuccessful.
1047  */
1048
1049 viewer_icon viewer_findIcon(viewer v,char *text)
1050 {
1051   viewer_icon i=v->iconlist;
1052   while (i)
1053   {
1054     if (utils_caselessCmp(text,i->text)==0)
1055       return (i);
1056     i=i->next;
1057   }
1058   return (viewer_NOICON);
1059 }
1060
1061 /*
1062  * void viewer_removeIcon(viewer_icon i)
1063  *
1064  * Use
1065  *  Removes the icon specified.  This routine is real easy!
1066  *
1067  * Parameters
1068  *  viewer_icon i == the icon to remove (they ALL have unique handles, so
1069  *    this is OK)
1070  */
1071
1072 void viewer_removeIcon(viewer_icon i)
1073 {
1074   viewer v=i->v;
1075   if (i->selected)
1076     v->selected--;
1077   if (i->last)
1078     i->last->next=i->next;
1079   else
1080     v->iconlist=i->next;
1081   if (i->next)
1082     i->next->last=i->last;
1083   mem_free(i->text);
1084   mem_free(i);
1085   v->icons--;
1086   v->across=-1;
1087   v->menuSel=viewer_NOICON;
1088   viewer__resize(v);
1089 }
1090
1091 /*
1092  * wimp_w viewer_syshandle(viewer v)
1093  *
1094  * Use
1095  *  Returns the WIMP window handle used for the viewer (and much use may it
1096  *  do you!)
1097  *
1098  * Parameters
1099  *  viewer v == the viewer handle
1100  *
1101  * Returns
1102  *  The wimp_w window handle
1103  */
1104
1105 wimp_w viewer_syshandle(viewer v)
1106 {
1107   return (v->wind);
1108 }
1109
1110 /*
1111  * viewer_icon viewer_iconFromCoords(viewer v,int x,int y)
1112  *
1113  * Use
1114  *  Given a set of (absolute) coordinates, this returns the icon in the
1115  *  viewer specified that the point is on.  This is mainly useful for menu
1116  *  maker routines, which will want to be able to select items and so on.
1117  *
1118  * Parameters
1119  *  viewer v == the viewer handle
1120  *  int x == the x-coordinate of the point
1121  *  int y == the y-coordinate of the point
1122  *
1123  * Returns
1124  *  The icon handle, or viewer__NOICON if there isn't one.
1125  */
1126
1127 viewer_icon viewer_iconFromCoords(viewer v,int x,int y)
1128 {
1129   wimp_wstate s;
1130   int ix,iy;
1131   int i;
1132   int cnt;
1133   viewer_icon icn=v->iconlist;
1134   wimpt_noerr(wimp_get_wind_state(v->wind,&s));
1135   x-=(s.o.box.x0-s.o.x);
1136   y-=(s.o.box.y1-s.o.y);
1137   y=-y;
1138   if (v->banner[0]!='\0')
1139     y-=viewer__BANNERHEIGHT;
1140   ix=x/(v->width+viewer__GAP);
1141   x-=(v->width+viewer__GAP)*ix;
1142   if (x<viewer__GAP/2 || x-viewer__GAP/2>v->width || ix>=v->across)
1143     return (viewer_NOICON);
1144   iy=y/(v->height+viewer__GAP);
1145   y-=(v->height+viewer__GAP)*iy;
1146   if (y<viewer__GAP/2 || y-viewer__GAP/2>v->height || iy>=v->down)
1147     return (viewer_NOICON);
1148   i=ix+iy*v->across;
1149   if (i>=v->icons)
1150     return (viewer_NOICON);
1151   for (cnt=0;cnt<i;cnt++)
1152   {
1153     if (icn==0)
1154       return (viewer_NOICON);
1155     icn=icn->next;
1156   }
1157   return (icn);
1158 }
1159
1160 /*
1161  * void viewer_iconToCoords(viewer_icon i,wimp_box *box)
1162  *
1163  * Use
1164  *  Calculates the bounding box of the icon given.  If there is an error,
1165  *  nothing is written in the block.
1166  *
1167  * Parameters
1168  *  viewer_icon i == the icon we're interested in
1169  *  wimp_box *box == where to store the coordinates
1170  */
1171
1172 void viewer_iconToCoords(viewer_icon i,wimp_box *box)
1173 {
1174   int x,y;
1175   viewer v=i->v;
1176   BOOL end=FALSE;
1177   viewer_icon icn=v->iconlist;
1178   wimp_wstate s;
1179   wimpt_noerr(wimp_get_wind_state(v->wind,&s));
1180   x=y=0;
1181   while (!end)
1182   {
1183     if (icn==0)
1184       return;
1185     if (icn==i)
1186       end=TRUE;
1187     else
1188     {
1189       if ((++x)==v->across)
1190       {
1191         x=0;
1192         y++;
1193       }
1194     }
1195     icn=icn->next;
1196   }
1197   box->x0=s.o.box.x0-s.o.x+viewer__GAP/2+(viewer__GAP+v->width)*x;
1198   box->x1=box->x0+v->width;
1199   box->y1=s.o.box.y1-s.o.y-viewer__GAP/2-(viewer__GAP+v->height)*y;
1200   if (v->banner[0]!='\0')
1201     box->y1-=viewer__BANNERHEIGHT;
1202   box->y0=box->y1-v->height;
1203 }
1204
1205 /*
1206  * BOOL viewer_isSelected(viewer_icon i)
1207  *
1208  * Use
1209  *  Returns whether an icon is selected or not.
1210  *
1211  * Parameters
1212  *  viewer_icon i == the icon handle
1213  *
1214  * Returns
1215  *  TRUE if the icon is selected, or FALSE otherwise.
1216  */
1217
1218 BOOL viewer_isSelected(viewer_icon i)
1219 {
1220   return (i->selected);
1221 }
1222
1223 /*
1224  * void viewer_selectIcon(viewer_icon i,BOOL onOrOff)
1225  *
1226  * Use
1227  *  Selects or deselects the icon specified.
1228  *
1229  * Parameters
1230  *  viewer_icon i == the icon handle
1231  *  BOOL onOrOff == TRUE to select, FALSE to deselect
1232  */
1233
1234 void viewer_selectIcon(viewer_icon i,BOOL onOrOff)
1235 {
1236   wimp_redrawstr r;
1237   viewer v;
1238   wimp_wstate s;
1239   int ox,oy;
1240   wimp_box box;
1241   BOOL more;
1242   if (i==viewer_NOICON)
1243     return;
1244   v=i->v;
1245   if (i->selected!=onOrOff)
1246   {
1247     if (onOrOff)
1248       v->selected++;
1249     else
1250       v->selected--;
1251   }
1252   else
1253     return;
1254   i->selected=onOrOff;
1255   r.w=v->wind;
1256   viewer_iconToCoords(i,&box);
1257   wimpt_noerr(wimp_get_wind_state(v->wind,&s));
1258   ox=s.o.box.x0-s.o.x;
1259   oy=s.o.box.y1-s.o.y;
1260   box.x0-=ox;
1261   box.y0-=oy;
1262   box.x1-=ox;
1263   box.y1-=oy;
1264   r.box=box;
1265   wimpt_noerr(wimp_update_wind(&r,&more));
1266   while (more)
1267   {
1268     viewer__redrawicon(&r,i,&box);
1269     wimpt_noerr(wimp_get_rectangle(&r,&more));
1270   }
1271 }
1272
1273 /*
1274  * viewer viewer_iconToViewer(viewer_icon i)
1275  *
1276  * Use
1277  *  Returns the viewer in which an icon is contained.
1278  *
1279  * Parameters
1280  *  viewer_icon i == the icon handle
1281  *
1282  * Returns
1283  *  The viewer handle.
1284  */
1285
1286 viewer viewer_iconToViewer(viewer_icon i)
1287 {
1288   return (i->v);
1289 }
1290
1291 /*
1292  * void *viewer_iconHandle(viewer_icon i)
1293  *
1294  * Use
1295  *  Returns the handle attached to the icon when it was created
1296  *
1297  * Parameters
1298  *  viewer_icon i == the icon in question
1299  *
1300  * Returns
1301  *  The handle attached
1302  */
1303
1304 void *viewer_iconHandle(viewer_icon i)
1305 {
1306   return (i->handle);
1307 }
1308
1309 /*
1310  * char *viewer_textOfIcon(viewer_icon i)
1311  *
1312  * Use
1313  *  Returns the text of the icon given.
1314  *
1315  * Parameters
1316  *  viewer_icon i == the icon
1317  *
1318  * Returns
1319  *  Pointer to the string (read only!).
1320  */
1321
1322 char *viewer_textOfIcon(viewer_icon i)
1323 {
1324   return (i->text);
1325 }
1326
1327 /*
1328  * int viewer_selected(viewer v)
1329  *
1330  * Use
1331  *  Informs caller how many icons are selected.
1332  *
1333  * Parameters
1334  *  viewer v == the viewer handle
1335  *
1336  * Returns
1337  *  The number of icons selected.
1338  */
1339
1340 int viewer_selected(viewer v)
1341 {
1342   return (v->selected);
1343 }
1344
1345 /*
1346  * int viewer_icons(viewer v)
1347  *
1348  * Use
1349  *  Returns the number of icons in a viewer.
1350  *
1351  * Parameters
1352  *  viewer v == the viewer
1353  *
1354  * Returns
1355  *  The number of icons.
1356  */
1357
1358 int viewer_icons(viewer v)
1359 {
1360   return (v->icons);
1361 }
1362
1363 /*
1364  * void viewer_doForIcons
1365  * (
1366  *   viewer v,
1367  *   BOOL onlySelected,
1368  *   void (*proc)(viewer_icon i,void *handle)
1369  * )
1370  *
1371  * Use
1372  *  Does the same thing for either all the icons in a viewer, or just the
1373  *  selected ones.
1374  *
1375  * Parameters
1376  *  viewer v == the viewer handle
1377  *  BOOL onlySelected == whether you want to handle just the selected ones,
1378  *    or the whole lot.
1379  *  void (*proc)(viewer_icon i,void *handle) == the routine to do whatever
1380  *    it is you want to do.
1381  */
1382
1383 void viewer_doForIcons
1384 (
1385   viewer v,
1386   BOOL onlySelected,
1387   void (*proc)(viewer_icon i,void *handle)
1388 )
1389 {
1390   viewer_icon i=v->iconlist;
1391   viewer_icon next;
1392   while (i)
1393   {
1394     next=i->next;
1395     if (i->selected || !onlySelected)
1396       proc(i,i->handle);
1397     i=next;
1398   }
1399 }
1400
1401 /*
1402  * void viewer__select(viewer_icon i,void *handle)
1403  *
1404  * Use
1405  *  Selects an icon.
1406  *
1407  * Parameters
1408  *  viewer_icon i == the icon handle
1409  *  void *handle == a handle (ignored)
1410  */
1411
1412 static void viewer__select(viewer_icon i,void *handle)
1413 {
1414   handle=handle;
1415   viewer_selectIcon(i,TRUE);
1416 }
1417
1418 /*
1419  * void viewer__deselect(viewer_icon i,void *handle)
1420  *
1421  * Use
1422  *  Deselects an icon.
1423  *
1424  * Parameters
1425  *  viewer_icon i == the icon handle
1426  *  void *handle == a handle (ignored)
1427  */
1428
1429 static void viewer__deselect(viewer_icon i,void *handle)
1430 {
1431   handle=handle;
1432   viewer_selectIcon(i,FALSE);
1433 }
1434
1435 /*
1436  * void viewer_selectAll(viewer v,BOOL onOrOff)
1437  *
1438  * Use
1439  *  Selects or deselects all the icons in a viewer.
1440  *
1441  * Parameters
1442  *  viewer v == the viewer handle
1443  *  BOOL onOrOff == whether you want the icons to be on or off
1444  */
1445
1446 void viewer_selectAll(viewer v,BOOL onOrOff)
1447 {
1448   viewer_doForIcons(v,FALSE,onOrOff ? viewer__select : viewer__deselect);
1449   v->menuSel=viewer_NOICON;
1450 }
1451
1452 /*
1453  * BOOL viewer__selectUnknowns(wimp_eventstr *e,void *handle)
1454  *
1455  * Use
1456  *  Selects the icons inside the rubber-band box created, RISC OS 3-like.
1457  *
1458  * Parameters
1459  *  wimp_eventstr *e == the event that I might be interested in
1460  *  void *handle == a (useless) handle
1461  *
1462  * Returns
1463  *  TRUE if the event was interesting.
1464  */
1465
1466 static BOOL viewer__selectUnknowns(wimp_eventstr *e,void *handle)
1467 {
1468   BOOL handled=FALSE;
1469   wimp_box b;
1470   wimp_wstate s;
1471   coords_cvtstr c;
1472   viewer v=viewer__currentViewer;
1473   wimp_box vib;
1474   int i;
1475   int x,y;
1476   viewer_icon icn=v->iconlist;
1477   handle=handle;
1478   switch (e->e)
1479   {
1480     case wimp_EUSERDRAG:
1481       wimpt_noerr(wimp_get_wind_state(v->wind,&s));
1482       c.box=s.o.box;
1483       c.scx=s.o.x;
1484       c.scy=s.o.y;
1485       b.x0=min(e->data.dragbox.x0,e->data.dragbox.x1);
1486       b.y0=min(e->data.dragbox.y0,e->data.dragbox.y1);
1487       b.x1=max(e->data.dragbox.x0,e->data.dragbox.x1);
1488       b.y1=max(e->data.dragbox.y0,e->data.dragbox.y1);
1489       b.x0+=viewer__GAP/2;
1490       b.x1-=viewer__GAP/2;
1491       b.y0+=viewer__GAP/2;
1492       b.y1-=viewer__GAP/2;
1493       coords_box_toworkarea(&b,&c);
1494       if (v->banner[0]!='\0')
1495         coords_offsetbox(&b,0,viewer__BANNERHEIGHT,&b);
1496       vib.x0=b.x0/(v->width+viewer__GAP);
1497       vib.y0=(-b.y1)/(v->height+viewer__GAP);
1498       vib.x1=b.x1/(v->width+viewer__GAP);
1499       vib.y1=(-b.y0)/(v->height+viewer__GAP);
1500       if (b.y1>0)
1501         vib.y0=-1;
1502       if (b.y0>0)
1503         vib.y1=-1;
1504       handled=TRUE;
1505       win_remove_unknown_event_processor(viewer__selectUnknowns,0);
1506       for (i=0;icn!=0;i++,icn=icn->next)
1507       {
1508         x=i%(v->across);
1509         y=i/(v->across);
1510         if (x>=vib.x0 && x<=vib.x1 && y>=vib.y0 && y<=vib.y1)
1511           viewer_selectIcon(icn,TRUE);
1512       }
1513       break;
1514   }
1515   return (handled);
1516 }
1517
1518 /*
1519  * void viewer_clickSelect(viewer v,viewer_icon i,wimp_bbits b)
1520  *
1521  * Use
1522  *  Handles a click on an icon just like clicks in Filer windows.
1523  *
1524  * Parameters
1525  *  viewer v == the viewer handle
1526  *  viewer_icon i == the icon that was clicked
1527  *  wimp_bbits b == the mouse button status
1528  */
1529
1530 void viewer_clickSelect(viewer v,viewer_icon i,wimp_bbits b)
1531 {
1532   viewer_icon icn=v->iconlist;
1533   switch (b)
1534   {
1535     case wimp_BCLICKRIGHT:
1536       if (i!=viewer_NOICON)
1537       {
1538         viewer_selectIcon(v->menuSel,FALSE);
1539         v->menuSel=viewer_NOICON;
1540         viewer_selectIcon(i,!(i->selected));
1541       }
1542       break;
1543     case wimp_BCLICKLEFT:
1544       v->menuSel=viewer_NOICON;
1545       if (i->selected==FALSE || i==viewer_NOICON)
1546       {
1547         while (icn)
1548         {
1549           if (icn->selected)
1550             viewer_selectIcon(icn,FALSE);
1551           icn=icn->next;
1552         }
1553         viewer_selectIcon(i,TRUE);
1554       }
1555       break;
1556     case wimp_BDRAGLEFT:
1557     case wimp_BDRAGRIGHT:
1558       if (i==viewer_NOICON)
1559       {
1560         wimp_mousestr m;
1561         wimp_wstate s;
1562         wimp_dragstr d;
1563         wimpt_noerr(wimp_get_point_info(&m));
1564         wimpt_noerr(wimp_get_wind_state(v->wind,&s));
1565         d.window=v->wind;
1566         d.type=wimp_USER_RUBBER;
1567         d.box.x0=m.x;
1568         d.box.y0=m.y;
1569         d.box.x1=m.x+wimpt_dx();
1570         d.box.y1=m.y+wimpt_dy();
1571         d.parent=s.o.box;
1572         wimpt_noerr(wimp_drag_box(&d));
1573         viewer__currentViewer=v;
1574         win_add_unknown_event_processor(viewer__selectUnknowns,0);
1575       }
1576       else
1577       {
1578         v->menuSel=viewer_NOICON;
1579         viewer_selectIcon(i,TRUE);
1580       }
1581       break;
1582     case wimp_BMID:
1583       if ((v->menuSel!=viewer_NOICON || v->selected==0) && v->menuSel!=i)
1584       {
1585         viewer_selectIcon(v->menuSel,FALSE);
1586         v->menuSel=i;
1587         viewer_selectIcon(v->menuSel,TRUE);
1588       }
1589   }
1590 }
1591
1592 /*
1593  * char *viewer_menuItem(viewer v,char *header)
1594  *
1595  * Use
1596  *  Returns a menu item of the form either "~<header> ''",
1597  *  "<header> '<icon name>'", or "Selection", for inclusion in a menu
1598  *  string.
1599  *
1600  * Parameters
1601  *  viewer v == the viewer handle
1602  *  char *header == the header for the menu item
1603  *
1604  * Returns
1605  *  A pointer to a read-only string.
1606  */
1607
1608 char *viewer_menuItem(viewer v,char *header)
1609 {
1610   char *buffer=buffer_find();
1611   char *ret;
1612   switch (v->selected)
1613   {
1614     case 0:
1615       sprintf(buffer,"~%s ''",header);
1616       ret=buffer;
1617       break;
1618     case 1:
1619       {
1620         viewer_icon i=v->iconlist;
1621         while (i->selected==FALSE)
1622         {
1623           i=i->next;
1624         }
1625         sprintf(buffer,"%s '%s'",header,i->text);
1626         ret=buffer;
1627       }
1628       break;
1629     default:
1630       strcpy(buffer,msgs_lookup("viewerMIS:Selection"));
1631       ret=buffer;
1632       break;
1633   }
1634   return (ret);
1635 }
1636
1637 /*
1638  * void viewer_setupMenu(viewer v,char *header,menu m,int i,char *buff)
1639  *
1640  * Use
1641  *  Writes a menu item out as for the previous routine, but implants it
1642  *  directly into the menu, so you don't need to fiddle about with things
1643  *  like that, and also means that the menu pointer changes.  The menu item
1644  *  must have been previously set up with menu_redirectItem.  This call will
1645  *  also set the menu to the correct width.  However, ensure that you call
1646  *  menu_minWidth(m,0) before fiddling with the width!
1647  *
1648  * Parameters
1649  *  viewer v == the viewer handle pertaining to this request
1650  *  menu m == the menu to doctor
1651  *  int i == the menu item
1652  *  char *buff == where the menu item wants the data
1653  */
1654
1655 void viewer_setupMenu(viewer v,char *header,menu m,int i,char *buff)
1656 {
1657   char *t=viewer_menuItem(v,header);
1658   if (*t=='~')
1659   {
1660     t++;
1661     menu_setflags(m,i,FALSE,TRUE);
1662   }
1663   else
1664     menu_setflags(m,i,FALSE,FALSE);
1665   strcpy(buff,t);
1666   menu_minWidth(m,strlen(buff));
1667 }
1668
1669 /*
1670  * viewer_icon viewer_firstSelected(viewer v)
1671  *
1672  * Use
1673  *  Returns the handle of the first selected icon.
1674  *
1675  * Parameters
1676  *  viewer v == the viewer handle
1677  *
1678  * Returns
1679  *  A handle to the first one, or viewer_NOICON.
1680  */
1681
1682 viewer_icon viewer_firstSelected(viewer v)
1683 {
1684   viewer_icon i=v->iconlist;
1685   while (i)
1686   {
1687     if (i->selected)
1688       return (i);
1689     i=i->next;
1690   }
1691   return (viewer_NOICON);
1692 }
1693
1694 /*
1695  * void viewer_settitle(viewer v,char *title)
1696  *
1697  * Use
1698  *  Changes a viewer's title, so that the extent is updated as well.
1699  *
1700  * Parameters
1701  *  viewer v == the viewer handle
1702  *  char *title == the new title string
1703  *     (internal: may be 0 to indicate just to resize the viewer)
1704  */
1705
1706 void viewer_settitle(viewer v,char *title)
1707 {
1708   wimp_wstate s;
1709   int minw;
1710   int scw;
1711   int maxacc;
1712   wimp_redrawstr r;
1713   if (title)
1714     win_settitle(v->wind,"%s",title);
1715   wimpt_noerr(wimp_get_wind_state(v->wind,&s));
1716   scw=wimpt_scwidth();
1717   maxacc=scw/(v->width+viewer__GAP);
1718   if (maxacc>v->icons)
1719     maxacc=v->icons;
1720   minw=wimpt_stringWidth(v->title)+164;
1721   if (v->banner[0]!='\0')  /* Bug fix */
1722   {
1723     int l=wimpt_stringWidth(v->banner)+64;
1724     if (l>minw)
1725       minw=l;
1726   }
1727   if (minw<viewer__MINWIDTH)
1728     minw=viewer__MINWIDTH;
1729   r.w=v->wind;
1730   r.box.x0=0;
1731   r.box.y0=-(v->height+viewer__GAP)*v->down;
1732   if (v->banner[0]!='\0')
1733     r.box.y0-=viewer__BANNERHEIGHT;
1734   if (r.box.y0>-viewer__MINHEIGHT)
1735     r.box.y0=-viewer__MINHEIGHT;
1736   r.box.x1=(v->width+viewer__GAP)*maxacc;
1737   if (r.box.x1<minw)
1738     r.box.x1=minw;
1739   r.box.y1=0;
1740   wimpt_noerr(wimp_set_extent(&r));
1741   if (s.flags & wimp_WOPEN)
1742     wimpt_noerr(wimp_open_wind(&s.o));
1743 }
1744
1745 /*
1746  * void viewer_dragSelected(viewer_icon icn,wimp_bbits b)
1747  *
1748  * Use
1749  *  Drags a set of icons around a window.
1750  *
1751  * Parameters
1752  *  viewer_icon icn == the viewer icon handle
1753  *  wimp_bbits b == the button types that started this lot off
1754  */
1755
1756 void viewer_dragSelected(viewer_icon icn,wimp_bbits b)
1757 {
1758   wimp_box box;
1759   wimp_dragstr d;
1760   wimp_mousestr m;
1761   BOOL started=FALSE;
1762   viewer v;
1763   viewer_icon i;
1764   os_regset r;
1765   int scWidth=wimpt_scwidth();
1766   int scHeight=wimpt_scheight();
1767   sprite_id sid;
1768   if (icn==viewer_NOICON)
1769     return;
1770   if (b!=wimp_BDRAGLEFT && b!=wimp_BDRAGRIGHT)
1771     return;
1772   viewer_selectIcon(icn,TRUE);
1773   v=icn->v;
1774   wimpt_noerr(wimp_get_point_info(&m));
1775   r.r[0]=161;
1776   r.r[1]=28;
1777   wimpt_noerr(os_swix(XOS_Byte,&r)); /* Support DragASprite? */
1778   if (wimpt_getVersion()>=300 && (r.r[2] & 2))
1779   {
1780     r.r[0]=0xC5;
1781     d.box.x0=m.x-50;
1782     d.box.x1=m.x+50;
1783     d.box.y0=m.y-50;
1784     d.box.y1=m.y+50;
1785     if (v->selected==1)
1786     {
1787       if (icn->filetype==-1)
1788       {
1789         r.r[2]=(int)icn->sprite;
1790         r.r[1]=(int)v->spr;
1791       }
1792       else
1793       {
1794         r.r[2]=(int)fileicon_spriteName(icn->filetype,icn->text);
1795         r.r[1]=1;
1796       }
1797     }
1798     else
1799     {
1800       r.r[2]=(int)"package";
1801       r.r[1]=1;
1802       if ((int)v->spr>1)
1803       {
1804         sid.s.name="package";
1805         sid.tag=0;
1806         if (!sprite_select(v->spr,&sid))
1807           r.r[1]=(int)v->spr;
1808       }
1809     }
1810     r.r[3]=(int)&d.box;
1811     wimpt_noerr(os_swix(XDragASprite_Start,&r));
1812     return;
1813   }
1814   i=v->iconlist;
1815   while (i)
1816   {
1817     if (i->selected==TRUE)
1818     {
1819       viewer_iconToCoords(i,&box);
1820       if (started)
1821       {
1822         if (d.box.x0>box.x0)
1823           d.box.x0=box.x0;
1824         if (d.box.y0>box.y0)
1825           d.box.y0=box.y0;
1826         if (d.box.x1<box.x1)
1827           d.box.x1=box.x1;
1828         if (d.box.y1<box.y1)
1829           d.box.y1=box.y1;
1830       }
1831       else
1832       {
1833         d.box=box;
1834         started=TRUE;
1835       }
1836     }
1837     i=i->next;
1838   }
1839   d.box.x0-=viewer__GAP/2;
1840   d.box.y0-=viewer__GAP/2;
1841   d.box.x1+=viewer__GAP/2;
1842   d.box.y1+=viewer__GAP/2;
1843   d.window=0;
1844   d.type=wimp_USER_FIXED;
1845   d.parent.x0=d.box.x0-m.x;
1846   d.parent.y0=d.box.y0-m.y;
1847   d.parent.x1=scWidth-m.x+d.box.x1;
1848   d.parent.y1=scHeight-m.y+d.box.y1;
1849   wimpt_noerr(wimp_drag_box(&d));
1850 }
1851
1852 /*
1853  * The routines for the data transfer.
1854  */
1855
1856 static BOOL viewer__save(char *name,void *handle)
1857 {
1858   handle=handle;
1859   return
1860   (
1861     viewer__usersave(viewer__currentIcon,name,viewer__currentIcon->handle)
1862   );
1863 }
1864
1865 static BOOL viewer__send(void *handle,int *maxbuf)
1866 {
1867   handle=handle;
1868   return
1869   (
1870     viewer__usersend(viewer__currentIcon,viewer__currentIcon->handle,maxbuf)
1871   );
1872 }
1873
1874 static int viewer__print(char *filename,void *handle)
1875 {
1876   handle=handle;
1877   return
1878   (
1879     viewer__userprint
1880     (
1881       viewer__currentIcon,
1882       filename,
1883       viewer__currentIcon->handle
1884     )
1885   );
1886 }
1887
1888 /*
1889  * void viewer__export(viewer_icon i,void *handle)
1890  *
1891  * Use
1892  *  Exports each icon in turn.
1893  *
1894  * Parameters
1895  *  viewer_icon i == the icon handle
1896  *  void *handle == a handle to the user's data (ignored)
1897  */
1898
1899 static void viewer__export(viewer_icon i,void *handle)
1900 {
1901   wimp_eventstr e;
1902   handle=handle;
1903   viewer__currentIcon=i;
1904   e.e=wimp_EUSERDRAG;
1905   wimpt_fake_event(&e);
1906   xfersend
1907   (
1908     i->filetype==-1 ? viewer__filetype : i->filetype,
1909     i->text,
1910     1,
1911     viewer__usersave==0 ? 0 : viewer__save,
1912     viewer__usersend==0 ? 0 : viewer__send,
1913     viewer__userprint==0 ? 0 : viewer__print,
1914     &e,
1915     0
1916   );
1917 }
1918
1919 /*
1920  * BOOL viewer__unknowns(wimp_eventstr *e,void *handle)
1921  *
1922  * Use
1923  *  Unknown event processor - handles the end of the drag operation.
1924  *
1925  * Parameters
1926  *  wimp_eventstr *e == a pointer to the event.
1927  *  void *handle == a handle to some data (ignored).
1928  *
1929  * Returns
1930  *  Whether the event was interesting.
1931  */
1932
1933 static BOOL viewer__exportUnknowns(wimp_eventstr *e,void *handle)
1934 {
1935   BOOL handled=FALSE;
1936   wimp_mousestr m;
1937   handle=handle;
1938   switch (e->e)
1939   {
1940     case wimp_EUSERDRAG:
1941       wimpt_noerr(wimp_get_point_info(&m));
1942       if (m.w!=viewer__currentViewer->wind)
1943         viewer_doForIcons(viewer__currentViewer,TRUE,viewer__export);
1944       handled=TRUE;
1945       win_remove_unknown_event_processor(viewer__exportUnknowns,0);
1946       break;
1947   }
1948   return (handled);
1949 }
1950
1951 /*
1952  * void viewer_exportSelected
1953  * (
1954  *   viewer_icon icn,
1955  *   wimp_bbits b,
1956  *   int filetype,
1957  *   viewer_saveproc save,
1958  *   viewer_sendproc send,
1959  *   viewer_printproc print
1960  * )
1961  *
1962  * Use
1963  *  Allows you to export the data connected with each selected icon to
1964  *  another application.  The filename used is the icon's text.
1965  *
1966  * Parameters
1967  *  viewer_icon icn == the icon on which the user clicked to start the drag
1968  *    operation.
1969  *  wimp_bbits b == the mouse buttons which started this up.
1970  *  int filetype == the filetype of the data.
1971  *  viewer_saveproc save == the save routine (saving and <Wimp$Scrap>
1972  *    transfer.
1973  *  viewer_sendproc send == the send routine (RAM transfer).
1974  *  viewer_printproc print == the print routine (printing etc.)
1975  */
1976
1977 void viewer_exportSelected
1978 (
1979   viewer_icon icn,
1980   wimp_bbits b,
1981   int filetype,
1982   viewer_saveproc save,
1983   viewer_sendproc send,
1984   viewer_printproc print
1985 )
1986 {
1987   if (icn==viewer_NOICON)
1988     return;
1989   if (b!=wimp_BDRAGLEFT && b!=wimp_BDRAGRIGHT)
1990     return;
1991   viewer_dragSelected(icn,b);
1992   viewer__filetype=filetype;
1993   viewer__usersave=save;
1994   viewer__usersend=send;
1995   viewer__userprint=print;
1996   viewer__currentViewer=icn->v;
1997   win_add_unknown_event_processor(viewer__exportUnknowns,0);
1998 }
1999
2000 /*
2001  * viewer_icon viewer_helpIcon(viewer v)
2002  *
2003  * Use
2004  *  Informs the caller which icon the Help system is interested in.
2005  *
2006  * Parameters
2007  *  viewer v == the viewer handle
2008  *
2009  * Returns
2010  *  The icon's handle (or maybe viewer_NOICON)
2011  */
2012
2013 viewer_icon viewer_helpIcon(viewer v)
2014 {
2015   wimp_eventstr *e=wimpt_last_event();
2016   if (help_wasHelp())
2017     return
2018     (
2019       viewer_iconFromCoords
2020       (
2021         v,
2022         e->data.msg.data.helprequest.m.x,
2023         e->data.msg.data.helprequest.m.y
2024       )
2025     );
2026   else
2027     werr(TRUE,msgs_lookup("viewerVIE:(viewer_helpIcon, caller fault): "
2028                                       "Not called on HELPREQUEST event."));
2029   return (0);
2030 }