Changeset 2563 in josm for trunk/src/org


Ignore:
Timestamp:
2009-12-03T19:02:25+01:00 (14 years ago)
Author:
Gubaer
Message:

fixed #3400: relation editor: improvement to highlight an element
fixed #3873: Feature request: download selected elements in relation editor
New: Dbl-Click in member table to set the map selection to this member
New: Ctrl-Dbl-Clik in member table to add the member to the the map selection
New: Download selected incomplete members only

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

Legend:

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

    r2512 r2563  
    22package org.openstreetmap.josm.actions;
    33
     4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
    45import static org.openstreetmap.josm.tools.I18n.tr;
    5 import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
    66
    77import java.awt.event.ActionEvent;
     
    8686            for (File f : files) {
    8787                if (cancelled) return;
    88                 getProgressMonitor().subTask(tr("Opening file ''{0}'' ...", f.getAbsolutePath()));
     88                getProgressMonitor().indeterminateSubTask(tr("Opening file ''{0}'' ...", f.getAbsolutePath()));
    8989                try {
    9090                    System.out.println("Open file: " + f.getAbsolutePath() + " (" + f.length() + " bytes)");
  • trunk/src/org/openstreetmap/josm/data/osm/DataSetMerger.java

    r2512 r2563  
    3838     * to relation members) after the first phase of merging
    3939     */
    40     private Set<Long> childrenToMerge;
     40    private Set<PrimitiveId> objectsWithChildrenToMerge;
    4141    private Set<OsmPrimitive> deletedObjectsToUnlink;
    4242
     
    5757        conflicts = new ConflictCollection();
    5858        mergedMap = new HashMap<Long, Long>();
    59         childrenToMerge = new HashSet<Long>();
     59        objectsWithChildrenToMerge = new HashSet<PrimitiveId>();
    6060        deletedObjectsToUnlink = new HashSet<OsmPrimitive>();
    6161    }
     
    7575     * @param source  the other primitive
    7676     */
    77     protected <P extends OsmPrimitive> void mergePrimitive(P source) {
     77    protected void mergePrimitive(OsmPrimitive source) {
    7878        if (!source.isNew() ) {
    7979            // try to merge onto a matching primitive with the same
     
    112112                        target.setTimestamp(source.getTimestamp());
    113113                        target.setModified(source.isModified());
    114                         childrenToMerge.add(source.getUniqueId());
     114                        objectsWithChildrenToMerge.add(source.getPrimitiveId());
    115115                    }
    116116                    return;
     
    131131        targetDataSet.addPrimitive(target);
    132132        mergedMap.put(source.getUniqueId(), target.getUniqueId());
    133         childrenToMerge.add(source.getUniqueId());
     133        objectsWithChildrenToMerge.add(source.getPrimitiveId());
    134134    }
    135135
     
    154154
    155155    /**
     156     * A way in the target dataset might be incomplete because at least of of its nodes is incomplete.
     157     * The nodes might have become complete because a complete node was merged onto into in the
     158     * merge operation.
     159     *
     160     * This method loops over all parent ways of such nodes and turns them into complete ways
     161     * if necessary.
     162     *
     163     * @param other
     164     */
     165    protected void fixIncompleteParentWays(Node other) {
     166        Node myNode = (Node)getMergeTarget(other);
     167        if (myNode == null)
     168            throw new RuntimeException(tr("Missing merge target for node with id {0}", other.getUniqueId()));
     169        if (myNode.incomplete || myNode.isDeleted() || !myNode.isVisible()) return;
     170        wayloop: for (Way w: OsmPrimitive.getFilteredList(myNode.getReferrers(), Way.class)) {
     171            if (w.isDeleted() || ! w.isVisible() || ! w.incomplete) {
     172                continue;
     173            }
     174            for (Node n: w.getNodes()) {
     175                if (n.incomplete) {
     176                    continue wayloop;
     177                }
     178            }
     179            // all nodes are complete - set the way complete too
     180            w.incomplete = false;
     181        }
     182    }
     183
     184    /**
    156185     * Postprocess the dataset and fix all merged references to point to the actual
    157186     * data.
     
    159188    public void fixReferences() {
    160189        for (Way w : sourceDataSet.getWays()) {
    161             if (!conflicts.hasConflictForTheir(w) && childrenToMerge.contains(w.getUniqueId())) {
     190            if (!conflicts.hasConflictForTheir(w) && objectsWithChildrenToMerge.contains(w.getPrimitiveId())) {
    162191                mergeNodeList(w);
    163192                fixIncomplete(w);
     
    165194        }
    166195        for (Relation r : sourceDataSet.getRelations()) {
    167             if (!conflicts.hasConflictForTheir(r) && childrenToMerge.contains(r.getUniqueId())) {
     196            if (!conflicts.hasConflictForTheir(r) && objectsWithChildrenToMerge.contains(r.getPrimitiveId())) {
    168197                mergeRelationMembers(r);
    169198            }
     
    175204            targetDataSet.unlinkReferencesToPrimitive(target);
    176205        }
     206        // objectsWithChildrenToMerge also includes complete nodes which have
     207        // been merged into their incomplete equivalents.
     208        //
     209        for (PrimitiveId id: objectsWithChildrenToMerge) {
     210            if (!id.getType().equals(OsmPrimitiveType.NODE)) {
     211                continue;
     212            }
     213            Node n = (Node)sourceDataSet.getPrimitiveById(id);
     214            if (!conflicts.hasConflictForTheir(n)) {
     215                fixIncompleteParentWays(n);
     216            }
     217        }
     218
    177219    }
    178220
     
    269311            //
    270312            target.mergeFrom(source);
    271             childrenToMerge.add(source.getUniqueId());
     313            objectsWithChildrenToMerge.add(source.getPrimitiveId());
    272314        } else if (!target.incomplete && source.incomplete) {
    273315            // target is complete and source is incomplete
     
    282324            // otherwise too many conflicts when refreshing from the server
    283325        } else if (target.isDeleted() != source.isDeleted()) {
    284             // differences in deleted state have to be resolved manually
     326            // differences in deleted state have to be resolved manually. This can
     327            // happen if one layer is merged onto another layer
    285328            //
    286329            conflicts.add(target,source);
     
    288331            // target not modified. We can assume that source is the most recent version.
    289332            // clone it into target. But check first, whether source is deleted. if so,
    290             // make sure that target is not referenced anymore in myDataSet.
     333            // make sure that target is not referenced any more in myDataSet.
    291334            //
    292335            if (source.isDeleted()) {
     
    294337            }
    295338            target.mergeFrom(source);
    296             childrenToMerge.add(source.getUniqueId());
     339            objectsWithChildrenToMerge.add(source.getPrimitiveId());
    297340        } else if (! target.isModified() && !source.isModified() && target.getVersion() == source.getVersion()) {
    298341            // both not modified. Keep mine
     
    302345            //
    303346            target.mergeFrom(source);
    304             childrenToMerge.add(source.getUniqueId());
     347            objectsWithChildrenToMerge.add(source.getPrimitiveId());
    305348        } else if (target.isModified() && ! source.isModified() && target.getVersion() == source.getVersion()) {
    306349            // target is same as source but target is modified
     
    318361            target.mergeFrom(source);
    319362            target.setModified(true);
    320             childrenToMerge.add(source.getUniqueId());
     363            objectsWithChildrenToMerge.add(source.getPrimitiveId());
    321364        }
    322365        return true;
  • trunk/src/org/openstreetmap/josm/gui/DefaultNameFormatter.java

    r2512 r2563  
    77import java.util.ArrayList;
    88import java.util.Arrays;
     9import java.util.Collections;
    910import java.util.HashSet;
    1011import java.util.List;
     
    199200        return tr("Changeset {0}",changeset.getId());
    200201    }
     202
     203    /**
     204     * Builds a default tooltip text for the primitive <code>primitive</code>.
     205     *
     206     * @param primitive the primitmive
     207     * @return the tooltip text
     208     */
     209    public String buildDefaultToolTip(OsmPrimitive primitive) {
     210        StringBuilder sb = new StringBuilder();
     211        sb.append("<html>");
     212        sb.append("<strong>id</strong>=")
     213        .append(primitive.getId())
     214        .append("<br>");
     215        ArrayList<String> keyList = new ArrayList<String>(primitive.keySet());
     216        Collections.sort(keyList);
     217        for (int i = 0; i < keyList.size(); i++) {
     218            if (i > 0) {
     219                sb.append("<br>");
     220            }
     221            String key = keyList.get(i);
     222            sb.append("<strong>")
     223            .append(key)
     224            .append("</strong>")
     225            .append("=");
     226            String value = primitive.get(key);
     227            while(value.length() != 0) {
     228                sb.append(value.substring(0,Math.min(50, value.length())));
     229                if (value.length() > 50) {
     230                    sb.append("<br>");
     231                    value = value.substring(50);
     232                } else {
     233                    value = "";
     234                }
     235            }
     236        }
     237        sb.append("</html>");
     238        return sb.toString();
     239    }
    201240}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/RelationListDialog.java

    r2514 r2563  
    4343import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
    4444import org.openstreetmap.josm.gui.SideButton;
    45 import org.openstreetmap.josm.gui.dialogs.relation.GenericRelationEditor;
     45import org.openstreetmap.josm.gui.dialogs.relation.DownloadRelationTask;
    4646import org.openstreetmap.josm.gui.dialogs.relation.RelationEditor;
    4747import org.openstreetmap.josm.gui.layer.DataChangeListener;
     
    248248                displaylist.setSelectedIndex(index);
    249249            }
    250             popupMenu.show(RelationListDialog.this, p.x, p.y-3);
     250            popupMenu.show(displaylist, p.x, p.y-3);
    251251        }
    252252        @Override public void mousePressed(MouseEvent e) {
     
    500500            if (relations.isEmpty())
    501501                return;
    502             Main.worker.submit(new GenericRelationEditor.DownloadTask(
     502            Main.worker.submit(new DownloadRelationTask(
    503503                    model.getSelectedNonNewRelations(),
    504                     Main.map.mapView.getEditLayer(), null));
     504                    Main.map.mapView.getEditLayer())
     505            );
    505506        }
    506507    }
  • trunk/src/org/openstreetmap/josm/gui/dialogs/SelectionListDialog.java

    r2243 r2563  
    169169        BasicArrowButton arrowButton = new BasicArrowButton(SwingConstants.SOUTH, null, null, Color.BLACK, null);
    170170        arrowButton.setBorder(BorderFactory.createEmptyBorder());
    171         //        selectionHistoryMenuButton.setContentAreaFilled(false);
    172         //        selectionHistoryMenuButton.setOpaque(false);
    173         //        selectionHistoryMenuButton.setBorderPainted(false);
    174         //        selectionHistoryMenuButton.setBackground(null);
    175171        parentButton.setLayout(new BorderLayout());
    176172        parentButton.add(arrowButton, BorderLayout.EAST);
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java

    r2512 r2563  
    55
    66import java.awt.BorderLayout;
    7 import java.awt.Dialog;
    87import java.awt.Dimension;
    98import java.awt.FlowLayout;
    109import java.awt.GridBagConstraints;
    1110import java.awt.GridBagLayout;
    12 import java.awt.Insets;
    1311import java.awt.event.ActionEvent;
    1412import java.awt.event.FocusAdapter;
     
    1917import java.awt.event.WindowAdapter;
    2018import java.awt.event.WindowEvent;
    21 import java.io.IOException;
     19import java.beans.PropertyChangeEvent;
     20import java.beans.PropertyChangeListener;
    2221import java.util.ArrayList;
    2322import java.util.Collection;
     
    3029import javax.swing.AbstractAction;
    3130import javax.swing.BorderFactory;
    32 import javax.swing.JButton;
    3331import javax.swing.JComponent;
    3432import javax.swing.JLabel;
     
    3836import javax.swing.JSplitPane;
    3937import javax.swing.JTabbedPane;
    40 import javax.swing.JTable;
     38import javax.swing.JToolBar;
    4139import javax.swing.KeyStroke;
    42 import javax.swing.SwingUtilities;
    4340import javax.swing.event.ChangeEvent;
    4441import javax.swing.event.ChangeListener;
     
    5754import org.openstreetmap.josm.data.osm.DataSet;
    5855import org.openstreetmap.josm.data.osm.OsmPrimitive;
    59 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
    6056import org.openstreetmap.josm.data.osm.Relation;
    6157import org.openstreetmap.josm.data.osm.RelationMember;
    6258import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
    6359import org.openstreetmap.josm.gui.DefaultNameFormatter;
    64 import org.openstreetmap.josm.gui.ExceptionDialogUtil;
    6560import org.openstreetmap.josm.gui.HelpAwareOptionPane;
    66 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
    6761import org.openstreetmap.josm.gui.SideButton;
    6862import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
     
    7064import org.openstreetmap.josm.gui.help.HelpUtil;
    7165import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    72 import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor;
    73 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
    7466import org.openstreetmap.josm.gui.tagging.AutoCompletingTextField;
    7567import org.openstreetmap.josm.gui.tagging.TagEditorPanel;
    7668import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionCache;
    7769import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList;
    78 import org.openstreetmap.josm.io.OsmServerBackreferenceReader;
    79 import org.openstreetmap.josm.io.OsmServerObjectReader;
    80 import org.openstreetmap.josm.io.OsmTransferException;
    8170import org.openstreetmap.josm.tools.ImageProvider;
    8271import org.openstreetmap.josm.tools.Shortcut;
    83 import org.xml.sax.SAXException;
    8472
    8573/**
     
    125113        //
    126114        memberTableModel = new MemberTableModel(getLayer());
     115        DataSet.selListeners.add(memberTableModel);
     116        getLayer().data.addDataSetListener(memberTableModel);
     117        getLayer().listenerDataChanged.add(memberTableModel);
    127118        selectionTableModel = new SelectionTableModel(getLayer());
     119        DataSet.selListeners.add(selectionTableModel);
    128120        referrerModel = new ReferringRelationsBrowserModel(relation);
    129121
     
    173165        );
    174166
     167        getContentPane().add(buildToolBar(), BorderLayout.NORTH);
    175168        getContentPane().add(tabbedPane, BorderLayout.CENTER);
    176169        getContentPane().add(buildOkCancelButtonPanel(), BorderLayout.SOUTH);
     
    189182        memberTableModel.setSelectedMembers(selectedMembers);
    190183        HelpUtil.setHelpContext(getRootPane(),ht("/Dialog/RelationEditor"));
     184    }
     185
     186    /**
     187     * Creates the toolbar
     188     *
     189     * @return the toolbar
     190     */
     191    protected JToolBar buildToolBar() {
     192        JToolBar tb  = new JToolBar();
     193        tb.setFloatable(false);
     194        tb.add(new ApplyAction());
     195        tb.add(new DuplicateRelationAction());
     196        DeleteCurrentRelationAction deleteAction = new DeleteCurrentRelationAction();
     197        addPropertyChangeListener(deleteAction);
     198        tb.add(deleteAction);
     199        return tb;
    191200    }
    192201
     
    280289        pnl.add(scrollPane, gc);
    281290
     291        // --- role editing
     292        JPanel p3 = new JPanel(new FlowLayout(FlowLayout.LEFT));
     293        p3.add(new JLabel(tr("Apply Role:")));
     294        tfRole = new AutoCompletingTextField(10);
     295        tfRole.setToolTipText(tr("Enter a role and apply it to the selected relation members"));
     296        tfRole.addFocusListener(new FocusAdapter() {
     297            @Override
     298            public void focusGained(FocusEvent e) {
     299                tfRole.selectAll();
     300            }
     301        });
     302        tfRole.setAutoCompletionList(new AutoCompletionList());
     303        tfRole.addFocusListener(
     304                new FocusAdapter() {
     305                    @Override
     306                    public void focusGained(FocusEvent e) {
     307                        AutoCompletionList list = tfRole.getAutoCompletionList();
     308                        AutoCompletionCache.getCacheForLayer(Main.main.getEditLayer()).populateWithMemberRoles(list);
     309                    }
     310                }
     311        );
     312        p3.add(tfRole);
     313        SetRoleAction setRoleAction = new SetRoleAction();
     314        memberTableModel.getSelectionModel().addListSelectionListener(setRoleAction);
     315        tfRole.getDocument().addDocumentListener(setRoleAction);
     316        tfRole.addActionListener(setRoleAction);
     317        memberTableModel.getSelectionModel().addListSelectionListener(
     318                new ListSelectionListener() {
     319                    public void valueChanged(ListSelectionEvent e) {
     320                        tfRole.setEnabled(memberTable.getSelectedRowCount() > 0);
     321                    }
     322                }
     323        );
     324        tfRole.setEnabled(memberTable.getSelectedRowCount() > 0);
     325
     326        gc.gridx = 1;
     327        gc.gridy = 2;
     328        gc.fill = GridBagConstraints.BOTH;
     329        gc.anchor = GridBagConstraints.CENTER;
     330        gc.weightx = 1.0;
     331        gc.weighty = 0.0;
     332        pnl.add(p3, gc);
     333
    282334        JPanel pnl2 = new JPanel();
    283335        pnl2.setLayout(new GridBagLayout());
     
    326378        pnl3.setLayout(new BorderLayout());
    327379        pnl3.add(splitPane, BorderLayout.CENTER);
    328         pnl3.add(buildButtonPanel(), BorderLayout.SOUTH);
    329380        return pnl3;
    330381    }
     
    338389        JPanel pnl = new JPanel();
    339390        pnl.setLayout(new BorderLayout());
    340         JTable tbl = new JTable(selectionTableModel, new SelectionTableColumnModel(memberTableModel));
    341         tbl.setEnabled(false);
     391        SelectionTable tbl = new SelectionTable(selectionTableModel, new SelectionTableColumnModel(memberTableModel));
     392        tbl.setMemberTableModel(memberTableModel);
    342393        JScrollPane pane = new JScrollPane(tbl);
    343394        pnl.add(pane, BorderLayout.CENTER);
     
    371422     * @return
    372423     */
    373     protected JPanel buildLeftButtonPanel() {
    374         JPanel pnl = new JPanel();
    375         pnl.setLayout(new GridBagLayout());
    376 
    377         GridBagConstraints gc = new GridBagConstraints();
    378         gc.gridx = 0;
    379         gc.gridy = 0;
    380         gc.gridheight = 1;
    381         gc.gridwidth = 1;
    382         gc.insets = new Insets(0, 5, 0, 5);
    383         gc.fill = GridBagConstraints.HORIZONTAL;
    384         gc.anchor = GridBagConstraints.CENTER;
    385         gc.weightx = 0.0;
    386         gc.weighty = 0.0;
    387 
    388         // -----
    389         gc.gridy = 0;
     424    protected JToolBar buildLeftButtonPanel() {
     425        JToolBar tb = new JToolBar();
     426        tb.setOrientation(JToolBar.VERTICAL);
     427        tb.setFloatable(false);
     428
     429        // -- move up action
    390430        MoveUpAction moveUpAction = new MoveUpAction();
    391431        memberTableModel.getSelectionModel().addListSelectionListener(moveUpAction);
    392         pnl.add(new JButton(moveUpAction), gc);
    393 
    394         // -----
    395         gc.gridy = 1;
     432        tb.add(moveUpAction);
     433
     434        // -- move down action
    396435        MoveDownAction moveDownAction = new MoveDownAction();
    397436        memberTableModel.getSelectionModel().addListSelectionListener(moveDownAction);
    398         pnl.add(new JButton(moveDownAction), gc);
     437        tb.add(moveDownAction);
     438
     439        tb.addSeparator();
    399440
    400441        // -- edit action
    401         gc.gridy = 2;
    402442        EditAction editAction = new EditAction();
    403443        memberTableModel.getSelectionModel().addListSelectionListener(editAction);
    404         pnl.add(new JButton(editAction),gc);
    405 
    406         // ------
    407         gc.gridy = 3;
     444        tb.add(editAction);
     445
     446        // -- delete action
    408447        RemoveAction removeSelectedAction = new RemoveAction();
    409448        memberTable.getSelectionModel().addListSelectionListener(removeSelectedAction);
    410         pnl.add(new JButton(removeSelectedAction), gc);
    411 
    412         // ------
    413         gc.gridy = 4;
    414         SelectPrimitivesForSelectedMembersAction selectAction = new SelectPrimitivesForSelectedMembersAction();
    415         memberTable.getSelectionModel().addListSelectionListener(selectAction);
    416         pnl.add(new JButton(selectAction), gc);
    417 
    418         // ------
    419         gc.gridy = 5;
     449        tb.add(removeSelectedAction);
     450
     451        tb.addSeparator();
     452        // -- sort action
    420453        SortAction sortAction = new SortAction();
    421         pnl.add(new JButton(sortAction), gc);
    422 
    423         // ------
    424         // just grab the remaining space
    425         gc.gridy = 6;
    426         gc.weighty = 1.0;
    427         gc.fill = GridBagConstraints.BOTH;
    428         pnl.add(new JPanel(), gc);
    429         return pnl;
     454        tb.add(sortAction);
     455
     456        tb.addSeparator();
     457
     458        // -- download action
     459        DownloadIncompleteMembersAction downloadIncompleteMembersAction = new DownloadIncompleteMembersAction();
     460        memberTable.getModel().addTableModelListener(downloadIncompleteMembersAction);
     461        tb.add(downloadIncompleteMembersAction);
     462
     463        // -- download selected action
     464        DownloadSelectedIncompleteMembersAction downloadSelectedIncompleteMembersAction = new DownloadSelectedIncompleteMembersAction();
     465        memberTable.getModel().addTableModelListener(downloadSelectedIncompleteMembersAction);
     466        memberTable.getSelectionModel().addListSelectionListener(downloadSelectedIncompleteMembersAction);
     467        tb.add(downloadSelectedIncompleteMembersAction);
     468
     469        return tb;
    430470    }
    431471
     
    435475     * @return
    436476     */
    437     protected JPanel buildSelectionControlButtonPanel() {
    438         JPanel pnl = new JPanel();
    439         pnl.setLayout(new GridBagLayout());
    440 
    441         GridBagConstraints gc = new GridBagConstraints();
    442         gc.gridx = 0;
    443         gc.gridy = 0;
    444         gc.gridheight = 1;
    445         gc.gridwidth = 1;
    446         gc.insets = new Insets(0, 5, 0, 5);
    447         gc.fill = GridBagConstraints.HORIZONTAL;
    448         gc.anchor = GridBagConstraints.CENTER;
    449         gc.weightx = 0.0;
    450         gc.weighty = 0.0;
     477    protected JToolBar buildSelectionControlButtonPanel() {
     478        JToolBar tb = new JToolBar(JToolBar.VERTICAL);
     479        tb.setFloatable(false);
     480
     481
     482        // -- add at end action
    451483        AddSelectedAtEndAction addSelectedAtEndAction = new AddSelectedAtEndAction();
    452484        selectionTableModel.addTableModelListener(addSelectedAtEndAction);
    453         pnl.add(new JButton(addSelectedAtEndAction), gc);
    454 
    455         // -----
    456         gc.gridy = 1;
     485        tb.add(addSelectedAtEndAction);
     486
     487        // -- select members action
    457488        SelectedMembersForSelectionAction selectMembersForSelectionAction = new SelectedMembersForSelectionAction();
    458489        selectionTableModel.addTableModelListener(selectMembersForSelectionAction);
    459490        memberTableModel.addTableModelListener(selectMembersForSelectionAction);
    460         pnl.add(new JButton(selectMembersForSelectionAction), gc);
    461 
    462         // -----
    463         gc.gridy = 2;
     491        tb.add(selectMembersForSelectionAction);
     492
     493        tb.addSeparator();
     494
     495        // -- remove selected action
    464496        RemoveSelectedAction removeSelectedAction = new RemoveSelectedAction();
    465497        selectionTableModel.addTableModelListener(removeSelectedAction);
    466         pnl.add(new JButton(removeSelectedAction), gc);
    467 
    468         // ------
    469         // just grab the remaining space
    470         gc.gridy = 3;
    471         gc.weighty = 1.0;
    472         gc.fill = GridBagConstraints.BOTH;
    473         pnl.add(new JPanel(), gc);
    474 
    475         // -----
    476         gc.gridy = 4;
    477         gc.weighty = 0.0;
     498        tb.add(removeSelectedAction);
     499
     500        // -- select action
     501        SelectPrimitivesForSelectedMembersAction selectAction = new SelectPrimitivesForSelectedMembersAction();
     502        memberTable.getSelectionModel().addListSelectionListener(selectAction);
     503        tb.add(selectAction);
     504
     505        tb.addSeparator();
     506
     507        // -- add at start action
    478508        AddSelectedAtStartAction addSelectionAction = new AddSelectedAtStartAction();
    479509        selectionTableModel.addTableModelListener(addSelectionAction);
    480         pnl.add(new JButton(addSelectionAction), gc);
    481 
    482         // -----
    483         gc.gridy = 5;
     510        tb.add(addSelectionAction);
     511
     512        // -- add before selected action
    484513        AddSelectedBeforeSelection addSelectedBeforeSelectionAction = new AddSelectedBeforeSelection();
    485514        selectionTableModel.addTableModelListener(addSelectedBeforeSelectionAction);
    486515        memberTableModel.getSelectionModel().addListSelectionListener(addSelectedBeforeSelectionAction);
    487         pnl.add(new JButton(addSelectedBeforeSelectionAction), gc);
    488 
    489         // -----
    490         gc.gridy = 6;
     516        tb.add(addSelectedBeforeSelectionAction);
     517
     518        // -- add after selected action
    491519        AddSelectedAfterSelection addSelectedAfterSelectionAction = new AddSelectedAfterSelection();
    492520        selectionTableModel.addTableModelListener(addSelectedAfterSelectionAction);
    493521        memberTableModel.getSelectionModel().addListSelectionListener(addSelectedAfterSelectionAction);
    494         pnl.add(new JButton(addSelectedAfterSelectionAction), gc);
    495 
    496         return pnl;
    497     }
    498 
    499     /**
    500      * Creates the buttons for the basic editing layout
    501      * @return {@see JPanel} with basic buttons
    502      */
    503     protected JPanel buildButtonPanel() {
    504         JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
    505 
    506         // --- download members
    507         buttonPanel.add(new SideButton(new DownlaodAction()));
    508 
    509         // --- role editing
    510         buttonPanel.add(new JLabel(tr("Role:")));
    511         tfRole = new AutoCompletingTextField(10);
    512         tfRole.addFocusListener(new FocusAdapter() {
    513             @Override
    514             public void focusGained(FocusEvent e) {
    515                 tfRole.selectAll();
    516             }
    517         });
    518         tfRole.setAutoCompletionList(new AutoCompletionList());
    519         tfRole.addFocusListener(
    520                 new FocusAdapter() {
    521                     @Override
    522                     public void focusGained(FocusEvent e) {
    523                         AutoCompletionList list = tfRole.getAutoCompletionList();
    524                         AutoCompletionCache.getCacheForLayer(Main.main.getEditLayer()).populateWithMemberRoles(list);
    525                     }
    526                 }
    527         );
    528 
    529         buttonPanel.add(tfRole);
    530         SetRoleAction setRoleAction = new SetRoleAction();
    531         memberTableModel.getSelectionModel().addListSelectionListener(setRoleAction);
    532         buttonPanel.add(new SideButton(setRoleAction));
    533         tfRole.getDocument().addDocumentListener(setRoleAction);
    534         tfRole.addActionListener(setRoleAction);
    535 
    536         // --- copy relation action
    537         buttonPanel.add(new SideButton(new DuplicateRelationAction()));
    538 
    539         // --- apply relation action
    540         buttonPanel.add(new SideButton(new ApplyAction()));
    541 
    542         // --- delete relation action
    543         buttonPanel.add(new SideButton(new DeleteCurrentRelationAction()));
    544         return buttonPanel;
     522        tb.add(addSelectedAfterSelectionAction);
     523
     524        return tb;
    545525    }
    546526
     
    548528    protected Dimension findMaxDialogSize() {
    549529        // FIXME: Make it remember dialog size
    550         return new Dimension(700, 500);
    551     }
    552 
    553     @Override
    554     public void dispose() {
    555         selectionTableModel.unregister();
    556         DataSet.selListeners.remove(memberTableModel);
    557         super.dispose();
     530        return new Dimension(700, 650);
    558531    }
    559532
     
    561534    public void setVisible(boolean visible) {
    562535        if (visible) {
    563             tagEditorPanel.initAutoCompletion(Main.main.getEditLayer());
     536            tagEditorPanel.initAutoCompletion(getLayer());
    564537        }
    565538        super.setVisible(visible);
    566539        if (!visible) {
     540            // make sure all registered listeners are unregistered
     541            //
     542            selectionTableModel.unregister();
     543            DataSet.selListeners.remove(memberTableModel);
     544            DataSet.selListeners.remove(selectionTableModel);
     545            getLayer().data.removeDataSetListener(memberTableModel);
     546            getLayer().listenerDataChanged.remove(memberTableModel);
    567547            dispose();
    568548        }
     
    888868    }
    889869
    890     class SortAction extends AbstractAction {
     870    class SortAction extends AbstractAction implements ListSelectionListener {
    891871        public SortAction() {
    892872            putValue(SHORT_DESCRIPTION, tr("Sort the relation members"));
    893873            putValue(SMALL_ICON, ImageProvider.get("dialogs", "sort"));
    894             // putValue(NAME, tr("Sort"));
     874            putValue(NAME, tr("Sort"));
    895875            Shortcut.registerShortcut("relationeditor:sort", tr("Relation Editor: Sort"), KeyEvent.VK_T,
    896876                    Shortcut.GROUP_MNEMONIC);
    897             //setEnabled(false);
     877            updateEnabledState();
    898878        }
    899879
    900880        public void actionPerformed(ActionEvent e) {
    901881            memberTableModel.sort();
     882        }
     883
     884        protected void updateEnabledState() {
     885            setEnabled(memberTable.getSelectedRowCount() > 0);
     886        }
     887
     888        public void valueChanged(ListSelectionEvent e) {
     889            updateEnabledState();
    902890        }
    903891    }
     
    960948    }
    961949
    962     class DeleteCurrentRelationAction extends AbstractAction {
     950    class DeleteCurrentRelationAction extends AbstractAction implements PropertyChangeListener{
    963951        public DeleteCurrentRelationAction() {
    964952            putValue(SHORT_DESCRIPTION, tr("Delete the currently edited relation"));
     
    983971
    984972        protected void updateEnabledState() {
    985             setEnabled(getRelation() != null);
     973            setEnabled(getRelationSnapshot() != null);
     974        }
     975
     976        public void propertyChange(PropertyChangeEvent evt) {
     977            if (evt.getPropertyName().equals(RELATION_SNAPSHOT_PROP)) {
     978                updateEnabledState();
     979            }
    986980        }
    987981    }
     
    11871181    }
    11881182
    1189     class DownlaodAction extends AbstractAction {
    1190         public DownlaodAction() {
    1191             putValue(SHORT_DESCRIPTION, tr("Download all incomplete ways and nodes in relation"));
    1192             putValue(SMALL_ICON, ImageProvider.get("dialogs", "downloadincomplete"));
     1183    class DownloadIncompleteMembersAction extends AbstractAction implements TableModelListener {
     1184        public DownloadIncompleteMembersAction() {
     1185            putValue(SHORT_DESCRIPTION, tr("Download all incomplete members"));
     1186            putValue(SMALL_ICON, ImageProvider.get("dialogs/relation", "downloadincomplete"));
    11931187            putValue(NAME, tr("Download Members"));
    11941188            Shortcut.registerShortcut("relationeditor:downloadincomplete", tr("Relation Editor: Download Members"),
     
    12001194            if (!isEnabled())
    12011195                return;
    1202             Main.worker.submit(new DownloadTask(
    1203                     Collections.singletonList(getRelation()),
     1196            Main.worker.submit(new DownloadRelationMemberTask(
     1197                    getRelation(),
     1198                    memberTableModel.getIncompleteMemberPrimitives(),
    12041199                    getLayer(),
    12051200                    memberTableModel,
     
    12091204
    12101205        protected void updateEnabledState() {
    1211             setEnabled(getRelation() != null && !getRelation().isNew());
     1206            setEnabled(
     1207                    getRelation() != null
     1208                    && !getRelation().isNew()
     1209                    && memberTableModel.hasIncompleteMembers()
     1210            );
     1211        }
     1212
     1213        public void tableChanged(TableModelEvent e) {
     1214            updateEnabledState();
     1215        }
     1216    }
     1217
     1218    class DownloadSelectedIncompleteMembersAction extends AbstractAction implements ListSelectionListener, TableModelListener{
     1219        public DownloadSelectedIncompleteMembersAction() {
     1220            putValue(SHORT_DESCRIPTION, tr("Download selected incomplete members"));
     1221            putValue(SMALL_ICON, ImageProvider.get("dialogs/relation", "downloadincompleteselected"));
     1222            putValue(NAME, tr("Download Members"));
     1223            Shortcut.registerShortcut("relationeditor:downloadincomplete", tr("Relation Editor: Download Members"),
     1224                    KeyEvent.VK_K, Shortcut.GROUP_MNEMONIC);
     1225            updateEnabledState();
     1226        }
     1227
     1228        public void actionPerformed(ActionEvent e) {
     1229            if (!isEnabled())
     1230                return;
     1231            Main.worker.submit(new DownloadRelationMemberTask(
     1232                    getRelation(),
     1233                    memberTableModel.getSelectedIncompleteMemberPrimitives(),
     1234                    getLayer(),
     1235                    memberTableModel,
     1236                    GenericRelationEditor.this)
     1237            );
     1238        }
     1239
     1240        protected void updateEnabledState() {
     1241            setEnabled(
     1242                    getRelation() != null
     1243                    && !getRelation().isNew()
     1244                    && memberTableModel.hasIncompleteSelectedMembers()
     1245            );
     1246        }
     1247
     1248        public void valueChanged(ListSelectionEvent e) {
     1249            updateEnabledState();
     1250        }
     1251
     1252        public void tableChanged(TableModelEvent e) {
     1253            updateEnabledState();
    12121254        }
    12131255    }
     
    13661408        }
    13671409    }
    1368 
    1369     /**
    1370      * The asynchronous task for downloading relation members.
    1371      *
    1372      */
    1373     public static class DownloadTask extends PleaseWaitRunnable {
    1374         private boolean cancelled;
    1375         private Exception lastException;
    1376         private List<Relation> relations;
    1377         private OsmDataLayer curLayer;
    1378         private MemberTableModel memberTableModel;
    1379         private OsmServerObjectReader objectReader;
    1380         private OsmServerBackreferenceReader parentReader;
    1381 
    1382         public DownloadTask(List<Relation> relations, OsmDataLayer curLayer, MemberTableModel memberTableModel, Dialog parent) {
    1383             super(tr("Download relation members"), new PleaseWaitProgressMonitor(parent), false /* don't ignore exception */);
    1384             this.relations = relations;
    1385             this.curLayer = curLayer;
    1386             this.memberTableModel = memberTableModel;
    1387         }
    1388 
    1389         public DownloadTask(List<Relation> relations, OsmDataLayer curLayer, MemberTableModel memberTableModel) {
    1390             super(tr("Download relation members"), new PleaseWaitProgressMonitor(), false /* don't ignore exception */);
    1391             this.relations = relations;
    1392             this.curLayer = curLayer;
    1393             this.memberTableModel = memberTableModel;
    1394         }
    1395 
    1396         @Override
    1397         protected void cancel() {
    1398             cancelled = true;
    1399             synchronized(this) {
    1400                 if (objectReader != null) {
    1401                     objectReader.cancel();
    1402                 } else if (parentReader != null) {
    1403                     parentReader.cancel();
    1404                 }
    1405             }
    1406         }
    1407 
    1408         @Override
    1409         protected void finish() {
    1410             Main.map.repaint();
    1411             if (cancelled)
    1412                 return;
    1413             if (memberTableModel != null) {
    1414                 memberTableModel.fireTableDataChanged();
    1415             }
    1416             if (lastException != null) {
    1417                 ExceptionDialogUtil.explainException(lastException);
    1418             }
    1419         }
    1420 
    1421         @Override
    1422         protected void realRun() throws SAXException, IOException, OsmTransferException {
    1423             try {
    1424                 for (Relation relation : relations) {
    1425                     // download the relation
    1426                     //
    1427                     progressMonitor.indeterminateSubTask(tr("Downloading relation ''{0}''", relation.getDisplayName(DefaultNameFormatter.getInstance())));
    1428                     synchronized(this) {
    1429                         if (cancelled) return;
    1430                         objectReader = new OsmServerObjectReader(relation.getId(), OsmPrimitiveType.RELATION, true /* full download */);
    1431                     }
    1432                     final DataSet dataSet = objectReader.parseOsm(progressMonitor
    1433                             .createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
    1434                     if (dataSet == null)
    1435                         return;
    1436                     synchronized (this) {
    1437                         if (cancelled) return;
    1438                         objectReader = null;
    1439                     }
    1440 
    1441                     // download referring objects of the downloaded member objects
    1442                     //
    1443                     // asked for in #3999, but uncommented for the time being. Could be used
    1444                     // later, perhaps if user explicity requests so (for instance by checking
    1445                     // a checkbox)
    1446                     //                    for (OsmPrimitive p: relation.getMemberPrimitives()) {
    1447                     //                        synchronized(this) {
    1448                     //                            if (cancelled) return;
    1449                     //                            parentReader = new OsmServerBackreferenceReader(p);
    1450                     //                        }
    1451                     //                        DataSet parents = parentReader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
    1452                     //                        synchronized(this) {
    1453                     //                            if (cancelled) return;
    1454                     //                            parentReader = null;
    1455                     //                        }
    1456                     //                        DataSetMerger merger = new DataSetMerger(dataSet, parents);
    1457                     //                        merger.merge();
    1458                     //                    }
    1459                     //                    if (cancelled) return;
    1460 
    1461                     // has to run on the EDT because mergeFrom may trigger events
    1462                     // which update the UI
    1463                     //
    1464                     SwingUtilities.invokeAndWait(
    1465                             new Runnable() {
    1466                                 public void run() {
    1467                                     curLayer.mergeFrom(dataSet);
    1468                                     curLayer.fireDataChange();
    1469                                     curLayer.onPostDownloadFromServer();
    1470                                 }
    1471                             }
    1472                     );
    1473                 }
    1474             } catch (Exception e) {
    1475                 if (cancelled) {
    1476                     System.out.println(tr("Warning: ignoring exception because task is cancelled. Exception: {0}", e
    1477                             .toString()));
    1478                     return;
    1479                 }
    1480                 lastException = e;
    1481             }
    1482         }
    1483     }
    14841410}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTable.java

    r2512 r2563  
    1818import javax.swing.KeyStroke;
    1919import javax.swing.ListSelectionModel;
     20import javax.swing.SwingUtilities;
    2021import javax.swing.event.ListSelectionEvent;
    2122import javax.swing.event.ListSelectionListener;
     
    7172
    7273        addMouseListener(new PopupListener());
     74        addMouseListener(new DblClickHandler());
    7375    }
    7476
     
    241243        return (MemberTableModel) getModel();
    242244    }
     245
     246    class DblClickHandler extends MouseAdapter {
     247        protected void setSelection(MouseEvent e) {
     248            int row = rowAtPoint(e.getPoint());
     249            if (row < 0) return;
     250            OsmPrimitive primitive = getMemberTableModel().getReferredPrimitive(row);
     251            getMemberTableModel().getLayer().data.setSelected(primitive.getPrimitiveId());
     252        }
     253
     254        protected void addSelection(MouseEvent e) {
     255            int row = rowAtPoint(e.getPoint());
     256            if (row < 0) return;
     257            OsmPrimitive primitive = getMemberTableModel().getReferredPrimitive(row);
     258            getMemberTableModel().getSelectionModel().addSelectionInterval(row, row);
     259            getMemberTableModel().getLayer().data.addSelected(primitive.getPrimitiveId());
     260
     261        }
     262
     263        @Override
     264        public void mouseClicked(MouseEvent e) {
     265            if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() > 1) {
     266                if (e.isControlDown()) {
     267                    addSelection(e);
     268                } else {
     269                    setSelection(e);
     270                }
     271            }
     272        }
     273    }
    243274}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableCellRenderer.java

    r2512 r2563  
    44import java.awt.Color;
    55import java.awt.Component;
    6 import java.util.ArrayList;
    7 import java.util.Collections;
    86
    97import javax.swing.JLabel;
    108import javax.swing.JTable;
     9import javax.swing.UIManager;
    1110import javax.swing.table.TableCellRenderer;
    1211
     
    1817 */
    1918public abstract class MemberTableCellRenderer extends JLabel implements TableCellRenderer {
    20     public final static Color BGCOLOR_SELECTED = new Color(143, 170, 255);
    2119    public final static Color BGCOLOR_EMPTY_ROW = new Color(234, 234, 234);
     20    public final static Color BGCOLOR_IN_JOSM_SELECTION = new Color(235,255,177);
    2221
    2322    public final static Color BGCOLOR_NOT_IN_OPPOSITE = new Color(255, 197, 197);
    24     public final static Color BGCOLOR_DOUBLE_ENTRY = new Color(255, 234, 213);
     23    public final static Color BGCOLOR_DOUBLE_ENTRY = new Color(254,226,214);
    2524
    2625    /**
     
    3231    }
    3332
    34     public String buildToolTipText(OsmPrimitive primitive) {
    35         StringBuilder sb = new StringBuilder();
    36         sb.append("<html>");
    37         sb.append("<strong>id</strong>=").append(primitive.getId()).append("<br>");
    38         ArrayList<String> keyList = new ArrayList<String>(primitive.keySet());
    39         Collections.sort(keyList);
    40         for (int i = 0; i < keyList.size(); i++) {
    41             if (i > 0) {
    42                 sb.append("<br>");
    43             }
    44             String key = keyList.get(i);
    45             sb.append("<strong>").append(key).append("</strong>").append("=");
    46             String value = primitive.get(key);
    47             while (value.length() != 0) {
    48                 sb.append(value.substring(0, Math.min(50, value.length())));
    49                 if (value.length() > 50) {
    50                     sb.append("<br>");
    51                     value = value.substring(50);
    52                 } else {
    53                     value = "";
    54                 }
    55             }
    56         }
    57         sb.append("</html>");
    58         return sb.toString();
    59     }
    60 
    6133    /**
    6234     * reset the renderer
    6335     */
    6436    protected void reset() {
    65         setBackground(Color.WHITE);
    66         setForeground(Color.BLACK);
     37        setBackground(UIManager.getColor("Table.background"));
     38        setForeground(UIManager.getColor("Table.foreground"));
    6739        setBorder(null);
    6840        setIcon(null);
     
    7143
    7244    protected void renderBackground(MemberTableModel model, OsmPrimitive primitive, boolean isSelected) {
    73         Color bgc = Color.WHITE;
     45        Color bgc = UIManager.getColor("Table.background");
    7446        if (isSelected) {
    75             bgc = BGCOLOR_SELECTED;
     47            bgc = UIManager.getColor("Table.selectionBackground");
     48        } else if (primitive != null && model.isInJosmSelection(primitive)) {
     49            bgc = BGCOLOR_IN_JOSM_SELECTION;
    7650        } else if (primitive != null && model.getNumMembersWithPrimitive(primitive) > 1) {
    7751            bgc = BGCOLOR_DOUBLE_ENTRY;
     
    8155
    8256    protected void renderForeground(boolean isSelected) {
    83         Color fgc = Color.BLACK;
     57        Color fgc;
     58        if (isSelected) {
     59            fgc = UIManager.getColor("Table.selectionForeground");
     60        } else {
     61            fgc = UIManager.getColor("Table.foreground");
     62        }
    8463        setForeground(fgc);
    8564    }
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableMemberCellRenderer.java

    r2512 r2563  
    33
    44import java.awt.Component;
    5 import java.util.HashMap;
    65
    7 import javax.swing.ImageIcon;
    86import javax.swing.JTable;
    97
    108import org.openstreetmap.josm.data.osm.OsmPrimitive;
    11 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
    129import org.openstreetmap.josm.gui.DefaultNameFormatter;
    1310import org.openstreetmap.josm.tools.ImageProvider;
    1411
    1512public class MemberTableMemberCellRenderer extends MemberTableCellRenderer {
    16     private HashMap<OsmPrimitiveType, ImageIcon> icons;
    1713
    1814    public MemberTableMemberCellRenderer() {
    1915        super();
    20         loadIcons();
    21     }
    22 
    23     /**
    24      * Load the image icon for an OSM primitive of type node
    25      *
    26      * @return the icon; null, if not found
    27      */
    28     protected void loadIcons() {
    29         icons = new HashMap<OsmPrimitiveType, ImageIcon>();
    30         icons.put(OsmPrimitiveType.NODE, ImageProvider.get("data", "node"));
    31         icons.put(OsmPrimitiveType.WAY, ImageProvider.get("data", "way"));
    32         icons.put(OsmPrimitiveType.RELATION, ImageProvider.get("data", "relation"));
    3316    }
    3417
    3518    protected void renderPrimitive(OsmPrimitive primitive) {
    36         setIcon(icons.get(OsmPrimitiveType.from(primitive)));
     19        setIcon(ImageProvider.get(primitive.getPrimitiveId().getType()));
    3720        setText(primitive.getDisplayName(DefaultNameFormatter.getInstance()));
    38         setToolTipText(buildToolTipText(primitive));
     21        setToolTipText(DefaultNameFormatter.getInstance().buildDefaultToolTip(primitive));
    3922    }
    4023
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java

    r2512 r2563  
    2626
    2727import org.openstreetmap.josm.Main;
     28import org.openstreetmap.josm.data.SelectionChangedListener;
    2829import org.openstreetmap.josm.data.coor.EastNorth;
     30import org.openstreetmap.josm.data.osm.DataSetListener;
    2931import org.openstreetmap.josm.data.osm.Node;
    3032import org.openstreetmap.josm.data.osm.OsmPrimitive;
     
    3335import org.openstreetmap.josm.data.osm.Way;
    3436import org.openstreetmap.josm.gui.dialogs.relation.WayConnectionType.Direction;
     37import org.openstreetmap.josm.gui.layer.DataChangeListener;
    3538import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    3639
    37 public class MemberTableModel extends AbstractTableModel implements TableModelListener {
     40public class MemberTableModel extends AbstractTableModel implements TableModelListener, SelectionChangedListener, DataChangeListener, DataSetListener{
    3841
    3942    /**
     
    5659        addTableModelListener(this);
    5760    }
     61
     62    public OsmDataLayer getLayer() {
     63        return layer;
     64    }
     65
     66    /* --------------------------------------------------------------------------- */
     67    /* Interface SelectionChangedListener                                          */
     68    /* --------------------------------------------------------------------------- */
     69    public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
     70        if (Main.main.getEditLayer() != this.layer) return;
     71        // just trigger a repaint
     72        Collection<RelationMember> sel = getSelectedMembers();
     73        fireTableDataChanged();
     74        setSelectedMembers(sel);
     75    }
     76
     77    /* --------------------------------------------------------------------------- */
     78    /* Interface DataChangeListener                                                */
     79    /* --------------------------------------------------------------------------- */
     80    public void dataChanged(OsmDataLayer l) {
     81        if (l != this.layer) return;
     82        // just trigger a repaint
     83        Collection<RelationMember> sel = getSelectedMembers();
     84        fireTableDataChanged();
     85        setSelectedMembers(sel);
     86    }
     87    /* --------------------------------------------------------------------------- */
     88    /* Interface DataSetListener                                                   */
     89    /* --------------------------------------------------------------------------- */
     90    public void dataChanged() {
     91        // just trigger a repaint - the display name of the relation members may
     92        // have changed
     93        Collection<RelationMember> sel = getSelectedMembers();
     94        fireTableDataChanged();
     95        setSelectedMembers(sel);
     96    }
     97
     98    public void nodeMoved(Node node) {/* ignore */}
     99    public void primtivesAdded(Collection<? extends OsmPrimitive> added) {/* ignore */}
     100
     101    public void primtivesRemoved(Collection<? extends OsmPrimitive> removed) {
     102        // ignore - the relation in the editor might become out of sync with the relation
     103        // in the dataset. We will deal with it when the relation editor is closed or
     104        // when the changes in the editor are applied.
     105    }
     106
     107    public void relationMembersChanged(Relation r) {
     108        // ignore - the relation in the editor might become out of sync with the relation
     109        // in the dataset. We will deal with it when the relation editor is closed or
     110        // when the changes in the editor are applied.
     111    }
     112
     113    public void tagsChanged(OsmPrimitive prim) {
     114        // just refresh the respective table cells
     115        //
     116        Collection<RelationMember> sel = getSelectedMembers();
     117        for (int i=0; i < members.size();i++) {
     118            if (members.get(i).getMember() == prim) {
     119                fireTableCellUpdated(i, 1 /* the column with the primitive name */);
     120            }
     121        }
     122        setSelectedMembers(sel);
     123    }
     124
     125    public void wayNodesChanged(Way way) {/* ignore */}
     126    /* --------------------------------------------------------------------------- */
    58127
    59128    public void addMemberModelListener(IMemberModelListener listener) {
     
    242311    }
    243312
     313    /**
     314     * Replies the set of incomplete primitives
     315     *
     316     * @return the set of incomplete primitives
     317     */
     318    public Set<OsmPrimitive> getIncompleteMemberPrimitives() {
     319        Set<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
     320        for (RelationMember member : members) {
     321            if (member.getMember().incomplete) {
     322                ret.add(member.getMember());
     323            }
     324        }
     325        return ret;
     326    }
     327
     328    /**
     329     * Replies the set of selected incomplete primitives
     330     *
     331     * @return the set of selected incomplete primitives
     332     */
     333    public Set<OsmPrimitive> getSelectedIncompleteMemberPrimitives() {
     334        Set<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
     335        for (RelationMember member : getSelectedMembers()) {
     336            if (member.getMember().incomplete) {
     337                ret.add(member.getMember());
     338            }
     339        }
     340        return ret;
     341    }
     342
     343    /**
     344     * Replies true if at least one the relation members is incomplete
     345     *
     346     * @return true if at least one the relation members is incomplete
     347     */
    244348    public boolean hasIncompleteMembers() {
    245349        for (RelationMember member : members) {
     350            if (member.getMember().incomplete)
     351                return true;
     352        }
     353        return false;
     354    }
     355
     356    /**
     357     * Replies true if at least one of the selected members is incomplete
     358     *
     359     * @return true if at least one of the selected members is incomplete
     360     */
     361    public boolean hasIncompleteSelectedMembers() {
     362        for (RelationMember member : getSelectedMembers()) {
    246363            if (member.getMember().incomplete)
    247364                return true;
     
    408525
    409526    /**
    410      * Replies true, if the selected {@see OsmPrimitive}s in the layer belonging
    411      * to this model are in sync with the selected referers in this model.
    412      *
    413      * @return
    414      */
    415     public boolean selectionsAreInSync() {
    416         HashSet<OsmPrimitive> s1 = new HashSet<OsmPrimitive>(getSelectedChildPrimitives());
    417         if (s1.size() != layer.data.getSelected().size()) return false;
    418         s1.removeAll(layer.data.getSelected());
    419         return s1.isEmpty();
    420     }
    421     /**
    422527     * Selects the members in the collection selectedMembers
    423528     *
     
    425530     */
    426531    public void setSelectedMembers(Collection<RelationMember> selectedMembers) {
    427         if (selectedMembers == null || selectedMembers.isEmpty())
     532        if (selectedMembers == null || selectedMembers.isEmpty()) {
     533            getSelectionModel().clearSelection();
    428534            return;
     535        }
    429536
    430537        // lookup the indices for the respective members
    431538        //
    432         ArrayList<Integer> selectedIndices = new ArrayList<Integer>();
     539        Set<Integer> selectedIndices = new HashSet<Integer>();
    433540        for (RelationMember member : selectedMembers) {
    434             for (int idx = 0; idx < members.size(); idx ++) {
    435                 if (members.get(idx).equals(member)) {
    436                     if (!selectedIndices.contains(idx)) {
    437                         selectedIndices.add(idx);
    438                     }
    439                 }
     541            int idx = members.indexOf(member);
     542            if ( idx >= 0) {
     543                selectedIndices.add(idx);
    440544            }
    441545        }
     
    443547        // select the members
    444548        //
    445         Collections.sort(selectedIndices);
    446549        getSelectionModel().setValueIsAdjusting(true);
    447550        getSelectionModel().clearSelection();
     
    450553        }
    451554        getSelectionModel().setValueIsAdjusting(false);
    452 
    453555        // make the first selected member visible
    454556        //
    455557        if (selectedIndices.size() > 0) {
    456             fireMakeMemberVisible(selectedIndices.get(0));
     558            fireMakeMemberVisible(Collections.min(selectedIndices));
    457559        }
    458560    }
     
    523625            fireMakeMemberVisible(getSelectedIndices().get(0));
    524626        }
     627    }
     628
     629    /**
     630     * Replies true if <code>primitive</code> is currently selected in the layer this
     631     * model is attached to
     632     *
     633     * @param primitive the primitive
     634     * @return true if <code>primitive</code> is currently selected in the layer this
     635     * model is attached to, false otherwise
     636     */
     637    public boolean isInJosmSelection(OsmPrimitive primitive) {
     638        return layer.data.isSelected(primitive);
    525639    }
    526640
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationEditor.java

    r2512 r2563  
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
     6import java.beans.PropertyChangeListener;
     7import java.beans.PropertyChangeSupport;
    68import java.lang.reflect.Constructor;
    79import java.lang.reflect.Method;
     
    1618
    1719public abstract class RelationEditor extends ExtendedDialog {
     20    /** the property name for the current relation.
     21     * @see #setRelation(Relation)
     22     * @see #getRelation()
     23     */
     24    static public final String RELATION_PROP = RelationEditor.class.getName() + ".relation";
     25
     26    /** the property name for the current relation snapshot
     27     * @see #getRelationSnapshot()
     28     */
     29    static public final String RELATION_SNAPSHOT_PROP = RelationEditor.class.getName() + ".relationSnapshot";
    1830
    1931    /** the list of registered relation editor classes */
     
    6274     * then an instance of that class will be used.
    6375     *
     76     * @param layer the data layer the relation is a member of
    6477     * @param r the relation to be edited
     78     * @param selectedMembers a collection of relation members which shall be selected when the
     79     * editor is first launched
    6580     * @return an instance of RelationEditor suitable for editing that kind of relation
    6681     */
     
    139154     */
    140155    protected void setRelation(Relation relation) {
    141         this.relationSnapshot = (relation == null) ? null : new Relation(relation);
     156        setRelationSnapshot((relation == null) ? null : new Relation(relation));
     157        Relation oldValue = this.relation;
    142158        this.relation = relation;
     159        if (this.relation != oldValue) {
     160            support.firePropertyChange(RELATION_PROP, oldValue, this.relation);
     161        }
    143162        updateTitle();
    144163    }
     
    164183    }
    165184
     185    protected void setRelationSnapshot(Relation snapshot) {
     186        Relation oldValue = relationSnapshot;
     187        relationSnapshot = snapshot;
     188        if (relationSnapshot != oldValue) {
     189            support.firePropertyChange(RELATION_SNAPSHOT_PROP, oldValue, relationSnapshot);
     190        }
     191    }
     192
    166193    /**
    167194     * Replies true if the currently edited relation has been changed elsewhere.
     
    175202        return ! relation.hasEqualSemanticAttributes(relationSnapshot);
    176203    }
     204
     205
     206    /* ----------------------------------------------------------------------- */
     207    /* property change support                                                 */
     208    /* ----------------------------------------------------------------------- */
     209    final private PropertyChangeSupport support = new PropertyChangeSupport(this);
     210
     211    @Override
     212    public void addPropertyChangeListener(PropertyChangeListener listener) {
     213        this.support.addPropertyChangeListener(listener);
     214    }
     215
     216    @Override
     217    public void removePropertyChangeListener(PropertyChangeListener listener) {
     218        this.support.removePropertyChangeListener(listener);
     219    }
    177220}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTableCellRenderer.java

    r2512 r2563  
    44import java.awt.Color;
    55import java.awt.Component;
    6 import java.util.ArrayList;
    7 import java.util.Collections;
    8 import java.util.HashMap;
    96
    10 import javax.swing.ImageIcon;
    117import javax.swing.JLabel;
    128import javax.swing.JTable;
     9import javax.swing.UIManager;
    1310import javax.swing.table.TableCellRenderer;
    1411
    1512import org.openstreetmap.josm.data.osm.OsmPrimitive;
    16 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
    1713import org.openstreetmap.josm.gui.DefaultNameFormatter;
    1814import org.openstreetmap.josm.tools.ImageProvider;
    1915
    2016/**
    21  * This is the {@see TableCellRenderer} used in the tables of {@see RelationMemberMerger}.
     17 * This is the {@see TableCellRenderer} used in {@see SelectionTable}.
    2218 *
    2319 */
    2420public  class SelectionTableCellRenderer extends JLabel implements TableCellRenderer {
    25     public final static Color BGCOLOR_SELECTED = new Color(143,170,255);
    26     public final static Color BGCOLOR_DOUBLE_ENTRY = new Color(255,234,213);
     21    public final static Color BGCOLOR_DOUBLE_ENTRY = new Color(254,226,214);
     22    public final static Color BGCOLOR_SINGLE_ENTRY = new Color(235,255,177);
    2723
    28     private HashMap<OsmPrimitiveType, ImageIcon>  icons;
    2924    /**
    3025     * reference to the member table model; required, in order to check whether a
     
    3530
    3631    /**
    37      * Load the image icon for an OSM primitive of type node
    38      *
    39      * @return the icon; null, if not found
    40      */
    41     protected void loadIcons() {
    42         icons = new HashMap<OsmPrimitiveType, ImageIcon>();
    43         icons.put(OsmPrimitiveType.NODE,ImageProvider.get("data", "node"));
    44         icons.put(OsmPrimitiveType.WAY, ImageProvider.get("data", "way"));
    45         icons.put(OsmPrimitiveType.RELATION, ImageProvider.get("data", "relation"));
    46     }
    47 
    48     /**
    4932     * constructor
    5033     */
     
    5235        setIcon(null);
    5336        setOpaque(true);
    54         loadIcons();
    55     }
    56 
    57     public String buildToolTipText(OsmPrimitive primitive) {
    58         StringBuilder sb = new StringBuilder();
    59         sb.append("<html>");
    60         sb.append("<strong>id</strong>=")
    61         .append(primitive.getId())
    62         .append("<br>");
    63         ArrayList<String> keyList = new ArrayList<String>(primitive.keySet());
    64         Collections.sort(keyList);
    65         for (int i = 0; i < keyList.size(); i++) {
    66             if (i > 0) {
    67                 sb.append("<br>");
    68             }
    69             String key = keyList.get(i);
    70             sb.append("<strong>")
    71             .append(key)
    72             .append("</strong>")
    73             .append("=");
    74             String value = primitive.get(key);
    75             while(value.length() != 0) {
    76                 sb.append(value.substring(0,Math.min(50, value.length())));
    77                 if (value.length() > 50) {
    78                     sb.append("<br>");
    79                     value = value.substring(50);
    80                 } else {
    81                     value = "";
    82                 }
    83             }
    84         }
    85         sb.append("</html>");
    86         return sb.toString();
    8737    }
    8838
     
    9141     */
    9242    protected void reset() {
    93         setBackground(Color.WHITE);
    94         setForeground(Color.BLACK);
     43        setBackground(UIManager.getColor("Table.background"));
     44        setForeground(UIManager.getColor("Table.foreground"));
    9545        setBorder(null);
    9646        setIcon(null);
     
    9949
    10050    protected void renderBackground(OsmPrimitive primitive, boolean isSelected) {
    101         Color bgc = Color.WHITE;
    102         if (isSelected) {
    103             bgc = BGCOLOR_SELECTED;
    104         } else if (primitive != null && model != null && model.getNumMembersWithPrimitive(primitive) > 0) {
     51        Color bgc = UIManager.getColor("Table.background");
     52        if (primitive != null && model != null && model.getNumMembersWithPrimitive(primitive) == 1) {
     53            bgc = BGCOLOR_SINGLE_ENTRY;
     54        } else if (primitive != null && model != null && model.getNumMembersWithPrimitive(primitive) > 1) {
    10555            bgc = BGCOLOR_DOUBLE_ENTRY;
    10656        }
     
    10858    }
    10959
    110     protected void renderForeground(boolean isSelected) {
    111         Color fgc = Color.BLACK;
    112         setForeground(fgc);
    113     }
    11460
    11561    protected void renderPrimitive(OsmPrimitive primitive) {
    116         setIcon(icons.get(OsmPrimitiveType.from(primitive)));
     62        setIcon(ImageProvider.get(primitive.getPrimitiveId().getType()));
    11763        setText(primitive.getDisplayName(DefaultNameFormatter.getInstance()));
    118         setToolTipText(buildToolTipText(primitive));
     64        setToolTipText(DefaultNameFormatter.getInstance().buildDefaultToolTip(primitive));
    11965    }
    12066
     
    12369
    12470        reset();
    125         renderForeground(isSelected);
    12671        renderBackground((OsmPrimitive)value, isSelected);
    12772        renderPrimitive((OsmPrimitive)value);
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTableModel.java

    r2512 r2563  
    3434        this.layer = layer;
    3535        cache = new ArrayList<OsmPrimitive>();
    36         DataSet.selListeners.add(this);
    3736        Layer.listeners.add(this);
    3837        populateSelectedPrimitives(layer);
     
    103102        selectionChanged(layer.data.getSelected());
    104103    }
     104
     105    /**
     106     * Replies the primitive at row <code>row</code> in this model
     107     *
     108     * @param row the row
     109     * @return  the primitive at row <code>row</code> in this model
     110     */
     111    public OsmPrimitive getPrimitive(int row) {
     112        return cache.get(row);
     113    }
    105114}
  • trunk/src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java

    r2512 r2563  
    33
    44import static org.openstreetmap.josm.tools.I18n.tr;
     5import static org.openstreetmap.josm.tools.I18n.trn;
    56
    67import java.io.InputStream;
     
    308309        progressMonitor.subTask(tr("Downloading OSM data..."));
    309310        try {
    310 
    311             merge(
    312                     OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false))
    313             );
     311            DataSet loaded = OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
     312            rememberNodesOfIncompleteWaysToLoad(loaded);
     313            merge(loaded);
    314314        } catch(Exception e) {
    315315            throw new OsmTransferException(e);
     
    333333        progressMonitor.subTask(tr("Downloading OSM data..."));
    334334        try {
    335             merge(
    336                     OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false))
    337             );
     335            DataSet loaded = OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
     336            rememberNodesOfIncompleteWaysToLoad(loaded);
     337            merge(loaded);
    338338        } catch(Exception e) {
    339339            throw new OsmTransferException(e);
     
    376376    }
    377377
     378    protected void rememberNodesOfIncompleteWaysToLoad(DataSet from) {
     379        for (Way w: from.getWays()) {
     380            if (w.incomplete) {
     381                for (Node n: w.getNodes()) {
     382                    if (n.incomplete) {
     383                        nodes.add(n.getId());
     384                    }
     385                }
     386            }
     387        }
     388    }
     389
    378390    /**
    379391     * merges the dataset <code>from</code> to {@see #outputDataSet}.
     
    401413        case RELATION:  msg = tr("Fetching a package of relations from ''{0}''", OsmApi.getOsmApi().getBaseUrl()); break;
    402414        }
    403         progressMonitor.setCustomText(msg);
     415        progressMonitor.indeterminateSubTask(msg);
    404416        Set<Long> toFetch = new HashSet<Long>(ids);
    405417        toFetch.addAll(ids);
     
    428440     * found on  the server (the server response code was 404)
    429441     *
    430      * Invoke {@see #getSkippedWay()} to get a list of ways which this reader could not build from
    431      * the fetched data because the ways refer to nodes which don't exist on the server.
    432      *
    433442     * @return the parsed data
    434443     * @exception OsmTransferException thrown if an error occurs while communicating with the API server
    435444     * @see #getMissingPrimitives()
    436      * @see #getSkippedWays()
    437      *
    438 
     445     *
    439446     */
    440447    @Override
    441448    public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException {
    442         progressMonitor.beginTask("");
     449        int n = nodes.size() + ways.size() + relations.size();
     450        progressMonitor.beginTask(trn("Downloading {0} object from ''{1}''", "Downloading {0} objects from ''{1}''", n, n, OsmApi.getOsmApi().getBaseUrl()));
    443451        try {
    444452            missingPrimitives = new HashSet<Long>();
    445453            if (isCanceled())return null;
     454            fetchPrimitives(ways,OsmPrimitiveType.WAY, progressMonitor);
     455            if (isCanceled())return null;
    446456            fetchPrimitives(nodes,OsmPrimitiveType.NODE, progressMonitor);
    447             if (isCanceled())return null;
    448             fetchPrimitives(ways,OsmPrimitiveType.WAY, progressMonitor);
    449457            if (isCanceled())return null;
    450458            fetchPrimitives(relations,OsmPrimitiveType.RELATION, progressMonitor);
  • trunk/src/org/openstreetmap/josm/io/OsmReader.java

    r2512 r2563  
    1212import java.util.List;
    1313import java.util.Map;
     14import java.util.logging.Level;
    1415import java.util.logging.Logger;
    1516
     
    443444            w.setNodes(wayNodes);
    444445            if (incomplete) {
    445                 logger.warning(tr("Marked way {0} with {1} nodes incomplete because at least one node was missing in the " +
    446                         "loaded data and is therefore incomplete too.", externalWayId, w.getNodesCount()));
     446                if (logger.isLoggable(Level.FINE)) {
     447                    logger.fine(tr("Marked way {0} with {1} nodes incomplete because at least one node was missing in the " +
     448                            "loaded data and is therefore incomplete too.", externalWayId, w.getNodesCount()));
     449                }
    447450                w.incomplete = true;
    448451                ds.addPrimitive(w);
  • trunk/src/org/openstreetmap/josm/io/OsmServerObjectReader.java

    r2484 r2563  
    99import org.openstreetmap.josm.data.osm.DataSet;
    1010import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
     11import org.openstreetmap.josm.data.osm.PrimitiveId;
     12import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
     13import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
    1114import org.openstreetmap.josm.gui.progress.ProgressMonitor;
    1215import org.xml.sax.SAXException;
    1316
     17/**
     18 * OsmServerObjectReader reads an individual object from the OSM server.
     19 *
     20 * It can either download the object including or not including its immediate children.
     21 * The former case is called a "full download".
     22 *
     23 */
    1424public class OsmServerObjectReader extends OsmServerReader {
     25    /** the id of the object to download */
     26    private PrimitiveId id;
     27    /** true if a full download is required, i.e. a download including the immediate children */
     28    private boolean full;
    1529
    16     long id;
    17     OsmPrimitiveType type;
    18     boolean full;
    19 
    20     public OsmServerObjectReader(long id, OsmPrimitiveType type, boolean full) {
    21         this.id = id;
    22         this.type = type;
     30    /**
     31     * Creates a new server object reader for a given id and a primitive type.
     32     *
     33     * @param id the object id. > 0 required.
     34     * @param type the type. Must not be null.
     35     * @param full true, if a full download is requested (i.e. a download including
     36     * immediate children); false, otherwise
     37     * @throws IllegalArgumentException thrown if id <= 0
     38     * @throws IllegalArgumentException thrown if type is null
     39     */
     40    public OsmServerObjectReader(long id, OsmPrimitiveType type, boolean full) throws IllegalArgumentException {
     41        if (id <= 0)
     42            throw new IllegalArgumentException(tr("Expected value > 0 for parameter ''{0}'', got {1}", "id", id));
     43        if (type == null)
     44            throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null", "type"));
     45        this.id = new SimplePrimitiveId(id, type);
    2346        this.full = full;
    2447    }
     48
    2549    /**
    26      * Method to download single objects from OSM server. ways, relations, nodes
    27      * @return the data requested
     50     * Creates a new server object reader for an object with the given <code>id</code>
     51     *
     52     * @param id the object id. Must not be null. Unique id > 0 required.
     53     * @param full true, if a full download is requested (i.e. a download including
     54     * immediate children); false, otherwise
     55     * @throws IllegalArgumentException thrown if id is null
     56     * @throws IllegalArgumentException thrown if id.getUniqueId() <= 0
     57     */
     58    public OsmServerObjectReader(PrimitiveId id, boolean full) {
     59        if (id == null)
     60            throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null", "id"));
     61        if (id.getUniqueId() <= 0)
     62            throw new IllegalArgumentException(tr("Expected value > 0 for parameter ''{0}'', got {1}", "id.getUniqueId()", id.getUniqueId()));
     63        this.id = id;
     64        this.full = full;
     65    }
     66
     67    /**
     68     * Downloads and parses the data.
     69     *
     70     * @param progressMonitor the progress monitor. Set to {@see NullProgressMonitor#INSTANCE} if
     71     * null
     72     * @return the downloaded data
    2873     * @throws SAXException
    2974     * @throws IOException
     
    3176    @Override
    3277    public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException {
     78        if (progressMonitor == null) {
     79            progressMonitor = NullProgressMonitor.INSTANCE;
     80        }
    3381        progressMonitor.beginTask("", 1);
    3482        InputStream in = null;
     
    3684            progressMonitor.indeterminateSubTask(tr("Downloading OSM data..."));
    3785            StringBuffer sb = new StringBuffer();
    38             sb.append(type.getAPIName());
     86            sb.append(id.getType().getAPIName());
    3987            sb.append("/");
    40             sb.append(id);
    41             if (full && ! type.equals(OsmPrimitiveType.NODE)) {
     88            sb.append(id.getUniqueId());
     89            if (full && ! id.getType().equals(OsmPrimitiveType.NODE)) {
    4290                sb.append("/full");
    4391            }
     
    59107                try {
    60108                    in.close();
    61                 } catch(Exception e) {}
     109                } catch(Exception e) {/* ignore this exception */}
    62110            }
    63111            activeConnection = null;
    64112        }
    65113    }
    66 
    67114}
Note: See TracChangeset for help on using the changeset viewer.