chiark / gitweb /
Initial revision
[ssr] / StraySrc / Libraries / Steel / c / tearoff
1 /*
2  * tearoff.c
3  *
4  * Straylight TMS Segment
5  * Nice tearoff menu system - impressive even if I do say so myself
6  *
7  * © 1995-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 #define dbox__INTERNALS
30 #define _CORE
31 #define _STDAPP
32
33 #include <stdlib.h>
34 #include <stdarg.h>
35
36 #include "steel.h"
37 #include "string.h"
38 #include "os.h"
39 #include "bbc.h"
40 #include "xtearoff.h"
41 #include "font.h"
42 #include "swis.h"
43 #include "alarm.h"
44
45 #include "tearSupt/tearSupt.h"
46
47 /*----- Global defintions -------------------------------------------------*/
48
49 static int tearoff__x, tearoff__y;     /* Position to open next menu */
50
51 static tearoff
52   tearoff__prevLevel = NULL,           /* Most recent parent */
53   tearoff__currentMenu = NULL,         /* Current Transient menu */
54   tearoff__originatingTearoff = NULL,  /* Most recent tornoff parent */
55   tearoff__tornoffList = NULL,         /* List of torn off menus */
56   tearoff__oldHandle = NULL;           /* Idle event handle */
57
58 static wimp_w tearoff__currentDbox=0;  /* Current dbox opened */
59 static BOOL tearoff__subMenuPending=FALSE,
60             riscos3,                   /* We are on risos3 */
61             tearoff__menuActive=TRUE;  /* Are menus active */
62 static int tearoff__alarmTime,         /* Time alarm was set */
63            tearoff__alarmTime2;        /* Time 2nd alarm was set */
64 static tearoff__alarm tearoff__alm;
65
66 /*----- Icon handles for the tear bar -------------------------------------*/
67
68 #define tearIcon__tear 0
69 #define tearIcon__close 0
70 #define tearIcon__fold 1
71
72 /*----- Function prototypes for cyclic definitions ------------------------*/
73
74 static void tearoff__doIdles(void *handle);
75 static void tearoff__windowHandler(wimp_eventstr *e, void *handle);
76
77 /*----- Tearoff code - Not for the casual observer!! ----------------------*/
78
79 static void tearoff_closeMenus(void)
80 {
81   tearSupport_switch(1);
82   wimp_create_menu((wimp_menustr*)-1,0,0);
83   tearSupport_switch(0);
84 }
85
86 /*----- Window creation routines ----------------------------------------- */
87
88 /* --- Create the tear icon in the given window --- */
89
90 static void tearoff__createTearIcon(tearoff t /* was wimp_w w [mdw] */)
91 {
92   wimp_w w=t->w; /* [mdw] */
93   wimp_icreate ict;
94   wimp_i i;
95
96 #ifdef notdef /* [mdw] */
97
98   ict.i.box.x0 = 4;
99   ict.i.box.x1 = 68;
100   ict.i.box.y1 = -4;
101   ict.i.box.y0 = -20;
102
103 #else /* [mdw] */
104
105   ict.i.box.x0 = 0;
106   ict.i.box.x1 = t->width;
107   ict.i.box.y1 = 0;
108   ict.i.box.y0 = -24;
109
110 #endif /* [mdw] */
111
112   ict.i.flags  = wimp_IVCENTRE |
113                  wimp_IFILLED |
114                  wimp_IBTYPE * wimp_BCLICKDEBOUNCE |
115                  wimp_ISPRITE |
116                  wimp_INDIRECT |
117                  (0x30000000) /* [mdw] */;
118
119   ict.i.data.indirectsprite.name       = "tear";
120   ict.i.data.indirectsprite.spritearea = resspr_area();
121   ict.i.data.indirectsprite.nameisname = 4;
122
123   ict.w = w;
124   wimpt_noerr(wimp_create_icon(&ict, &i));
125 }
126
127 /* --- Create the close/fold icons in the menu */
128
129 static void tearoff__createTornIcons(tearoff t)
130 {
131   wimp_icreate ict;
132   wimp_i i;
133
134 #ifdef notdef /* [mdw] */
135
136   ict.i.box.x0 = 4;
137   ict.i.box.x1 = 70;
138   ict.i.box.y1 = -4;
139   ict.i.box.y0 = -20;
140
141 #else /* [mdw] */
142
143   ict.i.box.x0 = 0;
144   ict.i.box.x1 = (t->width/2)-2;
145   ict.i.box.y1 = 0;
146   ict.i.box.y0 = -24;
147
148 #endif /* [mdw] */
149
150   ict.i.flags = wimp_IVCENTRE |
151                 wimp_IFILLED |
152                 wimp_IBTYPE * wimp_BCLICKDEBOUNCE |
153                 wimp_ISPRITE |
154                 wimp_INDIRECT |
155                 (0x30000000) /* [mdw] */;
156
157   ict.i.data.indirectsprite.name       = "close";
158   ict.i.data.indirectsprite.spritearea = resspr_area();
159   ict.i.data.indirectsprite.nameisname = 4;
160
161   ict.w = t->w;
162   wimpt_noerr(wimp_create_icon(&ict, &i));
163
164 /* wimp_set_icon_state(t->w, i, 0, 0); [mdw] */
165
166   /* Now create the fold icon */
167
168 #ifdef notdef /* [mdw] */
169
170   ict.i.box.x1 = t->width - 4;
171   ict.i.box.x0 = ict.i.box.x1 - 60;
172   ict.i.box.y1 = -4;
173   ict.i.box.y0 = -20;
174
175 #else /* [mdw] */
176
177   ict.i.box.x0 = (t->width/2)+2;
178   ict.i.box.x1 = t->width;
179   ict.i.box.y1 = 0;
180   ict.i.box.y0 = -24;
181
182 #endif /* [mdw] */
183
184   ict.i.flags = wimp_IVCENTRE |
185                 wimp_IFILLED |
186                 wimp_IBTYPE * wimp_BCLICKDEBOUNCE |
187                 wimp_ISPRITE | wimp_INDIRECT |
188                 wimp_IRJUST | /* [mdw] */
189                 (0x30000000) /* [mdw] */;
190
191
192   ict.i.data.indirectsprite.name       = "fold";
193   ict.i.data.indirectsprite.spritearea = resspr_area();
194   ict.i.data.indirectsprite.nameisname = 4;
195
196   ict.w = t->w;
197   wimpt_noerr(wimp_create_icon(&ict, &i));
198
199 /* wimp_set_icon_state(t->w, i, 0, 0); [mdw] */
200
201   { /* --- start [mdw] --- */
202
203   wimp_redrawstr r;
204   int more;
205
206   r.w=t->w;
207   r.box.x0=0; r.box.x1=t->width;
208   r.box.y0=-24; r.box.y1=0;
209   wimpt_noerr(wimp_update_wind(&r,&more));
210   while (more)
211   {
212     wimp_setcolour(0);
213     bbc_rectanglefill(r.box.x0-r.scx+t->width/2-2,
214                       r.box.y1-r.scy-24,
215                       4,24);
216     wimpt_noerr(wimp_get_rectangle(&r,&more));
217   }
218
219   /* --- end [mdw] --- */ }
220 }
221
222 /* --- Called to calculate the width of a menu */
223
224 void tearoff_calculateMenuWidth(tearoff t)
225 {
226   tearoff__item *i;
227   int cnt;
228   int max,w;
229
230   i=(tearoff__item *)(t+1);
231   max=0;
232   for (cnt=1;cnt<=t->numberOfItems;cnt++,i++)
233   {
234     w=wimpt_stringWidth(i->text);
235     if (w>max) max=w;
236   }
237
238   t->width=max;
239
240   i=(tearoff__item *)(t+1);
241   max=0;
242   for (cnt=1;cnt<=t->numberOfItems;cnt++,i++)
243   {
244     if (i->keys)
245     {
246       w=wimpt_stringWidth(i->keys);
247       if (w>max) max=w;
248     }
249   }
250   if (max) max+=16;
251
252   t->width+=48+16+max;
253   t->keyWidth=max;
254
255   w=wimpt_stringWidth(t->menuTitle);
256   if (t->width<w) t->width=w;
257   if (t->width<150 && t->tearoff) t->width=150;
258 }
259
260 /* --- Creates a window for a menu --- */
261
262 static wimp_w tearoff__createWindow(tearoff t)
263 {
264   wimp_wind wind;
265   wimp_icreate ict;
266   wimp_i i;
267   wimp_w w;
268   int extra = (t->tearoff)?tearoff__HEIGHT:0, h;
269   int scy=(bbc_vduvar(bbc_YWindLimit)+1)<<bbc_vduvar(bbc_YEigFactor);
270
271   /* First create the window itself */
272
273   tearoff_calculateMenuWidth(t);
274
275   wind.ex.x0   = 0;
276   wind.ex.x1   = t->width;
277   wind.ex.y0   = -t->numberOfItems * 44 - extra - t->dotted;
278   wind.ex.y1   = 0;
279
280   h = wind.ex.y1-wind.ex.y0;  /* Height */
281
282   wind.box.x0  = 0;
283   wind.box.x1  = t->width;
284   if (t->maxHeight && h > t->maxHeight)
285     wind.box.y0  = -t->maxHeight;
286   else
287     wind.box.y0  = -h;
288   wind.box.y1  = 0;
289   wind.scx     = 0;
290   wind.scy     = 0;
291
292   wind.minsize = 0;
293
294   wind.behind  = -1;
295
296   wind.flags   = wimp_WMOVEABLE |
297                  wimp_WTITLE |
298                  wimp_WNEW;
299
300   t->scrollBar=FALSE;
301
302   if (!t->folded && (t->maxHeight && h > t->maxHeight || (h+50)>scy))
303   {
304     wind.flags |= wimp_WVSCR;
305     t->scrollBar=TRUE;
306   }
307
308   wind.titleflags = wimp_ITEXT |
309                      wimp_IHCENTRE |
310                      wimp_IHCENTRE |
311                      wimp_INDIRECT;
312
313   wind.workflags  = wimp_IBTYPE * wimp_BCLICKDEBOUNCE;
314
315   wind.colours[wimp_WCTITLEFORE]   = 7;
316   wind.colours[wimp_WCTITLEBACK]   = 2;
317   wind.colours[wimp_WCWKAREAFORE]  = 7;
318   wind.colours[wimp_WCWKAREABACK]  = 0;
319   wind.colours[wimp_WCSCROLLOUTER] = 3;
320   wind.colours[wimp_WCSCROLLINNER] = 1;
321   wind.colours[wimp_WCTITLEHI]     = 12;
322   wind.colours[wimp_WCRESERVED]    = 0;
323
324   wind.spritearea = (void *)1;
325
326   wind.title.indirecttext.buffer      = t->menuTitle;
327   wind.title.indirecttext.validstring = 0;
328   wind.title.indirecttext.bufflen     = strlen(t->menuTitle + 1);
329
330   wind.nicons = 0;
331
332   if (wimp_create_wind(&wind, &w)) return -1;
333   t->w=w;
334
335   /* Create tearoff icon if needed */
336
337   if (t->tearoff)
338   {
339
340     #ifdef notdef /* [mdw] */
341
342     /* First the blank bar */
343
344     ict.i.box.x0 = 0;
345     ict.i.box.x1 = t->width;
346     ict.i.box.y1 = 0;
347     ict.i.box.y0 = -tearoff__HEIGHT;
348
349     ict.i.flags  = wimp_IVCENTRE |
350                    wimp_IFILLED |
351                    wimp_IBTYPE * wimp_BIGNORE |
352                    wimp_IBACKCOL * 3 |
353                    wimp_IFORECOL * 7;
354
355     ict.i.flags |= wimp_ITEXT;
356
357     strcpy(ict.i.data.text, "");
358
359     ict.w = w;
360     wimpt_noerr(wimp_create_icon(&ict, &i));
361
362     #endif /* [mdw] */
363
364     /* Now the icons */
365
366     if (!t->tornoff)
367       tearoff__createTearIcon(t /* was `w' [mdw] */);
368     else
369       tearoff__createTornIcons(t);
370
371   }
372   return w;
373 }
374
375 /*----- Functions to register with dbox -----------------------------------*/
376
377 /* --- Registered with dbox so that it can find out where to open dbox --- */
378
379 static void tearoff__finder(int *x,int *y)
380 {
381   *x=tearoff__x;
382   *y=tearoff__y;
383 }
384
385 /* --- Registered with dbox also, so that it can tell me to
386        open a dbox */
387
388 static void tearoff__openDbox(wimp_openstr *o)
389 {
390   if (tearoff__subMenuPending)
391   {
392     tearoff__currentDbox = o->w;
393     wimp_open_wind(o);
394     if (!tearoff__currentMenu) tearSupport_opened(wimpt_task());
395   }
396 }
397
398 /* --- Registered with dbox so that it knows whether to open the next
399        dbox using above registered function */
400
401 static BOOL tearoff__wasSubmenu(void)
402 {
403   return tearoff__subMenuPending;
404 }
405
406 /*----- The tearoff menu handling routines - Yuk --------------------------*/
407
408 /* --- Used to either highlight or unhighlight a menu item.
409        The current status of the icon is checked and
410        no change is made if is doesn't need to be. */
411
412 void tearoff_highlightItem(int s, tearoff t, BOOL select)
413 {
414   wimp_icon textIcon,keyIcon;
415   tearoff__item *item;
416   wimp_redrawstr r;
417   int more;
418
419   if (!s) return;
420   if (!select && s!=t->selected) return;
421
422   item = (tearoff__item *)(t+1);
423   item+=(s-1);
424
425   if (item->shaded) return;
426   r.w=t->w;
427   r.box.x1=t->width-24;
428   r.box.x0=24;
429   r.box.y0=item->y;
430   r.box.y1=item->y+44;
431
432   textIcon.box.y0=item->y;
433   textIcon.box.y1=item->y+44;
434   textIcon.box.x0 = 24;
435   textIcon.box.x1 = 24 + t->width - 48;
436   textIcon.flags = wimp_ITEXT |
437                    wimp_IVCENTRE |
438                    wimp_IFILLED |
439                    wimp_INDIRECT;
440   if (select)
441     textIcon.flags |= wimp_IBACKCOL * 7 |
442                       wimp_IFORECOL * 0;
443   else
444     textIcon.flags |= wimp_IBACKCOL * 0 |
445                       wimp_IFORECOL * 7;
446
447   if (item->shaded) textIcon.flags |= wimp_INOSELECT;
448   textIcon.data.indirecttext.buffer      = item->text;
449   textIcon.data.indirecttext.validstring = "";
450   textIcon.data.indirecttext.bufflen     = strlen(item->text) + 1;
451
452   if (item->keys)
453   {
454     keyIcon.box.x1=t->width-24;
455     keyIcon.box.x0=keyIcon.box.x1-t->keyWidth;
456     keyIcon.box.y0=textIcon.box.y0;
457     keyIcon.box.y1=textIcon.box.y1;
458     keyIcon.flags=wimp_ITEXT |
459                   wimp_IFILLED |
460                   wimp_INDIRECT |
461                   wimp_IRJUST;
462     if (select)
463       keyIcon.flags |= wimp_IBACKCOL * 7 |
464                     wimp_IFORECOL * 0;
465     else
466       keyIcon.flags |= wimp_IBACKCOL * 0 |
467                     wimp_IFORECOL * 7;
468
469     if (item->shaded) keyIcon.flags |= wimp_INOSELECT;
470     keyIcon.data.indirecttext.buffer=item->keys;
471     keyIcon.data.indirecttext.validstring="";
472     keyIcon.data.indirecttext.bufflen=strlen(item->keys)+1;
473   }
474
475   wimp_update_wind(&r,&more);
476   while(more)
477   {
478     wimp_ploticon(&textIcon);
479     if (item->keys) wimp_ploticon(&keyIcon);
480
481     wimp_get_rectangle(&r,&more);
482   }
483 }
484
485 /* --- Called to deselect the currently highlighted item
486        in a menu. Here, a selected item is one that has
487        been set up by going over a submenu arrow. */
488
489 static void tearoff__deselect(tearoff t)
490 {
491   if (!t) return;
492   tearoff_highlightItem(t->selected, t, FALSE);
493   t->warned   = FALSE;
494   t->selected = 0;
495 }
496
497 /* --- Clean up after pointer has lewft a window etc --- */
498
499 static void tearoff__cleanUp(void)
500 {
501   win_removeIdleClaimer(tearoff__doIdles, tearoff__oldHandle);
502   alarm_remove(tearoff__alarmTime,0);
503   alarm_remove(tearoff__alarmTime2,0);
504   tearoff__alm.item=-1;
505   tearoff__menuActive=TRUE;
506   tearoff__oldHandle = NULL;
507 }
508
509 /* --- Closes the menu passed, and all sub menus off of it.
510        If the menu passed is top of the current transient
511        menu, then TearoffSupport is told to stop giving me
512        button pressed/menu created messages. */
513
514 static void tearoff__closeMenuStructure(tearoff t,tearoff nowOn)
515 {
516   tearoff p;
517
518   /* Close dbox if opened */
519
520   if (tearoff__currentDbox)
521   {
522     wimp_close_wind(tearoff__currentDbox);
523     tearoff__currentDbox=0;
524     if (tearoff__prevLevel)
525     {
526       if (tearoff__prevLevel != nowOn)
527       {
528         tearoff__deselect(tearoff__prevLevel);
529         tearoff__prevLevel->selected = FALSE;
530       }
531       tearoff__prevLevel->warned = FALSE;
532       tearoff__prevLevel->sub = NULL;
533     }
534     if (!tearoff__currentMenu) tearSupport_closed();
535   }
536
537   if (!t)
538     return;
539
540   if (t->prev != NULL)
541   {
542     tearoff__prevLevel = NULL;
543     tearoff__originatingTearoff = NULL;
544     if (t->prev->tornoff)
545     {
546       if (t->prev != nowOn) tearoff__deselect(t->prev);
547       else
548         t->prev->warned   = FALSE;
549     }
550   }
551
552   if (t == tearoff__currentMenu)
553   {
554     tearoff__currentMenu = NULL;
555     tearSupport_closed();  /* Stop TearoffSupport Sending messages */
556   }
557
558   while (t)
559   {
560     p = t;
561
562     if (t->tornoff) return;  /* Dont' close a torn off menu */
563
564     wimpt_noerr(wimp_close_wind(t->w));
565     wimpt_noerr(wimp_delete_wind(t->w));
566     win_register_event_handler(t->w, (win_event_handler)0, 0);
567
568     if (t->warned) p = t->sub; else p = NULL;
569
570     if (tearoff__oldHandle == t)
571       tearoff__cleanUp();
572
573     t->warned = FALSE;
574     t->selected = FALSE;
575     t->prev = FALSE;
576     t->open = FALSE;
577     t->sub = NULL;
578     t->w=0;
579
580     (t->selectProc)(tearoff_CLOSE, NULL, t->userHandle);
581
582     t = p;
583   }
584 }
585
586 /* --- Called to shade a sub menu arrow --- */
587
588 #ifdef notdef  /* [mdw] */
589
590 static void tearoff__shadeSub(tearoff t,int i,BOOL shade)
591 {
592   tearoff__item *item;
593   wimp_redrawstr r;
594   wimp_icon icon;
595   int more;
596
597   if (!t) return;
598
599   item=(tearoff__item *)(t+1);
600   item+=(i-1);
601
602   item->subShaded=shade;
603
604   if (t->open)
605   {
606     r.w=t->w;
607     r.box.x1=icon.box.x1=t->width;
608     r.box.x0=icon.box.x0=t->width-24;
609     r.box.y0=icon.box.y0=item->y;
610     r.box.y1=icon.box.y1=item->y+44;
611
612     icon.flags = wimp_IVCENTRE |
613                  wimp_IHCENTRE |
614                  wimp_IFILLED |
615                  wimp_IBACKCOL * 0 |
616                  wimp_IFORECOL * 7;
617
618     if (item->shaded || item->subShaded) icon.flags |= wimp_INOSELECT;
619
620     if (!riscos3)
621     {
622        icon.flags |= wimp_ITEXT;
623        strcpy(icon.data.text, "\x89");
624     }
625     else
626     {
627       icon.flags |= wimp_ISPRITE |
628                     wimp_ITEXT |
629                     wimp_INDIRECT;
630       icon.data.indirecttext.validstring = "s\x89";
631       icon.data.indirecttext.bufflen     = 1;
632       icon.data.indirecttext.buffer      = "";
633     }
634
635     wimp_update_wind(&r,&more);
636     while(more)
637     {
638       wimp_ploticon(&icon);
639       wimp_get_rectangle(&r,&more);
640     }
641   }
642 }
643
644 #else
645
646 #define tearoff__shadeSub(x,y,z) ((void)0)
647
648 #endif
649
650 /* --- Called to 'tear' off the given menu --- */
651
652 static void tearoff__tearMenu(tearoff t, BOOL close)
653 {
654   wimp_dragstr d;
655
656   if (t->tornoff) return;
657
658   t->tornoff = TRUE;
659
660   tearoff__deselect(t->prev);
661
662   /* Do a window move drag */
663
664   d.window = t->w;
665   d.type = wimp_MOVE_WIND;
666   wimp_drag_box(&d);
667
668   /* Add it to the torn off list */
669
670   t->nextTornoff = tearoff__tornoffList;
671   tearoff__tornoffList = t;
672   if (tearoff__currentMenu==t)
673   {
674     tearoff__currentMenu=NULL;
675     tearSupport_closed();
676   }
677
678   /* Close the current menu structure if dragged with select */
679
680   if (close) tearoff__closeMenuStructure(tearoff__currentMenu,NULL);
681
682   /* Shade the sub menu arrow of the previous menu */
683
684   if (t->prev) tearoff__shadeSub(t->prev,t->fromItem,TRUE);
685
686   /* Now we need to alter the tearoff icons at the top of the menu */
687
688   wimp_delete_icon(t->w, tearIcon__tear);  /* Delete tear icon */
689
690   /* Create a close icon - this should be given the same
691      icon number as the tear icon had */
692
693   tearoff__createTornIcons(t);
694 }
695
696 /* --- Used to 'fold up' the given menu */
697
698 static void tearoff__foldMenu(tearoff t, BOOL close)
699 {
700   wimp_wstate state;
701   wimp_mousestr m;
702   BOOL sameMenu=FALSE;
703
704   if (!t) return;
705
706   if (close) tearoff__closeMenuStructure(tearoff__currentMenu,NULL);
707
708   /* Close any transient submenus opened from this menu */
709
710   if (t->warned)
711   {
712     tearoff__closeMenuStructure(t->sub,NULL);
713     tearoff__deselect(t);
714   }
715
716   /* Re-open the menu at the right size */
717
718   wimp_get_wind_state(t->w, &state);
719   wimp_get_point_info(&m);
720   if (m.w==t->w) sameMenu=TRUE;
721
722   t->folded = !t->folded;
723
724   if (t->folded)
725   {
726     if (t->scrollBar)
727     {
728       wimpt_noerr(wimp_close_wind(t->w));
729       wimpt_noerr(wimp_delete_wind(t->w));
730       win_register_event_handler(t->w, (win_event_handler)0, 0);
731       if (tearoff__oldHandle==t)
732         tearoff__cleanUp();
733       tearoff__createWindow(t);
734       state.o.w=t->w;
735       state.o.box.y0=state.o.box.y1-tearoff__HEIGHT;
736       win_register_event_handler(t->w, tearoff__windowHandler, t);
737       wimp_open_wind(&(state.o));
738       if (sameMenu && m.i>=-1)
739       {
740         win_addIdleClaimer(tearoff__doIdles, 0, t);
741         tearoff__oldHandle = t;
742       }
743     }
744     else
745     {
746       state.o.box.y0=state.o.box.y1-tearoff__HEIGHT;
747       wimp_open_wind(&(state.o));
748     }
749   }
750   else
751   {
752     int scy=(bbc_vduvar(bbc_YWindLimit)+1)<<bbc_vduvar(bbc_YEigFactor);
753     int h=(t->numberOfItems*44)+t->dotted+tearoff__HEIGHT;
754
755     if (t->maxHeight && h>t->maxHeight || (h+50)>scy)
756     {
757       wimpt_noerr(wimp_close_wind(t->w));
758       wimpt_noerr(wimp_delete_wind(t->w));
759       win_register_event_handler(t->w, (win_event_handler)0, 0);
760       if (tearoff__oldHandle==t)
761         tearoff__cleanUp();
762       tearoff__createWindow(t);
763       state.o.w=t->w;
764       if (t->maxHeight)
765         state.o.box.y0=state.o.box.y1-t->maxHeight;
766       else
767         state.o.box.y0=state.o.box.y1-tearoff__HEIGHT-
768                        (t->numberOfItems*44)-t->dotted;
769       win_register_event_handler(t->w, tearoff__windowHandler, t);
770       win_adjustBox(&state.o);
771       wimp_open_wind(&state.o);
772       if (sameMenu && m.i>=-1)
773       {
774         win_addIdleClaimer(tearoff__doIdles, 0, t);
775         tearoff__oldHandle = t;
776       }
777     }
778     else
779     {
780       state.o.box.y0=state.o.box.y1-tearoff__HEIGHT-
781                      (t->numberOfItems*44)-t->dotted;
782       win_adjustBox(&state.o);
783       wimp_open_wind(&(state.o));
784     }
785   }
786 }
787
788 /* --- Used to 'un-tear' the given menu - this will be called
789        if it is re-opened as a tranisent of another menu.
790        It is also called in the routine to close a torn off menu */
791
792 static wimp_w tearoff__unTearMenu(tearoff t, BOOL update)
793 {
794   wimp_redrawstr r;
795   int more;
796   tearoff ptr = tearoff__tornoffList, prev = NULL;
797
798   /* Go through the 'tornoff list', if the menu is found then
799      remove it, untear it, and return it's window handle */
800
801   while (ptr)
802   {
803     if (ptr == t)
804     {
805       /* remove it from list */
806
807       if (!prev) tearoff__tornoffList = t->nextTornoff;
808       else prev->nextTornoff = t->nextTornoff;
809       t->tornoff = FALSE;
810
811       /* Close any transient menu opened from it */
812
813       if (t->warned)
814       {
815         tearoff__closeMenuStructure(t->sub,NULL);
816         tearoff__deselect(t);
817       }
818
819       /* Unshade the sub menu pointer for the previous menu */
820
821       tearoff__shadeSub(t->prev,t->fromItem,FALSE);
822
823       /* Unfold it if it needs to be */
824
825       if (t->folded) tearoff__foldMenu(t, FALSE);
826
827       /* Update if it is being moved - rather than just closed */
828
829       if (update)
830       {
831         wimp_delete_icon(t->w, tearIcon__close);
832         wimp_delete_icon(t->w, tearIcon__fold);
833
834         /* Replace the tear icon */
835
836         tearoff__createTearIcon(t /* was t->w [mdw] */);
837
838         /* Now redraw top of menu */
839
840         r.w      = t->w;
841         r.box.x0 = 0;
842         r.box.x1 = t->width;
843         r.box.y1 = 0;
844         r.box.y0 = -tearoff__HEIGHT;
845
846         wimp_update_wind(&r, &more);
847         while (more)
848         {
849           wimp_get_rectangle(&r, &more);
850         }
851       }
852       return t->w;
853     }
854     prev = ptr;
855     ptr = ptr->nextTornoff;
856   }
857   return (wimp_w)-1;
858 }
859
860 /* --- Close a torn off menu --- */
861
862 static void tearoff__closeTornoff(tearoff t, BOOL close)
863 {
864   if (!t) return;
865
866   tearoff__unTearMenu(t,FALSE);
867   tearoff__closeMenuStructure(t,NULL);
868
869   if (tearoff__oldHandle == t)
870     tearoff__cleanUp();
871   if (close) tearoff__closeMenuStructure(tearoff__currentMenu,NULL);
872
873   /* Let the user know about it being close */
874
875   (t->selectProc)(tearoff_CLOSE,t->selected,t->userHandle);
876 }
877
878 /* --- Called to automatically open a sub menu --- */
879
880 static void tearoff__alarmHandler2(int called_at,void *handle)
881 {
882   tearoff__menuActive=TRUE;
883 }
884
885 static void tearoff__alarmHandler(int called_at,void *handle)
886 {
887   wimp_wstate state;
888   tearoff t=tearoff__alm.t;
889   tearoff__item *item;
890   int delay,dummy;
891
892   item=(tearoff__item *)(t+1);
893   item+=(tearoff__alm.item-1);
894
895   wimp_get_wind_state(t->w, &state);
896   tearoff__x = state.o.box.x1-24;
897   tearoff__y = state.o.box.y1 + tearoff__y;
898
899   tearoff__prevLevel=t;
900   t->warned = TRUE;
901   if (t->tornoff) tearoff__originatingTearoff = t;
902
903   /* No current submenu opened */
904
905   t->sub = NULL;
906
907   /* Set up the second alarm */
908
909   os_swi3r(XOS_Byte,161,23,0,&dummy,&dummy,&delay);
910   if (delay)
911   {
912     tearoff__menuActive=FALSE;
913     tearoff__alarmTime2=alarm_timenow()+delay*10;
914     alarm_set(tearoff__alarmTime2,tearoff__alarmHandler2,0);
915   }
916
917   /* If item->subMenu is not FALSE, it contains a menu to
918      be opened automatically */
919
920   if (item->subMenu)
921   {
922     tearoff__subMenuPending = TRUE;
923     tearoff_displayMenu(item->sub,0);
924     tearoff__subMenuPending = FALSE;
925   }
926
927   /* Does the user want a tearoff_SUBMENU message? Note
928      that he can have one even if the menu is opened
929      automatically - unlike menu.c */
930
931   if (item->subMenuWarning)
932   {
933     tearoff__subMenuPending = TRUE;
934     (t->selectProc)(tearoff_SUBMENU,t->selected,t->userHandle);
935     tearoff__subMenuPending = FALSE;
936   }
937 }
938
939 /* --- The biggy - all button events are returned here for
940        dealing with - alter at your own risk. */
941
942 static void tearoff__buttons(tearoff t,
943                              wimp_mousestr *m)
944 {
945   int itm=0, cnt;
946   BOOL subPointer,justClickShadedArrow=FALSE;
947   wimp_wstate state;
948   tearoff__item *item = (tearoff__item *)(t + 1), *selItem;
949
950   /* First we deal with any type if button event - including the
951      'always' type. Don't bother doing anything if it is folded though */
952
953   if (!tearoff__menuActive) return;
954   selItem=(tearoff__item *)(t+1);
955   if (t->selected) selItem+=(t->selected-1);
956   if (!t->folded)
957   {
958     int cnt,dummy,delay;
959     BOOL wasDbox=!!tearoff__currentDbox;
960
961     itm=0;
962     subPointer=FALSE;
963     tearoff__y=0;
964     wimp_get_wind_state(t->w, &state);
965     m->y = m->y+state.o.y-state.o.box.y1;
966     m->x = m->x+state.o.x-state.o.box.x0;
967     for (cnt = 1; cnt <= t->numberOfItems; cnt++, item++)
968     {
969       if (m->y >= item->y && m->y < item->y+44)
970       {
971         itm = cnt;
972         if (m->x >= t->width - 24) subPointer = TRUE;
973         tearoff__y = item->y+44;
974         break;
975       }
976     }
977
978     item=(tearoff__item *)(t+1);
979     item+=(itm-1);
980
981     if ((subPointer &&
982         (item->subMenu || item->subMenuWarning) &&
983         (t->selected != itm)) ||
984         (!(subPointer &&
985         (item->subMenu || item->subMenuWarning)) &&
986         itm!=tearoff__alm.item) &&
987         m->i<0)
988     {
989       /* We get here if we are over a sub menu arrow AND
990          we weren't over it before, OR we are not over
991          a sub menu arrow at all AND we are not over an
992          icon. */
993
994       /* First close the RISCOS menu using tearoffSupport */
995
996       tearoff_closeMenus();
997
998       /* If this menu is torn off, then close any
999          transient menu opened at the moment */
1000
1001       if (t->tornoff)
1002       tearoff__closeMenuStructure(tearoff__currentMenu, t);
1003
1004       /* If there is already a menu opened from this menu,
1005          then close it. NB This may already have been closed
1006          by the line above - but no harm done. */
1007
1008       if (t->warned)
1009       {
1010         tearoff__closeMenuStructure(t->sub,t);
1011         t->sub = NULL;
1012       }
1013
1014       /* Horrid hack because I can't do co-routines */
1015
1016       if (wasDbox)
1017       {
1018         wimp_eventstr e;
1019
1020         e.e=wimp_ENULL;
1021         wimpt_fake_event(&e);
1022         return;
1023       }
1024
1025       if (t->selected!=itm)
1026       {
1027         if (t->selected && !selItem->shaded || itm == 0)
1028           tearoff_highlightItem(t->selected, t, FALSE);
1029         if (itm) tearoff_highlightItem(itm, t, TRUE);
1030         t->selected=itm;
1031         alarm_remove(tearoff__alarmTime,0);
1032         alarm_remove(tearoff__alarmTime2,0);
1033         tearoff__alm.item=-1;
1034       }
1035       if (tearoff__alm.item==-1)
1036       {
1037         if (riscos3 && itm && (item->subMenu || item->subMenuWarning))
1038         {
1039           os_swi3r(XOS_Byte,161,197,0,&dummy,&dummy,&delay);
1040           if (delay & 0x80)
1041           {
1042             os_swi3r(XOS_Byte,161,23,0,&dummy,&dummy,&delay);
1043             if (delay)
1044             {
1045               tearoff__alm.t=t;
1046               tearoff__alm.item=itm;
1047               tearoff__alarmTime=alarm_timenow()+delay*10;
1048               alarm_set(tearoff__alarmTime,tearoff__alarmHandler,0);
1049             }
1050           }
1051         }
1052       }
1053
1054       /* Correct some variables */
1055
1056       t->warned = FALSE;
1057       tearoff__prevLevel = NULL;
1058       tearoff__originatingTearoff = NULL;
1059     }
1060
1061   /* Did we go over the 'icon bar' ? */
1062
1063     if (m->i>=0)
1064     {
1065       if (t->selected)
1066         tearoff_highlightItem(t->selected, t, FALSE);
1067       t->selected = 0;
1068       if (t->warned) tearoff__closeMenuStructure(t->sub,NULL);
1069       t->warned = FALSE;
1070       tearoff__alm.item=0;
1071       tearoff__prevLevel = NULL;
1072       tearoff__originatingTearoff = NULL;
1073     }
1074
1075     /* We are over an arrow icon */
1076
1077     if (m->i<0 && subPointer &&
1078         (item->subMenu || item->subMenuWarning) /* &&
1079         !item->subShaded ||
1080         (item->subShaded && subPointer && m->bbits) [mdw] */)
1081     {
1082       /* Get position at which to open next menu */
1083
1084       wimp_get_wind_state(m->w, &state);
1085       tearoff__x = state.o.box.x1+2;
1086       tearoff__y = state.o.box.y1 + tearoff__y;
1087
1088       if (!t->warned)  /* This menu isn't opened yet, and/or submenu
1089                           message hasn't been sent */
1090       {
1091         if (item->subShaded && subPointer && m->bbits)
1092           justClickShadedArrow=TRUE;
1093
1094         tearoff__prevLevel=t;
1095         t->warned = TRUE;
1096         if (t->tornoff) tearoff__originatingTearoff = t;
1097
1098         /* No current submenu opened */
1099
1100         t->sub = NULL;
1101
1102         /* If item->subMenu is not FALSE, it contains a menu to
1103            be opened automatically */
1104
1105         if (item->subMenu)
1106         {
1107           tearoff__subMenuPending = TRUE;
1108           tearoff_displayMenu(item->sub,0);
1109           tearoff__subMenuPending = FALSE;
1110         }
1111
1112         /* Does the user want a tearoff_SUBMENU message? Note
1113            that he can have one even if the menu is opened
1114            automatically - unlike menu.c */
1115
1116         if (item->subMenuWarning)
1117         {
1118           tearoff__subMenuPending = TRUE;
1119           (t->selectProc)(tearoff_SUBMENU,t->selected,t->userHandle);
1120           tearoff__subMenuPending = FALSE;
1121         }
1122       }
1123       else
1124       {
1125         /* Since we are here, we know that the menu is already opened,
1126            if there is one that is */
1127
1128         if (t->sub)
1129         {
1130           /* Close any submenus off of the submenu */
1131
1132           if (t->sub->warned)
1133           {
1134             tearoff__closeMenuStructure(t->sub->sub,NULL);
1135             tearoff__deselect(t->sub);
1136           }
1137
1138           /* And reposition submenu */
1139
1140           wimp_get_wind_state(t->sub->w, &state);
1141           state.o.box.x0 = tearoff__x;
1142           state.o.box.x1 = tearoff__x + t->sub->width;
1143           if (t->sub->tearoff) tearoff__y += tearoff__HEIGHT;
1144           state.o.box.y0 = tearoff__y - (state.o.box.y1 - state.o.box.y0);
1145           state.o.box.y1 = tearoff__y;
1146           state.o.behind=-1;
1147
1148           /* Mangle positional data so that it fits on the screen */
1149
1150           win_adjustBox(&(state.o));
1151
1152           /* Top and left to go here */
1153
1154           wimp_open_wind(&state.o);
1155         }
1156       }
1157     }
1158   }
1159
1160   /* Now we need to deal with actual button presses! */
1161
1162   if (m->bbits && !justClickShadedArrow)
1163   {
1164     int dummy;
1165
1166     /* Was click on tearoff bar */
1167
1168     if (m->i>=0)
1169     {
1170       if (m->i == tearIcon__tear && !t->tornoff)
1171         tearoff__tearMenu(t, m->bbits & 6);
1172       else if (m->i == tearIcon__close && t->tornoff)
1173         tearoff__closeTornoff(t, m->bbits & 6);
1174       else if (m->i == tearIcon__fold && t->tornoff)
1175         tearoff__foldMenu(t, m->bbits & 6);
1176     }
1177
1178     /* No, so select item */
1179
1180     else
1181     {
1182       int temp=t->selected;
1183
1184       /* Don't select a shaded item :-)
1185        * (but do close the menu, if we need to [mdw])
1186        */
1187
1188       if (item->shaded)
1189       {
1190         if (m->bbits & 6)
1191           tearoff__closeMenuStructure(tearoff__currentMenu,NULL);
1192         return;
1193       }
1194
1195       /* Flash icon in a similar way to the wimp - cunning */
1196
1197       for (cnt = 1; cnt <= 3; cnt++)
1198       {
1199         os_byte(19,&dummy,&dummy);  /* Wait for Vsync */
1200         os_byte(19,&dummy,&dummy);
1201         t->selected=itm;
1202         tearoff_highlightItem(itm, t, FALSE);
1203         os_byte(19,&dummy,&dummy);
1204         os_byte(19,&dummy,&dummy);
1205         t->selected=-1;
1206         tearoff_highlightItem(itm, t, TRUE);
1207       }
1208       t->selected=temp;
1209
1210       /* Close menu structure if select used */
1211
1212       if (m->bbits & 6)
1213         tearoff__closeMenuStructure(tearoff__currentMenu,NULL);
1214
1215       /* Tell user about click */
1216
1217       (t->selectProc)(tearoff_SELECTION,itm,t->userHandle);
1218     }
1219   }
1220   return;
1221 }
1222
1223 /* --- Called on Null events --- */
1224
1225 static void tearoff__doIdles(void *handle)
1226 {
1227   wimp_mousestr m;
1228   tearoff t = (tearoff)handle;
1229
1230   handle = handle;
1231   wimp_get_point_info(&m);
1232   m.bbits=0;
1233   tearoff__buttons(t, &m);
1234 }
1235
1236 /* --- Routine to program the VDU dash pattern --- */
1237
1238 static void tearoff__makeDashPattern(int ptn)
1239 {
1240   int byte1,byte2;
1241   byte1=242;
1242   byte2=8;
1243   wimpt_noerr(os_byte(163,&byte1,&byte2)); /* Dot-dash length */
1244   bbc_vduq(23,6,ptn,ptn,ptn,ptn,ptn,ptn,ptn,ptn);
1245 }
1246
1247 /* --- Menu redraw routine --- */
1248
1249 void tearoff__doRedraw(tearoff t, wimp_redrawstr *r)
1250 {
1251   tearoff__item *i;
1252   wimp_icon icon;
1253   int c;
1254
1255   i = (tearoff__item *)(t+1);
1256   wimp_setcolour(7);
1257
1258   /* Go through each item, and draw the dotted lines where
1259      appropriate */
1260
1261   for (c = 1; c <= t->numberOfItems; c++)
1262   {
1263     /* Select icon */
1264
1265     icon.box.y1=i->y+44;
1266     icon.box.y0=i->y;
1267
1268     if (i->selType != tearoff_NONE)
1269     {
1270       icon.box.x0=0;
1271       icon.box.x1=24;
1272       icon.flags=wimp_IVCENTRE |
1273                  wimp_IHCENTRE |
1274                  wimp_IFILLED |
1275                  wimp_IBACKCOL * 0 |
1276                  wimp_IFORECOL * 7;
1277       if (i->shaded) icon.flags |= wimp_INOSELECT;
1278       if (!riscos3 && i->selType == tearoff_TICKED)
1279       {
1280         icon.flags |= wimp_ITEXT;
1281         strcpy(icon.data.text, "\x80");
1282       }
1283       if (riscos3 && i->selType == tearoff_TICKED)
1284       {
1285         icon.flags |= wimp_ISPRITE | wimp_INDIRECT;
1286         icon.data.indirectsprite.name = "\x80";
1287         icon.data.indirectsprite.spritearea = (sprite_area *)1;
1288         icon.data.indirectsprite.nameisname = 4;
1289       }
1290       if (i->selType == tearoff_RADIOED)
1291       {
1292         icon.flags |= wimp_ITEXT;
1293         strcpy(icon.data.text, "\x8F");
1294       }
1295
1296       wimpt_noerr(wimp_ploticon(&icon));
1297     }
1298
1299     /* Main text part */
1300
1301     icon.box.x0 = 24;
1302     icon.box.x1 = 24 + t->width - 48;
1303     if (i->keys) icon.box.x1-=16*(strlen(i->keys) + 1);
1304
1305     icon.flags = wimp_ITEXT |
1306                  wimp_IVCENTRE |
1307                  wimp_IFILLED |
1308                  wimp_INDIRECT;
1309
1310     if (t->selected == c && !i->shaded)
1311       icon.flags |= wimp_IBACKCOL * 7 |
1312                     wimp_IFORECOL * 0;
1313     else
1314       icon.flags |= wimp_IBACKCOL * 0 |
1315                     wimp_IFORECOL * 7;
1316
1317     if (i->shaded) icon.flags |= wimp_INOSELECT;
1318
1319     icon.data.indirecttext.buffer      = i->text;
1320     icon.data.indirecttext.validstring = "";
1321     icon.data.indirecttext.bufflen     = strlen(i->text) + 1;
1322     wimpt_noerr(wimp_ploticon(&icon));
1323
1324     if (i->keys)
1325     {
1326       /* Print the control key short cut - right justified of course! */
1327
1328       icon.box.x0=t->width-24-t->keyWidth;
1329       icon.box.x1=t->width-24;
1330       icon.flags=wimp_ITEXT |
1331                  wimp_IFILLED |
1332                  wimp_INDIRECT |
1333                  wimp_IRJUST;
1334       if (t->selected==c && !i->shaded)
1335         icon.flags |= wimp_IBACKCOL * 7 |
1336                       wimp_IFORECOL * 0;
1337       else
1338         icon.flags |= wimp_IBACKCOL * 0 |
1339                       wimp_IFORECOL * 7;
1340       if (i->shaded) icon.flags |= wimp_INOSELECT;
1341       icon.data.indirecttext.buffer=i->keys;
1342       icon.data.indirecttext.validstring="";
1343       icon.data.indirecttext.bufflen=strlen(i->keys)+1;
1344       wimpt_noerr(wimp_ploticon(&icon));
1345     }
1346
1347     /* Sub Arrow */
1348
1349     if (i->subMenu || i->subMenuWarning)
1350     {
1351       icon.box.x0 = t->width - 24;
1352       icon.box.x1 = t->width;
1353
1354       icon.flags = wimp_IVCENTRE |
1355                    wimp_IHCENTRE |
1356                    wimp_IFILLED |
1357                    wimp_IBACKCOL * 0 |
1358                    wimp_IFORECOL * 7;
1359
1360       if (i->shaded /* || i->subShaded [mdw] */)
1361         icon.flags |= wimp_INOSELECT;
1362
1363       if (!riscos3)
1364       {
1365         icon.flags |= wimp_ITEXT;
1366         strcpy(icon.data.text, "\x89");
1367       }
1368       else
1369       {
1370         icon.flags |= wimp_ISPRITE |
1371                       wimp_ITEXT |
1372                       wimp_INDIRECT;
1373
1374         icon.data.indirecttext.validstring = "s\x89";
1375         icon.data.indirecttext.bufflen     = 1;
1376         icon.data.indirecttext.buffer      = "";
1377       }
1378       wimpt_noerr(wimp_ploticon(&icon));
1379     }
1380
1381     if (i->dotted)
1382     {
1383       bbc_move(r->box.x0,r->box.y1+i->y-12-r->scy);
1384
1385       /* Plot the dotted line */
1386
1387       tearoff__makeDashPattern(0xf0);
1388       wimp_setcolour(7);
1389       bbc_plot(bbc_DottedBoth+bbc_DrawRelFore,t->width,0);
1390     }
1391     i++;
1392   }
1393 }
1394
1395 /* --- Perform the redraw loop --- */
1396
1397 static void tearoff__redrawMenu(tearoff t)
1398 {
1399   wimp_redrawstr r;
1400   int more;
1401
1402   r.w = t->w;
1403   wimp_redraw_wind(&r, &more);
1404   while (more)
1405   {
1406     tearoff__doRedraw(t, &r);
1407     wimp_get_rectangle(&r, &more);
1408   }
1409 }
1410
1411 /* --- Called to re-open the window - used when the window flags
1412        have changed, and for things such as titles changing */
1413
1414 void tearoff_rebuildMenu(tearoff t)
1415 {
1416   wimp_wstate state;
1417   BOOL hadIdle=FALSE;
1418
1419   wimp_get_wind_state(t->w, &state);
1420
1421   if (tearoff__oldHandle==t)
1422   {
1423     tearoff__cleanUp();
1424     hadIdle=TRUE;
1425   }
1426   wimpt_noerr(wimp_close_wind(t->w));
1427   wimpt_noerr(wimp_delete_wind(t->w));
1428   win_register_event_handler(t->w, (win_event_handler)0, 0);
1429   tearoff__createWindow(t);
1430   state.o.w=t->w;
1431   state.o.box.x1=state.o.box.x0+t->width;
1432   if (t->maxHeight)
1433     state.o.box.y0=state.o.box.y1-t->maxHeight;
1434   else
1435     state.o.box.y0=state.o.box.y1-tearoff__HEIGHT-
1436                    (t->numberOfItems*44)-t->dotted;
1437   wimp_open_wind(&(state.o));
1438   win_register_event_handler(t->w, tearoff__windowHandler, t);
1439   if (hadIdle)
1440   {
1441     win_addIdleClaimer(tearoff__doIdles,0,t);
1442     tearoff__oldHandle=t;
1443   }
1444 }
1445
1446 /* --- Adds/Removes a scrollbar from a menu --- */
1447
1448 static void tearoff__checkForScrollBar(tearoff t)
1449 {
1450   int h;
1451   wimp_wstate state;
1452   int scy=(bbc_vduvar(bbc_YWindLimit)+1)<<bbc_vduvar(bbc_YEigFactor);
1453
1454   if (t->folded) return;  /* Don't do anything to a folded menu */
1455   h = t->numberOfItems * 44 + t->dotted;
1456   if (t->tearoff) h += tearoff__HEIGHT;
1457   h += 50;  /* Leave room for title bar */
1458
1459   if (t->scrollBar && h <= scy &&
1460       (!t->maxHeight || (t->maxHeight && h < t->maxHeight)))
1461   {
1462     /* remove scroll bar */
1463
1464     wimp_get_wind_state(t->w, &state);
1465     wimpt_noerr(wimp_close_wind(t->w));
1466     wimpt_noerr(wimp_delete_wind(t->w));
1467     win_register_event_handler(t->w, (win_event_handler)0, 0);
1468     tearoff__createWindow(t);
1469     state.o.w=t->w;
1470     state.o.box.y1=
1471       state.o.box.y0+(t->numberOfItems*44+t->dotted)+
1472       ((t->tearoff)?tearoff__HEIGHT:0);
1473     win_register_event_handler(t->w, tearoff__windowHandler, t);
1474     wimp_open_wind(&(state.o));
1475     return;
1476   }
1477   if ((!t->scrollBar && h > scy) || wimpt_justChangedMode())
1478   {
1479     /* Add scroll bar */
1480
1481     wimp_get_wind_state(t->w, &state);
1482     wimpt_noerr(wimp_close_wind(t->w));
1483     wimpt_noerr(wimp_delete_wind(t->w));
1484     win_register_event_handler(t->w, (win_event_handler)0, 0);
1485     tearoff__createWindow(t);
1486     state.o.w=t->w;
1487     state.o.box.x1=state.o.box.x0+t->width;
1488     wimp_open_wind(&(state.o));
1489     win_register_event_handler(t->w, tearoff__windowHandler, t);
1490     return;
1491   }
1492 }
1493
1494 /* --- Window handler for my psuedo-menus --- */
1495
1496 static void tearoff__windowHandler(wimp_eventstr *e, void *handle)
1497 {
1498   tearoff t = (tearoff)handle;
1499   wimp_mousestr m;
1500   BOOL ptrOverThisWindow=FALSE;
1501
1502   switch (e->e)
1503     {
1504     case wimp_EOPEN:
1505       if (wimpt_justChangedMode())
1506       {
1507         wimp_get_point_info(&m);
1508         if (m.w==t->w) ptrOverThisWindow=TRUE;
1509         tearoff__checkForScrollBar(t);
1510         if (ptrOverThisWindow && m.i>=-1 && !tearoff__oldHandle)
1511         {
1512           win_addIdleClaimer(tearoff__doIdles, 0, t);
1513           tearoff__oldHandle = t;
1514         }
1515       }
1516       else
1517         wimpt_noerr(wimp_open_wind(&e->data.o));
1518       break;
1519
1520     case wimp_EREDRAW:
1521       tearoff__redrawMenu(t);
1522       break;
1523
1524     case wimp_EBUT:
1525       tearoff__buttons(t,&e->data.but.m);
1526       break;
1527
1528     case wimp_EPTRENTER:
1529       if (tearoff__oldHandle)
1530         tearoff__cleanUp();
1531       win_addIdleClaimer(tearoff__doIdles, 0, t);
1532       tearoff__oldHandle = t;
1533       break;
1534
1535     case wimp_EPTRLEAVE:
1536       if (tearoff__oldHandle)
1537         tearoff__cleanUp();
1538       if (!t->warned && t->selected)
1539       {
1540         tearoff_highlightItem(t->selected, t, FALSE);
1541         t->selected=0;
1542       }
1543       break;
1544
1545     case wimp_ESEND:
1546     case wimp_ESENDWANTACK:
1547       switch (e->data.msg.hdr.action)
1548       {
1549         case wimp_MHELPREQUEST:
1550         {
1551           wimp_wstate state;
1552           int x,y,cnt;
1553           tearoff__item *item;
1554
1555           if (e->data.msg.data.helprequest.m.i>=0)
1556           {
1557             help_startHelp();
1558             if (e->data.msg.data.helprequest.m.i==1 && !t->tornoff)
1559               help_addLine("Drag to tear off this submenu");
1560             if (e->data.msg.data.helprequest.m.i==1 && t->tornoff)
1561               help_addLine("Click to close this menu");
1562             if (e->data.msg.data.helprequest.m.i==2)
1563             {
1564               if (t->folded)
1565                 help_addLine("Click to un-fold this menu");
1566               else
1567                 help_addLine("Click to fold this menu");
1568             }
1569             help_endHelp();
1570             return;
1571           }
1572           wimp_get_wind_state(t->w,&state);
1573           x=e->data.msg.data.helprequest.m.x+state.o.x-state.o.box.x0;
1574           y=e->data.msg.data.helprequest.m.y+state.o.y-state.o.box.y1;
1575           item=(tearoff__item *)(t+1);
1576           for (cnt = 1; cnt <= t->numberOfItems; cnt++, item++)
1577           {
1578             if (y >= item->y && y < item->y+44)
1579             {
1580               (t->selectProc)(tearoff_HELP,cnt,t->userHandle);
1581               return;
1582             }
1583           }
1584         }
1585       }
1586
1587     default:
1588       break;
1589     }
1590 }
1591
1592 /* --- Called to create a menu or a sub menu --- */
1593
1594 BOOL tearoff_displayMenu(tearoff t1,void *handle)
1595 {
1596   wimp_w w;
1597   wimp_mousestr m;
1598   wimp_wstate state;
1599   int height;
1600
1601   wimp_get_point_info(&m);
1602
1603   /* If the window is not already torn off, then create
1604      the window for it */
1605
1606   if (w = tearoff__unTearMenu(t1, TRUE), w == -1)
1607   {
1608     if (!t1->open)
1609     {
1610       w = tearoff__createWindow(t1);
1611       if (w == -1) return FALSE;
1612       win_register_event_handler(w, tearoff__windowHandler, t1);
1613     }
1614     else
1615       w=t1->w;
1616   }
1617
1618   if (handle) t1->userHandle=handle;
1619
1620   wimp_get_wind_state(w, &state);
1621   height = state.o.box.y1 - state.o.box.y0;
1622   state.o.behind=-1;
1623
1624   if (t1->tearoff) tearoff__y += tearoff__HEIGHT;
1625
1626   if (!tearoff__subMenuPending || !tearoff__currentMenu)
1627   {
1628     /* Here it must be a new menu, not a sub menu of a transient
1629        menu */
1630
1631     tearoff__closeMenuStructure(tearoff__currentMenu,NULL);
1632     t1->prev = NULL;
1633     if (tearoff__originatingTearoff && tearoff__subMenuPending)
1634     {
1635       /* It is being opened from a torn off menu */
1636
1637       t1->prev = tearoff__originatingTearoff;
1638       state.o.box.x0 = tearoff__x;
1639       state.o.box.x1 = tearoff__x + t1->width;
1640       state.o.box.y0 = tearoff__y - height;
1641       state.o.box.y1 = tearoff__y;
1642       tearoff__originatingTearoff->sub=t1;
1643       t1->fromItem=t1->prev->selected;
1644     }
1645     else
1646     {
1647       /* Open over the pointer */
1648
1649       state.o.box.x0 = m.x-64;
1650       state.o.box.x1 = state.o.box.x0 + t1->width;
1651       state.o.box.y0 = m.y - height;
1652       state.o.box.y1 = m.y;
1653     }
1654     tearoff__currentMenu = t1;
1655
1656     /* Tell TearoffSupport to send me messages */
1657
1658     tearSupport_opened(wimpt_task());
1659   }
1660   else
1661   {
1662     /* It is a sub menu of a transient menu */
1663
1664     t1->prev = tearoff__prevLevel;
1665     state.o.box.x0 = tearoff__x;
1666     state.o.box.x1 = tearoff__x + t1->width;
1667     state.o.box.y0 = tearoff__y - height;
1668     state.o.box.y1 = tearoff__y;
1669     tearoff__prevLevel->sub=t1;
1670     t1->fromItem=t1->prev->selected;
1671   }
1672   t1->open=TRUE;
1673   win_adjustBox(&(state.o));
1674   wimp_open_wind(&(state.o));
1675   return TRUE;
1676 }
1677
1678 void tearoff_displayAt(tearoff t,void *handle,int x,int y)
1679 {
1680   wimp_w w;
1681   wimp_wstate state;
1682   int height;
1683
1684   /* If the window is not already torn off, then create
1685      the window for it */
1686
1687   if (w = tearoff__unTearMenu(t, TRUE), w == -1)
1688   {
1689     if (!t->w)
1690     {
1691       w = tearoff__createWindow(t);
1692       if (w == -1) return;
1693       win_register_event_handler(w, tearoff__windowHandler, t);
1694     }
1695     else
1696       w=t->w;
1697   }
1698
1699   if (handle) t->userHandle=handle;
1700
1701   tearoff__closeMenuStructure(tearoff__currentMenu,NULL);
1702   t->prev = NULL;
1703   wimp_get_wind_state(w, &state);
1704   height = state.o.box.y1 - state.o.box.y0;
1705   state.o.box.x0 = x;
1706   state.o.box.x1 = x + t->width;
1707   state.o.box.y0 = y - height;
1708   state.o.box.y1 = y;
1709   tearoff__currentMenu = t;
1710   tearSupport_opened(wimpt_task());
1711   t->open=TRUE;
1712   win_adjustBox(&(state.o));
1713   wimp_open_wind(&(state.o));
1714 }
1715
1716 int tearoff_height(tearoff t)
1717 {
1718   int height=0;
1719
1720   height+=t->numberOfItems*44+t->dotted;
1721   if (t->tearoff) height+=tearoff__HEIGHT;
1722   return height;
1723 }
1724
1725 /* --- Used to receive messages from TearoffSupport */
1726
1727 static BOOL unknown_event(wimp_eventstr *e, void *handle)
1728 {
1729   win_event_handler eh = 0;
1730
1731   handle = handle;
1732
1733   switch(e->e)
1734   {
1735     case wimp_ESEND:
1736     case wimp_ESENDWANTACK:
1737     switch (e->data.msg.hdr.action)
1738     {
1739       case wimp_MMODECHANGE:
1740         if (tearoff__oldHandle)
1741           tearoff__cleanUp();
1742         return FALSE;
1743
1744       case 0x4a340:
1745
1746         /* Button pressed message - close any transient menus
1747            iff button click was not in one of my menus */
1748
1749         if (e->data.msg.data.helprequest.m.w == tearoff__currentDbox)
1750           return TRUE;
1751         win_read_eventhandler(e->data.msg.data.helprequest.m.w,
1752                               &eh,
1753                               &handle);
1754         if (eh != tearoff__windowHandler)
1755           tearoff__closeMenuStructure(tearoff__currentMenu,NULL);
1756         return TRUE;
1757
1758       case 0x4a341:
1759
1760         /* A close menu message - close menus regardless.
1761            eg. escape pressed or wimp menu opened */
1762
1763         tearoff__closeMenuStructure(tearoff__currentMenu,NULL);
1764
1765         return TRUE;
1766     }
1767   }
1768 return FALSE;
1769 }
1770
1771 /* --- Some Functions the give functionality to user of library */
1772
1773 void tearoff_closeMenu(tearoff t)
1774 {
1775   if (t->open)
1776   {
1777     if (!t->tornoff)
1778       tearoff__closeMenuStructure(t,NULL);
1779     else
1780       tearoff__closeTornoff(t,FALSE);
1781   }
1782 }
1783
1784 static void tearoff__defaultmidb(wimp_w w,
1785                           int made,
1786                           void *handlea,
1787                           void *handleb,
1788                           void *handlec)
1789 {
1790   if (w==win_ICONBAR)
1791   {
1792     wimp_mousestr m;
1793
1794     wimp_get_point_info(&m);
1795     tearoff_displayAt((tearoff)handlea,
1796                       0,
1797                       m.x-64,
1798                       96+tearoff_height((tearoff)handlea));
1799   }
1800   else
1801     tearoff_displayMenu((tearoff)handlea,0);
1802 }
1803
1804 BOOL tearoff_attachMenu(wimp_w w,tearoff t,void *handle)
1805 {
1806   if (handle) t->userHandle=handle;
1807   return (event_attachMidbHandler(w,
1808                                   tearoff__defaultmidb,
1809                                   TRUE,
1810                                   (void *)t,
1811                                   NULL,
1812                                   NULL
1813                                   ));
1814 }
1815
1816 void tearoff_init(void)
1817 {
1818   static BOOL initialised=FALSE;
1819
1820   if (initialised) return;
1821   tearSupport_init();
1822   riscos3=(wimpt_getVersion()>=300);
1823   win_add_unknown_event_processor(unknown_event, 0);
1824   dbox__rms(tearoff__finder,tearoff__wasSubmenu,tearoff__openDbox);
1825   alarm_init();
1826   tearoff__alm.item=-1;
1827   initialised=TRUE;
1828 }