Tearoff Menu System
~~~~~~~~~~~~~~~~~~~

A while ago, as Tim was leaving after a hacking session, I mentioned how
nice it would be if only RISC OS allowed menus to be tearoffable, like
OpenLook menus are.  He pointed out that impossibility shouldn't be much
of a barrier to us, and we got coding.

Tearoff menus look just like normal menus, except there's a grey bar
along the top, just below the title, with a `Tear' button in it.  When
the user clicks the `Tear' button, the rest of the menu tree goes away,
leaving the tornoff menu behind, so that options can be chosen from it
whenever the user needs to.  Tornoff menus have two buttons in their
grey bar area: `Close' closes the menu, and `Fold' hides the menu part
away, leaving just the title behind, a bit like Corel's rollups (only
without the irritating animation).

The main support code was written as part of STEEL, in C.  It handles
the obvious (but quite difficult) stuff related to highlighting the
right items, opening the right submenus, and drawing the windows, and
Tim wrote it all.  (He later wrote the corresponding Sapphire code --
the user- and programmer-visible bits of our tearoff support are
entirely written by him.)

There's one aspect of tearoff handling which can't be handled from an
application: making the main menu tree `transient'.  There are three
times when the Wimp closes a transient window `silently':

  * when the user clicks outside of the menu tree;
  * when the user presses the `Escape' key; and
  * when an application calls Wimp_CreateMenu.

In the first case, the mouse click is passed on to the receiving window;
in the second, the keypress is swallowed by the Wimp.

These three cases are handled by some vector interceptions.  Normally
this would be done by a module.  Tim wanted the vector traps to be hard
to extract, though, and a small module would be easy for other
programmers to reverse engineer and implement their own tearoffs.  The
`tearoff support code' is statically linked into the application which
uses it, and copied into an RMA block if it's not there already.

The interface to the TearSupt code is simple:

  * ts_init initialises the support code.

  * ts_opened says that the application has opened a transient window,
    and wants to be notified about events which might cause it to be
    closed.

  * ts_closed says that the transient window has been closed.

  * ts_switch switches Wimp_CreateMenu trapping on and off (an
    implemention detail requires this).

  * ts_unload removes the tearoff support code from memory.

Tearoff Support traps several vectors.  One is trapped all the time:

  * ChangeEnvironmentV is trapped, as part of a particularly hacky way
    of communicating with the TearSupt code.  It traps a call with R0 =
    -1 and returns its address and version number.  This is very nasty.

Others are only trapped while there's a `fake transient' open, and cause
Wimp messages to be sent to the owner of the window:

  * MouseV is trapped: all mouse clicks are picked up, and passed on as
    messages.

  * InsV is trapped: insertion of an `escape' keypress into the keyboard
    buffer causes a message to be sent.

  * The hardware SWI vector is trapped: a call to Wimp_CreateMenu causes
    a message to be sent.

Just to make life more awkward, the code is encrypted (very weakly: it's
not worth the effort and code space of doing it properly).  I considered
removing the encryption code, but (a) I preferred to leave everything
the way it was (more or less) as a record of the nasty things Straylight
sometimes do, and (b) I couldn't be bothered.

Anyway, all the code is really nasty.  I'd recommend that you avoid
reading it too closely.  Oh, the SWI patch may be instructive to those
who've not seen the job done before: I think it works properly in all
circumstances.

Of course, anyone's allowed to use tearoff menus.  You're even allowed
to use our code to implement them, as long as you stick to the GPL.  And
finally, if someone comes up with a SWI chunk, I may be convinced to
permit binary distribution of a tearoff support module using our code.
--
[mdw]
