001/* 002 * $Id: McVTextField.java,v 1.12 2011/03/24 16:06:35 davep Exp $ 003 * 004 * This file is part of McIDAS-V 005 * 006 * Copyright 2007-2011 007 * Space Science and Engineering Center (SSEC) 008 * University of Wisconsin - Madison 009 * 1225 W. Dayton Street, Madison, WI 53706, USA 010 * https://www.ssec.wisc.edu/mcidas 011 * 012 * All Rights Reserved 013 * 014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 015 * some McIDAS-V source code is based on IDV and VisAD source code. 016 * 017 * McIDAS-V is free software; you can redistribute it and/or modify 018 * it under the terms of the GNU Lesser Public License as published by 019 * the Free Software Foundation; either version 3 of the License, or 020 * (at your option) any later version. 021 * 022 * McIDAS-V is distributed in the hope that it will be useful, 023 * but WITHOUT ANY WARRANTY; without even the implied warranty of 024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 025 * GNU Lesser Public License for more details. 026 * 027 * You should have received a copy of the GNU Lesser Public License 028 * along with this program. If not, see http://www.gnu.org/licenses. 029 */ 030 031package edu.wisc.ssec.mcidasv.util; 032 033import java.awt.BorderLayout; 034import java.awt.Color; 035import java.awt.event.FocusEvent; 036import java.awt.event.FocusListener; 037import java.util.regex.Matcher; 038import java.util.regex.Pattern; 039import java.util.regex.PatternSyntaxException; 040 041import javax.swing.InputVerifier; 042import javax.swing.JComponent; 043import javax.swing.JLabel; 044import javax.swing.JTextField; 045import javax.swing.SwingConstants; 046import javax.swing.border.EmptyBorder; 047import javax.swing.event.DocumentEvent; 048import javax.swing.event.DocumentListener; 049import javax.swing.text.AttributeSet; 050import javax.swing.text.BadLocationException; 051import javax.swing.text.Document; 052import javax.swing.text.JTextComponent; 053import javax.swing.text.PlainDocument; 054 055/** 056 * Extend JTextField to add niceties such as uppercase, 057 * length limits, and allow/deny character sets 058 */ 059public class McVTextField extends JTextField { 060 061 public static char[] mcidasDeny = new char[] { '/', '.', ' ', '[', ']', '%' }; 062 063 public static Pattern ipAddress = Pattern.compile("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"); 064 065 private McVTextFieldDocument document = new McVTextFieldDocument(); 066 067 private Pattern validPattern; 068 private String[] validStrings; 069 070 public McVTextField() { 071 this("", 0, false); 072 } 073 074 public McVTextField(String defaultString) { 075 this(defaultString, 0, false); 076 } 077 078 public McVTextField(String defaultString, int limit) { 079 this(defaultString, limit, false); 080 } 081 082 public McVTextField(String defaultString, boolean upper) { 083 this(defaultString, 0, upper); 084 } 085 086 // All other constructors call this one 087 public McVTextField(String defaultString, int limit, boolean upper) { 088 super(limit); 089 this.document = new McVTextFieldDocument(limit, upper); 090 super.setDocument(document); 091 this.setText(defaultString); 092 } 093 094 public McVTextField(String defaultString, int limit, boolean upper, String allow, String deny) { 095 this(defaultString, limit, upper); 096 setAllow(makePattern(allow)); 097 setDeny(makePattern(deny)); 098 } 099 100 public McVTextField(String defaultString, int limit, boolean upper, char[] allow, char[] deny) { 101 this(defaultString, limit, upper); 102 setAllow(makePattern(allow)); 103 setDeny(makePattern(deny)); 104 } 105 106 public McVTextField(String defaultString, int limit, boolean upper, Pattern allow, Pattern deny) { 107 this(defaultString, limit, upper); 108 setAllow(allow); 109 setDeny(deny); 110 } 111 112 public int getLimit() { 113 return this.document.getLimit(); 114 } 115 116 public void setLimit(int limit) { 117 this.document.setLimit(limit); 118 super.setDocument(document); 119 } 120 121 public boolean getUppercase() { 122 return this.document.getUppercase(); 123 } 124 125 public void setUppercase(boolean uppercase) { 126 this.document.setUppercase(uppercase); 127 super.setDocument(document); 128 } 129 130 public void setAllow(String string) { 131 this.document.setAllow(makePattern(string)); 132 super.setDocument(document); 133 } 134 135 public void setDeny(String string) { 136 this.document.setDeny(makePattern(string)); 137 super.setDocument(document); 138 } 139 140 public void setAllow(char[] characters) { 141 this.document.setAllow(makePattern(characters)); 142 super.setDocument(document); 143 } 144 145 public void setDeny(char[] characters) { 146 this.document.setDeny(makePattern(characters)); 147 super.setDocument(document); 148 } 149 150 public void setAllow(Pattern newPattern) { 151 this.document.setAllow(newPattern); 152 super.setDocument(document); 153 } 154 155 public void setDeny(Pattern newPattern) { 156 this.document.setDeny(newPattern); 157 super.setDocument(document); 158 } 159 160 // Take a string and turn it into a pattern 161 private Pattern makePattern(String string) { 162 if (string == null) return null; 163 try { 164 return Pattern.compile(string); 165 } 166 catch (PatternSyntaxException e) { 167 return null; 168 } 169 } 170 171 // Take a character array and turn it into a [abc] class pattern 172 private Pattern makePattern(char[] characters) { 173 if (characters == null) return null; 174 String string = ".*"; 175 if (characters.length > 0) { 176 string = "["; 177 for (char c : characters) { 178 if (c == '[') string += "\\["; 179 else if (c == ']') string += "\\]"; 180 else if (c == '\\') string += "\\\\"; 181 else string += c; 182 } 183 string += "]"; 184 } 185 try { 186 return Pattern.compile(string); 187 } 188 catch (PatternSyntaxException e) { 189 return null; 190 } 191 } 192 193 // Add an InputVerifier if we want to validate a particular pattern 194 public void setValidPattern(String string) { 195 if (string == null) return; 196 try { 197 Pattern newPattern = Pattern.compile(string); 198 setValidPattern(newPattern); 199 } 200 catch (PatternSyntaxException e) { 201 return; 202 } 203 } 204 205 // Add an InputVerifier if we want to validate a particular pattern 206 public void setValidPattern(Pattern pattern) { 207 if (pattern == null) { 208 this.validPattern = null; 209 if (this.validStrings == null) removeInputVerifier(); 210 } 211 else { 212 this.validPattern = pattern; 213 addInputVerifier(); 214 } 215 } 216 217 // Add an InputVerifier if we want to validate a particular set of strings 218 public void setValidStrings(String[] strings) { 219 if (strings == null) { 220 this.validStrings = null; 221 if (this.validPattern == null) removeInputVerifier(); 222 } 223 else { 224 this.validStrings = strings; 225 addInputVerifier(); 226 } 227 } 228 229 private void addInputVerifier() { 230 this.setInputVerifier(new InputVerifier() { 231 public boolean verify(JComponent comp) { 232 return verifyInput(); 233 } 234 public boolean shouldYieldFocus(JComponent comp) { 235 boolean valid = verify(comp); 236 if (!valid) { 237 getToolkit().beep(); 238 } 239 return valid; 240 } 241 }); 242 verifyInput(); 243 } 244 245 private void removeInputVerifier() { 246 this.setInputVerifier(null); 247 } 248 249 private boolean verifyInput() { 250 boolean isValid = false; 251 String checkValue = this.getText(); 252 if (checkValue=="") return true; 253 254 if (this.validStrings != null) { 255 for (String string : validStrings) { 256 if (checkValue.equals(string)) isValid = true; 257 } 258 } 259 260 if (this.validPattern != null) { 261 Matcher validMatch = this.validPattern.matcher(checkValue); 262 isValid = isValid || validMatch.matches(); 263 } 264 265 if (!isValid) { 266 this.selectAll(); 267 } 268 return isValid; 269 } 270 271 /** 272 * Extend PlainDocument to get the character validation features we require 273 */ 274 private class McVTextFieldDocument extends PlainDocument { 275 private int limit; 276 private boolean toUppercase = false; 277 private boolean hasPatterns = false; 278 private Pattern allow = Pattern.compile(".*"); 279 private Pattern deny = null; 280 281 public McVTextFieldDocument() { 282 super(); 283 } 284 285 public McVTextFieldDocument(int limit, boolean upper) { 286 super(); 287 setLimit(limit); 288 setUppercase(upper); 289 } 290 291 public void insertString(int offset, String str, AttributeSet attr) throws BadLocationException { 292 if (str == null) return; 293 if (toUppercase) str = str.toUpperCase(); 294 295 // Only allow certain patterns, and only check if we think we have patterns 296 if (hasPatterns) { 297 char[] characters = str.toCharArray(); 298 String okString = ""; 299 for (char c : characters) { 300 String s = "" + c; 301 if (deny != null) { 302 Matcher denyMatch = deny.matcher(s); 303 if (denyMatch.matches()) continue; 304 } 305 if (allow != null) { 306 Matcher allowMatch = allow.matcher(s); 307 if (allowMatch.matches()) okString += s; 308 } 309 } 310 str = okString; 311 } 312 if (str.equals("")) return; 313 314 if ((getLength() + str.length()) <= limit || limit <= 0) { 315 super.insertString(offset, str, attr); 316 } 317 } 318 319 public int getLimit() { 320 return this.limit; 321 } 322 323 public void setLimit(int limit) { 324 this.limit = limit; 325 } 326 327 public boolean getUppercase() { 328 return this.toUppercase; 329 } 330 331 public void setUppercase(boolean uppercase) { 332 this.toUppercase = uppercase; 333 } 334 335 public void setAllow(Pattern newPattern) { 336 if (newPattern==null) return; 337 this.allow = newPattern; 338 hasPatterns = true; 339 } 340 341 public void setDeny(Pattern newPattern) { 342 if (newPattern==null) return; 343 this.deny = newPattern; 344 hasPatterns = true; 345 } 346 347 } 348 349 public static class Prompt extends JLabel implements FocusListener, DocumentListener { 350 351 public enum FocusBehavior { ALWAYS, FOCUS_GAINED, FOCUS_LOST }; 352 353 private final JTextComponent component; 354 355 private final Document document; 356 357 private FocusBehavior focus; 358 359 private boolean showPromptOnce; 360 361 private int focusLost; 362 363 public Prompt(final JTextComponent component, final String text) { 364 this(component, FocusBehavior.FOCUS_LOST, text); 365 } 366 367 public Prompt(final JTextComponent component, final FocusBehavior focusBehavior, final String text) { 368 this.component = component; 369 setFocusBehavior(focusBehavior); 370 371 document = component.getDocument(); 372 373 setText(text); 374 setFont(component.getFont()); 375 setForeground(component.getForeground()); 376 setHorizontalAlignment(JLabel.LEADING); 377 setEnabled(false); 378 379 component.addFocusListener(this); 380 document.addDocumentListener(this); 381 382 component.setLayout(new BorderLayout()); 383 component.add(this); 384 checkForPrompt(); 385 } 386 387 public FocusBehavior getFocusBehavior() { 388 return focus; 389 } 390 391 public void setFocusBehavior(final FocusBehavior focus) { 392 this.focus = focus; 393 } 394 395 public boolean getShowPromptOnce() { 396 return showPromptOnce; 397 } 398 399 public void setShowPromptOnce(final boolean showPromptOnce) { 400 this.showPromptOnce = showPromptOnce; 401 } 402 403 /** 404 * Check whether the prompt should be visible or not. The visibility 405 * will change on updates to the Document and on focus changes. 406 */ 407 private void checkForPrompt() { 408 // text has been entered, remove the prompt 409 if (document.getLength() > 0) { 410 setVisible(false); 411 return; 412 } 413 414 // prompt has already been shown once, remove it 415 if (showPromptOnce && focusLost > 0) { 416 setVisible(false); 417 return; 418 } 419 420 // check the behavior property and component focus to determine if the 421 // prompt should be displayed. 422 if (component.hasFocus()) { 423 if (focus == FocusBehavior.ALWAYS || focus == FocusBehavior.FOCUS_GAINED) { 424 setVisible(true); 425 } else { 426 setVisible(false); 427 } 428 } else { 429 if (focus == FocusBehavior.ALWAYS || focus == FocusBehavior.FOCUS_LOST) { 430 setVisible( true); 431 } else { 432 setVisible(false); 433 } 434 } 435 } 436 437 // from FocusListener 438 public void focusGained(FocusEvent e) { 439 checkForPrompt(); 440 } 441 442 public void focusLost(FocusEvent e) { 443 focusLost++; 444 checkForPrompt(); 445 } 446 447 // from DocumentListener 448 public void insertUpdate(DocumentEvent e) { 449 checkForPrompt(); 450 } 451 452 public void removeUpdate(DocumentEvent e) { 453 checkForPrompt(); 454 } 455 456 public void changedUpdate(DocumentEvent e) {} 457 } 458}