Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 43613
Collapse All | Expand All

(-)Eclipse UI/org/eclipse/ui/internal/Workbench.java (-63 / +82 lines)
Lines 208-219 Link Here
208
				if (shell.getParent() != null)
208
				if (shell.getParent() != null)
209
					return;
209
					return;
210
			}
210
			}
211
			
212
			KeyStroke[] keyStrokes = new KeyStroke[3];
213
			keyStrokes[0] = KeySupport.convertAcceleratorToKeyStroke(KeySupport.convertEventToUnmodifiedAccelerator(event));
214
			keyStrokes[1] = KeySupport.convertAcceleratorToKeyStroke(KeySupport.convertEventToUnshiftedModifiedAccelerator(event));
215
			keyStrokes[2] = KeySupport.convertAcceleratorToKeyStroke(KeySupport.convertEventToModifiedAccelerator(event));
211
216
212
			KeyStroke keyStroke =
217
			if (press(keyStrokes, event)) {
213
				KeySupport.convertAcceleratorToKeyStroke(
214
					KeySupport.convertEventToAccelerator(event));
215
216
			if (press(keyStroke, event)) {
217
				switch (event.type) {
218
				switch (event.type) {
218
					case SWT.KeyDown :
219
					case SWT.KeyDown :
219
						event.doit = false;
220
						event.doit = false;
Lines 413-481 Link Here
413
		return true;
414
		return true;
414
	}
415
	}
415
416
416
	// TODO move this method to CommandManager once getMode() is added to ICommandManager (and triggers and change event)
417
	/**
417
	// TODO remove event parameter once key-modified actions are removed
418
	 * Processes a key press with respect to the key binding architecture.  This
418
	public final boolean press(KeyStroke keyStroke, Event event) {
419
	 * updates the mode of the command manager, and runs the current handler for
419
		final KeySequence modeBeforeKeyStroke = commandManager.getMode();
420
	 * the command that matches the key sequence, if any.
420
		final List keyStrokes =
421
	 * 
421
			new ArrayList(modeBeforeKeyStroke.getKeyStrokes());
422
	 * @param potentialKeyStrokes The key strokes that could potentially match,
422
		keyStrokes.add(keyStroke);
423
	 * in the order of priority; must not be <code>null</code>.
423
		final KeySequence modeAfterKeyStroke =
424
	 * @param event The event to pass to the action; may be <code>null</code>.
424
			KeySequence.getInstance(keyStrokes);
425
	 * 
425
		final Map matchesByKeySequenceForModeBeforeKeyStroke =
426
	 * @return <code>true</code> if a command is executed; <code>false</code>
426
			commandManager.getMatchesByKeySequenceForMode();
427
	 * otherwise.
427
		commandManager.setMode(modeAfterKeyStroke);
428
	 */
428
		final Map matchesByKeySequenceForModeAfterKeyStroke =
429
	public boolean press(KeyStroke[] potentialKeyStrokes, Event event) {
429
			commandManager.getMatchesByKeySequenceForMode();
430
		// TODO move this method to CommandManager once getMode() is added to ICommandManager (and triggers and change event)
430
		boolean consumeKeyStroke = false;
431
		// TODO remove event parameter once key-modified actions are removed
431
432
		
432
		if (!matchesByKeySequenceForModeAfterKeyStroke.isEmpty()) {
433
		// Check every potential key stroke until one matches.
433
			// this key stroke is part of one or more possible completions: consume the keystroke
434
		for (int i = 0; i < potentialKeyStrokes.length; i++) {
434
			updateModeLines(modeAfterKeyStroke);
435
			KeySequence modeBeforeKeyStroke = commandManager.getMode();
435
			consumeKeyStroke = true;
436
			List keyStrokes = new ArrayList(modeBeforeKeyStroke.getKeyStrokes());
436
		} else {
437
			keyStrokes.add(potentialKeyStrokes[i]);
437
			// there are no possible longer multi-stroke sequences, allow a completion now if possible
438
			KeySequence modeAfterKeyStroke = KeySequence.getInstance(keyStrokes);
438
			final Match match =
439
			Map matchesByKeySequenceForModeBeforeKeyStroke = commandManager.getMatchesByKeySequenceForMode();
439
				(Match) matchesByKeySequenceForModeBeforeKeyStroke.get(
440
			commandManager.setMode(modeAfterKeyStroke);
440
					modeAfterKeyStroke);
441
			Map matchesByKeySequenceForModeAfterKeyStroke = commandManager.getMatchesByKeySequenceForMode();
441
442
			boolean consumeKeyStroke = false;
442
			if (match != null) {
443
443
				// a completion was found. 
444
			if (!matchesByKeySequenceForModeAfterKeyStroke.isEmpty()) {
444
				final String commandId = match.getCommandId();
445
				// this key stroke is part of one or more possible completions: consume the keystroke
445
				final Map actionsById = commandManager.getActionsById();
446
				updateModeLines(modeAfterKeyStroke);
446
				org.eclipse.ui.commands.IAction action =
447
				consumeKeyStroke = true;
447
					(org.eclipse.ui.commands.IAction) actionsById.get(
448
			} else {
448
						commandId);
449
				// there are no possible longer multi-stroke sequences, allow a completion now if possible
449
450
				Match match = (Match) matchesByKeySequenceForModeBeforeKeyStroke.get(modeAfterKeyStroke);
450
				if (action != null) {
451
451
					// an action was found corresponding to the completion
452
				if (match != null) {
452
453
					// a completion was found. 
453
					if (action.isEnabled()) {
454
					String commandId = match.getCommandId();
454
						updateModeLines(modeAfterKeyStroke);
455
					Map actionsById = commandManager.getActionsById();
455
						try {
456
					org.eclipse.ui.commands.IAction action = (org.eclipse.ui.commands.IAction) actionsById.get(commandId);
456
							action.execute(event);
457
457
						} catch (final Exception e) {
458
					if (action != null) {
458
							// TODO 						
459
						// an action was found corresponding to the completion
460
461
						if (action.isEnabled()) {
462
							updateModeLines(modeAfterKeyStroke);
463
							try {
464
								action.execute(event);
465
							} catch (Exception e) {
466
								// TODO 						
467
							}
459
						}
468
						}
469
	
470
						// consume the keystroke
471
						consumeKeyStroke = true;
460
					}
472
					}
461
462
					// consume the keystroke
463
					consumeKeyStroke = true;
464
				}
473
				}
474
	
475
				// possibly no completion was found, or no action was found corresponding to the completion, but if we were already in a mode consume the keystroke anyway.									
476
				if (modeBeforeKeyStroke.getKeyStrokes().size() >= 1)
477
					consumeKeyStroke = true;
478
	
479
				// clear mode			
480
				commandManager.setMode(KeySequence.getInstance());
481
				updateModeLines(KeySequence.getInstance());
482
			}
483
	
484
			// TODO is this necessary?		
485
			updateActiveContextIds();
486
			
487
			if (consumeKeyStroke) {
488
				// We found a match, so stop now.
489
				return consumeKeyStroke;
490
			} else {
491
				// Restore the mode, so we can try again.
492
				commandManager.setMode(modeBeforeKeyStroke);
465
			}
493
			}
466
467
			// possibly no completion was found, or no action was found corresponding to the completion, but if we were already in a mode consume the keystroke anyway.									
468
			if (modeBeforeKeyStroke.getKeyStrokes().size() >= 1)
469
				consumeKeyStroke = true;
470
471
			// clear mode			
472
			commandManager.setMode(KeySequence.getInstance());
473
			updateModeLines(KeySequence.getInstance());
474
		}
494
		}
475
495
		
476
		// TODO is this necessary?		
496
		// No key strokes match.
477
		updateActiveContextIds();
497
		return false;
478
		return consumeKeyStroke;
479
	}	
498
	}	
480
	
499
	
481
	/**
500
	/**
(-)Eclipse UI/org/eclipse/ui/internal/commands/KeysPreferencePage.java (-1 / +1 lines)
Lines 1320-1326 Link Here
1320
	}
1320
	}
1321
1321
1322
	private void setKeySequence(KeySequence keySequence) {
1322
	private void setKeySequence(KeySequence keySequence) {
1323
        textKeySequence.setKeySequence(keySequence, null);
1323
        textKeySequence.setKeySequence(keySequence);
1324
	}
1324
	}
1325
1325
1326
	private void update() {
1326
	private void update() {
(-)Eclipse UI/org/eclipse/ui/internal/keys/KeySequenceText.java (-209 / +148 lines)
Lines 16-34 Link Here
16
import java.util.List;
16
import java.util.List;
17
17
18
import org.eclipse.swt.SWT;
18
import org.eclipse.swt.SWT;
19
import org.eclipse.swt.events.FocusEvent;
20
import org.eclipse.swt.events.FocusListener;
19
import org.eclipse.swt.events.ModifyEvent;
21
import org.eclipse.swt.events.ModifyEvent;
20
import org.eclipse.swt.events.ModifyListener;
22
import org.eclipse.swt.events.ModifyListener;
21
import org.eclipse.swt.events.TraverseEvent;
22
import org.eclipse.swt.events.TraverseListener;
23
import org.eclipse.swt.graphics.Font;
23
import org.eclipse.swt.graphics.Font;
24
import org.eclipse.swt.widgets.Composite;
24
import org.eclipse.swt.widgets.Composite;
25
import org.eclipse.swt.widgets.Display;
25
import org.eclipse.swt.widgets.Event;
26
import org.eclipse.swt.widgets.Event;
26
import org.eclipse.swt.widgets.Listener;
27
import org.eclipse.swt.widgets.Listener;
27
import org.eclipse.swt.widgets.Text;
28
import org.eclipse.swt.widgets.Text;
28
import org.eclipse.ui.keys.CharacterKey;
29
import org.eclipse.ui.keys.KeySequence;
29
import org.eclipse.ui.keys.KeySequence;
30
import org.eclipse.ui.keys.KeyStroke;
30
import org.eclipse.ui.keys.KeyStroke;
31
import org.eclipse.ui.keys.NaturalKey;
32
import org.eclipse.ui.keys.ParseException;
31
import org.eclipse.ui.keys.ParseException;
33
32
34
/**
33
/**
Lines 53-60 Link Here
53
	private KeySequence keySequence = KeySequence.getInstance();
52
	private KeySequence keySequence = KeySequence.getInstance();
54
	/** The maximum number of key strokes permitted in the sequence. */
53
	/** The maximum number of key strokes permitted in the sequence. */
55
	private int maxStrokes = INFINITE;
54
	private int maxStrokes = INFINITE;
56
	/** The incomplete key stroke, if any. */
57
	private KeyStroke temporaryStroke = null;
58
	/** The text widget that is wrapped for this class. */
55
	/** The text widget that is wrapped for this class. */
59
	private final Text text;
56
	private final Text text;
60
	/** The listener that makes sure that the text widget remains up-to-date
57
	/** The listener that makes sure that the text widget remains up-to-date
Lines 92-105 Link Here
92
		final Listener keyFilter = new KeyTrapListener();
89
		final Listener keyFilter = new KeyTrapListener();
93
		text.addListener(SWT.KeyUp, keyFilter);
90
		text.addListener(SWT.KeyUp, keyFilter);
94
		text.addListener(SWT.KeyDown, keyFilter);
91
		text.addListener(SWT.KeyDown, keyFilter);
95
92
		
96
		// Add the traversal listener.
93
		// Add the focus listener that attaches the global traversal filter.
97
		text.addTraverseListener(new FocusTrapListener());
94
		text.addFocusListener(new TraversalFilterManager());
98
95
99
		// Add an internal modify listener.
96
		// Add an internal modify listener.
100
		text.addModifyListener(updateSequenceListener);
97
		text.addModifyListener(updateSequenceListener);
101
	}
98
	}
102
99
100
	/**
101
	 * Changes the font on the underlying text widget.
102
	 * @param font The new font.
103
	 */
103
	public void setFont(Font font) {
104
	public void setFont(Font font) {
104
		text.setFont(font);
105
		text.setFont(font);
105
	}
106
	}
Lines 117-124 Link Here
117
	 * Clears the text field and resets all the internal values.
118
	 * Clears the text field and resets all the internal values.
118
	 */
119
	 */
119
	public final void clear() {
120
	public final void clear() {
120
		keySequence = null;
121
		keySequence = KeySequence.getInstance();
121
		temporaryStroke = null;
122
		text.setText(EMPTY_STRING);
122
		text.setText(EMPTY_STRING);
123
	}
123
	}
124
124
Lines 131-152 Link Here
131
		return keySequence;
131
		return keySequence;
132
	}
