package com.yoursway.completion.gui; import java.util.List; import java.util.Timer; import java.util.TimerTask; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.custom.VerifyKeyListener; import org.eclipse.swt.events.VerifyEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import com.yoursway.completion.CompletionProposal; import com.yoursway.completion.CompletionProposalUpdatesListener; import com.yoursway.completion.CompletionProposalsProvider; public class CompletionController implements CompletionProposalUpdatesListener, CompletionProvider { private static final int LONG_CLICK_THRESHOLD = 100; private ProposalsView proposalsView; private final StyledText styledText; private final CompletionProposalsProvider proposalsProvider; private List<? extends CompletionProposal> proposals; public final CompletionStrategy strategy; private Timer timer; protected boolean tabNotPressed = true; private TimerTask task; /** * * @param parent * parent shell for the completion list. * @param styledText * text editor to enable completion for. */ public CompletionController(final StyledText styledText, final CompletionProposalsProvider proposalsProvider) { if (styledText == null || proposalsProvider == null) throw new IllegalArgumentException(); this.styledText = styledText; this.strategy = new CompletionStrategy(this); this.proposalsView = new ProposalsView(styledText, strategy); this.proposalsProvider = proposalsProvider; proposalsView.setSize(new Point(200, 100)); setListeners(); } private TimerTask createLongTabWaiter() { return new TimerTask(){ @Override public void run() { Display.getDefault().asyncExec(new Runnable(){ public void run() { strategy.tabLongClickThreshold(); }; }); } }; } /** * Commands are ignored when tab is pressed */ boolean isCommand(char character, int keyCode){ return character == SWT.CR || (keyCode & SWT.KEYCODE_BIT) != 0; } /** * Completables are applied when tab is pressed */ boolean isCompletable(char character, int keyCode){ return proposalsProvider.isCompletable(character) || character == SWT.BS; } /** * Stoppers finish the completion when tab is pressed */ boolean isStopper(char character, int keyCode){ return !(isCommand(character, keyCode) || isCompletable(character, keyCode)); } private void setListeners() { styledText.addVerifyKeyListener(new VerifyKeyListener(){ public void verifyKey(VerifyEvent event) { if (event.character == SWT.TAB) { if(tabNotPressed){ proposalsView.hookArrowKeys(); strategy.tabPressed(); timer.purge(); task = createLongTabWaiter(); timer.schedule(task, LONG_CLICK_THRESHOLD); tabNotPressed = false; } event.doit = false; } else if (isCommand(event.character, event.keyCode) && !tabNotPressed) { event.doit = false; } else if (isStopper(event.character, event.keyCode)){ strategy.tabReleased(); } else if (isCompletable(event.character, event.keyCode)){ strategy.keyPressed(); } } }); styledText.addListener(SWT.KeyUp, new Listener() { public void handleEvent(Event event) { if (event.character == SWT.TAB) { tabNotPressed = true; task.cancel(); timer.purge(); strategy.tabReleased(); event.doit = false; } } }); styledText.addListener(SWT.FocusOut, new Listener() { public void handleEvent(Event event) { strategy.focusLost(); } }); timer = new Timer(); } private Point completionLocation() { Point location = styledText.getCaret().getLocation(); Control control= styledText.getCaret().getParent(); int caretLineHeight = styledText.getLineHeight(styledText.getCaretOffset()); return control.toDisplay(location.x, location.y + caretLineHeight); } public void setProposals(List<? extends CompletionProposal> proposals) { this.proposals = proposals; String[] strings = new String[proposals.size()]; int i = 0; for (CompletionProposal proposal : proposals) { strings[i++] = proposal.completion(); } proposalsView.setItems(strings); strategy.proposalsCalculated(); } public void cancel() { proposalsProvider.stopCompletion(); proposalsView.unhookArrowKeys(); } public void restart() { proposalsProvider.startCompletionFor(CompletionController.this, styledText.getText(), styledText.getCaretOffset()); } public void start() { proposalsProvider.startCompletionFor(CompletionController.this, styledText.getText(), styledText.getCaretOffset()); } public void complete() { int currentIndex = proposalsView.getSelectionIndex(); if(currentIndex<0 || currentIndex >= proposalsView.getItems().length){ return; } CompletionProposal proposal = proposals.get(currentIndex); int end = styledText.getCaretOffset(); int start = proposalsProvider.findStartOfWord(styledText.getText(), end); String completion = proposal.completion(); styledText.replaceTextRange(start, end - start, completion); styledText.setCaretOffset(start + completion.length()); proposalsView.unhookArrowKeys(); } public void show(DisplayState state) { if(proposalsView.isDisposed()) return; proposalsView.setLocation(completionLocation()); //System.out.println("STATE:", state); proposalsView.show(state); } }