1 /******************************************************************************* 2 * Copyright (c) 2010, 2015 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 ******************************************************************************/ 14 15 package org.eclipse.e4.ui.bindings.internal; 16 17 import java.util.ArrayList; 18 import java.util.Collection; 19 import java.util.List; 20 import java.util.ListIterator; 21 import javax.inject.Inject; 22 import org.eclipse.core.commands.ParameterizedCommand; 23 import org.eclipse.core.commands.contexts.Context; 24 import org.eclipse.e4.core.contexts.IEclipseContext; 25 import org.eclipse.jface.bindings.Binding; 26 import org.eclipse.jface.bindings.TriggerSequence; 27 28 /** 29 * manage tables of bindings that can be used to look up commands from keys. 30 */ 31 public class BindingTableManager { 32 private static final String BINDING_TABLE_PREFIX = "bindingTable:"; //$NON-NLS-1$ 33 34 @Inject 35 private IEclipseContext eclipseContext; 36 37 private ContextSet definedTables = ContextSet.EMPTY; 38 39 private String[] activeSchemeIds; 40 addTable(BindingTable table)41 public void addTable(BindingTable table) { 42 String contextId = getTableId(table.getId()); 43 if (eclipseContext.containsKey(contextId)) { 44 return; // it's already there 45 // throw new IllegalArgumentException("Already contains table " + contextId); //$NON-NLS-1$ 46 } 47 eclipseContext.set(contextId, table); 48 final List<Context> contexts = definedTables.getContexts(); 49 if (!contexts.contains(table.getTableId())) { 50 // this is only valid because I'm throwing away the old definedTables contextSet 51 contexts.add(table.getTableId()); 52 definedTables = createContextSet(contexts); 53 } 54 } 55 getTableId(String id)56 private String getTableId(String id) { 57 return BINDING_TABLE_PREFIX + id; 58 } 59 removeTable(BindingTable table)60 public void removeTable(BindingTable table) { 61 String contextId = getTableId(table.getId()); 62 if (!eclipseContext.containsKey(contextId)) { 63 throw new IllegalArgumentException("Does not contains table " + contextId); //$NON-NLS-1$ 64 } 65 eclipseContext.remove(contextId); 66 final List<Context> contexts = definedTables.getContexts(); 67 if (contexts.contains(table.getTableId())) { 68 // this is only valid because I'm throwing away the old definedTables contextSet 69 contexts.remove(table.getTableId()); 70 definedTables = createContextSet(contexts); 71 } 72 } 73 getTable(String id)74 public BindingTable getTable(String id) { 75 return (BindingTable) eclipseContext.get(getTableId(id)); 76 } 77 78 // we're just going through each binding table, and returning a 79 // flat list of bindings here getActiveBindings()80 public Collection<Binding> getActiveBindings() { 81 ArrayList<Binding> bindings = new ArrayList<>(); 82 for (Context ctx : definedTables.getContexts()) { 83 BindingTable table = getTable(ctx.getId()); 84 if (table != null) { 85 bindings.addAll(table.getBindings()); 86 } 87 } 88 return bindings; 89 } 90 createContextSet(Collection<Context> contexts)91 public ContextSet createContextSet(Collection<Context> contexts) { 92 return new ContextSet(contexts); 93 } 94 getConflictsFor(ContextSet contextSet, TriggerSequence triggerSequence)95 public Collection<Binding> getConflictsFor(ContextSet contextSet, 96 TriggerSequence triggerSequence) { 97 Collection<Binding> matches = new ArrayList<>(); 98 for (Context ctx : contextSet.getContexts()) { 99 BindingTable table = getTable(ctx.getId()); 100 if (table != null) { 101 final Collection<Binding> matchesFor = table.getConflictsFor(triggerSequence); 102 if (matchesFor != null) { 103 matches.addAll(matchesFor); 104 } 105 } 106 } 107 return matches.isEmpty() ? null : matches; 108 } 109 getAllConflicts()110 public Collection<Binding> getAllConflicts() { 111 Collection<Binding> conflictsList = new ArrayList<>(); 112 for (Context ctx : definedTables.getContexts()) { 113 BindingTable table = getTable(ctx.getId()); 114 if (table != null) { 115 Collection<Binding> conflictsInTable = table.getConflicts(); 116 if (conflictsInTable != null) { 117 conflictsList.addAll(conflictsInTable); 118 } 119 } 120 } 121 return conflictsList; 122 } 123 getPerfectMatch(ContextSet contextSet, TriggerSequence triggerSequence)124 public Binding getPerfectMatch(ContextSet contextSet, TriggerSequence triggerSequence) { 125 Binding result = null; 126 Binding currentResult = null; 127 List<Context> contexts = contextSet.getContexts(); 128 ListIterator<Context> it = contexts.listIterator(contexts.size()); 129 while (it.hasPrevious()) { 130 Context c = it.previous(); 131 BindingTable table = getTable(c.getId()); 132 if (table != null) { 133 currentResult = table.getPerfectMatch(triggerSequence); 134 } 135 if (currentResult != null) { 136 if (isMostActiveScheme(currentResult)) { 137 return currentResult; 138 } 139 if (result == null) { 140 result = currentResult; 141 } else { 142 int rc = compareSchemes(result.getSchemeId(), currentResult.getSchemeId()); 143 if (rc > 0) { 144 result = currentResult; 145 } 146 } 147 } 148 } 149 return result; 150 } 151 152 /** 153 * @param currentResult 154 * @return 155 */ isMostActiveScheme(Binding currentResult)156 private boolean isMostActiveScheme(Binding currentResult) { 157 if (activeSchemeIds == null || activeSchemeIds.length < 2) { 158 return true; 159 } 160 final String mostActive = activeSchemeIds[0]; 161 return mostActive == null ? false : mostActive.equals(currentResult.getSchemeId()); 162 } 163 getBestSequenceFor(ContextSet contextSet, ParameterizedCommand parameterizedCommand)164 public Binding getBestSequenceFor(ContextSet contextSet, 165 ParameterizedCommand parameterizedCommand) { 166 ArrayList<Binding> bindings = (ArrayList<Binding>) getSequencesFor(contextSet, 167 parameterizedCommand); 168 if (bindings.isEmpty()) { 169 return null; 170 } 171 return bindings.get(0); 172 } 173 getSequencesFor(ContextSet contextSet, ParameterizedCommand parameterizedCommand)174 public Collection<Binding> getSequencesFor(ContextSet contextSet, 175 ParameterizedCommand parameterizedCommand) { 176 ArrayList<Binding> bindings = new ArrayList<>(); 177 List<Context> contexts = contextSet.getContexts(); 178 ListIterator<Context> it = contexts.listIterator(contexts.size()); 179 while (it.hasPrevious()) { 180 Context c = it.previous(); 181 BindingTable table = getTable(c.getId()); 182 if (table != null) { 183 Collection<Binding> sequences = table.getSequencesFor(parameterizedCommand); 184 if (sequences != null) { 185 bindings.addAll(sequences); 186 } 187 } 188 } 189 bindings.sort(BindingTable.BEST_SEQUENCE); 190 return bindings; 191 } 192 getBindingsFor(ContextSet contextSet, ParameterizedCommand cmd)193 public Collection<Binding> getBindingsFor(ContextSet contextSet, ParameterizedCommand cmd) { 194 Collection<Binding> bindings = new ArrayList<>(); 195 for (Context ctx : contextSet.getContexts()) { 196 BindingTable table = getTable(ctx.getId()); 197 if (table != null) { 198 Collection<Binding> matches = table.getSequencesFor(cmd); 199 if (matches != null) { 200 bindings.addAll(matches); 201 } 202 } 203 } 204 return bindings; 205 } 206 isPartialMatch(ContextSet contextSet, TriggerSequence sequence)207 public boolean isPartialMatch(ContextSet contextSet, TriggerSequence sequence) { 208 List<Context> contexts = contextSet.getContexts(); 209 ListIterator<Context> it = contexts.listIterator(contexts.size()); 210 while (it.hasPrevious()) { 211 Context c = it.previous(); 212 BindingTable table = getTable(c.getId()); 213 if (table != null) { 214 if (table.isPartialMatch(sequence)) { 215 return true; 216 } 217 } 218 } 219 return false; 220 } 221 getPartialMatches(ContextSet contextSet, TriggerSequence sequence)222 public Collection<Binding> getPartialMatches(ContextSet contextSet, TriggerSequence sequence) { 223 ArrayList<Binding> bindings = new ArrayList<>(); 224 List<Context> contexts = contextSet.getContexts(); 225 ListIterator<Context> it = contexts.listIterator(contexts.size()); 226 while (it.hasPrevious()) { 227 Context c = it.previous(); 228 BindingTable table = getTable(c.getId()); 229 if (table != null) { 230 Collection<Binding> partialMatches = table.getPartialMatches(sequence); 231 if (partialMatches != null) { 232 bindings.addAll(partialMatches); 233 } 234 } 235 } 236 return bindings; 237 } 238 239 /** 240 * @param activeSchemeIds 241 */ setActiveSchemes(String[] activeSchemeIds)242 public void setActiveSchemes(String[] activeSchemeIds) { 243 this.activeSchemeIds = activeSchemeIds; 244 BindingTable.BEST_SEQUENCE.setActiveSchemes(activeSchemeIds); 245 } 246 247 /* 248 * Copied from org.eclipse.jface.bindings.BindingManager.compareSchemes(String, String) 249 * 250 * Returns an in based on scheme 1 < scheme 2 251 */ compareSchemes(final String schemeId1, final String schemeId2)252 private final int compareSchemes(final String schemeId1, final String schemeId2) { 253 if (activeSchemeIds == null) { 254 return 0; 255 } 256 if (!schemeId2.equals(schemeId1)) { 257 for (final String schemePointer : activeSchemeIds) { 258 if (schemeId2.equals(schemePointer)) { 259 return 1; 260 } else if (schemeId1.equals(schemePointer)) { 261 return -1; 262 } 263 } 264 } 265 return 0; 266 } 267 } 268