Changeset 5799 in josm for trunk


Ignore:
Timestamp:
2013-03-23T19:56:44+01:00 (7 years ago)
Author:
akks
Message:

Membership tabled in properties toggle dialog supports multiselect (and multiple membership deletion)
Property toggle dialog refactoring - methods splitting and reordering
see #7846: more RelationListDialog refactoring, all other relation-related actions separated from dialogs, @Override, JavaDocs

Location:
trunk/src/org/openstreetmap/josm
Files:
3 added
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/relation/AbstractRelationAction.java

    r5794 r5799  
    1616public abstract class AbstractRelationAction extends AbstractAction {
    1717    protected Collection<Relation> relations = Collections.<Relation>emptySet();
    18    
     18
    1919    /**
    2020     * Specifies the working set of relations.
  • trunk/src/org/openstreetmap/josm/actions/relation/DownloadMembersAction.java

    r5794 r5799  
    1313/**
    1414 * The action for downloading members of relations
    15  * @since 5793
     15
    1616 */
    1717public class DownloadMembersAction extends AbstractRelationAction {
     
    2727    }
    2828   
     29    @Override
    2930    public void actionPerformed(ActionEvent e) {
    3031        if (!isEnabled() || relations.isEmpty() || Main.map==null || Main.map.mapView==null) return;
  • trunk/src/org/openstreetmap/josm/actions/relation/DownloadSelectedIncompleteMembersAction.java

    r5794 r5799  
    4343    }
    4444
     45    @Override
    4546    public void actionPerformed(ActionEvent e) {
    4647        if (!isEnabled() || relations.isEmpty() || Main.map==null || Main.map.mapView==null) return;
  • trunk/src/org/openstreetmap/josm/actions/relation/EditRelationAction.java

    r5794 r5799  
    6363    }
    6464
     65    @Override
    6566    public void actionPerformed(ActionEvent e) {
    6667        if (!isEnabled() || relations.size()!=1) return;
  • trunk/src/org/openstreetmap/josm/actions/relation/SelectInRelationListAction.java

    r5794 r5799  
    2424    }
    2525
     26    @Override
    2627    public void actionPerformed(ActionEvent e) {
    2728        if (!isEnabled() || relations.isEmpty() || Main.map==null || Main.map.relationListDialog==null) return;
    2829        Main.map.relationListDialog.selectRelations(relations);
     30        Main.map.relationListDialog.unfurlDialog();
    2931    }
    3032}
  • trunk/src/org/openstreetmap/josm/actions/relation/SelectRelationAction.java

    r5794 r5799  
    2929    }
    3030
     31    @Override
    3132    public void actionPerformed(ActionEvent e) {
    3233        if (!isEnabled() || relations.isEmpty() || Main.map==null || Main.map.mapView==null) return;
  • trunk/src/org/openstreetmap/josm/gui/SideButton.java

    r5762 r5799  
    2020import javax.swing.plaf.basic.BasicArrowButton;
    2121
    22 import org.openstreetmap.josm.Main;
    2322import org.openstreetmap.josm.tools.Destroyable;
    2423import org.openstreetmap.josm.tools.ImageProvider;
    25 import org.openstreetmap.josm.tools.Shortcut;
    2624
     25/**
     26 * Button that is usually used in toggle dialogs
     27 */
    2728public class SideButton extends JButton implements Destroyable {
    2829    private final static int iconHeight = 20;
  • trunk/src/org/openstreetmap/josm/gui/dialogs/RelationListDialog.java

    r5793 r5799  
    22package org.openstreetmap.josm.gui.dialogs;
    33
    4 import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
    54import static org.openstreetmap.josm.tools.I18n.tr;
    6 import static org.openstreetmap.josm.tools.I18n.trn;
    75
    86import java.awt.BorderLayout;
     
    1917import java.util.HashSet;
    2018import java.util.Iterator;
    21 import java.util.LinkedList;
    2219import java.util.List;
    2320import java.util.Set;
     
    4542
    4643import org.openstreetmap.josm.Main;
     44import org.openstreetmap.josm.actions.relation.AddSelectionToRelations;
     45import org.openstreetmap.josm.actions.relation.DeleteRelationsAction;
    4746import org.openstreetmap.josm.actions.relation.DownloadMembersAction;
    4847import org.openstreetmap.josm.actions.relation.DownloadSelectedIncompleteMembersAction;
     48import org.openstreetmap.josm.actions.relation.DuplicateRelationAction;
    4949import org.openstreetmap.josm.actions.relation.EditRelationAction;
    5050import org.openstreetmap.josm.actions.relation.SelectMembersAction;
    5151import org.openstreetmap.josm.actions.relation.SelectRelationAction;
    5252import org.openstreetmap.josm.actions.search.SearchCompiler;
    53 import org.openstreetmap.josm.command.Command;
    54 import org.openstreetmap.josm.command.SequenceCommand;
    55 import org.openstreetmap.josm.data.SelectionChangedListener;
    5653import org.openstreetmap.josm.data.osm.DataSet;
    5754import org.openstreetmap.josm.data.osm.OsmPrimitive;
     
    7370import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
    7471import org.openstreetmap.josm.gui.SideButton;
    75 import org.openstreetmap.josm.gui.dialogs.relation.GenericRelationEditor;
    7672import org.openstreetmap.josm.gui.dialogs.relation.RelationEditor;
    7773import org.openstreetmap.josm.gui.layer.Layer;
    7874import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    79 import org.openstreetmap.josm.gui.util.GuiHelper;
    8075import org.openstreetmap.josm.gui.widgets.DisableShortcutsOnFocusGainedTextField;
    8176import org.openstreetmap.josm.tools.ImageProvider;
     
    9893    private final RelationListModel model;
    9994
    100     /** the delete action */
    101     private final DeleteAction deleteAction;
    10295    private final NewAction newAction;
    103     private final AddToRelation addToRelation;
    104     private final DuplicateAction duplicateAction;
    10596   
    10697    /** the popup menu */
     
    111102    // Actions
    112103    /** the edit action */
    113     private final EditRelationAction editAction;
     104    private final EditRelationAction editAction = new EditRelationAction();
     105    /** the delete action */
     106    private final DeleteRelationsAction deleteRelationsAction = new DeleteRelationsAction();
     107    /** the duplicate action */
     108    private final DuplicateRelationAction duplicateAction = new DuplicateRelationAction();
    114109    private final DownloadMembersAction downloadMembersAction = new DownloadMembersAction();
    115110    private final DownloadSelectedIncompleteMembersAction downloadSelectedIncompleteMembersAction = new DownloadSelectedIncompleteMembersAction();
     
    118113    private final SelectRelationAction selectRelationAction = new SelectRelationAction(false);
    119114    private final SelectRelationAction addRelationToSelectionAction = new SelectRelationAction(true);
    120 
     115    /** add all selected primitives to the given realtions */
     116    private final AddSelectionToRelations addSelectionToRelations = new AddSelectionToRelations();
     117   
    121118    /**
    122      * constructor
     119     * Constructs <code>RelationListDialog</code>
    123120     */
    124121    public RelationListDialog() {
     
    149146        newAction = new NewAction();
    150147
    151         // the edit action
    152         //
    153         editAction = new EditRelationAction();
    154        
    155         // the duplicate action
    156         //
    157         duplicateAction = new DuplicateAction();
    158        
    159         // the delete action
    160         //
    161         deleteAction = new DeleteAction();
    162 
    163         // Add to realaion action
    164         //
    165         addToRelation = new AddToRelation();
    166 
    167148        filter = setupFilter();
    168149
     
    170151            @Override
    171152            public void valueChanged(ListSelectionEvent e) {
    172                 duplicateAction.valueChanged(e);
    173                 deleteAction.valueChanged(e);
    174                 addToRelation.valueChanged(e);
    175 
    176                 List<Relation> rels;
    177                 rels = model.getSelectedNonNewRelations();
    178                 downloadMembersAction.setRelations(rels);
    179 
    180                 rels = model.getSelectedRelationsWithIncompleteMembers();
    181                 downloadSelectedIncompleteMembersAction.setRelations(rels);
    182 
    183                 rels = model.getSelectedRelations();
    184                 selectMemebersAction.setRelations(rels);
    185                 addMembersToSelectionAction.setRelations(rels);
    186                 selectRelationAction.setRelations(rels);
    187                 addRelationToSelectionAction.setRelations(rels);
     153                updateActionsRelationLists();
    188154            }
    189155        });
     
    196162                new SideButton(editAction, false),
    197163                new SideButton(duplicateAction, false),
    198                 new SideButton(deleteAction, false),
     164                new SideButton(deleteRelationsAction, false),
    199165                new SideButton(selectRelationAction, false)
    200166        }));
     
    214180        displaylist.getActionMap().put("edit", editAction);
    215181        displaylist.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.CTRL_MASK), "edit");
     182       
     183        updateActionsRelationLists();
     184    }
     185   
     186    // inform all actions about list of relations they need
     187    private void updateActionsRelationLists() {
     188        List<Relation> rels;
     189        rels = model.getSelectedNonNewRelations();
     190        downloadMembersAction.setRelations(rels);
     191
     192        rels = model.getSelectedRelationsWithIncompleteMembers();
     193        downloadSelectedIncompleteMembersAction.setRelations(rels);
     194
     195        rels = model.getSelectedRelations();
     196        editAction.setRelations(rels);
     197        deleteRelationsAction.setRelations(rels);
     198        addSelectionToRelations.setRelations(rels);
     199        selectMemebersAction.setRelations(rels);
     200        addMembersToSelectionAction.setRelations(rels);
     201        selectRelationAction.setRelations(rels);
     202        addRelationToSelectionAction.setRelations(rels);
     203        duplicateAction.setRelations(rels);
    216204    }
    217205   
     
    220208        newAction.updateEnabledState();
    221209        DatasetEventManager.getInstance().addDatasetListener(this, FireMode.IN_EDT);
    222         DataSet.addSelectionListener(addToRelation);
     210        DataSet.addSelectionListener(addSelectionToRelations);
    223211        dataChanged(null);
    224212    }
     
    227215        MapView.removeLayerChangeListener(newAction);
    228216        DatasetEventManager.getInstance().removeDatasetListener(this);
    229         DataSet.removeSelectionListener(addToRelation);
     217        DataSet.removeSelectionListener(addSelectionToRelations);
    230218    }
    231219   
     
    250238        model.setRelations(l.data.getRelations());
    251239        model.updateTitle();
     240        updateActionsRelationLists();
    252241    }
    253242
     
    367356   
    368357    /**
    369      * The delete action
    370      *
    371      */
    372     class DeleteAction extends AbstractAction implements ListSelectionListener {
    373         class AbortException extends Exception {}
    374 
    375         public DeleteAction() {
    376             putValue(SHORT_DESCRIPTION,tr("Delete the selected relation"));
    377             putValue(NAME, tr("Delete"));
    378             putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete"));
    379             setEnabled(false);
    380         }
    381 
    382         protected void deleteRelation(Relation toDelete) {
    383             if (toDelete == null)
    384                 return;
    385             org.openstreetmap.josm.actions.mapmode.DeleteAction.deleteRelation(
    386                     Main.main.getEditLayer(),
    387                     toDelete
    388                     );
    389         }
    390 
    391         public void actionPerformed(ActionEvent e) {
    392             if (!isEnabled())
    393                 return;
    394             List<Relation> toDelete = new LinkedList<Relation>();
    395             for (int i : displaylist.getSelectedIndices()) {
    396                 toDelete.add(model.getVisibleRelation(i));
    397             }
    398             for (Relation r : toDelete) {
    399                 deleteRelation(r);
    400             }
    401             displaylist.clearSelection();
    402         }
    403 
    404         public void valueChanged(ListSelectionEvent e) {
    405             setEnabled(displaylist.getSelectedIndices() != null && displaylist.getSelectedIndices().length > 0);
    406         }
    407     }
    408 
    409     /**
    410358     * The action for creating a new relation
    411359     *
     
    423371        }
    424372
     373        @Override
    425374        public void actionPerformed(ActionEvent e) {
    426375            run();
     
    431380        }
    432381
     382        @Override
    433383        public void activeLayerChange(Layer oldLayer, Layer newLayer) {
    434384            updateEnabledState();
    435         }
    436 
     385    }
     386
     387        @Override
    437388        public void layerAdded(Layer newLayer) {
    438389            updateEnabledState();
    439390        }
    440391
     392        @Override
    441393        public void layerRemoved(Layer oldLayer) {
    442394            updateEnabledState();
    443         }
    444     }
    445 
    446     /**
    447      * Creates a new relation with a copy of the current editor state
    448      *
    449      */
    450     class DuplicateAction extends AbstractAction implements ListSelectionListener {
    451         public DuplicateAction() {
    452             putValue(SHORT_DESCRIPTION, tr("Create a copy of this relation and open it in another editor window"));
    453             putValue(SMALL_ICON, ImageProvider.get("duplicate"));
    454             putValue(NAME, tr("Duplicate"));
    455             updateEnabledState();
    456         }
    457 
    458         public void launchEditorForDuplicate(Relation original) {
    459             Relation copy = new Relation(original, true);
    460             copy.setModified(true);
    461             RelationEditor editor = RelationEditor.getEditor(
    462                     Main.main.getEditLayer(),
    463                     copy,
    464                     null /* no selected members */
    465                     );
    466             editor.setVisible(true);
    467         }
    468 
    469         public void actionPerformed(ActionEvent e) {
    470             if (!isEnabled())
    471                 return;
    472             launchEditorForDuplicate(getSelected());
    473         }
    474 
    475         protected void updateEnabledState() {
    476             setEnabled(displaylist.getSelectedIndices() != null && displaylist.getSelectedIndices().length == 1);
    477         }
    478 
    479         public void valueChanged(ListSelectionEvent e) {
    480             updateEnabledState();
    481         }
    482     }
    483 
    484     class AddToRelation extends AbstractAction implements ListSelectionListener, SelectionChangedListener {
    485 
    486         public AddToRelation() {
    487             super("", ImageProvider.get("dialogs/conflict", "copyendright"));
    488             putValue(SHORT_DESCRIPTION, tr("Add all objects selected in the current dataset after the last member"));
    489             setEnabled(false);
    490         }
    491 
    492         @Override
    493         public void actionPerformed(ActionEvent e) {
    494             Collection<Command> cmds = new LinkedList<Command>();
    495             for (Relation orig : getSelectedRelations()) {
    496                 Command c = GenericRelationEditor.addPrimitivesToRelation(orig, Main.main.getCurrentDataSet().getSelected());
    497                 if (c != null) {
    498                     cmds.add(c);
    499                 }
    500             }
    501             if (!cmds.isEmpty()) {
    502                 Main.main.undoRedo.add(new SequenceCommand(tr("Add selection to relation"), cmds));
    503             }
    504         }
    505 
    506         @Override
    507         public void valueChanged(ListSelectionEvent e) {
    508             putValue(NAME, trn("Add selection to {0} relation", "Add selection to {0} relations",
    509                     getSelectedRelations().size(), getSelectedRelations().size()));
    510         }
    511 
    512         @Override
    513         public void selectionChanged(final Collection<? extends OsmPrimitive> newSelection) {
    514             GuiHelper.runInEDT(new Runnable() {
    515                 @Override
    516                 public void run() {
    517                     setEnabled(newSelection != null && !newSelection.isEmpty());
    518                 }
    519             });
    520395        }
    521396    }
     
    802677            addSeparator();
    803678
    804             add(addToRelation);
    805         }
    806     }
     679            add(addSelectionToRelations);
     680        }
     681    }
     682
     683    /* ---------------------------------------------------------------------------------- */
     684    /* Methods that can be called from plugins                                                                    */
     685    /* ---------------------------------------------------------------------------------- */
    807686
    808687    public void addPopupMenuSeparator() {
     
    830709    /* ---------------------------------------------------------------------------------- */
    831710
     711    @Override
    832712    public void nodeMoved(NodeMovedEvent event) {/* irrelevant in this context */}
    833713
     714    @Override
    834715    public void wayNodesChanged(WayNodesChangedEvent event) {/* irrelevant in this context */}
    835716
     717    @Override
    836718    public void primitivesAdded(final PrimitivesAddedEvent event) {
    837719        model.addRelations(event.getPrimitives());
     
    839721    }
    840722
     723    @Override
    841724    public void primitivesRemoved(final PrimitivesRemovedEvent event) {
    842725        model.removeRelations(event.getPrimitives());
     
    844727    }
    845728
     729    @Override
    846730    public void relationMembersChanged(final RelationMembersChangedEvent event) {
    847731        List<Relation> sel = model.getSelectedRelations();
     
    851735    }
    852736
     737    @Override
    853738    public void tagsChanged(TagsChangedEvent event) {
    854739        OsmPrimitive prim = event.getPrimitive();
     
    864749    }
    865750
     751    @Override
    866752    public void dataChanged(DataChangedEvent event) {
    867753        initFromLayer(Main.main.getEditLayer());
    868754    }
    869755
     756    @Override
    870757    public void otherDatasetChange(AbstractDatasetChangedEvent event) {/* ignore */}
    871758}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/SelectionListDialog.java

    r5793 r5799  
    110110        lstPrimitives.getSelectionModel().addListSelectionListener(actSelect);
    111111        selectButton.createArrow(new ActionListener() {
     112            @Override
    112113            public void actionPerformed(ActionEvent e) {
    113114                SelectionHistoryPopup.launch(selectButton, model.getSelectionHistory());
     
    118119        final SideButton searchButton = new SideButton(actSearch = new SearchAction());
    119120        searchButton.createArrow(new ActionListener() {
     121            @Override
    120122            public void actionPerformed(ActionEvent e) {
    121123                SearchPopupMenu.launch(searchButton);
     
    318320        }
    319321
     322        @Override
    320323        public void actionPerformed(ActionEvent e) {
    321324            Collection<OsmPrimitive> sel = model.getSelected();
     
    329332        }
    330333
     334        @Override
    331335        public void valueChanged(ListSelectionEvent e) {
    332336            updateEnabledState();
     
    487491        }
    488492
     493        @Override
    489494        public Object getElementAt(int index) {
    490495            return selection.get(index);
    491496        }
    492497
     498        @Override
    493499        public int getSize() {
    494500            return selection.size();
  • trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java

    r5793 r5799  
    2323import java.util.EnumSet;
    2424import java.util.HashMap;
    25 import java.util.HashSet;
    2625import java.util.LinkedList;
    2726import java.util.List;
     
    8180import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
    8281import org.openstreetmap.josm.gui.dialogs.properties.PresetListPanel.PresetHandler;
    83 import org.openstreetmap.josm.gui.dialogs.relation.DownloadRelationMemberTask;
    8482import org.openstreetmap.josm.gui.dialogs.relation.RelationEditor;
    8583import org.openstreetmap.josm.gui.layer.OsmDataLayer;
     
    113111 */
    114112public class PropertiesDialog extends ToggleDialog implements SelectionChangedListener, MapView.EditLayerChangeListener, DataSetListenerAdapter.Listener {
    115     /**
    116      * Watches for mouse clicks
    117      * @author imi
    118      */
    119     public class MouseClickWatch extends MouseAdapter {
    120         @Override public void mouseClicked(MouseEvent e) {
    121             if (e.getClickCount() < 2)
    122             {
    123                 // single click, clear selection in other table not clicked in
    124                 if (e.getSource() == propertyTable) {
    125                     membershipTable.clearSelection();
    126                 } else if (e.getSource() == membershipTable) {
    127                     propertyTable.clearSelection();
    128                 }
    129             }
    130             // double click, edit or add property
    131             else if (e.getSource() == propertyTable)
    132             {
    133                 int row = propertyTable.rowAtPoint(e.getPoint());
    134                 if (row > -1) {
    135                     boolean focusOnKey = (propertyTable.columnAtPoint(e.getPoint()) == 0);
    136                     editHelper.editProperty(row, focusOnKey);
    137                 } else {
    138                     editHelper.addProperty();
    139                     btnAdd.requestFocusInWindow();
    140                 }
    141             } else if (e.getSource() == membershipTable) {
    142                 int row = membershipTable.rowAtPoint(e.getPoint());
    143                 if (row > -1) {
    144                     editMembership(row);
    145                 }
    146             }
    147             else
    148             {
    149                 editHelper.addProperty();
    150                 btnAdd.requestFocusInWindow();
    151             }
    152         }
    153         @Override public void mousePressed(MouseEvent e) {
    154             if (e.getSource() == propertyTable) {
    155                 membershipTable.clearSelection();
    156             } else if (e.getSource() == membershipTable) {
    157                 propertyTable.clearSelection();
    158             }
    159         }
    160 
    161     }
    162 
    163113    // hook for roadsigns plugin to display a small
    164114    // button in the upper right corner of this dialog
    165115    public static final JPanel pluginHook = new JPanel();
    166116
     117    /**
     118     * The property data of selected objects.
     119     */
     120    private final DefaultTableModel propertyData = new ReadOnlyTableModel();
     121
     122    /**
     123     * The membership data of selected objects.
     124     */
     125    private final DefaultTableModel membershipData = new ReadOnlyTableModel();
     126
     127    /**
     128     * The properties table.
     129     */
     130    private final JTable propertyTable = new JTable(propertyData);
     131    /**
     132     * The membership table.
     133     */
     134    private final JTable membershipTable = new JTable(membershipData);
     135
    167136    private JPopupMenu propertyMenu;
    168137    private JPopupMenu membershipMenu;
    169138
    170139    private final Map<String, Map<String, Integer>> valueCount = new TreeMap<String, Map<String, Integer>>();
    171 
     140    /**
     141     * This sub-object is responsible for all adding and editing of properties
     142     */
     143    private final TagEditHelper editHelper = new TagEditHelper(propertyData, valueCount);
    172144   
    173145    private final DataSetListenerAdapter dataChangedAdapter = new DataSetListenerAdapter(this);
     
    190162    private final SelectRelationAction selectRelationAction = new SelectRelationAction(false);
    191163   
    192     @Override
    193     public void showNotify() {
    194         DatasetEventManager.getInstance().addDatasetListener(dataChangedAdapter, FireMode.IN_EDT_CONSOLIDATED);
    195         SelectionEventManager.getInstance().addSelectionListener(this, FireMode.IN_EDT_CONSOLIDATED);
    196         MapView.addEditLayerChangeListener(this);
    197         for (JosmAction action : josmActions) {
    198             Main.registerActionShortcut(action);
    199         }
    200         updateSelection();
    201     }
    202 
    203     @Override
    204     public void hideNotify() {
    205         DatasetEventManager.getInstance().removeDatasetListener(dataChangedAdapter);
    206         SelectionEventManager.getInstance().removeSelectionListener(this);
    207         MapView.removeEditLayerChangeListener(this);
    208         for (JosmAction action : josmActions) {
    209             Main.unregisterActionShortcut(action);
    210         }
    211     }
    212 
    213     /**
    214      * This simply fires up an {@link RelationEditor} for the relation shown; everything else
    215      * is the editor's business.
    216      *
    217      * @param row
    218      */
    219     private void editMembership(int row) {
    220         Relation relation = (Relation)membershipData.getValueAt(row, 0);
    221         Main.map.relationListDialog.selectRelation(relation);
    222         RelationEditor.getEditor(
    223                 Main.map.mapView.getEditLayer(),
    224                 relation,
    225                 ((MemberInfo) membershipData.getValueAt(row, 1)).role).setVisible(true);
    226     }
    227 
    228     /**
    229      * The property data of selected objects.
    230      */
    231     private final DefaultTableModel propertyData = new DefaultTableModel() {
    232         @Override public boolean isCellEditable(int row, int column) {
    233             return false;
    234         }
    235         @Override public Class<?> getColumnClass(int columnIndex) {
    236             return String.class;
    237         }
    238     };
    239 
    240     /**
    241      * The membership data of selected objects.
    242      */
    243     private final DefaultTableModel membershipData = new DefaultTableModel() {
    244         @Override public boolean isCellEditable(int row, int column) {
    245             return false;
    246         }
    247         @Override public Class<?> getColumnClass(int columnIndex) {
    248             return String.class;
    249         }
    250     };
    251 
    252     /**
    253      * The properties table.
    254      */
    255     private final JTable propertyTable = new JTable(propertyData);
    256     /**
    257      * The membership table.
    258      */
    259     private final JTable membershipTable = new JTable(membershipData);
    260 
    261     /**
    262      * This sub-object is responsible for all adding and editing of properties
    263      */
    264     private final TagEditHelper editHelper = new TagEditHelper(propertyData, valueCount);
    265    
    266164    /**
    267165     * The Add button (needed to be able to disable it)
    268166     */
    269     private final SideButton btnAdd;
     167    private final SideButton btnAdd = new SideButton(addAction);
    270168    /**
    271169     * The Edit button (needed to be able to disable it)
    272170     */
    273     private final SideButton btnEdit;
     171    private final SideButton btnEdit = new SideButton(editAction);
    274172    /**
    275173     * The Delete button (needed to be able to disable it)
    276174     */
    277     private final SideButton btnDel;
     175    private final SideButton btnDel = new SideButton(deleteAction);
    278176    /**
    279177     * Matching preset display class
    280178     */
    281179    private final PresetListPanel presets = new PresetListPanel();
    282 
     180   
    283181    /**
    284182     * Text to display when nothing selected.
     
    287185            + tr("Select objects for which to change properties.") + "</p></html>");
    288186
    289     static class MemberInfo {
    290         List<RelationMember> role = new ArrayList<RelationMember>();
    291         List<Integer> position = new ArrayList<Integer>();
    292         private String positionString = null;
    293         void add(RelationMember r, Integer p) {
    294             role.add(r);
    295             position.add(p);
    296         }
    297         String getPositionString() {
    298             if (positionString == null) {
    299                 Collections.sort(position);
    300                 positionString = String.valueOf(position.get(0));
    301                 int cnt = 0;
    302                 int last = position.get(0);
    303                 for (int i = 1; i < position.size(); ++i) {
    304                     int cur = position.get(i);
    305                     if (cur == last + 1) {
    306                         ++cnt;
    307                     } else if (cnt == 0) {
    308                         positionString += "," + String.valueOf(cur);
    309                     } else {
    310                         positionString += "-" + String.valueOf(last);
    311                         positionString += "," + String.valueOf(cur);
    312                         cnt = 0;
    313                     }
    314                     last = cur;
    315                 }
    316                 if (cnt >= 1) {
    317                     positionString += "-" + String.valueOf(last);
    318                 }
    319             }
    320             if (positionString.length() > 20) {
    321                 positionString = positionString.substring(0, 17) + "...";
    322             }
    323             return positionString;
    324         }
    325     }
    326 
     187    private PresetHandler presetHandler = new PresetHandler() {
     188        @Override public void updateTags(List<Tag> tags) {
     189            Command command = TaggingPreset.createCommand(getSelection(), tags);
     190            if (command != null) Main.main.undoRedo.add(command);
     191        }
     192
     193        @Override public Collection<OsmPrimitive> getSelection() {
     194            if (Main.main == null) return null;
     195            if (Main.main.getCurrentDataSet() == null) return null;
     196            return Main.main.getCurrentDataSet().getSelected();
     197        }
     198    };
     199   
     200    // <editor-fold defaultstate="collapsed" desc="Dialog construction and helper methods">
     201   
    327202    /**
    328203     * Create a new PropertiesDialog
     
    333208                        Shortcut.ALT_SHIFT), 150, true);
    334209
     210        setupPropertiesMenu();
     211        buildPropertiesTable();
     212
     213        setupMembershipMenu();
     214        buildMembershipTable();
     215       
     216        // combine both tables and wrap them in a scrollPane
     217        JPanel bothTables = new JPanel();
     218        boolean top = Main.pref.getBoolean("properties.presets.top", true);
     219        bothTables.setLayout(new GridBagLayout());
     220        if(top) {
     221            bothTables.add(presets, GBC.std().fill(GBC.HORIZONTAL).insets(5, 2, 5, 2).anchor(GBC.NORTHWEST));
     222            double epsilon = Double.MIN_VALUE; // need to set a weight or else anchor value is ignored
     223            bothTables.add(pluginHook, GBC.eol().insets(0,1,1,1).anchor(GBC.NORTHEAST).weight(epsilon, epsilon));
     224        }
     225        bothTables.add(selectSth, GBC.eol().fill().insets(10, 10, 10, 10));
     226        bothTables.add(propertyTable.getTableHeader(), GBC.eol().fill(GBC.HORIZONTAL));
     227        bothTables.add(propertyTable, GBC.eol().fill(GBC.BOTH));
     228        bothTables.add(membershipTable.getTableHeader(), GBC.eol().fill(GBC.HORIZONTAL));
     229        bothTables.add(membershipTable, GBC.eol().fill(GBC.BOTH));
     230        if(!top) {
     231            bothTables.add(presets, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 2, 5, 2));
     232        }
     233       
     234        setupKeyboardShortcuts();
     235
     236        // Let the action know when selection in the tables change
     237        propertyTable.getSelectionModel().addListSelectionListener(editAction);
     238        membershipTable.getSelectionModel().addListSelectionListener(editAction);
     239        propertyTable.getSelectionModel().addListSelectionListener(deleteAction);
     240        membershipTable.getSelectionModel().addListSelectionListener(deleteAction);
     241       
     242
     243        JScrollPane scrollPane = (JScrollPane) createLayout(bothTables, true, Arrays.asList(new SideButton[] {
     244                this.btnAdd, this.btnEdit, this.btnDel
     245        }));
     246
     247        MouseClickWatch mouseClickWatch = new MouseClickWatch();
     248        propertyTable.addMouseListener(mouseClickWatch);
     249        membershipTable.addMouseListener(mouseClickWatch);
     250        scrollPane.addMouseListener(mouseClickWatch);
     251
     252        selectSth.setPreferredSize(scrollPane.getSize());
     253        presets.setSize(scrollPane.getSize());
     254
     255        editHelper.loadTagsIfNeeded();
     256    }
     257       
     258    private void buildPropertiesTable() {
    335259        // setting up the properties table
    336         propertyMenu = new JPopupMenu();
    337         propertyMenu.add(pasteValueAction);
    338         propertyMenu.add(copyValueAction);
    339         propertyMenu.add(copyKeyValueAction);
    340         propertyMenu.add(copyAllKeyValueAction);
    341         propertyMenu.addSeparator();
    342         propertyMenu.add(searchActionAny);
    343         propertyMenu.add(searchActionSame);
    344         propertyMenu.addSeparator();
    345         propertyMenu.add(helpAction);
    346260
    347261        propertyData.setColumnIdentifiers(new String[]{tr("Key"),tr("Value")});
    348262        propertyTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
    349263        propertyTable.getTableHeader().setReorderingAllowed(false);
    350         propertyTable.addMouseListener(new PopupMenuLauncher() {
    351             @Override
    352             public void launch(MouseEvent evt) {
    353                 Point p = evt.getPoint();
    354                 int row = propertyTable.rowAtPoint(p);
    355                 if (row > -1) {
    356                     propertyTable.changeSelection(row, 0, false, false);
    357                     propertyMenu.show(propertyTable, p.x, p.y-3);
    358                 }
    359             }
    360         });
    361 
     264       
    362265        propertyTable.getColumnModel().getColumn(1).setCellRenderer(new DefaultTableCellRenderer(){
    363266            @Override public Component getTableCellRendererComponent(JTable table, Object value,
     
    385288            }
    386289        });
    387 
    388         // setting up the membership table
    389         membershipMenu = new JPopupMenu();
    390         membershipMenu.add(addRelationToSelectionAction);
    391         membershipMenu.add(selectRelationAction);
    392         membershipMenu.add(addMembersToSelectionAction);
    393         membershipMenu.add(downloadSelectedIncompleteMembersAction);
    394         membershipMenu.addSeparator();
    395         membershipMenu.add(helpAction);
    396 
     290    }
     291
     292    private void buildMembershipTable() {
    397293        membershipData.setColumnIdentifiers(new String[]{tr("Member Of"),tr("Role"),tr("Position")});
    398294        membershipTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
    399         membershipTable.addMouseListener(new PopupMenuLauncher() {
    400             @Override
    401             public void launch(MouseEvent evt) {
    402                 Point p = evt.getPoint();
    403                 int row = membershipTable.rowAtPoint(p);
    404                 membershipTable.changeSelection(row, 0, false, false);
    405                 int idx[] = membershipTable.getSelectedRows();
    406                 List<Relation> rels =  new ArrayList<Relation>(10);
    407                 if (idx!=null) {
    408                     for (int i: idx) {
    409                         Relation r = (Relation) (membershipData.getValueAt(i, 0));
    410                         rels.add(r);
    411                     }
    412                 }
    413                 selectRelationAction.setRelations(rels);
    414                 addMembersToSelectionAction.setRelations(rels);
    415                 addMembersToSelectionAction.setRelations(rels);
    416                 downloadSelectedIncompleteMembersAction.setRelations(rels);
    417                 membershipMenu.show(membershipTable, p.x, p.y-3);
    418             }
    419         });
    420295
    421296        TableColumnModel mod = membershipTable.getColumnModel();
     
    488363        mod.getColumn(1).setPreferredWidth(40);
    489364        mod.getColumn(0).setPreferredWidth(200);
    490 
    491         // combine both tables and wrap them in a scrollPane
    492         JPanel bothTables = new JPanel();
    493         boolean top = Main.pref.getBoolean("properties.presets.top", true);
    494         bothTables.setLayout(new GridBagLayout());
    495         if(top) {
    496             bothTables.add(presets, GBC.std().fill(GBC.HORIZONTAL).insets(5, 2, 5, 2).anchor(GBC.NORTHWEST));
    497             double epsilon = Double.MIN_VALUE; // need to set a weight or else anchor value is ignored
    498             bothTables.add(pluginHook, GBC.eol().insets(0,1,1,1).anchor(GBC.NORTHEAST).weight(epsilon, epsilon));
    499         }
    500         bothTables.add(selectSth, GBC.eol().fill().insets(10, 10, 10, 10));
    501         bothTables.add(propertyTable.getTableHeader(), GBC.eol().fill(GBC.HORIZONTAL));
    502         bothTables.add(propertyTable, GBC.eol().fill(GBC.BOTH));
    503         bothTables.add(membershipTable.getTableHeader(), GBC.eol().fill(GBC.HORIZONTAL));
    504         bothTables.add(membershipTable, GBC.eol().fill(GBC.BOTH));
    505         if(!top) {
    506             bothTables.add(presets, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 2, 5, 2));
    507         }
     365    }
     366   
     367    /**
     368     * creates the popup menu @field membershipMenu and its launcher on membership table
     369     */
     370    private void setupMembershipMenu() {
     371        // setting up the membership table
     372        membershipMenu = new JPopupMenu();
     373        membershipMenu.add(addRelationToSelectionAction);
     374        membershipMenu.add(selectRelationAction);
     375        membershipMenu.add(addMembersToSelectionAction);
     376        membershipMenu.add(downloadSelectedIncompleteMembersAction);
     377        membershipMenu.addSeparator();
     378        membershipMenu.add(helpAction);
     379
     380        membershipTable.addMouseListener(new PopupMenuLauncher() {
     381            @Override
     382            public void launch(MouseEvent evt) {
     383                Point p = evt.getPoint();
     384                int row = membershipTable.rowAtPoint(p);
     385                int idx[] = membershipTable.getSelectedRows();
     386                // if nothing or one row is selected, select row under mouse instead
     387                if (idx.length<2 && row>-1) {
     388                    membershipTable.changeSelection(row, 0, false, false);
     389                    idx = new int[]{row};
     390                }
     391                List<Relation> rels =  new ArrayList<Relation>(10);
     392                for (int i: idx) {
     393                    Relation r = (Relation) (membershipData.getValueAt(i, 0));
     394                    rels.add(r);
     395                }
     396                selectRelationAction.setRelations(rels);
     397                addMembersToSelectionAction.setRelations(rels);
     398                addMembersToSelectionAction.setRelations(rels);
     399                downloadSelectedIncompleteMembersAction.setRelations(rels);
     400                membershipMenu.show(membershipTable, p.x, p.y-3);
     401            }
     402        });
     403    }
     404   
     405    /**
     406     * creates the popup menu @field propertyMenu and its launcher on property table
     407     */
     408    private void setupPropertiesMenu() {
     409        propertyMenu = new JPopupMenu();
     410        propertyMenu.add(pasteValueAction);
     411        propertyMenu.add(copyValueAction);
     412        propertyMenu.add(copyKeyValueAction);
     413        propertyMenu.add(copyAllKeyValueAction);
     414        propertyMenu.addSeparator();
     415        propertyMenu.add(searchActionAny);
     416        propertyMenu.add(searchActionSame);
     417        propertyMenu.addSeparator();
     418        propertyMenu.add(helpAction);
     419        propertyTable.addMouseListener(new PopupMenuLauncher() {
     420            @Override
     421            public void launch(MouseEvent evt) {
     422                Point p = evt.getPoint();
     423                int row = propertyTable.rowAtPoint(p);
     424                if (row > -1) {
     425                    propertyTable.changeSelection(row, 0, false, false);
     426                    propertyMenu.show(propertyTable, p.x, p.y-3);
     427                }
     428            }
     429        });
     430    }
     431   
     432    /**
     433     * Assignas all needed keys like Enter and Spacebar to most important actions
     434     */
     435    private void setupKeyboardShortcuts() {
    508436       
    509         // Open edit dialog whe enter pressed in tables
     437        // ENTER = editAction, open "edit" dialog
    510438        propertyTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
    511439                .put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),"onTableEnter");
     
    515443        membershipTable.getActionMap().put("onTableEnter",editAction);
    516444       
    517         // Open add property dialog when INS is pressed in tables
     445        // INSERT button = addAction, open "add property" dialog
    518446        propertyTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
    519447                .put(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, 0),"onTableInsert");
    520448        propertyTable.getActionMap().put("onTableInsert",addAction);
    521449       
    522         //  unassign some standard shortcuts for JTable to allow upload / download
     450        // unassign some standard shortcuts for JTable to allow upload / download
    523451        InputMapUtils.unassignCtrlShiftUpDown(propertyTable, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
     452        // allow using enter to add tags for all look&feel configurations
     453        InputMapUtils.enableEnter(this.btnAdd);
    524454       
    525         // -- add action and shortcut
    526         this.btnAdd = new SideButton(addAction);
    527         InputMapUtils.enableEnter(this.btnAdd);
    528 
    529         // -- edit action
    530         //
    531         propertyTable.getSelectionModel().addListSelectionListener(editAction);
    532         membershipTable.getSelectionModel().addListSelectionListener(editAction);
    533         this.btnEdit = new SideButton(editAction);
    534 
    535         // -- delete action
    536         //
    537         this.btnDel = new SideButton(deleteAction);
    538         membershipTable.getSelectionModel().addListSelectionListener(deleteAction);
    539         propertyTable.getSelectionModel().addListSelectionListener(deleteAction);
     455        // DEL button = deleteAction
    540456        getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
    541457                KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0),"delete"
    542458                );
    543459        getActionMap().put("delete", deleteAction);
    544 
    545         JScrollPane scrollPane = (JScrollPane) createLayout(bothTables, true, Arrays.asList(new SideButton[] {
    546                 this.btnAdd, this.btnEdit, this.btnDel
    547         }));
    548 
    549         MouseClickWatch mouseClickWatch = new MouseClickWatch();
    550         propertyTable.addMouseListener(mouseClickWatch);
    551         membershipTable.addMouseListener(mouseClickWatch);
    552         scrollPane.addMouseListener(mouseClickWatch);
    553 
    554         selectSth.setPreferredSize(scrollPane.getSize());
    555         presets.setSize(scrollPane.getSize());
    556 
    557         editHelper.loadTagsIfNeeded();
    558         // -- help action
    559         //
     460       
     461        // F1 button = custom help action
    560462        getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
    561463                KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0), "onHelp");
    562464        getActionMap().put("onHelp", helpAction);
     465    }
     466   
     467         /**
     468     * This simply fires up an {@link RelationEditor} for the relation shown; everything else
     469     * is the editor's business.
     470     *
     471     * @param row
     472     */
     473    private void editMembership(int row) {
     474        Relation relation = (Relation)membershipData.getValueAt(row, 0);
     475        Main.map.relationListDialog.selectRelation(relation);
     476        RelationEditor.getEditor(
     477                Main.map.mapView.getEditLayer(),
     478                relation,
     479                ((MemberInfo) membershipData.getValueAt(row, 1)).role).setVisible(true);
     480    }
     481   
     482    private int findRow(TableModel model, Object value) {
     483        for (int i=0; i<model.getRowCount(); i++) {
     484            if (model.getValueAt(i, 0).equals(value))
     485                return i;
     486        }
     487        return -1;
     488    }
     489   
     490    /**
     491     * Update selection status, call @{link #selectionChanged} function.
     492     */
     493    private void updateSelection() {
     494        if (Main.main.getCurrentDataSet() == null) {
     495            selectionChanged(Collections.<OsmPrimitive>emptyList());
     496        } else {
     497            selectionChanged(Main.main.getCurrentDataSet().getSelected());
     498        }
     499    }
     500   
     501   // </editor-fold>
     502
     503    // <editor-fold defaultstate="collapsed" desc="Event listeners methods">
     504   
     505    @Override
     506    public void showNotify() {
     507        DatasetEventManager.getInstance().addDatasetListener(dataChangedAdapter, FireMode.IN_EDT_CONSOLIDATED);
     508        SelectionEventManager.getInstance().addSelectionListener(this, FireMode.IN_EDT_CONSOLIDATED);
     509        MapView.addEditLayerChangeListener(this);
     510        for (JosmAction action : josmActions) {
     511            Main.registerActionShortcut(action);
     512        }
     513        updateSelection();
     514    }
     515
     516    @Override
     517    public void hideNotify() {
     518        DatasetEventManager.getInstance().removeDatasetListener(dataChangedAdapter);
     519        SelectionEventManager.getInstance().removeSelectionListener(this);
     520        MapView.removeEditLayerChangeListener(this);
     521        for (JosmAction action : josmActions) {
     522            Main.unregisterActionShortcut(action);
     523        }
    563524    }
    564525
     
    570531        }
    571532    }
    572 
    573     private int findRow(TableModel model, Object value) {
    574         for (int i=0; i<model.getRowCount(); i++) {
    575             if (model.getValueAt(i, 0).equals(value))
    576                 return i;
    577         }
    578         return -1;
    579     }
    580 
    581     private PresetHandler presetHandler = new PresetHandler() {
    582 
    583         @Override
    584         public void updateTags(List<Tag> tags) {
    585             Command command = TaggingPreset.createCommand(getSelection(), tags);
    586             if (command != null) {
    587                 Main.main.undoRedo.add(command);
    588             }
    589         }
    590 
    591         @Override
    592         public Collection<OsmPrimitive> getSelection() {
    593             if (Main.main == null) return null;
    594             if (Main.main.getCurrentDataSet() == null) return null;
    595 
    596             return Main.main.getCurrentDataSet().getSelected();
    597         }
    598     };
     533   
     534    @Override
     535    public void destroy() {
     536        super.destroy();
     537        for (JosmAction action : josmActions) {
     538            action.destroy();
     539        }
     540        Container parent = pluginHook.getParent();
     541        if (parent != null) {
     542            parent.remove(pluginHook);
     543        }
     544    }
    599545
    600546    @Override
     
    725671        }
    726672    }
    727 
    728     /**
    729      * Update selection status, call @{link #selectionChanged} function.
    730      */
    731     private void updateSelection() {
    732         if (Main.main.getCurrentDataSet() == null) {
    733             selectionChanged(Collections.<OsmPrimitive>emptyList());
    734         } else {
    735             selectionChanged(Main.main.getCurrentDataSet().getSelected());
    736         }
    737     }
    738 
     673   
    739674    /* ---------------------------------------------------------------------------------- */
    740675    /* EditLayerChangeListener                                                            */
     
    753688    }
    754689
     690    // </editor-fold>
     691
     692    // <editor-fold defaultstate="collapsed" desc="Methods that are called by plugins to extend fuctionalty ">
     693    public void addPropertyPopupMenuSeparator() {
     694        propertyMenu.addSeparator();
     695    }
     696
     697    public JMenuItem addPropertyPopupMenuAction(Action a) {
     698        return propertyMenu.add(a);
     699    }
     700
     701    public void addPropertyPopupMenuListener(PopupMenuListener l) {
     702        propertyMenu.addPopupMenuListener(l);
     703    }
     704
     705    public void removePropertyPopupMenuListener(PopupMenuListener l) {
     706        propertyMenu.addPopupMenuListener(l);
     707    }
     708
     709    @SuppressWarnings("unchecked")
     710    public Tag getSelectedProperty() {
     711        int row = propertyTable.getSelectedRow();
     712        if (row == -1) return null;
     713        TreeMap<String, Integer> map = (TreeMap<String, Integer>) propertyData.getValueAt(row, 1);
     714        return new Tag(
     715                propertyData.getValueAt(row, 0).toString(),
     716                map.size() > 1 ? "" : map.keySet().iterator().next());
     717    }
     718
     719    public void addMembershipPopupMenuSeparator() {
     720        membershipMenu.addSeparator();
     721    }
     722
     723    public JMenuItem addMembershipPopupMenuAction(Action a) {
     724        return membershipMenu.add(a);
     725    }
     726
     727    public void addMembershipPopupMenuListener(PopupMenuListener l) {
     728        membershipMenu.addPopupMenuListener(l);
     729    }
     730
     731    public void removeMembershipPopupMenuListener(PopupMenuListener l) {
     732        membershipMenu.addPopupMenuListener(l);
     733    }
     734
     735    public IRelation getSelectedMembershipRelation() {
     736        int row = membershipTable.getSelectedRow();
     737        return row > -1 ? (IRelation) membershipData.getValueAt(row, 0) : null;
     738    }
     739       
     740    // </editor-fold>
     741   
     742     /**
     743     * Class that watches for mouse clicks
     744     * @author imi
     745     */
     746    public class MouseClickWatch extends MouseAdapter {
     747        @Override public void mouseClicked(MouseEvent e) {
     748            if (e.getClickCount() < 2)
     749            {
     750                // single click, clear selection in other table not clicked in
     751                if (e.getSource() == propertyTable) {
     752                    membershipTable.clearSelection();
     753                } else if (e.getSource() == membershipTable) {
     754                    propertyTable.clearSelection();
     755                }
     756            }
     757            // double click, edit or add property
     758            else if (e.getSource() == propertyTable)
     759            {
     760                int row = propertyTable.rowAtPoint(e.getPoint());
     761                if (row > -1) {
     762                    boolean focusOnKey = (propertyTable.columnAtPoint(e.getPoint()) == 0);
     763                    editHelper.editProperty(row, focusOnKey);
     764                } else {
     765                    editHelper.addProperty();
     766                    btnAdd.requestFocusInWindow();
     767                }
     768            } else if (e.getSource() == membershipTable) {
     769                int row = membershipTable.rowAtPoint(e.getPoint());
     770                if (row > -1) {
     771                    editMembership(row);
     772                }
     773            }
     774            else
     775            {
     776                editHelper.addProperty();
     777                btnAdd.requestFocusInWindow();
     778            }
     779        }
     780        @Override public void mousePressed(MouseEvent e) {
     781            if (e.getSource() == propertyTable) {
     782                membershipTable.clearSelection();
     783            } else if (e.getSource() == membershipTable) {
     784                propertyTable.clearSelection();
     785            }
     786        }
     787
     788    }
     789
     790    static class MemberInfo {
     791        List<RelationMember> role = new ArrayList<RelationMember>();
     792        List<Integer> position = new ArrayList<Integer>();
     793        private String positionString = null;
     794        void add(RelationMember r, Integer p) {
     795            role.add(r);
     796            position.add(p);
     797        }
     798        String getPositionString() {
     799            if (positionString == null) {
     800                Collections.sort(position);
     801                positionString = String.valueOf(position.get(0));
     802                int cnt = 0;
     803                int last = position.get(0);
     804                for (int i = 1; i < position.size(); ++i) {
     805                    int cur = position.get(i);
     806                    if (cur == last + 1) {
     807                        ++cnt;
     808                    } else if (cnt == 0) {
     809                        positionString += "," + String.valueOf(cur);
     810                    } else {
     811                        positionString += "-" + String.valueOf(last);
     812                        positionString += "," + String.valueOf(cur);
     813                        cnt = 0;
     814                    }
     815                    last = cur;
     816                }
     817                if (cnt >= 1) {
     818                    positionString += "-" + String.valueOf(last);
     819                }
     820            }
     821            if (positionString.length() > 20) {
     822                positionString = positionString.substring(0, 17) + "...";
     823            }
     824            return positionString;
     825        }
     826    }
     827
     828    /**
     829     * Class that allows fast creation of read-only table model with String columns
     830     */
     831    public static class ReadOnlyTableModel extends DefaultTableModel {
     832        @Override public boolean isCellEditable(int row, int column) {
     833            return false;
     834        }
     835        @Override public Class<?> getColumnClass(int columnIndex) {
     836            return String.class;
     837        }
     838    };
     839   
    755840    /**
    756841     * Action handling delete button press in properties dialog.
     
    839924                deleteProperties(rows);
    840925            } else if (membershipTable.getSelectedRowCount() > 0) {
    841                 int row = membershipTable.getSelectedRow();
    842                 deleteFromRelation(row);
     926                int[] rows = membershipTable.getSelectedRows();
     927                // delete from last relation to convserve row numbers in the table
     928                for (int i=rows.length-1; i>=0; i--) {
     929                    deleteFromRelation(rows[i]);
     930                }
    843931            }
    844932        }
     
    848936            setEnabled(
    849937                    (propertyTable != null && propertyTable.getSelectedRowCount() >= 1)
    850                     || (membershipTable != null && membershipTable.getSelectedRowCount() == 1)
     938                    || (membershipTable != null && membershipTable.getSelectedRowCount() > 0)
    851939                    );
    852940        }
     
    9201008        }
    9211009
     1010        @Override
    9221011        public void actionPerformed(ActionEvent e) {
    9231012            try {
     
    9591048
    9601049                Main.worker.execute(new Runnable(){
    961                     public void run() {
     1050                    @Override public void run() {
    9621051                        try {
    9631052                            // find a page that actually exists in the wiki
     
    10071096    }
    10081097
    1009     public void addPropertyPopupMenuSeparator() {
    1010         propertyMenu.addSeparator();
    1011     }
    1012 
    1013     public JMenuItem addPropertyPopupMenuAction(Action a) {
    1014         return propertyMenu.add(a);
    1015     }
    1016 
    1017     public void addPropertyPopupMenuListener(PopupMenuListener l) {
    1018         propertyMenu.addPopupMenuListener(l);
    1019     }
    1020 
    1021     public void removePropertyPopupMenuListener(PopupMenuListener l) {
    1022         propertyMenu.addPopupMenuListener(l);
    1023     }
    1024 
    1025     @SuppressWarnings("unchecked")
    1026     public Tag getSelectedProperty() {
    1027         int row = propertyTable.getSelectedRow();
    1028         if (row == -1) return null;
    1029         TreeMap<String, Integer> map = (TreeMap<String, Integer>) propertyData.getValueAt(row, 1);
    1030         return new Tag(
    1031                 propertyData.getValueAt(row, 0).toString(),
    1032                 map.size() > 1 ? "" : map.keySet().iterator().next());
    1033     }
    1034 
    1035     public void addMembershipPopupMenuSeparator() {
    1036         membershipMenu.addSeparator();
    1037     }
    1038 
    1039     public JMenuItem addMembershipPopupMenuAction(Action a) {
    1040         return membershipMenu.add(a);
    1041     }
    1042 
    1043     public void addMembershipPopupMenuListener(PopupMenuListener l) {
    1044         membershipMenu.addPopupMenuListener(l);
    1045     }
    1046 
    1047     public void removeMembershipPopupMenuListener(PopupMenuListener l) {
    1048         membershipMenu.addPopupMenuListener(l);
    1049     }
    1050 
    1051     public IRelation getSelectedMembershipRelation() {
    1052         int row = membershipTable.getSelectedRow();
    1053         return row > -1 ? (IRelation) membershipData.getValueAt(row, 0) : null;
    1054     }
    1055 
    10561098    class PasteValueAction extends AbstractAction {
    10571099        public PasteValueAction() {
     
    11551197        }
    11561198
     1199        @Override
    11571200        public void actionPerformed(ActionEvent e) {
    11581201            if (propertyTable.getSelectedRowCount() != 1)
     
    11891232        }
    11901233    }
    1191 
    1192     @Override
    1193     public void destroy() {
    1194         super.destroy();
    1195         for (JosmAction action : josmActions) {
    1196             action.destroy();
    1197         }
    1198         Container parent = pluginHook.getParent();
    1199         if (parent != null) {
    1200             parent.remove(pluginHook);
    1201         }
    1202     }
    12031234}
Note: See TracChangeset for help on using the changeset viewer.