/** * Copyright (c) 2009-2011 Mark Feber, MulgaSoft * * 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 */ package com.mulgasoft.emacsplus.minibuffer; import java.util.Collections; import java.util.List; import java.util.SortedMap; import java.util.TreeMap; import org.eclipse.swt.SWT; import org.eclipse.swt.events.VerifyEvent; import com.mulgasoft.emacsplus.RingBuffer; /** * Read text from the minibuffer, allowing only elements in the list of candidates to be entered. * <SPACE> or <TAB> will provoke completion unless the subclass overrides the behavior * * @author Mark Feber - initial API and implementation */ public abstract class StrictMinibuffer extends CompletionMinibuffer { private List<String> candidates = Collections.emptyList(); // If true, execute immediately if only one completion is found private boolean immediate = false; /** * @param executable */ public StrictMinibuffer(IMinibufferExecutable executable) { super(executable); } public StrictMinibuffer(IMinibufferExecutable executable, List<String> candidates) { this(executable); setCandidates(candidates); } public StrictMinibuffer(IMinibufferExecutable executable, List<String> candidates, boolean immediate) { this(executable, candidates); setImmediate(immediate); } void setImmediate(boolean immediate) { this.immediate = immediate; } boolean isImmediate() { return immediate; } protected void setCandidates(List<String> candidates) { this.candidates = candidates; } protected boolean canBeCandidate(String possible) { return matchCandidate(possible) != null; } protected String matchCandidate(String possible) { String result = null; for (String c : candidates) { if (c.startsWith(possible)) { result = c; break; } } return result; } public SortedMap<String, String> getCompletions() { SortedMap<String, String> result = new TreeMap<String, String>(); for (String s : candidates) { result.put(s, s); } return result; } /** * @see com.mulgasoft.emacsplus.execute.ISelectExecute#execute(java.lang.Object) */ public void execute(Object selection) { String key = (String) selection; setExecuting(true); executeResult(getEditor(), key); leave(true); } /** * Read a string that must be one of the candidate strings * * @see com.mulgasoft.emacsplus.minibuffer.WithMinibuffer#charEvent(org.eclipse.swt.events.VerifyEvent) */ protected void charEvent(VerifyEvent event) { event.doit = false; switch (event.character) { case 0x0D: // CR - execute command (if complete) \r case 0x1B: // ESC - another way to leave case 0x08: // BS case 0x7F: // DEL super.charEvent(event); break; case ' ': // handleCompleting(event); break; case SWT.TAB: if (handlesTab()) { handleCompleting(event); break; } default: if (checkControl(event)) { if (dispatchCtrl(event)) { event.doit = false; break; } } else if (Character.isLetter(event.character) && ((event.stateMask & SWT.MODIFIER_MASK) == 0) && (canBeCandidate(getMBString() + event.character))) { super.charEvent(event); } else { beep(); } } } protected boolean dispatchCtrl(VerifyEvent event) { boolean result = false; switch (event.keyCode) { case 'g': leave(); result = true; break; default: beep(); break; } return result; } protected void handleCompleting(VerifyEvent event) { super.charEvent(event); if (isImmediate() && candidates.contains(getMBString())) { executeCR(event); } } /** * @see com.mulgasoft.emacsplus.minibuffer.HistoryMinibuffer#getHistoryRing() */ @Override protected <T> RingBuffer<T> getHistoryRing() { return null; } /** * @see com.mulgasoft.emacsplus.minibuffer.WithMinibuffer#handlesCtrl() */ @Override protected boolean handlesCtrl() { return true; } /** * @see com.mulgasoft.emacsplus.minibuffer.WithMinibuffer#handlesCtrl() */ @Override protected boolean handlesAlt() { // disable history for strict commands return false; } }