Implement and document menu item mnemonic escaping

This commit is contained in:
Ankur Bohra
2024-03-20 14:57:52 +05:30
parent 74a5b6f0e1
commit 55ca2a6cc5
3 changed files with 87 additions and 6 deletions

View File

@@ -128,9 +128,16 @@
<BLOCKQUOTE>
<P>The tag indicates the top-level menu path. Path levels are delimited using the "."
character.</P>
character. A mnemonic can be defined by adding an ampersand ("&") in front of the mnemonic
key. Ampersands can be escaped by adding another ampersand ("&&"). </P>
<P>For example, <TT>"@menupath File.Run.My Script"</TT>.</P>
<P>For example:</P>
<BLOCKQUOTE>
<P><TT>@menupath File.Run.My Script<BR>
@menupath File.Run.My &Script<BR>
@menupath File.Run.Me && My &Script<BR>
</TT></P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<P><CODE><B>@toolbar</B></CODE></P>

View File

@@ -117,7 +117,7 @@ public class MenuData {
/**
* Returns the menu path as a string. This method filters accelerator chars('&') from the path.
* @return the menu path as a string without '&' chars
* @return the menu path as a string without unescaped '&' chars
*/
public String getMenuPathDisplayString() {
if (menuPath == null || menuPath.length == 0) {
@@ -260,7 +260,7 @@ public class MenuData {
}
/**
* Sets the menu item name and the mnemonic, using the first '&amp;' found in the text
* Sets the menu item name and the mnemonic, using the first unescaped '&amp;' found in the text
* as a marker ("S&amp;ave As").
* <p>
* NOTE: do NOT use this method with strings that contain user-supplied text. Instead, use
@@ -305,7 +305,12 @@ public class MenuData {
}
private static int getMnemonic(String string) {
int indexOf = string.indexOf('&');
int indexOf;
int fromIndex = 0;
do {
indexOf = string.indexOf('&', fromIndex);
fromIndex = indexOf + 2;
} while (indexOf >= 0 && indexOf < string.length() - 1 && string.charAt(indexOf + 1) == '&');
if (indexOf >= 0 && indexOf < string.length() - 1) {
return string.charAt(indexOf + 1);
}
@@ -321,7 +326,18 @@ public class MenuData {
}
private static String processMenuItemName(String string) {
return string.replaceFirst("&", "");
StringBuilder builder = new StringBuilder();
for (int i = 0; i < string.length(); i++) {
if (string.charAt(i) == '&') {
if (i < string.length() - 1 && string.charAt(i+1) == '&') {
builder.append('&');
i++;
}
} else {
builder.append(string.charAt(i));
}
}
return builder.toString();
}
public String getMenuItemName() {

View File

@@ -34,6 +34,45 @@ public class MenuDataTest {
MenuData menuData = new MenuData(new String[] { "One", "Two", "&Three" });
assertEquals(menuData.getMnemonic(), 'T');
}
/**
* There should be no mnemonic, the ampersand is escaped.
*/
@Test
public void testMenuDataMnemonicSkipsEscapedAmpersand() {
MenuData menuData = new MenuData(new String[] { "One", "Two", "&&Three" });
assertEquals(menuData.getMnemonic(), MenuData.NO_MNEMONIC);
}
/**
* The mnemonic should be 'h'. The first two ampersands form an escaped
* ampersand. The third ampersand is not escaped.
*/
@Test
public void testMenuDataMnemonicEscapesAmpersandLeftToRight() {
MenuData menuData = new MenuData(new String[] { "One", "Two", "T&&&hree" });
assertEquals(menuData.getMnemonic(), 'h');
}
/**
* There should be no mnemonic, the trailing ampersand is not followed by any
* character.
*/
@Test
public void testMenuDataMnemonicIgnoresTrailingAmpersand() {
MenuData menuData = new MenuData(new String[] { "One", "Two", "Three&" });
assertEquals(menuData.getMnemonic(), MenuData.NO_MNEMONIC);
}
/**
* The mnemonic should be 'T'. This is the expected behaviour as per the
* "Desktop development with C++" workload in Visual Studio.
*/
@Test
public void testMenuDataMnemonicParsesLeftToRight() {
MenuData menuData = new MenuData(new String[] { "One", "Two", "&T&hree" });
assertEquals(menuData.getMnemonic(), 'T');
}
/**
* Mnemonic should be 'h' based on the value that was explicitly set
@@ -86,4 +125,23 @@ public class MenuDataTest {
menuData.setMenuPath(newPath);
assertEquals(menuData.getMnemonic(), MenuData.NO_MNEMONIC);
}
@Test
public void testGetMenuItemNameEscapesAmpersand() {
MenuData menuData = new MenuData(new String[] { "One", "Two", "&&Three" });
assertEquals(menuData.getMenuItemName(), "&Three");
}
/**
* Ampersands that are not escaped should be ignored regardless of use as
* mnemonics.
*/
@Test
public void testGetMenuItemNameIgnoresUnescapedAmpersand() {
MenuData menuData = new MenuData(new String[] { "One", "Two", "Three&" });
assertEquals(menuData.getMenuItemName(), "Three");
menuData.setMenuItemName("&T&hree");
assertEquals(menuData.getMenuItemName(), "Three");
}
}