001/* 002 The Broad Institute 003 SOFTWARE COPYRIGHT NOTICE AGREEMENT 004 This software and its documentation are copyright (2003-2008) by the 005 Broad Institute/Massachusetts Institute of Technology. All rights are 006 reserved. 007 008 This software is supplied without any warranty or guaranteed support 009 whatsoever. Neither the Broad Institute nor MIT can be responsible for its 010 use, misuse, or functionality. 011*/ 012 013 014package ca.bc.webarts.widgets.treetable; 015// package org.genomespace.gsui.ui.table; 016 017import java.awt.AlphaComposite; 018import java.awt.Color; 019import java.awt.Component; 020import java.awt.GradientPaint; 021import java.awt.Graphics; 022import java.awt.Graphics2D; 023import java.awt.Point; 024import java.awt.Rectangle; 025import java.awt.SystemColor; 026import java.awt.datatransfer.Transferable; 027import java.awt.dnd.DnDConstants; 028import java.awt.dnd.DragGestureEvent; 029import java.awt.dnd.DragGestureListener; 030import java.awt.dnd.DragSource; 031import java.awt.dnd.DragSourceDragEvent; 032import java.awt.dnd.DragSourceDropEvent; 033import java.awt.dnd.DragSourceEvent; 034import java.awt.dnd.DragSourceListener; 035import java.awt.event.MouseAdapter; 036import java.awt.event.MouseEvent; 037import java.awt.image.BufferedImage; 038import java.util.ArrayList; 039import java.util.List; 040import javax.swing.Icon; 041import javax.swing.JLabel; 042import javax.swing.JTable; 043import javax.swing.table.TableModel; 044import javax.swing.JTree; 045import javax.swing.ListSelectionModel; 046import javax.swing.table.JTableHeader; 047import javax.swing.table.TableCellRenderer; 048import javax.swing.table.TableColumnModel; 049import javax.swing.tree.TreePath; 050import javax.swing.tree.TreeSelectionModel; 051 052/** 053 * Description of the Class 054 * 055 * @author Joshua Gould 056 */ 057public class SortableHeaderRenderer implements TableCellRenderer { 058 public final static int DESCENDING = -1; 059 060 public final static int NOT_SORTED = 0; 061 062 public final static int ASCENDING = 1; 063 private TableCellRenderer tableCellRenderer; 064 private JTable table; 065 private ColumnSorter model; 066 private List sortingColumns = new ArrayList(); 067 068 private static Directive EMPTY_DIRECTIVE = new Directive(-1, NOT_SORTED); 069 070 071 public SortableHeaderRenderer(JTable table, ColumnSorter model) { 072 this.table = table; 073 this.model = model; 074 tableCellRenderer = new JTable().getTableHeader() 075 .getDefaultRenderer(); 076 table.getTableHeader().setDefaultRenderer(this); 077 table.getTableHeader().addMouseListener(new MouseHandler()); 078 079 } 080 081 082 private void cancelSorting() { 083 sortingColumns.clear(); 084 sortingStatusChanged(); 085 } 086 087 088 private void sortingStatusChanged() { 089 // clearSortingState(); 090 // fireTableDataChanged(); 091 if(table.getTableHeader()!=null) { 092 table.getTableHeader().repaint(); 093 } 094 } 095 096 097 public void setSortingStatus(int column, int status) { 098 Directive directive = getDirective(column); 099 if(directive != EMPTY_DIRECTIVE) { 100 sortingColumns.remove(directive); 101 } 102 if(status != NOT_SORTED) { 103 sortingColumns.add(new Directive(column, status)); 104 } 105 sortingStatusChanged(); 106 model.sortOrderChanged(new SortEvent(this, column, 107 status != DESCENDING)); 108 } 109 110 111 public Component getTableCellRendererComponent(JTable table, 112 Object value, boolean isSelected, boolean hasFocus, int row, 113 int column) { 114 Component c = tableCellRenderer.getTableCellRendererComponent( 115 table, value, isSelected, hasFocus, row, column); 116 if(c instanceof JLabel) { 117 JLabel l = (JLabel) c; 118 l.setHorizontalTextPosition(JLabel.LEFT); 119 int modelColumn = table.convertColumnIndexToModel(column); 120 l.setIcon(getHeaderRendererIcon(modelColumn, l.getFont() 121 .getSize())); 122 } 123 return c; 124 } 125 126 127 public int getSortingStatus(int column) { 128 return getDirective(column).direction; 129 } 130 131 132 protected Icon getHeaderRendererIcon(int column, int size) { 133 Directive directive = getDirective(column); 134 if(directive == EMPTY_DIRECTIVE) { 135 return null; 136 } 137 return new Arrow(directive.direction == DESCENDING, size, 138 sortingColumns.indexOf(directive)); 139 } 140 141 142 private Directive getDirective(int column) { 143 for(int i = 0; i < sortingColumns.size(); i++) { 144 Directive directive = (Directive) sortingColumns.get(i); 145 if(directive.column == column) { 146 return directive; 147 } 148 } 149 return EMPTY_DIRECTIVE; 150 } 151 152 153 private String getStatusString(int status) { 154 switch (status) { 155 case NOT_SORTED: 156 return "NOT_SORTED"; 157 case ASCENDING: 158 return "ASCENDING"; 159 case DESCENDING: 160 return "DESCENDING"; 161 default: 162 return null; 163 } 164 } 165 166 167 168 private static class Directive { 169 private int column; 170 171 private int direction; 172 173 174 public Directive(int column, int direction) { 175 this.column = column; 176 this.direction = direction; 177 } 178 } 179 180 181 private static class Arrow implements Icon { 182 private boolean descending; 183 184 private int size; 185 186 private int priority; 187 188 189 public Arrow(boolean descending, int size, int priority) { 190 this.descending = descending; 191 this.size = size; 192 this.priority = priority; 193 } 194 195 196 public void paintIcon(Component c, Graphics g, int x, int y) { 197 Color color = c == null ? Color.GRAY : c.getBackground(); 198 // In a compound sort, make each succesive triangle 20% 199 // smaller than the previous one. 200 int dx = (int) (size / 2 * Math.pow(0.8, priority)); 201 int dy = descending ? dx : -dx; 202 // Align icon (roughly) with font baseline. 203 y = y + 5 * size / 6 + (descending ? -dy : 0); 204 int shift = descending ? 1 : -1; 205 g.translate(x, y); 206 207 // Right diagonal. 208 g.setColor(color.darker()); 209 g.drawLine(dx / 2, dy, 0, 0); 210 g.drawLine(dx / 2, dy + shift, 0, shift); 211 212 // Left diagonal. 213 g.setColor(color.brighter()); 214 g.drawLine(dx / 2, dy, dx, 0); 215 g.drawLine(dx / 2, dy + shift, dx, shift); 216 217 // Horizontal line. 218 if(descending) { 219 g.setColor(color.darker().darker()); 220 } else { 221 g.setColor(color.brighter().brighter()); 222 } 223 g.drawLine(dx, 0, 0, 0); 224 225 g.setColor(color); 226 g.translate(-x, -y); 227 } 228 229 230 public int getIconWidth() { 231 return size; 232 } 233 234 235 public int getIconHeight() { 236 return size; 237 } 238 } 239 240 241 private class MouseHandler extends MouseAdapter { 242 public MouseHandler() { 243 } 244 245 246 public void mouseClicked(MouseEvent e) { 247 JTableHeader h = (JTableHeader) e.getSource(); 248 TableColumnModel columnModel = h.getColumnModel(); 249 int viewColumn = columnModel.getColumnIndexAtX(e.getX()); 250 int column = columnModel.getColumn(viewColumn).getModelIndex(); 251 252 if(column != -1) { 253 int status = getSortingStatus(column); 254 if(!e.isControlDown()) { 255 cancelSorting(); 256 } 257 258 switch (status) { 259 case NOT_SORTED: 260 status = ASCENDING; 261 break; 262 case ASCENDING: 263 status = DESCENDING; 264 break; 265 case DESCENDING: 266 status = ASCENDING; 267 break; 268 default: 269 break; 270 } 271 setSortingStatus(column, status); 272 } 273 } 274 } 275}