132
	}
133
	
133
	
134
	/**
135
	 * An accessor for the <code>KeyStroke</code> that is currently held as an
136
	 * incomplete stroke (i.e., one without a natural key).
137
	 * @return The incomplete stroke; may be <code>null</code> if there are no
138
	 * incomplete strokes.
139
	 */
140
	final KeyStroke getTemporaryStroke() {
141
		return temporaryStroke;
142
	}
143
	
144
	/** 
134
	/** 
145
	 * An accessor for the underlying text widget used by this entry field.
135
	 * An accessor for the underlying text widget's contents.
146
	 * @return The <code>Text</code> instance; never <code>null</code>.
136
	 * @return The text contents of this entry; never <code>null</code>.
147
	 */
137
	 */
148
	final Text getText() {
138
	final String getText() {
149
		return text;
139
		return text.getText();
150
	}
140
	}
151
141
152
	/**
142
	/**
Lines 155-182 Link Here
155
	 * <code>false</code> otherwise.
145
	 * <code>false</code> otherwise.
156
	 */
146
	 */
157
	public final boolean hasIncompleteStroke() {
147
	public final boolean hasIncompleteStroke() {
158
		return (temporaryStroke != null);
148
		return !keySequence.isComplete();
159
	}
160
161
	/**
162
	 * Checks whether the given key stroke is a temporary key stroke or not.
163
	 * @param keyStroke The key stroke to check for completion; may be
164
	 * <code>null</code>, which results in <code>false</code>.
165
	 * @return <code>true</code> if the key stroke has no natural key; 
166
	 * <code>false</code> otherwise.
167
	 */
168
	static final boolean isComplete(final KeyStroke keyStroke) {
169
		if (keyStroke != null) {
170
			final NaturalKey naturalKey = keyStroke.getNaturalKey();
171
172
			if (naturalKey instanceof CharacterKey) {
173
				final CharacterKey characterKey = (CharacterKey) naturalKey;
174
				return (characterKey.getCharacter() != '\0');
175
			} else
176
				return true;
177
		}
178
179
		return false;
180
	}
149
	}
