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