Support Group Application Note
Number: 241
Issue: 1.00
Author: SH/JB
Applicable Hardware: All Acorn RISC OS based computers
Related Application Notes: The RISC OS Selection Model and
Clipboard
Copyright © Acorn Computers Limited 1992
Every effort has been made to ensure that the information in this leaflet is true and correct at the time of printing. However, the products described in this leaflet are subject to continuous development and improvements and Acorn Computers Limited reserves the right to change its specifications at any time. Acorn Computers Limited cannot accept liability for any loss or damage arising from the use of any information or particulars in this leaflet. ACORN, ECONET and ARCHIMEDES are trademarks of Acorn Computers Limited.
The goal of this protocol is to allow the user to copy and move data, both within and between applications, by direct manipulation. This means that the user should be able to make a selection in a document, pick the selected objects up with the mouse, drag them to their destination and release the mouse button.
To facilitate precise positioning of the dragged objects, provision is made for the receiving task to display a ghost caret, which tracks the mouse pointer in the target window and indicates exactly where the data will be inserted. For text, the ghost caret should look similar to the normal Wimp caret, and as its follows the mouse pointer it should "snap" to the nearest inter-character gap. The appearance of a ghost caret for non-textual selections will vary, but might typically be the bounding box of the dragged data, scaled according to the destination window's zoom factors.
This document assumes that you have implemented the selection system described in the application note The RISC OS Selection Model and Clipboard.
The following mouse controls exist on a text area. Non-textual documents will work similarly, but the details of the selection model and visual feedback may differ according to the nature of the data being manipulated.
Clicking Select where there is no selection sets the caret position
Clicking Adjust when there is a caret creates a selection between the old caret position and the mouse position.
Dragging with Select where there is no selection starts a new selection, and subsequent dragging moves the other end.
Dragging with Adjust alters the 'nearest' end of the selection.
Clicking Select over the selection has no effect (to allow the drag to occur properly - see below).
Dragging with Select over the selection causes a box the size of the selection to be dragged. While the box is being dragged over a potential destination, a ghost caret may be displayed by the application that owns the destination window.
Releasing Select at the destination causes the selection to be copied or moved to the destination. If the destination is within the same window, then the data should be moved, unless SHIFT is held down, when the data should be copied instead. However, if the destination is a different window, the sense of the SHIFT key should be reversed so that the default action is a copy.
While the selection is being dragged, the following message protocol is used to implement automatic scrolling and 'ghost caret' positioning:
Message_Dragging (17) | |
0 | message size |
4 | task handle of task performing the drag |
8 | message id |
12 | your_ref (0) |
16 | Message_Dragging |
20 | window handle |
24 | icon handle |
28 | x |
32 | y |
36 | flags: |
bit 1 set => sending data from selection | |
bit 2 set => sending data from clipboard | |
bit 3 set => source data will be deleted | |
bit 4 set => do NOT claim this message | |
all other bits reserved (must be 0) | |
40 | x0, y0, x1, y1: bounding box of data relative to the pointer, |
in 1/72000" (not scaled according to the zoom factors of the source window) | |
x0 > x1 => source data size unknown | |
56 | list of filetypes in sender's order of preference, terminated by -1 |
Message_DragClaim (18) | |
0 | message size |
4 |
task handle of task replying to Message_Dragging
|
8 | message id |
12 |
your_ref (message id of the Message_Dragging
received)
|
16 |
Message_DragClaim
|
20 | flags: |
bit 0 set => pointer shape has been changed | |
bit 1 set => remove wimp dragbox | |
bit 3 set => source data should be deleted | |
all other bits reserved (must be 0) | |
24 | list of filetypes in receiver's order of preference, terminated by -1 |
All messages are sent using type 18 (reply required), and the 'your_ref' field of each message is set to the message id of that to which it is replying, unless otherwise stated.
During the drag, the dragging task enables
null events using Wimp_PollIdle
with a minimum
return time of about 0.25 second. On each null event, it
finds the current mouse position using
Wimp_GetPointerInfo
. It then constructs the message
block for
Message_Dragging
using the information gleaned.
If no task has claimed control of the pointer (as none has at
the start), the message is sent to the owner of the
window/icon pair returned by
Wimp_GetPointerInfo
.
If the dragging task receives a Message_DragClaim
in reply,
it stores the task / your_ref / flags of the message to
indicate that the given task has claimed the drag process.
On the next null event, if the drag has
been claimed, the Message_Dragging
is sent to
the claimant (with your_ref set as appropriate), otherwise it
is sent to the window/icon pair returned by
Wimp_GetPointerInfo
as before.
If the
Message_Dragging
bounces (note that a
DragClaim
reply is required for each
Message_Dragging
that is sent), and the drag was being
claimed, the record of the claimant is reset and the
Message_Dragging
is resent, this time to the
window/icon pair (with a your_ref of 0).
The claimant will normally only claim the drag again if the
pointer is still within its window, but it may continue if it
has started auto-scrolling. If
bit 4 of the
flags in the
Message_Dragging
is set, the claimant MUST
relinquish the drag, since this indicates that the sender is
terminating the drag process.
When the drag terminates,
the dragging task sets a flag and calls the null event code
once more (to ensure that the mouse position is
up-to-date). This results in a Message_Dragging
being sent,
either to the claimant or to the window/icon pair under the
pointer.
This will result in a
DragClaim
being returned, or the message bouncing.
If the message bounces, and there was a claimant, the
Message_Dragging
is
resent, as for a null event.
However, if a DragClaim
is received, or the
Message_Dragging
bounces and there is no
claimant, then the 'drag terminated' flag is inspected, and
if set, the data can finally be saved to the destination.
The sender is also responsible for detecting the user
aborting the drag using Escape - in this case it should set
an internal 'Aborted' flag, call Wimp_DragBox
with -1 to terminate the drag, and then proceed as for drag finished,
terminating the process just before the DataSave
would have been sent.
The sender should keep a record of whether the pointer shape
has been changed (ie. keep a copy of the last set of DragClaim
flags
received), and reset the pointer shape whenever the 'pointer
altered' bit changes from a 1 to a 0. Thus if the other application first changes
the pointer shape, sending a DragClaim
with
flags bit 0 set, then next time does not wish to change the
pointer shape, but nevertheless wants to keep the drag, it
will send a DragClaim
with flags bit 0 clear, so
the sender should reset the pointer shape.
The 'remove wimp dragbox' flag
works slightly differently - since the receiver is not
allowed to alter the wimp dragbox, it should instead ask the
sender to do this by setting bit 1 of the DragClaim
message. If at some
later time a DragClaim
is sent without this bit
set, or the
Message_Dragging
bounces, the sender should
restore the wimp dragbox. It should do this by calling
Wimp_DragBox
again with the appropriate drag type.
Note that sometimes the sender does
not know the bounding box of the data, or sometimes the
concept may be meaningless (e.g. for text transfer). In
this case the bounding box in the Message_Dragging
should be set
so that x0 > x1, and the receiver should check for this
and not use a box to display the position of the data
(although it may still use a ghost caret if appropriate). The
Wimp drag box should be used if the receiver has no way to
display the destination position.
Bit 3 of the DragClaim
message can be set
to indicate that the sender should delete the source data
even if the user did not press the Shift
key. This option could be used by a trashcan application,
so that objects dragged to it are simply deleted.
When the sender finally comes to save the data, it should
send in its own preferred format, unless a
DragClaim
is in force and the sender can do one of the
the filetypes in the list returned. If so, it must do the
first one in the list that it can.
Before sending the data, the sender should now reset the
pointer shape to the default if the claimant's last
DragClaim
had the 'pointer
shape altered' flag set.
The normal Message_DataSave
protocol is used,
except that the your_ref of the Message_DataSave
is set to 0 if there was no claimant, and to the stored
claimant's last DragClaim
message id otherwise.
Thus the receiver of the Message_DataSave
knows
whether to alter the insertion point according to the x,y
position indicated, and whether the 'ghost caret' needs to be removed from
view.
In summary, the two tasks should behave as follows:
Start:
call Wimp_DragBox to start the drag, and enable null events every 0.25s set claimant to 'none' set drag_finished to 'false' set drag_aborted to 'false' set lastref to 'none'
read mouse position and construct Message_Dragging if claimant send message (18) to claimant (your_ref = lastref) else send message (17/18) to window/icon pair (your_ref = 0) endif
Escape pressed:
set drag_aborted to 'true' call Wimp_DragBox with -1 to terminate the drag proceed as for drag finished
Drag finished:
set drag_finished to 'true', then call null event code
Message_DragClaim received:
if drag_finished reset pointer shape if claimant altered it unless drag_aborted send data to claimant (your_ref = my_ref of DragClaim) else reset pointer shape if (old_flags and not new_flags) has bit 0 set set claimant to task handle of DragClaim message set lastref to my_ref of DragClaim message endif
Message_Dragging bounced:
if claimant is not 'none' reset pointer shape if claimant altered it set claimant to 'none' resend message to window/icon pair (type 17/18) else if drag_finished unless drag_aborted send data to window/icon pair (your_ref = 0) endif endif
'17/18' means "if drag_finished, send as type 18, else as
type 17". Bit 4 of any
Message_Dragging
sent is set if drag_aborted is
true.
Start:
set claiming to 'false' set pointer_altered to 'false'
Message_Dragging received:
if claiming, if flags bit 4 clear, and we're auto-scrolling or in a text area reply with Message_DragClaim (type 17) perform auto-scroll if required update ghost caret if required else set claiming to 'false' undraw ghost caret if necessary don't reply to message endif else if flags bit 4 clear if we're in the auto-scroll area change pointer shape to indicate auto-scroll set timer for auto-scroll set pointer_altered to 'true' reply with Message_DragClaim (type 17) elseif we're in a text area draw ghost caret in correct place set pointer_altered to 'false' reply with Message_DragClaim (type 17) endif endif endif
Message_DataSave received:
if your_ref indicates we're claiming: if ghost caret is set up set insertion point to ghost caret undraw ghost caret endif endif import data to insertion point
All
Message_DragClaim
messages are sent with bit 0 of
the flags indicating the current value of the
'pointer_altered' flag.