//----------------------------------------------------------------------------
// COMPONENT NAME: LPEX Editor
//
// (C) Copyright IBM Corporation 2004, 2005
// 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 (&aacute;)
 * by typing a compose sequence consisting of a and apostrophe ('), or the
 * plus/minus sign (&plusmn;) 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#line47">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.
 *
 * @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 (.)
  "\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 ())

  "\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 (-)
  "\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 ())
  "\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 (")

  "\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
  };


 /**
  * Command to compose special characters from regular ones.
  * Also used by {@link TestUserProfile}.
  */
 public static LpexCommand composeCommand = new LpexCommand() {
  public boolean doCommand(LpexView lpexView, String parameters) {
   // clear any previous error message we might have issued
   lpexView.doDefaultCommand("set messageText");

   // NOTE: in order to accept *only* special characters in the given
   // sequence, enter this on an LPEX command line:
   //  set userParameter.Compose.onlySpecialCharacters on
   boolean onlySpecialCharacters =
    lpexView.queryOn("userParameter.Compose.onlySpecialCharacters");

   // NOTE: in order to accept the compose sequences *only* in their defined
   // order, enter this on an LPEX command line:
   //  set userParameter.Compose.onlyDefinedOrder on
   boolean onlyDefinedOrder =
    lpexView.queryOn("userParameter.Compose.onlyDefinedOrder");

   while (parameters.length() > 0)
    {
     // look greedily for a compose sequence at the start of parameters
     int index = findSpecialCharacter(parameters, onlyDefinedOrder);
     if (index == 0)
      {
       if (onlySpecialCharacters)
        {
         break; // not a special character, reject...
        }
      }

     // type in the (special) character
     String c = (index != 0)? _compose[index-1] : String.valueOf(parameters.charAt(0));
     if (!typeIn(lpexView, c))
      {
       // error while inserting: restate prompt with what's still left
       promptForCompose(lpexView, parameters);
       return false;
      }

     // there may be more sequences to compose - drop what we've used
     parameters = parameters.substring((index != 0)? _compose[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, parameters);
   return false;
   }
  };

 // Returns the index in our table of the composition sequence, if any,
 // found at the start of the given sequence.  Returns 0 if none.
 static int findSpecialCharacter(String sequence, boolean onlyDefinedOrder)
 {
  if (sequence.length() > 1)
   {
    // reversed possible sequence - see check below
    String reverseSequence = onlyDefinedOrder? null :
     new String(new char[] { sequence.charAt(1), sequence.charAt(0) });

    for (int i = 1; i < _compose.length; i += 2)
     {
      if (sequence.startsWith(_compose[i]))
       {
        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 &&
          _compose[i].length() == 2 &&
          _compose[i].equals(reverseSequence) &&
          !_compose[i].equals("AE") && !_compose[i].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);
  // also check "status" when enforcing text limit is supported...
  return success;
 }

 // Prompts the user for the compose sequence.
 // When user types in text and presses Enter, the "compose" command is run.
 static void promptForCompose(LpexView lpexView)
 {
  promptForCompose(lpexView, null);
 }

 // Prompts the user for the compose sequence with an optional initial sequence.
 // When user (types in text and) presses Enter, (a new) "compose" command is run.
 static void promptForCompose(LpexView lpexView, String composeSequence)
 {
  String text = (composeSequence != null)?
                 LpexStringTokenizer.addQuotes(composeSequence) : "";
  lpexView.doCommand("input \"Compose:\" " + text + " \"compose \"");
 }

 /**
  * Compose action that can be used when the <b>compose</b> command has been
  * already defined in that view.  Used by {@link TestUserProfile}.
  */
 public static LpexAction composeAction = new LpexAction() {
  public void doAction(LpexView lpexView) {
   promptForCompose(lpexView);
   }
  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);
 }

 /**
  * 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");
 }
}