/** * Copyright (C) 2001-2017 by RapidMiner and the contributors * * Complete list of developers available at our web site: * * http://rapidminer.com * * This program is free software: you can redistribute it and/or modify it under the terms of the * GNU Affero General Public License as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License along with this program. * If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.gui.tools; import com.rapidminer.repository.RepositoryLocation; import com.rapidminer.tools.I18N; import com.rapidminer.tools.LogService; import com.rapidminer.tools.Observable; import com.rapidminer.tools.Observer; import java.awt.Color; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.KeyListener; import java.util.LinkedList; import java.util.List; import java.util.logging.Level; import javax.swing.Icon; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.SwingUtilities; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.BadLocationException; /** * Creates a panel which contains a {@link JTextField} and a {@link JLabel} which can be used to * enter the name of a repository entry. Invalid names (checked via * {@link RepositoryLocation#isNameValid(String)}) are marked invalid while typing and an event is * fired to signal invalid/valid entries. * <p> * To listen to the event (firing a simple {@link Boolean} to signal valid (<code>true</code>) and * invalid (<code>false</code>) entries), register your listener via * {@link #addObserver(Observer, boolean)} or {@link #addObserverAsFirst(Observer, boolean)}. * * @author Marco Boeck */ public class RepositoryEntryTextField extends JPanel implements Observable<Boolean> { private static final long serialVersionUID = -750857028654448541L; private JLabel entryTextLabel; private JTextField entryTextField; private JLabel entryErrorIconLabel; private JLabel entryErrorTextLabel; private final Color standardTextColor; private final Color errorTextColor; private final Icon standardIcon; private final Icon errorIcon; private List<Observer<Boolean>> observerOnEDTList; private List<Observer<Boolean>> observerNotOnEDTList; private Object lock = new Object(); /** * Standard constructor. */ public RepositoryEntryTextField() { super(); standardIcon = null; errorIcon = SwingTools.createIcon("16/" + I18N.getMessage(I18N.getGUIBundle(), "gui.dialog.repository_location.location_invalid.icon")); setupGUI(); standardTextColor = entryTextField.getForeground(); errorTextColor = Color.RED; observerOnEDTList = new LinkedList<Observer<Boolean>>(); observerNotOnEDTList = new LinkedList<Observer<Boolean>>(); checkName(); } /** * Sets up this GUI element. */ private void setupGUI() { setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.insets = new Insets(5, 0, 5, 5); gbc.weightx = 0.0; gbc.fill = GridBagConstraints.NONE; gbc.gridwidth = 1; entryTextField = new JTextField(); entryTextLabel = new ResourceLabel("repository_location.location_entry_name"); entryTextLabel.setLabelFor(entryTextField); add(entryTextLabel, gbc); gbc.gridx = 1; gbc.weightx = 1.0; gbc.insets = new Insets(5, 0, 5, 0); gbc.fill = GridBagConstraints.HORIZONTAL; gbc.gridwidth = 2; entryTextField.getDocument().addDocumentListener(new DocumentListener() { @Override public void removeUpdate(DocumentEvent e) { checkName(); } @Override public void insertUpdate(DocumentEvent e) { checkName(); } @Override public void changedUpdate(DocumentEvent e) { // not relevant } }); add(entryTextField, gbc); gbc.gridx = 1; gbc.gridy = 1; gbc.weightx = 0.0; gbc.gridwidth = 1; gbc.insets = new Insets(5, 0, 5, 5); entryErrorIconLabel = new JLabel(); entryErrorIconLabel.setMinimumSize(new Dimension(16, 16)); entryErrorIconLabel.setPreferredSize(new Dimension(16, 16)); add(entryErrorIconLabel, gbc); gbc.gridx = 2; gbc.gridy = 1; gbc.weightx = 1.0; gbc.gridwidth = 2; gbc.insets = new Insets(5, 0, 5, 0); entryErrorTextLabel = new JLabel(); add(entryErrorTextLabel, gbc); } /** * Checks if the entered repository entry name is valid. * * @param e */ private void checkName() { boolean valid = false; boolean empty = false; String illegalSubstring = null; try { String name = entryTextField.getDocument().getText(0, entryTextField.getDocument().getLength()); if ("".equals(name.trim())) { empty = true; } valid = RepositoryLocation.isNameValid(name); if (!valid) { illegalSubstring = RepositoryLocation.getIllegalCharacterInName(name); } } catch (BadLocationException e1) { LogService.getRoot().log( Level.SEVERE, I18N.getMessage(LogService.getRoot().getResourceBundle(), "com.rapidminer.gui.tools.RepositoryEntryTextField.bad_document_location"), e1); } if (!valid) { entryTextField.setForeground(errorTextColor); entryErrorIconLabel.setIcon(errorIcon); if (!empty) { entryErrorTextLabel.setText(I18N.getMessage(I18N.getGUIBundle(), "gui.dialog.repository_location.location_invalid_char.label", illegalSubstring)); } else { entryErrorTextLabel.setText(I18N.getMessage(I18N.getGUIBundle(), "gui.dialog.repository_location.location_invalid_empty.label")); } } else { entryTextField.setForeground(standardTextColor); entryErrorIconLabel.setIcon(standardIcon); entryErrorTextLabel.setText(""); } notifyObservers(valid); } /** * Notifies the observers. * * @param valid */ private void notifyObservers(final boolean valid) { synchronized (lock) { if (SwingUtilities.isEventDispatchThread()) { for (final Observer<Boolean> observer : observerOnEDTList) { observer.update(RepositoryEntryTextField.this, valid); } } else { for (final Observer<Boolean> observer : observerOnEDTList) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { observer.update(RepositoryEntryTextField.this, valid); } }); } } for (final Observer<Boolean> observer : observerNotOnEDTList) { observer.update(RepositoryEntryTextField.this, valid); } } } @Override public void addObserver(Observer<Boolean> observer, boolean onEDT) { if (onEDT) { synchronized (lock) { observerOnEDTList.add(observer); } } else { synchronized (lock) { observerNotOnEDTList.add(observer); } } } @Override public void removeObserver(Observer<Boolean> observer) { synchronized (lock) { if (!observerOnEDTList.remove(observer)) { observerNotOnEDTList.remove(observer); } } } @Override public void addObserverAsFirst(Observer<Boolean> observer, boolean onEDT) { if (onEDT) { synchronized (lock) { observerOnEDTList.add(0, observer); } } else { synchronized (lock) { observerNotOnEDTList.add(0, observer); } } } /** * Sets a text in the textfield. * * @param text */ public void setText(String text) { entryTextField.setText(text); } /** * Return the entered text. * * @return */ public String getText() { return entryTextField.getText(); } /** * Triggers a new check if the entered name is valid. Will notify all {@link Observer}s! */ public void triggerCheck() { checkName(); } @Override public void addKeyListener(KeyListener l) { this.entryTextField.addKeyListener(l); } @Override public void removeKeyListener(KeyListener l) { this.entryTextField.removeKeyListener(l); } @Override public boolean requestFocusInWindow() { return entryTextField.requestFocusInWindow(); } }