/******************************************************************************* * Copyright (c) 2001, 2005 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Jens Lukowski/Innoopract - initial renaming/restructuring * *******************************************************************************/ package org.eclipse.wst.sse.ui.internal; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.wst.sse.core.internal.util.Debug; import org.eclipse.wst.sse.core.internal.util.Utilities; import org.eclipse.wst.sse.ui.internal.view.events.CaretEvent; import org.eclipse.wst.sse.ui.internal.view.events.ICaretListener; /** * Has the responsibility of listening for key events, and mouse events, * deciding if the caret has moved (without a text change), and if so, will * notify CaretListeners that the caret has moved. Objects which are * interested in ALL caret postion changes will also have to listen for * textChanged events. * * @deprecated - use base selection notification */ public class CaretMediator implements Listener { class CaretMediatorListener implements KeyListener, MouseListener { public void keyPressed(KeyEvent e) { internalKeyPressed(e); } public void keyReleased(KeyEvent e) { internalKeyReleased(e); } public void mouseDoubleClick(MouseEvent e) { } public void mouseDown(MouseEvent e) { internalMouseDown(e); } public void mouseUp(MouseEvent e) { internalMouseUp(e); } } class RefreshDelayJob extends Job { private int fDelay = 0; RefreshDelayJob(int delay) { super(SSEUIMessages.caret_update); //$NON-NLS-1$ setSystem(true); fDelay = delay; } /** * Setup a delayed CaretEvent firing */ void touch() { cancel(); schedule(fDelay); } protected IStatus run(IProgressMonitor monitor) { handleEvent(null); return Status.OK_STATUS; } } RefreshDelayJob fDelayer = null; private static final int DELAY = 300; /** used just for debug print outs */ private long endTime; private long startTime; protected ICaretListener[] fCaretListeners; protected CaretMediatorListener internalListener; protected StyledText textWidget; /** * CaretMediator constructor comment. */ public CaretMediator() { super(); } /** * CaretMediator constructor comment. Must always provide the widget its * supposed to listen to. */ public CaretMediator(StyledText styledTextWidget) { this(); setTextWidget(styledTextWidget); } public synchronized void addCaretListener(ICaretListener listener) { if (Debug.debugStructuredDocument) { System.out.println("CaretMediator::addCaretListener. Request to add an instance of " + listener.getClass() + " as a listener on caretlistner.");//$NON-NLS-2$//$NON-NLS-1$ } // make sure listener is not already in listening array // (and if it is, print a warning to aid debugging, if needed) if (Utilities.contains(fCaretListeners, listener)) { if (Debug.displayWarnings) { System.out.println("CaretMediator::addCaretListener. listener " + listener + " was added more than once. ");//$NON-NLS-2$//$NON-NLS-1$ } } else { if (Debug.debugStructuredDocument) { System.out.println("CaretMediator::addCaretListener. Adding an instance of " + listener.getClass() + " as a listener on caret mediator.");//$NON-NLS-2$//$NON-NLS-1$ } int oldSize = 0; if (fCaretListeners != null) { // normally won't be null, but we need to be sure, for first // time through oldSize = fCaretListeners.length; } int newSize = oldSize + 1; ICaretListener[] newListeners = new ICaretListener[newSize]; if (fCaretListeners != null) { System.arraycopy(fCaretListeners, 0, newListeners, 0, oldSize); } // add listener to last position newListeners[newSize - 1] = listener; // // now switch new for old fCaretListeners = newListeners; } } protected void fireCaretEvent(CaretEvent event) { if (fCaretListeners != null) { // we must assign listeners to local variable to be thread safe, // since the add and remove listner methods // can change this object's actual instance of the listener array // from another thread // (and since object assignment is atomic, we don't need to // synchronize ICaretListener[] holdListeners = fCaretListeners; // for (int i = 0; i < holdListeners.length; i++) { holdListeners[i].caretMoved(event); } } } public void handleEvent(Event e) { Display display = null; if (Debug.debugCaretMediator) { endTime = System.currentTimeMillis(); System.out.println("Timer fired: " + (endTime - startTime)); //$NON-NLS-1$ } // check if 'okToUse' if (textWidget != null && !textWidget.isDisposed()) { display = textWidget.getDisplay(); if ((display != null) && (!display.isDisposed())) { display.asyncExec(new Runnable() { public void run() { if (textWidget != null && !textWidget.isDisposed()) { fireCaretEvent(new CaretEvent(textWidget, textWidget.getCaretOffset())); } } }); } } } protected void internalKeyPressed(KeyEvent e) { fDelayer.cancel(); } protected void internalKeyReleased(KeyEvent e) { switch (e.keyCode) { case SWT.ARROW_DOWN : case SWT.ARROW_UP : case SWT.ARROW_LEFT : case SWT.ARROW_RIGHT : case SWT.HOME : case SWT.END : case SWT.PAGE_DOWN : case SWT.PAGE_UP : { fDelayer.touch(); break; } default : { // always update cursor postion, even during normal typing // (the logic may look funny, since we always to the same // thing, but we haven't always done the same thing, so I // wanted to leave that fact documented via code.) fDelayer.touch(); } } } protected void internalMouseDown(MouseEvent e) { fDelayer.cancel(); } protected void internalMouseUp(MouseEvent e) { // Note, even during a swipe select, when the mouse button goes up, // and the widget is // queried for the current caret postion, it always returns the // beginning of the selection, // which is desirable (at least for the known use of this feature, // which is to signal // that the property sheet can update itself. fDelayer.touch(); } public void release() { fDelayer.cancel(); if (textWidget != null && !textWidget.isDisposed()) { textWidget.removeKeyListener(internalListener); textWidget.removeMouseListener(internalListener); textWidget = null; } } public synchronized void removeCaretListener(ICaretListener listener) { if ((fCaretListeners != null) && (listener != null)) { // if its not in the listeners, we'll ignore the request if (Utilities.contains(fCaretListeners, listener)) { int oldSize = fCaretListeners.length; int newSize = oldSize - 1; ICaretListener[] newListeners = new ICaretListener[newSize]; int index = 0; for (int i = 0; i < oldSize; i++) { if (fCaretListeners[i] == listener) { // ignore } else { // copy old to new if its not the one we are removing newListeners[index++] = fCaretListeners[i]; } } // now that we have a new array, let's switch it for the old // one fCaretListeners = newListeners; } } } public void setTextWidget(StyledText newTextWidget) { if(fDelayer == null) { fDelayer = new RefreshDelayJob(DELAY); } // unhook from previous, if any if (this.textWidget != null) { fDelayer.cancel(); this.textWidget.removeKeyListener(internalListener); this.textWidget.removeMouseListener(internalListener); } this.textWidget = newTextWidget; if (internalListener == null) { internalListener = new CaretMediatorListener(); } if (this.textWidget != null) { this.textWidget.addKeyListener(internalListener); this.textWidget.addMouseListener(internalListener); } } }