181
150
182
	/**
151
	/**
Lines 189-198 Link Here
189
158
190
	/**
159
	/**
191
	 * <p>
160
	 * <p>
192
	 * A mutator for the key sequence and incomplete stroke stored within this
161
	 * A mutator for the key sequence stored within this widget.  The text and
193
	 * widget.  This does some checking to see if the incomplete stroke is 
162
	 * caret position are updated.
194
	 * really incomplete; if it is complete, then it is rolled into the key 
195
	 * sequence.  The text and caret position are updated.
196
	 * </p>
163
	 * </p>
197
	 * <p>
164
	 * <p>
198
	 * All sequences are limited to maxStrokes number of strokes in length.
165
	 * All sequences are limited to maxStrokes number of strokes in length.
Lines 202-284 Link Here
202
	 *   
169
	 *   
203
	 * @param newKeySequence The new key sequence for this widget; may be
170
	 * @param newKeySequence The new key sequence for this widget; may be
204
	 * <code>null</code> if none.
171
	 * <code>null</code> if none.
205
	 * @param incompleteStroke The new incomplete stroke for this widget; may be
206
	 * <code>null</code> or incomplete -- both conditions are dealt with.
207
	 */
172
	 */
208
	public final void setKeySequence(final KeySequence newKeySequence, final KeyStroke incompleteStroke) {
173
	public void setKeySequence(KeySequence newKeySequence) {
209
		// Figure out whether the stroke should be rolled in.
174
		keySequence = newKeySequence;
210
		if (isComplete(incompleteStroke)) {
175
		
211
			if (newKeySequence == null) {
176
		// Trim any extra strokes.
212
				// This is guaranteed to be possible by setMaxStrokes
177
		if (maxStrokes != INFINITE) {
213
				keySequence = KeySequence.getInstance(incompleteStroke);
178
			List keyStrokes = new ArrayList(keySequence.getKeyStrokes());
214
			} else {
179
			int keyStrokesSize = keyStrokes.size();
215
				final List keyStrokes = new ArrayList(newKeySequence.getKeyStrokes());
180
			for (int i = keyStrokesSize - 1; i >= maxStrokes; i--) {
216
				keyStrokes.add(incompleteStroke);
181
				keyStrokes.remove(i);
217
				if (maxStrokes != INFINITE) {
218
					final int keyStrokesSize = keyStrokes.size();
219
					for (int i = keyStrokesSize - 1; i >= maxStrokes; i--) {
220
						keyStrokes.remove(i);
221
					}
222
				}
223
				keySequence = KeySequence.getInstance(keyStrokes);
224
			}
225
			temporaryStroke = null;
226
		} else {
227
			if ((newKeySequence != null) && (maxStrokes != INFINITE)) {
228
				final List untrimmedKeyStrokes = newKeySequence.getKeyStrokes();
229
				final int keyStrokesSize = untrimmedKeyStrokes.size();
230
				if (keyStrokesSize > maxStrokes) {
231
					final List keyStrokes = new ArrayList(untrimmedKeyStrokes);
232
					for (int i = keyStrokesSize - 1; i >= maxStrokes; i--) {
233
						keyStrokes.remove(i);
234
					}
235
					keySequence = KeySequence.getInstance(keyStrokes);
236
					temporaryStroke = null;
237
				} else if (keyStrokesSize == maxStrokes) {
238
					keySequence = newKeySequence;
239
					temporaryStroke = null;
240
				} else {
241
					keySequence = newKeySequence;
242
					temporaryStroke = incompleteStroke;
243
				}
244
			} else {
245
				keySequence = newKeySequence;
246
				temporaryStroke = incompleteStroke;
247
			}
248
		}
249
250
		/* Create a dummy (and rather invalid) sequence to get localized display
251
		 * formatting
252
		 */
253
		final KeySequence dummySequence;
254
		if (keySequence == null) {
255
			if (temporaryStroke == null) {
256
				dummySequence = KeySequence.getInstance();
257
			} else {
258
				dummySequence = KeySequence.getInstance(temporaryStroke);
259
			}
260
		} else {
261
			final List keyStrokes = new ArrayList(keySequence.getKeyStrokes());
262
			if (temporaryStroke != null) {
263
				keyStrokes.add(temporaryStroke);
264
			}
182
			}
265
			dummySequence = KeySequence.getInstance(keyStrokes);
183
			keySequence = KeySequence.getInstance(keyStrokes);	
266
		}
184
		}
267
		
185
				
268
		// We need to update the text, but we don't need to synchronize.
186
		// We need to update the text, but we don't need to synchronize.
269
		text.removeModifyListener(updateSequenceListener);
187
		text.removeModifyListener(updateSequenceListener);
270
		text.setText(dummySequence.format());
188
		text.setText(keySequence.format());
271
		text.addModifyListener(updateSequenceListener);
189
		text.addModifyListener(updateSequenceListener);
272
190
273
		// Update the caret position.
191
		// Update the caret position.
274
		text.setSelection(text.getText().length());
192
		text.setSelection(getText().length());
275
	}
193
	}
