david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 1 | /******************************************************************************* |
amywu | ecebb04 | 2007-04-10 20:07:35 +0000 | [diff] [blame] | 2 | * Copyright (c) 2001, 2005 IBM Corporation and others. |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 3 | * All rights reserved. This program and the accompanying materials |
| 4 | * are made available under the terms of the Eclipse Public License v1.0 |
| 5 | * which accompanies this distribution, and is available at |
| 6 | * http://www.eclipse.org/legal/epl-v10.html |
amywu | ecebb04 | 2007-04-10 20:07:35 +0000 | [diff] [blame] | 7 | * |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 8 | * Contributors: |
| 9 | * IBM Corporation - initial API and implementation |
| 10 | * Jens Lukowski/Innoopract - initial renaming/restructuring |
| 11 | * |
| 12 | *******************************************************************************/ |
pavery | 0f11a86 | 2005-03-29 21:14:36 +0000 | [diff] [blame] | 13 | package org.eclipse.wst.sse.ui.internal; |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 14 | |
nitind | 1e30e01 | 2004-11-16 17:01:35 +0000 | [diff] [blame] | 15 | import org.eclipse.core.runtime.IProgressMonitor; |
| 16 | import org.eclipse.core.runtime.IStatus; |
| 17 | import org.eclipse.core.runtime.Status; |
| 18 | import org.eclipse.core.runtime.jobs.Job; |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 19 | import org.eclipse.swt.SWT; |
| 20 | import org.eclipse.swt.custom.StyledText; |
| 21 | import org.eclipse.swt.events.KeyEvent; |
| 22 | import org.eclipse.swt.events.KeyListener; |
| 23 | import org.eclipse.swt.events.MouseEvent; |
| 24 | import org.eclipse.swt.events.MouseListener; |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 25 | import org.eclipse.swt.widgets.Display; |
| 26 | import org.eclipse.swt.widgets.Event; |
| 27 | import org.eclipse.swt.widgets.Listener; |
david_williams | 2aecf08 | 2005-04-13 05:03:21 +0000 | [diff] [blame] | 28 | import org.eclipse.wst.sse.core.internal.util.Debug; |
| 29 | import org.eclipse.wst.sse.core.internal.util.Utilities; |
david_williams | f3680f0 | 2005-04-13 22:43:54 +0000 | [diff] [blame] | 30 | import org.eclipse.wst.sse.ui.internal.view.events.CaretEvent; |
| 31 | import org.eclipse.wst.sse.ui.internal.view.events.ICaretListener; |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 32 | |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 33 | /** |
| 34 | * Has the responsibility of listening for key events, and mouse events, |
| 35 | * deciding if the caret has moved (without a text change), and if so, will |
| 36 | * notify CaretListeners that the caret has moved. Objects which are |
| 37 | * interested in ALL caret postion changes will also have to listen for |
| 38 | * textChanged events. |
nitind | 0d5b341 | 2005-09-27 21:36:16 +0000 | [diff] [blame] | 39 | * |
| 40 | * @deprecated - use base selection notification |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 41 | */ |
nitind | 1e30e01 | 2004-11-16 17:01:35 +0000 | [diff] [blame] | 42 | public class CaretMediator implements Listener { |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 43 | |
nitind | 1e30e01 | 2004-11-16 17:01:35 +0000 | [diff] [blame] | 44 | class CaretMediatorListener implements KeyListener, MouseListener { |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 45 | public void keyPressed(KeyEvent e) { |
| 46 | internalKeyPressed(e); |
| 47 | } |
| 48 | |
| 49 | public void keyReleased(KeyEvent e) { |
| 50 | internalKeyReleased(e); |
| 51 | } |
| 52 | |
| 53 | public void mouseDoubleClick(MouseEvent e) { |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 54 | } |
| 55 | |
| 56 | public void mouseDown(MouseEvent e) { |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 57 | internalMouseDown(e); |
| 58 | } |
| 59 | |
| 60 | public void mouseUp(MouseEvent e) { |
| 61 | internalMouseUp(e); |
| 62 | } |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 63 | } |
| 64 | |
nitind | 1e30e01 | 2004-11-16 17:01:35 +0000 | [diff] [blame] | 65 | class RefreshDelayJob extends Job { |
| 66 | private int fDelay = 0; |
| 67 | RefreshDelayJob(int delay) { |
david_williams | b9da93e | 2005-04-13 02:38:05 +0000 | [diff] [blame] | 68 | super(SSEUIMessages.caret_update); //$NON-NLS-1$ |
pavery | 1120ce8 | 2004-11-23 20:07:36 +0000 | [diff] [blame] | 69 | setSystem(true); |
nitind | 1e30e01 | 2004-11-16 17:01:35 +0000 | [diff] [blame] | 70 | fDelay = delay; |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 71 | } |
| 72 | |
| 73 | /** |
nitind | 1e30e01 | 2004-11-16 17:01:35 +0000 | [diff] [blame] | 74 | * Setup a delayed CaretEvent firing |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 75 | */ |
nitind | 1e30e01 | 2004-11-16 17:01:35 +0000 | [diff] [blame] | 76 | void touch() { |
| 77 | cancel(); |
| 78 | schedule(fDelay); |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 79 | } |
| 80 | |
nitind | 1e30e01 | 2004-11-16 17:01:35 +0000 | [diff] [blame] | 81 | protected IStatus run(IProgressMonitor monitor) { |
| 82 | handleEvent(null); |
| 83 | return Status.OK_STATUS; |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 84 | } |
| 85 | } |
nitind | 1e30e01 | 2004-11-16 17:01:35 +0000 | [diff] [blame] | 86 | |
| 87 | RefreshDelayJob fDelayer = null; |
| 88 | private static final int DELAY = 300; |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 89 | |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 90 | /** used just for debug print outs */ |
| 91 | private long endTime; |
nitind | 1e30e01 | 2004-11-16 17:01:35 +0000 | [diff] [blame] | 92 | private long startTime; |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 93 | |
| 94 | protected ICaretListener[] fCaretListeners; |
| 95 | protected CaretMediatorListener internalListener; |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 96 | protected StyledText textWidget; |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 97 | |
| 98 | /** |
| 99 | * CaretMediator constructor comment. |
| 100 | */ |
| 101 | public CaretMediator() { |
| 102 | super(); |
| 103 | } |
| 104 | |
| 105 | /** |
| 106 | * CaretMediator constructor comment. Must always provide the widget its |
| 107 | * supposed to listen to. |
| 108 | */ |
nitind | 1e30e01 | 2004-11-16 17:01:35 +0000 | [diff] [blame] | 109 | public CaretMediator(StyledText styledTextWidget) { |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 110 | this(); |
nitind | 1e30e01 | 2004-11-16 17:01:35 +0000 | [diff] [blame] | 111 | setTextWidget(styledTextWidget); |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 112 | } |
| 113 | |
| 114 | public synchronized void addCaretListener(ICaretListener listener) { |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 115 | if (Debug.debugStructuredDocument) { |
| 116 | System.out.println("CaretMediator::addCaretListener. Request to add an instance of " + listener.getClass() + " as a listener on caretlistner.");//$NON-NLS-2$//$NON-NLS-1$ |
| 117 | } |
| 118 | // make sure listener is not already in listening array |
| 119 | // (and if it is, print a warning to aid debugging, if needed) |
| 120 | |
| 121 | if (Utilities.contains(fCaretListeners, listener)) { |
| 122 | if (Debug.displayWarnings) { |
| 123 | System.out.println("CaretMediator::addCaretListener. listener " + listener + " was added more than once. ");//$NON-NLS-2$//$NON-NLS-1$ |
| 124 | } |
| 125 | } else { |
| 126 | if (Debug.debugStructuredDocument) { |
| 127 | System.out.println("CaretMediator::addCaretListener. Adding an instance of " + listener.getClass() + " as a listener on caret mediator.");//$NON-NLS-2$//$NON-NLS-1$ |
| 128 | } |
| 129 | int oldSize = 0; |
| 130 | if (fCaretListeners != null) { |
| 131 | // normally won't be null, but we need to be sure, for first |
| 132 | // time through |
| 133 | oldSize = fCaretListeners.length; |
| 134 | } |
| 135 | int newSize = oldSize + 1; |
| 136 | ICaretListener[] newListeners = new ICaretListener[newSize]; |
| 137 | if (fCaretListeners != null) { |
| 138 | System.arraycopy(fCaretListeners, 0, newListeners, 0, oldSize); |
| 139 | } |
| 140 | // add listener to last position |
| 141 | newListeners[newSize - 1] = listener; |
| 142 | // |
| 143 | // now switch new for old |
| 144 | fCaretListeners = newListeners; |
| 145 | |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | protected void fireCaretEvent(CaretEvent event) { |
| 150 | if (fCaretListeners != null) { |
| 151 | // we must assign listeners to local variable to be thread safe, |
| 152 | // since the add and remove listner methods |
| 153 | // can change this object's actual instance of the listener array |
| 154 | // from another thread |
| 155 | // (and since object assignment is atomic, we don't need to |
| 156 | // synchronize |
| 157 | ICaretListener[] holdListeners = fCaretListeners; |
| 158 | // |
| 159 | for (int i = 0; i < holdListeners.length; i++) { |
| 160 | holdListeners[i].caretMoved(event); |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 161 | } |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | public void handleEvent(Event e) { |
| 166 | Display display = null; |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 167 | |
| 168 | if (Debug.debugCaretMediator) { |
| 169 | endTime = System.currentTimeMillis(); |
| 170 | System.out.println("Timer fired: " + (endTime - startTime)); //$NON-NLS-1$ |
| 171 | } |
| 172 | |
| 173 | // check if 'okToUse' |
| 174 | if (textWidget != null && !textWidget.isDisposed()) { |
| 175 | display = textWidget.getDisplay(); |
| 176 | if ((display != null) && (!display.isDisposed())) { |
| 177 | display.asyncExec(new Runnable() { |
| 178 | public void run() { |
| 179 | if (textWidget != null && !textWidget.isDisposed()) { |
| 180 | fireCaretEvent(new CaretEvent(textWidget, textWidget.getCaretOffset())); |
| 181 | } |
| 182 | } |
| 183 | }); |
| 184 | } |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | protected void internalKeyPressed(KeyEvent e) { |
nitind | 1e30e01 | 2004-11-16 17:01:35 +0000 | [diff] [blame] | 189 | fDelayer.cancel(); |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 190 | } |
| 191 | |
| 192 | protected void internalKeyReleased(KeyEvent e) { |
| 193 | switch (e.keyCode) { |
| 194 | case SWT.ARROW_DOWN : |
| 195 | case SWT.ARROW_UP : |
| 196 | case SWT.ARROW_LEFT : |
| 197 | case SWT.ARROW_RIGHT : |
| 198 | case SWT.HOME : |
| 199 | case SWT.END : |
| 200 | case SWT.PAGE_DOWN : |
| 201 | case SWT.PAGE_UP : { |
nitind | 1e30e01 | 2004-11-16 17:01:35 +0000 | [diff] [blame] | 202 | fDelayer.touch(); |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 203 | break; |
| 204 | } |
| 205 | default : { |
| 206 | // always update cursor postion, even during normal typing |
| 207 | // (the logic may look funny, since we always to the same |
| 208 | // thing, but we haven't always done the same thing, so I |
| 209 | // wanted to leave that fact documented via code.) |
nitind | 1e30e01 | 2004-11-16 17:01:35 +0000 | [diff] [blame] | 210 | fDelayer.touch(); |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 211 | } |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | protected void internalMouseDown(MouseEvent e) { |
nitind | 1e30e01 | 2004-11-16 17:01:35 +0000 | [diff] [blame] | 216 | fDelayer.cancel(); |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 217 | } |
| 218 | |
| 219 | protected void internalMouseUp(MouseEvent e) { |
| 220 | // Note, even during a swipe select, when the mouse button goes up, |
| 221 | // and the widget is |
| 222 | // queried for the current caret postion, it always returns the |
| 223 | // beginning of the selection, |
| 224 | // which is desirable (at least for the known use of this feature, |
| 225 | // which is to signal |
| 226 | // that the property sheet can update itself. |
nitind | 1e30e01 | 2004-11-16 17:01:35 +0000 | [diff] [blame] | 227 | fDelayer.touch(); |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 228 | } |
| 229 | |
| 230 | public void release() { |
nitind | 1e30e01 | 2004-11-16 17:01:35 +0000 | [diff] [blame] | 231 | fDelayer.cancel(); |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 232 | if (textWidget != null && !textWidget.isDisposed()) { |
| 233 | textWidget.removeKeyListener(internalListener); |
| 234 | textWidget.removeMouseListener(internalListener); |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 235 | textWidget = null; |
| 236 | } |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 237 | } |
| 238 | |
| 239 | public synchronized void removeCaretListener(ICaretListener listener) { |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 240 | if ((fCaretListeners != null) && (listener != null)) { |
| 241 | // if its not in the listeners, we'll ignore the request |
| 242 | if (Utilities.contains(fCaretListeners, listener)) { |
| 243 | int oldSize = fCaretListeners.length; |
| 244 | int newSize = oldSize - 1; |
| 245 | ICaretListener[] newListeners = new ICaretListener[newSize]; |
| 246 | int index = 0; |
| 247 | for (int i = 0; i < oldSize; i++) { |
| 248 | if (fCaretListeners[i] == listener) { // ignore |
| 249 | } else { |
| 250 | // copy old to new if its not the one we are removing |
| 251 | newListeners[index++] = fCaretListeners[i]; |
| 252 | } |
| 253 | } |
| 254 | // now that we have a new array, let's switch it for the old |
| 255 | // one |
| 256 | fCaretListeners = newListeners; |
| 257 | } |
| 258 | } |
| 259 | } |
| 260 | |
| 261 | public void setTextWidget(StyledText newTextWidget) { |
nitind | 1e30e01 | 2004-11-16 17:01:35 +0000 | [diff] [blame] | 262 | if(fDelayer == null) { |
| 263 | fDelayer = new RefreshDelayJob(DELAY); |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 264 | } |
| 265 | |
nitind | 1e30e01 | 2004-11-16 17:01:35 +0000 | [diff] [blame] | 266 | // unhook from previous, if any |
| 267 | if (this.textWidget != null) { |
| 268 | fDelayer.cancel(); |
| 269 | this.textWidget.removeKeyListener(internalListener); |
| 270 | this.textWidget.removeMouseListener(internalListener); |
| 271 | } |
| 272 | |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 273 | this.textWidget = newTextWidget; |
| 274 | |
| 275 | if (internalListener == null) { |
| 276 | internalListener = new CaretMediatorListener(); |
| 277 | } |
| 278 | |
| 279 | if (this.textWidget != null) { |
| 280 | this.textWidget.addKeyListener(internalListener); |
| 281 | this.textWidget.addMouseListener(internalListener); |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 282 | } |
david_williams | cfdb2cd | 2004-11-11 08:37:49 +0000 | [diff] [blame] | 283 | } |
| 284 | } |