home *** CD-ROM | disk | FTP | other *** search
/ Symantec Visual Cafe for Java 2.5 / symantec-visual-cafe-2.5-database-dev-edition.iso / Visual Cafe Pro v1.0 / SOURCE.BIN / FormattedTextField.java < prev    next >
Encoding:
Java Source  |  1997-06-19  |  18.0 KB  |  625 lines

  1. package symantec.itools.awt;
  2.  
  3. import java.awt.Dimension;
  4. import java.awt.Event;
  5. import java.awt.Font;
  6. import java.awt.FontMetrics;
  7. import java.awt.TextField;
  8.  
  9. //    01/29/97    TWB    Integrated changes from Windows 
  10.  
  11. /**
  12.  * Creates a box in which your user can type text. Text formatting logic is 
  13.  * applied to the user input.
  14.  * <p>
  15.  * Use FormattedTextField to
  16.  * <UL>
  17.  * <DT>╖ limit the type of text that can be entered in the text box</DT>
  18.  * <DT>╖ display text captured from the keyboard</DT>
  19.  * <DT>╖ edit a line of text</DT>
  20.  * <DT>╖ post an event based on text input from the keyboard</DT>
  21.  * </UL>
  22.  * To post FormattedTextField events or text to other components, use the 
  23.  * Interaction Wizard.
  24.  * <p>
  25.  * If the text box already contains text, the user can select the default text
  26.  * and delete or edit it.
  27.  * <p>
  28.  * Upon input error, FormattedTextField sends an error to the standard output 
  29.  * device and reads the next character. 
  30.  * <p>
  31.  * FormattedTextField does not support logical AND, OR or XOR constructs and 
  32.  * does not interactively prompt the user to retry input upon error. You must 
  33.  * write project source code to accomplish these tasks.
  34.  * <p>
  35.  * @version 1.0, Nov 26, 1996
  36.  * @author Symantec
  37.  */
  38.  
  39. public class FormattedTextField
  40.     extends TextField
  41. {
  42.     /**
  43.      * Mask place constant, mask char following ESCAPE will be placed into input string.
  44.      */
  45.     public final char ESCAPE                = '/';
  46.     /**
  47.      * Mask place constant, input must be a digit char.
  48.      */
  49.     public final char DIGIT                 = '9';
  50.     /**
  51.      * Mask place constant, input must be a sign (+ or -) char.
  52.      */
  53.     public final char SIGN                  = '+';
  54.     /**
  55.      * Mask place constant, input must be a digit or sign (+ or -) char.
  56.      */
  57.     public final char DIGIT_OR_SIGN         = '-';
  58.     /**
  59.      * Mask place constant, input must be an uppercase alpha char.
  60.      */
  61.     public final char ALPHA_UPPER           = 'A';
  62.     /**
  63.      * Mask place constant, input must be a lowercase alpha char.
  64.      */
  65.     public final char ALPHA_LOWER           = 'a';
  66.     /**
  67.      * Mask place constant, input must be an alpha char (it will be converted to uppercase).
  68.      */
  69.     public final char ALPHA_TO_UPPER        = 'U';
  70.     /**
  71.      * Mask place constant, input must be an alpha char (it will be converted to lowercase).
  72.      */
  73.     public final char ALPHA_TO_LOWER        = 'L';
  74.     /**
  75.      * Mask place constant, input must be a digit or an uppercase alpha char.
  76.      */
  77.     public final char ALPHANUMERIC_UPPER    = 'X';
  78.     /**
  79.      * Mask place constant, input must be a digit or a lowercase alpha char.
  80.      */
  81.     public final char ALPHANUMERIC_LOWER    = 'x';
  82.     /**
  83.      * Mask place constant, input must be a digit or alpha char (it will be converted to uppercase).
  84.      */
  85.     public final char ALPHANUMERIC_TO_UPPER = 'N';
  86.     /**
  87.      * Mask place constant, input must be a digit or alpha char (it will be converted to lowercase).
  88.      */
  89.     public final char ALPHANUMERIC_TO_LOWER = 'n';
  90.     /**
  91.      * Mask place constant, input can be any char.
  92.      */
  93.     public final char ANY                   = '*';
  94.  
  95.     /**
  96.      * Character constant, the backspace character.
  97.      */
  98.     protected final int BACKSPACE           = 8;
  99.     /**
  100.      * Character constant, the enter character.
  101.      */
  102.     protected final int ENTER               = 10;
  103.     /**
  104.      * Character constant, the delete character.
  105.      */
  106.     protected final int DEL                 = 127;
  107.  
  108.     /**
  109.      * True if the field is editable.
  110.      */
  111.     protected boolean editable;
  112.     /**
  113.      * The zero-relative index of the character after the insertion point caret.
  114.      */
  115.     protected int caret;
  116.     /**
  117.      * The character mask used to define legal field input.
  118.      */
  119.     protected String mask;
  120.     /**
  121.      * A string containing all the mask characters except the ones immediately after ESCAPE characters.
  122.      */
  123.     protected String maskStripEscapes;
  124.     /**
  125.      * The length of maskStripEscapes.
  126.      */
  127.     protected int maskStripEscapesLen;
  128.  
  129.     /**
  130.      * Constructs a new FormattedTextField. It can have 256 columns.
  131.      */
  132.     public FormattedTextField() {
  133.         this(256);
  134.     }
  135.  
  136.     /**
  137.      * Constructs a new FormattedTextField with the specified number of columns.
  138.      */
  139.     public FormattedTextField(int i) {
  140.         this("", i);
  141.     }
  142.  
  143.     /**
  144.      * Constructs a new FormattedTextField containing the specified text.
  145.      * It can have 256 columns.
  146.      */
  147.     public FormattedTextField(String s) {
  148.         this(s, 256);
  149.     }
  150.  
  151.     /**
  152.      * Constructs a new FormattedTextField containing the specified text and 
  153.      * able to have the specified number of columns.
  154.      */
  155.     public FormattedTextField(String s, int i) {
  156.         super(s, i);
  157.  
  158.         caret = 0;
  159.     }
  160.  
  161.     /**
  162.      * Sets the valid string format mask for this field.
  163.      * @see #getMask
  164.      */
  165.     public void setMask(String mask) {
  166.         this.mask = mask;
  167.         setFillMask();
  168.         maskStripEscapes = stripMaskEscapes();
  169.         maskStripEscapesLen = maskStripEscapes.length();
  170.     }
  171.  
  172.     /**
  173.      * Gets the current valid string format mask.
  174.      * @return the string that defines valid input into this field.
  175.      * @see #setMask
  176.      */
  177.     public String getMask() {
  178.         return mask;
  179.     }
  180.  
  181.     /**
  182.      * Sets the font used in the field.
  183.      * @see #getEditFont
  184.      */
  185.     public void setEditFont(Font f) {
  186.         super.setFont(f);
  187.         invalidate();
  188.     }
  189.  
  190.     /**
  191.      * Gets the font used in the field.
  192.      * @return the font used for editing in this field
  193.      * @see #getEditFont
  194.      */
  195.     public Font getEditFont() {
  196.         return getFont();
  197.     }
  198.  
  199.     /**
  200.      * Takes no action, use setEditFont instead.
  201.      * This is a standard Java AWT method which gets called to change
  202.      * the font used for drawing text in this component.
  203.      * It has been overridden to eliminate it, use setEditFont instead.
  204.      * @param f the new font to use for drawing text
  205.      * @see #setEditFont
  206.      * @see java.awt.Component#getFont
  207.      */
  208.     public void setFont(Font f) {
  209.     }
  210.  
  211.     /**
  212.      * Sets whether the text field is editable.
  213.      * @param f true if the text field is editable, false otherwise
  214.      * @see #getEditable
  215.      */
  216.     public void setEditable(boolean f) {
  217.         editable = f;
  218.  
  219.         super.setEditable(f);
  220.  
  221.         if (editable)
  222.             enable();
  223.         else
  224.             disable();
  225.     }
  226.  
  227.     /**
  228.      * Gets whether the text field is editable.
  229.      * @return true if the text field is editable, false otherwise
  230.      * @see #setEditable
  231.      */
  232.     public boolean getEditable() {
  233.         return editable;
  234.     }
  235.  
  236.     /**
  237.      * Returns the next valid character in the input string.
  238.      * @param s the input string
  239.      * @param start the zero-relative index of the first character to check in
  240.      * the input string
  241.      * @param mc the mask character used to determine which input characaters 
  242.      * are valid
  243.      */
  244.     protected int validChar(String s, int start, char mc) {
  245.         int len = s.length();
  246.         for (int i = start; i < len; ++i) {
  247.             char c = s.charAt(i);
  248.             switch (mc) {
  249.  
  250.                 case DIGIT :
  251.                     if (Character.isDigit(c))
  252.                         return i;
  253.                     break;
  254.  
  255.                 case SIGN :
  256.                     if (c == '+' || c == '-')
  257.                         return i;
  258.                     break;
  259.  
  260.                 case DIGIT_OR_SIGN :
  261.                     if (Character.isDigit(c) || c == '+' || c == '-')
  262.                         return i;
  263.                    break;
  264.  
  265.                 case ALPHA_UPPER :
  266.                     if (Character.isUpperCase(c))
  267.                         return i;
  268.                     break;
  269.  
  270.                 case ALPHA_LOWER :
  271.                     if (Character.isLowerCase(c))
  272.                         return i;
  273.                     break;
  274.  
  275.                 case ALPHANUMERIC_UPPER :
  276.                     if (Character.isUpperCase(c) || Character.isDigit(c))
  277.                         return i;
  278.                     break;
  279.  
  280.                 case ALPHANUMERIC_LOWER :
  281.                     if (Character.isLowerCase(c) || Character.isDigit(c))
  282.                         return i;
  283.                     break;
  284.  
  285.                 case ALPHA_TO_UPPER :
  286.                     if (Character.isUpperCase(c) || Character.isLowerCase(c))
  287.                         return i;
  288.                     break;
  289.  
  290.                 case ALPHA_TO_LOWER :
  291.                     if (Character.isUpperCase(c) || Character.isLowerCase(c))
  292.                         return i;
  293.                     break;
  294.  
  295.                 case ALPHANUMERIC_TO_UPPER :
  296.                     if (Character.isUpperCase(c) || Character.isLowerCase(c) || Character.isDigit(c))
  297.                         return i;
  298.                     break;
  299.  
  300.                 case ALPHANUMERIC_TO_LOWER :
  301.                     if (Character.isUpperCase(c) || Character.isLowerCase(c) || Character.isDigit(c))
  302.                         return i;
  303.                     break;
  304.  
  305.                 case ANY :
  306.                     return i;
  307.  
  308.             }
  309.         }
  310.  
  311.         return -1;
  312.     }
  313.  
  314.     /**
  315.      * Sets the text field's contents to be an input template. The mask ESCAPE
  316.      * characters will be place in the field. All other mask characters will
  317.      * have a space in place of them. For example, a mask of "99/-9/-9" results
  318.      * in the text field getting set to "  - - ".
  319.      */
  320.     protected void setFillMask() {
  321.         int ml = mask.length();
  322.         int mi = 0;
  323.         String ns = "";
  324.  
  325.         while (mi < ml) {
  326.             char mc = mask.charAt(mi++);
  327.             ns += mc == ESCAPE ? mask.charAt(mi++) : ' ';
  328.         }
  329.  
  330.         setText(ns);
  331.     }
  332.  
  333.     /**
  334.      * Returns a formatted string, given an input string and the current valid
  335.      * character format mask.
  336.      * @param s the input string
  337.      * @return the input string formatted using the current valid character 
  338.      * format mask.
  339.      */
  340.     protected String applyMask(String s) {
  341.         boolean fill = false;
  342.         int len = mask.length();
  343.         int mi = 0;
  344.         int si = 0;
  345.         String ns = "";
  346.  
  347.         while (mi < len) {
  348.             char mc = mask.charAt(mi++);
  349.  
  350.             if (mc != ESCAPE) {
  351.                 if (!fill) {
  352.                     si = validChar(s, si, mc);
  353.                     if (si < 0) {
  354.                         fill = true;
  355.                         ns += ' ';
  356.                         continue;
  357.                     }
  358.  
  359.                     char sc = s.charAt(si++);
  360.  
  361.                     switch (mc) {
  362.  
  363.                         case ALPHA_TO_UPPER :
  364.                             if (Character.isLowerCase(sc))
  365.                                 sc = Character.toUpperCase(sc);
  366.                             break;
  367.  
  368.                         case ALPHA_TO_LOWER :
  369.                             if (Character.isUpperCase(sc))
  370.                                 sc = Character.toLowerCase(sc);
  371.                             break;
  372.  
  373.                         case ALPHANUMERIC_TO_UPPER :
  374.                             if (Character.isLowerCase(sc))
  375.                                 sc = Character.toUpperCase(sc);
  376.                             break;
  377.  
  378.                         case ALPHANUMERIC_TO_LOWER :
  379.                             if (Character.isUpperCase(sc))
  380.                                 sc = Character.toLowerCase(sc);
  381.                             break;
  382.  
  383.                     }
  384.  
  385.                     ns += String.valueOf(sc);
  386.  
  387.                 } else
  388.                     ns += ' ';
  389.             } else
  390.                 ns += mask.charAt(mi++);
  391.         }
  392.  
  393.         return ns;
  394.     }
  395.  
  396.     private boolean isEscape(int index) {
  397.         return (index < 0 || index >= maskStripEscapesLen) ? false : (maskStripEscapes.charAt(index) == ESCAPE);
  398.     }
  399.  
  400.     private String stripMaskEscapes() {
  401.         int ml = mask.length();
  402.         int mi = 0;
  403.         String nm = "";
  404.  
  405.         while (mi < ml) {
  406.             char mc = mask.charAt(mi++);
  407.             if (mc == ESCAPE)
  408.                 ++mi;
  409.             nm += mc;
  410.         }
  411.  
  412.         return nm;
  413.     }
  414.  
  415.     /**
  416.      * Strips escape characters from the given string.
  417.      * For example, a mask of "99/-9/-9" and an input string of "12-3-4"
  418.      * results in "1234".
  419.      * @param s the input string
  420.      * @return the input string stripped of mask escape characters.
  421.      */
  422.     protected String stripMask(String s) {
  423.         int sl = s.length();
  424.         int i = 0;
  425.         String ns = "";
  426.  
  427.         while (i < maskStripEscapesLen && i < sl)
  428.             if (isEscape(i))
  429.                 ++i;
  430.             else
  431.                 ns += s.charAt(i++);
  432.  
  433.         return ns;
  434.     }
  435.  
  436.     private boolean processChar(int key) {
  437.         int selStart = getSelectionStart();
  438.         int selEnd = getSelectionEnd();
  439.         boolean bs = key == BACKSPACE;
  440.         boolean del = key == DEL;
  441.  
  442.         boolean isSelection = selStart != selEnd;
  443.  
  444.         if (!isSelection) {
  445.             if (bs)
  446.                 selStart = selEnd - 1;
  447.             else if (del)
  448.                 selEnd = selStart + 1;
  449.         }
  450.  
  451.         int i = bs || del || isSelection ? selStart : selEnd;
  452.  
  453.         if (i < 0)
  454.             i = 0;
  455.  
  456.         boolean wasInEscape = isEscape(i > 0 ? i : 0);
  457.         boolean wasAtEscape = isEscape(bs || del ? (i > 0 ? i - 1 : 0) : i + 1);
  458.  
  459.         boolean inEscapeAtStart;
  460.         if (wasInEscape) {
  461.             inEscapeAtStart = true;
  462.             int j = i;
  463.             while (j >= 0)
  464.                 if (!isEscape(j--)) {
  465.                     inEscapeAtStart = false;
  466.                     break;
  467.                 }
  468.  
  469.             if (inEscapeAtStart)
  470.                 while (i < maskStripEscapesLen && isEscape(i++)) {}
  471.  
  472.         } else
  473.             inEscapeAtStart = false;
  474.  
  475.         // (for selection processing, should use a startSelAdj and endSelAdj)
  476.         int selAdj = 0;
  477.  
  478.         while (i >= 0)
  479.             if (isEscape(i--))
  480.                 ++selAdj;
  481.  
  482.         if (!inEscapeAtStart && wasInEscape && !bs   && !del)
  483.             --selAdj;
  484.  
  485.         selStart -= selAdj;        // (not necessarily correct if start < escape < end)
  486.         selEnd -= selAdj;
  487.  
  488.         if (selStart < 0)
  489.             selStart = 0;
  490.  
  491.         if (selEnd < 0)
  492.             selEnd = 0;
  493.  
  494.         String s = stripMask(getText());
  495.         int sl = s.length();
  496.  
  497.         String k = bs || del ? "" : String.valueOf((char)key);
  498.         String l = selStart > 0 ? s.substring(0, selStart) : "";
  499.         String r = selEnd < sl ? s.substring(selEnd) : "";
  500.         String es = bs || del ? l + r : l + k + r;
  501.         String ns = applyMask(es);
  502.  
  503.         if (bs || del || !s.equals(stripMask(ns))) {
  504.             setText(ns);
  505.  
  506.             if (bs || del) {
  507.                 --selAdj;
  508.  
  509.                 caret = selStart + selAdj;
  510.  
  511.                 if (wasAtEscape && caret > 0 && isEscape(caret)) {
  512.                     i = caret;
  513.                     while (i >= 0)
  514.                         if (isEscape(i--))
  515.                             --caret;
  516.                         else
  517.                             break;
  518.                 }
  519.  
  520.             } else {
  521.                 caret = (isSelection ? selStart : selEnd) + selAdj;
  522.  
  523.                 if (caret < sl && isEscape(caret + 1)) {
  524.                     i = caret + 1;
  525.                     while (i < sl)
  526.                         if (isEscape(i++))
  527.                             ++caret;
  528.                         else
  529.                             break;
  530.                 }
  531.  
  532.             }
  533.  
  534.             select(caret + 1, caret + 1);
  535.         }
  536.  
  537.         return true;
  538.     }
  539.  
  540.     private void processKeyAction(int key) {
  541.         switch (key) {
  542.  
  543.             case Event.LEFT :
  544.                 if (--caret < 0)
  545.                     caret = 0;
  546.                 break;
  547.  
  548.             case Event.RIGHT :
  549.                 if (++caret >= maskStripEscapesLen)
  550.                     caret = maskStripEscapesLen;
  551.                 break;
  552.  
  553.             case Event.HOME :
  554.                 caret = 0;
  555.                 break;
  556.  
  557.             case Event.END :
  558.                 caret = maskStripEscapesLen;
  559.                 break;
  560.  
  561.         }
  562.     }
  563.  
  564.     /**
  565.      * Processes KEY_PRESS and KEY_ACTION events.
  566.      * This is a standard Java AWT method which gets called by the AWT
  567.      * method handleEvent() in response to receiving a KEY_PRESS or
  568.      * KEY_ACTION event. These events occur when this component has the focus
  569.      * and the user presses a "normal" or an "action" (F1, page up, etc) key.
  570.      * 
  571.      * @param evt the Event
  572.      * @param key the key that was pressed
  573.      * @return true if the event was handled
  574.      * @see java.awt.Component#keyUp
  575.      * @see java.awt.Component#handleEvent
  576.      */
  577.     public boolean keyDown(Event evt, int key) {
  578.         //if the key was tab, pass up the chain
  579.         if(key == 9) return super.keyDown(evt, key);
  580.  
  581.         switch (evt.id) {
  582.  
  583.             case Event.KEY_PRESS :
  584.                 if (key != ENTER)
  585.                     return processChar(key);
  586.                 break;
  587.  
  588.             case Event.KEY_ACTION :
  589.                 processKeyAction(evt.key);
  590.                 break;
  591.  
  592.         }
  593.  
  594.         return super.keyDown(evt, key);
  595.     }
  596.  
  597.     /**
  598.      * Returns the recommended dimensions to properly display this component.
  599.      * This is a standard Java AWT method which gets called to determine
  600.      * the recommended size of this component. 
  601.      *
  602.      * @see #minimumSize
  603.      */
  604.     public Dimension preferredSize() {
  605.         Font f = getFont();
  606.         if (f != null) {
  607.             FontMetrics fm = getFontMetrics(f);
  608.             return new Dimension(fm.stringWidth("ABCDE") + 4, fm.getHeight() + 4);
  609.         }
  610.         return new Dimension(30, 15);
  611.     }
  612.  
  613.     /**
  614.      * Returns the minimum dimensions to properly display this component.
  615.      * This is a standard Java AWT method which gets called to determine
  616.      * the minimum size of this component. 
  617.      *
  618.      * @see #preferredSize
  619.      */
  620.     public Dimension minimumSize() {
  621.         return preferredSize();
  622.     }
  623. }
  624.  
  625.