// License: GPL. For details, see LICENSE file.
package relcontext.actions;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.BorderLayout;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.swing.AbstractListModel;
import javax.swing.DefaultListSelectionModel;
import javax.swing.JDialog;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.actions.JosmAction;
import org.openstreetmap.josm.data.osm.Relation;
import org.openstreetmap.josm.gui.DefaultNameFormatter;
import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
import org.openstreetmap.josm.tools.Shortcut;
import relcontext.ChosenRelation;
/**
* Opens a list of all relations with keyword search field. Choose selected relation.
*
* @author Zverik
*/
public class FindRelationAction extends JosmAction {
protected ChosenRelation chRel;
public FindRelationAction(ChosenRelation chRel) {
super(tr("Find"), "relcontext/find", tr("Find a relation"),
Shortcut.registerShortcut("reltoolbox:find", tr("Relation Toolbox: {0}", tr("Find a relation")),
KeyEvent.VK_F, Shortcut.ALT_CTRL), false);
this.chRel = chRel;
}
@Override
public void actionPerformed(ActionEvent e) {
JPanel panel = new JPanel(new BorderLayout());
final JTextField searchField = new JTextField();
panel.add(searchField, BorderLayout.NORTH);
final FindRelationListModel relationsData = new FindRelationListModel();
final JList<Relation> relationsList = new JList<>(relationsData);
relationsList.setSelectionModel(relationsData.getSelectionModel());
relationsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
relationsList.setCellRenderer(new OsmPrimitivRenderer());
panel.add(new JScrollPane(relationsList,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER), BorderLayout.CENTER);
panel.setPreferredSize(new Dimension(400, 400));
final JOptionPane optionPane = new JOptionPane(panel, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION) {
@Override
public void selectInitialValue() {
searchField.requestFocusInWindow();
}
};
final JDialog dlg = optionPane.createDialog(Main.parent, tr("Find a relation"));
dlg.setModalityType(ModalityType.DOCUMENT_MODAL);
relationsList.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() >= 2 && !relationsList.isSelectionEmpty()) {
dlg.setVisible(false);
optionPane.setValue(JOptionPane.OK_OPTION);
}
}
});
searchField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (!relationsList.isSelectionEmpty()) {
dlg.setVisible(false);
optionPane.setValue(JOptionPane.OK_OPTION);
}
}
});
searchField.addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
updateRelationData(relationsData, searchField.getText());
}
});
}
@Override
public void keyPressed(KeyEvent e) {
if (shouldForward(e)) {
relationsList.dispatchEvent(e);
}
}
@Override
public void keyReleased(KeyEvent e) {
if (shouldForward(e)) {
relationsList.dispatchEvent(e);
}
}
private boolean shouldForward(KeyEvent e) {
return e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_DOWN
|| e.getKeyCode() == KeyEvent.VK_PAGE_DOWN || e.getKeyCode() == KeyEvent.VK_PAGE_UP
|| e.getKeyCode() == KeyEvent.VK_HOME || e.getKeyCode() == KeyEvent.VK_END;
}
});
updateRelationData(relationsData, null);
dlg.setVisible(true);
Object answer = optionPane.getValue();
if (answer == null || answer == JOptionPane.UNINITIALIZED_VALUE
|| (answer instanceof Integer && (Integer) answer != JOptionPane.OK_OPTION))
return;
Relation r = relationsList.getSelectedValue();
if (r != null) {
chRel.set(r);
}
}
@Override
protected void updateEnabledState() {
setEnabled(getLayerManager().getEditDataSet() != null);
}
protected void updateRelationData(FindRelationListModel data, String filter) {
String[] keywords = filter == null ? new String[0] : filter.split("\\s+");
if (keywords.length > 0) {
List<String> filteredKeywords = new ArrayList<>(keywords.length);
for (String s : keywords) {
if (s.length() > 0) {
filteredKeywords.add(s.trim().toLowerCase());
}
}
keywords = filteredKeywords.toArray(new String[0]);
}
if (Main.isDebugEnabled()) {
Main.debug("keywords.length = " + keywords.length);
for (int i = 0; i < keywords.length; i++) {
Main.debug("keyword["+i+"] = " + keywords[i]);
}
}
List<Relation> relations = new ArrayList<>();
if (getLayerManager().getEditLayer() != null) {
for (Relation r : getLayerManager().getEditLayer().data.getRelations()) {
if (!r.isDeleted() && r.isVisible() && !r.isIncomplete()) {
boolean add = true;
for (int i = 0; i < keywords.length && add; i++) {
boolean ok = false;
if (String.valueOf(r.getPrimitiveId().getUniqueId()).contains(keywords[i])) {
ok = true;
} else {
for (String key : r.keySet()) {
if (key.contains(keywords[i]) || r.get(key).toLowerCase().contains(keywords[i])
|| tr(r.get(key)).toLowerCase().contains(keywords[i])) {
ok = true;
break;
}
}
}
if (!ok) {
add = false;
}
}
if (add) {
relations.add(r);
}
}
}
}
Collections.sort(relations, DefaultNameFormatter.getInstance().getRelationComparator());
data.setRelations(relations);
}
/**
* I admit, some of it was copypasted from {@link org.openstreetmap.josm.gui.dialogs.RelationListDialog.RelationListModel}.
*/
protected static class FindRelationListModel extends AbstractListModel<Relation> {
private final ArrayList<Relation> relations = new ArrayList<>();
private DefaultListSelectionModel selectionModel;
public FindRelationListModel(DefaultListSelectionModel selectionModel) {
super();
this.selectionModel = selectionModel;
}
public FindRelationListModel() {
this(new DefaultListSelectionModel());
}
public DefaultListSelectionModel getSelectionModel() {
return selectionModel;
}
@Override
public int getSize() {
return relations.size();
}
@Override
public Relation getElementAt(int index) {
return relations.get(index);
}
public void setRelations(Collection<Relation> relations) {
int selectedIndex = selectionModel.getMinSelectionIndex();
Relation sel = selectedIndex < 0 ? null : getElementAt(selectedIndex);
this.relations.clear();
selectionModel.clearSelection();
if (relations != null) {
this.relations.addAll(relations);
}
fireIntervalAdded(this, 0, getSize());
if (sel != null) {
selectedIndex = this.relations.indexOf(sel);
if (selectedIndex >= 0) {
selectionModel.addSelectionInterval(selectedIndex, selectedIndex);
}
}
if (selectionModel.isSelectionEmpty() && !this.relations.isEmpty()) {
selectionModel.addSelectionInterval(0, 0);
}
}
}
}