diff --git a/platform/platform-api/src/com/intellij/openapi/MnemonicHelper.java b/platform/platform-api/src/com/intellij/openapi/MnemonicHelper.java index 8035261e30cf..5260b9736639 100644 --- a/platform/platform-api/src/com/intellij/openapi/MnemonicHelper.java +++ b/platform/platform-api/src/com/intellij/openapi/MnemonicHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2011 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,9 @@ */ package com.intellij.openapi; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.registry.Registry; import com.intellij.ui.ComponentTreeWatcher; import com.intellij.util.ArrayUtil; import com.intellij.util.ui.DialogUtil; @@ -26,6 +28,8 @@ import java.awt.*; import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.util.HashMap; +import java.util.Map; /** * Automatically locates & characters in texts of buttons and labels on a component or dialog, @@ -35,6 +39,9 @@ import java.beans.PropertyChangeListener; * @since 5.1 */ public class MnemonicHelper extends ComponentTreeWatcher { + private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.MnemonicHelper"); + private Map myMnemonics = null; + public static final PropertyChangeListener TEXT_LISTENER = new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { final Object source = evt.getSource(); @@ -56,11 +63,12 @@ public class MnemonicHelper extends ComponentTreeWatcher { final AbstractButton abstractButton = ((AbstractButton)parentComponent); abstractButton.addPropertyChangeListener(AbstractButton.TEXT_CHANGED_PROPERTY, TEXT_LISTENER); DialogUtil.registerMnemonic(abstractButton); + checkForDuplicateMnemonics(abstractButton); } else if (parentComponent instanceof JLabel) { final JLabel jLabel = ((JLabel)parentComponent); jLabel.addPropertyChangeListener(TEXT_CHANGED_PROPERTY, TEXT_LISTENER); DialogUtil.registerMnemonic(jLabel, null); - + checkForDuplicateMnemonics(jLabel); if (SystemInfo.isMac) { // hack to make Labels mnemonic work for ALT+KEY_CODE on Macs. // Default implementation uses ALT+CTRL+KEY_CODE (see BasicLabelUI). @@ -83,4 +91,24 @@ public class MnemonicHelper extends ComponentTreeWatcher { protected void unprocessComponent(Component component) { } + + public void checkForDuplicateMnemonics(JLabel label) { + if (!Registry.is("ide.checkDuplicateMnemonics")) return; + checkForDuplicateMnemonics(label.getDisplayedMnemonic(), label.getText()); + } + + public void checkForDuplicateMnemonics(AbstractButton button) { + if (!Registry.is("ide.checkDuplicateMnemonics")) return; + checkForDuplicateMnemonics(button.getMnemonic(), button.getText()); + } + + public void checkForDuplicateMnemonics(int mnemonic, String text) { + if (mnemonic == 0) return; + if (myMnemonics == null) myMnemonics = new HashMap(); + final String other = myMnemonics.get(Integer.valueOf(mnemonic)); + if (other != null && !other.equals(text)) { + LOG.error("conflict: multiple components with mnemonic '" + (char)mnemonic + "' seen on '" + text + "' and '" + other + "'"); + } + myMnemonics.put(Integer.valueOf(mnemonic), text); + } } diff --git a/platform/platform-resources-en/src/misc/registry.properties b/platform/platform-resources-en/src/misc/registry.properties index 4283073fc9b4..6e6adab34446 100644 --- a/platform/platform-resources-en/src/misc/registry.properties +++ b/platform/platform-resources-en/src/misc/registry.properties @@ -7,7 +7,7 @@ actionSystem.fixLostTyping.description=Redispatch events, lost between actions a actionSystem.fixStickyFocusedWindows=true actionSystem.fixNullFocusedComponent=true actionSystem.commandProcessingTimeout=30000 -actionSystem.commandProcessingTimeout.description=Timeout after which currently proccessed command is forcibly cancelled +actionSystem.commandProcessingTimeout.description=Timeout after which currently processed command is forcibly cancelled actionSystem.typeAheadTimeBeforeDialog=2000 actionSystem.typeAheadTimeBeforeDialog.description=If dialog shown as a result of some typing, all key types withing this time will be redispatched after dialog is closed actionSystem.typeAheadTimeAfterPopupAction=500 @@ -34,13 +34,15 @@ actionSystem.mouseGesturesEnabled=true ide.firstStartup=true ide.debugMode=false -ide.debugMode.description=Record additonal information to make bug reports more informative +ide.debugMode.description=Record additional information to make bug reports more informative ide.forcedShowTooltip=alt ide.forcedShowTooltip.description=Shortcut for forced show tooltip ide.popup.dropShadow=false ide.consumeKnownToolkitBugs=true ide.highlight.match.in.selected.only=true ide.lazyIconLoading=true +ide.checkDuplicateMnemonics=false +ide.checkDuplicateMnemonics.description=Check for duplicate mnemonics ide.appIcon.progress=true ide.appIcon.badge=true