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