276
194
277
	/**
195
	/**
278
	 * A mutator for the layout information associated with the wrapped widget.
196
	 * A mutator for the layout information associated with the wrapped widget.
279
	 * @param layoutData The layout information; must not be <code>null</code>.
197
	 * @param layoutData The layout information; must not be <code>null</code>.
280
	 */
198
	 */
281
	public final void setLayoutData(final Object layoutData) {
199
	public void setLayoutData(Object layoutData) {
282
		text.setLayoutData(layoutData);
200
		text.setLayoutData(layoutData);
283
	}
201
	}
284
202
Lines 288-348 Link Here
288
	 * @param maximumStrokes The maximum number of strokes; should be a positive
206
	 * @param maximumStrokes The maximum number of strokes; should be a positive
289
	 * integer or <code>INFINITE</code>.
207
	 * integer or <code>INFINITE</code>.
290
	 */
208
	 */
291
	public final void setMaxStrokes(final int maximumStrokes) {
209
	public void setMaxStrokes(int maximumStrokes) {
292
		if ((maximumStrokes > 0) || (maximumStrokes == INFINITE)) {
210
		if ((maximumStrokes > 0) || (maximumStrokes == INFINITE)) {
293
			maxStrokes = maximumStrokes;
211
			maxStrokes = maximumStrokes;
294
		}
212
		}
295
	}
213
	}
296
214
297
	/**
215
	/**
298
	 * A traversal listener that blocks all traversal except for tabs and arrow
299
	 * keys.
300
	 */
301
	private final class FocusTrapListener implements TraverseListener {
302
303
		/**
304
		 * Handles the traverse event on the text field wrapped by this class.
305
		 * It swallows all traverse events example for tab and arrow key 
306
		 * navigation.  The other forms of navigation can be reached by tabbing
307
		 * off of the control.
308
		 * 
309
		 * @param event The trigger event; must not be <code>null</code>.
310
		 */
311
		public final void keyTraversed(final TraverseEvent event) {
312
			switch (event.detail) {
313
				case SWT.TRAVERSE_ESCAPE :
314
				case SWT.TRAVERSE_MNEMONIC :
315
				case SWT.TRAVERSE_NONE :
316
				case SWT.TRAVERSE_PAGE_NEXT :
317
				case SWT.TRAVERSE_PAGE_PREVIOUS :
318
				case SWT.TRAVERSE_RETURN :
319
					event.doit = false;
320
					break;
321
					
322
				case SWT.TRAVERSE_TAB_NEXT :
323
				case SWT.TRAVERSE_TAB_PREVIOUS :				
324
					// Check if modifiers other than just 'Shift' were down.
325
					if ((event.stateMask & (SWT.MODIFIER_MASK ^ SWT.SHIFT)) != 0) {
326
						// Modifiers other than shift were down.
327
						event.doit = false;
328
						break;
329
					}
330
					// fall through -- either no modifiers, or just shift.
331
				
332
				case SWT.TRAVERSE_ARROW_NEXT :
333
				case SWT.TRAVERSE_ARROW_PREVIOUS :
334
				default :
335
					// Let the traversal happen, but clear the incomplete stroke
336
					setKeySequence(getKeySequence(), null);
337
			}
338
		}
339
	}
