Changeset 4498 in josm


Ignore:
Timestamp:
Oct 7, 2011 9:55:13 PM (20 months 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.