diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAIInfo.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAIInfo.java index 549f4dd..8cfe471 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAIInfo.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAIInfo.java @@ -31,6 +31,7 @@ import org.openstreetmap.josm.data.StructUtils.StructEntry; import org.openstreetmap.josm.data.imagery.ImageryInfo; import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds; import org.openstreetmap.josm.data.imagery.Shape; +import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo.MapWithAIPreferenceEntry; import org.openstreetmap.josm.spi.preferences.Config; import org.openstreetmap.josm.tools.CheckParameterUtil; import org.openstreetmap.josm.tools.ImageProvider; @@ -126,8 +127,6 @@ public class MapWithAIInfo extends TileSourceInfo implements Comparable minimumCacheExpiryUnit; private TimeUnit currentUnit; - protected AddMapWithAIPanel() { + private MapWithAIInfo info; + + protected AddMapWithAIPanel(LayoutManager layout) { + super(layout); + registerValidableComponent(name); + } + + /** + * default constructor + */ + public AddMapWithAIPanel() { this(new GridBagLayout()); headersTable = new HeadersTable(); + parametersTable = new MapWithAIParametersPanel(); minimumCacheExpiry = new JSpinner(new SpinnerNumberModel( (Number) TimeUnit.MILLISECONDS.toSeconds(TMSCachedTileLoaderJob.MINIMUM_EXPIRES.get()), 0L, Long.valueOf(Integer.MAX_VALUE), 1)); @@ -86,10 +106,34 @@ public class AddMapWithAIPanel extends JPanel { minimumCacheExpiry.setValue(newValue); } }); + add(new JLabel(tr("{0} Make sure OSM has the permission to use this service", "1.")), GBC.eol()); + add(new JLabel(tr("{0} Enter Service URL", "2.")), GBC.eol()); + add(rawUrl, GBC.eop().fill(GBC.HORIZONTAL)); + rawUrl.setLineWrap(true); + rawUrl.setAlignmentY(TOP_ALIGNMENT); + add(layerPanel, GBC.eol().fill()); + addCommonSettings(); + + add(new JLabel(tr("{0} Enter name for this source", "3.")), GBC.eol()); + add(name, GBC.eol().fill(GBC.HORIZONTAL)); + registerValidableComponent(rawUrl); + } + + public AddMapWithAIPanel(MapWithAIInfo info) { + this(); + this.info = info; + rawUrl.setText(info.getUrl()); + name.setText(info.getName()); + if (info.getParameters() != null) { + parametersTable.setParameters(info.getParameters()); + } + parametersTable.addListener(e -> notifyListeners()); } protected void addCommonSettings() { + add(new JLabel(tr("Set parameters")), GBC.eop()); + add(parametersTable, GBC.eol().fill()); if (ExpertToggleAction.isExpert()) { add(new JLabel(tr("Minimum cache expiry: "))); add(minimumCacheExpiry); @@ -104,13 +148,12 @@ public class AddMapWithAIPanel extends JPanel { return headersTable.getHeaders(); } - protected boolean getCommonIsValidGeoreference() { - return validGeoreference.isSelected(); + protected Map> getCommonParameters() { + return parametersTable.getParameters(); } - protected AddMapWithAIPanel(LayoutManager layout) { - super(layout); - registerValidableComponent(name); + protected boolean getCommonIsValidGeoreference() { + return validGeoreference.isSelected(); } protected final void registerValidableComponent(AbstractButton component) { @@ -136,16 +179,33 @@ public class AddMapWithAIPanel extends JPanel { }); } - protected MapWithAIInfo getInfo() { - // TODO - return new MapWithAIInfo(); + private JsonArray convertToJsonParameterArray(Map> parameters) { + JsonArrayBuilder builder = Json.createArrayBuilder(); + for (Map.Entry> entry : parameters.entrySet()) { + JsonObjectBuilder entryBuilder = Json.createObjectBuilder(); + entryBuilder.add("parameter", entry.getKey()); + entryBuilder.add("description", entry.getValue().getKey()); + entryBuilder.add("enabled", entry.getValue().getValue()); + builder.add(entryBuilder.build()); + } + return builder.build(); + } + + protected MapWithAIInfo getSourceInfo() { + MapWithAIInfo ret = info == null ? new MapWithAIInfo() : info; + ret.setName(getImageryName()); + ret.setUrl(getImageryRawUrl()); + ret.setCustomHttpHeaders(getCommonHeaders()); + ret.setSourceType(MapWithAIType.THIRD_PARTY); + ret.setParameters(convertToJsonParameterArray(getCommonParameters())); + return ret; } protected static String sanitize(String s) { return s.replaceAll("[\r\n]+", "").trim(); } - protected static String sanitize(String s, MapWithAIType type) { + protected static String sanitize(String s, ImageryType type) { String ret = s; String imageryType = type.getTypeString() + ':'; if (ret.startsWith(imageryType)) { @@ -155,16 +215,16 @@ public class AddMapWithAIPanel extends JPanel { return sanitize(ret); } - protected final String getInfoName() { + protected final String getImageryName() { return sanitize(name.getText()); } - protected final String getInfoRawUrl() { + protected final String getImageryRawUrl() { return sanitize(rawUrl.getText()); } - protected boolean isInfoValid() { - return true; // TODO check validity + protected boolean isSourceValid() { + return !getImageryName().isEmpty() && !getImageryRawUrl().isEmpty(); } /** @@ -181,7 +241,7 @@ public class AddMapWithAIPanel extends JPanel { private void notifyListeners() { for (ContentValidationListener l : listeners) { - l.contentChanged(isInfoValid()); + l.contentChanged(isSourceValid()); } } } diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAIParametersPanel.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAIParametersPanel.java new file mode 100644 index 0000000..d1674e3 --- /dev/null +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAIParametersPanel.java @@ -0,0 +1,158 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.mapwithai.gui.preferences.mapwithai; + +import static org.openstreetmap.josm.tools.I18n.tr; + +import java.awt.GridBagLayout; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.event.TableModelListener; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.TableModel; + +import org.apache.commons.lang3.tuple.Pair; +import org.openstreetmap.josm.tools.GBC; + +public class MapWithAIParametersPanel extends JPanel { + + private final class ParametersTableModel extends AbstractTableModel { + @Override + public String getColumnName(int column) { + switch (column) { + case 0: + return tr("Parameter name"); + case 1: + return tr("Parameter value"); + case 2: + return tr("Enabled"); + default: + return ""; + } + } + + @Override + public Class getColumnClass(int column) { + switch (column) { + case 2: + return Boolean.class; + default: + return String.class; + } + } + + @Override + public int getRowCount() { + return headers.size() + 1; + } + + @Override + public int getColumnCount() { + return 3; + } + + @Override + public Object getValueAt(int row, int col) { + if (row < headers.size()) { + return headers.get(row)[col]; + } + if (String.class.equals(getColumnClass(col))) { + return ""; + } + return null; + } + + @Override + public boolean isCellEditable(int row, int column) { + return true; + } + + @Override + public void setValueAt(Object value, int row, int col) { + if (row < headers.size()) { + Object[] headerRow = headers.get(row); + headerRow[col] = value; + if ("".equals(headerRow[0]) && "".equals(headerRow[1])) { + headers.remove(row); + fireTableRowsDeleted(row, row); + } + + } else if (row == headers.size()) { + Object[] entry = { "", "", null }; + entry[col] = value; + headers.add(entry); + fireTableRowsInserted(row + 1, row + 1); + } + fireTableCellUpdated(row, col); + } + } + + private List headers; + private ParametersTableModel model; + + /** + * Creates empty table + */ + public MapWithAIParametersPanel() { + this(new ConcurrentHashMap<>()); + } + + /** + * Create table prefilled with headers + * + * @param headers contents of table + */ + public MapWithAIParametersPanel(Map> headers) { + super(new GridBagLayout()); + this.headers = getHeadersAsVector(headers); + this.model = new ParametersTableModel(); + JTable table = new JTable(model); + table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + table.setAutoCreateRowSorter(true); + table.setRowSelectionAllowed(false); + table.setColumnSelectionAllowed(false); + add(new JScrollPane(table), GBC.eol().fill()); + } + + private static List getHeadersAsVector(Map> headers) { + return headers.entrySet().stream().sorted((e1, e2) -> e1.getKey().compareTo(e2.getKey())) + .map(e -> new Object[] { e.getKey(), e.getValue().getLeft(), e.getValue().getRight() }) + .collect(Collectors.toList()); + } + + /** + * @return headers provided by user + */ + public Map> getParameters() { + return headers.stream().distinct() + .collect(Collectors.toMap(x -> (String) x[0], x -> Pair.of((String) x[1], (Boolean) x[2]))); + } + + public void setParameters(JsonArray parameters) { + int i = 0; + for (JsonObject obj : parameters.stream().filter(JsonObject.class::isInstance).map(JsonObject.class::cast) + .collect(Collectors.toList())) { + model.setValueAt(obj.getString("parameter"), i, 1); + model.setValueAt(obj.getString("description", ""), i, 0); + model.setValueAt(obj.getBoolean("enabled", false), i, 2); + } + model.fireTableDataChanged(); + } + + public void addListener(TableModelListener l) { + model.addTableModelListener(l); + } + + public TableModel getModel() { + return model; + } + +} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAIProvidersPanel.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAIProvidersPanel.java index 5c3692c..96026f8 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAIProvidersPanel.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAIProvidersPanel.java @@ -275,6 +275,9 @@ public class MapWithAIProvidersPanel extends JPanel { RemoveEntryAction remove = new RemoveEntryAction(); activeTable.getSelectionModel().addListSelectionListener(remove); + EditEntryAction edit = new EditEntryAction(); + activeTable.getSelectionModel().addListSelectionListener(edit); + add(new JLabel(tr("Available default entries:")), GBC.std().insets(5, 5, 0, 0)); add(new JLabel(tr("Boundaries of selected MapWithAI entries:")), GBC.eol().insets(5, 5, 0, 0)); @@ -341,6 +344,7 @@ public class MapWithAIProvidersPanel extends JPanel { activeToolbar.setBorderPainted(false); activeToolbar.setOpaque(false); activeToolbar.add(new NewEntryAction(MapWithAIInfo.MapWithAIType.THIRD_PARTY)); + activeToolbar.add(edit); activeToolbar.add(remove); add(activeToolbar, GBC.eol().anchor(GBC.NORTH).insets(0, 0, 5, 5)); } @@ -470,7 +474,7 @@ public class MapWithAIProvidersPanel extends JPanel { if (addDialog.getValue() == 1) { try { - activeModel.addRow(p.getInfo()); + activeModel.addRow(p.getSourceInfo()); } catch (IllegalArgumentException ex) { if (ex.getMessage() == null || ex.getMessage().isEmpty()) { throw ex; @@ -483,6 +487,41 @@ public class MapWithAIProvidersPanel extends JPanel { } } + private class EditEntryAction extends AbstractAction implements ListSelectionListener { + private static final long serialVersionUID = -1682304557691078801L; + + /** + * Constructs a new {@code EditEntryAction}. + */ + EditEntryAction() { + putValue(NAME, tr("Edit")); + putValue(SHORT_DESCRIPTION, tr("Edit entry")); + new ImageProvider("dialogs", "edit").getResource().attachImageIcon(this, true); + updateEnabledState(); + } + + protected final void updateEnabledState() { + setEnabled(activeTable.getSelectedRowCount() > 0); + } + + @Override + public void valueChanged(ListSelectionEvent e) { + updateEnabledState(); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (activeTable.getSelectedRow() != -1) { + final AddMapWithAIPanel p = new AddMapWithAIPanel(activeModel.getRow(activeTable.getSelectedRow())); + final AddMapWithAIDialog addDialog = new AddMapWithAIDialog(gui, p); + addDialog.showDialog(); + if (addDialog.getValue() == 1) { + p.getSourceInfo(); + } + } + } + } + private class RemoveEntryAction extends AbstractAction implements ListSelectionListener { private static final long serialVersionUID = 2666450386256004180L; diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/MapWithAISourceReader.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/MapWithAISourceReader.java index e9f2efb..d0f0074 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/MapWithAISourceReader.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/MapWithAISourceReader.java @@ -136,8 +136,8 @@ public class MapWithAISourceReader implements Closeable { bounds.forEach(bound::extend); bounds.forEach(b -> b.getShapes().forEach(bound::addShape)); info.setBounds(bound); - return info; } + return info; } return new MapWithAIInfo(name); }