340
341
	/**
342
	 * A key listener that traps incoming events and displays them in the 
216
	 * A key listener that traps incoming events and displays them in the 
343
	 * wrapped text field.  It has no effect on traversal operations.
217
	 * wrapped text field.  It has no effect on traversal operations.
344
	 */
218
	 */
345
	private final class KeyTrapListener implements Listener {
219
	private class KeyTrapListener implements Listener {
346
		/**
220
		/**
347
		 * Handles the key pressed and released events on the wrapped text 
221
		 * Handles the key pressed and released events on the wrapped text 
348
		 * widget.  This makes sure to either add the pressed key to the 
222
		 * widget.  This makes sure to either add the pressed key to the 
Lines 353-391 Link Here
353
		 * 
227
		 * 
354
		 * @param event The triggering event; must not be <code>null</code>. 
228
		 * @param event The triggering event; must not be <code>null</code>. 
355
		 */
229
		 */
356
		public final void handleEvent(final Event event) {
230
		public void handleEvent(Event event) {
231
			List keyStrokes = new ArrayList(getKeySequence().getKeyStrokes());
232
			
357
			if (event.type == SWT.KeyDown) {
233
			if (event.type == SWT.KeyDown) {
358
				if ((event.character == SWT.BS) && (event.stateMask == 0)) {
234
				if ((event.character == SWT.BS) && (event.stateMask == 0)) {
359
					// Remove the last key stroke.
235
					// Remove the last key stroke.
360
					if (hasIncompleteStroke()) {
236
					if (!keyStrokes.isEmpty()) {
361
						/* Remove the incomplete stroke.  This should not really
237
						keyStrokes.remove(keyStrokes.size() - 1);
362
						 * be possible, but it is better to be safe than sorry.
363
						 */
364
						setKeySequence(getKeySequence(), null);
365
					} else {
366
						// Remove the last complete stroke.
367
						final KeySequence sequence = getKeySequence();
368
						final List keyStrokes = new ArrayList(sequence.getKeyStrokes());
369
						if (!keyStrokes.isEmpty()) {
370
							keyStrokes.remove(keyStrokes.size() - 1);
371
							setKeySequence(KeySequence.getInstance(keyStrokes), null);
372
						}
373
					}
238
					}
239
					
374
				} else {
240
				} else {
375
					// Handles the key pressed event.
241
					// Handles the key pressed event.
376
					final int key = KeySupport.convertEventToAccelerator(event);
242
					// Remove the incomplete stroke, if any.
377
					final KeyStroke stroke = KeySupport.convertAcceleratorToKeyStroke(key);
243
					if ((hasIncompleteStroke()) && (!keyStrokes.isEmpty())) {
378
					setKeySequence(getKeySequence(), stroke);
244
						keyStrokes.remove(keyStrokes.size() - 1);
245
					}
246
					
247
					// Add the new stroke.
248
					int key = KeySupport.convertEventToUnmodifiedAccelerator(event);
249
					KeyStroke stroke = KeySupport.convertAcceleratorToKeyStroke(key);
250
					keyStrokes.add(stroke);
379
				}
251
				}
380
252
381
			} else if (hasIncompleteStroke()) {
253
			} else if ((event.type == SWT.KeyUp) && (hasIncompleteStroke())) {
382
				/* Handles the key released event, which is only relevant if
254
				/* Handles the key released event, which is only relevant if
383
				 * there is an incomplete stroke.
255
				 * there is an incomplete stroke.
384
				 */
256
				 */
385
				/* Figure out the SWT integer representation of the remaining
257
				/* Figure out the SWT integer representation of the remaining
386
				 * values.
258
				 * values.
387
				 */
259
				 */
388
				final Event mockEvent = new Event();
260
				Event mockEvent = new Event();
389
				if ((event.keyCode & SWT.MODIFIER_MASK) != 0) {
261
				if ((event.keyCode & SWT.MODIFIER_MASK) != 0) {
390
					// This key up is a modifier key being released.
262
					// This key up is a modifier key being released.
391
					mockEvent.stateMask = event.stateMask - event.keyCode;
263
					mockEvent.stateMask = event.stateMask - event.keyCode;
Lines 399-414 Link Here
399
				/* Get a reasonable facsimile of the stroke that is still
271
				/* Get a reasonable facsimile of the stroke that is still
400
				 * pressed.
272
				 * pressed.
401
				 */
273
				 */
402
				final int key = KeySupport.convertEventToAccelerator(mockEvent);
274
				int key = KeySupport.convertEventToUnmodifiedAccelerator(mockEvent);
403
				final KeyStroke remainingStroke = KeySupport.convertAcceleratorToKeyStroke(key);
275
				KeyStroke remainingStroke = KeySupport.convertAcceleratorToKeyStroke(key);
404
276
				if (!keyStrokes.isEmpty()) {
405
				if (remainingStroke.getModifierKeys().isEmpty()) {
277
					keyStrokes.remove(keyStrokes.size() - 1);
406
					setKeySequence(getKeySequence(), null);
278
				}
407
				} else {
279
				if (!remainingStroke.getModifierKeys().isEmpty()) {
408
					setKeySequence(getKeySequence(), remainingStroke);
280
					keyStrokes.add(remainingStroke);
409
				}
281
				}
410
282
411
			}
283
			}
284
					
285
			// Update the underlying widget.
286
			setKeySequence(KeySequence.getInstance(keyStrokes));
412
287
413
			// Prevent the event from reaching the widget.
288
			// Prevent the event from reaching the widget.
414
			event.doit = false;
289
			event.doit = false;
Lines 416-460 Link Here
416
	}
291
	}
417
292
418
	/**
293
	/**
294
	 * A traversal listener that blocks all traversal except for tabs and arrow
295
	 * keys.
296
	 */
297
	private class TraversalFilter implements Listener {
298
		/**
299
		 * Handles the traverse event on the text field wrapped by this class.
300
		 * It swallows all traverse events example for tab and arrow key 
301
		 * navigation.  The other forms of navigation can be reached by tabbing
302
		 * off of the control.
303
		 * 
304
		 * @param event The trigger event; must not be <code>null</code>.
305
		 */
306
		public void handleEvent(Event event) {
307
			switch (event.detail) {
308
				case SWT.TRAVERSE_ESCAPE :
309
				case SWT.TRAVERSE_MNEMONIC :
310
				case SWT.TRAVERSE_NONE :
311
				case SWT.TRAVERSE_PAGE_NEXT :
312
				case SWT.TRAVERSE_PAGE_PREVIOUS :
313
				case SWT.TRAVERSE_RETURN :
314
					event.type = SWT.None;
315
					event.doit = false;
316
					break;
317
					
318
				case SWT.TRAVERSE_TAB_NEXT :
319
				case SWT.TRAVERSE_TAB_PREVIOUS :				
320
					// Check if modifiers other than just 'Shift' were down.
321
					if ((event.stateMask & (SWT.MODIFIER_MASK ^ SWT.SHIFT)) != 0) {
322
						// Modifiers other than shift were down.
323
						event.type = SWT.None;
324
						event.doit = false;
325
						break;
326
					}
327
					// fall through -- either no modifiers, or just shift.
328
				
329
				case SWT.TRAVERSE_ARROW_NEXT :
330
				case SWT.TRAVERSE_ARROW_PREVIOUS :
331
				default :
332
					// Let the traversal happen, but clear the incomplete stroke
333
					if (hasIncompleteStroke()) {
334
						List keyStrokes = new ArrayList(getKeySequence().getKeyStrokes());
335
						if (!keyStrokes.isEmpty()) {
336
							keyStrokes.remove(keyStrokes.size() - 1);
337
						}
338
						setKeySequence(KeySequence.getInstance(keyStrokes));
339
					}
340
			}
341
			
342
		}
343
	}
344
	
345
	/**
346
	 * The manager resposible for installing and removing the traversal filter
347
	 * when the key sequence entry widget gains and loses focus.
348
	 */
349
	private class TraversalFilterManager implements FocusListener {
350
		/** The managed filter.  We only need one instance. */
351
		private TraversalFilter filter = new TraversalFilter();
352
		
353
		/**
354
		 * Attaches the global traversal filter.
355
		 * @param event Ignored.
356
		 */
357
		public void focusGained(FocusEvent event) {
358
			Display.getCurrent().addFilter(SWT.Traverse, filter);
359
		}
360
		
361
		/**
362
		 * Detaches the global traversal filter.
363
		 * @param event Ignored.
364
		 */
365
		public void focusLost(FocusEvent event) {
366
			Display.getCurrent().removeFilter(SWT.Traverse, filter);
367
		}
368
	}
369
370
	/**
419
     * A modification listener that makes sure that external events to this 
371
     * A modification listener that makes sure that external events to this 
420
     * class (i.e., direct modification of the underlying text) do not break
372
     * class (i.e., direct modification of the underlying text) do not break
421
     * this class' view of the world.
373
     * this class' view of the world.
422
     */
374
     */
423
	private final class UpdateSequenceListener implements ModifyListener {
375
	private class UpdateSequenceListener implements ModifyListener {
424
		/**
376
		/**
425
		 * Handles the modify event on the underlying text widget.
377
		 * Handles the modify event on the underlying text widget.
426
         * @param event The triggering event; ignored.
378
         * @param event The triggering event; ignored.
427
         */
379
         */
428
		public final void modifyText(final ModifyEvent event) {
380
		public void modifyText(ModifyEvent event) {
429
			try {
381
			try {
430
				// The original sequence.
382
				// The original sequence.
431
				final KeySequence originalSequence = getKeySequence();
383
				KeySequence originalSequence = getKeySequence();
432
				final List keyStrokes = new ArrayList(originalSequence.getKeyStrokes());
433
				if (getTemporaryStroke() != null) {
434
					keyStrokes.add(getTemporaryStroke());
435
				}
436
				final KeySequence sequenceFromStrokes = KeySequence.getInstance(keyStrokes);
437
384
438
				// The new sequence drawn from the text.
385
				// The new sequence drawn from the text.
439
				final String contents = getText().getText();
386
				String contents = getText();
440
				final KeySequence sequenceFromText = KeySequence.getInstance(contents);
387
				KeySequence newSequence = KeySequence.getInstance(contents);
441
388
442
				// Check to see if they're the same.
389
				// Check to see if they're the same.
443
				if (!sequenceFromStrokes.equals(sequenceFromText)) {
390
				if (!originalSequence.equals(newSequence)) {
444
					final List strokes = sequenceFromText.getKeyStrokes();
391
					setKeySequence(newSequence);
445
					final Iterator strokeItr = strokes.iterator();
446
					while (strokeItr.hasNext()) {
447
						// Make sure that it's a valid sequence.
448
						if (!isComplete((KeyStroke) strokeItr.next())) {
449
							setKeySequence(getKeySequence(), getTemporaryStroke());
450
							return;
451
						}
452
					}
453
					setKeySequence(sequenceFromText, null);
454
				}
392
				}
455
			} catch (final ParseException e) {
393
				
394
			} catch (ParseException e) {
456
				// Abort any cut/paste-driven modifications
395
				// Abort any cut/paste-driven modifications
457
				setKeySequence(getKeySequence(), getTemporaryStroke());
396
				setKeySequence(getKeySequence());
458
			}
397
			}
459
		}
398
		}
460
	}
399
	}
(-)Eclipse UI/org/eclipse/ui/internal/keys/KeySupport.java (-84 / +158 lines)
Lines 41-138 Link Here
41
		if ((accelerator & SWT.SHIFT) != 0)
41
		if ((accelerator & SWT.SHIFT) != 0)
42
			modifierKeys.add(ModifierKey.SHIFT);
42
			modifierKeys.add(ModifierKey.SHIFT);
43
43
44
		accelerator &= SWT.KEY_MASK;
44
		if (((accelerator & SWT.KEY_MASK) == 0) && (accelerator != 0)) {
45
45
			// There were only accelerators
46
		switch (accelerator) {
46
			naturalKey = null;
47
			case SWT.ARROW_DOWN :
47
		} else {
48
				naturalKey = SpecialKey.ARROW_DOWN;
48
			// There were other keys.
49
				break;
49
			accelerator &= SWT.KEY_MASK;
50
			case SWT.ARROW_LEFT :
50
51
				naturalKey = SpecialKey.ARROW_LEFT;
51
			switch (accelerator) {
52
				break;
52
				case SWT.ARROW_DOWN :
53
			case SWT.ARROW_RIGHT :
53
					naturalKey = SpecialKey.ARROW_DOWN;
54
				naturalKey = SpecialKey.ARROW_RIGHT;
54
					break;
55
				break;
55
				case SWT.ARROW_LEFT :
56
			case SWT.ARROW_UP :
56
					naturalKey = SpecialKey.ARROW_LEFT;
57
				naturalKey = SpecialKey.ARROW_UP;
57
					break;
58
				break;
58
				case SWT.ARROW_RIGHT :
59
			case SWT.END :
59
					naturalKey = SpecialKey.ARROW_RIGHT;
60
				naturalKey = SpecialKey.END;
60
					break;
61
				break;
61
				case SWT.ARROW_UP :
62
			case SWT.F1 :
62
					naturalKey = SpecialKey.ARROW_UP;
63
				naturalKey = SpecialKey.F1;
63
					break;
64
				break;
64
				case SWT.END :
65
			case SWT.F10 :
65
					naturalKey = SpecialKey.END;
66
				naturalKey = SpecialKey.F10;
66
					break;
67
				break;
67
				case SWT.F1 :
68
			case SWT.F11 :
68
					naturalKey = SpecialKey.F1;
69
				naturalKey = SpecialKey.F11;
69
					break;
70
				break;
70
				case SWT.F10 :
71
			case SWT.F12 :
71
					naturalKey = SpecialKey.F10;
72
				naturalKey = SpecialKey.F12;
72
					break;
73
				break;
73
				case SWT.F11 :
74
			case SWT.F2 :
74
					naturalKey = SpecialKey.F11;
75
				naturalKey = SpecialKey.F2;
75
					break;
76
				break;
76
				case SWT.F12 :
77
			case SWT.F3 :
77
					naturalKey = SpecialKey.F12;
78
				naturalKey = SpecialKey.F3;
78
					break;
79
				break;
79
				case SWT.F2 :
80
			case SWT.F4 :
80
					naturalKey = SpecialKey.F2;
81
				naturalKey = SpecialKey.F4;
81
					break;
82
				break;
82
				case SWT.F3 :
83
			case SWT.F5 :
83
					naturalKey = SpecialKey.F3;
84
				naturalKey = SpecialKey.F5;
84
					break;
85
				break;
85
				case SWT.F4 :
86
			case SWT.F6 :
86
					naturalKey = SpecialKey.F4;
87
				naturalKey = SpecialKey.F6;
87
					break;
88
				break;
88
				case SWT.F5 :
89
			case SWT.F7 :
89
					naturalKey = SpecialKey.F5;
90
				naturalKey = SpecialKey.F7;
90
					break;
91
				break;
91
				case SWT.F6 :
92
			case SWT.F8 :
92
					naturalKey = SpecialKey.F6;
93
				naturalKey = SpecialKey.F8;
93
					break;
94
				break;
94
				case SWT.F7 :
95
			case SWT.F9 :
95
					naturalKey = SpecialKey.F7;
96
				naturalKey = SpecialKey.F9;
96
					break;
97
				break;
97
				case SWT.F8 :
98
			case SWT.HOME :
98
					naturalKey = SpecialKey.F8;
99
				naturalKey = SpecialKey.HOME;
99
					break;
100
				break;
100
				case SWT.F9 :
101
			case SWT.INSERT :
101
					naturalKey = SpecialKey.F9;
102
				naturalKey = SpecialKey.INSERT;
102
					break;
103
				break;
103
				case SWT.HOME :
104
			case SWT.PAGE_DOWN :
104
					naturalKey = SpecialKey.HOME;
105
				naturalKey = SpecialKey.PAGE_DOWN;
105
					break;
106
				break;
106
				case SWT.INSERT :
107
			case SWT.PAGE_UP :
107
					naturalKey = SpecialKey.INSERT;
108
				naturalKey = SpecialKey.PAGE_UP;
108
					break;
109
				break;
109
				case SWT.PAGE_DOWN :
110
			default :
110
					naturalKey = SpecialKey.PAGE_DOWN;
111
				naturalKey = CharacterKey.getInstance((char) (accelerator & 0xFFFF));
111
					break;
112
				case SWT.PAGE_UP :
113
					naturalKey = SpecialKey.PAGE_UP;
114
					break;
115
				default :
116
					naturalKey = CharacterKey.getInstance((char) (accelerator & 0xFFFF));
117
			}
112
		}
118
		}
113
119
114
		return KeyStroke.getInstance(modifierKeys, naturalKey);
120
		return KeyStroke.getInstance(modifierKeys, naturalKey);
115
	}
121
	}
116
122
117
	public static int convertEventToAccelerator(Event event) {
123
	/**
118
		int key = event.character;
124
	 * Converts the given event into an SWT accelerator value -- considering
119
125
	 * the modified character with the shift modifier. This is the third
120
		if (key == 0)
126
	 * accelerator value that should be checked.
121
			key = event.keyCode;
127
	 * 
122
		else {
128
	 * @param event
123
			if (0 <= key && key <= 0x1F) {
129
	 *           The event to be converted; must not be <code>null</code>.
124
				if ((event.stateMask & SWT.CTRL) != 0 && event.keyCode != event.character)
130
	 * @return The combination of the state mask and the unmodified character.
125
					key += 0x40;
131
	 */
126
			} else {
132
	public static int convertEventToModifiedAccelerator(Event event) {
127
				if ('a' <= key && key <= 'z')
133
		int modifiers = event.stateMask & SWT.MODIFIER_MASK;
128
					key -= 'a' - 'A';
134
		int modifiedCharacter = topKey(event);
129
			}
135
		return modifiers + modifiedCharacter;
130
		}
136
	}
131
137
138
	/**
139
	 * Converts the given event into an SWT accelerator value -- considering
140
	 * the unmodified character with all modifier keys. This is the first
141
	 * accelerator value that should be checked. However, all alphabetic
142
	 * characters are considered as their uppercase equivalents.
143
	 * 
144
	 * @param event
145
	 *           The event to be converted; must not be <code>null</code>.
146
	 * @return The combination of the state mask and the unmodified character.
147
	 */
148
	public static int convertEventToUnmodifiedAccelerator(Event event) {
132
		int modifiers = event.stateMask & SWT.MODIFIER_MASK;
149
		int modifiers = event.stateMask & SWT.MODIFIER_MASK;
133
		return modifiers + key;
150
		int unmodifiedCharacter = upperCase(event.keyCode);
151
152
		return modifiers + unmodifiedCharacter;
153
	}
154
155
	/**
156
	 * Converts the given event into an SWT accelerator value -- considering
157
	 * the modified character without the shift modifier. This is the second
158
	 * accelerator value that should be checked.
159
	 * 
160
	 * @param event
161
	 *           The event to be converted; must not be <code>null</code>.
162
	 * @return The combination of the state mask without shift, and the
163
	 *         modified character.
164
	 */
165
	public static int convertEventToUnshiftedModifiedAccelerator(Event event) {
166
		int modifiers = event.stateMask & (SWT.MODIFIER_MASK ^ SWT.SHIFT);
167
		int modifiedCharacter = topKey(event);
168
		return modifiers + modifiedCharacter;
134
	}
169
	}
