/*
* Copyright (c) 2010, SQL Power Group Inc.
*
* This file is part of Power*Architect.
*
* Power*Architect 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 3 of the License, or
* (at your option) any later version.
*
* Power*Architect 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, see <http://www.gnu.org/licenses/>.
*/
package ca.sqlpower.swingui.enterprise.client;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import org.apache.http.client.CookieStore;
import org.json.JSONException;
import ca.sqlpower.enterprise.ClientSideSessionUtils;
import ca.sqlpower.enterprise.client.ProjectLocation;
import ca.sqlpower.enterprise.client.SPServerInfo;
import ca.sqlpower.enterprise.client.SPServerInfoManager;
import ca.sqlpower.swingui.SPSUtils;
import ca.sqlpower.util.UserPrompterFactory;
import ca.sqlpower.util.UserPrompter.UserPromptOptions;
import ca.sqlpower.util.UserPrompter.UserPromptResponse;
import ca.sqlpower.util.UserPrompterFactory.UserPromptType;
import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.factories.ButtonBarFactory;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
/**
* This class can create a dialog to display server connections and the projects
* in them.
*/
public abstract class ServerProjectsManagerPanel {
private final Component dialogOwner;
private final UserPrompterFactory upf;
private final File defaultFileDirectory;
private final JPanel panel;
private final Action closeAction;
private final JList projects;
private final JList servers;
private final Action refreshAction = new AbstractAction("Refresh") {
public void actionPerformed(ActionEvent e) {
refreshInfoList();
}
};
private final Action newAction = new AbstractAction("New...") {
public void actionPerformed(ActionEvent e) {
if (getSelectedServerInfo() != null) {
String name = JOptionPane.showInputDialog(dialogOwner, "Please specify the name of your project", "", JOptionPane.QUESTION_MESSAGE);
if (name != null) {
DefaultListModel model = (DefaultListModel) projects.getModel();
for (int i = 0; i < model.size(); i++) {
if (((ProjectLocation) model.getElementAt(i)).getName().trim().equalsIgnoreCase(name.trim())) {
JOptionPane.showMessageDialog(dialogOwner, "A project called \"" + name + "\" already exists. Please use a different name.", "Duplicate project name", JOptionPane.ERROR_MESSAGE);
return;
}
}
try {
Window frame = SwingUtilities.getWindowAncestor(dialogOwner);
JLabel messageLabel = new JLabel("Creating New Project");
JProgressBar progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
final JDialog dialog = new JDialog(frame, "Creating New Project");
DefaultFormBuilder builder = new DefaultFormBuilder(new FormLayout("pref:grow, 5dlu, pref"));
builder.setDefaultDialogBorder();
builder.append(messageLabel, 3);
builder.nextLine();
builder.append(progressBar, 3);
dialog.add(builder.getPanel());
dialog.setSize(new Dimension(300,90));
dialog.setLocationRelativeTo(frame);
dialog.setAlwaysOnTop(true);
dialog.setVisible(true);
ClientSideSessionUtils.createNewServerSession(getSelectedServerInfo(), name, cookieStore, upf);
dialog.dispose();
} catch (Exception ex) {
throw new RuntimeException("Unable to create new project", ex);
}
refreshInfoList();
}
}
}
};
private final Action uploadAction = new AbstractAction("Upload") {
public void actionPerformed(ActionEvent e) {
final JDialog dialog = new JDialog(SwingUtilities.getWindowAncestor(dialogOwner), "Upload Project", ModalityType.DOCUMENT_MODAL);
JPanel panel = new JPanel();
FormLayout layout = new FormLayout("4dlu, pref, 4dlu, max(150dlu;pref), 4dlu, pref, 4dlu", "pref, pref, pref");
DefaultFormBuilder builder = new DefaultFormBuilder(layout, panel);
CellConstraints cc = new CellConstraints();
final JTextField nameField = new JTextField();
builder.add(new JLabel("Name"), cc.xy(2, 1));
builder.add(nameField, cc.xyw(4, 1, 3));
final JTextField fileField = new JTextField();
JButton fileButton = new JButton("...");
fileButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JFileChooser chooser = new JFileChooser(defaultFileDirectory);
chooser.addChoosableFileFilter(SPSUtils.ARCHITECT_FILE_FILTER);
chooser.setDialogTitle("Choose Project to Upload");
int response = chooser.showOpenDialog(dialog);
if (response != JFileChooser.APPROVE_OPTION) {
return;
}
fileField.setText(chooser.getSelectedFile().getAbsolutePath());
}
});
builder.add(new JLabel("File"), cc.xy(2, 2));
builder.add(fileField, cc.xy(4, 2));
builder.add(fileButton, cc.xy(6, 2));
final JButton okButton = new JButton("Upload");
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
File f = new File(fileField.getText());
if (!f.canRead()) {
JOptionPane.showMessageDialog(dialogOwner, "File cannot be read", "Invalid File", JOptionPane.ERROR_MESSAGE);
return;
}
try {
ClientSideSessionUtils.uploadProject(getSelectedServerInfo(), nameField.getText(), f, upf, cookieStore);
dialog.dispose();
refreshInfoList();
} catch (Exception ex) {
SPSUtils.showExceptionDialogNoReport(dialog, "Unable to upload project", ex);
}
}
});
JButton cancelButton = new JButton("Cancel");
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dialog.dispose();
}
});
okButton.setEnabled(false);
DocumentListener documentListener = new DocumentListener() {
@Override
public void changedUpdate(DocumentEvent e) {
textChanged(e);
}
@Override
public void insertUpdate(DocumentEvent e) {
textChanged(e);
}
@Override
public void removeUpdate(DocumentEvent e) {
textChanged(e);
}
private void textChanged(DocumentEvent e) {
okButton.setEnabled(!fileField.getText().isEmpty() && !nameField.getText().isEmpty());
}
};
fileField.getDocument().addDocumentListener(documentListener);
nameField.getDocument().addDocumentListener(documentListener);
JPanel buttonBar = ButtonBarFactory.buildRightAlignedBar(okButton, cancelButton);
builder.add(buttonBar, cc.xyw(2, 3, 5));
dialog.add(panel);
SPSUtils.makeJDialogCancellable(dialog, null);
dialog.pack();
dialog.setLocationRelativeTo(dialogOwner);
dialog.setVisible(true);
}
};
private final Action deleteAction = new AbstractAction("Delete") {
public void actionPerformed(ActionEvent e) {
if (getSelectedServerInfo() != null) {
int [] indices = projects.getSelectedIndices();
if (indices.length >= 1) {
final Object [] objs = new Object[indices.length];
for (int i = 0; i < indices.length; i++) {
objs[i] = projects.getModel().getElementAt(indices[i]);
}
String promptMessage;
if (indices.length == 1) {
promptMessage = "Are you sure you want to delete the selected project?" +
"\nThis action cannot be undone.";
} else {
promptMessage = "Are you sure you want to delete these " + indices.length + " selected projects?" +
"\nThis action cannot be undone.";
}
if (JOptionPane.showConfirmDialog(dialogOwner, promptMessage, "Confirm Delete Projects",
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) {
for (Object obj : objs) {
if (obj instanceof ProjectLocation) {
ProjectLocation location = (ProjectLocation) obj;
try {
ClientSideSessionUtils.deleteServerWorkspace(location, cookieStore, upf);
} catch (Exception ex) {
throw new RuntimeException("Unable to delete project", ex);
}
}
}
refreshInfoList();
}
}
}
}
};
private boolean connected = false;
private SPServerInfo serverInfo = null;
private final CookieStore cookieStore;
private final JButton securityButton = new JButton();
private final JButton openButton = new JButton();
/**
* This constructor creates a dialog for modifying and loading a project
* from a single server designated by the given serverInfo parameter.
*
* @param serverInfo
* Projects will be retrieved from this server based on the
* information and displayed. The dialog will allow editing the
* security as well as creating and deleting projects on this
* server.
* @param dialogOwner
* The dialog to parent new dialogs to.
* @param upf
* A user prompter factory for displaying error and warning
* messages to users.
* @param closeAction
* An action that will close the dialog or frame that this dialog
* is contained in.
* @param defaultFileDirectory
* A default file directory to start looking for files in if the
* user wants to upload a project. If null this will default to
* the user's home directory.
* @param cookieStore
* A cookie store for HTTP requests. Used by the
* {@link ClientSideSessionUtils}.
*/
public ServerProjectsManagerPanel(
SPServerInfo serverInfo,
Component dialogOwner,
UserPrompterFactory upf,
Action closeAction,
File defaultFileDirectory,
CookieStore cookieStore) {
this.serverInfo = serverInfo;
this.dialogOwner = dialogOwner;
this.upf = upf;
this.closeAction = closeAction;
this.defaultFileDirectory = defaultFileDirectory;
this.cookieStore = cookieStore;
cookieStore.clear();
DefaultFormBuilder builder = new DefaultFormBuilder(new FormLayout(
"pref:grow, 5dlu, pref",
"pref, pref, pref"));
servers = null;
projects = new JList(new DefaultListModel());
projects.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
refreshPanel();
if (e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton(e)) {
getOpenAction().actionPerformed(null);
}
}
});
JScrollPane projectsPane = new JScrollPane(projects);
projectsPane.setPreferredSize(new Dimension(250, 300));
CellConstraints cc = new CellConstraints();
builder.add(new JLabel(serverInfo.getName() + "'s projects:"), cc.xyw(1, 1, 2));
builder.nextLine();
builder.add(projectsPane, cc.xywh(1, 2, 1, 2));
DefaultFormBuilder buttonBarBuilder = new DefaultFormBuilder(new FormLayout("pref"));
buttonBarBuilder.append(new JButton(refreshAction));
buttonBarBuilder.append(securityButton);
buttonBarBuilder.append(new JButton(newAction));
buttonBarBuilder.append(openButton);
buttonBarBuilder.append(new JButton(deleteAction));
buttonBarBuilder.append(new JButton(closeAction));
builder.add(buttonBarBuilder.getPanel(), cc.xy(3, 2));
builder.setDefaultDialogBorder();
panel = builder.getPanel();
}
/**
* Creates a dialog that lets the user choose a server connection and then a
* project.
*
* @param serverManager
* This object contains all of the server information objects
* known and the servers based on their information will be
* displayed in a list so a user can navigate to different
* projects in different servers.
* @param dialogOwner
* The dialog to parent new dialogs to.
* @param upf
* A user prompter factory for displaying error and warning
* messages to users.
* @param closeAction
* An action that will close the dialog or frame that this dialog
* is contained in.
* @param defaultFileDirectory
* A default file directory to start looking for files in if the
* user wants to upload a project. If null this will default to
* the user's home directory.
* @param cookieStore
* A cookie store for HTTP requests. Used by the
* {@link ClientSideSessionUtils}.
*/
public ServerProjectsManagerPanel(
SPServerInfoManager serverManager,
Component dialogOwner,
UserPrompterFactory upf,
Action closeAction,
File defaultFileDirectory,
CookieStore cookieStore) {
this.dialogOwner = dialogOwner;
this.upf = upf;
this.closeAction = closeAction;
this.defaultFileDirectory = defaultFileDirectory;
this.cookieStore = cookieStore;
cookieStore.clear();
DefaultFormBuilder builder = new DefaultFormBuilder(new FormLayout(
"pref:grow, 5dlu, pref:grow, 5dlu, pref",
"pref, pref, pref"));
servers = new JList(new DefaultListModel());
servers.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
refreshInfoList();
}
}
});
DefaultListModel serversModel = (DefaultListModel) servers.getModel();
serversModel.removeAllElements();
if (serverManager.getServers(false).size() > 0) {
for (SPServerInfo serverInfo : serverManager.getServers(false)) {
serversModel.addElement(serverInfo);
}
} else {
serversModel.addElement("No Servers");
servers.setEnabled(false);
}
projects = new JList(new DefaultListModel());
projects.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
refreshPanel();
if (e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton(e)) {
getOpenAction().actionPerformed(null);
}
}
});
JScrollPane projectsPane = new JScrollPane(projects);
projectsPane.setPreferredSize(new Dimension(250, 300));
JScrollPane serverPane = new JScrollPane(servers);
serverPane.setPreferredSize(new Dimension(250, 300));
CellConstraints cc = new CellConstraints();
builder.add(new JLabel("Servers:"), cc.xyw(1, 1, 2));
builder.add(new JLabel("Projects:"), cc.xyw(3, 1, 2));
builder.nextLine();
builder.add(serverPane, cc.xywh(1, 2, 1, 2));
builder.add(projectsPane, cc.xywh(3, 2, 1, 2));
DefaultFormBuilder buttonBarBuilder = new DefaultFormBuilder(new FormLayout("pref"));
buttonBarBuilder.append(new JButton(refreshAction));
buttonBarBuilder.append(securityButton);
buttonBarBuilder.append(new JButton(newAction));
buttonBarBuilder.append(openButton);
buttonBarBuilder.append(new JButton(uploadAction));
buttonBarBuilder.append(new JButton(deleteAction));
buttonBarBuilder.append(new JButton(closeAction));
builder.add(buttonBarBuilder.getPanel(), cc.xy(5, 2));
builder.setDefaultDialogBorder();
panel = builder.getPanel();
}
public boolean isConnected() {
refreshInfoList();
return connected;
}
public JPanel getPanel() {
openButton.setAction(getOpenAction());
securityButton.setAction(getSecurityAction());
refreshInfoList();
return panel;
}
private void refreshPanel() {
// Update the status of buttons and lists .
if (connected) {
securityButton.setEnabled(true);
newAction.setEnabled(true);
uploadAction.setEnabled(true);
if (projects.isSelectionEmpty()) {
openButton.setEnabled(false);
deleteAction.setEnabled(false);
} else {
openButton.setEnabled(true);
deleteAction.setEnabled(true);
}
projects.setEnabled(true);
} else {
securityButton.setEnabled(false);
newAction.setEnabled(false);
openButton.setEnabled(false);
uploadAction.setEnabled(false);
deleteAction.setEnabled(false);
projects.setEnabled(false);
}
}
protected void refreshInfoList() {
DefaultListModel model = (DefaultListModel) projects.getModel();
model.removeAllElements();
SPServerInfo serviceInfo = getSelectedServerInfo();
if (serviceInfo != null) {
try {
List<ProjectLocation> projectLocations = getProjectLocations();
// Sorts the project locations alphabetically
Collections.sort(projectLocations, new Comparator<ProjectLocation>() {
public int compare(ProjectLocation proj1, ProjectLocation proj2) {
return proj1.getName().compareToIgnoreCase(proj2.getName());
}
});
model.clear();
for (ProjectLocation pl : projectLocations) {
model.addElement(pl);
}
connected = true;
} catch (Exception ex) {
model.removeAllElements();
model.addElement("Unable to get projects from server");
connected = false;
upf.createUserPrompter("Server Unavailable",
UserPromptType.MESSAGE,
UserPromptOptions.OK,
UserPromptResponse.OK,
"OK", "OK").promptUser("");
}
refreshPanel();
} else {
model.addElement("No Server Selected");
connected = false;
refreshPanel();
}
}
/**
* Returns all of the project locations for the selected server.
*/
protected abstract List<ProjectLocation> getProjectLocations() throws
IOException, URISyntaxException, JSONException;
/**
* Returns the server info for the selected server the user is currently
* viewing.
*/
protected SPServerInfo getSelectedServerInfo() {
if (serverInfo != null) return serverInfo;
int index = servers.getSelectedIndex();
Object obj;
if (index >= 0) {
obj = servers.getModel().getElementAt(index);
if (obj instanceof SPServerInfo) {
return (SPServerInfo) obj;
}
}
return null;
}
protected Component getDialogOwner() {
return dialogOwner;
}
protected Action getCloseAction() {
return closeAction;
}
protected List<ProjectLocation> getSelectedProjects() {
List<ProjectLocation> projectLocations = new ArrayList<ProjectLocation>();
if (getSelectedServerInfo() != null) {
int [] indices = projects.getSelectedIndices();
if (indices.length >= 1) {
for (int i = 0; i < indices.length; i++) {
projectLocations.add((ProjectLocation) projects.getModel().getElementAt(indices[i]));
}
}
}
return projectLocations;
}
/**
* This action will open the selected project(s).
*/
protected abstract Action getOpenAction();
/**
* This action will open a security editor for the selected server.
*/
protected abstract Action getSecurityAction();
}