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 }