001    // License: GPL. For details, see LICENSE file.
002    package org.openstreetmap.josm.gui.conflict.pair.nodes;
003    
004    import java.awt.Component;
005    import java.text.MessageFormat;
006    import java.util.ArrayList;
007    import java.util.Collections;
008    
009    import javax.swing.BorderFactory;
010    import javax.swing.ImageIcon;
011    import javax.swing.JLabel;
012    import javax.swing.JTable;
013    import javax.swing.border.Border;
014    import javax.swing.table.TableCellRenderer;
015    
016    import org.openstreetmap.josm.data.osm.Node;
017    import org.openstreetmap.josm.data.osm.OsmPrimitive;
018    import org.openstreetmap.josm.gui.DefaultNameFormatter;
019    import org.openstreetmap.josm.gui.conflict.ConflictColors;
020    import org.openstreetmap.josm.gui.conflict.pair.ListMergeModel;
021    import org.openstreetmap.josm.tools.ImageProvider;
022    
023    /**
024     * This is the {@link TableCellRenderer} used in the node tables of {@link NodeListMerger}.
025     *
026     */
027    public  class NodeListTableCellRenderer extends JLabel implements TableCellRenderer {
028    
029        private final ImageIcon icon;
030        private final Border rowNumberBorder;
031    
032        /**
033         * constructor
034         */
035        public NodeListTableCellRenderer() {
036            icon = ImageProvider.get("data", "node");
037            rowNumberBorder = BorderFactory.createEmptyBorder(0,4,0,0);
038            setOpaque(true);
039        }
040    
041        /**
042         * build the tool tip text for an {@link OsmPrimitive}. It consist of the formatted
043         * key/value pairs for this primitive.
044         *
045         * @param primitive
046         * @return the tool tip text
047         */
048        public String buildToolTipText(OsmPrimitive primitive) {
049            StringBuilder sb = new StringBuilder();
050    
051            sb.append("<html>");
052            // show the id
053            //
054            sb.append("<strong>id</strong>=")
055            .append(primitive.getId())
056            .append("<br>");
057    
058            // show the key/value-pairs, sorted by key
059            //
060            ArrayList<String> keyList = new ArrayList<String>(primitive.keySet());
061            Collections.sort(keyList);
062            for (int i = 0; i < keyList.size(); i++) {
063                if (i > 0) {
064                    sb.append("<br>");
065                }
066                String key = keyList.get(i);
067                sb.append("<strong>")
068                .append(key)
069                .append("</strong>")
070                .append("=");
071                // make sure long values are split into several rows. Otherwise
072                // the tool tip window can become to wide
073                //
074                String value = primitive.get(key);
075                while(value.length() != 0) {
076                    sb.append(value.substring(0,Math.min(50, value.length())));
077                    if (value.length() > 50) {
078                        sb.append("<br>");
079                        value = value.substring(50);
080                    } else {
081                        value = "";
082                    }
083                }
084            }
085            sb.append("</html>");
086            return sb.toString();
087        }
088    
089        /**
090         * reset the renderer
091         */
092        protected void reset() {
093            setBackground(ConflictColors.BGCOLOR.get());
094            setForeground(ConflictColors.FGCOLOR.get());
095            setBorder(null);
096            setIcon(null);
097            setToolTipText(null);
098        }
099    
100        /**
101         * render a node
102         * @param model  the model
103         * @param node the node
104         * @param isSelected true, if the current row is selected
105         */
106        protected  void renderNode(ListMergeModel<Node>.EntriesTableModel model, Node node, int row, boolean isSelected) {
107            setIcon(icon);
108            setBorder(null);
109            if (model.getListMergeModel().isFrozen()) {
110                setBackground(ConflictColors.BGCOLOR_FROZEN.get());
111            } else if (isSelected) {
112                setBackground(ConflictColors.BGCOLOR_SELECTED.get());
113            } else if (model.isParticipatingInCurrentComparePair()) {
114                if (model.isSamePositionInOppositeList(row)) {
115                    setBackground(ConflictColors.BGCOLOR_SAME_POSITION_IN_OPPOSITE.get());
116                } else if (model.isIncludedInOppositeList(row)) {
117                    setBackground(ConflictColors.BGCOLOR_IN_OPPOSITE.get());
118                } else {
119                    setBackground(ConflictColors.BGCOLOR_NOT_IN_OPPOSITE.get());
120                }
121            }
122            setText(node.getDisplayName(DefaultNameFormatter.getInstance()));
123            setToolTipText(buildToolTipText(node));
124        }
125    
126        /**
127         * render an empty row
128         */
129        protected void renderEmptyRow() {
130            setIcon(null);
131            setBackground(ConflictColors.BGCOLOR_EMPTY_ROW.get());
132            setText("");
133        }
134    
135        /**
136         * render the row id
137         * @param model  the model
138         * @param row the row index
139         * @param isSelected true, if the current row is selected
140         */
141        protected  void renderRowId( ListMergeModel<Node>.EntriesTableModel model, int row, boolean isSelected) {
142            setIcon(null);
143            setBorder(rowNumberBorder);
144            if (model.getListMergeModel().isFrozen()) {
145                setBackground(ConflictColors.BGCOLOR_FROZEN.get());
146            } else if (model.isParticipatingInCurrentComparePair()) {
147                setBackground(ConflictColors.BGCOLOR_PARTICIPAING_IN_COMPARISON.get());
148                setForeground(ConflictColors.FGCOLOR_PARTICIPAING_IN_COMPARISON.get());
149            }
150            setText(Integer.toString(row+1));
151        }
152    
153        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
154                int row, int column) {
155    
156            Node node = (Node)value;
157            reset();
158            if (node == null) {
159                renderEmptyRow();
160            } else {
161                switch(column) {
162                case 0:
163                    renderRowId(getModel(table),row, isSelected);
164                    break;
165                case 1:
166                    renderNode(getModel(table), node, row, isSelected);
167                    break;
168                default:
169                    // should not happen
170                    throw new RuntimeException(MessageFormat.format("Unexpected column index. Got {0}.", column));
171                }
172            }
173            return this;
174        }
175    
176        /**
177         * replies the model
178         * @param table  the table
179         * @return the table model
180         */
181        @SuppressWarnings("unchecked")
182        protected ListMergeModel<Node>.EntriesTableModel getModel(JTable table) {
183            return (ListMergeModel.EntriesTableModel)table.getModel();
184        }
185    }