Ignore:
Timestamp:
2011-10-07T21:55:13+02:00 (8 years ago)
Author:
simon04
Message:

see #6774 - Make History dialog more diff-like (patch by olejorgenb)

Location:
trunk/src/org/openstreetmap/josm/gui/history
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserModel.java

    r4406 r4498  
    77import java.util.Collections;
    88import java.util.HashSet;
     9import java.util.List;
    910import java.util.Observable;
    1011
    11 import javax.swing.table.DefaultTableModel;
     12import javax.swing.table.AbstractTableModel;
    1213
    1314import org.openstreetmap.josm.Main;
     
    1516import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1617import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
    17 import org.openstreetmap.josm.data.osm.PrimitiveId;
    1818import org.openstreetmap.josm.data.osm.Relation;
    1919import org.openstreetmap.josm.data.osm.RelationMember;
    20 import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
    2120import org.openstreetmap.josm.data.osm.Way;
    2221import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
     
    4039import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    4140import org.openstreetmap.josm.tools.CheckParameterUtil;
     41import org.openstreetmap.josm.tools.Diff;
    4242
    4343/**
     
    8080    private TagTableModel currentTagTableModel;
    8181    private TagTableModel referenceTagTableModel;
    82     private NodeListTableModel currentNodeListTableModel;
    83     private NodeListTableModel referenceNodeListTableModel;
    8482    private RelationMemberTableModel currentRelationMemberTableModel;
    8583    private RelationMemberTableModel referenceRelationMemberTableModel;
     84    private DiffTableModel referenceNodeListTableModel;
     85    private DiffTableModel currentNodeListTableModel;
    8686
    8787    /**
     
    9292        currentTagTableModel = new TagTableModel(PointInTimeType.CURRENT_POINT_IN_TIME);
    9393        referenceTagTableModel = new TagTableModel(PointInTimeType.REFERENCE_POINT_IN_TIME);
    94         currentNodeListTableModel = new NodeListTableModel(PointInTimeType.CURRENT_POINT_IN_TIME);
    95         referenceNodeListTableModel = new NodeListTableModel(PointInTimeType.REFERENCE_POINT_IN_TIME);
     94        referenceNodeListTableModel = new DiffTableModel();
     95        currentNodeListTableModel = new DiffTableModel();
    9696        currentRelationMemberTableModel = new RelationMemberTableModel(PointInTimeType.CURRENT_POINT_IN_TIME);
    9797        referenceRelationMemberTableModel = new RelationMemberTableModel(PointInTimeType.REFERENCE_POINT_IN_TIME);
     
    146146        if (primitive == null) return false;
    147147        if (primitive.isNew() || !primitive.isUsable()) return false;
    148        
     148
    149149        //try creating a history primitive. if that fails, the primitive cannot be used.
    150150        try {
     
    196196
    197197    protected void fireModelChange() {
     198        initNodeListTableModels();
    198199        setChanged();
    199200        notifyObservers();
     
    216217    }
    217218
    218     protected void initNodeListTabeModels() {
     219    /**
     220     * Should be called everytime either reference of current changes to update the diff.
     221     * TODO: Maybe rename to reflect this? eg. updateNodeListTableModels
     222     */
     223    protected void initNodeListTableModels() {
     224
     225        if(current.getType() != OsmPrimitiveType.WAY || reference.getType() != OsmPrimitiveType.WAY)
     226            return;
     227        TwoColumnDiff diff = new TwoColumnDiff(
     228                ((HistoryWay)reference).getNodes().toArray(),
     229                ((HistoryWay)current).getNodes().toArray());
     230        referenceNodeListTableModel.setRows(diff.referenceDiff);
     231        currentNodeListTableModel.setRows(diff.currentDiff);
     232
     233        referenceNodeListTableModel.fireTableDataChanged();
    219234        currentNodeListTableModel.fireTableDataChanged();
    220         referenceNodeListTableModel.fireTableDataChanged();
    221235    }
    222236
     
    244258    }
    245259
    246     public NodeListTableModel getNodeListTableModel(PointInTimeType pointInTimeType) throws IllegalArgumentException {
     260    public DiffTableModel getNodeListTableModel(PointInTimeType pointInTimeType) throws IllegalArgumentException {
    247261        CheckParameterUtil.ensureParameterNotNull(pointInTimeType, "pointInTimeType");
    248262        if (pointInTimeType.equals(PointInTimeType.CURRENT_POINT_IN_TIME))
     
    290304        this.reference = reference;
    291305        initTagTableModels();
    292         initNodeListTabeModels();
     306        initNodeListTableModels();
    293307        initMemberListTableModels();
    294308        setChanged();
     
    319333        this.current = current;
    320334        initTagTableModels();
    321         initNodeListTabeModels();
     335        initNodeListTableModels();
    322336        initMemberListTableModels();
    323337        setChanged();
     
    378392     *
    379393     */
    380     public class VersionTableModel extends DefaultTableModel{
     394    public class VersionTableModel extends AbstractTableModel {
    381395
    382396        private VersionTableModel() {
     
    459473            return p;
    460474        }
     475
     476        @Override
     477        public int getColumnCount() {
     478            return 1;
     479        }
    461480    }
    462481
     
    466485     *
    467486     */
    468     public class TagTableModel extends DefaultTableModel {
     487    public class TagTableModel extends AbstractTableModel {
    469488
    470489        private ArrayList<String> keys;
     
    554573            return pointInTimeType.equals(PointInTimeType.REFERENCE_POINT_IN_TIME);
    555574        }
    556     }
    557 
    558     /**
    559      * The table model for the nodes of the version at {@see PointInTimeType#REFERENCE_POINT_IN_TIME}
    560      * or {@see PointInTimeType#CURRENT_POINT_IN_TIME}
    561      *
    562      */
    563     public class NodeListTableModel extends DefaultTableModel {
    564 
    565         private PointInTimeType pointInTimeType;
    566 
    567         private NodeListTableModel(PointInTimeType pointInTimeType) {
    568             this.pointInTimeType = pointInTimeType;
    569         }
    570 
    571         @Override
    572         public int getRowCount() {
    573             int n = 0;
    574             if (current != null && current.getType().equals(OsmPrimitiveType.WAY)) {
    575                 n = ((HistoryWay)current).getNumNodes();
    576             }
    577             if (reference != null && reference.getType().equals(OsmPrimitiveType.WAY)) {
    578                 n = Math.max(n,((HistoryWay)reference).getNumNodes());
    579             }
    580             return n;
    581         }
    582 
    583         protected HistoryWay getWay() {
    584             if (pointInTimeType.equals(PointInTimeType.CURRENT_POINT_IN_TIME)) {
    585                 if (! current.getType().equals(OsmPrimitiveType.WAY))
    586                     return null;
    587                 return (HistoryWay)current;
    588             }
    589             if (pointInTimeType.equals(PointInTimeType.REFERENCE_POINT_IN_TIME)) {
    590                 if (! reference.getType().equals(OsmPrimitiveType.WAY))
    591                     return null;
    592                 return (HistoryWay)reference;
    593             }
    594 
    595             // should not happen
    596             return null;
    597         }
    598 
    599         protected HistoryWay getOppositeWay() {
    600             PointInTimeType opposite = pointInTimeType.opposite();
    601             if (opposite.equals(PointInTimeType.CURRENT_POINT_IN_TIME)) {
    602                 if (! current.getType().equals(OsmPrimitiveType.WAY))
    603                     return null;
    604                 return (HistoryWay)current;
    605             }
    606             if (opposite.equals(PointInTimeType.REFERENCE_POINT_IN_TIME)) {
    607                 if (! reference.getType().equals(OsmPrimitiveType.WAY))
    608                     return null;
    609                 return (HistoryWay)reference;
    610             }
    611 
    612             // should not happen
    613             return null;
    614         }
    615 
    616         @Override
    617         public Object getValueAt(int row, int column) {
    618             HistoryWay way = getWay();
    619             if (way == null)
    620                 return null;
    621             if (row >= way.getNumNodes())
    622                 return null;
    623             return way.getNodes().get(row);
    624         }
    625 
    626         public PrimitiveId getNodeId(int row) {
    627             HistoryWay way = getWay();
    628             if (way == null) return null;
    629             if (row > way.getNumNodes()) return null;
    630             return new SimplePrimitiveId(way.getNodeId(row), OsmPrimitiveType.NODE);
    631         }
    632 
    633         @Override
    634         public boolean isCellEditable(int row, int column) {
    635             return false;
    636         }
    637 
    638         public boolean isSameInOppositeWay(int row) {
    639             HistoryWay thisWay = getWay();
    640             HistoryWay oppositeWay = getOppositeWay();
    641             if (thisWay == null || oppositeWay == null)
    642                 return false;
    643             if (row >= oppositeWay.getNumNodes())
    644                 return false;
    645             return thisWay.getNodeId(row) == oppositeWay.getNodeId(row);
    646         }
    647 
    648         public boolean isInOppositeWay(int row) {
    649             HistoryWay thisWay = getWay();
    650             HistoryWay oppositeWay = getOppositeWay();
    651             if (thisWay == null || oppositeWay == null)
    652                 return false;
    653             return oppositeWay.getNodes().contains(thisWay.getNodeId(row));
     575
     576        @Override
     577        public int getColumnCount() {
     578            return 1;
    654579        }
    655580    }
     
    661586     */
    662587
    663     public class RelationMemberTableModel extends DefaultTableModel {
     588    public class RelationMemberTableModel extends AbstractTableModel {
    664589
    665590        private PointInTimeType pointInTimeType;
     
    748673            return oppositeRelation.getMembers().contains(thisRelation.getMembers().get(row));
    749674        }
     675
     676        @Override
     677        public int getColumnCount() {
     678            return 1;
     679        }
    750680    }
    751681
     
    918848    }
    919849}
     850
     851/**
     852 * Simple model storing "diff cells" in a list. Could probably have used a DefaultTableModel instead..
     853 *
     854 * {@see NodeListDiffTableCellRenderer}
     855 */
     856class DiffTableModel extends AbstractTableModel {
     857    private List<TwoColumnDiff.Item> rows;
     858
     859    public void setRows(List<TwoColumnDiff.Item> rows) {
     860        this.rows = rows;
     861    }
     862
     863    public DiffTableModel(List<TwoColumnDiff.Item> rows) {
     864        this.rows = rows;
     865    }
     866    public DiffTableModel() {
     867        this.rows = new ArrayList<TwoColumnDiff.Item>();
     868    }
     869    @Override
     870    public int getRowCount() {
     871        return rows.size();
     872    }
     873
     874    @Override
     875    public int getColumnCount() {
     876        return 1;
     877    }
     878
     879    @Override
     880    public TwoColumnDiff.Item getValueAt(int rowIndex, int columnIndex) {
     881        return rows.get(rowIndex);
     882    }
     883}
     884
     885
     886/// Feel free to move me somewhere else. Maybe a bit specific for josm.tools?
     887/**
     888 * Produces a "two column diff" of two lists. (same as diff -y)
     889 *
     890 * Each list is annotated with the changes relative to the other, and "empty" cells are inserted so the lists are comparable item by item.
     891 *
     892 * diff on [1 2 3 4] [1 a 4 5] yields:
     893 *
     894 * item(SAME, 1)    item(SAME, 1)
     895 * item(CHANGED, 2) item(CHANGED, 2)
     896 * item(DELETED, 3) item(EMPTY)
     897 * item(SAME, 4)    item(SAME, 4)
     898 * item(EMPTY)      item(INSERTED, 5)
     899 *
     900 * @author olejorgenb
     901 */
     902class TwoColumnDiff {
     903    public static class Item {
     904        public static final int INSERTED = 1;
     905        public static final int DELETED = 2;
     906        public static final int CHANGED = 3;
     907        public static final int SAME = 4;
     908        public static final int EMPTY = 5; // value should be null
     909        public Item(int state, Object value) {
     910            this.state = state;
     911            this.value = state == EMPTY ? null : value;
     912        }
     913
     914        public final Object value;
     915        public final int state;
     916    }
     917
     918    public ArrayList<Item> referenceDiff;
     919    public ArrayList<Item> currentDiff;
     920    Object[] reference;
     921    Object[] current;
     922
     923    /**
     924     * The arguments will _not_ be modified
     925     */
     926    public TwoColumnDiff(Object[] reference, Object[] current) {
     927        this.reference = reference;
     928        this.current = current;
     929        referenceDiff = new ArrayList<Item>();
     930        currentDiff = new ArrayList<Item>();
     931        diff();
     932    }
     933    private void diff() {
     934        Diff diff = new Diff(reference, current);
     935        Diff.change script = diff.diff_2(false);
     936        twoColumnDiffFromScript(script, reference, current);
     937    }
     938
     939    /**
     940     * The result from the diff algorithm is a "script" (a compressed description of the changes)
     941     * This method expands this script into a full two column description.
     942     */
     943    private void twoColumnDiffFromScript(Diff.change script, Object[] a, Object[] b) {
     944        int ia = 0;
     945        int ib = 0;
     946
     947        while(script != null) {
     948            int deleted = script.deleted;
     949            int inserted = script.inserted;
     950            while(ia < script.line0 && ib < script.line1){
     951                // System.out.println(" "+a[ia] + "\t "+b[ib]);
     952                Item cell = new Item(Item.SAME, a[ia]);
     953                referenceDiff.add(cell);
     954                currentDiff.add(cell);
     955                ia++;
     956                ib++;
     957            }
     958
     959            while(inserted > 0 || deleted > 0) {
     960                if(inserted > 0 && deleted > 0) {
     961                    // System.out.println("="+a[ia] + "\t="+b[ib]);
     962                    referenceDiff.add(new Item(Item.CHANGED, a[ia++]));
     963                    currentDiff.add(new Item(Item.CHANGED, b[ib++]));
     964                } else if(inserted > 0) {
     965                    // System.out.println("\t+" + b[ib]);
     966                    referenceDiff.add(new Item(Item.EMPTY, null));
     967                    currentDiff.add(new Item(Item.INSERTED, b[ib++]));
     968                } else if(deleted > 0) {
     969                    // System.out.println("-"+a[ia]);
     970                    referenceDiff.add(new Item(Item.DELETED, a[ia++]));
     971                    currentDiff.add(new Item(Item.EMPTY, null));
     972                }
     973                inserted--;
     974                deleted--;
     975            }
     976            script = script.link;
     977        }
     978        while(ia < a.length && ib < b.length) {
     979            // System.out.println((ia < a.length ? " "+a[ia]+"\t" : "\t") + (ib < b.length ? " "+b[ib] : ""));
     980            referenceDiff.add(new Item(Item.SAME, a[ia++]));
     981            currentDiff.add(new Item(Item.SAME, b[ib++]));
     982        }
     983    }
     984}
  • trunk/src/org/openstreetmap/josm/gui/history/NodeListTableCellRenderer.java

    r4072 r4498  
    1414import org.openstreetmap.josm.tools.ImageProvider;
    1515
    16 /**
    17  * The {@see TableCellRenderer} for a list of nodes in [@see HistoryBrower}
    18  *
    19  *
    20  */
    2116public class NodeListTableCellRenderer extends JLabel implements TableCellRenderer {
    2217
    2318    public final static Color BGCOLOR_EMPTY_ROW = new Color(234,234,234);
    24     public final static Color BGCOLOR_NOT_IN_OPPOSITE = new Color(255,197,197);
    25     public final static Color BGCOLOR_IN_OPPOSITE = new Color(255,234,213);
     19    public final static Color BGCOLOR_DELETED = new Color(255,197,197);
     20    public final static Color BGCOLOR_INSERTED = new Color(0xDD, 0xFF, 0xDD);
     21    public final static Color BGCOLOR_CHANGED = new Color(255,234,213);
    2622    public final static Color BGCOLOR_SELECTED = new Color(143,170,255);
    2723
     
    3430    }
    3531
    36     protected void renderNode(HistoryBrowserModel.NodeListTableModel model, Long nodeId, int row, boolean isSelected) {
     32    protected void renderNode(TwoColumnDiff.Item item, boolean isSelected) {
    3733        String text = "";
    3834        Color bgColor = Color.WHITE;
    39         if (nodeId == null) {
     35        setIcon(nodeIcon);
     36        if (item.value != null) {
     37            text = tr("Node {0}", item.value.toString());
     38        }
     39        switch(item.state) {
     40        case TwoColumnDiff.Item.EMPTY:
    4041            text = "";
    4142            bgColor = BGCOLOR_EMPTY_ROW;
    4243            setIcon(null);
    43         } else {
    44             text = tr("Node {0}", nodeId.toString());
    45             setIcon(nodeIcon);
    46             if (model.isSameInOppositeWay(row)) {
    47                 bgColor = Color.WHITE;
    48             } else if (model.isInOppositeWay(row)) {
    49                 bgColor = BGCOLOR_IN_OPPOSITE;
    50             } else {
    51                 bgColor = BGCOLOR_NOT_IN_OPPOSITE;
    52             }
     44            break;
     45        case TwoColumnDiff.Item.CHANGED:
     46            bgColor = BGCOLOR_CHANGED;
     47            break;
     48        case TwoColumnDiff.Item.INSERTED:
     49            bgColor = BGCOLOR_INSERTED;
     50            break;
     51        case TwoColumnDiff.Item.DELETED:
     52            bgColor = BGCOLOR_DELETED;
     53            break;
     54        default:
     55            bgColor = BGCOLOR_EMPTY_ROW;
    5356        }
    5457        if (isSelected) {
     
    6467            return this;
    6568
    66         HistoryBrowserModel.NodeListTableModel model = getNodeListTableModel(table);
    67         Long nodeId = (Long)value;
    68         renderNode(model, nodeId, row, isSelected);
     69        renderNode((TwoColumnDiff.Item)value, isSelected);
    6970        return this;
    7071    }
    71 
    72     protected HistoryBrowserModel.NodeListTableModel getNodeListTableModel(JTable table) {
    73         return (HistoryBrowserModel.NodeListTableModel) table.getModel();
    74     }
    7572}
  • trunk/src/org/openstreetmap/josm/gui/history/NodeListTableColumnModel.java

    r3083 r4498  
    66import javax.swing.table.DefaultTableColumnModel;
    77import javax.swing.table.TableColumn;
     8
    89
    910/**
  • trunk/src/org/openstreetmap/josm/gui/history/NodeListViewer.java

    r3327 r4498  
    1818import javax.swing.JTable;
    1919import javax.swing.ListSelectionModel;
     20import javax.swing.table.TableModel;
    2021
    2122import org.openstreetmap.josm.Main;
    2223import org.openstreetmap.josm.actions.AutoScaleAction;
    2324import org.openstreetmap.josm.data.osm.OsmPrimitive;
     25import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
    2426import org.openstreetmap.josm.data.osm.PrimitiveId;
     27import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
    2528import org.openstreetmap.josm.data.osm.history.History;
    2629import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
    27 import org.openstreetmap.josm.gui.history.HistoryBrowserModel.NodeListTableModel;
    2830import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    2931import org.openstreetmap.josm.tools.ImageProvider;
     
    274276    }
    275277
     278    static private PrimitiveId primitiveIdAtRow(TableModel model, int row) {
     279        DiffTableModel castedModel = (DiffTableModel) model;
     280        Long id = (Long)castedModel.getValueAt(row, 0).value;
     281        if(id == null) return null;
     282        return new SimplePrimitiveId(id, OsmPrimitiveType.NODE);
     283    }
     284
    276285    class PopupMenuLauncher extends MouseAdapter {
    277286        private JTable table;
     
    295304            Point p = e.getPoint();
    296305            int row = table.rowAtPoint(p);
    297             NodeListTableModel model = (NodeListTableModel) table.getModel();
    298             PrimitiveId pid = model.getNodeId(row);
     306
     307            PrimitiveId pid = primitiveIdAtRow(table.getModel(), row);
     308            if (pid == null)
     309                return;
    299310            popupMenu.prepare(pid);
    300311            popupMenu.show(e.getComponent(), e.getX(), e.getY());
     
    309320            this.table = table;
    310321            showHistoryAction = new ShowHistoryAction();
    311         }
    312 
    313         protected NodeListTableModel getModel() {
    314             return (NodeListTableModel)table.getModel();
    315322        }
    316323
     
    319326            if (e.getClickCount() < 2) return;
    320327            int row = table.rowAtPoint(e.getPoint());
    321             PrimitiveId pid = getModel().getNodeId(row);
     328            if(row <= 0) return;
     329            PrimitiveId pid = primitiveIdAtRow(table.getModel(), row);
    322330            if (pid == null)
    323331                return;
  • trunk/src/org/openstreetmap/josm/gui/history/VersionTable.java

    r4406 r4498  
    7272        public void valueChanged(ListSelectionEvent e) {
    7373            DefaultListSelectionModel model = (DefaultListSelectionModel)e.getSource();
    74             if (model.getMinSelectionIndex() >= 0) {
     74            // For some reason we receive multiple "adjusting" events here even when the source is a simple "set selection" action
     75            // The last and proper event will have getValueIsAdjusting() == false
     76            if (model.getMinSelectionIndex() >= 0 && e.getValueIsAdjusting() == false) {
    7577                handleSelectCurrentPointInTime(model.getMinSelectionIndex());
    7678            }
Note: See TracChangeset for help on using the changeset viewer.