//----------------------------------------------------------------------------
// COMPONENT NAME: LPEX Editor
//
// © Copyright IBM Corporation 2004, 2008
// All Rights Reserved.
//
// DESCRIPTION:
// ComposeAction - sample user-defined action (compose)
//----------------------------------------------------------------------------
package com.ibm.lpex.samples;
import com.ibm.lpex.core.LpexAction;
import com.ibm.lpex.core.LpexCommand;
import com.ibm.lpex.core.LpexStringTokenizer;
import com.ibm.lpex.core.LpexView;
/**
* Sample action <b>compose</b> - enter special characters.
* Use this action to enter into the document special characters that are
* not found on your default keyboard. For example, enter a acute (á)
* by typing a compose sequence consisting of a and apostrophe ('), and the
* plus-minus sign (±) by typing + and -.
*
* <p>Here is the ComposeAction
* <a href="doc-files/ComposeAction.java.html">source code</a>.</p>
*
* <p>To run this sample:
* <ul>
* <li>Define this user action via an editor preference page, where available,
* or from the editor command line:
* <pre>set actionClass.compose com.ibm.lpex.samples.ComposeAction</pre></li>
* <li>Run it from the editor command line:
* <pre>action compose</pre>
* or associate it with a key (here, <b>Alt+F1</b> in the text area):
* <pre>set keyAction.a-f1 compose</pre></li>
* </ul></p>
* See the <a href="doc-files/ComposeAction.java.html#line48">source code</a>
* for the supported compose sequences. To see these characters in the document,
* you may need a Unicode font installed on your system. The document must be
* saved to an encoding which supports the characters composed.
*
* @see com.ibm.lpex.samples All the samples
*/
public class ComposeAction implements LpexAction
{
private static final String[] _compose =
/*----------------------------------------------------------------*/
/* The special characters supported and their compose sequences */
/*----------------------------------------------------------------*/
// character compose sequence
// ========= ================
{"\u00c1", "A'", // Á A acute A + APOSTROPHE (')
"\u00e1", "a'", // á a acute a + APOSTROPHE (')
"\u00c5", "A*", // Å A angstrom A + ASTERISK (*)
"\u00e5", "a*", // å a angstrom a + ASTERISK (*)
"\u00c2", "A^", // Â A circumflex A + CARET (^)
"\u00e2", "a^", // â a circumflex a + CARET (^)
"\u00c0", "A`", // À A grave A + ACCENT GRAVE (`)
"\u00e0", "a`", // à a grave a + ACCENT GRAVE (`)
"\u00c3", "A~", // Ã A tilde A + TILDE (~)
"\u00e3", "a~", // ã a tilde a + TILDE (~)
"\u00c4", "A\"", // Ä A umlaut A + QUOTATION MARK (")
"\u00e4", "a\"", // ä a umlaut a + QUOTATION MARK (")
"\u0102", "A&", // A breve A + AMPERSAND (&)
"\u0103", "a&", // a breve a + AMPERSAND (&)
"\u00c6", "AE", // Æ AE diphthong A + E
"\u00e6", "ae", // æ ae diphthong a + e
"\u00a6", "/|", // ¦ broken bar SLASH (/) + VERTICAL LINE (|)
"\u00c7", "C,", // Ç C cedilla C + COMMA (,)
"\u00e7", "c,", // ç c cedilla c + COMMA (,)
"\u00b8", ",,", // ¸ cedilla COMMA (,) + COMMA (,)
"\u00b7", "^.", // · center dot CARET (^) + PERIOD (.)
"\u00b0", "/0", // ° degree SLASH (/) + 0
"\u00f7", ":-", // ÷ division COLON (:) + MINUS (-)
"\u00c9", "E'", // É E acute E + APOSTROPHE (')
"\u00e9", "e'", // é e acute e + APOSTROPHE (')
"\u00ca", "E^", // Ê E circumflex E + CARET(^)
"\u00ea", "e^", // ê e circumflex e + CARET (^)
"\u00c8", "E`", // È E grave E + ACCENT GRAVE (`)
"\u00e8", "e`", // è e grave e + ACCENT GRAVE (`)
"\u00cb", "E\"", // Ë E umlaut E + QUOTATION MARK (")
"\u00eb", "e\"", // ë e umlaut e + QUOTATION MARK (")
"\u2014", "--", // em dash MINUS (-) + MINUS (-)
"\u00f0", "d-", // ð edh Latin small d + MINUS (-)
"\u00d0", "D-", // Ð edh Latin capital D + MINUS (-)
"\u00aa", "a_", // ª feminine ordinal a + UNDERSCORE (_), or
"\u00aa", "A_", // A + UNDERSCORE (_)
"\u00ad", "-=", // hyphen MINUS (-) + EQUAL SIGN (=)
"\u00cd", "I'", // Í I acute I + APOSTROPHE (')
"\u00ed", "i'", // í i acute i + APOSTROPHE (')
"\u00ce", "I^", // Î I circumflex I + CARET (^)
"\u00ee", "i^", // î i circumflex i + CARET (^)
"\u00cc", "I`", // Ì I grave I + ACCENT GRAVE (`)
"\u00ec", "i`", // ì i grave i + ACCENT GRAVE (`)
"\u00cf", "I\"", // Ï I umlaut I + QUOTATION MARK (")
"\u00ef", "i\"", // ï i umlaut i + QUOTATION MARK (")
"\u00a1", "!!", // ¡ inverted ! EXCLAMATION (!) + EXCLAMATION (!)
"\u00bf", "??", // ¿ inverted ? QUESTION MARK (?) + QUESTION MARK (?)
"\u00ab", "<<", // « left angle quotes LESS THAN SIGN (<) + LESS THAN SIGN (<)
"\u00ba", "O_", // º masculine ordinal O + UNDERSCORE (_), or
"\u00ba", "o_", // o + UNDERSCORE (_)
"\u00b5", "/u", // µ micro sign SLASH (/) + u
"\u00d7", "xx", // × multiply sign x + x, or
"\u00d7", "XX", // X + X
"\u00d1", "N~", // Ñ N tilde N + TILDE (~)
"\u00f1", "n~", // ñ n tilde n + TILDE (~)
"\u00ac", "-]", // ¬ not sign MINUS (-) + CLOSING BRACKET (])
"\u2260", "/=", // not equal SLASH (/) + EQUAL SIGN (=)
"\u00d3", "O'", // Ó O acute O + APOSTROPHE (')
"\u00f3", "o'", // ó o acute o + APOSTROPHE (')
"\u00d4", "O^", // Ô O circumflex O + CARET (^)
"\u00f4", "o^", // ô o circumflex o + CARET (^)
"\u00d2", "O`", // Ò O grave O + ACCENT GRAVE (`)
"\u00f2", "o`", // ò o grave o + ACCENT GRAVE (`)
"\u00d8", "O/", // Ø O slash O + SLASH (/)
"\u00f8", "o/", // ø o slash o + SLASH (/)
"\u00d5", "O~", // Õ O tilde O + TILDE (~)
"\u00f5", "o~", // õ o tilde o + TILDE (~)
"\u00d6", "O\"", // Ö O umlaut O + QUOTATION MARK (")
"\u00f6", "o\"", // ö o umlaut o + QUOTATION MARK (")
"\u00af", "^_", // ¯ overline CARET (^) + UNDERSCORE (_)
"\u00b6", "/p", // ¶ paragraph symbol SLASH (/) + p
"\u00b1", "+-", // ± plus-minus sign PLUS (+) + MINUS (-)
"\u00bb", ">>", // » right angle quotes GREATER THAN (>) + GREATER THAN (>)
"\u015e", "S,", // S cedilla S + COMMA (,)
"\u015f", "s,", // s cedilla s + COMMA (,)
"\u00a7", "/s", // § section sign SLASH (/) + s
"\u00df", "ss", // ß sharp s German small s + s
"\u0162", "T,", // T cedilla T + COMMA (,)
"\u0163", "t,", // t cedilla t + COMMA (,)
"\u00fe", "p-", // þ thorn Latin small p + MINUS (-)
"\u00de", "P-", // Þ thorn Latin capital P + MINUS (-)
"\u2122", "T0", // Trade mark sign T + 0, or
"\u2122", "t0", // t + 0, or
"\u2122", "(T)", // LEFT PAREN (() + T + RIGHT PAREN ()), or
"\u2122", "(t)", // LEFT PAREN (() + t + RIGHT PAREN ())
"\u00da", "U'", // Ú U acute U + APOSTROPHE (')
"\u00fa", "u'", // ú u acute u + APOSTROPHE (')
"\u00db", "U^", // Û U circumflex U + CARET (^)
"\u00fb", "u^", // û u circumflex u + CARET (^)
"\u00d9", "U`", // Ù U grave U + ACCENT GRAVE (`)
"\u00f9", "u`", // ù u grave u + ACCENT GRAVE (`)
"\u00dc", "U\"", // Ü U umlaut U + QUOTATION MARK (")
"\u00fc", "u\"", // ü u Umlaut u + QUOTATION MARK (")
"\u00dd", "Y'", // Ý Y acute Y + APOSTROPHE (')
"\u00fd", "y'", // ý y acute y + APOSTROPHE (')
"\u00ff", "y\"", // ÿ y umlaut y + QUOTATION MARK (")
"\u00a9", "C0", // © Copyright sign C + 0, or
"\u00a9", "c0", // c + 0, or
"\u00a9", "(C)", // LEFT PAREN (() + C + RIGHT PAREN ()), or
"\u00a9", "(c)", // LEFT PAREN (() + c + RIGHT PAREN ())
"\u00ae", "R0", // ® Registered sign R + 0, or
"\u00ae", "r0", // r + 0, or
"\u00ae", "(R)", // LEFT PAREN (() + R + RIGHT PAREN ()), or
"\u00ae", "(r)", // LEFT PAREN (() + r + RIGHT PAREN ())
"\u00a4", "X0", // ¤ currency sign X + 0, or
"\u00a4", "x0", // x + 0
"\u00a2", "c|", // ¢ cent c + VERTICAL LINE (|), or
"\u00a2", "c/", // c + SLASH (/), or
"\u00a2", "C|", // C + VERTICAL LINE (|), or
"\u00a2", "C/", // C + SLASH (/)
"\u20ac", "C=", // Euro sign C + EQUAL SIGN (=), or
"\u20ac", "c=", // c + EQUAL SIGN (=), or
"\u20ac", "C-", // C + MINUS (-), or
"\u20ac", "c-", // c + MINUS (-)
"\u00a3", "L=", // £ Pound sign L + EQUAL SIGN (=), or
"\u00a3", "l=", // l + EQUAL SIGN (=), or
"\u00a3", "L-", // L + MINUS (-), or
"\u00a3", "l-", // l + MINUS (-)
"\u00a5", "Y=", // ¥ Yen sign Y + EQUAL SIGN (=), or
"\u00a5", "y=", // y + EQUAL SIGN (=), or
"\u00a5", "Y-", // Y + MINUS (-), or
"\u00a5", "y-", // y + MINUS (-)
"\u2070", "^0", // 0 superscript CARET (^) + 0
"\u00b9", "^1", // ¹ 1 superscript CARET (^) + 1
"\u00b2", "^2", // ² 2 superscript CARET (^) + 2
"\u00b3", "^3", // ³ 3 superscript CARET (^) + 3
"\u2074", "^4", // 4 superscript CARET (^) + 4
"\u2075", "^5", // 5 superscript CARET (^) + 5
"\u2076", "^6", // 6 superscript CARET (^) + 6
"\u2077", "^7", // 7 superscript CARET (^) + 7
"\u2078", "^8", // 8 superscript CARET (^) + 8
"\u2079", "^9", // 9 superscript CARET (^) + 9
"\u207a", "^+", // superscript plus CARET (^) + PLUS (+)
"\u207b", "^-", // superscript minus CARET (^) + MINUS (-)
"\u00bd", "1/2", // ½ one half fraction 1 + SLASH (/) + 2
"\u2153", "1/3", // one third fraction 1 + SLASH (/) + 3
"\u00bc", "1/4", // ¼ one quarter fraction 1 + SLASH (/) + 4
"\u2155", "1/5", // one fifth fraction 1 + SLASH (/) + 5
"\u2159", "1/6", // one sixth fraction 1 + SLASH (/) + 6
"\u215b", "1/8", // one eighth fraction 1 + SLASH (/) + 8
"\u2154", "2/3", // two thirds fraction 2 + SLASH (/) + 3
"\u2156", "2/5", // two fifths fraction 2 + SLASH (/) + 5
"\u00be", "3/4", // ¾ 3 quarters fraction 3 + SLASH (/) + 4
"\u2157", "3/5", // 3 fifths fraction 3 + SLASH (/) + 5
"\u215c", "3/8", // 3 eighths fraction 3 + SLASH (/) + 8
"\u2158", "4/5", // 4 fifths fraction 4 + SLASH (/) + 5
"\u215a", "5/6", // 5 sixths fraction 5 + SLASH (/) + 6
"\u215d", "5/8", // 5 eighths fraction 5 + SLASH (/) + 8
"\u215e", "7/8" // 7 eighths fraction 7 + SLASH (/) + 8
};
// NOTES: In order to accept *only* special characters in
// the given sequence, enter this on an LPEX command line:
// set userParameter.Compose.onlySpecialCharacters on
// In order to accept the compose sequences *only* in their
// defined order, enter this on an LPEX command line:
// set userParameter.Compose.onlyDefinedOrder on
/**
* Command to compose special characters from a sequence of regular ones.
* Called by the <b>input</b> editor command when prompting for the compose sequence.
* Also used by {@link TestUserProfile}.
*/
public static LpexCommand composeCommand = new LpexCommand() {
public boolean doCommand(LpexView lpexView, String parameters) {
return doComposeCommand(lpexView, parameters, _compose, "compose",
lpexView.queryOn("userParameter.Compose.onlyDefinedOrder"));
}
};
/**
* Handles a compose command's doCommand() using the given
* composition / mapping table and compose command's name.
*/
static boolean doComposeCommand(LpexView lpexView, String parameters,
String[] composeTable, String composeCommandName,
boolean onlyDefinedOrder)
{
// clear any previous error message we might have issued
lpexView.doDefaultCommand("set messageText");
boolean onlySpecialCharacters =
lpexView.queryOn("userParameter.Compose.onlySpecialCharacters");
while (parameters.length() > 0)
{
// look greedily for a compose sequence at the start of parameters
int index = findSpecialCharacter(parameters, onlyDefinedOrder, composeTable);
if (index == 0)
{
if (onlySpecialCharacters)
{
break; // not a special character, reject...
}
}
// 'type in' the (special) character
String c = (index != 0)? composeTable[index-1] : String.valueOf(parameters.charAt(0));
if (!typeIn(lpexView, c))
{
// error while inserting: restate prompt with what's still left
promptForCompose(lpexView, composeCommandName, parameters);
return false;
}
// there may be more sequences to compose - drop what we've used
parameters = parameters.substring((index != 0)? composeTable[index].length() : 1);
if (parameters.length() == 0)
{
return true;
}
}
// cannot compose: display error, restate prompt with what's still left
lpexView.doCommand("set messageText Cannot compose character from " +
"\"" + parameters + "\".");
promptForCompose(lpexView, composeCommandName, parameters);
return false;
}
// Returns the index of the composition sequence or mapping character in the given
// table, if any, that is found at the start of the given sequence. Returns 0 if none.
static int findSpecialCharacter(String sequence, boolean onlyDefinedOrder, String[] composeTable)
{
// possible reversed sequence - see check below
String reverseSequence = (onlyDefinedOrder || sequence.length() < 2)? null :
new String(new char[] { sequence.charAt(1), sequence.charAt(0) });
for (int i = 1; i < composeTable.length; i += 2)
{
String composeSequence = composeTable[i];
if (composeSequence.length() == 1)
{
if (composeSequence.charAt(0) == sequence.charAt(0))
{
return i;
}
}
else
{
if (sequence.startsWith(composeSequence))
{
return i;
}
// for most, why not allow the two compose characters to be in any order
// (not for the various 3-char-compose fractions, and letters-only AE ae)...
if (!onlyDefinedOrder &&
composeSequence.length() == 2 &&
composeSequence.equals(reverseSequence) &&
!composeSequence.equals("AE") && !composeSequence.equals("ae"))
{
return i;
}
}
}
return 0;
}
// Inserts the specified character (given as a one-character-long
// string) into the document as if typed in by the user.
static boolean typeIn(LpexView lpexView, String c)
{
// if any stream selection in this view, the character replaces it
boolean inserting;
if (lpexView.queryOn("block.inView") && "stream".equals(lpexView.query("block.type")))
{
lpexView.doCommand("block delete");
inserting = true;
}
// otherwise go by the current insert/replace mode
else
{
inserting = lpexView.queryOn("insertMode");
}
boolean success = lpexView.doCommand((inserting? "insertText " : "replaceText ") + c);
// to also check "status" when LPEX supports enforcing the text limit...
return success;
}
// Prompts the user for the compose sequence.
// When user types in text and presses Enter, the composeCommand is run.
static void promptForCompose(LpexView lpexView, String composeCommand)
{
promptForCompose(lpexView, composeCommand, null);
}
// Prompts the user for the compose sequence with an optional initial sequence.
// When user (types in text and) presses Enter, (a new) composeCommand is run.
static void promptForCompose(LpexView lpexView, String composeCommand, String composeSequence)
{
String text = (composeSequence != null)?
LpexStringTokenizer.addQuotes(composeSequence) : "";
LpexView.doGlobalCommand("set status");
lpexView.doCommand("input \"Compose:\" " + text + " \"" + composeCommand + " \"");
}
/**
* Compose action that can be used when the <b>compose</b> command has been
* already defined in the view. It is used by {@link TestUserProfile}.
*/
public static LpexAction composeAction = new LpexAction() {
public void doAction(LpexView lpexView) {
promptForCompose(lpexView, "compose");
}
public boolean available(LpexView lpexView) {
return lpexView.currentElement() != 0 && !lpexView.queryOn("readonly");
}
};
/**
* Runs the action.
* Prompts the user for a compose sequence, then enters the (special)
* character(s) into the document. Uses the <b>input</b> default editor
* command, and the <b>compose</b> command defined in here.
*/
public void doAction(LpexView lpexView)
{
// ensure command to handle composition is defined in the view this action runs
if (lpexView.command("compose") != composeCommand)
{
lpexView.defineCommand("compose", composeCommand);
}
// prompt user for the composition characters, insert result
promptForCompose(lpexView, "compose");
}
/**
* Returns the availability of this action.
* This action can be run in any visible, writable document view.
*/
public boolean available(LpexView lpexView)
{
return lpexView.currentElement() != 0 && !lpexView.queryOn("readonly");
}
}