chiark / gitweb /
Initial revision
[ssr] / StraySrc / Libraries / Steel / c / listbox
1 /*
2  * ListBox
3  *  Provides handling for list boxes
4  *
5  * v. 1.00 (27 July 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 <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include "wimp.h"
34 #include "wimpt.h"
35 #include "event.h"
36 #include "win.h"
37 #include "template.h"
38 #include "listbox.h"
39 #include "werr.h"
40 #include "msgs.h"
41 #include "bbc.h"
42 #include "mem.h"
43 #include "utils.h"
44 #include "help.h"
45 #include "visdelay.h"
46 #include "dll.h"
47
48 #ifndef _dll_NODLL
49   extern void _dllEntry(list__events)(wimp_eventstr *e,void *handle);
50 #endif
51
52 #define list__ITEMHEIGHT 44
53 #define list__MINHEIGHT 176
54
55 /*
56  * A structure for the list items
57  */
58 typedef struct list__item
59 {
60   struct list__item *next;
61   struct list__item *prev;
62   void *handle;
63   char *data;
64   BOOL selected :1;
65   BOOL nsel :1;
66 }
67 list__item;
68
69 /*
70  * A structure that says everything about a listbox
71  */
72 typedef struct list__liststr
73 {
74   wimp_w wind;
75   wimp_box visSize;
76   wimp_wind *windDef;
77   int foreg;
78   int backg;
79   list_eventhandler events;
80   void *evntHandle;
81   list_raweventhandler rawEvents;
82   void *rawHandle;
83   list_redrawhandler rdr;
84   void *redrawHandle;
85   int items;
86   int width;
87   list_item selected;
88   list_item list;
89   int nsel;
90   pane p;
91   int iHeight;
92   int extWidth;
93   int extraWidth;
94   BOOL alterSize :1;
95   BOOL multSel :1;
96   BOOL update :1;
97 }
98 list__liststr;
99
100 static list list__dragging;
101 static int list__dragIndex;
102 static int list__currentIndex;
103
104 _dll_static void list__events(wimp_eventstr *e,void *handle);
105
106 /*
107  * void list__resize(list l)
108  *
109  * Use
110  *  This routine will resize a list box according to how many items there
111  *  are in it.
112  *
113  * Parameters
114  *  list l == the list handle
115  */
116
117 static void list__resize(list l)
118 {
119   wimp_wstate s;
120   int height;
121   int width;
122   BOOL open;
123   int maxlen;
124   wimp_redrawstr extent;
125   wimp_box b;
126   BOOL hscr=FALSE,ohscr;
127   BOOL vscr=FALSE,ovscr;
128   wimp_winfo w;
129   menu_handler mhnd;
130   pane p=0;
131   int xe=0,ye=0;
132   int scbh=0;
133
134   /* --- Find out if there's anything to do --- */
135
136   if (!l->wind || !l->update)
137     return;
138
139   /* --- Get information about the window --- */
140
141   wimpt_noerr(wimp_get_wind_state(l->wind,&s));
142   open=s.flags & wimp_WOPEN;
143   height=l->items*l->iHeight;
144   width=maxlen=l->width+16+l->extraWidth;
145
146   /* --- If it's a pane listbox, add in scroll bars if necessary --- */
147
148   if (!l->alterSize)
149   {
150     /* --- First, read the position and scroll sizes --- */
151
152     win_gadgetWidths(wimp_WNEW | wimp_WVSCR | wimp_WHSCR,&b);
153
154     /* --- Get the outline extents of the window --- */
155
156     ohscr=s.flags & wimp_WHSCR;
157     ovscr=s.flags & wimp_WVSCR;
158     scbh=b.y0-wimpt_dy();
159     xe=s.o.box.x1-s.o.box.x0+(ovscr ? b.x1-wimpt_dx() : 0);
160     ye=s.o.box.y1-s.o.box.y0+(ohscr ? scbh : 0);
161
162     /* --- Now find out if we really want scroll bars --- */
163
164     if (maxlen>xe)
165     {
166       hscr=TRUE;
167       ye-=b.y0-wimpt_dy();
168     }
169
170     if (height>ye)
171     {
172       vscr=TRUE;
173       xe-=b.x1-wimpt_dx();
174     }
175
176     if (!hscr && maxlen>xe)
177     {
178       hscr=TRUE;
179       ye-=b.y0-wimpt_dy();
180     }
181
182     /* --- If we changed the scroll bars, recreate the window --- */
183
184     if (!hscr!=!ohscr || !vscr!=!ovscr)
185     {
186       /* --- Update the scroll bar flags --- */
187
188       ohscr=hscr;
189       ovscr=vscr;
190
191       /* --- Save all the window's info so we recreate it properly --- */
192
193       if (l->p)
194       {
195         p=l->p;
196         pane_removePane(p,l->wind);
197       }
198       w.w=l->wind;
199       wimpt_noerr(wimp_get_wind_info(&w));
200       menu_saveHandler(l->wind,&mhnd);
201       wimpt_noerr(wimp_delete_wind(l->wind));
202       win_register_event_handler(l->wind,0,0);
203
204       /* --- Set up window with new scroll bar gadgetry --- */
205
206       w.info.flags&=~(wimp_WHSCR | wimp_WVSCR);
207       if (hscr)
208         w.info.flags|=wimp_WHSCR;
209       if (vscr)
210         w.info.flags|=wimp_WVSCR;
211       w.info.flags|=wimp_WNEW;
212       w.info.ex.x1+=200;
213       w.info.ex.y0-=200;
214       wimpt_noerr(wimp_create_wind(&w.info,&l->wind));
215
216       /* --- Read the new outline and check the sizes again --- */
217
218       s.o.w=l->wind;
219       s.o.box.x1=s.o.box.x0+xe;
220       s.o.box.y0=s.o.box.y1-ye;
221       wimpt_noerr(wimp_open_wind(&s.o));
222
223       /* --- Restore everything we saved above --- */
224
225       if (!open)
226         wimpt_noerr(wimp_close_wind(l->wind));
227       win_register_event_handler(l->wind,_dllEntry(list__events),l);
228       menu_restoreHandler(l->wind,&mhnd);
229     }
230   }
231   else
232     xe=l->visSize.x1-l->visSize.x0;
233
234   /* --- Correct the extent dimensions --- */
235
236   if (width<xe)
237     width=xe;
238   if (height<ye && !l->alterSize)
239     height=ye;
240   if (height<list__MINHEIGHT-scbh)
241     height=list__MINHEIGHT-scbh;
242
243   extent.w=l->wind;
244   extent.box.x0=l->visSize.x0;
245   extent.box.x1=l->visSize.x0+width;
246   extent.box.y0=-height;
247   extent.box.y1=0;
248   l->extWidth=width; /* Remember the width of the window for redrawing */
249   wimpt_noerr(wimp_set_extent(&extent));
250   if (open)
251   {
252     wimpt_noerr(wimp_open_wind(&s.o));
253     wimpt_noerr(wimp_force_redraw(&extent));
254   }
255   if (p)
256   {
257     l->p=p;
258     pane_addListbox(p,l);
259   }
260 }
261
262 /*
263  * void list__rescanSize(list l)
264  *
265  * Use
266  *  Recalculates a list box's width.
267  */
268
269 static void list__rescanSize(list l)
270 {
271   list_item itm;
272   int len;
273
274   if (!l->wind || !l->update)
275     return;
276
277   l->width=0;
278   itm=l->list;
279   while (itm)
280   {
281     len=wimpt_stringWidth(itm->data);
282     if (len>l->width)
283       l->width=len;
284     itm=itm->next;
285   }
286   list__resize(l);
287 }
288
289 /*
290  * void list__event(list l,list_item clicked)
291  *
292  * Use
293  *  Posts an event to the event handler registered for the list box (if it's
294  *  there).
295  *
296  * Parameters
297  *  list l == the list box handle
298  *  list_item clicked == the item that was clicked
299  */
300
301 static void list__event(list l,list_item clicked)
302 {
303   if (l->events)
304     (l->events)(l,clicked,l->evntHandle);
305 }
306
307 /*
308  * void list__defaultRedraw(list l,
309  *                          wimp_box *b,
310  *                          char *text,
311  *                          BOOL selected)
312  *
313  * Use
314  *  Standard redraw handler for list boxes.  This may be overridden if the
315  *  list box is a fancy one.
316  *
317  * Parameters
318  *  As for list redraw handlers, except that there's no handle passed to it.
319  */
320
321 static void list__defaultRedraw(list l,
322                                 wimp_box *b,
323                                 char *text,
324                                 BOOL selected)
325 {
326   wimp_icon icn;
327   icn.box=*b;
328   icn.flags=0x00000131+(l->foreg<<24)+(l->backg<<28);
329   icn.data.indirecttext.validstring=(char *)-1;
330   icn.data.indirecttext.bufflen=0;
331   if (selected)
332     icn.flags|=wimp_ISELECTED;
333   else
334     icn.flags&=~wimp_ISELECTED;
335   icn.data.indirecttext.buffer=text;
336   wimpt_noerr(wimp_ploticon(&icn));
337 }
338
339 /*
340  * void list__redraw(wimp_redrawstr *r,void *handle)
341  *
342  * Use
343  *  Redraws a list box window
344  *
345  * Parameters
346  *  wimp_redrawstr *r == redraw structure for this redraw event
347  *  void *handle == pointer to this list box
348  */
349
350 static void list__redraw(wimp_redrawstr *r,void *handle)
351 {
352   list l=(list)handle;
353   list_item itm=l->list;
354   int ox=r->box.x0-r->scx;
355   int oy=r->box.y1-r->scy;
356   int i=0;
357   int first=-(r->g.y1-r->box.y1+r->scy)/l->iHeight;
358   int last=first+(r->g.y1-r->g.y0)/l->iHeight+1;
359   wimp_box box;
360   box.x0=l->visSize.x0;
361   box.x1=l->visSize.x0+l->extWidth;
362   box.y1=0;
363   box.y0=-l->iHeight;
364   while (itm)
365   {
366     if (i>=first && i<=last)
367     {
368       if (l->rdr)
369       {
370         (l->rdr)(l,itm,r,&box,itm->data,
371                  itm->selected || itm->nsel,l->redrawHandle);
372       }
373       else
374         list__defaultRedraw(l,&box,itm->data,itm->selected || itm->nsel);
375     }
376     itm=itm->next;
377     box.y0-=l->iHeight;
378     box.y1-=l->iHeight;
379     i++;
380   }
381   wimpt_noerr(wimp_setcolour(l->backg));
382   if (i<=last)
383   {
384     bbc_rectanglefill(l->visSize.x0+ox,
385                       box.y1+oy-l->iHeight*(last-i+1)-wimpt_dy(),
386                       l->extWidth,
387                       l->iHeight*(last-i+1));
388   }
389 }
390
391 /*
392  * list_item list__coordsToItem(int x,int y,list l)
393  *
394  * Use
395  *  Given a screen position and a list handle, returns
396  *  the item beneath the point.
397  *
398  * Parameters
399  *  int x == absolute OS-unit screen x-coordinate of the point
400  *  int y == absolute OS-unit screen y-coordinate of the point
401  *  list l == the list handle
402  *
403  * Returns
404  *  The list item handle
405  */
406
407 static list_item list__coordsToItem(int x,int y,list l)
408 {
409   wimp_wstate state;
410   int oy;
411   list_item i;
412   int index;
413   x=x;
414   wimpt_noerr(wimp_get_wind_state(l->wind,&state));
415   oy=state.o.box.y1-state.o.y;
416   index=-(y-oy+wimpt_dy())/l->iHeight;
417   if (index>=l->items)
418     return (list_NOITEM);
419   i=list_indexToItem(l,index);
420   return (i);
421 }
422
423 /*
424  * void list__selectIdles(void *handle)
425  *
426  * Use
427  *  Gets passed idle events when dragging out a selection.
428  *
429  * Parameters
430  *  void *handle == a NULL pointer
431  */
432
433 #define min2(x,y) ((x)<(y) ? (x) : (y))
434 #define max2(x,y) ((x)>(y) ? (x) : (y))
435 #define min3(x,y,z) min2(min2(x,y),z)
436 #define max3(x,y,z) max2(max2(x,y),z)
437
438 static void list__redrawItem(list l,int i);
439
440 static void list__selectIdles(void *handle)
441 {
442   /* --- Rewritten 24-Jun-1993 - MDW --- */
443
444   /* --- Variables required to update the drag --- */
445   wimp_mousestr m;                    /* Pointer state (window coords)     */
446   wimp_wstate s;                      /* Position of window                */
447   int ox,oy;                          /* Screen coords of window origin    */
448   int i;                              /* Item index counter                */
449   int start;                          /* Index to start processing items   */
450   int end;                            /* Index to finish processing items  */
451   int index;                          /* Index of item under pointer       */
452   list_item item;                     /* List item ptr (for list traverse) */
453   int sel;                            /* -1 == ignore, 0 == deselect,      */
454                                       /* 1 == select                       */
455
456   handle=handle;
457
458   /* --- Set up pointer position --- */
459   wimpt_noerr(wimp_get_point_info(&m)); /* I only need the coordinates     */
460   wimpt_noerr(wimp_get_wind_state(list__dragging->wind,&s)); /* For origin,*/
461                                       /* and scrolling the window          */
462   ox=s.o.box.x0-s.o.x;                /* Derive position of window origin  */
463   oy=s.o.box.y1-s.o.y;                /* from state as per PRM             */
464   m.x-=ox;                            /* Now convert mouse position to     */
465   m.y-=oy;                            /* window coordinates                */
466   index=-(m.y+wimpt_dy())/list__dragging->iHeight; /* Calc item under ptr  */
467   if (index<0)                        /* Stop negative indices             */
468     index=0;
469
470   /* --- Set up for loop through list items --- */
471   start=min3(list__currentIndex,list__dragIndex,index);
472   end=max3(list__currentIndex,list__dragIndex,index);
473   item=list__dragging->list;
474   i=0;
475   sel=-1;
476
477   /* --- Now loop through the relevant icons --- */
478   while (item)                        /* Carry on until we run out of items*/
479   {
480     if (i==start)
481       sel=0;
482     if (list__dragIndex<index)
483     {
484       if (i==list__dragIndex)
485         sel=TRUE;
486       if (i==index+1)
487         sel=FALSE;
488     }
489     else
490     {
491       if (i==index)
492         sel=TRUE;
493       if (i==list__dragIndex+1)
494         sel=FALSE;
495     }
496     if (i==end+1)
497       sel=-1;
498     if (sel!=-1 && !item->selected && sel!=item->nsel)
499     {
500       item->nsel=sel;
501       list__redrawItem(list__dragging,i);
502       list__dragging->nsel+=(sel==TRUE)-(sel==FALSE);
503     }
504     i++;
505     item=item->next;
506   }
507   list__currentIndex=index;
508
509   /* --- Scroll the window to include the added selection --- */
510   if (m.y>s.o.y)                      /* If pointer too far up, scroll up  */
511     s.o.y=m.y;
512   else if (m.y+s.o.box.y1-s.o.box.y0<s.o.y) /* If too far down, scroll down*/
513     s.o.y=m.y+s.o.box.y1-s.o.box.y0;
514   wimpt_noerr(wimp_open_wind(&s.o));  /* Open the window in new position   */
515 }
516
517 /*
518  * BOOL list__selectUnknowns(wimp_eventstr *e,void *handle)
519  *
520  * Use
521  *  Unknown event handler for dragging out selections.
522  *
523  * Parameters
524  *  wimp_eventstr *e == the unknown event
525  *  void *handle == a NULL pointer
526  *
527  * Returns
528  *  TRUE if the event was interesting
529  */
530
531 static BOOL list__selectUnknowns(wimp_eventstr *e,void *handle)
532 {
533   BOOL handled=FALSE;
534   list_item i;
535   handle=handle;
536   switch (e->e)
537   {
538     case wimp_EUSERDRAG:
539       list__selectIdles(0);
540       for (i=list__dragging->list;i;i=i->next)
541       {
542         if (i->nsel)
543           i->selected=TRUE;
544         i->nsel=FALSE;
545       }
546       win_remove_unknown_event_processor(list__selectUnknowns,0);
547       win_remove_idle_claimer(list__selectIdles,0);
548       handled=TRUE;
549       break;
550   }
551   return (handled);
552 }
553
554 /*
555  * BOOL list__modeChange(wimp_eventstr *e,void *handle)
556  *
557  * Use
558  *  Handles mode change events for list boxes (rescan the width)
559  */
560
561 static BOOL list__modeChange(wimp_eventstr *e,void *handle)
562 {
563   list l=handle;
564
565   switch (e->e)
566   {
567     case wimp_ESEND:
568     case wimp_ESENDWANTACK:
569       if (e->data.msg.hdr.action==wimp_MMODECHANGE ||
570           e->data.msg.hdr.action==0x400CF)
571         list__rescanSize(l);
572       break;
573   }
574   return (FALSE);
575 }
576
577 /*
578  * void list__events(wimp_eventstr *e,void *handle)
579  *
580  * Use
581  *  The event handler for list boxes.
582  *
583  * Parameters
584  *  wimp_eventstr *e == the event
585  *  void *handle == the list box handle
586  */
587
588 _dll_static void list__events(wimp_eventstr *e,void *handle)
589 {
590   list l=(list)handle;
591   BOOL handled=FALSE;
592   if (l->rawEvents)
593     handled=(l->rawEvents)(l,e,l->rawHandle);
594   if (!handled)
595   {
596     switch (e->e)
597     {
598       case wimp_EREDRAW:
599         wimpt_redraw(list__redraw,l);
600         break;
601       case wimp_EOPEN:
602         wimpt_noerr(wimp_open_wind(&e->data.o));
603         break;
604       case wimp_ECLOSE:
605         list__event(l,list_CLOSE);
606         break;
607       case wimp_EBUT:
608         if
609         (
610           e->data.but.m.bbits & wimp_BLEFT ||
611           e->data.but.m.bbits & wimp_BRIGHT ||
612           e->data.but.m.bbits & wimp_BCLICKLEFT ||
613           e->data.but.m.bbits & wimp_BCLICKRIGHT
614         )
615         {
616           list_item i=list__coordsToItem(e->data.but.m.x,e->data.but.m.y,l);
617           if (e->data.but.m.bbits&wimp_BLEFT || l->multSel==FALSE)
618             list_selectItem(l,i);
619           else
620             list_addToSelection(l,i,list_TOGGLESTATE);
621           list__event(l,l->selected);
622         }
623         else if
624         (
625           (e->data.but.m.bbits & wimp_BDRAGLEFT ||
626           e->data.but.m.bbits & wimp_BDRAGRIGHT) &&
627           l->multSel==TRUE
628         )
629         {
630           wimp_dragstr d;
631           wimp_wstate s;
632           wimpt_noerr(wimp_get_wind_state(l->wind,&s));
633           d.type=wimp_USER_HIDDEN;
634           d.parent.x0=e->data.but.m.x;
635           d.parent.y0=0x9001;
636           d.parent.x1=e->data.but.m.x;
637           d.parent.y1=0x6FFE;
638           wimpt_noerr(wimp_drag_box(&d));
639           list__dragging=l;
640           list__dragIndex=-(e->data.but.m.y+wimpt_dy()-s.o.box.y1+s.o.y)/
641                                                             l->iHeight;
642           list__currentIndex=list__dragIndex;
643           list__selectIdles(0);
644           win_add_unknown_event_processor(list__selectUnknowns,0);
645           win_addIdleClaimer(list__selectIdles,2,0);
646         }
647         break;
648       case wimp_EKEY:
649         wimpt_noerr(wimp_processkey(e->data.key.chcode));
650         break;
651       case wimp_ESCROLL:
652         {
653           int height=e->data.scroll.o.box.y1-e->data.scroll.o.box.y0;
654           int width=e->data.scroll.o.box.x1-e->data.scroll.o.box.x0;
655           switch (e->data.scroll.y)
656           {
657             case -2:
658               {
659                 int bottom=-(e->data.scroll.o.y-height)/l->iHeight;
660                 int itms=height/l->iHeight;
661                 if (-bottom*l->iHeight==e->data.scroll.o.y-height)
662                   bottom--;
663                 e->data.scroll.o.y=-(bottom+itms)*l->iHeight+height;
664               }
665               break;
666             case -1:
667               {
668                 int bottom=-(e->data.scroll.o.y-height)/l->iHeight;
669                 bottom++;
670                 e->data.scroll.o.y=-bottom*l->iHeight+height;
671               }
672               break;
673             case 0:
674               break;
675             case 1:
676               {
677                 int top=-(e->data.scroll.o.y)/l->iHeight;
678                 if (-top*l->iHeight==e->data.scroll.o.y)
679                   top--;
680                 e->data.scroll.o.y=-top*l->iHeight;
681               }
682               break;
683             case 2:
684               {
685                 int top=-(e->data.scroll.o.y)/l->iHeight;
686                 int itms=height/l->iHeight;
687                 if (-top*l->iHeight!=e->data.scroll.o.y);
688                   top++;
689                 e->data.scroll.o.y=-(top-itms)*l->iHeight;
690               }
691               break;
692           }
693           switch (e->data.scroll.x)
694           {
695             case -2:
696               e->data.scroll.o.x-=width;
697               break;
698             case -1:
699               e->data.scroll.o.x-=32;
700               break;
701             case 0:
702               break;
703             case 1:
704               e->data.scroll.o.x+=32;
705               break;
706             case 2:
707               e->data.scroll.o.x+=width;
708               break;
709           }
710           wimpt_noerr(wimp_open_wind(&(e->data.scroll.o)));
711         }
712         break;
713       case wimp_ESEND:
714       case wimp_ESENDWANTACK:
715         switch (e->data.msg.hdr.action)
716         {
717           case wimp_MHELPREQUEST:
718             list__event(l,list_HELP);
719             break;
720         }
721         break;
722     }
723   }
724 }
725
726 /*
727  * list_item list__findItem(list l,list_item i)
728  *
729  * Use
730  *  Returns a pointer to a list item.
731  *
732  * Parameters
733  *  list l == the list
734  *  list_item i == the item number
735  *
736  * Returns
737  *  A pointer to the list item or 0 if it couldn't be found.
738  */
739
740 #define list__findItem(l,i) (i)
741
742 /*
743  * void list_isPane(list l,pane p)
744  *
745  * Use
746  *  This routine is part of the private interface between the listbox and
747  *  pane segments.  Do *NOT* try to call it in your own code.
748  */
749
750 void list_isPane(list l,pane p)
751 {
752   l->p=p;
753   if (l->p!=p && p)
754     list__resize(l);
755 }
756
757 /*
758  * void list__redrawItem(list l,int item)
759  *
760  * Use
761  *  Calls for a redraw of a single list item
762  *
763  * Parameters
764  *  list l == the list handle
765  *  int i == the item index
766  */
767
768 static void list__redrawItem(list l,int i)
769 {
770   wimp_redrawstr r;
771   wimp_wstate s;
772   BOOL more;
773   if (i!=-1)
774   {
775     wimpt_noerr(wimp_get_wind_state(l->wind,&s));
776     r.w=l->wind;
777     r.box.x0=s.o.x;
778     r.box.x1=s.o.x+s.o.box.x1-s.o.box.x0;
779     r.box.y0=-l->iHeight*i-l->iHeight;
780     r.box.y1=-l->iHeight*i;
781     wimpt_noerr(wimp_update_wind(&r,&more));
782     while (more)
783     {
784       list__redraw(&r,l);
785       wimpt_noerr(wimp_get_rectangle(&r,&more));
786     }
787   }
788 }
789
790 /*
791  * void list_selectItem(list l,list_item item)
792  *
793  * Use
794  *  Makes the item the currently selected one.
795  *
796  * Parameters
797  *  list l == the list handle
798  *  list_item item == the item number
799  */
800
801 void list_selectItem(list l,list_item item)
802 {
803   list_item i=l->list;
804   int c=0;
805   visdelay_begin();
806   while (i)
807   {
808     if (i->selected==TRUE && i!=item)
809     {
810       i->selected=FALSE;
811       list__redrawItem(l,c);
812       l->nsel--;
813     }
814     c++;
815     i=i->next;
816   }
817   visdelay_end();
818   l->selected=list_NOITEM;
819   list_addToSelection(l,item,list_SETSTATE);
820 }
821
822 /*
823  * list_item list_selected(list l)
824  *
825  * Use
826  *  Returns the currently selected item of the list.
827  *
828  * Parameters
829  *  list l == the list handle
830  *
831  * Returns
832  *  The item number of the currently selected item.
833  */
834
835 list_item list_selected(list l)
836 {
837   list_item itm=l->list;
838   while (itm)
839   {
840     if (itm->selected)
841       return (itm);
842     itm=itm->next;
843   }
844   return (list_NOITEM);
845 }
846
847 /*
848  * list list_create(char *name,BOOL alterSize)
849  *
850  * Use
851  *  Creates a new list box window, and returns a handle to it.  Initially,
852  *  there are no items, and the list box is not shown.  The function returns
853  *  0 if the call fails for one reason or another (not enough windows or
854  *  memory).
855  *
856  * Parameters
857  *  char *name == the name to match in the template file
858  *  BOOL alterSize == TRUE if we are allowed to change the box's visible
859  *    size.
860  *
861  * Returns
862  *  An abstract handle for the list box.
863  */
864
865 list list_create(char *name,BOOL alterSize)
866 {
867   list new=(list)mem_alloc(sizeof(list__liststr));
868   if (new==0)
869   {
870     werr(FALSE,msgs_lookup("listNEM:Not enough room to create list box."));
871     return (0);
872   }
873
874   win_activeinc();
875   new->events=0;
876   new->rawEvents=0;
877   new->items=0;
878   new->width=0;
879   new->list=0;
880   new->selected=list_NOITEM;
881   new->windDef=template_syshandle(name);
882   new->alterSize=alterSize;
883   new->wind=0;
884   new->foreg=new->windDef->colours[wimp_WCWKAREAFORE];
885   new->backg=new->windDef->colours[wimp_WCWKAREABACK];
886   new->multSel=FALSE;
887   new->nsel=0;
888   new->p=0;
889   new->rdr=0;
890   new->extraWidth=0;
891   new->iHeight=list__ITEMHEIGHT;
892   return (new);
893 }
894
895 /*
896  * char *list_itemData(list l,list_item i)
897  *
898  * Use
899  *  Returns the string for an item
900  *
901  * Parameters
902  *  list l == the list handle
903  *  list_item i == the item's handle
904  *
905  * Returns
906  *  A pointer to the item's data
907  */
908
909 char *list_itemData(list l,list_item i)
910 {
911   l=l;
912   return (list__findItem(l,i)->data);
913 }
914
915 /*
916  * list_item list_addItem(list l,char *data,BOOL inOrder)
917  *
918  * Use
919  *  Adds a new piece of data into the list.
920  *
921  * Parameters
922  *  list l == the list handle
923  *  char *data == the data (as a char *, because most of the time you'll
924  *    want to be using character strings).
925  *
926  * Returns
927  *  A handle for the list item.  This is 0 if the call failed.
928  */
929
930 list_item list_addItem(list l,char *data,BOOL inOrder)
931 {
932   list_item itm=(list_item)&(l->list);
933   list_item new=(list_item)mem_alloc(sizeof(list__item));
934   int len;
935
936   if (!new)
937   {
938     werr(FALSE,msgs_lookup("listNEMI:Not enough room to add new item."));
939     return (0);
940   }
941   if (new->data=mem_alloc(strlen(data)+1),!new->data)
942   {
943     mem_free(new);
944     werr(FALSE,msgs_lookup("listNEMI:Not enough room to add new item."));
945     return (0);
946   }
947   strcpy(new->data,data);
948   while (itm->next)
949   {
950     if (utils_caselessCmp(itm->next->data,data)>0 && inOrder==TRUE)
951       break;
952     itm=itm->next;
953   }
954   new->next=itm->next;
955   new->prev=itm;
956   itm->next=new;
957   if (new->next)
958     new->next->prev=new;
959   new->handle=0;
960   new->selected=FALSE;
961   new->nsel=FALSE;
962   l->items++;
963   len=wimpt_stringWidth(data);
964   if (len>l->width)
965     l->width=len;
966   list__resize(l);
967   return (new);
968 }
969
970 /*
971  * list_item list_findItem(list l,char *data)
972  *
973  * Use
974  *  Searches through a list and returns the item number of the item whose
975  *  data matches that given in the function.
976  *
977  * Parameters
978  *  list l == the list handle
979  *  char *data == the data to compare with
980  *
981  * Returns
982  *  The item number of the item, or -1 if no match.
983  */
984
985 list_item list_findItem(list l,char *data)
986 {
987   list_item itm=l->list;
988   list_item found=list_NOITEM;
989   while (itm!=0)
990   {
991     if (utils_caselessCmp(itm->data,data)==0)
992       found=itm;
993     itm=itm->next;
994   }
995   return (found);
996 }
997
998 /*
999  * void list_removeItem(list l,list_item i)
1000  *
1001  * Use
1002  *  Removes an item from the list.
1003  *
1004  * Parameters
1005  *  list l == the list's handle
1006  *  list_item i == the item number
1007  */
1008
1009 void list_removeItem(list l,list_item i)
1010 {
1011   if (i==l->selected)
1012     l->selected=list_NOITEM;
1013   if (i->selected)
1014     l->nsel--;
1015   if (i->prev)
1016     i->prev->next=i->next;
1017   if (i->next)
1018     i->next->prev=i->prev;
1019   l->items--;
1020   mem_free(i->data);
1021   mem_free(i);
1022   list__rescanSize(l);
1023 }
1024
1025 /*
1026  * void list_delete(list l)
1027  *
1028  * Use
1029  *  Destroys a list totally.
1030  *
1031  * Parameters
1032  *  list l == the list handle
1033  */
1034
1035 void list_delete(list l)
1036 {
1037   list_item itm=l->list;
1038   list_item i;
1039   if (l->wind)
1040     list_unlink(l);
1041   win_activedec();
1042   while (itm)
1043   {
1044     i=itm;
1045     itm=i->next;
1046     mem_free(i->data);
1047     mem_free(i);
1048   }
1049   mem_free(l);
1050 }
1051
1052 /*
1053  * void list_updatePosition(list l)
1054  *
1055  * Use
1056  *  Writes the position of the listbox back into the template, so any new
1057  *  list boxes created from the template open at that position.
1058  *
1059  * Parameters
1060  *  list l == the list handle
1061  */
1062
1063 void list_updatePosition(list l)
1064 {
1065   wimp_winfo *w;
1066   if (w=(wimp_winfo *)mem_alloc
1067   (
1068     sizeof(wimp_winfo)+l->windDef->nicons*sizeof(wimp_icon)
1069   ),
1070   w);
1071   {
1072     w->w=l->wind;
1073     wimpt_noerr(wimp_get_wind_info(w));
1074     *(l->windDef)=w->info;
1075     l->windDef->colours[wimp_WCWKAREABACK]=l->backg;
1076                                    /* Put colour back into *your* life :-) */
1077     mem_free(w);
1078   }
1079 }
1080
1081 /*
1082  * BOOL list_link(list l)
1083  *
1084  * Use
1085  *  Links a list box to a WIMP window.  If the list is already linked,
1086  *  nothing happens.  The list box is not displayed.
1087  *
1088  * Parameters
1089  *  list l == the list handle
1090  *
1091  * Returns
1092  *  TRUE if the link succeeded (may run out of windows!)
1093  */
1094
1095 BOOL list_link(list l)
1096 {
1097   int ox,oy;
1098   if (l->wind!=0)
1099     return(TRUE);
1100   l->windDef->colours[wimp_WCWKAREABACK]=255;
1101                        /* We draw the whole lot anyway, so this is quicker */
1102   if (wimp_create_wind(l->windDef,&l->wind))
1103   {
1104     werr
1105     (
1106       FALSE,
1107       msgs_lookup("listTMW:Too many windows - "
1108                                          "%s could not create list box."),
1109       wimpt_programname()
1110     );
1111     l->wind=0;
1112     l->windDef->colours[wimp_WCWKAREABACK]=l->backg;
1113                                     /* Put it back for reading again later */
1114     return (FALSE);
1115   }
1116   l->windDef->colours[wimp_WCWKAREABACK]=l->backg;
1117                                     /* Put it back for reading again later */
1118   win_register_event_handler(l->wind,_dllEntry(list__events),l);
1119   win_add_unknown_event_processor(list__modeChange,l);
1120   ox=l->windDef->box.x0-l->windDef->scx;
1121   oy=l->windDef->box.y1-l->windDef->scy;
1122   l->visSize.x0=l->windDef->ex.x0;
1123   l->visSize.y0=l->windDef->box.y0-oy;
1124   l->visSize.x1=l->windDef->ex.x1;
1125   l->visSize.y1=l->windDef->box.y1-oy;
1126   l->update=TRUE;
1127   list__rescanSize(l);
1128   return (TRUE);
1129 }
1130
1131 /*
1132  * void list_unlink(list l)
1133  *
1134  * Use
1135  *  Unlinks the list box from its window and deletes the window.
1136  *
1137  * Parameters
1138  *  list l == the list handle
1139  */
1140
1141 void list_unlink(list l)
1142 {
1143   if (l->wind==0)
1144     return;
1145   list_updatePosition(l);
1146   win_register_event_handler(l->wind,0,0);
1147   win_remove_unknown_event_processor(list__modeChange,l);
1148   wimpt_noerr(wimp_delete_wind(l->wind));
1149   l->wind=0;
1150 }
1151 /*
1152  * void list_unlinkNoUpdate(list l)
1153  *
1154  * Use
1155  *  Unlinks a list from its window without storing its final position back
1156  *  in memory.  Note that list_delete() calls list_unlink(), so make sure
1157  *  you call this routine before deleting the list box it you want to
1158  *  prevent the position being written back.
1159  *
1160  * Parameters
1161  *  list l == the list handle
1162  */
1163
1164 void list_unlinkNoUpdate(list l)
1165 {
1166   if (l->wind==0)
1167     return;
1168   win_register_event_handler(l->wind,0,0);
1169   wimpt_noerr(wimp_delete_wind(l->wind));
1170   l->wind=0;
1171 }
1172
1173
1174 /*
1175  * void list_update(list l,BOOL really)
1176  *
1177  * Use
1178  *  Enables or disables list updating (i.e. whether the listbox resizes and
1179  *  redraws itself between every operation).  If you're going to do some
1180  *  lengthy operation, it would be a good idea to turn update off first,
1181  *  and then turn it on again afterwards.
1182  *
1183  *  Note that this is an all-or-nothing thing -- no counter is kept.
1184  *
1185  * Parameters
1186  *  list l == the listbox which we're messing about with
1187  *  BOOL really == whether to turn the update on or off
1188  */
1189
1190 void list_update(list l,BOOL really)
1191 {
1192   l->update=really;
1193   if (really)
1194     list__rescanSize(l);
1195 }
1196
1197 /*
1198  * void list_display(list l)
1199  *
1200  * Use
1201  *  Displays a list box on-screen
1202  *
1203  * Parameters
1204  *  list l == the list
1205  */
1206
1207 void list_display(list l)
1208 {
1209   wimp_wstate state;
1210   if (l->wind==0)
1211   {
1212     if (list_link(l)==0)
1213       return;
1214   }
1215   wimpt_noerr(wimp_get_wind_state(l->wind,&state));
1216   state.o.behind=(wimp_w)-1;
1217   wimpt_noerr(wimp_open_wind(&state.o));
1218 }
1219
1220 /*
1221  * void list_openDisplaced(list l)
1222  *
1223  * Use
1224  *  Opens the listbox on-screen displaced from its previous position by the
1225  *  normal 48 OS units.  It must be unlinked using list_unlinkNoUpdate
1226  *  before deletion.
1227  *
1228  * Parameters
1229  *  list l == the list handle.
1230  */
1231
1232 void list_openDisplaced(list l)
1233 {
1234   int new=0;
1235   wimp_wstate s;
1236   if (l->windDef->box.y0-48<132)
1237   {
1238     new=((976-l->windDef->box.y1)/48-4)*48+l->windDef->box.y1;
1239     l->windDef->box.y0+=new-l->windDef->box.y1;
1240     l->windDef->box.y1=new;
1241   }
1242   else
1243   {
1244     l->windDef->box.y0-=48;
1245     l->windDef->box.y1-=48;
1246   }
1247   wimpt_noerr(wimp_get_wind_state(l->wind,&s));
1248   s.o.box=l->windDef->box;
1249   s.o.behind=-1;
1250   wimpt_noerr(wimp_open_wind(&s.o));
1251 }
1252
1253 /*
1254  * void list_hide(list l)
1255  *
1256  * Use
1257  *  Stops the list box being displayed
1258  *
1259  * Parameters
1260  *  list l == the list handle
1261  */
1262
1263 void list_hide(list l)
1264 {
1265   wimpt_noerr(wimp_close_wind(l->wind));
1266 }
1267
1268 /*
1269  * wimp_w list_syshandle(list l)
1270  *
1271  * Use
1272  *  Returns the WIMP window handle being used for the list box.
1273  *
1274  * Parameters
1275  *  list l == the list
1276  *
1277  * Returns
1278  *  The window handle (a wimp_w).
1279  */
1280
1281 wimp_w list_syshandle(list l)
1282 {
1283   return (l->wind);
1284 }
1285
1286 /*
1287  * void list_eventHandler(list l,list_eventhandler proc,void *handle)
1288  *
1289  * Use
1290  *  Attaches an event handler to the list box.  Most won't actually need
1291  *  this, just the stand-alone ones, or ones that do clever-dick things.
1292  *
1293  * Parameters
1294  *  list l == the list handle
1295  *  list_eventhandler proc == the procedure to handle events
1296  *  void *handle == the jolly olde pointer to the user-defined data
1297  */
1298
1299 void list_eventHandler(list l,list_eventhandler proc,void *handle)
1300 {
1301   l->events=proc;
1302   l->evntHandle=handle;
1303 }
1304
1305 /*
1306  * void list_rawEventHandler(list l,list_raweventhandler proc,void *handle)
1307  *
1308  * Use
1309  *  Attaches the raw event handler procedure to the list box.  You can then
1310  *  vet any events coming into the list box and have your wicked way with
1311  *  them.
1312  *
1313  * Parameters
1314  *  list l == the list handle
1315  *  list_raweventhandler proc == the raw event handler routine
1316  *  void *handle == the (now-famous) caller defined handle.
1317  */
1318
1319 void list_rawEventHandler(list l,list_raweventhandler proc,void *handle)
1320 {
1321   l->rawEvents=proc;
1322   l->rawHandle=handle;
1323 }
1324
1325 /*
1326  * void list_redrawHandler(list l,list_redrawhandler rdr,void *handle)
1327  *
1328  * Use
1329  *  Attaches a replacement redraw handler to the list box.  The new handler
1330  *  redraws individual items in the list.  It must fill in the box it is
1331  *  passed, because the WIMP does not fill in the background.  The part of
1332  *  the list box not populated by items is filled in the window background
1333  *  colour automatically.  Using this, you can achieve quite a pleasing
1334  *  effect by making the background grey (colour 1) and the actual item
1335  *  backgrounds white (colour 0), indicating clearly the bottom of the list.
1336  *
1337  *  You can also implement other things like items being shaded etc.
1338  *
1339  * Parameters
1340  *  list l == the list to which the redraw handler is to be attached
1341  *  list_redrawhandler == the replacement redraw routine (or 0 to cancel)
1342  *  void *handle == a pointer to pass the redraw routine
1343  */
1344
1345 void list_redrawHandler(list l,list_redrawhandler rdr,void *handle)
1346 {
1347   l->rdr=rdr;
1348   l->redrawHandle=handle;
1349 }
1350
1351 /*
1352  * void list_widthAdd(list l,int extra)
1353  *
1354  * Use
1355  *  Since redraw handlers can basically do whatever they want to for each
1356  *  items, the list manager no longer really has any idea about how wide an
1357  *  item is.  Since the main thing displayed in lists is text, it is assumed
1358  *  that the width of an item is the width of the longest piece of text plus
1359  *  a constant (e.g. for a sprite on the side).  If this is not so, I'll
1360  *  have to rethink this bit...
1361  *
1362  * Parameters
1363  *  list l == the list we're setting the width of
1364  *  int extra == the constant to add to each item
1365  */
1366
1367 void list_widthAdd(list l,int extra)
1368 {
1369   l->extraWidth=extra;
1370   list__resize(l); /* In case it's open */
1371 }
1372
1373 /*
1374  * void list_setItemHeight(list l,int height)
1375  *
1376  * Use
1377  *  Sets the height of items in the specified list box.  By default, the
1378  *  height is 44 OS units (the height of menu items).  This should be
1379  *  sufficient for most purposes.
1380  *
1381  * Parameters
1382  *  list l == the list to set
1383  *  int height == the new height of each item, in OS units
1384  */
1385
1386 void list_setItemHeight(list l,int height)
1387 {
1388   l->iHeight=height;
1389   list__resize(l); /* In case it's open now */
1390 }
1391
1392 /*
1393  * int list_items(list l)
1394  *
1395  * Use
1396  *  Informs the caller how many items there are in a list
1397  *
1398  * Parameters
1399  *  list l == the list handle
1400  *
1401  * Returns
1402  *  The number of items in a list
1403  */
1404
1405 int list_items(list l)
1406 {
1407   return (l->items);
1408 }
1409
1410 /*
1411  * void list_doForItems
1412  * (
1413  *   list l,
1414  *   void (*proc)(list l,list_item i,void *handle),
1415  *   void *handle
1416  * )
1417  *
1418  * Use
1419  *  Performs an operation on all of the items in a list box.
1420  *
1421  * Parameters
1422  *  list l == the list handle
1423  *  void (*proc)(list l,list_item i,void *handle) == the procedure to use
1424  *  void *handle == a handle to pass to the procedure.
1425  */
1426
1427 void list_doForItems
1428 (
1429   list l,
1430   void (*proc)(list l,list_item i,void *handle),
1431   void *handle
1432 )
1433 {
1434   list_item i=l->list;
1435   list_item next;
1436   visdelay_begin();
1437   while (i)
1438   {
1439     next=i->next;
1440     proc(l,i,handle);
1441     i=next;
1442   }
1443   visdelay_end();
1444 }
1445
1446 /*
1447  * void list_attachData(list l,list_item i,void *data)
1448  *
1449  * Use
1450  *  Attaches a data structure to a list item.  This is a bit of an
1451  *  afterthought really.
1452  *
1453  * Parameters
1454  *  list l == the list
1455  *  list_item i == the list item
1456  *  void *data == a pointer to the data structure
1457  */
1458
1459 void list_attachData(list l,list_item i,void *data)
1460 {
1461   l=l;
1462   list__findItem(l,i)->handle=data;
1463 }
1464
1465 /*
1466  * void *list_getData(list l,list_item i)
1467  *
1468  * Use
1469  *  Returns the data attached to a list item.  The item number may have
1470  *  changed etc. with items deleted and added.
1471  *
1472  * Parameters
1473  *  list l == the list
1474  *  list_item i == the list item number
1475  *
1476  * Returns
1477  *  A pointer to the data structure attached using list_attachData().
1478  */
1479
1480 void *list_getData(list l,list_item i)
1481 {
1482   l=l;
1483   if (i!=list_NOITEM)
1484     return (list__findItem(l,i)->handle);
1485   else
1486     return (0);
1487 }
1488
1489 /*
1490  * void list_multipleSelection(list l)
1491  *
1492  * Use
1493  *  Makes a list able to handle a multiple selection.
1494  *
1495  * Parameters
1496  *  list l == the list handle
1497  */
1498
1499 void list_multipleSelection(list l)
1500 {
1501   l->multSel=TRUE;
1502 }
1503
1504 /*
1505  * BOOL list_addToSelection(list l,list_item i,list_action a)
1506  *
1507  * Use
1508  *  Adds an item to the current selection.
1509  *
1510  * Parameters
1511  *  list l == the list
1512  *  list_item i == the list item
1513  *  list_action a == what to do with the state
1514  *
1515  * Returns
1516  *  The previous state of the item
1517  */
1518
1519 BOOL list_addToSelection(list l,list_item i,list_action a)
1520 {
1521 #ifdef notdef
1522   wimp_wstate state;
1523   int top;
1524   int bottom;
1525   int height;
1526 #endif
1527   BOOL ostate;
1528   if (i==list_NOITEM)
1529     return (FALSE);
1530   l->selected=i;
1531   ostate=i->selected;
1532   switch (a)
1533   {
1534     case list_SETSTATE:
1535       i->selected=TRUE;
1536       break;
1537     case list_RESETSTATE:
1538       i->selected=FALSE;
1539       break;
1540     case list_TOGGLESTATE:
1541       i->selected=!(i->selected);
1542       break;
1543   }
1544   l->nsel+=i->selected-ostate;
1545   if (l->wind==0 || i->selected==ostate)
1546     return (ostate);
1547   list__redrawItem(l,list_itemToIndex(l,l->selected));
1548 #ifdef notdef
1549   top=-l->iHeight*list_itemToIndex(l,i);
1550   bottom=top-l->iHeight;
1551   wimpt_noerr(wimp_get_wind_state(l->wind,&state));
1552   height=state.o.box.y1-state.o.box.y0;
1553   if (state.o.y<top)
1554     state.o.y=top;
1555   if (state.o.y-height>bottom)
1556     state.o.y=bottom+height;
1557   wimpt_noerr(wimp_open_wind(&state.o));
1558 #endif
1559   return (ostate);
1560 }
1561
1562 /*
1563  * void list_doForSelected
1564  * (
1565  *   list l,
1566  *   void (*proc)(list l,list_item i,void *handle),
1567  *   void *handle
1568  * )
1569  *
1570  * Use
1571  *  Performs a user-specified action for each selected list item.
1572  *
1573  * Parameters
1574  *  list l == the list box
1575  *  void (*proc)(list l,list_item i,void *handle) == the procedure to do
1576  *    the thing
1577  *  void *handle == a handle to pass to the routine.
1578  */
1579
1580 void list_doForSelected
1581 (
1582   list l,
1583   void (*proc)(list l,list_item i,void *handle),
1584   void *handle
1585 )
1586 {
1587   list_item i=l->list;
1588   list_item next;
1589   visdelay_begin();
1590   while (i)
1591   {
1592     next=i->next;
1593     if (i->selected)
1594       proc(l,i,handle);
1595     i=next;
1596   }
1597   visdelay_end();
1598 }
1599
1600 /*
1601  * void list__sel(list l,list_item i,void *handle)
1602  *
1603  * Use
1604  *  Selects a single item in a list.  This is a list_doForItems() procedure.
1605  *
1606  * Parameters
1607  *  list l == the list
1608  *  list_item i == the list item to select
1609  *  void *handle == a pointer to a variable of type list_action
1610  */
1611
1612 static void list__sel(list l,list_item i,void *handle)
1613 {
1614   list_action *a=(list_action *)handle;
1615   list_addToSelection(l,i,*a);
1616 }
1617
1618 /*
1619  * void list_selectAll(list l,list_action a)
1620  *
1621  * Use
1622  *  Selects all the items in a list
1623  *
1624  * Parameters
1625  *  list l == the list
1626  *  list_action a == what to do with the icons (list_READSTATE ain't all
1627  *    that useful, and list_TOGGLESTATE is downright wierd).
1628  */
1629
1630 void list_selectAll(list l,list_action a)
1631 {
1632   list_doForItems(l,list__sel,&a);
1633 }
1634
1635 /*
1636  * int list_numSelected(list l)
1637  *
1638  * Use
1639  *  Informs the caller how many items are selected.
1640  *
1641  * Parameters
1642  *  list l == the list handle
1643  *
1644  * Returns
1645  *  An integer indicating the number of selected items
1646  */
1647
1648 int list_numSelected(list l)
1649 {
1650   return (l->nsel);
1651 }
1652
1653 /*
1654  * int list_itemToIndex(list l,list_item i)
1655  *
1656  * Use
1657  *  Translates a list_item into the index of the item.  This avoids
1658  *  assumptions about the mapping of indices to item numbers.  In the
1659  *  future, for example, the list_item data type may be used as a pointer to
1660  *  the list item data structure in memory.  Most of the time this will be
1661  *  irrelevant anyway.
1662  *
1663  * Parameters
1664  *  list l == the list handle
1665  *  list_item i == the list item to translate
1666  *
1667  * Returns
1668  *  An integer giving the index of the item.  The first item has index 0, as
1669  *  for C arrays.
1670  */
1671
1672 int list_itemToIndex(list l,list_item i)
1673 {
1674   int ind=0;
1675   list_item itm=l->list;
1676   while (itm)
1677   {
1678     if (itm==i)
1679       return (ind);
1680     itm=itm->next;
1681     ind++;
1682   }
1683   return (-1);
1684 }
1685
1686 /*
1687  * list_item list_indexToItem(list l,int index)
1688  *
1689  * Use
1690  *  Performs the reverse operation to the previous routine.  It will
1691  *  translate an index to a list_item.
1692  *
1693  * Parameters
1694  *  list l == the list handle
1695  *  int index == the index of the item required
1696  *
1697  * Returns
1698  *  The equivalent list_item for the item.
1699  */
1700
1701 list_item list_indexToItem(list l,int index)
1702 {
1703   int ind=0;
1704   list_item itm=l->list;
1705   while (itm)
1706   {
1707     if (ind==index)
1708       return (itm);
1709     itm=itm->next;
1710     ind++;
1711   }
1712   return (list_NOITEM);
1713 }
1714
1715 /*
1716  * list_item list_helpItem(list l)
1717  *
1718  * Use
1719  *  Returns the item that Help is interested in.
1720  *
1721  * Parameters
1722  *  list l == the list's handle
1723  *
1724  * Returns
1725  *  A list_item
1726  */
1727
1728 list_item list_helpItem(list l)
1729 {
1730   wimp_eventstr *e=wimpt_last_event();
1731   if (help_wasHelp())
1732     return (list__coordsToItem
1733     (
1734       e->data.msg.data.helprequest.m.x,
1735       e->data.msg.data.helprequest.m.y,l
1736     ));
1737   else
1738     werr(TRUE,msgs_lookup("listHIE:(list_helpItem, caller fault): "
1739                                      "Not called on HELPREQUEST event."));
1740   return (0);
1741 }