Changeset 1790 in josm for trunk


Ignore:
Timestamp:
2009-07-15T17:22:56+02:00 (15 years ago)
Author:
Gubaer
Message:

Relation Editor: complete rework
Relation Editor: had to temporarily remove code for "link information" and "sorting"
IO Subsystem: clean up in exception handling
some cosmetics

Location:
trunk/src/org/openstreetmap/josm
Files:
7 added
16 edited

Legend:

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

    r1169 r1790  
    1616    public NewAction() {
    1717        super(tr("New"), "new", tr("Create a new map."),
    18         Shortcut.registerShortcut("system:new", tr("File: {0}", tr("New")), KeyEvent.VK_N, Shortcut.GROUP_MENU), true);
     18                Shortcut.registerShortcut("system:new", tr("File: {0}", tr("New")), KeyEvent.VK_N, Shortcut.GROUP_MENU), true);
    1919    }
    2020
    2121    public void actionPerformed(ActionEvent e) {
    22         Main.main.addLayer(new OsmDataLayer(new DataSet(), tr("unnamed"), null));
     22        Main.main.addLayer(new OsmDataLayer(new DataSet(), OsmDataLayer.createNewName(), null));
    2323    }
    2424}
  • trunk/src/org/openstreetmap/josm/data/osm/RelationMember.java

    r1690 r1790  
    3232    }
    3333
    34     @Override public boolean equals(Object other) {
    35         if (other == null || !(other instanceof RelationMember)) return false;
    36         RelationMember otherMember = (RelationMember) other;
    37         return otherMember.role.equals(role) && otherMember.member.equals(member);
    38     }
    39 
    4034    @Override public String toString() {
    4135        return '"' + role + "\"=" + member;
    4236    }
     37
     38    @Override
     39    public int hashCode() {
     40        final int prime = 31;
     41        int result = 1;
     42        result = prime * result + ((member == null) ? 0 : member.hashCode());
     43        result = prime * result + ((role == null) ? 0 : role.hashCode());
     44        return result;
     45    }
     46
     47    @Override
     48    public boolean equals(Object obj) {
     49        if (this == obj)
     50            return true;
     51        if (obj == null)
     52            return false;
     53        if (getClass() != obj.getClass())
     54            return false;
     55        RelationMember other = (RelationMember) obj;
     56        if (member == null) {
     57            if (other.member != null)
     58                return false;
     59        } else if (!member.equals(other.member))
     60            return false;
     61        if (role == null) {
     62            if (other.role != null)
     63                return false;
     64        } else if (!role.equals(other.role))
     65            return false;
     66        return true;
     67    }
    4368}
  • trunk/src/org/openstreetmap/josm/gui/ExtendedDialog.java

    r1661 r1790  
    9595
    9696            button = new JButton(action);
    97             if(buttonIcons != null && buttonIcons[i] != null)
     97            if(buttonIcons != null && buttonIcons[i] != null) {
    9898                button.setIcon(ImageProvider.get(buttonIcons[i]));
     99            }
    99100
    100             if(i == 0) rootPane.setDefaultButton(button);
     101            if(i == 0) {
     102                rootPane.setDefaultButton(button);
     103            }
    101104            buttonsPanel.add(button, GBC.std().insets(2,2,2,2));
    102105            buttons.add(button);
     
    120123        boolean limitedInHeight = d.height > x.height;
    121124
    122         if(x.width  > 0 && d.width  > x.width)  d.width  = x.width;
    123         if(x.height > 0 && d.height > x.height) d.height = x.height;
     125        if(x.width  > 0 && d.width  > x.width) {
     126            d.width  = x.width;
     127        }
     128        if(x.height > 0 && d.height > x.height) {
     129            d.height = x.height;
     130        }
    124131
    125132        // We have a vertical scrollbar and enough space to prevent a horizontal one
    126         if(!limitedInWidth && limitedInHeight)
     133        if(!limitedInWidth && limitedInHeight) {
    127134            d.width += new JScrollBar().getPreferredSize().width;
     135        }
    128136
    129137        setSize(d);
     
    161169        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    162170        Dimension x = new Dimension(Math.round(screenSize.width*2/3),
    163                                     Math.round(screenSize.height*2/3));
     171                Math.round(screenSize.height*2/3));
    164172        try {
    165             if(parent != null)
     173            if(parent != null) {
    166174                x = JOptionPane.getFrameForComponent(parent).getSize();
     175            }
    167176        } catch(NullPointerException e) { }
    168177        return x;
     
    184193
    185194        rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
    186             .put(KeyStroke.getKeyStroke("ESCAPE"), "ESCAPE");
     195        .put(KeyStroke.getKeyStroke("ESCAPE"), "ESCAPE");
    187196        rootPane.getActionMap().put("ESCAPE", actionListener);
    188197    }
  • trunk/src/org/openstreetmap/josm/gui/SideButton.java

    r1247 r1790  
    1818        super(action);
    1919        doStyle();
    20         setText(null);
    2120    }
    2221
     
    4645        {
    4746            shortcut.setMnemonic(this);
    48             if(tooltip != null)
     47            if(tooltip != null) {
    4948                tooltip = Main.platform.makeTooltip(tooltip, shortcut);
     49            }
    5050        }
    5151        setup(name, property, tooltip, actionListener);
  • trunk/src/org/openstreetmap/josm/gui/conflict/nodes/NodeListTableCellRenderer.java

    r1654 r1790  
    5050        rowNumberBorder = BorderFactory.createEmptyBorder(0,4,0,0);
    5151        setOpaque(true);
    52     }
    53 
    54     /**
    55      * creates the display name for a node. The name is derived from the nodes id,
    56      * its name (i.e. the value of the tag with key name) and its coordinates.
    57      *
    58      * @param node  the node
    59      * @return the display name
    60      */
    61     protected String getDisplayName(Node node) {
    62         StringBuilder sb = new StringBuilder();
    63         if (node.get("name") != null) {
    64             sb.append(node.get("name"));
    65             sb.append("/");
    66             sb.append(node.id);
    67         } else {
    68             sb.append(node.id);
    69         }
    70         sb.append(" (");
    71 
    72         if (node.getCoor() != null) {
    73             sb.append(COORD_FORMATTER.format(node.getCoor().lat()));
    74             sb.append(",");
    75             sb.append(COORD_FORMATTER.format(node.getCoor().lon()));
    76         } else {
    77             sb.append("?,?");
    78         }
    79         sb.append(")");
    80         return sb.toString();
    8152    }
    8253
     
    159130            }
    160131        }
    161         setText(getDisplayName(node));
     132        setText(node.getName());
    162133        setToolTipText(buildToolTipText(node));
    163134    }
  • trunk/src/org/openstreetmap/josm/gui/conflict/relation/RelationMemberTableCellRenderer.java

    r1650 r1790  
    9797        }
    9898        sb.append("</html>");
    99         return sb.toString();
    100     }
    101 
    102     /**
    103      * creates the display name for a node. The name is derived from the nodes id,
    104      * its name (i.e. the value of the tag with key name) and its coordinates.
    105      *
    106      * @param node  the node
    107      * @return the display name
    108      */
    109     protected String getDisplayName(RelationMember member) {
    110         StringBuilder sb = new StringBuilder();
    111         OsmPrimitive primitive = member.member;
    112         if (primitive.get("name") != null) {
    113             sb.append(primitive.get("name"));
    114             sb.append("/");
    115             sb.append(primitive.id);
    116         } else {
    117             sb.append(primitive.id);
    118         }
    119 
    120         if (primitive instanceof Node) {
    121             Node n = (Node)primitive;
    122             sb.append(" (");
    123             if (n.getCoor() != null) {
    124                 sb.append(COORD_FORMATTER.format(n.getCoor().lat()));
    125                 sb.append(",");
    126                 sb.append(COORD_FORMATTER.format(n.getCoor().lon()));
    127             } else {
    128                 sb.append("?,?");
    129             }
    130             sb.append(")");
    131         }
    13299        return sb.toString();
    133100    }
     
    191158
    192159    protected void renderPrimitive(RelationMember member) {
    193         String displayName = getDisplayName(member);
     160        String displayName = member.member.getName();
    194161        setText(displayName);
    195162        setToolTipText(buildToolTipText(member.member));
  • trunk/src/org/openstreetmap/josm/gui/dialogs/PropertiesDialog.java

    r1772 r1790  
    6464import org.openstreetmap.josm.gui.MapFrame;
    6565import org.openstreetmap.josm.gui.SideButton;
    66 import org.openstreetmap.josm.gui.dialogs.relation.GenericRelationEditor;
    6766import org.openstreetmap.josm.gui.dialogs.relation.RelationEditor;
    6867import org.openstreetmap.josm.gui.preferences.TaggingPresetPreference;
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java

    r1785 r1790  
    11package org.openstreetmap.josm.gui.dialogs.relation;
    22
    3 import static org.openstreetmap.josm.tools.I18n.marktr;
    43import static org.openstreetmap.josm.tools.I18n.tr;
    54
     5import java.awt.BorderLayout;
    66import java.awt.Dimension;
    77import java.awt.EventQueue;
     8import java.awt.FlowLayout;
     9import java.awt.GridBagConstraints;
    810import java.awt.GridBagLayout;
    9 import java.awt.GridLayout;
     11import java.awt.Insets;
    1012import java.awt.event.ActionEvent;
    11 import java.awt.event.ActionListener;
    1213import java.awt.event.ComponentAdapter;
    1314import java.awt.event.ComponentEvent;
    1415import java.awt.event.KeyEvent;
    15 import java.awt.event.WindowAdapter;
    16 import java.awt.event.WindowEvent;
    1716import java.io.IOException;
    1817import java.util.ArrayList;
    19 import java.util.Arrays;
    2018import java.util.Collection;
    21 import java.util.LinkedList;
    22 import java.util.Vector;
    23 
     19import java.util.logging.Logger;
     20
     21import javax.swing.AbstractAction;
     22import javax.swing.BorderFactory;
     23import javax.swing.JButton;
     24import javax.swing.JComponent;
    2425import javax.swing.JDialog;
    2526import javax.swing.JLabel;
     
    2728import javax.swing.JPanel;
    2829import javax.swing.JScrollPane;
    29 import javax.swing.JTabbedPane;
     30import javax.swing.JSplitPane;
    3031import javax.swing.JTable;
     32import javax.swing.KeyStroke;
    3133import javax.swing.ListSelectionModel;
    32 import javax.swing.SwingUtilities;
    3334import javax.swing.event.ListSelectionEvent;
    3435import javax.swing.event.ListSelectionListener;
    3536import javax.swing.event.TableModelEvent;
    3637import javax.swing.event.TableModelListener;
    37 import javax.swing.table.DefaultTableModel;
    3838
    3939import org.openstreetmap.josm.Main;
     
    4242import org.openstreetmap.josm.data.osm.DataSet;
    4343import org.openstreetmap.josm.data.osm.DataSource;
    44 import org.openstreetmap.josm.data.osm.Node;
    4544import org.openstreetmap.josm.data.osm.OsmPrimitive;
    4645import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
    4746import org.openstreetmap.josm.data.osm.Relation;
    4847import org.openstreetmap.josm.data.osm.RelationMember;
    49 import org.openstreetmap.josm.data.osm.Way;
    5048import org.openstreetmap.josm.data.osm.visitor.MergeVisitor;
    51 import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
    5249import org.openstreetmap.josm.gui.PleaseWaitRunnable;
    5350import org.openstreetmap.josm.gui.SideButton;
     
    5855import org.openstreetmap.josm.io.OsmServerObjectReader;
    5956import org.openstreetmap.josm.io.OsmTransferException;
    60 import org.openstreetmap.josm.tools.GBC;
     57import org.openstreetmap.josm.tools.ImageProvider;
    6158import org.openstreetmap.josm.tools.Shortcut;
    6259import org.xml.sax.SAXException;
    6360
    64 enum WayConnectionType
    65 {
    66     none,
    67     head_to_head,
    68     tail_to_tail,
    69     head_to_tail,
    70     tail_to_head;
    71     @Override
    72     public String toString()
    73     {
    74         String  result = "";
    75         switch(this)
    76         {
    77         case head_to_head:
    78             result = "-><-";
    79             break;
    80         case head_to_tail:
    81             result = "->->";
    82             break;
    83         case tail_to_head:
    84             result = "<-<-";
    85             break;
    86         case tail_to_tail:
    87             result = "<-->";
    88             break;
    89         }
    90 
    91         return result;
    92     }
    93 }
    9461
    9562/**
     
    10673public class GenericRelationEditor extends RelationEditor {
    10774
    108     // We need this twice, so cache result
    109     protected final static String applyChangesText = tr("Apply Changes");
    110 
    111     private JLabel status;
    112 
    113     /**
    114      * The membership data.
    115      */
    116     private final DefaultTableModel memberData = new DefaultTableModel() {
    117         @Override public boolean isCellEditable(int row, int column) {
    118             return column == 0;
    119         }
    120         @Override public Class<?> getColumnClass(int columnIndex) {
    121             return columnIndex == 1 ? OsmPrimitive.class : String.class;
    122         }
    123     };
    124 
    125     /**
    126      * The properties and membership lists.
    127      */
    128     private final JTable memberTable = new JTable(memberData);
     75    static private final Logger logger = Logger.getLogger(GenericRelationEditor.class.getName());
    12976
    13077    /** the tag table and its model */
     
    13481    private AutoCompletionList acList;
    13582
     83    /** the member table */
     84    private MemberTable memberTable;
     85    private MemberTableModel memberTableModel;
     86
     87    /** the model for the selection table */
     88    private SelectionTableModel selectionTableModel;
    13689
    13790    /**
     
    14194     * If no relation is given, will create an editor for a new relation.
    14295     *
     96     * @param layer the {@see OsmDataLayer} the new or edited relation belongs to
    14397     * @param relation relation to edit, or null to create a new one.
     98     * @param selectedMembers a collection of members which shall be selected initially
    14499     */
    145100    public GenericRelationEditor(OsmDataLayer layer, Relation relation, Collection<RelationMember> selectedMembers )
    146101    {
    147         // Initalizes ExtendedDialog
    148102        super(layer, relation, selectedMembers);
     103
     104        // initialize the autocompletion infrastructure
     105        //
    149106        acCache = AutoCompletionCache.getCacheForLayer(Main.map.mapView.getEditLayer());
    150107        acList = new AutoCompletionList();
    151108
    152         JPanel bothTables = setupBasicLayout(selectedMembers);
     109        // init the various models
     110        //
     111        tagEditorModel = new TagEditorModel();
     112        memberTableModel = new MemberTableModel();
     113        selectionTableModel = new SelectionTableModel(getLayer());
     114
     115        // populate the models
     116        //
    153117        if (relation != null) {
    154118            this.tagEditorModel.initFromPrimitive(relation);
     119            this.memberTableModel.populate(relation);
    155120        } else {
    156121            tagEditorModel.clear();
    157         }
     122            this.memberTableModel.populate(null);
     123        }
     124        memberTableModel.selectMembers(selectedMembers);
    158125        tagEditorModel.ensureOneTag();
    159         addWindowListener(
    160                 new WindowAdapter() {
    161                     protected void requestFocusInTopLeftCell() {
    162                         SwingUtilities.invokeLater(new Runnable(){
    163                             public void run()
    164                             {
    165                                 tagEditorModel.ensureOneTag();
    166                                 tagTable.requestFocusInCell(0, 0);
    167                             }
    168                         });
    169                     }
    170                     @Override
    171                     public void windowDeiconified(WindowEvent e) {
    172                         requestFocusInTopLeftCell();
    173                     }
    174                     @Override
    175                     public void windowOpened(WindowEvent e) {
    176                         requestFocusInTopLeftCell();
    177                     }
    178                 }
    179         );
    180 
    181         JTabbedPane tabPane = new JTabbedPane();
    182         tabPane.add(bothTables, tr("Basic"));
    183 
    184         // This sets the minimum size before scrollbars appear on the dialog
    185         tabPane.setPreferredSize(new Dimension(100, 100));
    186         contentConstraints = GBC.eol().fill().insets(5,10,5,0);
    187         setupDialog(tabPane, new String[] { "ok.png", "cancel.png" });
    188         // FIXME: Make it remember last screen position
     126
     127        JSplitPane pane = buildSplitPane();
     128        pane.setPreferredSize(new Dimension(100, 100));
     129
     130        JPanel pnl = new JPanel();
     131        pnl.setLayout(new BorderLayout());
     132        pnl.add(pane,BorderLayout.CENTER);
     133        pnl.setBorder(BorderFactory.createRaisedBevelBorder());
     134
     135        getContentPane().setLayout(new BorderLayout());
     136        getContentPane().add(pnl,BorderLayout.CENTER);
     137        getContentPane().add(buildOkCancelButtonPanel(), BorderLayout.SOUTH);
     138
    189139        setSize(findMaxDialogSize());
    190 
    191         try { setAlwaysOnTop(true); } catch (SecurityException sx) {}
    192         setVisible(true);
    193     }
    194 
    195 
    196     /**
    197      * Basic Editor panel has two blocks: a tag table at the top and a membership list below
    198      * @param selectedMembers
    199      * @return a JPanel with the described layout
    200      */
    201     private JPanel setupBasicLayout(Collection<RelationMember> selectedMembers) {
     140        try {
     141            setAlwaysOnTop(true);
     142        } catch(SecurityException e) {
     143            logger.warning(tr("Caught security exception for setAlwaysOnTop(). Ignoring. Exception was: {0}", e.toString()));
     144        }
     145    }
     146
     147    /**
     148     * builds the panel with the OK and  the Cancel button
     149     *
     150     * @return the panel with the OK and  the Cancel button
     151     */
     152    protected JPanel buildOkCancelButtonPanel() {
     153        JPanel pnl = new JPanel();
     154        pnl.setLayout(new FlowLayout(FlowLayout.CENTER));
     155
     156        pnl.add(new SideButton(new OKAction()));
     157        pnl.add(new SideButton(new CancelAction()));
     158
     159        return pnl;
     160    }
     161
     162    /**
     163     * build the panel with the buttons on the left
     164     *
     165     * @return
     166     */
     167    protected JPanel buildTagEditorControlPanel() {
     168        JPanel pnl = new JPanel();
     169        pnl.setLayout(new GridBagLayout());
     170
     171        GridBagConstraints gc = new GridBagConstraints();
     172        gc.gridx = 0;
     173        gc.gridy = 0;
     174        gc.gridheight  =1;
     175        gc.gridwidth = 1;
     176        gc.insets = new Insets(0,5,0,5);
     177        gc.fill = GridBagConstraints.HORIZONTAL;
     178        gc.anchor = GridBagConstraints.CENTER;
     179        gc.weightx = 0.0;
     180        gc.weighty = 0.0;
     181
     182        // -----
     183        AddTagAction addTagAction = new AddTagAction();
     184        pnl.add(new JButton(addTagAction), gc);
     185
     186        // -----
     187        gc.gridy = 1;
     188        DeleteTagAction deleteTagAction = new DeleteTagAction();
     189        tagTable.getSelectionModel().addListSelectionListener(deleteTagAction);
     190        pnl.add(new JButton(deleteTagAction), gc);
     191
     192        // ------
     193        // just grab the remaining space
     194        gc.gridy = 2;
     195        gc.weighty = 1.0;
     196        gc.fill = GridBagConstraints.BOTH;
     197        pnl.add(new JPanel(),gc);
     198        return pnl;
     199    }
     200
     201    /**
     202     * builds the panel with the tag editor
     203     *
     204     * @return the panel with the tag editor
     205     */
     206    protected JPanel buildTagEditorPanel() {
     207        JPanel pnl = new JPanel();
     208        pnl.setLayout(new GridBagLayout());
     209
    202210        // setting up the tag table
    203211        //
    204         tagEditorModel = new TagEditorModel();
    205212        tagTable = new TagTable(tagEditorModel);
    206213        acCache.initFromJOSMDataset();
     
    212219        editor.setAutoCompletionList(acList);
    213220
    214 
    215         // setting up the member table
    216 
    217         memberData.setColumnIdentifiers(new String[]{tr("Role"),tr("Occupied By"), tr("linked")});
    218         memberTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
    219         memberTable.getColumnModel().getColumn(1).setCellRenderer(new OsmPrimitivRenderer());
    220         memberData.addTableModelListener(new TableModelListener() {
    221             public void tableChanged(TableModelEvent tme) {
    222                 if (tme.getType() == TableModelEvent.UPDATE && tme.getColumn() == 0) {
    223                     int row = tme.getFirstRow();
    224                     getClone().members.get(row).role = memberData.getValueAt(row, 0).toString();
    225                 }
    226             }
    227         });
    228         ListSelectionModel lsm = memberTable.getSelectionModel();
    229         lsm.addListSelectionListener(new ListSelectionListener() {
    230             public void valueChanged(ListSelectionEvent e) {
    231                 ArrayList<OsmPrimitive> sel;
    232                 int cnt = memberTable.getSelectedRowCount();
    233                 if(cnt > 0)
    234                 {
    235                     sel = new ArrayList<OsmPrimitive>(cnt);
    236                     for (int i : memberTable.getSelectedRows()) {
    237                         sel.add((OsmPrimitive)memberTable.getValueAt(i, 1));
    238                     }
    239                 }
    240                 else
    241                 {
    242                     cnt = memberTable.getRowCount();
    243                     sel = new ArrayList<OsmPrimitive>(cnt);
    244                     for (int i = 0; i < cnt; ++i) {
    245                         sel.add((OsmPrimitive)memberTable.getValueAt(i, 1));
    246                     }
    247                 }
    248                 Main.ds.setSelected(sel);
    249             }
    250         });
    251         memberTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
    252 
    253         // combine both tables and wrap them in a scrollPane
    254         JPanel bothTables = new JPanel();
    255         bothTables.setLayout(new GridBagLayout());
    256         bothTables.add(new JLabel(tr("Tags")), GBC.eol().fill(GBC.HORIZONTAL));
    257221        final JScrollPane scrollPane = new JScrollPane(tagTable);
    258222
     
    270234                }
    271235        );
    272         bothTables.add(scrollPane, GBC.eop().fill(GBC.BOTH));
    273         bothTables.add(status = new JLabel(tr("Members")), GBC.eol().fill(GBC.HORIZONTAL));
    274         // this is not exactly pretty but the four buttons simply don't fit in one line.
    275         // we should have smaller buttons for situations like this.
    276         JPanel buttonPanel = setupBasicButtons();
    277 
    278         bothTables.add(new JScrollPane(memberTable), GBC.eol().fill(GBC.BOTH));
    279         bothTables.add(buttonPanel, GBC.eop().fill(GBC.HORIZONTAL));
    280         refreshTables();
    281 
    282         if (selectedMembers != null) {
    283             boolean scrolled = false;
    284             for (int i = 0; i < memberData.getRowCount(); i++) {
    285                 for (RelationMember m : selectedMembers) {
    286                     if (m.member == memberData.getValueAt(i, 1)
    287                             && m.role.equals(memberData.getValueAt(i, 0))) {
    288                         memberTable.addRowSelectionInterval(i, i);
    289                         if (!scrolled) {
    290                             // Ensure that the first member is visible
    291                             memberTable.scrollRectToVisible(memberTable.getCellRect(i, 0, true));
    292                             scrolled = true;
    293                         }
    294                         break;
     236
     237        GridBagConstraints gc = new GridBagConstraints();
     238        gc.gridx = 0;
     239        gc.gridy = 0;
     240        gc.gridheight  =1;
     241        gc.gridwidth = 3;
     242        gc.fill = GridBagConstraints.HORIZONTAL;
     243        gc.anchor = GridBagConstraints.FIRST_LINE_START;
     244        gc.weightx = 1.0;
     245        gc.weighty = 0.0;
     246        pnl.add(new JLabel(tr("Tags")), gc);
     247
     248        gc.gridx = 0;
     249        gc.gridy = 1;
     250        gc.gridheight  =1;
     251        gc.gridwidth = 1;
     252        gc.fill = GridBagConstraints.VERTICAL;
     253        gc.anchor = GridBagConstraints.NORTHWEST;
     254        gc.weightx = 0.0;
     255        gc.weighty = 1.0;
     256        pnl.add(buildTagEditorControlPanel(), gc);
     257
     258        gc.gridx = 1;
     259        gc.gridy = 1;
     260        gc.fill = GridBagConstraints.BOTH;
     261        gc.anchor = GridBagConstraints.CENTER;
     262        gc.weightx = 0.8;
     263        gc.weighty = 1.0;
     264        pnl.add(scrollPane, gc);
     265        return pnl;
     266    }
     267
     268    /**
     269     * builds the panel for the relation member editor
     270     *
     271     * @return the panel for the relation member editor
     272     */
     273    protected JPanel buildMemberEditorPanel() {
     274        JPanel pnl = new JPanel();
     275        pnl.setLayout(new GridBagLayout());
     276        // setting up the member table
     277        memberTable = new MemberTable(memberTableModel);
     278
     279        ListSelectionModel lsm = memberTable.getSelectionModel();
     280        lsm.addListSelectionListener(new ListSelectionListener() {
     281            public void valueChanged(ListSelectionEvent e) {
     282                ArrayList<OsmPrimitive> sel;
     283                int cnt = memberTable.getSelectedRowCount();
     284                if(cnt > 0) {
     285                    sel = new ArrayList<OsmPrimitive>(cnt);
     286                    for (int i : memberTable.getSelectedRows()) {
     287                        sel.add(memberTableModel.getReferredPrimitive(i));
     288                    }
     289                } else {
     290                    cnt = memberTable.getRowCount();
     291                    sel = new ArrayList<OsmPrimitive>(cnt);
     292                    for (int i = 0; i < cnt; ++i) {
     293                        sel.add(memberTableModel.getReferredPrimitive(i));
    295294                    }
    296295                }
    297 
     296                Main.ds.setSelected(sel);
    298297            }
    299         }
    300         return bothTables;
    301     }
    302 
    303     /**
    304      * Creates the buttons for the basic editing layout
    305      * @return JPanel with basic buttons
    306      */
    307     private JPanel setupBasicButtons() {
    308         JPanel buttonPanel = new JPanel(new GridLayout(2, 4));
    309 
    310         buttonPanel.add(createButton(marktr("Move Up"), "moveup", tr("Move the currently selected members up"), KeyEvent.VK_N, new ActionListener() {
    311             public void actionPerformed(ActionEvent e) {
    312                 moveMembers(-1);
    313             }
    314         }));
    315 
    316         buttonPanel.add(createButton(marktr("Add Selected"),"addselected",
    317                 tr("Add all currently selected objects as members"), KeyEvent.VK_D, new ActionListener() {
    318             public void actionPerformed(ActionEvent e) {
    319                 addSelected();
    320             }
    321         }));
    322 
    323         buttonPanel.add(createButton(marktr("Remove Selected"),"removeselected",
    324                 tr("Remove all currently selected objects from relation"), KeyEvent.VK_S, new ActionListener() {
    325             public void actionPerformed(ActionEvent e) {
    326                 deleteSelected();
    327             }
    328         }));
    329 
    330         buttonPanel.add(createButton(marktr("Sort"), "sort",
    331                 tr("Sort the selected relation members or the whole list"), KeyEvent.VK_O, new ActionListener() {
    332             public void actionPerformed(ActionEvent e) {
    333                 sort();
    334             }
    335         }));
    336 
    337         buttonPanel.add(createButton(marktr("Move Down"), "movedown", tr("Move the currently selected members down"), KeyEvent.VK_J, new ActionListener() {
    338             public void actionPerformed(ActionEvent e) {
    339                 moveMembers(1);
    340             }
    341         }));
    342 
    343         buttonPanel.add(createButton(marktr("Remove"),"remove",
    344                 tr("Remove the member in the current table row from this relation"), KeyEvent.VK_X, new ActionListener() {
    345             public void actionPerformed(ActionEvent e) {
    346                 int[] rows = memberTable.getSelectedRows();
    347                 RelationMember mem = new RelationMember();
    348                 for (int row : rows) {
    349                     mem.role = memberTable.getValueAt(row, 0).toString();
    350                     mem.member = (OsmPrimitive) memberTable.getValueAt(row, 1);
    351                     getClone().members.remove(mem);
    352                 }
    353                 refreshTables();
    354             }
    355         }));
    356 
    357         buttonPanel.add(createButton(marktr("Download Members"),"downloadincomplete",
    358                 tr("Download all incomplete ways and nodes in relation"), KeyEvent.VK_K, new ActionListener() {
    359             public void actionPerformed(ActionEvent e) {
    360                 downloadRelationMembers();
    361                 refreshTables();
    362             }
    363         }));
    364 
    365         return buttonPanel;
    366     }
    367 
    368     private void sort() {
    369         RelationNodeMap                    map = new RelationNodeMap(getClone());
    370         Vector<LinkedList<Integer>>        segments;
    371         LinkedList<Integer>                segment;
    372         Node                               startSearchNode;
    373         Node                               endSearchNode;
    374         boolean                            something_done;
    375 
    376         /*
    377          * sort any 2 or more connected elements together
    378          * may be slow with many unconnected members
    379          * TODO: cleanup again, too much code in 1 method
    380          */
    381 
    382         segments = new Vector<LinkedList<Integer>>();
    383         // add first member of relation, not strictly necessary
    384         if (map.remove(0, getClone().members.get(0)))
    385         {
    386             segment = new LinkedList<Integer>();
    387             segment.add(Integer.valueOf(0));
    388             segments.add(segment);
    389         }
    390         while (!map.isEmpty())
    391         {
    392             segment = segments.lastElement();
    393 
    394             do
    395             {
    396                 something_done = false;
    397                 startSearchNode = null;
    398                 endSearchNode = null;
    399                 if (segment.size() == 1)
    400                 {
    401                     RelationMember  m = getClone().members.get(segment.getFirst());
    402                     try
    403                     {
    404                         Way             w = (Way)m.member;
    405                         endSearchNode = w.lastNode();
    406                         startSearchNode = w.firstNode();
    407                     }
    408                     catch(ClassCastException e1)
    409                     {
    410                         try
    411                         {
    412                             Node n = (Node)m.member;
    413                             endSearchNode = n;
    414                         }
    415                         catch(ClassCastException e2)
    416                         {
    417                             // impossible
    418                         }
     298        });
     299
     300        final JScrollPane scrollPane = new JScrollPane(memberTable);
     301        // this adapters ensures that the width of the tag table columns is adjusted
     302        // to the width of the scroll pane viewport. Also tried to overwrite
     303        // getPreferredViewportSize() in JTable, but did not work.
     304        //
     305        scrollPane.addComponentListener(
     306                new ComponentAdapter() {
     307                    @Override public void componentResized(ComponentEvent e) {
     308                        super.componentResized(e);
     309                        Dimension d = scrollPane.getViewport().getExtentSize();
     310                        memberTable.adjustColumnWidth(d.width);
    419311                    }
    420312                }
    421                 else
    422                 {
    423                     // add unused node of first element and unused node of last element
    424                     // start with the first element
    425                     RelationMember element = getClone().members.get(segment.getFirst());
    426                     RelationMember other_element = getClone().members.get(segment.get(1));
    427 
    428                     try
    429                     {
    430                         Way w = (Way)element.member;
    431                         try
    432                         {
    433                             Way x = (Way)other_element.member;
    434                             if ((w.firstNode() == x.firstNode()) || (w.firstNode() == x.lastNode()))
    435                             {
    436                                 startSearchNode = w.lastNode();
    437                             }
    438                             else
    439                             {
    440                                 startSearchNode = w.firstNode();
    441                             }
    442                         }
    443                         catch(ClassCastException e3)
    444                         {
    445                             try
    446                             {
    447                                 Node m = (Node)other_element.member;
    448                                 if (w.firstNode() == m)
    449                                 {
    450                                     startSearchNode = w.lastNode();
    451                                 }
    452                                 else
    453                                 {
    454                                     startSearchNode = w.firstNode();
    455                                 }
    456                             }
    457                             catch(ClassCastException e4)
    458                             {
    459                                 // impossible
    460                             }
    461                         }
    462                     }
    463                     catch(ClassCastException e1)
    464                     {
    465                         try
    466                         {
    467                             Node n = (Node)element.member;
    468                             startSearchNode = n;
    469                         }
    470                         catch(ClassCastException e2)
    471                         {
    472                             // impossible
    473                         }
    474                     }
    475                     // now the same for the last element
    476                     element = getClone().members.get(segment.getLast());
    477                     other_element = getClone().members.get(segment.get(segment.size() - 2));
    478 
    479                     try
    480                     {
    481                         Way w = (Way)element.member;
    482                         try
    483                         {
    484                             Way x = (Way)other_element.member;
    485                             if ((w.firstNode() == x.firstNode()) || (w.firstNode() == x.lastNode()))
    486                             {
    487                                 endSearchNode = w.lastNode();
    488                             }
    489                             else
    490                             {
    491                                 endSearchNode = w.firstNode();
    492                             }
    493                         }
    494                         catch(ClassCastException e3)
    495                         {
    496                             try
    497                             {
    498                                 Node m = (Node)other_element.member;
    499                                 if (w.firstNode() == m)
    500                                 {
    501                                     endSearchNode = w.lastNode();
    502                                 }
    503                                 else
    504                                 {
    505                                     endSearchNode = w.firstNode();
    506                                 }
    507                             }
    508                             catch(ClassCastException e4)
    509                             {
    510                                 // impossible
    511                             }
    512                         }
    513                     }
    514                     catch(ClassCastException e1)
    515                     {
    516                         try
    517                         {
    518                             Node n = (Node)element.member;
    519                             endSearchNode = n;
    520                         }
    521                         catch(ClassCastException e2)
    522                         {
    523                             // impossible
    524                         }
    525                     }
    526                 }
    527 
    528                 // let's see if we can find connected elements for endSearchNode and startSearchNode
    529                 if (startSearchNode != null)
    530                 {
    531                     Integer m2 = map.find(startSearchNode, segment.getFirst());
    532                     if (m2 != null)
    533                     {
    534                         segment.add(0, m2);
    535                         map.remove(m2, getClone().members.get(m2));
    536                         something_done = true;
    537                     }
    538                 }
    539                 if (endSearchNode != null)
    540                 {
    541                     Integer m2 = map.find(endSearchNode, segment.getLast());
    542                     if (m2 != null)
    543                     {
    544                         segment.add(segment.size(), m2);
    545                         map.remove(m2, getClone().members.get(m2));
    546                         something_done = true;
    547                     }
    548                 }
    549             } while (something_done);
    550 
    551             Integer next = map.pop();
    552             if (next == null)
    553             {
    554                 break;
    555             }
    556 
    557             segment = new LinkedList<Integer>();
    558             segment.add(next);
    559             segments.add(segment);
    560         }
    561         // append map.remaining() to segments list (as a single segment)
    562         segment = new LinkedList<Integer>();
    563         segment.addAll(map.getRemaining());
    564         segments.add(segment);
    565 
    566         // now we need to actually re-order the relation members
    567         ArrayList<RelationMember>  newmembers = new ArrayList<RelationMember>();
    568         for (LinkedList<Integer> segment2 : segments)
    569         {
    570             for (Integer p : segment2)
    571             {
    572                 newmembers.add(getClone().members.get(p));
    573             }
    574         }
    575         getClone().members.clear();
    576         getClone().members.addAll(newmembers);
    577 
    578         refreshTables();
    579     }
    580 
     313        );
     314
     315
     316        GridBagConstraints gc = new GridBagConstraints();
     317        gc.gridx = 0;
     318        gc.gridy = 0;
     319        gc.gridheight  =1;
     320        gc.gridwidth = 3;
     321        gc.fill = GridBagConstraints.HORIZONTAL;
     322        gc.anchor = GridBagConstraints.FIRST_LINE_START;
     323        gc.weightx = 1.0;
     324        gc.weighty = 0.0;
     325        pnl.add(new JLabel(tr("Members")), gc);
     326
     327        gc.gridx = 0;
     328        gc.gridy = 1;
     329        gc.gridheight  =1;
     330        gc.gridwidth = 1;
     331        gc.fill = GridBagConstraints.VERTICAL;
     332        gc.anchor = GridBagConstraints.NORTHWEST;
     333        gc.weightx = 0.0;
     334        gc.weighty = 1.0;
     335        pnl.add(buildLeftButtonPanel(), gc);
     336
     337        gc.gridx = 1;
     338        gc.gridy = 1;
     339        gc.fill = GridBagConstraints.BOTH;
     340        gc.anchor = GridBagConstraints.CENTER;
     341        gc.weightx = 0.8;
     342        gc.weighty = 1.0;
     343        pnl.add(scrollPane, gc);
     344
     345        gc.gridx = 2;
     346        gc.gridy = 1;
     347        gc.weightx = 0.0;
     348        gc.weighty = 1.0;
     349        pnl.add(buildSelectionControlButtonPanel(), gc);
     350
     351        gc.gridx = 3;
     352        gc.gridy = 1;
     353        gc.weightx = 0.2;
     354        gc.weighty = 1.0;
     355        pnl.add(buildSelectionTablePanel(), gc);
     356
     357        gc.gridx = 1;
     358        gc.gridy = 2;
     359        gc.weightx = 1.0;
     360        gc.weighty = 0.0;
     361        pnl.add(buildButtonPanel(), gc);
     362
     363        return pnl;
     364    }
     365
     366    /**
     367     * builds the panel with the table displaying the currently selected primitives
     368     *
     369     * @return
     370     */
     371    protected JPanel buildSelectionTablePanel() {
     372        JPanel pnl = new JPanel();
     373        pnl.setLayout(new BorderLayout());
     374
     375        JTable tbl = new JTable(selectionTableModel,new SelectionTableColumnModel());
     376        tbl.setEnabled(false);
     377
     378        JScrollPane pane = new JScrollPane(tbl);
     379        pnl.add(pane, BorderLayout.CENTER);
     380        return pnl;
     381    }
     382
     383    /**
     384     * builds the {@see JSplitPane} which divides the editor in an upper and a lower
     385     * half
     386     *
     387     * @return the split panel
     388     */
     389    protected JSplitPane buildSplitPane() {
     390        JSplitPane pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
     391        pane.setTopComponent(buildTagEditorPanel());
     392        pane.setBottomComponent(buildMemberEditorPanel());
     393        pane.setOneTouchExpandable(true);
     394        pane.setDividerLocation(150);
     395        return pane;
     396    }
     397
     398    /**
     399     * build the panel with the buttons on the left
     400     *
     401     * @return
     402     */
     403    protected JPanel buildLeftButtonPanel() {
     404        JPanel pnl = new JPanel();
     405        pnl.setLayout(new GridBagLayout());
     406
     407        GridBagConstraints gc = new GridBagConstraints();
     408        gc.gridx = 0;
     409        gc.gridy = 0;
     410        gc.gridheight  =1;
     411        gc.gridwidth = 1;
     412        gc.insets = new Insets(0,5,0,5);
     413        gc.fill = GridBagConstraints.HORIZONTAL;
     414        gc.anchor = GridBagConstraints.CENTER;
     415        gc.weightx = 0.0;
     416        gc.weighty = 0.0;
     417        MoveUpAction moveUpAction = new MoveUpAction();
     418        memberTableModel.getSelectionModel().addListSelectionListener(moveUpAction);
     419        pnl.add(new JButton(moveUpAction), gc);
     420
     421        // -----
     422        gc.gridy = 1;
     423        MoveDownAction moveDownAction = new MoveDownAction();
     424        memberTableModel.getSelectionModel().addListSelectionListener(moveDownAction);
     425        pnl.add(new JButton(moveDownAction), gc);
     426
     427        // ------
     428        gc.gridy = 2;
     429        RemoveAction removeSelectedAction = new RemoveAction();
     430        memberTable.getSelectionModel().addListSelectionListener(removeSelectedAction);
     431        pnl.add(new JButton(removeSelectedAction),gc);
     432
     433        // ------
     434        // just grab the remaining space
     435        gc.gridy = 3;
     436        gc.weighty = 1.0;
     437        gc.fill = GridBagConstraints.BOTH;
     438        pnl.add(new JPanel(),gc);
     439        return pnl;
     440    }
     441
     442    /**
     443     * build the panel with the buttons for adding or removing the current selection
     444     *
     445     * @return
     446     */
     447    protected JPanel buildSelectionControlButtonPanel() {
     448        JPanel pnl = new JPanel();
     449        pnl.setLayout(new GridBagLayout());
     450
     451        GridBagConstraints gc = new GridBagConstraints();
     452        gc.gridx = 0;
     453        gc.gridy = 0;
     454        gc.gridheight  =1;
     455        gc.gridwidth = 1;
     456        gc.insets = new Insets(0,5,0,5);
     457        gc.fill = GridBagConstraints.HORIZONTAL;
     458        gc.anchor = GridBagConstraints.CENTER;
     459        gc.weightx = 0.0;
     460        gc.weighty = 0.0;
     461        AddSelectedAtStartAction addSelectionAction = new AddSelectedAtStartAction();
     462        selectionTableModel.addTableModelListener(addSelectionAction);
     463        pnl.add(new JButton(addSelectionAction), gc);
     464
     465        // -----
     466        gc.gridy = 1;
     467        AddSelectedBeforeSelection addSelectedBeforeSelectionAction = new AddSelectedBeforeSelection();
     468        selectionTableModel.addTableModelListener(addSelectedBeforeSelectionAction);
     469        memberTableModel.getSelectionModel().addListSelectionListener(addSelectedBeforeSelectionAction);
     470        pnl.add(new JButton(addSelectedBeforeSelectionAction), gc);
     471
     472        // -----
     473        gc.gridy = 2;
     474        AddSelectedAfterSelection addSelectedAfterSelectionAction = new AddSelectedAfterSelection();
     475        selectionTableModel.addTableModelListener(addSelectedAfterSelectionAction);
     476        memberTableModel.getSelectionModel().addListSelectionListener(addSelectedAfterSelectionAction);
     477        pnl.add(new JButton(addSelectedAfterSelectionAction), gc);
     478
     479        // -----
     480        gc.gridy = 3;
     481        AddSelectedAtEndAction addSelectedAtEndAction = new AddSelectedAtEndAction();
     482        selectionTableModel.addTableModelListener(addSelectedAtEndAction);
     483        pnl.add(new JButton(addSelectedAtEndAction), gc);
     484
     485        // -----
     486        gc.gridy = 4;
     487        RemoveSelectedAction removeSelectedAction = new RemoveSelectedAction();
     488        selectionTableModel.addTableModelListener(removeSelectedAction);
     489        pnl.add(new JButton(removeSelectedAction), gc);
     490
     491        // ------
     492        // just grab the remaining space
     493        gc.gridy = 5;
     494        gc.weighty = 1.0;
     495        gc.fill = GridBagConstraints.BOTH;
     496        pnl.add(new JPanel(),gc);
     497
     498        return pnl;
     499    }
     500
     501    /**
     502     * Creates the buttons for the basic editing layout
     503     * @return {@see JPanel} with basic buttons
     504     */
     505    protected JPanel buildButtonPanel() {
     506        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
     507        buttonPanel.add(new SideButton(new DownlaodAction()));
     508        return buttonPanel;
     509    }
    581510
    582511    /**
     
    587516            // If the user wanted to create a new relation, but hasn't added any members or
    588517            // tags, don't add an empty relation
    589             if(getClone().members.size() == 0 && tagEditorModel.getKeys().isEmpty())
     518            if(memberTableModel.getRowCount() == 0 && tagEditorModel.getKeys().isEmpty())
    590519                return;
    591             tagEditorModel.applyToPrimitive(getClone());
    592             Main.main.undoRedo.add(new AddCommand(getClone()));
     520            Relation clone = new Relation(getRelation());
     521            tagEditorModel.applyToPrimitive(clone);
     522            memberTableModel.applyToRelation(clone);
     523            Main.main.undoRedo.add(new AddCommand(clone));
    593524            DataSet.fireSelectionChanged(Main.ds.getSelected());
    594         } else if (! getRelation().hasEqualSemanticAttributes(getClone()) || tagEditorModel.isDirty()) {
    595             tagEditorModel.applyToPrimitive(getClone());
    596             Main.main.undoRedo.add(new ChangeCommand(getRelation(), getClone()));
     525        } else if (! memberTableModel.hasSameMembersAs(getRelation()) || tagEditorModel.isDirty()) {
     526            Relation clone = new Relation(getRelation());
     527            tagEditorModel.applyToPrimitive(clone);
     528            memberTableModel.applyToRelation(clone);
     529            Main.main.undoRedo.add(new ChangeCommand(getRelation(), clone));
    597530            DataSet.fireSelectionChanged(Main.ds.getSelected());
    598531        }
    599     }
    600 
    601     @Override
    602     protected void buttonAction(ActionEvent evt) {
    603         String a = evt.getActionCommand();
    604         if(applyChangesText.equals(a)) {
    605             applyChanges();
    606         }
    607 
    608         setVisible(false);
    609532    }
    610533
     
    616539
    617540    /**
    618      * Updates the references from members of the cloned relation (see {@see #getClone())
    619      * after an update from the server.
     541     * Asynchronously download the members of the currently edited relation
    620542     *
    621543     */
    622     protected void updateMemberReferencesInClone() {
    623         DataSet ds = getLayer().data;
    624         for (RelationMember member : getClone().members) {
    625             if (member.member.id == 0) {
    626                 continue;
     544    private void downloadRelationMembers() {
     545        if (!memberTableModel.hasIncompleteMembers())
     546            return;
     547        Main.worker.submit(new DownloadTask());
     548    }
     549
     550    @Override
     551    public void dispose() {
     552        selectionTableModel.unregister();
     553        super.dispose();
     554    }
     555
     556    @Override
     557    public void setVisible(boolean b) {
     558        super.setVisible(b);
     559        if (!b) {
     560            dispose();
     561        }
     562    }
     563
     564    class AddSelectedAtStartAction extends AbstractAction implements TableModelListener {
     565        public AddSelectedAtStartAction() {
     566            putValue(SHORT_DESCRIPTION,  tr("Add all primitives selected in the current dataset before the first member"));
     567            putValue(SMALL_ICON, ImageProvider.get("dialogs/conflict", "copystartright"));
     568            //putValue(NAME, tr("Add Selected"));
     569            refreshEnabled();
     570        }
     571
     572        protected void refreshEnabled() {
     573            setEnabled(selectionTableModel.getRowCount() >  0);
     574        }
     575
     576        public void actionPerformed(ActionEvent e) {
     577            memberTableModel.addMembersAtBeginning(selectionTableModel.getSelection());
     578        }
     579
     580        public void tableChanged(TableModelEvent e) {
     581            refreshEnabled();
     582        }
     583    }
     584
     585    class AddSelectedAtEndAction extends AbstractAction implements TableModelListener {
     586        public AddSelectedAtEndAction() {
     587            putValue(SHORT_DESCRIPTION,  tr("Add all primitives selected in the current dataset after the last member"));
     588            putValue(SMALL_ICON, ImageProvider.get("dialogs/conflict", "copyendright"));
     589            //putValue(NAME, tr("Add Selected"));
     590            refreshEnabled();
     591        }
     592
     593        protected void refreshEnabled() {
     594            setEnabled(selectionTableModel.getRowCount() >  0);
     595        }
     596
     597        public void actionPerformed(ActionEvent e) {
     598            memberTableModel.addMembersAtEnd(selectionTableModel.getSelection());
     599        }
     600
     601        public void tableChanged(TableModelEvent e) {
     602            refreshEnabled();
     603        }
     604    }
     605
     606    class AddSelectedBeforeSelection extends AbstractAction implements TableModelListener, ListSelectionListener {
     607        public AddSelectedBeforeSelection() {
     608            putValue(SHORT_DESCRIPTION,  tr("Add all primitives selected in the current dataset before the first selected member"));
     609            putValue(SMALL_ICON, ImageProvider.get("dialogs/conflict", "copybeforecurrentright"));
     610            //putValue(NAME, tr("Add Selected"));
     611            refreshEnabled();
     612        }
     613
     614        protected void refreshEnabled() {
     615            setEnabled(selectionTableModel.getRowCount() >  0
     616                    && memberTableModel.getSelectionModel().getMinSelectionIndex() >= 0);
     617        }
     618
     619        public void actionPerformed(ActionEvent e) {
     620            memberTableModel.addMembersBeforeIdx(selectionTableModel.getSelection(), memberTableModel.getSelectionModel().getMinSelectionIndex());
     621        }
     622
     623        public void tableChanged(TableModelEvent e) {
     624            refreshEnabled();
     625        }
     626
     627        public void valueChanged(ListSelectionEvent e) {
     628            refreshEnabled();
     629        }
     630    }
     631
     632    class AddSelectedAfterSelection extends AbstractAction implements TableModelListener, ListSelectionListener {
     633        public AddSelectedAfterSelection() {
     634            putValue(SHORT_DESCRIPTION,  tr("Add all primitives selected in the current dataset after the last selected member"));
     635            putValue(SMALL_ICON, ImageProvider.get("dialogs/conflict", "copyaftercurrentright"));
     636            //putValue(NAME, tr("Add Selected"));
     637            refreshEnabled();
     638        }
     639
     640        protected void refreshEnabled() {
     641            setEnabled(selectionTableModel.getRowCount() >  0
     642                    && memberTableModel.getSelectionModel().getMinSelectionIndex() >= 0);
     643        }
     644
     645        public void actionPerformed(ActionEvent e) {
     646            memberTableModel.addMembersAfterIdx(selectionTableModel.getSelection(), memberTableModel.getSelectionModel().getMaxSelectionIndex());
     647        }
     648
     649        public void tableChanged(TableModelEvent e) {
     650            refreshEnabled();
     651        }
     652
     653        public void valueChanged(ListSelectionEvent e) {
     654            refreshEnabled();
     655        }
     656    }
     657
     658    class RemoveSelectedAction extends AbstractAction implements TableModelListener {
     659        public RemoveSelectedAction() {
     660            putValue(SHORT_DESCRIPTION,  tr("Remove all currently selected objects from relation"));
     661            putValue(SMALL_ICON, ImageProvider.get("dialogs", "removeselected"));
     662            // putValue(NAME, tr("Remove Selected"));
     663            Shortcut.registerShortcut("relationeditor:removeselected",
     664                    tr("Relation Editor: Remove Selected"),
     665                    KeyEvent.VK_S,
     666                    Shortcut.GROUP_MNEMONIC);
     667
     668            DataSet ds = getLayer().data;
     669            setEnabled(ds != null && !ds.getSelected().isEmpty());
     670        }
     671
     672        public void actionPerformed(ActionEvent e) {
     673            memberTableModel.removeMembersReferringTo(selectionTableModel.getSelection());
     674        }
     675
     676        public void tableChanged(TableModelEvent e) {
     677            setEnabled(selectionTableModel.getRowCount() >0);
     678        }
     679    }
     680
     681    class MoveUpAction extends AbstractAction implements ListSelectionListener {
     682        public MoveUpAction() {
     683            putValue(SHORT_DESCRIPTION,  tr("Move the currently selected members up"));
     684            putValue(SMALL_ICON, ImageProvider.get("dialogs", "moveup"));
     685            //putValue(NAME, tr("Move Up"));
     686            Shortcut.registerShortcut("relationeditor:moveup",
     687                    tr("Relation Editor: Move Up"),
     688                    KeyEvent.VK_N,
     689                    Shortcut.GROUP_MNEMONIC);
     690            setEnabled(false);
     691        }
     692
     693        public void actionPerformed(ActionEvent e) {
     694            memberTableModel.moveUp(memberTable.getSelectedRows());
     695        }
     696
     697        public void valueChanged(ListSelectionEvent e) {
     698            setEnabled(memberTableModel.canMoveUp(memberTable.getSelectedRows()));
     699        }
     700    }
     701
     702    class MoveDownAction extends AbstractAction implements ListSelectionListener {
     703        public MoveDownAction() {
     704            putValue(SHORT_DESCRIPTION,  tr("Move the currently selected members down"));
     705            putValue(SMALL_ICON, ImageProvider.get("dialogs", "movedown"));
     706            //putValue(NAME, tr("Move Down"));
     707            Shortcut.registerShortcut("relationeditor:moveup",
     708                    tr("Relation Editor: Move Down"),
     709                    KeyEvent.VK_J,
     710                    Shortcut.GROUP_MNEMONIC);
     711            setEnabled(false);
     712        }
     713
     714        public void actionPerformed(ActionEvent e) {
     715            memberTableModel.moveDown(memberTable.getSelectedRows());
     716        }
     717
     718        public void valueChanged(ListSelectionEvent e) {
     719            setEnabled(memberTableModel.canMoveDown(memberTable.getSelectedRows()));
     720        }
     721    }
     722
     723    class RemoveAction extends AbstractAction implements ListSelectionListener {
     724        public RemoveAction() {
     725            putValue(SHORT_DESCRIPTION,  tr("Remove the member in the current table row from this relation"));
     726            putValue(SMALL_ICON, ImageProvider.get("dialogs", "remove"));
     727            //putValue(NAME, tr("Remove"));
     728            Shortcut.registerShortcut("relationeditor:remove",
     729                    tr("Relation Editor: Remove"),
     730                    KeyEvent.VK_J,
     731                    Shortcut.GROUP_MNEMONIC);
     732            setEnabled(false);
     733        }
     734
     735        public void actionPerformed(ActionEvent e) {
     736            memberTableModel.remove(memberTable.getSelectedRows());
     737        }
     738
     739        public void valueChanged(ListSelectionEvent e) {
     740            setEnabled(memberTableModel.canRemove(memberTable.getSelectedRows()));
     741        }
     742    }
     743
     744    class OKAction extends AbstractAction {
     745        public OKAction() {
     746            putValue(SHORT_DESCRIPTION,  tr("Apply the updates and close the dialog"));
     747            putValue(SMALL_ICON, ImageProvider.get("ok"));
     748            putValue(NAME, tr("Apply"));
     749            setEnabled(true);
     750        }
     751
     752        public void actionPerformed(ActionEvent e) {
     753            applyChanges();
     754            setVisible(false);
     755        }
     756    }
     757
     758    class CancelAction extends AbstractAction {
     759        public CancelAction() {
     760            putValue(SHORT_DESCRIPTION,  tr("Cancel the updates and close the dialog"));
     761            putValue(SMALL_ICON, ImageProvider.get("cancel"));
     762            putValue(NAME, tr("Cancel"));
     763
     764            getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
     765            .put(KeyStroke.getKeyStroke("ESCAPE"), "ESCAPE");
     766            getRootPane().getActionMap().put("ESCAPE", this);
     767            setEnabled(true);
     768        }
     769
     770        public void actionPerformed(ActionEvent e) {
     771            setVisible(false);
     772        }
     773    }
     774
     775    class AddTagAction extends AbstractAction {
     776        public AddTagAction() {
     777            putValue(SHORT_DESCRIPTION,  tr("Add an empty tag"));
     778            putValue(SMALL_ICON, ImageProvider.get("dialogs","add"));
     779            //putValue(NAME, tr("Cancel"));
     780            setEnabled(true);
     781        }
     782        public void actionPerformed(ActionEvent e) {
     783            tagEditorModel.appendNewTag();
     784        }
     785    }
     786
     787    class DeleteTagAction extends AbstractAction implements ListSelectionListener{
     788        public DeleteTagAction() {
     789            putValue(SHORT_DESCRIPTION,  tr("Delete the currently selected tags"));
     790            putValue(SMALL_ICON, ImageProvider.get("dialogs","delete"));
     791            //putValue(NAME, tr("Cancel"));
     792            refreshEnabled();
     793        }
     794
     795        public void actionPerformed(ActionEvent e) {
     796            run();
     797        }
     798
     799        /**
     800         * delete a selection of tag names
     801         */
     802        protected void deleteTagNames() {
     803            int[] rows = tagTable.getSelectedRows();
     804            tagEditorModel.deleteTagNames(rows);
     805        }
     806
     807        /**
     808         * delete a selection of tag values
     809         */
     810        protected void deleteTagValues() {
     811            int[] rows = tagTable.getSelectedRows();
     812            tagEditorModel.deleteTagValues(rows);
     813        }
     814
     815        /**
     816         * delete a selection of tags
     817         */
     818        protected void deleteTags() {
     819            tagEditorModel.deleteTags(tagTable.getSelectedRows());
     820        }
     821
     822        public void run() {
     823            if (!isEnabled())
     824                return;
     825            if (tagTable.getSelectedColumnCount() == 1) {
     826                if (tagTable.getSelectedColumn() == 0) {
     827                    deleteTagNames();
     828                } else if (tagTable.getSelectedColumn() == 1) {
     829                    deleteTagValues();
     830                } else
     831                    // should not happen
     832                    //
     833                    throw new IllegalStateException("unexpected selected clolumn: getSelectedColumn() is " + tagTable.getSelectedColumn());
     834            } else if (tagTable.getSelectedColumnCount() == 2) {
     835                deleteTags();
    627836            }
    628             OsmPrimitive primitive = ds.getPrimitiveById(member.member.id);
    629             if (primitive != null) {
    630                 member.member = primitive;
     837            if (tagEditorModel.getRowCount() == 0) {
     838                tagEditorModel.ensureOneTag();
    631839            }
    632840        }
    633     }
    634 
    635     private void refreshTables() {
    636         // re-load property data
    637         int numLinked = 0;
    638 
    639         // re-load membership data
    640 
    641         memberData.setRowCount(0);
    642         for (int i=0; i<getClone().members.size(); i++) {
    643 
    644             // this whole section is aimed at finding out whether the
    645             // relation member is "linked" with the next, i.e. whether
    646             // (if both are ways) these ways are connected. It should
    647             // really produce a much more beautiful output (with a linkage
    648             // symbol somehow places between the two member lines!), and
    649             // it should cache results, so... FIXME ;-)
    650 
    651             RelationMember em = getClone().members.get(i);
    652             WayConnectionType link = WayConnectionType.none;
    653             RelationMember m = em;
    654             RelationMember way1 = null;
    655             RelationMember way2 = null;
    656             int depth = 0;
    657 
    658             while (m != null && depth < 10) {
    659                 if (m.member instanceof Way) {
    660                     way1 = m;
    661                     break;
    662                 } else if (m.member instanceof Relation) {
    663                     if (m.member == this.getRelation()) {
    664                         break;
    665                     }
    666                     m = ((Relation)m.member).lastMember();
    667                     depth++;
    668                 } else {
    669                     break;
    670                 }
    671             }
    672             if (way1 != null) {
    673                 int next = (i+1) % getClone().members.size();
    674                 while (next != i) {
    675                     m = getClone().members.get(next);
    676                     next = (next + 1) % getClone().members.size();
    677                     depth = 0;
    678                     while (m != null && depth < 10) {
    679                         if (m.member instanceof Way) {
    680                             way2 = m;
    681                             break;
    682                         } else if (m.member instanceof Relation) {
    683                             if (m.member == this.getRelation()) {
    684                                 break;
    685                             }
    686                             m = ((Relation)(m.member)).firstMember();
    687                             depth++;
    688                         } else {
    689                             break;
     841
     842        protected void refreshEnabled() {
     843            setEnabled(tagTable.getSelectedRowCount()>0 || tagTable.getSelectedColumnCount() > 0);
     844        }
     845
     846        public void valueChanged(ListSelectionEvent e) {
     847            refreshEnabled();
     848        }
     849    }
     850
     851    class DownlaodAction extends AbstractAction {
     852        public DownlaodAction() {
     853            putValue(SHORT_DESCRIPTION,   tr("Download all incomplete ways and nodes in relation"));
     854            putValue(SMALL_ICON, ImageProvider.get("dialogs","downloadincomplete"));
     855            putValue(NAME, tr("Download Members"));
     856            Shortcut.registerShortcut("relationeditor:downloadincomplete",
     857                    tr("Relation Editor: Download Members"),
     858                    KeyEvent.VK_K,
     859                    Shortcut.GROUP_MNEMONIC);
     860            setEnabled(true);
     861        }
     862        public void actionPerformed(ActionEvent e) {
     863            downloadRelationMembers();
     864        }
     865    }
     866
     867    class DownloadTask extends PleaseWaitRunnable {
     868        private boolean cancelled;
     869        private Exception lastException;
     870
     871        protected void setIndeterminateEnabled(final boolean enabled) {
     872            EventQueue.invokeLater(
     873                    new Runnable() {
     874                        public void run() {
     875                            Main.pleaseWaitDlg.setIndeterminate(enabled);
    690876                        }
    691877                    }
    692                     if (way2 != null) {
    693                         break;
     878            );
     879        }
     880
     881        public DownloadTask() {
     882            super(tr("Download relation members"), false /* don't ignore exception */);
     883        }
     884        @Override
     885        protected void cancel() {
     886            cancelled = true;
     887            OsmApi.getOsmApi().cancel();
     888        }
     889
     890        protected void showLastException() {
     891            String msg = lastException.getMessage();
     892            if (msg == null) {
     893                msg = lastException.toString();
     894            }
     895            JOptionPane.showMessageDialog(
     896                    null,
     897                    msg,
     898                    tr("Error"),
     899                    JOptionPane.ERROR_MESSAGE
     900            );
     901        }
     902
     903        @Override
     904        protected void finish() {
     905            if (cancelled) return;
     906            memberTableModel.updateMemberReferences(getLayer().data);
     907            if (lastException == null) return;
     908            showLastException();
     909        }
     910
     911        @Override
     912        protected void realRun() throws SAXException, IOException, OsmTransferException {
     913            try {
     914                Main.pleaseWaitDlg.setAlwaysOnTop(true);
     915                Main.pleaseWaitDlg.toFront();
     916                setIndeterminateEnabled(true);
     917                OsmServerObjectReader reader = new OsmServerObjectReader(getRelation().id, OsmPrimitiveType.RELATION, true);
     918                DataSet dataSet = reader.parseOsm();
     919                if (dataSet != null) {
     920                    final MergeVisitor visitor = new MergeVisitor(getLayer().data, dataSet);
     921                    visitor.merge();
     922
     923                    // copy the merged layer's data source info
     924                    for (DataSource src : dataSet.dataSources) {
     925                        getLayer().data.dataSources.add(src);
    694926                    }
     927                    getLayer().fireDataChange();
     928
     929                    if (visitor.getConflicts().isEmpty())
     930                        return;
     931                    getLayer().getConflicts().add(visitor.getConflicts());
     932                    JOptionPane op = new JOptionPane(
     933                            tr("There were {0} conflicts during import.",
     934                                    visitor.getConflicts().size()),
     935                                    JOptionPane.WARNING_MESSAGE
     936                    );
     937                    JDialog dialog = op.createDialog(Main.pleaseWaitDlg, tr("Conflicts in data"));
     938                    dialog.setAlwaysOnTop(true);
     939                    dialog.setModal(true);
     940                    dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
     941                    dialog.setVisible(true);
    695942                }
     943            } catch(Exception e) {
     944                if (cancelled) {
     945                    System.out.println(tr("Warning: ignoring exception because task is cancelled. Exception: {0}", e.toString()));
     946                    return;
     947                }
     948                lastException = e;
     949            } finally {
     950                Main.pleaseWaitDlg.setAlwaysOnTop(false);
     951                setIndeterminateEnabled(false);
    696952            }
    697             if (way2 != null) {
    698                 Node way1first = ((Way)(way1.member)).firstNode();
    699                 Node way1last = ((Way)(way1.member)).lastNode();
    700                 Node way2first = ((Way)(way2.member)).firstNode();
    701                 Node way2last = ((Way)(way2.member)).lastNode();
    702                 /*
    703                 if (way1.role.equals("forward")) {
    704                     way1first = null;
    705                 } else if (way1.role.equals("backward")) {
    706                     way1last = null;
    707                 }
    708                 if (way2.role.equals("forward")) {
    709                     way2last = null;
    710                 } else if (way2.role.equals("backward")) {
    711                     way2first = null;
    712                 }
    713                  */
    714                 if (way1first != null && way2first != null && way1first.equals(way2first)) {
    715                     link = WayConnectionType.tail_to_tail;
    716                 } else if (way1first != null && way2last != null && way1first.equals(way2last)) {
    717                     link = WayConnectionType.tail_to_head;
    718                 } else if (way1last != null && way2first != null && way1last.equals(way2first)) {
    719                     link = WayConnectionType.head_to_tail;
    720                 } else if (way1last != null && way2last != null && way1last.equals(way2last)) {
    721                     link = WayConnectionType.head_to_head;
    722                 }
    723 
    724                 // end of section to determine linkedness.
    725                 if (link != WayConnectionType.none)
    726                 {
    727                     ++numLinked;
    728                 }
    729 
    730             }
    731             memberData.addRow(new Object[]{em.role, em.member, link});
    732         }
    733         status.setText(tr("Members: {0} (linked: {1})", getClone().members.size(), numLinked));
    734     }
    735 
    736     private SideButton createButton(String name, String iconName, String tooltip, int mnemonic, ActionListener actionListener) {
    737         return
    738         new SideButton(name, iconName, "relationEditor",
    739                 tooltip,
    740                 Shortcut.registerShortcut("relationeditor:"+iconName,
    741                         tr("Relation Editor: {0}", name == null ? tooltip : name),
    742                         mnemonic,
    743                         Shortcut.GROUP_MNEMONIC),
    744                         actionListener
    745         );
    746     }
    747 
    748     private void addSelected() {
    749         for (OsmPrimitive p : Main.ds.getSelected()) {
    750             // ordered relations may have the same member multiple times.
    751             // TODO: visual indication of the fact that one is there more than once?
    752             RelationMember em = new RelationMember();
    753             em.member = p;
    754             em.role = "";
    755             // when working with ordered relations, we make an effort to
    756             // add the element before the first selected member.
    757             int[] rows = memberTable.getSelectedRows();
    758             if (rows.length > 0) {
    759                 getClone().members.add(rows[0], em);
    760             } else {
    761                 getClone().members.add(em);
    762             }
    763         }
    764         refreshTables();
    765     }
    766 
    767     private void deleteSelected() {
    768         for (OsmPrimitive p : Main.ds.getSelected()) {
    769             Relation c = new Relation(getClone());
    770             for (RelationMember rm : c.members) {
    771                 if (rm.member == p)
    772                 {
    773                     RelationMember mem = new RelationMember();
    774                     mem.role = rm.role;
    775                     mem.member = rm.member;
    776                     getClone().members.remove(mem);
    777                 }
    778             }
    779         }
    780         refreshTables();
    781     }
    782 
    783     private void moveMembers(int direction) {
    784         int[] rows = memberTable.getSelectedRows();
    785         if (rows.length == 0) return;
    786 
    787         // check if user attempted to move anything beyond the boundary of the list
    788         if (rows[0] + direction < 0) return;
    789         if (rows[rows.length-1] + direction >= getClone().members.size()) return;
    790 
    791         RelationMember m[] = new RelationMember[getClone().members.size()];
    792 
    793         // first move all selected rows from the member list into a new array,
    794         // displaced by the move amount
    795         for (Integer i: rows) {
    796             m[i+direction] = getClone().members.get(i);
    797             getClone().members.set(i, null);
    798         }
    799 
    800         // now fill the empty spots in the destination array with the remaining
    801         // elements.
    802         int i = 0;
    803         for (RelationMember rm : getClone().members) {
    804             if (rm != null) {
    805                 while (m[i] != null) {
    806                     i++;
    807                 }
    808                 m[i++] = rm;
    809             }
    810         }
    811 
    812         // and write the array back into the member list.
    813         getClone().members.clear();
    814         getClone().members.addAll(Arrays.asList(m));
    815         refreshTables();
    816         ListSelectionModel lsm = memberTable.getSelectionModel();
    817         lsm.setValueIsAdjusting(true);
    818         for (Integer j: rows) {
    819             lsm.addSelectionInterval(j + direction, j + direction);
    820         }
    821         lsm.setValueIsAdjusting(false);
    822     }
    823 
    824 
    825     /**
    826      * Asynchronously download the members of the currently edited relation
    827      *
    828      */
    829     private void downloadRelationMembers() {
    830 
    831         class DownloadTask extends PleaseWaitRunnable {
    832             private boolean cancelled;
    833             private Exception lastException;
    834 
    835             protected void setIndeterminateEnabled(final boolean enabled) {
    836                 EventQueue.invokeLater(
    837                         new Runnable() {
    838                             public void run() {
    839                                 Main.pleaseWaitDlg.setIndeterminate(enabled);
    840                             }
    841                         }
    842                 );
    843             }
    844 
    845             public DownloadTask() {
    846                 super(tr("Download relation members"), false /* don't ignore exception */);
    847             }
    848             @Override
    849             protected void cancel() {
    850                 cancelled = true;
    851                 OsmApi.getOsmApi().cancel();
    852             }
    853 
    854             protected void showLastException() {
    855                 String msg = lastException.getMessage();
    856                 if (msg == null) {
    857                     msg = lastException.toString();
    858                 }
    859                 JOptionPane.showMessageDialog(
    860                         null,
    861                         msg,
    862                         tr("Error"),
    863                         JOptionPane.ERROR_MESSAGE
    864                 );
    865             }
    866 
    867             @Override
    868             protected void finish() {
    869                 if (cancelled) return;
    870                 updateMemberReferencesInClone();
    871                 refreshTables();
    872                 if (lastException == null) return;
    873                 showLastException();
    874             }
    875 
    876             @Override
    877             protected void realRun() throws SAXException, IOException, OsmTransferException {
    878                 try {
    879                     Main.pleaseWaitDlg.setAlwaysOnTop(true);
    880                     Main.pleaseWaitDlg.toFront();
    881                     setIndeterminateEnabled(true);
    882                     OsmServerObjectReader reader = new OsmServerObjectReader(getClone().id, OsmPrimitiveType.RELATION, true);
    883                     DataSet dataSet = reader.parseOsm();
    884                     if (dataSet != null) {
    885                         final MergeVisitor visitor = new MergeVisitor(getLayer().data, dataSet);
    886                         visitor.merge();
    887 
    888                         // copy the merged layer's data source info
    889                         for (DataSource src : dataSet.dataSources) {
    890                             getLayer().data.dataSources.add(src);
    891                         }
    892                         getLayer().fireDataChange();
    893 
    894                         if (visitor.getConflicts().isEmpty())
    895                             return;
    896                         getLayer().getConflicts().add(visitor.getConflicts());
    897                         JOptionPane op = new JOptionPane(
    898                                 tr("There were {0} conflicts during import.",
    899                                         visitor.getConflicts().size()),
    900                                         JOptionPane.WARNING_MESSAGE
    901                         );
    902                         JDialog dialog = op.createDialog(Main.pleaseWaitDlg, tr("Conflicts in data"));
    903                         dialog.setAlwaysOnTop(true);
    904                         dialog.setModal(true);
    905                         dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
    906                         dialog.setVisible(true);
    907                     }
    908                 } catch(Exception e) {
    909                     if (cancelled) {
    910                         System.out.println(tr("Warning: ignoring exception because task is cancelled. Exception: {0}", e.toString()));
    911                         return;
    912                     }
    913                     lastException = e;
    914                 } finally {
    915                     Main.pleaseWaitDlg.setAlwaysOnTop(false);
    916                     setIndeterminateEnabled(false);
    917                 }
    918             }
    919         }
    920 
    921         boolean download = false;
    922         for (RelationMember member : getClone().members) {
    923             if (member.member.incomplete) {
    924                 download = true;
    925                 break;
    926             }
    927         }
    928         if (! download) return;
    929         Main.worker.submit(new DownloadTask());
    930     }
    931 
    932     @Override
    933     public void setVisible(boolean visible) {
    934         super.setVisible(visible);
    935         if (!visible) {
    936             dispose();
    937953        }
    938954    }
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationEditor.java

    r1783 r1790  
    4141     */
    4242    private Relation relation;
    43     private Relation clone;
    4443
    4544    /** the data layer the relation belongs to */
     
    103102        this.relation = relation;
    104103        this.layer = layer;
    105 
    106         if (relation == null) {
    107             // create a new relation
    108             this.clone = new Relation();
    109         } else {
    110             // edit an existing relation
    111             this.clone = new Relation(relation);
    112         }
    113104    }
    114105
     
    117108    }
    118109
    119     protected Relation getClone() {
    120         return clone;
    121     }
    122 
    123110    protected OsmDataLayer getLayer() {
    124111        return layer;
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/TagEditorModel.java

    r1770 r1790  
    99import java.util.Collection;
    1010import java.util.Comparator;
     11import java.util.HashMap;
    1112import java.util.List;
    1213import java.util.logging.Logger;
     
    285286            add(key,value);
    286287        }
     288        TagModel tag = new TagModel();
    287289        sort();
     290        tags.add(tag);
    288291        setDirty(false);
    289292    }
    290293
    291294
     295    /**
     296     * applies the current state of the tag editor model to a primitive
     297     *
     298     * @param primitive the primitive
     299     *
     300     */
    292301    public void applyToPrimitive(OsmPrimitive primitive) {
     302        if (primitive.keys == null) {
     303            primitive.keys = new HashMap<String, String>();
     304        } else {
     305            primitive.keys.clear();
     306        }
    293307        for (TagModel tag: tags) {
    294308            // tag still holds an unchanged list of different values for the same key.
  • trunk/src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java

    r1772 r1790  
    6262    private HashSet<Long> relations;
    6363    private HashSet<Long> missingPrimitives;
    64     private HashSet<Long> skippedWayIds;
    6564    private DataSet outputDataSet;
    6665
     
    320319        try {
    321320            final OsmReader osm = OsmReader.parseDataSetOsm(in, Main.pleaseWaitDlg);
    322             skippedWayIds.addAll(osm.getSkippedWayIds());
    323321            merge(osm.getDs());
    324         } catch(IOException e) {
    325             throw new OsmTransferException(e);
    326         } catch(SAXException e) {
     322        } catch(Exception e) {
    327323            throw new OsmTransferException(e);
    328324        }
     
    346342        try {
    347343            final OsmReader osm = OsmReader.parseDataSetOsm(in,Main.pleaseWaitDlg);
    348             skippedWayIds.addAll(osm.getSkippedWayIds());
    349344            merge(osm.getDs());
    350         } catch(IOException e) {
    351             throw new OsmTransferException(e);
    352         } catch(SAXException e) {
     345        } catch(Exception e) {
    353346            throw new OsmTransferException(e);
    354347        }
     
    440433    @Override
    441434    public DataSet parseOsm() throws OsmTransferException {
    442         skippedWayIds = new HashSet<Long>();
    443435        missingPrimitives = new HashSet<Long>();
    444436
     
    450442
    451443    /**
    452      * replies the set of {@see Way}s which were present in the data fetched from the
    453      * server but which were not included in the JOSM dataset because they referred
    454      * to nodes not present in the dataset
    455      *
    456      * @return  the set of ids of skipped ways
    457      */
    458     public Set<Long> getSkippedWays() {
    459         return skippedWayIds;
    460     }
    461 
    462     /**
    463444     * replies the set of ids of all primitives for which a fetch request to the
    464445     * server was submitted but which are not available from the server (the server
  • trunk/src/org/openstreetmap/josm/io/OsmImporter.java

    r1772 r1790  
    5151        Main.main.addLayer(layer);
    5252        layer.fireDataChange();
    53         if (osm.getParseNotes().length() != 0) {
    54             /* display at most five lines */
    55             String notes = osm.getParseNotes();
    56             int j = 0;
    57             for (int i = 0; i < 5; i++) {
    58                 j = notes.indexOf('\n', j + 1);
    59             }
    60             j = j >= 0 ? j : notes.length();
    61             JOptionPane.showMessageDialog(Main.parent, notes.substring(0, j));
    62         }
    6353    }
    6454}
  • trunk/src/org/openstreetmap/josm/io/OsmReader.java

    r1772 r1790  
    1616import java.util.Set;
    1717import java.util.Map.Entry;
    18 
     18import java.util.logging.Logger;
     19
     20import javax.swing.SwingUtilities;
    1921import javax.xml.parsers.ParserConfigurationException;
    2022import javax.xml.parsers.SAXParserFactory;
    2123
    22 import org.openstreetmap.josm.Main;
    2324import org.openstreetmap.josm.data.Bounds;
    2425import org.openstreetmap.josm.data.coor.LatLon;
     
    5051 */
    5152public class OsmReader {
     53    static private final Logger logger = Logger.getLogger(OsmReader.class.getName());
    5254
    5355    /**
     
    5658    private DataSet ds = new DataSet();
    5759    public DataSet getDs() { return ds; }
    58 
    59     /**
    60      * Record warnings.  If there were any data inconsistencies, append
    61      * a newline-terminated string.
    62      */
    63     private String parseNotes = new String();
    64     private int parseNotesCount = 0;
    65     public String getParseNotes() {
    66         return parseNotes;
    67     }
    68 
    69     /** the list of ids of skipped {@see Way}s, i.e. ways which referred to nodes
    70      * not included in the parsed data
    71      */
    72     private Set<Long> skippedWayIds = new HashSet<Long>();
    7360
    7461    /**
     
    350337    protected void createWays() {
    351338        for (Entry<OsmPrimitiveData, Collection<Long>> e : ways.entrySet()) {
    352             Way w = new Way();
     339            Way w = new Way(e.getKey().id);
    353340            boolean failed = false;
    354341            for (long id : e.getValue()) {
    355342                Node n = findNode(id);
    356343                if (n == null) {
    357                     /* don't report ALL of them, just a few */
    358                     if (parseNotesCount++ < 6) {
    359                         parseNotes += tr("Skipping a way because it includes a node that doesn''t exist: {0}\n", id);
    360                     } else if (parseNotesCount == 6) {
    361                         parseNotes += "...\n";
    362                     }
    363344                    failed = true;
    364345                    break;
     
    367348            }
    368349            if (failed) {
    369                 skippedWayIds.add(e.getKey().id);
    370                 continue;
    371             }
    372             e.getKey().copyTo(w);
    373             adder.visit(w);
    374         }
    375 
     350                logger.warning(tr("marked way {0} incomplete because referred nodes are missing in the loaded data", e.getKey().id));
     351                e.getKey().copyTo(w);
     352                w.incomplete = true;
     353                w.nodes.clear();
     354            } else {
     355                e.getKey().copyTo(w);
     356                w.incomplete = false;
     357                adder.visit(w);
     358            }
     359        }
    376360    }
    377361
     
    475459    }
    476460
    477     public static OsmReader parseDataSetOsm(InputStream source,PleaseWaitDialog pleaseWaitDlg) throws SAXException, IOException {
     461    public static OsmReader parseDataSetOsm(InputStream source, final PleaseWaitDialog pleaseWaitDlg) throws SAXException, IOException {
    478462        OsmReader osm = new OsmReader();
    479463
     
    487471        }
    488472
    489         Main.pleaseWaitDlg.currentAction.setText(tr("Prepare OSM data..."));
    490         Main.pleaseWaitDlg.setIndeterminate(true);
     473        SwingUtilities.invokeLater(
     474                new Runnable() {
     475                    public void run() {
     476                        pleaseWaitDlg.currentAction.setText(tr("Prepare OSM data..."));
     477                        pleaseWaitDlg.setIndeterminate(true);
     478                    }
     479                }
     480        );
    491481
    492482        for (Node n : osm.nodes.values()) {
     
    508498            }
    509499
    510         Main.pleaseWaitDlg.setIndeterminate(false);
    511         Main.pleaseWaitDlg.progress.setValue(0);
     500        SwingUtilities.invokeLater(
     501                new Runnable() {
     502                    public void run() {
     503                        pleaseWaitDlg.setIndeterminate(false);
     504                        pleaseWaitDlg.progress.setValue(0);
     505                    }
     506                }
     507        );
    512508
    513509        return osm;
    514510    }
    515 
    516     /**
    517      * replies a set of ids of skipped {@see Way}s, i.e. ways which were included in the downloaded
    518      * data but which referred to nodes <strong>not</strong>  available in the downloaded data
    519      *
    520      * @return the set of ids
    521      */
    522     public Set<Long> getSkippedWayIds() {
    523         return skippedWayIds;
    524     }
    525511}
  • trunk/src/org/openstreetmap/josm/io/OsmServerHistoryReader.java

    r1670 r1790  
    7474            HistoryDataSet data = reader.parse(Main.pleaseWaitDlg);
    7575            return data;
    76         } catch (IOException e) {
    77             if (cancel)
    78                 return null;
    79             throw new OsmTransferException(e);
    80         } catch (SAXException e) {
    81             throw new OsmTransferException(e);
    8276        } catch(OsmTransferException e) {
    8377            throw e;
  • trunk/src/org/openstreetmap/josm/io/OsmServerLocationReader.java

    r1772 r1790  
    2424    @Override
    2525    public DataSet parseOsm() throws OsmTransferException {
     26        InputStream in = null;
    2627        try {
    2728            Main.pleaseWaitDlg.progress.setValue(0);
    2829            Main.pleaseWaitDlg.currentAction.setText(tr("Contacting Server..."));
    2930
    30             final InputStream in = getInputStreamRaw(url, Main.pleaseWaitDlg);
     31            in = getInputStreamRaw(url, Main.pleaseWaitDlg);
    3132            if (in == null)
    3233                return null;
    3334            Main.pleaseWaitDlg.currentAction.setText(tr("Downloading OSM data..."));
    34             final DataSet data = OsmReader.parseDataSet(in, Main.pleaseWaitDlg);
    35             in.close();
    36             activeConnection = null;
    37             return data;
    38         } catch (IOException e) {
    39             if (cancel)
    40                 return null;
    41             throw new OsmTransferException(e);
    42         } catch (SAXException e) {
    43             throw new OsmTransferException(e);
     35            return OsmReader.parseDataSet(in, Main.pleaseWaitDlg);
    4436        } catch(OsmTransferException e) {
    4537            throw e;
     
    4840                return null;
    4941            throw new OsmTransferException(e);
     42        } finally {
     43            try {
     44                if (in != null) {
     45                    in.close();
     46                }
     47                activeConnection = null;
     48            } catch(Exception e) {/* ignore it */}
    5049        }
    5150    }
  • trunk/src/org/openstreetmap/josm/io/OsmServerObjectReader.java

    r1772 r1790  
    66import java.io.IOException;
    77import java.io.InputStream;
    8 
    9 import javax.swing.JOptionPane;
    108
    119import org.openstreetmap.josm.Main;
     
    5149            final DataSet data = osm.getDs();
    5250
    53             if (osm.getParseNotes().length() != 0) {
    54                 JOptionPane.showMessageDialog(Main.parent, osm.getParseNotes());
    55             }
    5651            in.close();
    5752            activeConnection = null;
Note: See TracChangeset for help on using the changeset viewer.