Connecting objects
~~~~~~~~~~~~~~~~~~

One of the nice features of DIA (and therefore DiaCanvas) is the ability to
connect one object to another object. This is done by means of `handles' and
`connection points'. Handles are small rectangles that allow the user to
(for example) resize an object or move the point of a line.

The notion of connection points has been abstracted to allow for other
kinds of connections (e.g. by using linear constraints).

There are several ways to make handles work. One is to put the handle in
charge and let the handle notify objects whenever needed. The other one is
when the handle has become a passive element and the objects handle all the
events. The last one is the scheme I picked, basically because the handles
are already moved around by the DiaHandleLayer. Creating a GnomeCanvas object
for every handle would mean a massive increase of memory usage (one object
might have more than 10 handles for example).

So handles are passive objects as far as canvas interaction is conserned.
You might see it as an object that acts as a bridge between two canvas items.
The DiaHandleLayer takes care of selecting handles, moving handles and looking
for objects the handle could connect to.

If a handle is moved the owners handle_motion() event is triggered.

We can distinguish these actions:
1. A handle is moved by the mouse and is not connected
2. A handle is moved by the mouse and is connected
3. A handle is connected and moved, as a result it is to be connected to
   another canvas item or no object at all
4. The handle's owner is moved
5. The canvas item the handle is connected to is moved.
6. A group of objects is moved

Case 1. is the most simple case: do nothing.

2. Connecting handles
~~~~~~~~~~~~~~~~~~~~~
When a handle comes close to another canvas item the following things happen:
1. The user grabs a handle by pressing the mouse button and starts moving
   the handle.
2. All canvas item closer than "glue_distance" (in pixels) are asked if they
   want to glue() the handle.
3. The handle is located at the glue point closest to the handles position.
4. Step 2. and 3. are repeated as long as the user moves the handle around.
5. If the handle is glue()-ed and the user releases the mouse button the
   handle is connect()-ed to the canvas item and a signal is emited to notify
   the rest of the world about this cheerful event.

This way of handling handles ;-) has a few nice features:
- Objects can refuse connections from certain types of canvas items.
- Both Visio-like connection (with connection points) and Rose-like
  connections (connection along a line) can be implemented.
- The data is only notified if the connection has become established.

Implementation points of attention:
- This means that all canvas items are queried that are have a bounding
  box within "glue_distance" pixels from the handle.
- The handle's position should be kept relative to the canvas item.

If a handle is moved several things should happen in a specific order:
1. Ask the owning canvas item if the movement is allowed at all
2. Find a connection point nearby the handle
3. If no CP is found, snap to the grid (if enabled)
4. Ask the owner again, you can't be too sure.
5. Move the handle.

Between those steps checks are done to see if the movement is (0, 0). If there
is no movement, the sequence is terminated.


3. Disconnecting
~~~~~~~~~~~~~~~~
Disconnecting only happens under the following circumstances:
1. A handle is to be connected to another canvas item
2. A handle is no longer connected to any canvas item at all

This means that if a handle is to be connected to the same canvas item it was
connected before the move action, no disconnect() signal is send. A connect()
is send, however, to notify the canvas item of the new situation. The connect()
function has to check if there already exists a connection and if so, only
update the point of connection rather than creating a new connection.

What does this mean? Well, imagine the following:
1. A user moves one handle and connects it on an Object2
2. The user grabs the handles again and connects it to another point on Object2

This way Object2 recieves two connect() signals of the same handle, but no
disconnect() signals.

The reason I do this is because a lot of things might be updated on connect()
and disconnect() events. Moving a line on (for example) a UML class object
does not change the meaning of the connection, only the place where it is
*visually* attached. For an EDA diagram (where the place is of interest, for
example an opamp) the connect() handler should check if it already has a
connectin and do a disconnect or move action internally (and maybe issue a
diaconnect()).


AJM 04072001: The following text is presumed obsolete since I will implement
the Cassowary toolkit for this purpose. This means that all moving etc. is
not handled by the DiaCanvas, but the constraint solver in Cassowary will
fix this.

=============== O B S O L E T E ============================

4. Owner is moving
~~~~~~~~~~~~~~~~~~
If the handle's owner is moved it should notify the canvas item the handle is
connected too to do a disconnect. If the canvas item the handle is connected to
is moving, it should notify the the handle. If the handle should stay connected
the handle should not be moved (it's offset should be moved in negative
direction wrt to the canvas items transformation). If the handle should also
move the handle has to be disconnected.


5. Attached canvas item is moving
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When the handle->connected_to object is moved, the handle will normally follow.
There might be exceptions, however, so we have to notify the handle's owner.
This is done by sending a handle_motion() event.
Basically the same actions should be taken when doing an interactive move,
except for the glue() part.

6. Group of canvas items
~~~~~~~~~~~~~~~~~~~~~~~~
If a group of objects is moved all object are moved, and if handle owner is
moved and handle->connected_to is moved, no action should be taken. Otherwise
act like in point 5.
  It is pretty tricky to determine which objects are (about to be) moved. There
are two reasonable alternatives:
 1. Set the selected flags for all objects on the canvas and let the canvas
    object take care of it. The disadvantage is that every object should be
    updated to the state of the active view as soon as an (motion) event occurs.
 2. The canvas object should emit a "move" event and the canvas view item
    should tell the objects to move. The advantage is that the object can move
    it's shapes also (they should not have to be rendered again :-).

I think the second scheme (although the most complicated) is the best solution.
(1) The CanvasItem should call dia_canvas_item_move (canvas_item, dx, dy). 
(2) Then DiaCanvasCalss:move_selected(dx, dy) is emitted.
(3) Movements are cached and an idle handler is registered. The movements
    should be handled at the same priority as update()'s. 
    Now the handles can check if their connected_to object is selected and take
    appropriate actions.

In idle time:
(4) All objects are updated to represent their latest state (for that view).
    The objects are moved and the shapes are updated (moved rather
    than rendered).
    Now the handles should be notified and should update themselves.
(5) Moves are handles quite the same way as updates are (note that ::update()
    should also check for moves)


Arjan