135
	
170
136
	public static final int convertKeyStrokeToAccelerator(final KeyStroke keyStroke) {
171
	public static final int convertKeyStrokeToAccelerator(final KeyStroke keyStroke) {
137
		if (keyStroke == null)
172
		if (keyStroke == null)
138
			throw new NullPointerException();
173
			throw new NullPointerException();
Lines 205-210 Link Here
205
		}
240
		}
206
241
207
		return accelerator;
242
		return accelerator;
243
	}
244
245
	/**
246
	 * Makes sure that a fully-modified character is converted to the normal
247
	 * form. This means that "Ctrl+" key strokes must reverse the modification
248
	 * caused by control-escaping. Also, all lower case letters are converted
249
	 * to uppercase.
250
	 * 
251
	 * @param event
252
	 *           The event from which the fully-modified character should be
253
	 *           pulled.
254
	 * @return The modified character, uppercase and without control-escaping.
255
	 */
256
	private static int topKey(Event event) {
257
		boolean ctrlDown = (event.stateMask & SWT.CTRL) != 0;
258
		char modifiedCharacter;
259
		if ((ctrlDown) && (event.character != event.keyCode) && (event.character < 0x20)) {
260
			modifiedCharacter = (char) (event.character + 0x40);
261
		} else {
262
			modifiedCharacter = event.character;
263
		}
264
265
		return upperCase(modifiedCharacter);
266
	}
267
268
	/**
269
	 * Makes the given character uppercase if it is a letter.
270
	 * 
271
	 * @param character
272
	 *           The character to convert.
273
	 * @return The uppercase equivalent, if any; otherwise, the character
274
	 *         itself.
275
	 */
276
	private static int upperCase(int character) {
277
		if (Character.isLetter((char) character)) {
278
			return Character.toUpperCase((char) character);
279
		}
280
281
		return character;
208
	}
282
	}
209
283
210
	private KeySupport() {
284
	private KeySupport() {

Return to bug 43613