Changeset 1916 in josm for trunk/src/org


Ignore:
Timestamp:
2009-08-05T21:35:30+02:00 (15 years ago)
Author:
Gubaer
Message:

new: Ctrl-DblClick in the relation list dialog opens the relation editor
new: Duplicate button in the relation list dialog
new: autocompletion on member roles in the member table
new: autocompletion on the member field for role assignments on multiple members
new: two-way synchronization between the selection in the map and the selection in the member table of the relation editor

Location:
trunk/src/org/openstreetmap/josm/gui/dialogs
Files:
1 added
6 edited
1 moved

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/dialogs/RelationListDialog.java

    r1897 r1916  
    1919import javax.swing.JScrollPane;
    2020import javax.swing.ListSelectionModel;
     21import javax.swing.SwingUtilities;
    2122import javax.swing.event.ListSelectionEvent;
    2223import javax.swing.event.ListSelectionListener;
     
    7778        displaylist.setCellRenderer(new OsmPrimitivRenderer());
    7879        displaylist.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    79         displaylist.addMouseListener(new MouseAdapter(){
    80             @Override public void mouseClicked(MouseEvent e) {
    81                 if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
    82                     Main.main.getCurrentDataSet().setSelected((Relation)displaylist.getSelectedValue());
    83                 }
    84             }
    85         });
     80        displaylist.addMouseListener(new DoubleClickAdapter());
    8681        add(new JScrollPane(displaylist), BorderLayout.CENTER);
    8782
     
    10196        displaylist.addListSelectionListener(editAction);
    10297        buttonPanel.add(new SideButton(editAction), GBC.std());
     98
     99        // the edit action
     100        //
     101        DuplicateAction duplicateAction = new DuplicateAction();
     102        displaylist.addListSelectionListener(duplicateAction);
     103        buttonPanel.add(new SideButton(duplicateAction), GBC.std());
    103104
    104105        // the delete action
     
    235236    }
    236237
     238    class DoubleClickAdapter extends MouseAdapter {
     239        protected void setCurrentRelationAsSelection() {
     240            Main.main.getCurrentDataSet().setSelected((Relation)displaylist.getSelectedValue());
     241        }
     242
     243        protected void editCurrentRelation() {
     244            new EditAction().launchEditor(getSelected());
     245        }
     246
     247        @Override public void mouseClicked(MouseEvent e) {
     248            if (e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton(e)) {
     249                if (e.isControlDown()) {
     250                    editCurrentRelation();
     251                } else {
     252                    setCurrentRelationAsSelection();
     253                }
     254            }
     255        }
     256    }
     257
    237258    /**
    238259     * The edit action
    239260     *
    240261     */
    241     class EditAction extends AbstractAction implements ListSelectionListener, Runnable{
     262    class EditAction extends AbstractAction implements ListSelectionListener{
    242263        public EditAction() {
    243264            putValue(SHORT_DESCRIPTION,tr( "Open an editor for the selected relation"));
     
    257278        }
    258279
    259         public void run() {
    260             if (!isEnabled()) return;
    261             Relation toEdit = getSelected();
     280        public void launchEditor(Relation toEdit) {
    262281            if (toEdit == null)
    263282                return;
     
    266285
    267286        public void actionPerformed(ActionEvent e) {
    268             run();
     287            if (!isEnabled())
     288                return;
     289            launchEditor(getSelected());
    269290        }
    270291
     
    344365        }
    345366    }
     367
     368    /**
     369     * Creates a new relation with a copy of the current editor state
     370     *
     371     */
     372    class DuplicateAction extends AbstractAction implements ListSelectionListener {
     373        public DuplicateAction() {
     374            putValue(SHORT_DESCRIPTION, tr("Create a copy of this relation and open it in another editor window"));
     375            // FIXME provide an icon
     376            putValue(SMALL_ICON, ImageProvider.get("duplicate"));
     377            putValue(NAME, tr("Duplicate"));
     378            updateEnabledState();
     379        }
     380
     381        public void launchEditorForDuplicate(Relation original) {
     382            Relation copy = new Relation();
     383            copy.cloneFrom(original);
     384            copy.id = 0;
     385            copy.modified = true;
     386            RelationEditor editor = RelationEditor.getEditor(
     387                    Main.main.getEditLayer(),
     388                    copy,
     389                    null /* no selected members */
     390            );
     391            editor.setVisible(true);
     392        }
     393
     394        public void actionPerformed(ActionEvent e) {
     395            if (!isEnabled())
     396                return;
     397            launchEditorForDuplicate(getSelected());
     398        }
     399
     400        protected void updateEnabledState() {
     401            setEnabled(displaylist.getSelectedIndices() != null && displaylist.getSelectedIndices().length == 1);
     402        }
     403
     404        public void valueChanged(ListSelectionEvent e) {
     405            updateEnabledState();
     406        }
     407    }
    346408}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/AutoCompletingTextField.java

    r1910 r1916  
    1717
    1818/**
    19  * TagFieldEditor is an editor for tag names or tag values. It supports auto completion
    20  * from a list of auto completion items.
     19 * AutoCompletingTextField is an text field with autocompletion behaviour. It
     20 * can be used as table cell editor in {@see JTable}s.
     21 *
     22 * Autocompletion is controlled by a list of {@see AutoCompletionListItem}s
     23 * managed in a {@see AutoCompletionList}.
     24 *
    2125 *
    2226 */
    23 public class TagFieldEditor extends JTextField  {
     27public class AutoCompletingTextField extends JTextField  {
    2428
    25     static private Logger logger = Logger.getLogger(TagFieldEditor.class.getName());
     29    static private Logger logger = Logger.getLogger(AutoCompletingTextField.class.getName());
    2630
    2731    /**
     
    8993    }
    9094
    91     /**
    92      * constructor
    93      */
    94     public TagFieldEditor() {
    95 
     95    protected void init() {
    9696        addFocusListener(
    9797                new FocusAdapter() {
     
    114114                }
    115115        );
     116    }
     117
     118    /**
     119     * constructor
     120     */
     121    public AutoCompletingTextField() {
     122        init();
     123    }
     124
     125    public AutoCompletingTextField(int columns) {
     126        super(columns);
     127        init();
    116128    }
    117129
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java

    r1890 r1916  
    2727import java.util.Iterator;
    2828import java.util.List;
     29import java.util.Set;
    2930import java.util.logging.Logger;
    3031
     
    106107    private SelectionTableModel selectionTableModel;
    107108
    108     private JTextField tfRole;
     109    private AutoCompletingTextField tfRole;
    109110
    110111    /**
     
    124125        // initialize the autocompletion infrastructure
    125126        //
    126         acCache = AutoCompletionCache.getCacheForLayer(Main.map.mapView.getEditLayer());
     127        acCache = AutoCompletionCache.getCacheForLayer(getLayer());
     128        acCache.initFromJOSMDataset();
    127129        acList = new AutoCompletionList();
    128130
     
    130132        //
    131133        tagEditorModel = new TagEditorModel();
    132         memberTableModel = new MemberTableModel();
     134        memberTableModel = new MemberTableModel(getLayer());
    133135        selectionTableModel = new SelectionTableModel(getLayer());
    134136        referrerModel = new ReferringRelationsBrowserModel(relation);
     
    198200
    199201        memberTableModel.setSelectedMembers(selectedMembers);
     202        DataSet.selListeners.add(memberTableModel);
    200203    }
    201204
     
    266269        //
    267270        tagTable = new TagTable(tagEditorModel);
    268         acCache.initFromJOSMDataset();
    269271        TagCellEditor editor = ((TagCellEditor) tagTable.getColumnModel().getColumn(0).getCellEditor());
    270272        editor.setAutoCompletionCache(acCache);
     
    330332        // setting up the member table
    331333        memberTable = new MemberTable(getLayer(),memberTableModel);
     334        MemberRoleCellEditor editor = ((MemberRoleCellEditor) memberTable.getColumnModel().getColumn(0).getCellEditor());
     335        editor.setAutoCompletionCache(acCache);
     336        editor.setAutoCompletionList(acList);
    332337
    333338        memberTable.getSelectionModel().addListSelectionListener(new SelectionSynchronizer());
     
    592597        // --- role editing
    593598        buttonPanel.add(new JLabel(tr("Role:")));
    594         tfRole = new JTextField(10);
     599        tfRole = new AutoCompletingTextField(10);
    595600        tfRole.addFocusListener(new FocusAdapter() {
    596601            @Override
     
    599604            }
    600605        });
     606        tfRole.setAutoCompletionList(acList);
     607        tfRole.addFocusListener(
     608                new FocusAdapter() {
     609                    @Override
     610                    public void focusGained(FocusEvent e) {
     611                        acCache.populateWithMemberRoles(acList);
     612                    }
     613                }
     614        );
     615
    601616        buttonPanel.add(tfRole);
    602617        SetRoleAction setRoleAction = new SetRoleAction();
     
    626641    public void dispose() {
    627642        selectionTableModel.unregister();
     643        DataSet.selListeners.remove(memberTableModel);
    628644        super.dispose();
    629645    }
     
    14321448    }
    14331449
     1450    /**
     1451     * Updates the selection in the current data set with the selected referers in
     1452     * in the member table.
     1453     *
     1454     */
    14341455    class SelectionSynchronizer implements ListSelectionListener {
    14351456        public void valueChanged(ListSelectionEvent e) {
    1436             ArrayList<OsmPrimitive> sel;
    1437             int cnt = memberTable.getSelectedRowCount();
    1438             if (cnt <= 0)
     1457            if (e.getValueIsAdjusting())
    14391458                return;
    1440             sel = new ArrayList<OsmPrimitive>(cnt);
    1441             for (int i : memberTable.getSelectedRows()) {
    1442                 sel.add(memberTableModel.getReferredPrimitive(i));
    1443             }
    1444             getLayer().data.setSelected(sel);
     1459
     1460            // Avoid endless loops. memberTableModel is registered as SelectionChangeListener
     1461            // too. Only update the selection if it is not in sync with what is already
     1462            // selected.
     1463            //
     1464            if (!memberTableModel.selectionsAreInSync()) {
     1465                getLayer().data.setSelected(memberTableModel.getSelectedReferers());
     1466            }
    14451467        }
    14461468    }
     
    14481470    /**
    14491471     * The asynchronous task for downloading relation members.
    1450      *
    14511472     *
    14521473     */
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableColumnModel.java

    r1822 r1916  
    1717        col.setResizable(true);
    1818        col.setCellRenderer(new MemberTableRoleCellRenderer());
    19 
     19        col.setCellEditor(new MemberRoleCellEditor());
    2020        addColumn(col);
    2121
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java

    r1886 r1916  
    1 // License: GPL. For details, see LICENSE file.
    21package org.openstreetmap.josm.gui.dialogs.relation;
    32
     
    109import java.util.LinkedList;
    1110import java.util.List;
     11import java.util.Set;
    1212import java.util.Vector;
    1313import java.util.concurrent.CopyOnWriteArrayList;
     
    1717import javax.swing.table.AbstractTableModel;
    1818
     19import org.openstreetmap.josm.Main;
     20import org.openstreetmap.josm.data.SelectionChangedListener;
    1921import org.openstreetmap.josm.data.osm.DataSet;
    2022import org.openstreetmap.josm.data.osm.Node;
     
    2325import org.openstreetmap.josm.data.osm.RelationMember;
    2426import org.openstreetmap.josm.data.osm.Way;
    25 
    26 public class MemberTableModel extends AbstractTableModel {
     27import org.openstreetmap.josm.gui.layer.OsmDataLayer;
     28
     29public class MemberTableModel extends AbstractTableModel implements SelectionChangedListener{
    2730
    2831    private ArrayList<RelationMember> members;
    2932    private DefaultListSelectionModel listSelectionModel;
    3033    private CopyOnWriteArrayList<IMemberModelListener> listeners;
     34    private OsmDataLayer layer;
    3135
    3236    /**
    3337     * constructor
    3438     */
    35     public MemberTableModel() {
     39    public MemberTableModel(OsmDataLayer layer) {
    3640        members = new ArrayList<RelationMember>();
    3741        listeners = new CopyOnWriteArrayList<IMemberModelListener>();
     42        this.layer = layer;
    3843    }
    3944
     
    8287    public Object getValueAt(int rowIndex, int columnIndex) {
    8388        switch (columnIndex) {
    84         case 0:
    85             return members.get(rowIndex).role;
    86         case 1:
    87             return members.get(rowIndex).member;
    88         case 2:
    89             return linked(rowIndex);
     89            case 0:
     90                return members.get(rowIndex).role;
     91            case 1:
     92                return members.get(rowIndex).member;
     93            case 2:
     94                return linked(rowIndex);
    9095        }
    9196        // should not happen
     
    119124        }
    120125        fireTableDataChanged();
    121         getSelectionModel();
    122         listSelectionModel.clearSelection();
     126        getSelectionModel().setValueIsAdjusting(true);
     127        getSelectionModel().clearSelection();
    123128        for (int row : selectedRows) {
    124129            row--;
    125             listSelectionModel.addSelectionInterval(row, row);
    126         }
     130            getSelectionModel().addSelectionInterval(row, row);
     131        }
     132        getSelectionModel().setValueIsAdjusting(false);
    127133        fireMakeMemberVisible(selectedRows[0] - 1);
    128134    }
     
    141147        fireTableDataChanged();
    142148        getSelectionModel();
    143         listSelectionModel.clearSelection();
     149        getSelectionModel().setValueIsAdjusting(true);
     150        getSelectionModel().clearSelection();
    144151        for (int row : selectedRows) {
    145152            row++;
    146             listSelectionModel.addSelectionInterval(row, row);
    147         }
     153            getSelectionModel().addSelectionInterval(row, row);
     154        }
     155        getSelectionModel().setValueIsAdjusting(false);
    148156        fireMakeMemberVisible(selectedRows[0] + 1);
    149157    }
     
    290298        }
    291299        fireTableDataChanged();
     300        getSelectionModel().setValueIsAdjusting(true);
    292301        getSelectionModel().clearSelection();
    293302        for (int i = 0; i < primitives.size(); i++) {
    294303            getSelectionModel().addSelectionInterval(idx + i, idx + i);
    295304        }
     305        getSelectionModel().setValueIsAdjusting(false);
    296306        fireMakeMemberVisible(idx);
    297307    }
     
    307317        }
    308318        fireTableDataChanged();
     319        getSelectionModel().setValueIsAdjusting(true);
    309320        getSelectionModel().clearSelection();
    310321        for (int i = 0; i < primitives.size(); i++) {
    311322            getSelectionModel().addSelectionInterval(idx + 1 + i, idx + 1 + i);
    312323        }
     324        getSelectionModel().setValueIsAdjusting(false);
    313325        fireMakeMemberVisible(idx + 1);
    314326    }
     
    361373    }
    362374
     375    /**
     376     * Replies the set of selected referers. Never null, but may be empty.
     377     *
     378     * @return the set of selected referers
     379     */
     380    public Set<OsmPrimitive> getSelectedReferers() {
     381        HashSet<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
     382        for (RelationMember m: getSelectedMembers()) {
     383            ret.add(m.member);
     384        }
     385        return ret;
     386    }
     387
     388    /**
     389     * Replies true, if the selected {@see OsmPrimitive}s in the layer belonging
     390     * to this model are in sync with the selected referers in this model.
     391     *
     392     * @return
     393     */
     394    public boolean selectionsAreInSync() {
     395        HashSet<OsmPrimitive> s1 = new HashSet<OsmPrimitive>(getSelectedReferers());
     396        if (s1.size() > layer.data.getSelected().size()) return false;
     397        s1.removeAll(layer.data.getSelected());
     398        return s1.isEmpty();
     399    }
    363400    /**
    364401     * Selects the members in the collection selectedMembers
     
    386423        //
    387424        Collections.sort(selectedIndices);
     425        getSelectionModel().setValueIsAdjusting(true);
    388426        getSelectionModel().clearSelection();
    389427        for (int row : selectedIndices) {
    390428            getSelectionModel().addSelectionInterval(row, row);
    391429        }
     430        getSelectionModel().setValueIsAdjusting(false);
    392431
    393432        // make the first selected member visible
     
    441480        }
    442481        return false;
     482    }
     483
     484    /**
     485     * Selects all mebers which refer to {@see OsmPrimitive}s in the collections
     486     * <code>primitmives</code>. Does nothing is primitives is null.
     487     *
     488     * @param primitives the collection of primitives
     489     */
     490    public void selectMembersReferringTo(Collection<? extends OsmPrimitive> primitives) {
     491        if (primitives == null || primitives.isEmpty()) return;
     492        getSelectionModel().setValueIsAdjusting(true);
     493        getSelectionModel().clearSelection();
     494        for (int i=0; i< members.size();i++) {
     495            RelationMember m = members.get(i);
     496            if (primitives.contains(m.member)) {
     497                this.getSelectionModel().addSelectionInterval(i,i);
     498            }
     499        }
     500        getSelectionModel().setValueIsAdjusting(false);
     501        if (getSelectedIndices().size() > 0) {
     502            fireMakeMemberVisible(getSelectedIndices().get(0));
     503        }
     504    }
     505
     506    /**
     507     * Replies true if the layer this model belongs to is equal to the active
     508     * layer
     509     *
     510     * @return true if the layer this model belongs to is equal to the active
     511     * layer
     512     */
     513    protected boolean isActiveLayer() {
     514        if (Main.map == null || Main.map.mapView == null) return false;
     515        return Main.map.mapView.getActiveLayer() == layer;
     516    }
     517
     518
     519    /* ------------------------------------------------------------------------- */
     520    /* Interface SelectionChangedListener                                        */
     521    /* ------------------------------------------------------------------------- */
     522    public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
     523        // ignore selection change events if they happen for a dataset in another
     524        // layer
     525        if (!isActiveLayer()) return;
     526        selectMembersReferringTo(newSelection);
    443527    }
    444528
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/TagCellEditor.java

    r1762 r1916  
    2424    static private Logger logger = Logger.getLogger(TagCellEditor.class.getName());
    2525
    26     private TagFieldEditor editor = null;
     26    private AutoCompletingTextField editor = null;
    2727    private TagModel currentTag = null;
    2828    private TagEditorModel tagEditorModel = null;
     
    3939     */
    4040    public TagCellEditor() {
    41         editor = new TagFieldEditor();
     41        editor = new AutoCompletingTextField();
    4242        acCache = new AutoCompletionCache();
    4343    }
     
    5959            return;
    6060        }
    61 
    6261        autoCompletionList.clear();
    6362
     
    9089     */
    9190    protected void initAutoCompletionListForValues(String forKey) {
    92 
    9391        if (autoCompletionList == null) {
    9492            logger.warning("autoCompletionList is null. Make sure an instance of AutoCompletionList is injected into TableCellEditor.");
     
    9694        }
    9795        autoCompletionList.clear();
    98 
    9996        for (String value : acCache.getValues(forKey)) {
    10097            autoCompletionList.add(
     
    193190    }
    194191
    195     public TagFieldEditor getEditor() {
     192    public AutoCompletingTextField getEditor() {
    196193        return editor;
    197194    }
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ac/AutoCompletionCache.java

    r1762 r1916  
    33import java.util.ArrayList;
    44import java.util.Collection;
     5import java.util.Collections;
    56import java.util.HashMap;
    67import java.util.List;
     8import java.util.logging.Logger;
    79
    810import org.openstreetmap.josm.data.osm.OsmPrimitive;
     11import org.openstreetmap.josm.data.osm.Relation;
     12import org.openstreetmap.josm.data.osm.RelationMember;
    913import org.openstreetmap.josm.gui.layer.Layer;
    1014import org.openstreetmap.josm.gui.layer.OsmDataLayer;
     
    2933 */
    3034public class AutoCompletionCache {
     35    static private final Logger logger = Logger.getLogger(AutoCompletionCache.class.getName());
    3136
    3237    private static HashMap<OsmDataLayer, AutoCompletionCache> caches;
     38
    3339    static {
    3440        caches = new HashMap<OsmDataLayer, AutoCompletionCache>();
     
    6369    /** the cache */
    6470    private HashMap<String, ArrayList<String>> cache;
     71    private  ArrayList<String> roleCache;
    6572    private OsmDataLayer layer;
    6673
     
    7077    public AutoCompletionCache(OsmDataLayer layer) {
    7178        cache = new HashMap<String, ArrayList<String>>();
     79        roleCache = new ArrayList<String>();
    7280        this.layer = layer;
    7381    }
     
    114122            String value = primitive.get(key);
    115123            cacheValue(key, value);
     124        }
     125    }
     126
     127    /**
     128     * Caches all member roles of the relation <code>relation</code>
     129     *
     130     * @param relation the relation
     131     */
     132    protected void cacheRelationMemberRoles(Relation relation){
     133        for (RelationMember m: relation.members) {
     134            if (m.role == null || m.role.trim().equals("")) {
     135                continue;
     136            }
     137            if (!roleCache.contains(m.role)) {
     138                roleCache.add(m.role);
     139            }
    116140        }
    117141    }
     
    130154            cachePrimitive(primitive);
    131155        }
     156        for (Relation relation : layer.data.relations) {
     157            if (relation.incomplete || relation.deleted) {
     158                continue;
     159            }
     160            cacheRelationMemberRoles(relation);
     161            Collections.sort(roleCache);
     162        }
    132163    }
    133164
     
    151182        if (!cache.containsKey(key))
    152183            return new ArrayList<String>();
    153         else
    154             return cache.get(key);
     184        return cache.get(key);
     185    }
     186
     187    /**
     188     * Replies the list of member roles
     189     *
     190     * @return the list of member roles
     191     */
     192    public List<String> getMemberRoles() {
     193        return roleCache;
     194    }
     195
     196    /**
     197     * Populates the an {@see AutoCompletionList} with the currently cached
     198     * member roles.
     199     *
     200     * @param list the list to populate
     201     */
     202    public void populateWithMemberRoles(AutoCompletionList list) {
     203        list.clear();
     204        for (String role: roleCache) {
     205            list.add(new AutoCompletionListItem(role, AutoCompletionItemPritority.IS_IN_DATASET));
     206        }
    155207    }
    156208}
Note: See TracChangeset for help on using the changeset viewer.