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}