001    /*
002     * Copyright 2002-2004 the original author or authors.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package org.springframework.richclient.table;
017    
018    import java.awt.Component;
019    import java.awt.Rectangle;
020    import java.awt.SystemColor;
021    import java.awt.event.InputEvent;
022    import java.awt.event.MouseAdapter;
023    import java.awt.event.MouseEvent;
024    
025    import javax.swing.Icon;
026    import javax.swing.JOptionPane;
027    import javax.swing.JTable;
028    import javax.swing.SwingConstants;
029    import javax.swing.UIManager;
030    import javax.swing.event.ChangeEvent;
031    import javax.swing.event.ListSelectionEvent;
032    import javax.swing.event.TableColumnModelEvent;
033    import javax.swing.event.TableColumnModelListener;
034    import javax.swing.table.DefaultTableCellRenderer;
035    import javax.swing.table.JTableHeader;
036    import javax.swing.table.TableColumn;
037    import javax.swing.table.TableColumnModel;
038    
039    import org.springframework.richclient.core.UIConstants;
040    import org.springframework.richclient.image.ArrowIcon;
041    import org.springframework.util.Assert;
042    
043    /**
044     * @author Keith Donald
045     */
046    public class TableSortIndicator implements TableColumnModelListener {
047            public static final ArrowIcon STANDARD_UP_ARROW = new ArrowIcon(ArrowIcon.Direction.UP, 4,
048                            SystemColor.controlDkShadow);
049    
050            public static final ArrowIcon STANDARD_DOWN_ARROW = new ArrowIcon(ArrowIcon.Direction.DOWN, 4,
051                            SystemColor.controlDkShadow);
052    
053            private final TableHeaderClickHandler tableHeaderClickHandler = new TableHeaderClickHandler();
054    
055            private JTable table;
056    
057            private Icon ascendingIcon;
058    
059            private Icon descendingIcon;
060    
061            private ColumnSortList sortList;
062    
063            public TableSortIndicator(JTable table) {
064                    this(table, STANDARD_UP_ARROW, STANDARD_DOWN_ARROW);
065            }
066    
067            public TableSortIndicator(JTable table, Icon ascendingIcon, Icon descendingIcon) {
068                    Assert.notNull(table);
069                    Assert.notNull(ascendingIcon);
070                    Assert.notNull(descendingIcon);
071                    this.table = table;
072                    this.ascendingIcon = ascendingIcon;
073                    this.descendingIcon = descendingIcon;
074                    sortList = new ColumnSortList();
075                    initHeaderRenderers();
076                    this.table.getColumnModel().addColumnModelListener(this);
077                    this.table.getTableHeader().addMouseListener(tableHeaderClickHandler);
078            }
079    
080            public ColumnSortList getColumnSortList() {
081                    return sortList;
082            }
083    
084            private void initHeaderRenderers() {
085                    for (int i = 0; i < table.getColumnCount(); i++) {
086                            TableColumn col = table.getColumnModel().getColumn(i);
087                            col.setHeaderRenderer(new HeaderRenderer(table.getTableHeader()));
088                            col.setPreferredWidth(TableUtils.calculatePreferredColumnWidth(table, col));
089                            col.setWidth(col.getPreferredWidth());
090                    }
091            }
092    
093            private class TableHeaderClickHandler extends MouseAdapter {
094                    public void mouseClicked(MouseEvent e) {
095                            if (e.isMetaDown()) {
096                                    return;
097                            }
098                            int columnView = table.getColumnModel().getColumnIndexAtX(e.getX());
099                            if (columnView == -1) {
100                                    return;
101                            }
102                            // make sure mouseclick was not in resize area
103                            Rectangle r = table.getTableHeader().getHeaderRect(columnView);
104                            // working with a magic value of 3 here, as it is in TableHeaderUI
105                            r.grow(-3, 0);
106                            if (!r.contains(e.getPoint())) {
107                                    return;
108                            }
109    
110                            int column = table.convertColumnIndexToModel(columnView);
111                            int shiftPressed = e.getModifiers() & InputEvent.SHIFT_MASK;
112                            ColumnToSort columnToSort = sortList.getSortLevel(column);
113                            if (columnToSort != null) {
114                                    if (shiftPressed == 1) {
115                                            sortList.toggleSortOrder(column);
116                                            displayRendererIcon(column, columnToSort.getSortOrder());
117                                    }
118                                    else {
119                                            SortOrder order;
120                                            if (sortList.size() > 1) {
121                                                    order = SortOrder.ASCENDING;
122                                            }
123                                            else {
124                                                    order = columnToSort.getSortOrder().flip();
125                                            }
126                                            sortList.setSingleSortLevel(column, order);
127                                            removeRendererIcons();
128                                            displayRendererIcon(columnView, order);
129                                    }
130                            }
131                            else {
132                                    if (shiftPressed == 1) {
133                                            try {
134                                                    sortList.addSortLevel(column, SortOrder.ASCENDING);
135                                                    displayRendererIcon(columnView, SortOrder.ASCENDING);
136                                            }
137                                            catch (IllegalArgumentException ex) {
138                                                    JOptionPane.showMessageDialog(table.getTopLevelAncestor(),
139                                                                    "Maximum number of sort levels reached.", "Table Sorter", JOptionPane.WARNING_MESSAGE);
140                                                    return;
141                                            }
142                                    }
143                                    else {
144                                            sortList.setSingleSortLevel(column, SortOrder.ASCENDING);
145                                            removeRendererIcons();
146                                            displayRendererIcon(columnView, SortOrder.ASCENDING);
147                                    }
148                            }
149                            table.getTableHeader().resizeAndRepaint();
150                    }
151            }
152    
153            private static final class HeaderRenderer extends DefaultTableCellRenderer {
154                    private JTableHeader tableHeader;
155    
156                    public HeaderRenderer(JTableHeader header) {
157                            setForeground(header.getForeground());
158                            setBackground(header.getBackground());
159                            setBorder(UIManager.getBorder("TableHeader.cellBorder"));
160                            setIconTextGap(UIConstants.TWO_SPACES);
161                            setHorizontalTextPosition(SwingConstants.LEFT);
162                            this.tableHeader = header;
163                    }
164    
165                    public Component getTableCellRendererComponent(JTable table, Object value, boolean iSelected, boolean hasFocus,
166                                    int row, int column) {
167                            setText((value == null) ? "" : value.toString());
168                            setFont(tableHeader.getFont());
169                            return this;
170                    }
171            }
172    
173            private void displayRendererIcon(int column, SortOrder order) {
174                    HeaderRenderer rend = getRenderer(column);
175                    if (order == SortOrder.ASCENDING) {
176                            rend.setIcon(this.ascendingIcon);
177                    }
178                    else {
179                            rend.setIcon(this.descendingIcon);
180                    }
181            }
182    
183            private void removeRendererIcons() {
184                    TableColumnModel colModel = table.getColumnModel();
185                    for (int i = 0; i < colModel.getColumnCount(); i++) {
186                            HeaderRenderer rend = (HeaderRenderer) colModel.getColumn(i).getHeaderRenderer();
187                            rend.setIcon(null);
188                    }
189            }
190    
191            private HeaderRenderer getRenderer(int columnIndex) {
192                    TableColumn column = table.getColumnModel().getColumn(columnIndex);
193                    return (HeaderRenderer) column.getHeaderRenderer();
194            }
195    
196            public void columnAdded(TableColumnModelEvent e) {
197                    TableColumn column = table.getColumnModel().getColumn(e.getToIndex());
198                    if (column.getHeaderRenderer() instanceof HeaderRenderer) {
199                            ((HeaderRenderer)column.getHeaderRenderer()).setIcon(null);
200                    }
201                    else {
202                            column.setHeaderRenderer(new HeaderRenderer(table.getTableHeader()));
203                    }
204            }
205    
206            public void columnMarginChanged(ChangeEvent e) {
207            }
208    
209            public void columnMoved(TableColumnModelEvent e) {
210            }
211    
212            public void columnRemoved(TableColumnModelEvent e) {
213            }
214    
215            public void columnSelectionChanged(ListSelectionEvent e) {
216            }
217    }