title: SWT ScrolledComposite Hand Tool: Scroll Proportional To Mouse Movement
body: <p>I would like to implement a "hand tool" with which the user can scroll/move something with the mouse. This feature is known from various programs such as <a href="http://docs.gimp.org/en/gimp-tool-move.html" rel="nofollow">Gimp</a>. If the user presses the left mouse button, the tool should be activated. If the mouse is now moved, the underlying object should also be moved - <strong>proportional</strong> to the mouse movement. Hence, if the user presses the mouse and moves it by e.g. 10px, the underlying object should also be moved by 10px. I thought that it should work with my code below, but it does not work properly. The movement of the underlying <a href="http://help.eclipse.org/kepler/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/custom/ScrolledComposite.html" rel="nofollow"><code>ScrolledComposite</code></a> is too fast.</p>
  
  <p>The code below is ready-to-run, so you can directly play with it.</p>
  
  <pre><code>import org.eclipse.swt.SWT;
  import org.eclipse.swt.custom.ScrolledComposite;
  import org.eclipse.swt.events.MouseAdapter;
  import org.eclipse.swt.events.MouseEvent;
  import org.eclipse.swt.events.MouseMoveListener;
  import org.eclipse.swt.graphics.Point;
  import org.eclipse.swt.layout.FillLayout;
  import org.eclipse.swt.widgets.Display;
  import org.eclipse.swt.widgets.Shell;
  import org.eclipse.swt.widgets.Text;
  
  public class HandTool {
  
    public static boolean moveGraphActive = false;
    public static Point moveStartPos = new Point(0, 0);
    public static ScrolledComposite sc;
  
    public static void main(String[] args) {
      Display display = new Display();
      Shell shell = new Shell(display);
      shell.setLayout(new FillLayout());
      sc = new ScrolledComposite(shell, SWT.V_SCROLL);
  
      Text text = new Text(sc, SWT.MULTI | SWT.READ_ONLY);
      String multiLineText = "";
      for (int i = 0; i &lt; 30; i++) {
        multiLineText += "Hello World SWT " + i + System.lineSeparator();
      }
      text.setText(multiLineText);
      text.setEnabled(false);
      text.pack();
      sc.setContent(text);
  
      sc.addMouseListener(new MouseAdapter() {
        @Override
        public void mouseDown(final MouseEvent e) {
          moveGraphActive = e.button == 1;
          if (moveGraphActive) {
            moveStartPos.x = e.x;
            moveStartPos.y = e.y;
          }
        }
  
        @Override
        public void mouseUp(final MouseEvent e) {
          moveGraphActive = false;
        }
      });
  
      sc.addMouseMoveListener(new MouseMoveListener() {
        @Override
        public void mouseMove(final MouseEvent e) {
          if (moveGraphActive) {
            // FIXME the movement of the canvas should be proportional
            // to the mouse
            // movement
            final int newX = sc.getOrigin().x + moveStartPos.x - e.x;
            final int newY = sc.getOrigin().y + moveStartPos.y - e.y;
            sc.setOrigin(newX, newY);
          }
        }
      });
  
      shell.setSize(500, 150);
      shell.open();
      while (!shell.isDisposed()) {
        if (!display.readAndDispatch())
          display.sleep();
      }
      display.dispose();
    }
  }
  </code></pre>