1 11 package org.eclipse.jdt.internal.ui.text; 12 13 import java.util.ArrayList ; 14 import java.util.HashSet ; 15 import java.util.Iterator ; 16 import java.util.List ; 17 import java.util.Set ; 18 19 import org.eclipse.core.runtime.Assert; 20 21 import org.eclipse.swt.SWT; 22 import org.eclipse.swt.custom.StyledText; 23 import org.eclipse.swt.events.FocusEvent; 24 import org.eclipse.swt.events.FocusListener; 25 import org.eclipse.swt.events.KeyEvent; 26 import org.eclipse.swt.events.KeyListener; 27 import org.eclipse.swt.events.MouseEvent; 28 import org.eclipse.swt.events.MouseListener; 29 30 import org.eclipse.jface.text.DocumentEvent; 31 import org.eclipse.jface.text.ITextListener; 32 import org.eclipse.jface.text.ITextViewer; 33 import org.eclipse.jface.text.TextEvent; 34 35 import org.eclipse.jdt.internal.ui.text.TypingRun.ChangeType; 36 37 38 48 public class TypingRunDetector { 49 53 54 55 private static final boolean DEBUG= false; 56 57 64 private static final class Change { 65 private ChangeType fType; 66 private int fNextOffset; 67 68 74 public Change(ChangeType type, int nextOffset) { 75 fType= type; 76 fNextOffset= nextOffset; 77 } 78 79 87 public boolean canFollow(Change change) { 88 if (fType == TypingRun.NO_CHANGE) 89 return true; 90 if (fType.equals(TypingRun.UNKNOWN)) 91 return false; 92 if (fType.equals(change.fType)) { 93 if (fType == TypingRun.DELETE) 94 return fNextOffset == change.fNextOffset - 1; 95 else if (fType == TypingRun.INSERT) 96 return fNextOffset == change.fNextOffset + 1; 97 else if (fType == TypingRun.OVERTYPE) 98 return fNextOffset == change.fNextOffset + 1; 99 else if (fType == TypingRun.SELECTION) 100 return true; 101 } 102 return false; 103 } 104 105 112 public boolean isModification() { 113 return fType.isModification(); 114 } 115 116 119 public String toString() { 120 return fType.toString() + "@" + fNextOffset; } 122 123 128 public ChangeType getType() { 129 return fType; 130 } 131 } 132 133 138 private class TextListener implements ITextListener { 139 140 143 public void textChanged(TextEvent event) { 144 handleTextChanged(event); 145 } 146 } 147 148 154 private class SelectionListener implements MouseListener, KeyListener, FocusListener { 155 156 159 public void focusGained(FocusEvent e) { 160 handleSelectionChanged(); 161 } 162 163 166 public void focusLost(FocusEvent e) { 167 } 168 169 172 public void mouseDoubleClick(MouseEvent e) { 173 } 174 175 179 public void mouseDown(MouseEvent e) { 180 if (e.button == 1) 181 handleSelectionChanged(); 182 } 183 184 187 public void mouseUp(MouseEvent e) { 188 } 189 190 193 public void keyReleased(KeyEvent e) { 194 } 195 196 200 public void keyPressed(KeyEvent e) { 201 switch (e.keyCode) { 202 case SWT.ARROW_UP: 203 case SWT.ARROW_DOWN: 204 case SWT.ARROW_LEFT: 205 case SWT.ARROW_RIGHT: 206 case SWT.END: 207 case SWT.HOME: 208 case SWT.PAGE_DOWN: 209 case SWT.PAGE_UP: 210 handleSelectionChanged(); 211 break; 212 } 213 } 214 } 215 216 217 private final Set fListeners= new HashSet (); 218 222 private ITextViewer fViewer; 223 224 private final TextListener fTextListener= new TextListener(); 225 228 private SelectionListener fSelectionListener; 229 230 231 232 233 private Change fLastChange; 234 235 private TypingRun fRun; 236 237 242 public void install(ITextViewer viewer) { 243 Assert.isLegal(viewer != null); 244 fViewer= viewer; 245 connect(); 246 } 247 248 251 private void connect() { 252 if (fViewer != null) { 253 fLastChange= new Change(TypingRun.UNKNOWN, -1); 254 fRun= null; 255 fSelectionListener= null; 256 fViewer.addTextListener(fTextListener); 257 } 258 } 259 260 264 public void uninstall() { 265 if (fViewer != null) { 266 fListeners.clear(); 267 disconnect(); 268 fViewer= null; 269 } 270 } 271 272 275 private void disconnect() { 276 fViewer.removeTextListener(fTextListener); 277 ensureSelectionListenerRemoved(); 278 } 279 280 287 public void addTypingRunListener(ITypingRunListener listener) { 288 Assert.isLegal(listener != null); 289 fListeners.add(listener); 290 if (fListeners.size() == 1) 291 connect(); 292 } 293 294 300 public void removeTypingRunListener(ITypingRunListener listener) { 301 fListeners.remove(listener); 302 if (fListeners.size() == 0) 303 disconnect(); 304 } 305 306 311 void handleTextChanged(TextEvent event) { 312 Change type= computeChange(event); 313 handleChange(type); 314 } 315 316 322 private Change computeChange(TextEvent event) { 323 DocumentEvent e= event.getDocumentEvent(); 324 if (e == null) 325 return new Change(TypingRun.NO_CHANGE, -1); 326 327 int start= e.getOffset(); 328 int end= e.getOffset() + e.getLength(); 329 String newText= e.getText(); 330 if (newText == null) 331 newText= new String (); 332 333 if (start == end) { 334 if (newText.length() == 1) 336 return new Change(TypingRun.INSERT, end + 1); 337 } else if (start == end - 1) { 338 if (newText.length() == 1) 339 return new Change(TypingRun.OVERTYPE, end); 340 if (newText.length() == 0) 341 return new Change(TypingRun.DELETE, start); 342 } 343 344 return new Change(TypingRun.UNKNOWN, -1); 345 } 346 347 350 void handleSelectionChanged() { 351 handleChange(new Change(TypingRun.SELECTION, -1)); 352 } 353 354 360 private void handleChange(Change change) { 361 if (change.getType() == TypingRun.NO_CHANGE) 362 return; 363 364 if (DEBUG) 365 System.err.println("Last change: " + fLastChange); 367 if (!change.canFollow(fLastChange)) 368 endIfStarted(change); 369 fLastChange= change; 370 if (change.isModification()) 371 startOrContinue(); 372 373 if (DEBUG) 374 System.err.println("New change: " + change); } 376 377 381 private void startOrContinue() { 382 if (!hasRun()) { 383 if (DEBUG) 384 System.err.println("+Start run"); fRun= new TypingRun(fLastChange.getType()); 386 ensureSelectionListenerAdded(); 387 fireRunBegun(fRun); 388 } 389 } 390 391 398 private boolean hasRun() { 399 return fRun != null; 400 } 401 402 408 private void endIfStarted(Change change) { 409 if (hasRun()) { 410 ensureSelectionListenerRemoved(); 411 if (DEBUG) 412 System.err.println("-End run"); fireRunEnded(fRun, change.getType()); 414 fRun= null; 415 } 416 } 417 418 422 private void ensureSelectionListenerAdded() { 423 if (fSelectionListener == null) { 424 fSelectionListener= new SelectionListener(); 425 StyledText textWidget= fViewer.getTextWidget(); 426 textWidget.addFocusListener(fSelectionListener); 427 textWidget.addKeyListener(fSelectionListener); 428 textWidget.addMouseListener(fSelectionListener); 429 } 430 } 431 432 436 private void ensureSelectionListenerRemoved() { 437 if (fSelectionListener != null) { 438 StyledText textWidget= fViewer.getTextWidget(); 439 textWidget.removeFocusListener(fSelectionListener); 440 textWidget.removeKeyListener(fSelectionListener); 441 textWidget.removeMouseListener(fSelectionListener); 442 fSelectionListener= null; 443 } 444 } 445 446 451 private void fireRunBegun(TypingRun run) { 452 List listeners= new ArrayList (fListeners); 453 for (Iterator it= listeners.iterator(); it.hasNext();) { 454 ITypingRunListener listener= (ITypingRunListener) it.next(); 455 listener.typingRunStarted(fRun); 456 } 457 } 458 459 465 private void fireRunEnded(TypingRun run, ChangeType reason) { 466 List listeners= new ArrayList (fListeners); 467 for (Iterator it= listeners.iterator(); it.hasNext();) { 468 ITypingRunListener listener= (ITypingRunListener) it.next(); 469 listener.typingRunEnded(fRun, reason); 470 } 471 } 472 } 473 | Popular Tags |