001/** 002 * '$Author: barseghian $' 003 * '$Date: 2010-10-12 18:29:51 +0000 (Tue, 12 Oct 2010) $' 004 * '$Revision: 26052 $' 005 * 006 * For Details: 007 * http://www.kepler-project.org 008 * 009 * Copyright (c) 2009-2010 The Regents of the 010 * University of California. All rights reserved. Permission is hereby granted, 011 * without written agreement and without license or royalty fees, to use, copy, 012 * modify, and distribute this software and its documentation for any purpose, 013 * provided that the above copyright notice and the following two paragraphs 014 * appear in all copies of this software. IN NO EVENT SHALL THE UNIVERSITY OF 015 * CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, 016 * OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS 017 * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE 018 * POSSIBILITY OF SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY 019 * DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 020 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE 021 * SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 022 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 023 * ENHANCEMENTS, OR MODIFICATIONS. 024 */ 025 026package org.kepler.workflowrunmanager.gui; 027 028import java.awt.Component; 029import java.awt.Dimension; 030import java.awt.Graphics; 031import java.awt.Point; 032import java.awt.Rectangle; 033import java.awt.event.MouseEvent; 034import java.util.Enumeration; 035import java.util.Hashtable; 036import java.util.Iterator; 037import java.util.Vector; 038 039import javax.swing.JComponent; 040import javax.swing.SwingUtilities; 041import javax.swing.event.MouseInputListener; 042import javax.swing.plaf.basic.BasicTableHeaderUI; 043import javax.swing.table.TableCellRenderer; 044import javax.swing.table.TableColumn; 045import javax.swing.table.TableColumnModel; 046 047/** 048 * ColumnGroup, GroupableTableColumnModel, GroupableTableHeader, 049 * GroupableTableHeaderUI and GroupableTableCellRenderer taken from a post by 050 * Steve Webb (16/09/04). He extended code originally posted by Nobuo Tamemasa. Many thanks 051 * to both authors. I've made some changes, including making the headers editable. 052 * 053 */ 054 055/** 056 * This class paints groupable header cells. These can be a combination of 057 * normal header cells and groupable cells. 058 */ 059public class GroupableTableHeaderUI extends BasicTableHeaderUI { 060 061 /** 062 * Contains a list of ColumnGroups that have already been painted in the 063 * current paint request. 064 */ 065 protected Vector<ColumnGroup> paintedGroups = new Vector<ColumnGroup>(); 066 067 /** 068 * Paint a representation of the table header. 069 * 070 * @param g 071 * the Graphics context in which to paint 072 * @param c 073 * the component being painted; this argument is often ignored 074 */ 075 public void paint(Graphics g, JComponent c) { 076 Rectangle clipBounds = g.getClipBounds(); 077 GroupableTableColumnModel cm = (GroupableTableColumnModel) header 078 .getColumnModel(); 079 if (cm == null){ 080 return; 081 } 082 ((GroupableTableHeader) header).setColumnMargin(); 083 int column = 0; 084 Dimension size = header.getSize(); 085 Rectangle cellRect = new Rectangle(0, 0, size.width, size.height); 086 Hashtable<ColumnGroup, Rectangle> h = new Hashtable<ColumnGroup, Rectangle>(); 087 088 Enumeration<TableColumn> columns = cm.getColumns(); 089 while (columns.hasMoreElements()) { 090 091 cellRect.height = size.height; 092 cellRect.y = 0; 093 TableColumn aColumn = (TableColumn) columns.nextElement(); 094 Iterator<ColumnGroup> colGrpIter = cm.getColumnGroups(aColumn); 095 if (colGrpIter != null) { 096 int groupHeight = 0; 097 while (colGrpIter.hasNext()) { 098 ColumnGroup cGroup = (ColumnGroup) colGrpIter.next(); 099 Rectangle groupRect = (Rectangle) h.get(cGroup); 100 if (groupRect == null) { 101 groupRect = new Rectangle(cellRect); 102 Dimension d = cGroup.getSize(header.getTable()); 103 groupRect.width = d.width; 104 groupRect.height = d.height; 105 h.put(cGroup, groupRect); 106 } 107 if (!paintedGroups.contains(cGroup)) { 108 paintCell(g, groupRect, cGroup, column); 109 paintedGroups.add(cGroup); 110 } 111 groupHeight += groupRect.height; 112 cellRect.height = size.height - groupHeight; 113 cellRect.y = groupHeight; 114 } 115 } 116 cellRect.width = aColumn.getWidth(); 117 if (cellRect.intersects(clipBounds)) { 118 // System.out.println("cellRect.intersects(clipBounds). calling 119 // paintCell 120 // on column: "+column); 121 paintCell(g, cellRect, column); 122 } 123 cellRect.x += cellRect.width; 124 column++; 125 } 126 127 // Paint the dragged column if we are dragging. 128 TableColumn draggedColumn = header.getDraggedColumn(); 129 130 if (draggedColumn != null) { 131 int draggedColumnIndex = viewIndexForColumn(draggedColumn); 132 133 Rectangle draggedCellRect = header 134 .getHeaderRect(draggedColumnIndex); 135 136 // Draw a gray well in place of the moving column. 137 g.setColor(header.getParent().getBackground()); 138 g.fillRect(draggedCellRect.x, draggedCellRect.y, 139 draggedCellRect.width, draggedCellRect.height); 140 141 draggedCellRect.x += header.getDraggedDistance(); 142 143 // Fill the background. 144 g.setColor(header.getBackground()); 145 g.fillRect(draggedCellRect.x, draggedCellRect.y, 146 draggedCellRect.width, draggedCellRect.height); 147 148 paintCell(g, draggedCellRect, draggedColumnIndex); 149 } 150 151 paintedGroups.clear(); 152 } 153 154 // TODO better name. method gets height of group of special editable header 155 // cells for one column 156 // for a column with standard one editable header cell per column, this is 157 // about half the total header height, 17 158 public int getCGroupHeight() { 159 int groupHeight = 0; 160 161 GroupableTableColumnModel cm = (GroupableTableColumnModel) header 162 .getColumnModel(); 163 if (cm == null){ 164 return 0; 165 } 166 ((GroupableTableHeader) header).setColumnMargin(); 167 Dimension size = header.getSize(); 168 Rectangle cellRect = new Rectangle(0, 0, size.width, size.height); 169 Enumeration<TableColumn> columns = cm.getColumns(); 170 while (columns.hasMoreElements()) { 171 172 cellRect.height = size.height; 173 cellRect.y = 0; 174 TableColumn aColumn = (TableColumn) columns.nextElement(); 175 Iterator<ColumnGroup> colGrpIter = cm.getColumnGroups(aColumn); 176 if (colGrpIter != null) { 177 groupHeight = 0; 178 while (colGrpIter.hasNext()) { 179 ColumnGroup cGroup = (ColumnGroup) colGrpIter.next(); 180 Rectangle groupRect = new Rectangle(cellRect); 181 Dimension d = cGroup.getSize(header.getTable()); 182 groupRect.width = d.width; 183 groupRect.height = d.height; 184 groupHeight += groupRect.height; 185 cellRect.height = size.height - groupHeight; 186 cellRect.y = groupHeight; 187 } 188 } 189 } 190 return groupHeight; 191 } 192 193 private int viewIndexForColumn(TableColumn aColumn) { 194 TableColumnModel cm = header.getColumnModel(); 195 for (int column = 0; column < cm.getColumnCount(); column++) { 196 if (cm.getColumn(column) == aColumn) { 197 return column; 198 } 199 } 200 return -1; 201 } 202 203 /** 204 * Paints a header column cell. 205 * 206 * @param g 207 * Graphics context 208 * @param cellRect 209 * The rectangle to contain the cell 210 * @param columnIndex 211 * The header column to be painted 212 */ 213 private void paintCell(Graphics g, Rectangle cellRect, int columnIndex) { 214 TableColumn aColumn = header.getColumnModel().getColumn(columnIndex); 215 TableCellRenderer renderer = aColumn.getHeaderRenderer(); 216 if (renderer == null) { 217 renderer = header.getDefaultRenderer(); 218 } 219 220 Component component = renderer.getTableCellRendererComponent(header 221 .getTable(), aColumn.getHeaderValue(), false, false, -1, 222 columnIndex); 223 rendererPane.add(component); 224 rendererPane.paintComponent(g, component, header, cellRect.x, 225 cellRect.y, cellRect.width, cellRect.height, true); 226 } 227 228 /** 229 * Paint group column cell. 230 * 231 * @param g 232 * Graphics context 233 * @param cellRect 234 * Rectangle that the cell with be painted in 235 * @param cGroup 236 * Current column group 237 */ 238 private void paintCell(Graphics g, Rectangle cellRect, ColumnGroup cGroup, 239 int columnIndex) { 240 TableCellRenderer renderer = cGroup.getHeaderRenderer(); 241 242 Component component = renderer.getTableCellRendererComponent(header 243 .getTable(), cGroup.getHeaderValue(), false, false, -1, 244 columnIndex); 245 rendererPane.add(component); 246 rendererPane.paintComponent(g, component, header, cellRect.x, 247 cellRect.y, cellRect.width, cellRect.height, true); 248 } 249 250 /** 251 * Calculate and return the height of the header. 252 * 253 * @return Header Height 254 */ 255 private int getHeaderHeight() { 256 int height = 0; 257 GroupableTableColumnModel columnModel = (GroupableTableColumnModel) header 258 .getColumnModel(); 259 for (int column = 0; column < columnModel.getColumnCount(); column++) { 260 TableColumn aColumn = columnModel.getColumn(column); 261 TableCellRenderer renderer = aColumn.getHeaderRenderer(); 262 if (renderer == null) { 263 renderer = header.getDefaultRenderer(); 264 } 265 Component comp = renderer.getTableCellRendererComponent(header 266 .getTable(), aColumn.getHeaderValue(), false, false, -1, 267 column); 268 int cHeight = comp.getPreferredSize().height; 269 Iterator<ColumnGroup> iter = columnModel.getColumnGroups(aColumn); 270 if (iter != null) { 271 while (iter.hasNext()) { 272 ColumnGroup cGroup = (ColumnGroup) iter.next(); 273 cHeight += cGroup.getSize(header.getTable()).height; 274 } 275 } 276 height = Math.max(height, cHeight); 277 } 278 return height; 279 } 280 281 /** 282 * Calculate and return the dimension of the header. 283 * 284 * @param width 285 * Starting width to be used. 286 * @return Dimension of the header 287 */ 288 private Dimension createHeaderSize(long width) { 289 TableColumnModel columnModel = header.getColumnModel(); 290 width += columnModel.getColumnMargin() * columnModel.getColumnCount(); 291 if (width > Integer.MAX_VALUE) { 292 width = Integer.MAX_VALUE; 293 } 294 return new Dimension((int) width, getHeaderHeight()); 295 } 296 297 /** 298 * Invokes the getPreferredSize method on each UI handled by this object. 299 * 300 * @param c 301 * the component whose preferred size is being queried; this 302 * argument is ignored. 303 * @return the dimension of the whole header 304 */ 305 public Dimension getPreferredSize(JComponent c) { 306 long width = 0; 307 Enumeration<TableColumn> columns = header.getColumnModel().getColumns(); 308 while (columns.hasMoreElements()) { 309 TableColumn aColumn = (TableColumn) columns.nextElement(); 310 width = width + aColumn.getPreferredWidth(); 311 } 312 return createHeaderSize(width); 313 } 314 315 // code for editable: 316 317 protected MouseInputListener createMouseInputListener() { 318 return new MouseInputHandler((GroupableTableHeader) header); 319 } 320 321 public class MouseInputHandler extends BasicTableHeaderUI.MouseInputHandler { 322 private Component dispatchComponent; 323 protected GroupableTableHeader header; 324 325 public MouseInputHandler(GroupableTableHeader header) { 326 this.header = header; 327 } 328 329 private void setDispatchComponent(MouseEvent e) { 330 331 Component editorComponent = header.getEditorComponent(); 332 Point p = e.getPoint(); 333 Point p2 = SwingUtilities.convertPoint(header, p, editorComponent); 334 dispatchComponent = SwingUtilities.getDeepestComponentAt( 335 editorComponent, p2.x, p2.y); 336 } 337 338 private boolean repostEvent(MouseEvent e) { 339 if (dispatchComponent == null) { 340 return false; 341 } 342 MouseEvent e2 = SwingUtilities.convertMouseEvent(header, e, 343 dispatchComponent); 344 dispatchComponent.dispatchEvent(e2); 345 return true; 346 } 347 348 public void mousePressed(MouseEvent e) { 349 if (!SwingUtilities.isLeftMouseButton(e)) { 350 return; 351 } 352 super.mousePressed(e); 353 354 if (header.getResizingColumn() == null) { 355 Point p = e.getPoint(); 356 // TODO: make sure this makes sense: 357 // TableColumnModel columnModel = header.getColumnModel(); 358 GroupableTableColumnModel columnModel = (GroupableTableColumnModel) header 359 .getColumnModel(); 360 int index = columnModel.getColumnIndexAtX(p.x); 361 if ((index != -1) && (p.y < getCGroupHeight())) { 362 // ie && don't edit if they click in the 363 // regular-looking header area 364 if (header.editCellAt(index, e)) { 365 setDispatchComponent(e); 366 repostEvent(e); 367 } 368 } 369 } 370 } 371 372 public void mouseReleased(MouseEvent e) { 373 374 super.mouseReleased(e); 375 if (!SwingUtilities.isLeftMouseButton(e)) { 376 return; 377 } 378 repostEvent(e); 379 dispatchComponent = null; 380 } 381 382 } 383 384}