1// BEGIN LICENSE BLOCK 2// Version: CMPL 1.1 3// 4// The contents of this file are subject to the Cisco-style Mozilla Public 5// License Version 1.1 (the "License"); you may not use this file except 6// in compliance with the License. You may obtain a copy of the License 7// at www.eclipse-clp.org/license. 8// 9// Software distributed under the License is distributed on an "AS IS" 10// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 11// the License for the specific language governing rights and limitations 12// under the License. 13// 14// The Original Code is The ECLiPSe Constraint Logic Programming System. 15// The Initial Developer of the Original Code is Cisco Systems, Inc. 16// Portions created by the Initial Developer are 17// Copyright (C) 2006 Cisco Systems, Inc. All Rights Reserved. 18// 19// Contributor(s): 20// 21// END LICENSE BLOCK 22 23package com.parctechnologies.eclipse.visualisation; 24 25 26import java.util.*; 27import java.awt.*; 28import java.beans.*; 29import java.awt.event.*; 30import javax.swing.*; 31import javax.swing.table.*; 32import javax.swing.event.*; 33import javax.swing.border.*; 34import com.parctechnologies.eclipse.*; 35import com.parctechnologies.eclipse.visualisation.*; 36 37/** 38 * Extends the JTable swing componennt to provide a more spread sheet 39 * like functionality. 40 * <P> 41 * Includes RowHeader equivalents to the column header of JTable. 42 **/ 43public class SpreadSheet extends JTable { 44 45 /** Implements the table row hedaers */ 46 protected Component tableRowHeader ; 47 48 public SpreadSheet(TableModel dm, 49 TableColumnModel cm, 50 ListSelectionModel sm) { 51 super(dm, cm, sm); 52 } 53 54 /** 55 * If this <code>JTable</code> is the <code>viewportView</code> of an enclosing <code>JScrollPane</code> 56 * (the usual situation), configure this <code>ScrollPane</code> by, amongst other things, 57 * installing the table's <code>tableHeader</code> as the <code>columnHeaderView</code> of the scroll pane. 58 * When a <code>JTable</code> is added to a <code>JScrollPane</code> in the usual way, 59 * using <code>new JScrollPane(myTable)</code>, <code>addNotify</code> is 60 * called in the <code>JTable</code> (when the table is added to the viewport). 61 * <code>JTable</code>'s <code>addNotify</code> method in turn calls this method, 62 * which is protected so that this default installation procedure can 63 * be overridden by a subclass. 64 * 65 * @see #addNotify 66 */ 67 protected void configureEnclosingScrollPane() { 68 super.configureEnclosingScrollPane(); 69 Container p = getParent(); 70 if (p instanceof JViewport) { 71 Container gp = p.getParent(); 72 if (gp instanceof JScrollPane) { 73 JScrollPane scrollPane = (JScrollPane)gp; 74 // Make certain we are the viewPort's view and not, for 75 // example, the rowHeaderView of the scrollPane - 76 // an implementor of fixed columns might do this. 77 JViewport viewport = scrollPane.getViewport(); 78 if (viewport == null || viewport.getView() != this) { 79 return; 80 } 81 //scrollPane.setRowHeaderView(getTableHeader()); 82 Component header = getTableRowHeader(); 83 scrollPane.setRowHeaderView(header); 84 scrollPane.getRowHeader().setPreferredSize(header.getPreferredSize()); 85 } 86 } 87 } 88 89 /** 90 * Returns the table row header component 91 * 92 */ 93 protected Component getTableRowHeader() { 94 if (tableRowHeader == null) { 95 // initialy just return a table set to display the row headers 96 RowHeaderModel model = new RowHeaderModel(); 97 JTable table = new JTable(model); 98 // Add a listener to the main TableModel to listen for 99 // row-change events and to update the row header 100 // accordingly 101 getModel().addTableModelListener(model); 102 103 // Set theintercell spacing to zero 104 table.setIntercellSpacing(new Dimension(0,0)); 105 table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 106 table.setCellSelectionEnabled(false); 107 table.setOpaque(false); 108 TableColumn column = table.getColumn("row"); 109 110 // Set the renderer for the row headings 111 TableCellRenderer headerRenderer = 112 new RowHeaderCellRenderer(); 113 // Works in JDK 1.3 but not JDK 1.2 114 //TableCellRenderer headerRenderer = 115 // getTableHeader().getDefaultRenderer(); 116 117 //headerRenderer.setBackground(getParent().getBackground()); 118 column.setCellRenderer(headerRenderer); 119 tableRowHeader = table; 120 } 121 return tableRowHeader; 122 } 123 124 /** 125 * Set the height of each row 126 */ 127 public void setRowHeight(int height) { 128 DebuggingSupport.logMessage(this, "setRowHeight for height="+height); 129 // resize the row headers aswell 130 ((JTable)getTableRowHeader()).setRowHeight(height); 131 super.setRowHeight(height); 132 } 133 134 /** 135 * Set the width of each column 136 */ 137 public void setColumnWidth(int width) { 138 DebuggingSupport.logMessage(this, "setColumnWidth for width="+width); 139 final int col_count = getColumnCount(); 140 final TableColumnModel col_model = getColumnModel(); 141 for(int i = 0; i < col_count; i++) { 142 col_model.getColumn(i).setPreferredWidth(width); 143 } 144 } 145 146 /** 147 * Set the height of each row 148 */ 149 public void setRowHeight(int row, int height) { 150 // resize the row headers aswell 151 // only works in JDK 1.3 152 //if (tableRowHeader != null) { 153 // ((JTable)tableRowHeader).setRowHeight(row, height); 154 //} 155 //super.setRowHeight(row, height); 156 } 157 158 /** 159 * Get the height of each row 160 */ 161 public int getRowHeight(int row) { 162 int height ; 163 // resize the row headers aswell 164 //if (tableRowHeader == null) { 165 height = super.getRowHeight(); 166 //} else { 167 //height = ((JTable)tableRowHeader).getRowHeight(row); 168 //} 169 return height; 170 } 171 172 /** 173 * Get the height of any table row 174 */ 175 public int getRowHeight() { 176 return getRowHeight(0); 177 } 178 179 180 /** 181 * Resize columns to fit width 182 */ 183 public void fitWidth() { 184 final int total = getParent().getWidth(); 185 final int col_count = getColumnCount(); 186 final TableColumnModel col_model = getColumnModel(); 187 for(int i = 0; i < col_count; i++) { 188 col_model.getColumn(i). 189 setPreferredWidth((((i+1)*total)/col_count)-((i*total)/col_count)); 190 } 191 } 192 193 /** 194 * Resize rows to fit height 195 */ 196 public void fitHeight() { 197 final int total = getParent().getHeight(); 198 final int row_count = getRowCount(); 199 // Poorly supported variable row width code, so cannot set each 200 // row seperatly and must use a single common value 201 //for(int i = 0; i < row_count; i++) { 202 // setRowHeight(i, (((i+1)*total)/row_count)-((i*total)/row_count)); 203 //} 204 setRowHeight(Math.max(3,total/row_count)); 205 } 206 207 208// /** 209// * Find bounding box for these elements within the spreadsheet. 210// * @return Rectangle where x=min column, y = min row, w=number of 211// * columns and h=number of rows 212// */ 213// public Rectangle findBoundingBox(Collection elements) { 214// SpreadSheetModel model = (SpreadSheetModel)(getModel()); 215// int col1 = Integer.MAX_VALUE; 216// int row1 = Integer.MAX_VALUE; 217// int col2 = -1; 218// int row2 = -1; 219// boolean changedObjectMidWay = false; 220// Object o = null; 221// for(Iterator it = elements.iterator(); it.hasNext(); ) { 222// if (!changedObjectMidWay) { 223// o = it.next(); 224// } else { 225// changedObjectMidWay = false; 226// } 227// int c=0; 228// int r=0; 229// for(r = 0; (r < model.getRowCount()) && (o != null); r++) { 230// for(c = 0; c < model.getColumnCount(); c++) { 231// if (getValueAt(r,c) == o) { 232// // found one 233// if (c < col1) { 234// col1 = c; 235// } 236// if (c > col2) { 237// col2 = c; 238// } 239// if (r < row1) { 240// row1 = r; 241// } 242// if (r > row2) { 243// row2 = r; 244// } 245// if (it.hasNext()) { 246// o = it.next(); 247// changedObjectMidWay = true; 248// } else { 249// o = null; 250// break; 251// } 252// } 253// } 254// } 255// } 256// return new Rectangle(col1,row1,(col2-col1)+1,(row2-row1)+1); 257// } 258 259 260 protected class RowHeaderModel 261 extends AbstractTableModel 262 implements TableModelListener { 263 public int getRowCount() { return getModel().getRowCount(); } 264 public int getColumnCount() { return 1; } 265 public String getColumnName(int col) { return "row"; } 266 public Object getValueAt(int row, int col) { 267 return ((SpreadSheetModel)getModel()).getRowName(row); 268 } 269 270 public void tableChanged(TableModelEvent e) { 271 if ((e.getType() == e.INSERT) || 272 (e.getType() == e.DELETE)) { 273 // fire row update events for the 274 // row-header model to keep the header in 275 // sync with the main datamodel 276 fireTableChanged(new TableModelEvent(this, 277 e.getFirstRow(), 278 e.getLastRow(), 279 e.ALL_COLUMNS, 280 e.getType())); 281 } 282 } 283 } 284 285 protected class RowHeaderCellRenderer extends DefaultTableCellRenderer { 286 protected Border border; 287 288 public RowHeaderCellRenderer() { 289 super(); 290 setHorizontalAlignment(SwingConstants.CENTER); 291 } 292 293 294 public Component getTableCellRendererComponent(JTable table, 295 Object value, 296 boolean isSelected, 297 boolean hasFocus, 298 int row, 299 int column) { 300 Component result = super.getTableCellRendererComponent(table, 301 value, 302 isSelected, 303 hasFocus, 304 row, 305 column); 306 if (border == null) { 307 border = BorderFactory.createBevelBorder(BevelBorder.RAISED); 308 } 309 ((JLabel)result).setBorder(border); 310 ((JLabel)result).setOpaque(false); 311 return result; 312 } 313 } 314 315} 316 317 318 319 320