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}