package org.openstreetmap.josm.gui.io;
import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.KeyStroke;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.APIDataSet;
import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent;
import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
import org.openstreetmap.josm.data.osm.Changeset;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.gui.HelpAwareOptionPane;
import org.openstreetmap.josm.gui.SideButton;
import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
import org.openstreetmap.josm.gui.help.HelpUtil;
import org.openstreetmap.josm.io.OsmApi;
import org.openstreetmap.josm.tools.ImageProvider;
import org.openstreetmap.josm.tools.WindowGeometry;
/**
* This is a dialog for entering upload options like the parameters for
* the upload changeset and the strategy for opening/closing a changeset.
*
*/
public class UploadDialog extends JDialog implements PropertyChangeListener, PreferenceChangedListener{
protected static final Logger logger = Logger.getLogger(UploadDialog.class.getName());
/** the unique instance of the upload dialog */
static private UploadDialog uploadDialog;
/**
* Replies the unique instance of the upload dialog
*
* @return the unique instance of the upload dialog
*/
static public UploadDialog getUploadDialog() {
if (uploadDialog == null) {
uploadDialog = new UploadDialog();
}
return uploadDialog;
}
/** the panel with the objects to upload */
private UploadedObjectsSummaryPanel pnlUploadedObjects;
/** the panel to select the changeset used */
private ChangesetManagementPanel pnlChangesetManagement;
private BasicUploadSettingsPanel pnlBasicUploadSettings;
private UploadStrategySelectionPanel pnlUploadStrategySelectionPanel;
/** checkbox for selecting whether an atomic upload is to be used */
private TagSettingsPanel pnlTagSettings;
/** the tabbed pane used below of the list of primitives */
private JTabbedPane tpConfigPanels;
/** the upload button */
private JButton btnUpload;
private boolean canceled = false;
/** the changeset comment model keeping the state of the changeset comment */
private ChangesetCommentModel changesetCommentModel;
/**
* builds the content panel for the upload dialog
*
* @return the content panel
*/
protected JPanel buildContentPanel() {
JPanel pnl = new JPanel();
pnl.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
pnl.setLayout(new BorderLayout());
// the panel with the list of uploaded objects
//
pnl.add(pnlUploadedObjects = new UploadedObjectsSummaryPanel(), BorderLayout.CENTER);
// a tabbed pane with two configuration panels in the
// lower half
//
tpConfigPanels = new JTabbedPane() {
@Override
public Dimension getPreferredSize() {
// make sure the tabbed pane never grabs more space than necessary
//
return super.getMinimumSize();
}
};
tpConfigPanels.add(new JPanel());
tpConfigPanels.add(new JPanel());
tpConfigPanels.add(new JPanel());
tpConfigPanels.add(new JPanel());
changesetCommentModel = new ChangesetCommentModel();
tpConfigPanels.setComponentAt(0, pnlBasicUploadSettings = new BasicUploadSettingsPanel(changesetCommentModel));
tpConfigPanels.setTitleAt(0, tr("Settings"));
tpConfigPanels.setToolTipTextAt(0, tr("Decide how to upload the data and which changeset to use"));
tpConfigPanels.setComponentAt(1,pnlTagSettings = new TagSettingsPanel(changesetCommentModel));
tpConfigPanels.setTitleAt(1, tr("Tags of new changeset"));
tpConfigPanels.setToolTipTextAt(1, tr("Apply tags to the changeset data is uploaded to"));
tpConfigPanels.setComponentAt(2,pnlChangesetManagement = new ChangesetManagementPanel(changesetCommentModel));
tpConfigPanels.setTitleAt(2, tr("Changesets"));
tpConfigPanels.setToolTipTextAt(2, tr("Manage open changesets and select a changeset to upload to"));
tpConfigPanels.setComponentAt(3, pnlUploadStrategySelectionPanel = new UploadStrategySelectionPanel());
tpConfigPanels.setTitleAt(3, tr("Advanced"));
tpConfigPanels.setToolTipTextAt(3, tr("Configure advanced settings"));
pnl.add(tpConfigPanels, BorderLayout.SOUTH);
return pnl;
}
/**
* builds the panel with the OK and CANCEL buttons
*
* @return
*/
protected JPanel buildActionPanel() {
JPanel pnl = new JPanel();
pnl.setLayout(new FlowLayout(FlowLayout.CENTER));
pnl.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
// -- upload button
UploadAction uploadAction = new UploadAction();
pnl.add(btnUpload = new SideButton(uploadAction));
btnUpload.setFocusable(true);
InputMap inputMap = btnUpload.getInputMap();
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0), "doUpload");
btnUpload.getActionMap().put("doUpload", uploadAction);
// -- cancel button
CancelAction cancelAction = new CancelAction();
pnl.add(new SideButton(cancelAction));
getRootPane().registerKeyboardAction(
cancelAction,
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE,0),
JComponent.WHEN_IN_FOCUSED_WINDOW
);
pnl.add(new SideButton(new ContextSensitiveHelpAction(ht("/Dialog/UploadDialog"))));
HelpUtil.setHelpContext(getRootPane(),ht("/Dialog/UploadDialog"));
return pnl;
}
/**
* builds the gui
*/
protected void build() {
setTitle(tr("Upload to ''{0}''", OsmApi.getOsmApi().getBaseUrl()));
getContentPane().setLayout(new BorderLayout());
getContentPane().add(buildContentPanel(), BorderLayout.CENTER);
getContentPane().add(buildActionPanel(), BorderLayout.SOUTH);
addWindowListener(new WindowEventHandler());
// make sure the the configuration panels listen to each other
// changes
//
pnlChangesetManagement.addPropertyChangeListener(
pnlBasicUploadSettings.getUploadParameterSummaryPanel()
);
pnlChangesetManagement.addPropertyChangeListener(this);
pnlUploadedObjects.addPropertyChangeListener(
pnlBasicUploadSettings.getUploadParameterSummaryPanel()
);
pnlUploadedObjects.addPropertyChangeListener(pnlUploadStrategySelectionPanel);
pnlUploadStrategySelectionPanel.addPropertyChangeListener(
pnlBasicUploadSettings.getUploadParameterSummaryPanel()
);
// users can click on either of two links in the upload parameter
// summary handler. This installs the handler for these two events.
// We simply select the appropriate tab in the tabbed pane with the
// configuration dialogs.
//
pnlBasicUploadSettings.getUploadParameterSummaryPanel().setConfigurationParameterRequestListener(
new ConfigurationParameterRequestHandler() {
public void handleUploadStrategyConfigurationRequest() {
tpConfigPanels.setSelectedIndex(3);
}
public void handleChangesetConfigurationRequest() {
tpConfigPanels.setSelectedIndex(2);
}
}
);
pnlBasicUploadSettings.setUploadCommentDownFocusTraversalHandler(
new AbstractAction() {
public void actionPerformed(ActionEvent e) {
btnUpload.requestFocusInWindow();
}
}
);
Main.pref.addPreferenceChangeListener(this);
}
/**
* constructor
*/
public UploadDialog() {
super(JOptionPane.getFrameForComponent(Main.parent), true /* modal */);
build();
}
/**
* Sets the collection of primitives to upload
*
* @param toUpload the dataset with the objects to upload. If null, assumes the empty
* set of objects to upload
*
*/
public void setUploadedPrimitives(APIDataSet toUpload) {
if (toUpload == null) {
List<OsmPrimitive> emptyList = Collections.emptyList();
pnlUploadedObjects.setUploadedPrimitives(emptyList, emptyList, emptyList);
return;
}
pnlUploadedObjects.setUploadedPrimitives(
toUpload.getPrimitivesToAdd(),
toUpload.getPrimitivesToUpdate(),
toUpload.getPrimitivesToDelete()
);
}
/**
* Remembers the user input in the preference settings
*/
public void rememberUserInput() {
pnlBasicUploadSettings.rememberUserInput();
pnlUploadStrategySelectionPanel.rememberUserInput();
}
/**
* Initializes the panel for user input
*/
public void startUserInput() {
tpConfigPanels.setSelectedIndex(0);
pnlBasicUploadSettings.startUserInput();
pnlTagSettings.startUserInput();
pnlTagSettings.initFromChangeset(pnlChangesetManagement.getSelectedChangeset());
pnlUploadStrategySelectionPanel.initFromPreferences();
UploadParameterSummaryPanel pnl = pnlBasicUploadSettings.getUploadParameterSummaryPanel();
pnl.setUploadStrategySpecification(pnlUploadStrategySelectionPanel.getUploadStrategySpecification());
pnl.setCloseChangesetAfterNextUpload(pnlChangesetManagement.isCloseChangesetAfterUpload());
pnl.setNumObjects(pnlUploadedObjects.getNumObjectsToUpload());
}
/**
* Replies the current changeset
*
* @return the current changeset
*/
public Changeset getChangeset() {
Changeset cs = pnlChangesetManagement.getSelectedChangeset();
if (cs == null) {
cs = new Changeset();
}
cs.setKeys(pnlTagSettings.getTags());
return cs;
}
public void setSelectedChangesetForNextUpload(Changeset cs) {
pnlChangesetManagement.setSelectedChangesetForNextUpload(cs);
}
/**
* Replies the {@see UploadStrategySpecification} the user entered in the dialog.
*
* @return the {@see UploadStrategySpecification} the user entered in the dialog.
*/
public UploadStrategySpecification getUploadStrategySpecification() {
UploadStrategySpecification spec = pnlUploadStrategySelectionPanel.getUploadStrategySpecification();
spec.setCloseChangesetAfterUpload(pnlChangesetManagement.isCloseChangesetAfterUpload());
return spec;
}
/**
* Replies the current value for the upload comment
*
* @return the current value for the upload comment
*/
protected String getUploadComment() {
return changesetCommentModel.getComment();
}
/**
* Replies true, if the dialog was canceled
*
* @return true, if the dialog was canceled
*/
public boolean isCanceled() {
return canceled;
}
/**
* Sets whether the dialog was canceled
*
* @param canceled true, if the dialog is canceled
*/
protected void setCanceled(boolean canceled) {
this.canceled = canceled;
}
@Override
public void setVisible(boolean visible) {
if (visible) {
new WindowGeometry(
getClass().getName() + ".geometry",
WindowGeometry.centerInWindow(
Main.parent,
new Dimension(400,600)
)
).applySafe(this);
startUserInput();
} else if (!visible && isShowing()){
new WindowGeometry(this).remember(getClass().getName() + ".geometry");
}
super.setVisible(visible);
}
/**
* Handles an upload
*
*/
class UploadAction extends AbstractAction {
public UploadAction() {
putValue(NAME, tr("Upload Changes"));
putValue(SMALL_ICON, ImageProvider.get("upload"));
putValue(SHORT_DESCRIPTION, tr("Upload the changed primitives"));
}
protected void warnIllegalUploadComment() {
HelpAwareOptionPane.showOptionDialog(
UploadDialog.this,
tr("Please enter a comment for this upload changeset (min. 3 characters)"),
tr("Illegal upload comment"),
JOptionPane.ERROR_MESSAGE,
ht("/Dialog/UploadDialog#IllegalUploadComment")
);
}
protected void warnIllegalChunkSize() {
HelpAwareOptionPane.showOptionDialog(
UploadDialog.this,
tr("Please enter a valid chunk size first"),
tr("Illegal chunk size"),
JOptionPane.ERROR_MESSAGE,
ht("/Dialog/UploadDialog#IllegalChunkSize")
);
}
public void actionPerformed(ActionEvent e) {
if (getUploadComment().trim().length() < 3) {
warnIllegalUploadComment();
tpConfigPanels.setSelectedIndex(0);
pnlBasicUploadSettings.initEditingOfUploadComment();
return;
}
UploadStrategySpecification strategy = getUploadStrategySpecification();
if (strategy.getStrategy().equals(UploadStrategy.CHUNKED_DATASET_STRATEGY)) {
if (strategy.getChunkSize() == UploadStrategySpecification.UNSPECIFIED_CHUNK_SIZE) {
warnIllegalChunkSize();
tpConfigPanels.setSelectedIndex(0);
return;
}
}
setCanceled(false);
setVisible(false);
}
}
/**
* Action for canceling the dialog
*
*/
class CancelAction extends AbstractAction {
public CancelAction() {
putValue(NAME, tr("Cancel"));
putValue(SMALL_ICON, ImageProvider.get("cancel"));
putValue(SHORT_DESCRIPTION, tr("Cancel the upload and resume editing"));
}
public void actionPerformed(ActionEvent e) {
setCanceled(true);
setVisible(false);
}
}
/**
* Listens to window closing events and processes them as cancel events.
* Listens to window open events and initializes user input
*
*/
class WindowEventHandler extends WindowAdapter {
@Override
public void windowClosing(WindowEvent e) {
setCanceled(true);
}
@Override
public void windowOpened(WindowEvent e) {
//startUserInput();
}
@Override
public void windowActivated(WindowEvent arg0) {
if (tpConfigPanels.getSelectedIndex() == 0) {
pnlBasicUploadSettings.initEditingOfUploadComment();
}
}
}
/* -------------------------------------------------------------------------- */
/* Interface PropertyChangeListener */
/* -------------------------------------------------------------------------- */
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(ChangesetManagementPanel.SELECTED_CHANGESET_PROP)) {
Changeset cs = (Changeset)evt.getNewValue();
if (cs == null) {
tpConfigPanels.setTitleAt(1, tr("Tags of new changeset"));
} else {
tpConfigPanels.setTitleAt(1, tr("Tags of changeset {0}", cs.getId()));
}
}
}
/* -------------------------------------------------------------------------- */
/* Interface PreferenceChangedListener */
/* -------------------------------------------------------------------------- */
public void preferenceChanged(PreferenceChangeEvent e) {
if (e.getKey() == null || ! e.getKey().equals("osm-server.url"))
return;
if (e.getNewValue() == null) {
setTitle(tr("Upload"));
} else {
setTitle(tr("Upload to ''{0}''", e.getNewValue()));
}
}
}