/* * org.openmicroscopy.shoola.util.ui.login.ServerEditor * *------------------------------------------------------------------------------ * Copyright (C) 2006-2014 University of Dundee. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *------------------------------------------------------------------------------ */ package org.openmicroscopy.shoola.util.ui.login; //Java imports import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Map.Entry; import java.util.prefs.Preferences; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JToolBar; import javax.swing.border.TitledBorder; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellEditor; //Third-party libraries //Application-internal dependencies import org.openmicroscopy.shoola.util.ui.IconManager; import org.openmicroscopy.shoola.util.ui.UIUtilities; import org.openmicroscopy.shoola.util.ui.border.PartialLineBorder; /** * UI component display controls and list of servers. * * @author Jean-Marie Burel      * <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a> * @author Donald MacDonald      * <a href="mailto:donald@lifesci.dundee.ac.uk">donald@lifesci.dundee.ac.uk</a> * @version 3.0 * <small> * (<b>Internal version:</b> $Revision: $Date: $) * </small> * @since OME3.0 */ public class ServerEditor extends JPanel { /** Bound property indicating to remove the warning message. */ public static final String REMOVE_MESSAGE_PROPERTY = "removeMessage"; /** Bound property indicating to add the warning message. */ public static final String ADD_MESSAGE_PROPERTY = "addMessage"; /** Bound property indicating that the edition is finished. */ static final String EDIT_PROPERTY = "edit"; /** Bound property indicating that a server is removed from the list. */ static final String REMOVE_PROPERTY = "remove"; /** Bound property indicating that a server is added to the list. */ static final String ADD_PROPERTY = "add"; /** Bound property indicating to apply the selection. */ static final String APPLY_SERVER_PROPERTY = "applyServer"; /** Separator used when storing various servers. */ static final String SERVER_PORT_SEPARATOR = ":"; /** Separator used when storing various servers. */ static final String SERVER_NAME_SEPARATOR = ","; /** The minimum port value. */ static final int MIN_PORT = 0; /** The minimum port value. */ static final int MAX_PORT = 64000; /** The old port value. */ private static final List<String> OLD_PORTS; /** Example of a new server. */ private static final String EXAMPLE = "e.g. test.openmicroscopy.org " + "or 134.20.12.33"; /** The note. */ private static final String NOTE = "You should not have to modify the port."; /** The header of the table. */ private static final String HEADER = "Server Address and Port"; /** The property name for the host to connect to <i>OMERO</i>. */ private static final String OMERO_SERVER = "omeroServer"; /** Font for progress bar label. */ private static final Font FONT = new Font("SansSerif", Font.ITALIC, 10); static { OLD_PORTS = new ArrayList<String>(); OLD_PORTS.add(""+1099); OLD_PORTS.add(""+4063); } /** Button to remove server from the list. */ private JButton removeButton; /** Button to add new server to the list. */ private JButton addButton; /** Button to edit an existing server. */ private JButton editButton; /** Component displaying the collection of available servers. */ private ServerTable table; /** The panel displaying the message when no name is entered. */ private JPanel emptyMessagePanel; /** Helper reference to the icons manager. */ private IconManager icons; /** * Sets to <code>true</code> if the message is displayed, * <code>false</code> otherwise. */ private boolean warning; /** Flag indicating if we are in the editing mode. */ private boolean editing; /** * The server the user is currently connected to or <code>null</code> * if not connected. */ private String activeServer; /** The port used to connect to the active server. */ private String activePort; /** The default port value. */ private String defaultPort; /** The original row selected corresponding to the server. */ private int originalRow; /** Indicates the edited row. */ private int editedRow; /** * Removes the selected server from the list. * * @param row The row to remove. */ private void removeRow(int row) { DefaultTableModel model = (DefaultTableModel) table.getModel(); if (row < 0) return; if (model.getColumnCount() < 2) return; String oldValue = null; if (row < model.getRowCount()) oldValue = (String) model.getValueAt(row, 1); TableCellEditor editor = table.getCellEditor(); if (editor != null) editor.stopCellEditing(); table.removeRow(row); //model.removeRow(row); int m = model.getRowCount()-1; String newValue = null; if (m > -1 && table.getColumnCount() > 1) { table.setManual(true); table.changeSelection(m, 1, false, false); newValue = (String) model.getValueAt(m, 1); table.setManual(false); requestFocusInWindow(); } editor = table.getCellEditor(); if (editor != null) editor.stopCellEditing(); if (model.getRowCount() == 0) setEditing(false); handleServers(activeServer, activePort); editing = false; fireEditProperty(model.getRowCount() != 0); firePropertyChange(REMOVE_PROPERTY, oldValue, newValue); } /** * Initializes the components. * * @param servers Collection of servers to display. * @param enabled Pass <code>true</code> to allow edition, * */ private void initComponents(Map<String, String> servers, boolean enabled) { editedRow = -1; table = new ServerTable(this, servers, icons.getIcon(IconManager.SERVER_22)); removeButton = new JButton(icons.getIcon(IconManager.REMOVE)); removeButton.setName("remove server button"); UIUtilities.unifiedButtonLookAndFeel(removeButton); removeButton.setToolTipText("Remove the selected server " + "from the list of servers."); addButton = new JButton(icons.getIcon(IconManager.ADD)); addButton.setName("add server button"); addButton.setToolTipText("Add a new server to the list of servers."); addButton.setBorder(new TitledBorder("")); UIUtilities.unifiedButtonLookAndFeel(addButton); removeButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { removeRow(table.getSelectedRow()); } }); addButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { addRow(""); } }); editButton = new JButton(icons.getIcon(IconManager.EDIT)); editButton.setName("edit server button"); UIUtilities.unifiedButtonLookAndFeel(editButton); editButton.setToolTipText("Edit an existing server."); editButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { int row = table.getSelectedRow(); if (editedRow == row) requestFocusOnEditedCell(row, 2); else requestFocusOnEditedCell(row, 1); } }); //addButton.setEnabled(enabled); editButton.setEnabled(enabled); } /** Builds and lays out the UI. */ private void buildGUI() { JPanel labels = new JPanel(); labels.setLayout(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.HORIZONTAL; c.anchor = GridBagConstraints.WEST; c.insets = new Insets(0, 2, 2, 0); c.gridx = 0; c.gridy = 0; c.gridwidth = GridBagConstraints.RELATIVE; //next-to-last c.fill = GridBagConstraints.NONE; //reset to default c.weightx = 0.0; JLabel label = UIUtilities.setTextFont(HEADER); labels.add(label, c); label = new JLabel(EXAMPLE); label.setFont(FONT); c.gridy++;// = 1; c.gridwidth = GridBagConstraints.REMAINDER; //end row //c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1.0; labels.add(label, c); label = new JLabel(NOTE); label.setFont(FONT); c.gridy++;// = 1; c.gridwidth = GridBagConstraints.REMAINDER; //end row //c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1.0; labels.add(label, c); if (activeServer != null) { c.gridx = 0; c.gridy++; c.gridwidth = GridBagConstraints.RELATIVE; //next-to-last c.fill = GridBagConstraints.NONE; //reset to default c.weightx = 0.0; label = UIUtilities.setTextFont("Connected to "); labels.add(label, c); c.gridwidth = GridBagConstraints.REMAINDER; //end row //c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1.0; c.gridx = 1; label = new JLabel(activeServer+SERVER_PORT_SEPARATOR+activePort); labels.add(label, c); } JPanel p = new JPanel(); p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); p.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10)); JScrollPane pane = new JScrollPane(table); Dimension d = pane.getPreferredSize(); pane.setPreferredSize(new Dimension(d.width, 150)); p.add(pane); p.add(buildControls()); setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); JPanel content = UIUtilities.buildComponentPanel(labels); add(content); add(p); } /** * Builds the component hosting the controls. * * @return See above. */ private JPanel buildControls() { JToolBar bar = new JToolBar(); bar.setFloatable(false); bar.setRollover(true); bar.setBorder(null); bar.add(addButton); bar.add(removeButton); bar.add(editButton); return UIUtilities.buildComponentPanel(bar); } /** * Sets the <code>enabled</code> flag of the * {@link #addButton} and {@link #removeButton}. * * @param b The value to set. */ private void setButtonsEnabled(boolean b) { //addButton.setEnabled(b); removeButton.setEnabled(b); editButton.setEnabled(b); } /** * Fires a property to * * @param b Pass <code>true</code> when editing, * <code>false</code> otherwise. */ private void fireEditProperty(boolean b) { firePropertyChange(EDIT_PROPERTY, Boolean.valueOf(!b), Boolean.valueOf(b)); } /** Creates the {@link #emptyMessagePanel} if required. */ private void buildEmptyPanel() { if (emptyMessagePanel != null) return; emptyMessagePanel = new JPanel(); emptyMessagePanel.setOpaque(false); emptyMessagePanel.setBorder(new PartialLineBorder(Color.BLACK)); emptyMessagePanel.setLayout(new BoxLayout(emptyMessagePanel, BoxLayout.X_AXIS)); } /** Requests focus if no server address at initialization time. */ private void initFocus() { int n = 0; Map<String, String> servers = getServers(); if (servers != null) n = servers.size(); originalRow = -1; if (n == 0) { requestFocusOnEditedCell(table.getRowCount()-1, 1); editButton.setEnabled(false); removeButton.setEnabled(false); } else { originalRow = n-1; table.setRowSelectionInterval(originalRow, originalRow); } } /** * Creates a new instance. * * @param defaultPort The default port to use. */ ServerEditor(String defaultPort) { this(null, null, defaultPort); } /** * Creates a new instance. * * @param activeServer The server the user is currently connected to. * @param defaultPort The default port to use. */ ServerEditor(String activeServer, String defaultPort) { this(activeServer, null, defaultPort); } /** * Creates a new instance. * * @param activeServer The server the user is currently connected to. * @param activePort The port used by the server. * @param defaultPort The default port to use. */ ServerEditor(String activeServer, String activePort, String defaultPort) { icons = IconManager.getInstance(); this.activeServer = activeServer; if (defaultPort == null) defaultPort = ""; this.defaultPort = defaultPort; if (activePort == null || activePort.trim().length() == 0) this.activePort = defaultPort; int n = 0; Map<String, String> servers = getServers(); if (servers != null) n = servers.size(); initComponents(servers, n != 0); editing = false; editedRow = -1; buildGUI(); } /** * Returns the default port value. * * @return See above. */ String getDefaultPort() { return defaultPort; } /** * Sets the editing mode. * * @param b Pass <code>true</code> if the editing mode is turned on, * <code>false</code> otherwise. */ void setEditing(boolean b) { //addButton.setEnabled(!b); editButton.setEnabled(!b); editing = b; } /** * Returns <code>true</code> if we are in the editing mode, * <code>false</code> otherwise. * * @return See above. */ boolean isEditing() { return editing; } /** * Requests the focus on the edited cell. * * @param row The selected row. * @param col The selected column. */ void requestFocusOnEditedCell(int row, int col) { if (col == 0) return; if (table.getColumnCount() > 1) { editing = true; editedRow = row; TableCellEditor editor = table.getCellEditor(); if (editor != null) editor.stopCellEditing(); table.editCellAt(row, col); table.changeSelection(row, col, false, false); table.requestFocus(); } } /** * Enables or not the {@link #finishButton}. * * @param row The selected row. * @param previousRow The previously selected row. * @param text The text of the previously selected row. */ void changeSelection(int row, int previousRow, String text) { fireEditProperty(row != -1); //if (previousRow == row) return; if (previousRow == -1 || previousRow == row) return; //if (!editing) return; editing = false; List<String> values = new ArrayList<String>(); for (int i = 0; i < table.getRowCount(); i++) { if (i != previousRow) { values.add((String) table.getValueAt(i, 1)); } } if (activeServer != null && !values.contains(activeServer)) values.add(activeServer); Iterator j = values.iterator(); String name; boolean found = false; while (j.hasNext()) { name = (String) j.next(); if (name.equals(text)) { found = true; break; } } handleServers(activeServer, activePort); if (found || text == null || text.trim().length() == 0) { if (previousRow != -1) removeRow(previousRow); //showMessagePanel(false); } TableCellEditor editor = table.getCellEditor(); if (editor != null) editor.stopCellEditing(); } /** * Shows the warning message if the passed value is <code>true</code>, * hides it otherwise. * * @param warning Pass <code>true</code> to show the message, * <code>false</code> otherwise. */ void showMessagePanel(boolean warning) { this.warning = warning; fireEditProperty(!warning); setButtonsEnabled(!warning); if (warning) { if (emptyMessagePanel != null) return; buildEmptyPanel(); firePropertyChange(ADD_MESSAGE_PROPERTY, null, emptyMessagePanel); } else { if (emptyMessagePanel == null) return; firePropertyChange(REMOVE_MESSAGE_PROPERTY, null, emptyMessagePanel); emptyMessagePanel = null; } } /** * Removes the row if the text entered is <code>null</code> or of length * <code>zero</code> and also if the warning message is displayed. * * @param text */ void finishEdition(String text) { if (!editing) return; editing = false; setButtonsEnabled(true); if (warning || text == null || text.length() == 0) { removeRow(table.getSelectedRow()); showMessagePanel(false); return; } handleServers(activeServer, activePort); } /** Stops the cell editing. */ void stopEdition() { setEditing(false); TableCellEditor editor = table.getCellEditor(); if (editor != null) editor.stopCellEditing(); } /** * Returns the value of the selected server. * * @return See above. */ String getSelectedServer() { int row = table.getSelectedRow(); if (row == -1) return null; String v = (String) table.getValueAt(row, 1); if (v == null) return null; String trim = v.trim(); if (trim.length() == 0) return null; return trim; } /** * Returns the value of the selected port. * * @return See above. */ String getSelectedPort() { int row = table.getSelectedRow(); if (row == -1) return null; String v = (String) table.getValueAt(row, 2); if (v == null) return null; return v.trim(); } /** * Returns the value of the active server. * * @return See above. */ String getActiveServer() { return activeServer; } /** * Returns the value of the active port. * * @return See above. */ String getActivePort() { return activePort; } /** * Returns the list of existing servers. * * @return See above. */ Map<String, String> getServers() { Preferences prefs = Preferences.userNodeForPackage(ServerEditor.class); String servers = prefs.get(OMERO_SERVER, null); if (servers == null || servers.length() == 0) return null; String[] l = servers.split(SERVER_NAME_SEPARATOR, 0); if (l == null) return null; Map<String, String> listOfServers = new LinkedHashMap<String, String>(); int index; String server; String name, p; String[] values; for (index = 0; index < l.length; index++) { server = l[index].trim(); if (server.length() > 0) { values = server.split(SERVER_PORT_SEPARATOR, 0); name = values[0]; if (values.length > 1) { p = values[1]; if (OLD_PORTS.contains(p)) p = defaultPort; } else p = defaultPort; if (!name.equals(activeServer)) listOfServers.put(name, p); } } return listOfServers; } /** * Saves the collection of servers. * * @param serverName The name of the server which has to be added at * the end of the list. * @param port */ void handleServers(String serverName, String port) { Map<String, String> l = new LinkedHashMap<String, String>(); String v; for (int i = 0; i < table.getRowCount(); i++) { v = (String) table.getValueAt(i, 1); if (v != null && v.trim().length() > 0) l.put(v, (String) table.getValueAt(i, 2)); } if (activeServer != null && l.get(activeServer) == null) l.put(activeServer, activePort); Preferences prefs = Preferences.userNodeForPackage(ServerEditor.class); if (l == null || l.size() == 0) { //if (l != null) { prefs.put(OMERO_SERVER, ""); return; } Map<String, String> servers = new LinkedHashMap<String, String>(l.size()); Set set = l.entrySet(); Entry entry; Iterator i = set.iterator(); String name; while (i.hasNext()) { entry = (Entry) i.next(); name = (String) entry.getKey(); if (!name.equals(serverName)) servers.put(name, (String) entry.getValue()); } if (serverName != null && serverName.length() != 0) servers.put(serverName, port); i = servers.entrySet().iterator(); int n = servers.size()-1; int index = 0; String list = ""; String value; StringBuffer buffer = new StringBuffer(); while (i.hasNext()) { entry = (Entry) i.next(); buffer.append((String) entry.getKey()); buffer.append(SERVER_PORT_SEPARATOR); if (entry.getValue() != null) buffer.append((String) entry.getValue()); else buffer.append(defaultPort); if (index != n) buffer.append(SERVER_NAME_SEPARATOR); index++; } list = buffer.toString(); if (list.length() != 0) prefs.put(OMERO_SERVER, list); } /** * Sets the focus on the row corresponding to the passed server. * * @param server The server to handle. */ void setFocus(String server) { if (server == null || server.equals(ScreenLogin.DEFAULT_SERVER)) { initFocus(); return; } DefaultTableModel model= ((DefaultTableModel) table.getModel()); int m = model.getRowCount(); String value; int row = -1; for (int i = 0; i < m; i++) { value = (String) model.getValueAt(i, 1); if (server.equals(value)) { row = i; } } if (row > -1) table.setRowSelectionInterval(row, row); } /** Makes sure to remove rows without server address.*/ void onApply() { DefaultTableModel model= ((DefaultTableModel) table.getModel()); int m = model.getRowCount(); if (m <= 1) return; String value; List<Integer> rowToDelete = new ArrayList<Integer>(); for (int i = 0; i < m; i++) { value = (String) model.getValueAt(i, 1); if (value == null || value.trim().length() == 0) rowToDelete.add(i); } Iterator<Integer> j = rowToDelete.iterator(); while (j.hasNext()) { removeRow(j.next()); } } /** * Returns the row corresponding to the file originally selected. * * @return See above. */ int getOriginalRow() { return originalRow; } /** * Returns <code>true</code> if the selected row is the original one, * <code>false</code> otherwise. * * @param value The server selected. * @return See above. */ boolean isOriginal(String value) { DefaultTableModel model = ((DefaultTableModel) table.getModel()); if (table.getSelectedRow() < 0) return false; String v = (String) model.getValueAt(table.getSelectedRow(), 1); if (value == null) return false; return (value.equals(v)); } /** * Adds a new server to the list. * * @param hostName The name of the server. */ void addRow(String hostName) { if (editing) { TableCellEditor editor = table.getCellEditor(); if (editor != null) editor.stopCellEditing(); //if (model.getRowCount() == 0) { editing = false; //if (hostName != null && hostName.trim().length() != 0) addRow(hostName); //} return; } //addButton.setEnabled(false); DefaultTableModel model= ((DefaultTableModel) table.getModel()); int m = model.getRowCount(); Object[] newRow = new Object[3]; newRow[0] = icons.getIcon(IconManager.SERVER_22); boolean editing = true; if (hostName != null && hostName.trim().length() != 0) { newRow[1] = hostName; editing = false; } else newRow[1] = ""; setButtonsEnabled(true); //addButton.setEnabled(true); newRow[2] = defaultPort; model.insertRow(m, newRow); model.fireTableDataChanged(); requestFocusOnEditedCell(m, 1); setEditing(editing); } /** Removes the last row. */ void removeLastRow() { DefaultTableModel model= ((DefaultTableModel) table.getModel()); int m = model.getRowCount(); if (m > 0) { model.removeRow(m-1); model.fireTableDataChanged(); } } /** * Returns the number of rows. * * @return See above. */ int getRowCount() { return table.getRowCount(); } }