package com.opendoorlogistics.studio.components.map.plugins;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import com.opendoorlogistics.api.standardcomponents.map.MapActionFactory;
import com.opendoorlogistics.api.standardcomponents.map.MapApi;
import com.opendoorlogistics.api.standardcomponents.map.MapApi.PanelPosition;
import com.opendoorlogistics.api.standardcomponents.map.MapPlugin;
import com.opendoorlogistics.api.standardcomponents.map.StandardMapMenuOrdering;
import com.opendoorlogistics.api.tables.ODLListener;
import com.opendoorlogistics.api.tables.ODLTable;
import com.opendoorlogistics.api.tables.ODLTableReadOnly;
import com.opendoorlogistics.api.tables.TableFlags;
import com.opendoorlogistics.api.ui.Disposable;
import com.opendoorlogistics.core.tables.utils.TableUtils;
import com.opendoorlogistics.core.utils.Pair;
import com.opendoorlogistics.core.utils.ui.VerticalLayoutPanel;
import com.opendoorlogistics.studio.components.map.AbstractMapMode;
import com.opendoorlogistics.studio.components.map.SuggestedFillValuesManager;
import com.opendoorlogistics.studio.components.map.plugins.utils.PluginUtils;
import com.opendoorlogistics.studio.controls.DynamicComboBox;
import com.opendoorlogistics.studio.panels.FieldSelectorPanel;
import gnu.trove.set.hash.TIntHashSet;
public class FillPlugin implements MapPlugin {
private static final long NEEDS_FLAGS = TableFlags.UI_SET_ALLOWED;
@Override
public String getId(){
return "com.opendoorlogistics.studio.components.map.plugins.FillPlugin";
}
@Override
public void initMap(final MapApi api) {
final SuggestedFillValuesManager fillSuggestedValues = new SuggestedFillValuesManager();
PluginUtils.registerActionFactory(api, new MapActionFactory() {
@Override
public Action create(MapApi api) {
return createAction(api, fillSuggestedValues);
}
}, StandardMapMenuOrdering.FILL_MODE, "mapmode",NEEDS_FLAGS );
}
private Action createAction(final MapApi api, final SuggestedFillValuesManager fillSuggestedValues) {
AbstractAction action = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
if(!PluginUtils.exitIfInMode(api, FillMode.class)){
api.setMapMode(new FillMode(api, fillSuggestedValues));
}
}
};
PluginUtils.initAction("Fill", "Fill the columns of objects on the map.", "tool-bucket-fill-16x16.png", action);
return action;
}
private static class FillMode extends AbstractMapMode {
private final MapApi api;
private final SuggestedFillValuesManager fillSuggestedValues;
public FillMode(MapApi api, SuggestedFillValuesManager fillSuggestedValues) {
this.api = api;
this.fillSuggestedValues = fillSuggestedValues;
}
@Override
public Cursor getCursor() {
return PluginUtils.createCursor("tool-bucket-fill-32x32.png", 22, 15);
}
@Override
public void onObjectsChanged(MapApi api) {
PluginUtils.exitModeIfNeeded(api, FillMode.class, NEEDS_FLAGS,false);
}
@Override
public void paint(MapApi api, Graphics2D g) {
Rectangle rectangle = getDragRectangle();
if (rectangle != null) {
Color fillColour = new Color(200, 192, 255, 100);
PluginUtils.drawRectangle(g, rectangle, fillColour);
}
}
@Override
public void mouseDragged(MouseEvent evt) {
super.mouseDragged(evt);
api.repaint(true);
}
@Override
public void mouseReleased(MouseEvent e) {
Rectangle rectangle = getDragRectangle();
super.mouseReleased(e);
if (rectangle != null) {
final long[] ids = api.getSelectableIdsWithinPixelRectangle(rectangle);
if (ids != null && ids.length > 0) {
final JPanel panel = api.getSidePanel(PanelPosition.LEFT);
if (panel != null && FillFieldPanel.class.isInstance(panel)) {
api.getMapDataApi().runTransactionOnGlobalDatastore(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
processFill((FillFieldPanel) panel,ids);
return true;
}
});
}
}
}
}
private Pair<ODLTable, Integer> getSelectedFillTableColumn(FillFieldPanel fieldSelectorPanel) {
if (fieldSelectorPanel == null) {
return null;
}
String[] tableFieldPair = fieldSelectorPanel.getField();
ODLTable table = null;
int col = -1;
if (tableFieldPair != null && tableFieldPair.length == 2 && tableFieldPair[0] != null && tableFieldPair[1] != null) {
table = TableUtils.findTable(api.getMapDataApi().getGlobalDatastore(), tableFieldPair[0], true);
if (table != null) {
col = TableUtils.findColumnIndx(table, tableFieldPair[1], true);
}
}
if (col != -1) {
return new Pair<ODLTable, Integer>(table, col);
}
return null;
}
private void processFill(FillFieldPanel panel, long[] selected) {
final Pair<ODLTable, Integer> pair = getSelectedFillTableColumn(panel);
if (pair == null) {
JOptionPane.showMessageDialog(api.getMapWindowComponent(), "No fill column selected.");
return;
}
// get the fill value and log this fill to the suggested values
// manager
final String value = panel.getFillValue();
fillSuggestedValues.addFill(pair.getFirst().getName(), pair.getFirst().getColumnName(pair.getSecond()), value);
int nbProcessed = 0;
int nbSet = 0;
for (long id : selected) {
nbProcessed++;
ODLTable objTable = api.getMapDataApi().getGlobalTable(TableUtils.getTableId(id));
if (objTable == null || objTable.getImmutableId() != pair.getFirst().getImmutableId()) {
continue;
}
pair.getFirst().setValueById(value, id, pair.getSecond());
nbSet++;
}
if (nbProcessed > 0 && nbSet == 0) {
JOptionPane.showMessageDialog(api.getMapWindowComponent(), "No values were set as none of the selected objects were from the selected table.");
}
}
@Override
public void onEnterMode(MapApi api) {
api.setSidePanel(new FillFieldPanel(api, fillSuggestedValues), PanelPosition.LEFT);
}
@Override
public void onExitMode(MapApi api) {
api.setSidePanel(null, PanelPosition.LEFT);
}
}
private static class FillFieldPanel extends JPanel implements ODLListener, Disposable {
final MapApi api;
final private SuggestedFillValuesManager fillSuggestedValues;
// final JTextField text = new JTextField();
final DynamicComboBox<String> text;
final FieldSelectorPanel fieldSelector;
FillFieldPanel(MapApi api, SuggestedFillValuesManager fillSuggestedValues) {
this.api = api;
this.fillSuggestedValues = fillSuggestedValues;
setLayout(new BorderLayout());
add(new JLabel(" Set fill column:"), BorderLayout.NORTH);
fieldSelector = new FieldSelectorPanel(api.getMapDataApi().getGlobalDatastore());
add(fieldSelector, BorderLayout.CENTER);
text = new DynamicComboBox<String>("", true, true) {
@Override
protected List<String> getAvailableItems() {
Pair<ODLTable, Integer> tableCol = getSelectedFillTableColumn();
if (tableCol == null) {
return new ArrayList<>();
}
return fillSuggestedValues.getSuggestions(tableCol.getFirst(), tableCol.getSecond(), PluginUtils.toDrawables(api.getMapDataApi().getUnfilteredActiveTable()));
}
};
// setPrototypeDisplayValue stops the resize of the combobox based on contents size,
// which can cause the dropdown arrow to disappear
text.setPrototypeDisplayValue("XXXXXXXXX");
VerticalLayoutPanel vlp = new VerticalLayoutPanel();
vlp.addLine(new JLabel("Set fill value:"));
vlp.addLine(text);
add(vlp, BorderLayout.SOUTH);
api.getMapDataApi().getGlobalDatastore().addListener(this);
// call data structured changed as this updates the available fields based on the used tables
datastoreStructureChanged();
}
String getFillValue() {
return text.getValue();
}
String[] getField() {
return fieldSelector.getSelected();
}
@Override
public void tableChanged(int tableId, int firstRow, int lastRow) {
// TODO Auto-generated method stub
}
@Override
public void datastoreStructureChanged() {
TIntHashSet selectableTableIds = new TIntHashSet();
ODLTableReadOnly table = api.getMapDataApi().getUnfilteredActiveTable();
if (table != null) {
int n = table.getRowCount();
for (int i = 0; i < n; i++) {
long id = table.getRowId(i);
if (id != -1) {
selectableTableIds.add(TableUtils.getTableId(id));
}
}
}
fieldSelector.update(api.getMapDataApi().getGlobalDatastore(), selectableTableIds);
}
@Override
public ODLListenerType getType() {
return ODLListenerType.DATASTORE_STRUCTURE_CHANGED;
}
private Pair<ODLTable, Integer> getSelectedFillTableColumn() {
String[] tableFieldPair = getField();
ODLTable table = null;
int col = -1;
if (tableFieldPair != null && tableFieldPair.length == 2 && tableFieldPair[0] != null && tableFieldPair[1] != null) {
table = TableUtils.findTable(api.getMapDataApi().getGlobalDatastore(), tableFieldPair[0], true);
if (table != null) {
col = TableUtils.findColumnIndx(table, tableFieldPair[1], true);
}
}
if (col != -1) {
return new Pair<ODLTable, Integer>(table, col);
}
return null;
}
@Override
public void dispose() {
api.getMapDataApi().getGlobalDatastore().removeListener(this);
}
}
}