Bug 335985 - Adapt the KeyController model to the e4 model
Preliminary approach using the runtime system only
diff --git a/bundles/org.eclipse.e4.ui.bindings/src/org/eclipse/e4/ui/bindings/EBindingService.java b/bundles/org.eclipse.e4.ui.bindings/src/org/eclipse/e4/ui/bindings/EBindingService.java
index 6462efd..308884b 100644
--- a/bundles/org.eclipse.e4.ui.bindings/src/org/eclipse/e4/ui/bindings/EBindingService.java
+++ b/bundles/org.eclipse.e4.ui.bindings/src/org/eclipse/e4/ui/bindings/EBindingService.java
@@ -19,8 +19,10 @@
 
 	public static final String DIALOG_CONTEXT_ID = "org.eclipse.ui.contexts.dialog"; //$NON-NLS-1$
 
+	// TODO perhaps use a map of attributes for things
+	// that aren't important to the model
 	Binding createBinding(TriggerSequence sequence, ParameterizedCommand command, String schemeId,
-			String contextId);
+			String contextId, String locale, String platform, int bindingType);
 
 	void activateBinding(Binding binding);
 
diff --git a/bundles/org.eclipse.e4.ui.bindings/src/org/eclipse/e4/ui/bindings/internal/BindingCopies.java b/bundles/org.eclipse.e4.ui.bindings/src/org/eclipse/e4/ui/bindings/internal/BindingCopies.java
new file mode 100644
index 0000000..0d38038
--- /dev/null
+++ b/bundles/org.eclipse.e4.ui.bindings/src/org/eclipse/e4/ui/bindings/internal/BindingCopies.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2011 IBM Corporation and others.
+ * 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
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.e4.ui.bindings.internal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import org.eclipse.core.commands.ParameterizedCommand;
+import org.eclipse.jface.bindings.Binding;
+import org.eclipse.jface.bindings.TriggerSequence;
+
+public class BindingCopies {
+
+	private static Collection<Binding> sysBindings;
+	private static Collection<Binding> inactiveSystemBindings;
+	private static Collection<Binding> userBindings;
+
+	public static void init() {
+		inactiveSystemBindings = new ArrayList<Binding>();
+		userBindings = new ArrayList<Binding>();
+	}
+
+	public static void setDefaultSysBindings(Collection<Binding> newSysBindings) {
+		sysBindings = newSysBindings;
+	}
+
+	public static Collection<Binding> getSystemBindings() {
+		return sysBindings;
+	}
+
+	public static void addInactiveSysBinding(Binding binding) {
+		inactiveSystemBindings.add(binding);
+	}
+
+	public static Binding[] getInactiveSysBindings() {
+		return inactiveSystemBindings.toArray(new Binding[inactiveSystemBindings.size()]);
+	}
+
+	public static void removeInactiveSysBinding(Binding binding) {
+		inactiveSystemBindings.remove(binding);
+	}
+
+	public static void addUserBinding(Binding b) {
+		userBindings.add(b);
+	}
+
+	public static Binding[] getUserDefinedBindings() {
+		return userBindings.toArray(new Binding[userBindings.size()]);
+	}
+
+	public static boolean isUserBinding(TriggerSequence sequence, ParameterizedCommand command,
+			String schemeId, String contextId) {
+		boolean isUserBinding = false;
+		Binding currBinding;
+		Iterator<Binding> iter = userBindings.iterator();
+		while (iter.hasNext() && !isUserBinding) {
+			currBinding = iter.next();
+			if (currBinding.getTriggerSequence().equals(sequence)
+					&& currBinding.getParameterizedCommand().equals(command)
+					&& currBinding.getSchemeId().equals(schemeId)
+					&& currBinding.getContextId().equals(contextId)) {
+				isUserBinding = true;
+			}
+		}
+		return isUserBinding;
+	}
+}
diff --git a/bundles/org.eclipse.e4.ui.bindings/src/org/eclipse/e4/ui/bindings/internal/BindingServiceImpl.java b/bundles/org.eclipse.e4.ui.bindings/src/org/eclipse/e4/ui/bindings/internal/BindingServiceImpl.java
index 183a956..6f60e53 100644
--- a/bundles/org.eclipse.e4.ui.bindings/src/org/eclipse/e4/ui/bindings/internal/BindingServiceImpl.java
+++ b/bundles/org.eclipse.e4.ui.bindings/src/org/eclipse/e4/ui/bindings/internal/BindingServiceImpl.java
@@ -53,9 +53,10 @@
 	 * java.lang.String)
 	 */
 	public Binding createBinding(TriggerSequence sequence, ParameterizedCommand command,
-			String schemeId, String contextId) {
-		return new KeyBinding((KeySequence) sequence, command, schemeId, contextId, null, null,
-				null, Binding.SYSTEM);
+			String schemeId, String contextId, String locale, String platform, int bindingType) {
+
+		return new KeyBinding((KeySequence) sequence, command, schemeId, contextId, locale,
+				platform, null, bindingType);
 	}
 
 	/*
@@ -70,7 +71,6 @@
 		BindingTable table = manager.getTable(contextId);
 		if (table == null) {
 			System.err.println("No binding table for " + contextId); //$NON-NLS-1$
-			return;
 		}
 		table.addBinding(binding);
 	}
@@ -87,7 +87,6 @@
 		BindingTable table = manager.getTable(contextId);
 		if (table == null) {
 			System.err.println("No binding table for " + contextId); //$NON-NLS-1$
-			return;
 		}
 		table.removeBinding(binding);
 	}
@@ -201,4 +200,5 @@
 		}
 		contextSet = manager.createContextSet(contexts);
 	}
+
 }
diff --git a/bundles/org.eclipse.e4.ui.bindings/src/org/eclipse/e4/ui/bindings/internal/BindingTable.java b/bundles/org.eclipse.e4.ui.bindings/src/org/eclipse/e4/ui/bindings/internal/BindingTable.java
index db071c1..d4491f3 100644
--- a/bundles/org.eclipse.e4.ui.bindings/src/org/eclipse/e4/ui/bindings/internal/BindingTable.java
+++ b/bundles/org.eclipse.e4.ui.bindings/src/org/eclipse/e4/ui/bindings/internal/BindingTable.java
@@ -86,7 +86,6 @@
 	};
 
 	private Context tableId;
-
 	private ArrayList<Binding> bindings = new ArrayList<Binding>();
 	private Map<TriggerSequence, Binding> bindingsByTrigger = new HashMap<TriggerSequence, Binding>();
 	private Map<ParameterizedCommand, ArrayList<Binding>> bindingsByCommand = new HashMap<ParameterizedCommand, ArrayList<Binding>>();
@@ -143,7 +142,10 @@
 		bindings.remove(binding);
 		bindingsByTrigger.remove(binding.getTriggerSequence());
 		ArrayList<Binding> sequences = bindingsByCommand.get(binding.getParameterizedCommand());
-		sequences.remove(binding);
+
+		if (sequences != null) {
+			sequences.remove(binding);
+		}
 		TriggerSequence[] prefs = binding.getTriggerSequence().getPrefixes();
 		for (int i = 1; i < prefs.length; i++) {
 			ArrayList<Binding> bindings = bindingsByPrefix.get(prefs[i]);
@@ -176,4 +178,8 @@
 		return bindingsByPrefix.get(seq) != null;
 	}
 
+	public Collection<Binding> getBindings() {
+		return Collections.unmodifiableCollection(bindings);
+	}
+
 }
diff --git a/bundles/org.eclipse.e4.ui.bindings/src/org/eclipse/e4/ui/bindings/internal/BindingTableManager.java b/bundles/org.eclipse.e4.ui.bindings/src/org/eclipse/e4/ui/bindings/internal/BindingTableManager.java
index 951e834..d5e3b2e 100644
--- a/bundles/org.eclipse.e4.ui.bindings/src/org/eclipse/e4/ui/bindings/internal/BindingTableManager.java
+++ b/bundles/org.eclipse.e4.ui.bindings/src/org/eclipse/e4/ui/bindings/internal/BindingTableManager.java
@@ -11,16 +11,16 @@
 
 package org.eclipse.e4.ui.bindings.internal;
 
-import org.eclipse.e4.core.contexts.IEclipseContext;
-
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.ListIterator;
 import javax.inject.Inject;
 import org.eclipse.core.commands.ParameterizedCommand;
 import org.eclipse.core.commands.contexts.Context;
+import org.eclipse.e4.core.contexts.IEclipseContext;
 import org.eclipse.jface.bindings.Binding;
 import org.eclipse.jface.bindings.TriggerSequence;
 
@@ -31,6 +31,8 @@
 	@Inject
 	private IEclipseContext eclipseContext;
 
+	HashSet<String> definedTables = new HashSet<String>();
+
 	public void addTable(BindingTable table) {
 		String contextId = table.getId();
 		if (eclipseContext.containsKey(contextId)) {
@@ -38,6 +40,7 @@
 			//			throw new IllegalArgumentException("Already contains table " + contextId); //$NON-NLS-1$
 		}
 		eclipseContext.set(contextId, table);
+		definedTables.add(contextId);
 	}
 
 	public void removeTable(BindingTable table) {
@@ -46,12 +49,26 @@
 			throw new IllegalArgumentException("Does not contains table " + contextId); //$NON-NLS-1$
 		}
 		eclipseContext.remove(contextId);
+		definedTables.remove(contextId);
 	}
 
 	public BindingTable getTable(String id) {
 		return (BindingTable) eclipseContext.get(id);
 	}
 
+	// we're just going through each binding table, and returning a
+	// flat list of bindings here
+	public Collection<Binding> getActiveBindings() {
+		ArrayList<Binding> bindings = new ArrayList<Binding>();
+		for (String id : definedTables) {
+			Object obj = eclipseContext.get(id);
+			if (obj instanceof BindingTable) {
+				bindings.addAll(((BindingTable) obj).getBindings());
+			}
+		}
+		return bindings;
+	}
+
 	public ContextSet createContextSet(Collection<Context> contexts) {
 		return new ContextSet(contexts);
 	}
diff --git a/bundles/org.eclipse.e4.ui.workbench.swt/src/org/eclipse/e4/ui/workbench/swt/util/BindingProcessingAddon.java b/bundles/org.eclipse.e4.ui.workbench.swt/src/org/eclipse/e4/ui/workbench/swt/util/BindingProcessingAddon.java
index 820ab50..5259784 100644
--- a/bundles/org.eclipse.e4.ui.workbench.swt/src/org/eclipse/e4/ui/workbench/swt/util/BindingProcessingAddon.java
+++ b/bundles/org.eclipse.e4.ui.workbench.swt/src/org/eclipse/e4/ui/workbench/swt/util/BindingProcessingAddon.java
@@ -11,6 +11,10 @@
 
 package org.eclipse.e4.ui.workbench.swt.util;
 
+import org.eclipse.e4.ui.model.application.commands.MBindingContext;
+
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -26,6 +30,7 @@
 import org.eclipse.e4.core.contexts.IEclipseContext;
 import org.eclipse.e4.core.services.events.IEventBroker;
 import org.eclipse.e4.ui.bindings.EBindingService;
+import org.eclipse.e4.ui.bindings.internal.BindingCopies;
 import org.eclipse.e4.ui.bindings.internal.BindingTable;
 import org.eclipse.e4.ui.bindings.internal.BindingTableManager;
 import org.eclipse.e4.ui.internal.workbench.Activator;
@@ -79,6 +84,7 @@
 	@PostConstruct
 	public void init() {
 		defineBindingTables();
+		cleanTables();
 		activateContexts(application);
 		registerModelListeners();
 	}
@@ -107,6 +113,71 @@
 		}
 	}
 
+	// goes through the entire active bindings list and replaces SYSTEM
+	// bindings with any USER bindings that were persisted
+	private void cleanTables() {
+		ArrayList<Binding> dirtyBindings = new ArrayList<Binding>();
+		Binding dirtyBinding;
+
+		Binding[] userBindings = BindingCopies.getUserDefinedBindings();
+		Binding curr;
+
+		// go through all USER bindings and check if there is an "equal" SYSTEM
+		// binding
+		for (int i = 0; i < userBindings.length; i++) {
+			curr = userBindings[i];
+
+			// they should all be USER bindings, but double check anyway
+			if (curr.getType() == Binding.USER) {
+				dirtyBinding = checkDirty(curr);
+
+				// if the SYSTEM binding is marked as dirty, then throw it in a
+				// list and we'll remove it later
+				if (dirtyBinding != null) {
+					dirtyBindings.add(dirtyBinding);
+				}
+			}
+		}
+
+		//System.out.println("@@@ dirty bindings -> " + dirtyBindings.size());
+
+		// go through the list of bindings that are marked as dirty (if any) and
+		// remove them from the BindingTableManager
+		for (int i = 0; i < dirtyBindings.size(); i++) {
+			dirtyBinding = dirtyBindings.get(i);
+			bindingTables.getTable(dirtyBinding.getContextId()).removeBinding(
+					dirtyBinding);
+		}
+	}
+
+	private Binding checkDirty(Binding b) {
+		Collection<Binding> activeBindings = bindingTables.getActiveBindings();
+		Iterator<Binding> iter = activeBindings.iterator();
+		Binding curr;
+		Binding dirtyBinding = null;
+
+		while (iter.hasNext() && dirtyBinding == null) {
+			curr = iter.next();
+
+			// make sure we're only comparing SYSTEM bindings so that we don't
+			// remove the wrong ones, and make sure the bindings we're comparing
+			// actually have a command
+			if (curr.getType() == Binding.SYSTEM
+					&& curr.getParameterizedCommand() != null
+					&& b.getParameterizedCommand() != null) {
+				if (curr.getContextId().equals(b.getContextId())
+						&& curr.getParameterizedCommand().equals(
+								b.getParameterizedCommand())
+						&& curr.getSchemeId().equals(b.getSchemeId())) {
+
+					// mark this binding as dirty, and it will be removed
+					dirtyBinding = curr;
+				}
+			}
+		}
+		return dirtyBinding;
+	}
+
 	private void defineBindingTables() {
 		Activator.trace(Policy.DEBUG_CMDS,
 				"Initialize binding tables from model", null); //$NON-NLS-1$
@@ -142,6 +213,7 @@
 				binding.getCommand(), binding.getParameters(),
 				binding.getKeySequence(), binding);
 		if (keyBinding != null) {
+			// if (keyBinding.getType() == Binding.USER)
 			bindingTable.addBinding(keyBinding);
 		}
 	}
@@ -168,11 +240,22 @@
 			System.err.println("Failed to handle binding: " + binding); //$NON-NLS-1$
 		} else {
 			try {
+				int bindingType = Binding.SYSTEM;
+
+				// go thru the copied list of USER defined bindings to see if
+				// this particular binding being created matches any of them
+				if (BindingCopies.isUserBinding(sequence, cmd,
+						"org.eclipse.ui.defaultAcceleratorConfiguration",
+						bindingContext.getId())) {
+					bindingType = Binding.USER;
+				}
+
+				// TODO: NEED TO CHANGE THIS!!!
 				keyBinding = bindingService
 						.createBinding(
 								sequence,
 								cmd,
-								"org.eclipse.ui.defaultAcceleratorConfiguration", bindingContext.getId()); //$NON-NLS-1$
+								"org.eclipse.ui.defaultAcceleratorConfiguration", bindingContext.getId(), null, null, bindingType); //$NON-NLS-1$
 			} catch (IllegalArgumentException e) {
 				Activator.trace(Policy.DEBUG_MENUS,
 						"failed to create: " + binding, e); //$NON-NLS-1$
diff --git a/tests/org.eclipse.e4.ui.bindings.tests/src/org/eclipse/e4/ui/bindings/tests/BindingLookupTest.java b/tests/org.eclipse.e4.ui.bindings.tests/src/org/eclipse/e4/ui/bindings/tests/BindingLookupTest.java
index ecad5cf..633fba6 100644
--- a/tests/org.eclipse.e4.ui.bindings.tests/src/org/eclipse/e4/ui/bindings/tests/BindingLookupTest.java
+++ b/tests/org.eclipse.e4.ui.bindings.tests/src/org/eclipse/e4/ui/bindings/tests/BindingLookupTest.java
@@ -473,9 +473,8 @@
 	private Binding createDefaultBinding(EBindingService bs,
 			TriggerSequence sequence, ParameterizedCommand command,
 			String contextId) {
-		return bs.createBinding(sequence, command,
-				"org.eclipse.ui.defaultAcceleratorConfiguration", //$NON-NLS-1$
-				contextId);
+		return bs.createBinding(sequence, command, "org.eclipse.ui.defaultAcceleratorConfiguration",
+				contextId, null, null, Binding.SYSTEM);
 	}
 
 }
diff --git a/tests/org.eclipse.e4.ui.bindings.tests/src/org/eclipse/e4/ui/bindings/tests/KeyDispatcherTest.java b/tests/org.eclipse.e4.ui.bindings.tests/src/org/eclipse/e4/ui/bindings/tests/KeyDispatcherTest.java
index 2dd2422..65d9279 100644
--- a/tests/org.eclipse.e4.ui.bindings.tests/src/org/eclipse/e4/ui/bindings/tests/KeyDispatcherTest.java
+++ b/tests/org.eclipse.e4.ui.bindings.tests/src/org/eclipse/e4/ui/bindings/tests/KeyDispatcherTest.java
@@ -98,9 +98,7 @@
 
 	private Binding createDefaultBinding(EBindingService bs,
 			TriggerSequence sequence, ParameterizedCommand command) {
-		return bs.createBinding(sequence, command,
-				"org.eclipse.ui.defaultAcceleratorConfiguration", //$NON-NLS-1$
-				ID_WINDOW);
+		return bs.createBinding(sequence, command, "org.eclipse.ui.defaultAcceleratorConfiguration", ID_WINDOW, null, null, Binding.SYSTEM);
 	}
 
 	@Override