home *** CD-ROM | disk | FTP | other *** search
-
- package sub_arctic.input;
-
- import sub_arctic.lib.interactor;
- import sub_arctic.lib.manager;
-
- /**
- * A positional dispatch agent responsible for managing press/release, click,
- * and double click interactions. This agent dispatches under three different
- * input protocols: pressable (which does single presses and releases of a
- * mouse button), clickable (which is a press and a release of a mouse button),
- * and double_clickable (which is two rapid clicks in a small area). Note:
- * that if the dispatch of a click is consumed, this resets the internal
- * finite state controller and precludes delivery of a subsequent click as a
- * double click. Similarly, if a press or release is consumed, those events
- * will not form part of a click dispatch.<p>
- *
- * @see sub_arctic.input.pressable
- * @see sub_arctic.input.clickable
- * @see sub_arctic.input.double_clickable
- * @author Scott Hudson
- */
- public class click_agent extends dispatch_agent implements click_tracking {
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /** Part of protocol for click_tracking which we use to be informed about
- * button presses or releases that occur and are consumed elsewhere.
- * Here we happen to ignore this. */
- public void focus_set_enter(
- event evt,
- focus_dispatch_agent agnt,
- Object user_info)
- {
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /** Part of protocol for click_tracking which we use to be informed about
- * button presses or releases that occur and are consumed elsewhere.
- * Here we happen to ignore this. */
- public void focus_set_exit(
- event evt,
- focus_dispatch_agent agnt,
- Object user_info)
- {
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /** State for finite state controller. States are:
- * 0 start state, have seen nothing yet.
- * 1 have seen press
- * 2 have seen release
- * 3 have dispatched press and release as a click
- * 4 have seen second press after click
- * 5 have seen second press and release after click
- */
- protected int state;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /** Sequence number of last event processed */
- protected int last_evt_seq;
-
- /** Sequence number of last press or release we monitored */
- protected int last_press_release_anywhere;
-
- /** X location of first press in sequence (in global coordinates).
- * We use this for proximity tests. */
- protected int first_x;
-
- /** Y location of first press in sequence (in global coordinates).
- * We use this for proximity tests. */
- protected int first_y;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /** Default constructor */
- public click_agent()
- {
- /* put us in the start state */
- state = 0;
-
- /* reset information about previous event */
- last_evt_seq = -1;
- last_press_release_anywhere = -1;
- first_x = 0xffffffff;
- first_y = 0xffffffff;
-
- /* arrange to monitor all presses and releases */
- manager.click_tracker.add_to_focus(this, null, null);
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * We are only interested in press and release of the mouse button.
- * @param event evt the event we are indicating interest or non-interest in.
- */
- public boolean event_is_useful(event evt)
- {
- return evt.id() == event.MOUSE_DOWN || evt.id() == event.MOUSE_UP;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /** Constant indicating how close together (in both x and y) events have
- * to be to qualify as parts of the same click/double-click.
- */
- protected static final int CLICK_CLOSE = 5;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Determine if event qualifies as close enough to match first event in
- * a sequence. We do this by comparing against the previously saved
- * position of the first event in the sequence.
- * @param event next_event event whose position we are testing
- */
- protected boolean matches_first(event next_event)
- {
- return next_event != null &&
- Math.abs(first_x - next_event.global_x()) < CLICK_CLOSE &&
- Math.abs(first_y - next_event.global_y()) < CLICK_CLOSE;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Track press and release events everywhere that are dispatched to us from
- * the click_track monitoring agent. We record the event sequence number
- * of this event and if we don't see a matching event then we know somebody
- * else got it first and so we reset our state. We detect this by noting
- * that we get a new event but the previous one was never processed. <p>
- *
- * @param event evt the press or release event.
- * @param Object user_info user_info associated with the even dispatch
- * (ignored here).
- */
- public boolean track_click(event evt, Object user_info)
- {
- /* if the previous event was never processed */
- if (last_evt_seq != last_press_release_anywhere)
- {
- /* reset our state */
- state = 0;
- }
-
- /* remember sequence number of this event */
- last_press_release_anywhere = manager.event_seq_num();
-
- return true;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This method makes state transitions for a finite state machine
- * controlling the interaction. This is a complicated state machine in
- * which three kinds of transitions can be made. Each new event moves
- * to a new state (via this method). In addition, if an event actually
- * causes a dispatch, a transition back to the start state (0) may occur
- * (within dispatch_event). Finally, in certain states (2 and 5 --
- * corresponding to a single release or a completed double-click
- * respectively) if the event is *not* dispatched the machine is also reset
- * to the start state. Since we can't actually tell when an event will not
- * be dispatched (e.g., we can't tell if the positional policy will call us
- * again with the same event), this final class of transition is actually
- * handled when the *next* event arrives. This corresponds to doing two
- * transitions here. <p>
- *
- * @param event evt the event potentially causing the transition.
- */
- protected void make_transition(event evt)
- {
- /* do transition based on what state we are in */
- switch(state)
- {
- case 2: /* have seen release */
- /* if we are still in this state, then we didn't dispatch
- * the release and we should really be back in the start
- * state. So... fall through to that state.
- */
- case 5: /* have seen press, release, press, release */
- /* if we are still in this state, then we didn't dispatch
- * the double-click and we should really be back in the
- * start state. So... fall through to that state.
- */
- case 0: /* start state */
- if (evt.id() == event.MOUSE_DOWN)
- state = 1;
- else /* MOUSE_UP */
- state = 2;
-
- /* remember position of this event for later comparison */
- first_x = evt.global_x();
- first_y = evt.global_y();
- break;
-
- case 1: /* have seen one press */
- if (evt.id() == event.MOUSE_UP)
- state = 3;
- else /* MOUSE_DOWN */
- state = 1;
- break;
-
-
- case 3: /* have seen both press and release */
- if (evt.id() == event.MOUSE_DOWN)
- {
- /* if there were close enough for a double go that way */
- if (evt.clickCount() >= 2)
- state = 4;
- else
- /* otherwise, we reset and take just the press */
- state = 1;
- }
- else /* MOUSE_UP */
- state = 2;
- break;
-
- case 4: /* have seen press, release, press */
- if (evt.id() == event.MOUSE_UP)
- state = 5;
- else /* MOUSE_UP */
- state = 1;
- break;
-
- }
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Attempt to perform event dispatch(es) to the given object, resetting
- * the state to the start state when appropriate (see comments for
- * make_transition).<p.
- *
- * @see sub_arctic.input.click_agent#make_transition
- * @param interactor to_obj the object we are trying to dispatch to.
- * @param event evt the event to pass to the object.
- * @param Object user_info uninterpreted user info to pass to the object.
- * @return boolean whether the event has been dispatched and consumed.
- */
- protected boolean do_dispatches(
- interactor to_obj,
- event evt,
- Object user_info)
- {
- boolean result = false;
-
- /* attempt dispatches called for in each state */
- switch (state)
- {
- case 0: /* start state */
- /* no dispatches out of this state */
- result = false;
- break;
-
- case 1: /* have seen press */
- case 4: /* have seen press, release, press */
- /* can dispatch a press, see if object wants it */
- if (to_obj instanceof pressable)
- {
- /* put event into local coords of object and try to dispatch */
- evt.global_to_local(to_obj);
- result = ((pressable)to_obj).press(evt, user_info);
-
- /* if the object took it we reset unless the object also wants
- * the full click or double click
- */
- if (result && !(to_obj instanceof clickable ||
- to_obj instanceof double_clickable))
- state = 0;
- }
- break;
-
- case 2: /* have seen release */
- /* can dispatch a release, see if object wants it */
- if (to_obj instanceof pressable)
- {
- /* put event into local coords of object and try to dispatch */
- evt.global_to_local(to_obj);
- result = ((pressable)to_obj).release(evt, user_info);
-
- /* if the object took it we reset unless the object also wants
- * the full click or double click
- */
- if (result && !(to_obj instanceof clickable ||
- to_obj instanceof double_clickable))
- state = 0;
- }
- break;
-
- case 3: /* have seen press and release */
- /* can dispatch a release or a click, try both, release first */
- if (to_obj instanceof pressable)
- {
- /* put event into local coords of object and try to dispatch */
- evt.global_to_local(to_obj);
- result = ((pressable)to_obj).release(evt, user_info);
-
- /* if the object took it we reset unless the object also wants
- * the full click or double click
- */
- if (result && !(to_obj instanceof clickable ||
- to_obj instanceof double_clickable))
- state = 0;
- }
- /* now try the click */
- if (to_obj instanceof clickable)
- {
- /* put event into local coords of object and try to dispatch */
- evt.global_to_local(to_obj);
- if (((clickable)to_obj).click(evt, user_info))
- {
- result = true;
-
- /* if the object took it we reset unless the object also
- * wants the double click
- */
- if (!(to_obj instanceof double_clickable))
- state = 0;
- }
- }
- break;
-
- case 5: /* have seen press, release, press, release */
- /* can dispatch a release, a click, or a double click, try all */
- if (to_obj instanceof pressable)
- {
- /* put event into local coords of object and try to dispatch */
- evt.global_to_local(to_obj);
- result = ((pressable)to_obj).release(evt, user_info);
-
- /* if the object took it we reset unless the object also wants
- * the full click or double click
- */
- if (result && !(to_obj instanceof clickable ||
- to_obj instanceof double_clickable))
- state = 0;
- }
- /* now try the click */
- if (to_obj instanceof clickable)
- {
- /* put event into local coords of object and try to dispatch */
- evt.global_to_local(to_obj);
- if (((clickable)to_obj).click(evt, user_info))
- {
- result = true;
-
- /* if the object took it we reset unless the object also
- * wants the double click
- */
- if (!(to_obj instanceof double_clickable))
- state = 0;
- }
- }
- /* finally, try the double-click */
- if (to_obj instanceof double_clickable)
- {
- /* put event into local coords of object and try to dispatch */
- evt.global_to_local(to_obj);
- if (((double_clickable)to_obj).double_click(evt, user_info))
- {
- result = true;
- state = 0;
- }
- }
- break;
- }
-
- return result;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Accept an event from the positional policy and attempt to dispatch it to
- * an object.<p>
- *
- * @param event evt the event being dispatched.
- * @param Object user_info uninterpreted information to be passed to
- * the object.
- * @param interactor to_obj the object we should attempt to dispatch to.
- * @param int seq_num the sequence number of this event (so we can
- * tell if we have seen this before in a failed
- * attempt to dispatch it to another object).
- * @return boolean indicating whether the event was dispatched and consumed.
- */
- public boolean dispatch_event(
- event evt,
- Object user_info,
- interactor to_obj,
- int seq_num)
- {
- boolean result, do_reset;
-
- /* if this is the first time we have seen this event, make a state
- * transition on the event */
- if (seq_num != last_evt_seq)
- {
- /* remember this sequence number */
- last_evt_seq = seq_num;
-
- /* reset if we are part way through a sequence and this is now too
- * far away.
- */
- if (!matches_first(evt))
- state = 0;
-
- /* make a transition on this event */
- make_transition(evt);
- }
-
- /* we are now in the proper state, determine which dispatches we can
- * do from this state and try to do them.
- */
- result = do_dispatches(to_obj, evt, user_info);
-
- /* return the result */
- return result;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
- }
- /*=========================== COPYRIGHT NOTICE ===========================
-
- This file is part of the subArctic user interface toolkit.
-
- Copyright (c) 1996 Scott Hudson and Ian Smith
- All rights reserved.
-
- The subArctic system is freely available for most uses under the terms
- and conditions described in
- http://www.cc.gatech.edu/gvu/ui/sub_arctic/sub_arctic/doc/usage.html
- and appearing in full in the lib/interactor.java source file.
-
- The current release and additional information about this software can be
- found starting at: http://www.cc.gatech.edu/gvu/ui/sub_arctic/
-
- ========================================================================*/
-