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 }