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 }