All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ardor3d.input.swt.SwtMouseWrapper Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (c) 2008-2012 Ardor Labs, Inc.
 *
 * This file is part of Ardor3D.
 *
 * Ardor3D is free software: you can redistribute it and/or modify it
 * under the terms of its license which may be found in the accompanying
 * LICENSE file or at .
 */

package com.ardor3d.input.swt;

import static com.google.common.base.Preconditions.checkNotNull;

import java.util.EnumMap;
import java.util.EnumSet;
import java.util.LinkedList;

import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.MouseWheelListener;
import org.eclipse.swt.widgets.Control;

import com.ardor3d.annotation.GuardedBy;
import com.ardor3d.annotation.ThreadSafe;
import com.ardor3d.input.ButtonState;
import com.ardor3d.input.MouseButton;
import com.ardor3d.input.MouseState;
import com.ardor3d.input.MouseWrapper;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.EnumMultiset;
import com.google.common.collect.Maps;
import com.google.common.collect.Multiset;
import com.google.common.collect.PeekingIterator;

/**
 * A mouse wrapper for use with SWT.
 */
@ThreadSafe
public class SwtMouseWrapper implements MouseWrapper, MouseListener, MouseMoveListener, MouseWheelListener {
    @GuardedBy("this")
    private final LinkedList _upcomingEvents = new LinkedList();

    private final Control _control;
    private boolean _ignoreInput;

    @GuardedBy("this")
    private SwtMouseIterator _currentIterator = null;

    @GuardedBy("this")
    private MouseState _lastState = null;

    private final Multiset _clicks = EnumMultiset.create(MouseButton.class);
    private final EnumMap _lastClickTime = Maps.newEnumMap(MouseButton.class);
    private final EnumSet _clickArmed = EnumSet.noneOf(MouseButton.class);

    public SwtMouseWrapper(final Control control) {
        _control = checkNotNull(control, "control");
        for (final MouseButton mb : MouseButton.values()) {
            _lastClickTime.put(mb, 0L);
        }
    }

    public void init() {
        _control.addMouseListener(this);
        _control.addMouseMoveListener(this);
        _control.addMouseWheelListener(this);
    }

    public synchronized PeekingIterator getEvents() {
        expireClickEvents();

        if (_currentIterator == null || !_currentIterator.hasNext()) {
            _currentIterator = new SwtMouseIterator();
        }

        return _currentIterator;
    }

    private void expireClickEvents() {
        if (!_clicks.isEmpty()) {
            for (final MouseButton mb : MouseButton.values()) {
                if (System.currentTimeMillis() - _lastClickTime.get(mb) > MouseState.CLICK_TIME_MS) {
                    _clicks.setCount(mb, 0);
                }
            }
        }
    }

    public synchronized void mouseDoubleClick(final MouseEvent mouseEvent) {
        // ignoring this. We'll handle (multi)click in a uniform way
    }

    public synchronized void mouseDown(final MouseEvent e) {
        if (_ignoreInput) {
            return;
        }

        final MouseButton b = getButtonForEvent(e);
        if (_clickArmed.contains(b)) {
            _clicks.setCount(b, 0);
        }
        _clickArmed.add(b);
        _lastClickTime.put(b, System.currentTimeMillis());

        initState(e);

        final EnumMap buttons = _lastState.getButtonStates();

        setStateForButton(e, buttons, ButtonState.DOWN);

        addNewState(e, 0, buttons, null);
    }

    public synchronized void mouseUp(final MouseEvent e) {
        if (_ignoreInput) {
            return;
        }

        initState(e);

        final EnumMap buttons = _lastState.getButtonStates();

        setStateForButton(e, buttons, ButtonState.UP);

        final MouseButton b = getButtonForEvent(e);
        if (_clickArmed.contains(b)
                && (System.currentTimeMillis() - _lastClickTime.get(b) <= MouseState.CLICK_TIME_MS)) {
            _clicks.add(b); // increment count of clicks for button b.
            // XXX: Note the double event add... this prevents sticky click counts, but is it the best way?
            addNewState(e, 0, buttons, EnumMultiset.create(_clicks));
        } else {
            _clicks.setCount(b, 0); // clear click count for button b.
        }
        _clickArmed.remove(b);

        addNewState(e, 0, buttons, null);
    }

    private int getDX(final MouseEvent e) {
        return e.x - _lastState.getX();
    }

    private int getDY(final MouseEvent e) {
        return getArdor3DY(e) - _lastState.getY();
    }

    /**
     * @param e
     *            our mouseEvent
     * @return the Y coordinate of the event, flipped relative to the component since we expect an origin in the lower
     *         left corner.
     */
    private int getArdor3DY(final MouseEvent e) {
        return _control.getSize().y - e.y;
    }

    private void setStateForButton(final MouseEvent e, final EnumMap buttons,
            final ButtonState buttonState) {
        final MouseButton button = getButtonForEvent(e);
        buttons.put(button, buttonState);
    }

    private MouseButton getButtonForEvent(final MouseEvent e) {
        MouseButton button;
        switch (e.button) { // ordering is different than swt
            case 1:
                button = MouseButton.LEFT;
                break;
            case 3:
                button = MouseButton.RIGHT;
                break;
            case 2:
                button = MouseButton.MIDDLE;
                break;
            default:
                throw new RuntimeException("unknown button: " + e.button);
        }
        return button;
    }

    public synchronized void mouseMove(final MouseEvent mouseEvent) {
        if (_ignoreInput) {
            return;
        }

        _clickArmed.clear();
        _clicks.clear();

        // check that we have a valid _lastState
        initState(mouseEvent);

        addNewState(mouseEvent, 0, _lastState.getButtonStates(), null);
    }

    public synchronized void mouseScrolled(final MouseEvent mouseEvent) {
        if (_ignoreInput) {
            return;
        }

        initState(mouseEvent);

        addNewState(mouseEvent, mouseEvent.count, _lastState.getButtonStates(), null);
    }

    private void initState(final MouseEvent mouseEvent) {
        if (_lastState == null) {
            _lastState = new MouseState(mouseEvent.x, getArdor3DY(mouseEvent), 0, 0, 0, null, null);
        }
    }

    private void addNewState(final MouseEvent mouseEvent, final int wheelDX,
            final EnumMap buttons, final Multiset clicks) {

        final MouseState newState = new MouseState(mouseEvent.x, getArdor3DY(mouseEvent), getDX(mouseEvent),
                getDY(mouseEvent), wheelDX, buttons, clicks);

        _upcomingEvents.add(newState);
        _lastState = newState;
    }

    private class SwtMouseIterator extends AbstractIterator implements PeekingIterator {
        @Override
        protected MouseState computeNext() {
            synchronized (SwtMouseWrapper.this) {
                if (_upcomingEvents.isEmpty()) {
                    return endOfData();
                }

                return _upcomingEvents.poll();
            }
        }
    }

    @Override
    public void setIgnoreInput(final boolean ignore) {
        _ignoreInput = ignore;
    }

    @Override
    public boolean isIgnoreInput() {
        return _ignoreInput;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy