home *** CD-ROM | disk | FTP | other *** search
/ CD Ware Multimedia 1999 February / CDW0299.iso / Demos / Cafe / Source.bin / FormattedTextField.java < prev    next >
Encoding:
Java Source  |  1998-03-19  |  35.8 KB  |  1,176 lines

  1. package symantec.itools.awt;
  2.  
  3. import java.awt.Toolkit;
  4. import java.awt.Dimension;
  5. import java.awt.Event;
  6. import java.awt.Font;
  7. import java.awt.FontMetrics;
  8. import java.awt.TextField;
  9. import java.awt.event.*;
  10.  
  11. //    05/29/97    RKM    Convert to 1.1 event model
  12. //                    Deprecated getter for booleans, and added is
  13. //                    Used add/removeNotify to listen to events
  14. //                    WARNING: Could not test component due bug in MRJ,
  15. //                    doesn't work InputEvent.consume()
  16. //  05/14/97    TNM If there is not mask it works like java.awt.TextField
  17. //    01/29/97    TWB    Integrated changes from Windows
  18. //  07/30/97    CAR inner adaptor class implements java.io.Serializable
  19. //  08/26/97    CAR changed keyPressed to call processchar with getKeyChar instead of getKeyCode
  20. //  10/09/97    LAB Re-wrote processChar.  This fixes many issues with this component.  Modified
  21. //                    several functions, and added several, in this process.  These modifications
  22. //                    address Mac bug #3383, #7636, #7638 and #8517. !!! LAB !!! Note, processChar
  23. //                    is by no means optimized; this should be done at a later date.  Deprecated
  24. //                    setEditFont and getEditFont.  Removed overriden setFont.
  25. //  10/11/97    LAB Removed unnecessary items related to overriden editable.  Handle empty
  26. //                    string masks correctly now (Addresses Mac Bug #9198).  Fixed problems
  27. //                    between setText and setMask confusing the unformattedCurrent string and
  28. //                    Re-wrote applyMask to correctly update the unformattedCurrent string
  29. //                    (Addresses Mac Bugs #9199, #9168, and #9167).  Moved filtering code
  30. //                    into filterChar to unify applyMask and applyMaskToChar.  Added
  31. //                    getFormattedText to return the contents of the text field, since
  32. //                    getText now returns the unformatted string.
  33. // 18/2/98          fbeninc cheched for \0 sent with shift key.line790/1006
  34. /**
  35.  * Creates a box in which the user can type text. Text formatting logic is
  36.  * applied to the user input.
  37.  * <p>
  38.  * Use FormattedTextField to
  39.  * <UL>
  40.  * <DT>╖ Limit the type of text that can be entered in the text box.</DT>
  41.  * <DT>╖ Display text captured from the keyboard.</DT>
  42.  * <DT>╖ Edit a line of text.</DT>
  43.  * <DT>╖ Post an event based on text input from the keyboard.</DT>
  44.  * </UL>
  45.  * If the text box already contains text, the user can select the default text
  46.  * and delete or edit it.
  47.  * <p>
  48.  * @version 1.1, August 30, 1997
  49.  * @author Symantec
  50.  */
  51. public class FormattedTextField extends TextField
  52. {
  53.     /**
  54.      * Character mask constant, the character following this ESCAPE will be placed into input string.
  55.      */
  56.     public final char ESCAPE                = '/';
  57.     /**
  58.      * Character mask constant, input must be a digit character.
  59.      */
  60.     public final char DIGIT                 = '9';
  61.     /**
  62.      * Character mask constant, input must be a sign (+ or -) character.
  63.      */
  64.     public final char SIGN                  = '+';
  65.     /**
  66.      * Character mask constant, input must be a digit or sign (+ or -) character.
  67.      */
  68.     public final char DIGIT_OR_SIGN         = '-';
  69.     /**
  70.      * Character mask constant, input must be an uppercase alpha character.
  71.      */
  72.     public final char ALPHA_UPPER           = 'A';
  73.     /**
  74.      * Character mask constant, input must be a lowercase alpha character.
  75.      */
  76.     public final char ALPHA_LOWER           = 'a';
  77.     /**
  78.      * Character mask constant, input must be an alpha character (it will be converted to uppercase).
  79.      */
  80.     public final char ALPHA_TO_UPPER        = 'U';
  81.     /**
  82.      * Character mask constant, input must be an alpha character (it will be converted to lowercase).
  83.      */
  84.     public final char ALPHA_TO_LOWER        = 'L';
  85.     /**
  86.      * Character mask constant, input must be a digit or an uppercase alpha character.
  87.      */
  88.     public final char ALPHANUMERIC_UPPER    = 'X';
  89.     /**
  90.      * Character mask constant, input must be a digit or a lowercase alpha character.
  91.      */
  92.     public final char ALPHANUMERIC_LOWER    = 'x';
  93.     /**
  94.      * Character mask constant, input must be a digit or alpha character (it will be converted to uppercase).
  95.      */
  96.     public final char ALPHANUMERIC_TO_UPPER = 'N';
  97.     /**
  98.      * Character mask constant, input must be a digit or alpha character (it will be converted to lowercase).
  99.      */
  100.     public final char ALPHANUMERIC_TO_LOWER = 'n';
  101.     /**
  102.      * Character mask constant, input can be any character.
  103.      */
  104.     public final char ANY                   = '*';
  105.  
  106.     /**
  107.      * Constructs a new FormattedTextField. It can have 256 columns.
  108.      */
  109.     public FormattedTextField()
  110.     {
  111.         this(256);
  112.     }
  113.  
  114.     /**
  115.      * Constructs a new FormattedTextField with the specified number of columns.
  116.      * @param i the number of character columns
  117.      */
  118.     public FormattedTextField(int i)
  119.     {
  120.         this("", i);
  121.     }
  122.  
  123.     /**
  124.      * Constructs a new FormattedTextField containing the specified text.
  125.      * It can have 256 columns.
  126.      * @param s the field text
  127.      */
  128.     public FormattedTextField(String s)
  129.     {
  130.         this(s, 256);
  131.     }
  132.  
  133.     /**
  134.      * Constructs a new FormattedTextField containing the specified text and
  135.      * able to have the specified number of columns.
  136.      * @param s the field text
  137.      * @param i the number of character columns
  138.      */
  139.     public FormattedTextField(String s, int i)
  140.     {
  141.         super(s, i);
  142.         caret = 0;
  143.         unformattedCurrent    = "";
  144.         designTimeText        = "";
  145.     }
  146.  
  147.     /**
  148.      * Sets the valid string format mask for this field.
  149.      * @param mask the new valid string format mask
  150.      * @see #getMask
  151.      */
  152.     public void setMask(String mask)
  153.     {
  154.         if(mask != null && mask.length() < 1)
  155.         {
  156.             this.mask = null;
  157.         }
  158.         else
  159.         {
  160.             this.mask = mask;
  161.         }
  162.             
  163.         maskStripEscapes = stripMaskEscapes();
  164.         maskStripEscapesLen = maskStripEscapes.length();
  165.         //Apply the new mask to our current text.
  166.         setText(getText());
  167.     }
  168.  
  169.     /**
  170.      * Gets the current valid string format mask.
  171.      * @return the string that defines valid input into this field.
  172.      * @see #setMask
  173.      */
  174.     public String getMask()
  175.     {
  176.         return mask;
  177.     }
  178.     
  179.     /**
  180.      * @deprecated
  181.      */
  182.     public void setEditFont(Font f)
  183.     {
  184.         setFont(f);
  185.     }
  186.  
  187.     /**
  188.      * @deprecated
  189.      */
  190.     public Font getEditFont()
  191.     {
  192.         return getFont();
  193.     }
  194.  
  195.     /**
  196.      * Sets the text of this TextComponent to the specified text
  197.      * after passing the specified text throught the filter.
  198.      * @param t the new text to be set
  199.      * @see #getText
  200.      */
  201.     public void setText(String t)
  202.     {
  203.         //Keep the original string around at design time to return from getText.
  204.         if(java.beans.Beans.isDesignTime())
  205.             designTimeText = t;
  206.  
  207.         String newString;
  208.         
  209.         if(mask == null)
  210.         {
  211.             newString    = t;
  212.             unformattedCurrent = newString;
  213.         }
  214.         else
  215.         {
  216.             newString = applyMask(t);
  217.         }
  218.         super.setText(newString);
  219.     }
  220.  
  221.     /**
  222.      * Gets the unformatted string of filtered characters.
  223.      * @return the string of unformatted characters that were accepted by
  224.      * the mask filtering process.  At design time this returns the text passed
  225.      * to setText.
  226.      * @see #setText
  227.      * @see #getFormattedText
  228.      */
  229.     public String getText()
  230.     {
  231.         if(java.beans.Beans.isDesignTime())
  232.             return designTimeText;
  233.         else
  234.             return unformattedCurrent;
  235.     }
  236.  
  237.     /**
  238.      * Gets the displayed contents of the component.
  239.      * @return the string currently displayed by the component.
  240.      * @see #setText
  241.      * @see #getText
  242.      */
  243.     public String getFormattedText()
  244.     {
  245.         return super.getText();
  246.     }
  247.  
  248.     /**
  249.      * Sets whether the text field is editable.
  250.      * @param f true if the text field is editable, false otherwise
  251.      * @see java.awt.TextComponent#isEditable
  252.      */
  253.     public void setEditable(boolean f)
  254.     {
  255.         super.setEditable(f);
  256.  
  257.         setEnabled(f);
  258.     }
  259.  
  260.     /**
  261.      * Returns a string defining an input template. The mask ESCAPE
  262.      * characters will be place in the string. All other mask characters will
  263.      * have spaces in place of them. For example, a mask of "99/-9/-9" results
  264.      * in a string set to "  - - ".
  265.      * @return the template string defined by the mask, with spaces as placeholders.
  266.      */
  267.     public String getMaskTemplateString()
  268.     {
  269.         String newString = "";
  270.         if(mask != null)
  271.         {
  272.             int ml = mask.length();
  273.             int maskIndex;
  274.             char maskChar;
  275.  
  276.             for (maskIndex = 0; maskIndex < ml; maskIndex++)
  277.             {
  278.                 maskChar = mask.charAt(maskIndex);
  279.                 newString += maskChar == ESCAPE ? mask.charAt(++maskIndex) : ' ';
  280.             }
  281.         }
  282.         return newString;
  283.     }
  284.     
  285.     /**
  286.      * Clears the contents of the field and sets the text field's contents to
  287.      * be an input template. The mask ESCAPE characters will be place in the
  288.      * field. All other mask characters will have spaces in place of them.
  289.      * For example, a mask of "99/-9/-9" results in the text field getting
  290.      * set to "  - - ".
  291.      */
  292.     public void setFillMask()
  293.     {
  294.         String newString = getMaskTemplateString();
  295.         unformattedCurrent = "";
  296.         super.setText(newString);
  297.     }
  298.  
  299.     /**
  300.      * Tells this component that it has been added to a container.
  301.      * This is a standard Java AWT method which gets called by the AWT when
  302.      * this component is added to a container. Typically, it is used to
  303.      * create this component's peer.
  304.      *
  305.      * It has been overridden here to call addKeyListener.
  306.      *
  307.      * @see #removeNotify
  308.      */
  309.     public synchronized void addNotify()
  310.     {
  311.  
  312.         if (keyListener == null)
  313.         {
  314.             keyListener = new Key();
  315.             addKeyListener(keyListener);
  316.         }
  317.         
  318.         super.addNotify();
  319.     }
  320.  
  321.     /**
  322.      * Tells this component that it is being removed from a container.
  323.      * This is a standard Java AWT method which gets called by the AWT when
  324.      * this component is removed from a container. Typically, it is used to
  325.      * destroy the peers of this component and all its subcomponents.
  326.      *
  327.      * It has been overridden here to call removeKeyListener.
  328.      *
  329.      * @see #addNotify
  330.      */
  331.     public synchronized void removeNotify()
  332.     {
  333.         if (keyListener != null)
  334.         {
  335.             removeKeyListener(keyListener);
  336.             keyListener = null;
  337.         }
  338.  
  339.         super.removeNotify();
  340.     }
  341.  
  342.     /**
  343.      * @deprecated As of JDK version 1.1,
  344.      * replaced by getPreferredSize().
  345.      */
  346.     public Dimension preferredSize() {
  347.         Font f = getFont();
  348.         if (f != null) {
  349.             FontMetrics fm = getFontMetrics(f);
  350.             if (mask != null) {
  351.                 int w = fm.stringWidth("W") * maskStripEscapesLen + 4;
  352.                 int h = fm.getHeight() + 4;
  353.                 return new Dimension(w, h);
  354.             }
  355.             else {
  356.                 return new Dimension(fm.stringWidth("ABCDE") + 4, fm.getHeight() + 4);
  357.             }
  358.         }
  359.         return new Dimension(30, 15);
  360.     }
  361.  
  362.     /**
  363.      * Returns the recommended dimensions to properly display this component.
  364.      * This is a standard Java AWT method which gets called to determine
  365.      * the recommended size of this component.
  366.      *
  367.      * @see #getMinimumSize
  368.      */
  369.     public Dimension getPreferredSize() {
  370.         return preferredSize();
  371.     }
  372.  
  373.     /**
  374.      * @deprecated As of JDK version 1.1,
  375.      * replaced by getMinimumSize().
  376.      */
  377.     public Dimension minimumSize() {
  378.         return preferredSize();
  379.     }
  380.  
  381.     /**
  382.      * Returns the minimum dimensions to properly display this component.
  383.      * This is a standard Java AWT method which gets called to determine
  384.      * the minimum size of this component.
  385.      *
  386.      * @see #getPreferredSize
  387.      */
  388.     public Dimension getMinimumSize() {
  389.         return minimumSize();
  390.     }
  391.  
  392.     /**
  393.      * Processes KEY_PRESS and KEY_ACTION events.
  394.      * This is a standard Java AWT method which gets called by the AWT
  395.      * method handleEvent() in response to receiving a KEY_PRESS or
  396.      * KEY_ACTION event. These events occur when this component has the focus
  397.      * and the user presses a "normal" or an "action" (F1, page up, etc) key.
  398.      *
  399.      * @param evt the Event
  400.      * @param key the key that was pressed
  401.      * @return true if the event was handled
  402.      * @see java.awt.Component#keyUp
  403.      * @see java.awt.Component#handleEvent
  404.      */
  405.  
  406.     class Key extends KeyAdapter implements java.io.Serializable
  407.     {
  408.         /**
  409.          * Invoked when a key has been pressed.
  410.          */
  411.         public void keyPressed(KeyEvent e)
  412.         {
  413.             int key = e.getKeyCode();
  414.  
  415.             switch (key)
  416.             {
  417.                 case KeyEvent.VK_LEFT:
  418.                 {
  419.                     if (--caret < 0)
  420.                         caret = 0;
  421.                     break;
  422.                 }
  423.  
  424.                 case KeyEvent.VK_RIGHT:
  425.                 {
  426.                     if (++caret >= maskStripEscapesLen)
  427.                         caret = maskStripEscapesLen;
  428.                     break;
  429.                 }
  430.  
  431.                 case KeyEvent.VK_HOME:
  432.                 {
  433.                     caret = 0;
  434.                     break;
  435.                 }
  436.  
  437.                 case KeyEvent.VK_END:
  438.                 {
  439.                     caret = maskStripEscapesLen;
  440.                     break;
  441.                 }
  442.  
  443.                 case KeyEvent.VK_ENTER:
  444.                 case KeyEvent.VK_TAB:
  445.                 {
  446.                     //Do nothing
  447.                     break;
  448.                 }
  449.  
  450.                 default:
  451.                 {
  452.                     processChar(e.getKeyChar());
  453.  
  454.                     e.consume();
  455.                 }
  456.             }
  457.         }
  458.     }
  459.     
  460.     /**
  461.      * Returns the next valid character in the input string.
  462.      * @param s the input string
  463.      * @param start the zero-relative index of the first character to check in
  464.      * the input string
  465.      * @param maskChar the mask character used to determine which input characters
  466.      * are valid
  467.      */
  468.     protected int validChar(String s, int start, char maskChar)
  469.     {
  470.         boolean isReturn;
  471.         int len = s.length();
  472.         for (int i = start; i < len; ++i)
  473.         {
  474.             isReturn = false;
  475.             char c = s.charAt(i);
  476.             switch (maskChar)
  477.             {
  478.                 case DIGIT :
  479.                     if (Character.isDigit(c))
  480.                         isReturn = true;
  481.                     break;
  482.  
  483.                 case SIGN :
  484.                     if (c == '+' || c == '-')
  485.                         isReturn = true;
  486.                     break;
  487.  
  488.                 case DIGIT_OR_SIGN :
  489.                     if (Character.isDigit(c) || c == '+' || c == '-')
  490.                         isReturn = true;
  491.                    break;
  492.  
  493.                 case ALPHA_UPPER :
  494.                     if (Character.isUpperCase(c))
  495.                         isReturn = true;
  496.                     break;
  497.  
  498.                 case ALPHA_LOWER :
  499.                     if (Character.isLowerCase(c))
  500.                         isReturn = true;
  501.                     break;
  502.  
  503.                 case ALPHANUMERIC_UPPER :
  504.                     if (Character.isUpperCase(c) || Character.isDigit(c))
  505.                         isReturn = true;
  506.                     break;
  507.  
  508.                 case ALPHANUMERIC_LOWER :
  509.                     if (Character.isLowerCase(c) || Character.isDigit(c))
  510.                         isReturn = true;
  511.                     break;
  512.  
  513.                 case ALPHA_TO_UPPER :
  514.                     if (Character.isUpperCase(c) || Character.isLowerCase(c))
  515.                         isReturn = true;
  516.                     break;
  517.  
  518.                 case ALPHA_TO_LOWER :
  519.                     if (Character.isUpperCase(c) || Character.isLowerCase(c))
  520.                         isReturn = true;
  521.                     break;
  522.  
  523.                 case ALPHANUMERIC_TO_UPPER :
  524.                     if (Character.isUpperCase(c) || Character.isLowerCase(c) || Character.isDigit(c))
  525.                         isReturn = true;
  526.                     break;
  527.  
  528.                 case ALPHANUMERIC_TO_LOWER :
  529.                     if (Character.isUpperCase(c) || Character.isLowerCase(c) || Character.isDigit(c))
  530.                         isReturn = true;
  531.                     break;
  532.  
  533.                 case ANY :
  534.                     isReturn = true;
  535.                     break;
  536.             }
  537.             if(isReturn)
  538.                 return i;
  539.         }
  540.  
  541.         return -1;
  542.     }
  543.  
  544.     /**
  545.      * Returns a formatted string, given an input string and the current valid
  546.      * string format mask.
  547.      * @param s the input string
  548.      * @return the input string formatted using the current valid string
  549.      * format mask.
  550.      */
  551.     protected String applyMask(String s)
  552.     {
  553.         //If there is no mask, there is no formatting needed.
  554.         if(mask == null)
  555.         {
  556.             unformattedCurrent = s;
  557.             return s;
  558.         }
  559.         
  560.         //Reset the unformatted string
  561.         unformattedCurrent = "";
  562.         
  563.         int maskLength = mask.length();
  564.         int stringLength = s == null ? 0 : s.length();
  565.         char maskChar;
  566.         char stringChar;
  567.         String newString = "";
  568.         boolean isValidChar;
  569.         
  570.         int maskIndex    = 0;
  571.         int stringIndex    = 0;
  572.         while(stringIndex < stringLength && maskIndex < maskLength)
  573.         {
  574.             maskChar = mask.charAt(maskIndex);
  575.             
  576.             //If the maskChar is a escape sequence initiator, add the next character to the display string.
  577.             if(maskChar == ESCAPE)
  578.             {
  579.                 ++maskIndex;
  580.                 if(maskIndex < maskLength)
  581.                     newString += mask.charAt(maskIndex);
  582.                 ++maskIndex;
  583.             }
  584.             else
  585.             {
  586.                 stringChar = s.charAt(stringIndex);
  587.                 isValidChar = validChar("" + stringChar, 0, maskChar) != -1 ? true : false;
  588.                 
  589.                 //If the character is valid at the index, as defined by the maskChar...
  590.                 if(isValidChar)
  591.                 {
  592.                     //Apply any transformations needed to the character.
  593.                     stringChar =  filterChar(stringChar, maskChar);
  594.                     //Add the character to the string to display.
  595.                     newString += stringChar;
  596.                     //Add the character to the unformatted string to keep track of characters that
  597.                     //were accepted, and not part of the mask.
  598.                     unformattedCurrent += stringChar;
  599.                     
  600.                     ++maskIndex;
  601.                 }
  602.                 ++stringIndex;
  603.             }
  604.         }
  605.         //If the string didn't fill the entire mask, then pad the display string with the mask template
  606.         int newLength = newString.length();
  607.         if(newLength < maskStripEscapesLen)
  608.         {
  609.             
  610.             String maskTemplate = getMaskTemplateString();
  611.             
  612.             newString += maskTemplate.substring(newLength > 0 ? newLength - 1 : 0);
  613.         }
  614.         return newString;
  615.     }
  616.     
  617.     /**
  618.      * Apply the mask at index to the given character.
  619.      * @param char targetChar the character to apply the mask to.
  620.      * @param int index the index in the mask to use on the given character.
  621.      * @return A string with the given character with the mask
  622.      * at the given index applied. An empty string if the character was
  623.      * invalid or if the given index was an escape character index.  
  624.      */
  625.     protected String applyMaskToChar(char targetChar, int index)
  626.     {
  627.         if(mask == null)
  628.             return ("" + targetChar);
  629.  
  630.  
  631.         if (isEscape(index))
  632.         {
  633.             return "";
  634.         }
  635.         else
  636.         {
  637.             //Count the number of escape characters before the index so we have a valid index into the mask
  638.             int numEscapes = 0;
  639.             for(int i = 0; i < index; ++i)
  640.             {
  641.                 if(isEscape(i))
  642.                     numEscapes++;
  643.             }
  644.  
  645.             char maskChar = mask.charAt(index + numEscapes);
  646.                 
  647.             boolean isValidChar = validChar("" + targetChar, 0, maskChar) != -1 ? true : false;
  648.             
  649.             if(isValidChar)
  650.             {
  651.                 targetChar =  filterChar(targetChar, maskChar);
  652.                 return (String.valueOf(targetChar));
  653.             }
  654.             else
  655.             {
  656.                 return "";
  657.             }
  658.         }
  659.     }
  660.  
  661.     /**
  662.      * Filters the given target character with the given mask character
  663.      * @param char targetChar the character to apply the filter to.
  664.      * @param char maskChar the character to use as a filter.  Should match
  665.      * one of the pre-defined types.
  666.      * @see #applyMask
  667.      * @see #applyMaskToChar
  668.      * @see #ALPHA_TO_UPPER
  669.      * @see #ALPHA_TO_LOWER
  670.      * @see #ALPHANUMERIC_TO_UPPER
  671.      * @see #ALPHANUMERIC_TO_LOWER
  672.      */
  673.     protected char filterChar(char targetChar, char maskChar)
  674.     {
  675.         char returnChar = targetChar;
  676.         
  677.         switch (maskChar)
  678.         {
  679.             case ALPHA_TO_UPPER:
  680.                 if (Character.isLowerCase(targetChar))
  681.                     returnChar = Character.toUpperCase(targetChar);
  682.                 break;
  683.  
  684.             case ALPHA_TO_LOWER:
  685.                 if (Character.isUpperCase(targetChar))
  686.                     returnChar = Character.toLowerCase(targetChar);
  687.                 break;
  688.  
  689.             case ALPHANUMERIC_TO_UPPER:
  690.                 if (Character.isLowerCase(targetChar))
  691.                     returnChar = Character.toUpperCase(targetChar);
  692.                 break;
  693.  
  694.             case ALPHANUMERIC_TO_LOWER:
  695.                 if (Character.isUpperCase(targetChar))
  696.                     returnChar = Character.toLowerCase(targetChar);
  697.                 break;
  698.         }
  699.         return returnChar;
  700.     }
  701.  
  702.     /**
  703.      * Strips escape characters from the given string.
  704.      * For example, a mask of "99/-9/-9" and an input string of "12-3-4"
  705.      * results in "1234".
  706.      * @param s the input string
  707.      * @return the input string stripped of mask escape characters.
  708.      */
  709.     protected String stripMask(String s)
  710.     {
  711.         int sl = s.length();
  712.         int i = 0;
  713.         String newString = "";
  714.  
  715.         while (i < maskStripEscapesLen && i < sl)
  716.         {
  717.             if (isEscape(i))
  718.                 ++i;
  719.             else
  720.                 newString += s.charAt(i++);
  721.         }
  722.  
  723.         return newString;
  724.     }
  725.  
  726.     /**
  727.      * Handles KEY_PRESS processing.
  728.      * @param int the key that was pressed
  729.      */
  730.     protected void processChar(int key)
  731.     {
  732.         int            selStart            = getSelectionStart();
  733.         int            selEnd                = getSelectionEnd();
  734.         boolean    isSelection            = (selStart != selEnd);
  735.         boolean    isBackspace            = (key == BACKSPACE);
  736.         boolean    isDelete            = (key == DEL);
  737.         String        keyString            = isBackspace || isDelete ? "" : String.valueOf((char)key);
  738.         String     currentString        = getFormattedText();
  739.         int            currentLength        = currentString.length();
  740.         String        leftOfSelection        = selStart > 0 ? currentString.substring(0, selStart) : "";
  741.         String        rightOfSelection    = selEnd < currentLength ? currentString.substring(selEnd) : "";
  742.         String        finalString            = "";
  743.         
  744.         //Set the default position of the caret to be the beginning of the selection.
  745.         caret = selStart;
  746.  
  747.         //If we have no mask to worry about
  748.         if(mask == null)
  749.         {            
  750.             //If we need to erase something
  751.             if(isBackspace || isDelete)
  752.             {
  753.                 if(isSelection)
  754.                 {
  755.                     finalString = leftOfSelection + rightOfSelection;
  756.                 }
  757.                 else
  758.                 {
  759.                     if(isBackspace)
  760.                     {
  761.                         String tempLeft = "";
  762.                         int leftLength = leftOfSelection.length();
  763.                         
  764.                         //If the left half has any size, chop the last character of the left half off,
  765.                         //and reduce the caret position by one.
  766.                         if(leftLength > 0)
  767.                         {
  768.                             tempLeft = leftOfSelection.substring(0, leftLength - 1);
  769.                             caret--;
  770.                         }
  771.                         finalString = tempLeft + rightOfSelection;
  772.                     }
  773.                     //we assume that if you get here, the only option is for isDelete to be true.
  774.                     else
  775.                     {
  776.                         String tempRight = "";
  777.                         int rightLength = rightOfSelection.length();
  778.                         //If the right half has any size, chop the first character of the right half off.
  779.                         if(rightLength > 0)
  780.                         {
  781.                             tempRight = rightOfSelection.substring(1, rightLength);
  782.                         }
  783.                         finalString = leftOfSelection + tempRight;
  784.                     }
  785.                 }
  786.             }
  787.             //If we need to add something
  788.             else
  789.             {
  790.                 //fbeninc cheched for \0 sent with shift.
  791.                 if(keyString.equals(String.valueOf('\0')))finalString = currentString;
  792.                 else {
  793.                     caret = leftOfSelection.length() + 1;
  794.                     finalString = leftOfSelection + keyString + rightOfSelection;
  795.                 }
  796.             }
  797.             //Since there is no formatting, the finalString is the same as the unformattedString.
  798.             unformattedCurrent = finalString;
  799.         }
  800.         //We need to wory about a mask
  801.         else
  802.         {
  803.             boolean isBeep        = false;
  804.             int i;
  805.             int maskIndex;
  806.             int unformattedLength = unformattedCurrent.length();
  807.             String newLeft;
  808.             String newRight = "";
  809.             String tempString = "";
  810.             String newPartialUnformatted = "";
  811.             String maskTemplate = getMaskTemplateString();
  812.  
  813.             //Count the number of possible non-escape characters before the start of the selection
  814.             int numCharsBeforeSelStart = countNonEscapeChars(0, selStart);
  815.  
  816.             if(isSelection)
  817.             {
  818.                 //We are typing over the selection, not deleting it.
  819.                 if(!(isBackspace || isDelete))
  820.                 {
  821.                     //Find the first non-escape character in the selection
  822.                     maskIndex = selStart;
  823.                     while(maskIndex < maskStripEscapesLen)
  824.                     {
  825.                         if(isEscape(maskIndex))
  826.                         {
  827.                             ++maskIndex;
  828.                         }
  829.                         else
  830.                             break;
  831.                     }
  832.                     //If the new character is not valid at this index, eat it, beep, and do nothing.
  833.                     if(applyMaskToChar(keyString.charAt(0), maskIndex) == "")
  834.                     {
  835.                         Toolkit.getDefaultToolkit().beep();
  836.                         return;
  837.                     }
  838.                 }
  839.                 
  840.                 //Count the number of real characters in the selection
  841.                 int numCharsInSelection    = 0;
  842.                 i                        = 0;
  843.                 maskIndex                = 0;
  844.                 while(i < unformattedLength && maskIndex < maskStripEscapesLen)
  845.                 {
  846.                     if(!isEscape(maskIndex))
  847.                     {
  848.                         ++i;
  849.                         if(maskIndex >= selStart && maskIndex < selEnd)
  850.                         {
  851.                             ++numCharsInSelection;
  852.                         }
  853.                     }
  854.                     ++maskIndex;
  855.                 }
  856.  
  857.                 //If the selection contains characters we care about
  858.                 if(numCharsInSelection > 0)
  859.                 {
  860.                     //We can assume that since the selection contains real characters, all non-escaped
  861.                     //characters before the selection are real too.
  862.                     
  863.                     //Remove the selected characters from the unformatted string
  864.                     String tempL    = numCharsBeforeSelStart > 0 ? unformattedCurrent.substring(0, numCharsBeforeSelStart) : "";
  865.                     int tempIndex    = numCharsBeforeSelStart + numCharsInSelection;
  866.                     String tempR    = tempIndex < unformattedLength ? unformattedCurrent.substring(tempIndex) : "";
  867.                     unformattedCurrent = tempL + tempR;
  868.                     //Recalculate unformattedLength
  869.                     unformattedLength = unformattedCurrent.length();
  870.                 }
  871.                 //The selection doesn't contain any real characters, but we need handle selections of just escape characters
  872.                 else
  873.                 {
  874.                     int numNonEscapeChars = countNonEscapeChars(selStart, selEnd);
  875.                     //The selection contains only escape characters
  876.                     if(numNonEscapeChars == 0)
  877.                     {
  878.                         Toolkit.getDefaultToolkit().beep();
  879.                         return;
  880.                     }
  881.                 }
  882.             }
  883.             //There was no selection
  884.             else
  885.             {
  886.                 if(isBackspace)
  887.                 {
  888.                     //Count the number of real characters before the selection
  889.                     int numCharsBeforeSelection    = 0;
  890.                     i                            = 0;
  891.                     maskIndex                    = 0;
  892.                     while(i < unformattedLength && maskIndex < maskStripEscapesLen)
  893.                     {
  894.                         if(!isEscape(maskIndex))
  895.                         {
  896.                             ++i;
  897.                             if(maskIndex < selStart)
  898.                             {
  899.                                 ++numCharsBeforeSelection;
  900.                             }
  901.                             else
  902.                                 break;
  903.                         }
  904.                         ++maskIndex;
  905.                     }
  906.                     
  907.                     //Move the caret past any consecutive escape characters
  908.                     while(caret >= 0)
  909.                     {
  910.                         if(isEscape(caret - 1))
  911.                         {
  912.                             --caret;
  913.                         }
  914.                         else
  915.                             break;
  916.                     }
  917.                     //Move the caret back one for the deleted character
  918.                     --caret;
  919.  
  920.                     //If there is a gap between characters we care about and the selection, just move the caret
  921.                     if(numCharsBeforeSelection < numCharsBeforeSelStart)
  922.                     {
  923.                         //Set the position of the caret
  924.                         select(caret, caret);
  925.                         return;
  926.                     }
  927.                     //There is no gap, and we need to delete a character
  928.                     else
  929.                     {
  930.                         //Remove the character from the unformattedCurrent string
  931.                         //A negative index is okay, since removeCharAtIndex will return the original in that case. 
  932.                         unformattedCurrent = symantec.itools.util.GeneralUtils.removeCharAtIndex(unformattedCurrent, numCharsBeforeSelection - 1);
  933.                         //Recalculate unformattedLength
  934.                         unformattedLength = unformattedCurrent.length();
  935.                     }
  936.                 }
  937.                 else if(isDelete)
  938.                 {
  939.                     //Count the number of real characters after the selection
  940.                     int numCharsAfterSelection    = 0;
  941.                     i                            = 0;
  942.                     maskIndex                    = 0;
  943.                     while(i < unformattedLength && maskIndex < maskStripEscapesLen)
  944.                     {
  945.                         if(!isEscape(maskIndex))
  946.                         {
  947.                             ++i;
  948.                             if(maskIndex >= selEnd)
  949.                             {
  950.                                 ++numCharsAfterSelection;
  951.                             }
  952.                         }
  953.                         ++maskIndex;
  954.                     }
  955.                     
  956.                     //If there are real characters in front of the cursor, delete one.
  957.                     //We assume that there is no gap and start dierctly after the cursor. 
  958.                     if(numCharsAfterSelection > 0)// unformattedLength - numCharsBeforeSelStart)
  959.                     {
  960.                         int tempStart = numCharsBeforeSelStart < unformattedLength ? numCharsBeforeSelStart : unformattedLength - 1;
  961.                         //Remove the character from the unformattedCurrent string
  962.                         unformattedCurrent = symantec.itools.util.GeneralUtils.removeCharAtIndex(unformattedCurrent, tempStart);
  963.                         //Recalculate unformattedLength
  964.                         unformattedLength = unformattedCurrent.length();
  965.                     }
  966.                     //There are no characters we care about on the right of the cursor, so
  967.                     //beep, and do nothing.
  968.                     else
  969.                     {
  970.                         Toolkit.getDefaultToolkit().beep();
  971.                         return;
  972.                     }
  973.                 }
  974.             }
  975.  
  976.             numCharsBeforeSelStart = countNonEscapeChars(0, caret); 
  977.             //If there is a gap between the current insertion point and the last character
  978.             //calculate the index of the current characters in the template, move the caret,
  979.             //and calculate a new left.
  980.             if(unformattedLength < numCharsBeforeSelStart)
  981.             {
  982.                 i                = 0;
  983.                 maskIndex        = 0;
  984.                 while(i < unformattedLength && maskIndex < maskStripEscapesLen)
  985.                 {
  986.                     if(!isEscape(maskIndex))
  987.                     {
  988.                         ++i;
  989.                     }
  990.                     ++maskIndex;
  991.                 }
  992.                 //If there was a backspace, we let it's code handle caret manipulation
  993.                 if(!isBackspace)
  994.                     caret = maskIndex;
  995.             }
  996.             //If there is no gap, use the start of the selection to insert with.
  997.             else
  998.             {
  999.                 maskIndex    = caret;    //The index in the formatted string
  1000.             }
  1001.             
  1002.             //Get the left characters
  1003.             newLeft = maskIndex > 0 ? currentString.substring(0, maskIndex) : "";
  1004.             
  1005.             //Apply the mask to the remaining non-escape characters in the right of selection and the new char
  1006.             //fbeninc 
  1007.             String tempUnformatted;
  1008.             if(keyString.equals(String.valueOf('\0')))
  1009.             {
  1010.                 tempUnformatted =unformattedCurrent.substring(numCharsBeforeSelStart <= unformattedLength ? numCharsBeforeSelStart : unformattedLength);
  1011.                 caret--;
  1012.                 }
  1013.             else 
  1014.             {
  1015.                 tempUnformatted = keyString + unformattedCurrent.substring(numCharsBeforeSelStart <= unformattedLength ? numCharsBeforeSelStart : unformattedLength);
  1016.                 }
  1017.            
  1018.             //Here 'i' is the index in the unformattedCurrent string, and
  1019.             i = 0;
  1020.             while(i < tempUnformatted.length() && maskIndex < maskStripEscapesLen)
  1021.             {
  1022.                 if(isEscape(maskIndex))
  1023.                 {
  1024.                     newRight += maskTemplate.charAt(maskIndex);
  1025.                 }
  1026.                 else
  1027.                 {
  1028.                     tempString = applyMaskToChar(tempUnformatted.charAt(i), maskIndex);
  1029.                     
  1030.                     //If the character was filtered out by the mask the we need to beep
  1031.                     if(tempString == "")
  1032.                     {
  1033.                         isBeep = true;
  1034.                     }
  1035.                     //If the first character was accepted and we have a new character, then move the caret forward
  1036.                     else if(i == 0 && keyString != "")
  1037.                     {
  1038.                         //Move the caret past any consecutive escape characters
  1039.                         while(caret < maskStripEscapesLen)
  1040.                         {
  1041.                             if(isEscape(caret))
  1042.                             {
  1043.                                 ++caret;
  1044.                             }
  1045.                             else
  1046.                                 break;
  1047.                         }
  1048.                         
  1049.                         //Move the caret one for the character that was accepted.
  1050.                         ++caret;
  1051.                         
  1052.                         //Now that the caret has moved, move the caret past any consecutive escape characters
  1053.                         while(caret < maskStripEscapesLen)
  1054.                         {
  1055.                             if(isEscape(caret))
  1056.                             {
  1057.                                 ++caret;
  1058.                             }
  1059.                             else
  1060.                                 break;
  1061.                         }
  1062.                     }
  1063.                     
  1064.                     //Keep track of the new acceptable characters
  1065.                     newPartialUnformatted += tempString;
  1066.                     //Keep track of the characters to display
  1067.                     newRight += tempString;
  1068.                     //Go on to the next character
  1069.                     ++i;
  1070.                 }
  1071.                 maskIndex++;
  1072.             }
  1073.  
  1074.             //Set the final string to the left, the newly constructed right, and any remainder of the mask template
  1075.             finalString = newLeft + newRight + maskTemplate.substring(newLeft.length() + newRight.length());
  1076.             //Set the unformattedCurrent to the unchanged left unformatted string and new right unformatted string.
  1077.             unformattedCurrent = unformattedCurrent.substring(0, numCharsBeforeSelStart <= unformattedLength ? numCharsBeforeSelStart : unformattedLength) + newPartialUnformatted;
  1078.             //Don't need to recalculate unformattedLength because it's not used after this
  1079.  
  1080.             if(isBeep)
  1081.                 Toolkit.getDefaultToolkit().beep();
  1082.         }
  1083.         
  1084.         //Set the field's contents to our finalString
  1085.         super.setText(finalString);
  1086.         //Set the position of the caret
  1087.         select(caret, caret);
  1088.     }
  1089.     
  1090.     /**
  1091.      * Counts the possible number of non-escape characters before the given index.
  1092.      * @param int start the index in the mask template to start from. Inclusive.
  1093.      * @param int beforeIndex the index in the mask template to count back from. Exclusive.
  1094.      * @return int the number of possible non-escape characters allowed before the given index.
  1095.      */
  1096.     protected int countNonEscapeChars(int start, int beforeIndex)
  1097.     {
  1098.         int numChars = 0;
  1099.         for(int i = start; i < beforeIndex; ++i)
  1100.         {
  1101.             if(!isEscape(i))
  1102.                 numChars++;
  1103.         }
  1104.         return numChars;
  1105.     }
  1106.  
  1107.     private boolean isEscape(int index)
  1108.     {
  1109.         return (index < 0 || index >= maskStripEscapesLen) ? false : (maskStripEscapes.charAt(index) == ESCAPE);
  1110.     }
  1111.  
  1112.     private String stripMaskEscapes()
  1113.     {
  1114.         if(mask == null)
  1115.         {
  1116.             return "";
  1117.         }
  1118.         else
  1119.         {
  1120.             int ml = mask.length();
  1121.             int maskIndex = 0;
  1122.             String nm = "";
  1123.     
  1124.             while (maskIndex < ml)
  1125.             {
  1126.                 char maskChar = mask.charAt(maskIndex++);
  1127.                 if (maskChar == ESCAPE)
  1128.                     ++maskIndex;
  1129.                 nm += maskChar;
  1130.             }
  1131.             return nm;
  1132.         }
  1133.  
  1134.     }
  1135.  
  1136.     /**
  1137.      * The string passed to setText at design time.  Returned from getText at design time.
  1138.      */
  1139.     protected String designTimeText;
  1140.     /**
  1141.      * Character constant, the backspace character.
  1142.      */
  1143.     protected final int BACKSPACE           = 8;
  1144.     /**
  1145.      * Character constant, the enter character.
  1146.      */
  1147.     protected final int ENTER               = 10;
  1148.     /**
  1149.      * Character constant, the delete character.
  1150.      */
  1151.     protected final int DEL                 = 127;
  1152.     /**
  1153.      * The zero-relative index of the character after the insertion point caret.
  1154.      */
  1155.     protected int caret;
  1156.     /**
  1157.      * The valid string format mask used to define legal field input.
  1158.      */
  1159.     protected String mask        = null;
  1160.     /**
  1161.      * The valid characters currently in the textfield, with no formatting.
  1162.      */
  1163.     protected String unformattedCurrent;
  1164.     /**
  1165.      * A string containing all the mask string characters except the ones immediately after ESCAPE characters.
  1166.      */
  1167.     protected String maskStripEscapes;
  1168.     /**
  1169.      * The length of maskStripEscapes.
  1170.      */
  1171.     protected int maskStripEscapesLen;
  1172.  
  1173.     private Key keyListener = null;
  1174. }
  1175.  
  1176.