Works around a nasty bug in Java 1.6, where it clears the selection if the context/popup menu mouse trigger is clicked. It needs to refuse the cell selection in the case of a popup trigger. by mrs, 19 Nov, 2009 10:21 AM
Diff this changeset:
AuctionTable.java
mrs 1   package com.jbidwatcher.ui.table;
mrs 2   
mrs 3   import com.jbidwatcher.auction.AuctionEntry;
mrs 4   import com.jbidwatcher.util.config.JConfig;
mrs 5   import com.jbidwatcher.util.Constants;
mrs 6   import com.jbidwatcher.util.Pair;
mrs 7   import com.jbidwatcher.util.queue.SuperQueue;
mrs 8   
mrs 9   import javax.swing.*;
mrs 10  import javax.swing.table.TableModel;
mrs 11  import javax.swing.table.TableColumn;
mrs 12  import javax.swing.table.TableCellEditor;
mrs 13  import java.awt.event.MouseEvent;
mrs 14  import java.awt.event.MouseListener;
mrs 15  import java.awt.Point;
mrs 16  import java.awt.Dimension;
mrs 17  import java.util.*;
mrs 18  
mrs 19  /**
mrs 20   * Created by IntelliJ IDEA.
mrs 21  * User: Morgan
mrs 22  * Date: Jun 20, 2008
mrs 23  * Time: 12:06:56 PM
mrs 24  * Handle tooltips, at least.  A very cool feature.
mrs 25  */
mrs 26  public class AuctionTable extends JTable {
mrs 27    private static TableCellEditor FAUX = new FauxEditor();
mrs 28    public TableCellEditor getCellEditor() {
mrs 29      return FAUX;
mrs 30    }
mrs 31  
mrs 32    /**
mrs 33     * @brief Constructs a JTable out of a prefix to search for in the
mrs 34     * configuration, and a TableModel to apply to the table.
mrs 35     *
mrs 36     * The TableModel has the list of column names, and when you add
mrs 37     * "prefix." to the front of them, it makes a configuration entry
mrs 38     * which says the size of that column.
mrs 39     *
mrs 40     * Returns a new JTable, properly spaced and filled out according to
mrs 41     * the configuration preferences.
mrs 42     *
mrs 43     * @param name - A string that gets prepended to the column name
mrs 44     *                 in order to produce a display property showing
mrs 45     *                 the preferred width of that column.
mrs 46     * @param atm - A TableModel that will be used for rendering the table.
mrs 47     */
mrs 48    public AuctionTable(String name, TableModel atm) {
mrs 49      super();
mrs 50      createDefaultRenderers();
mrs 51      setShowGrid(false);
mrs 52      setIntercellSpacing(new Dimension(0, 0));
mrs 53      setDoubleBuffered(true);
mrs 54      setAutoCreateColumnsFromModel(false);
mrs 55  
mrs 56      setModel(atm);
mrs 57      loadColumnSettings(name, atm);
mrs 58      doLayout();
mrs 59    }
mrs 60  
mrs 61    public String getToolTipText(MouseEvent event) {
mrs 62      Point point = new Point(event.getX(), event.getY());
mrs 63      int rowPoint = rowAtPoint(point);
mrs 64      String result = null;
mrs 65      int colPoint = columnAtPoint(point);
mrs 66  
mrs 67      if(rowPoint != -1) {
mrs 68        AuctionEntry ae = (AuctionEntry) getValueAt(rowPoint, -1);
mrs 69        boolean showThumbnail = true;
mrs 70  
mrs 71        if(getRowHeight() == Constants.MICROTHUMBNAIL_ROW_HEIGHT) {
mrs 72          showThumbnail = getColumnName(colPoint).equals("Thumbnail");
mrs 73        }
mrs 74  
mrs 75        result = ae.buildHTMLComment(showThumbnail);
mrs 76      }
mrs 77  
mrs 78      return result == null ? super.getToolTipText(event) : result;
mrs 79    }
mrs 80  
mrs 81    static class MouseListenerSelectProxy implements MouseListener {
mrs 82      private MouseListener m_peer;
mrs 83  
mrs 84      MouseListenerSelectProxy(MouseListener ml) { m_peer = ml; }
mrs 85  
mrs 86      public MouseListener getPeer() { return m_peer; }
mrs 87  
mrs 88      /**
mrs 89       * Invoked when the mouse button has been clicked (pressed
mrs 90       * and released) on a component.
mrs 91       */
mrs 92      public void mouseClicked(MouseEvent e) { if (!e.isPopupTrigger()) m_peer.mouseClicked(e); }
mrs 93  
mrs 94      /**
mrs 95       * Invoked when the mouse enters a component.
mrs 96       */
mrs 97      public void mouseEntered(MouseEvent e) { m_peer.mouseEntered(e); }
mrs 98  
mrs 99      /**
mrs 100      * Invoked when the mouse exits a component.
mrs 101      */
mrs 102     public void mouseExited(MouseEvent e) { m_peer.mouseExited(e); }
mrs 103 
mrs 104     /**
mrs 105      * Invoked when a mouse button has been pressed on a component.
mrs 106      */
mrs 107     public void mousePressed(MouseEvent e) { if (!e.isPopupTrigger()) m_peer.mousePressed(e); }
mrs 108 
mrs 109     /**
mrs 110      * Invoked when a mouse button has been released on a component.
mrs 111      */
mrs 112     public void mouseReleased(MouseEvent e) { if (!e.isPopupTrigger()) m_peer.mouseReleased(e); }
mrs 113   }
mrs 114 
mrs 115   private final static String METAL_MOUSE_LISTENER = "javax.swing.plaf.basic.BasicTableUI$MouseInputHandler";
mrs 116   private final static String AQUA_MOUSE_LISTENER = "apple.laf.AquaTableUI$MouseInputHandler";
mrs 117 
mrs 118   /** @noinspection InstanceVariableMayNotBeInitialized*/
mrs 119   // DO NOT INITIALIZE proxyMouseListener!!!!!
mrs 120   // It is used in addMouseListener BEFORE the constuctor is run
mrs 121   // (called from the base class constructor). If it is set here
mrs 122   // or in the constructor, it will clobber the value set by the
mrs 123   // call from the base class constructor.
mrs 124   private MouseListenerSelectProxy proxyMouseListener;
mrs 125 
mrs 126   public synchronized void addMouseListener(MouseListener ml) {
mrs 127     String mlClass = ml.getClass().getName();
mrs 128     if ((proxyMouseListener == null) &&
mrs 129         (mlClass.equals(METAL_MOUSE_LISTENER) ||
mrs 130          mlClass.equals(AQUA_MOUSE_LISTENER))) {
mrs 131       proxyMouseListener = new MouseListenerSelectProxy(ml);
mrs 132       super.addMouseListener(proxyMouseListener);
mrs 133     } else {
mrs 134       super.addMouseListener(ml);
mrs 135     }
mrs 136   }
mrs 137 
mrs 138   public synchronized void removeMouseListener(MouseListener ml) {
mrs 139     //noinspection ObjectEquality
mrs 140     if ((proxyMouseListener != null) && (ml == proxyMouseListener.getPeer())) {
mrs 141       super.removeMouseListener(proxyMouseListener);
mrs 142       proxyMouseListener = null;
mrs 143     } else {
mrs 144       super.removeMouseListener(ml);
mrs 145     }
mrs 146   }
mrs 147 
mrs 148   private static final String[][] DEFAULT_COLUMNS = new String[][]{
mrs 149           {"Number", "100"},
mrs 150           {"Current", "89"},
mrs 151           {"Max", "62"},
mrs 152           {"Description", "297"},
mrs 153           {"Time left", "127"},
mrs 154           {"Status", "67"},
mrs 155           {"Seller", "147"},
mrs 156   };
mrs 157 
mrs 158   private static int notify_delay = 0;
mrs 159 
mrs 160   private class ColumnIndex extends Pair<String,Integer> {
mrs 161     public ColumnIndex(String s, Integer i) { super(s, i); }
mrs 162   }
mrs 163 
mrs 164   private void loadColumnSettings(String prefix, TableModel atm) {
mrs 165     String curColumnName = "";
mrs 166     int columnCount = 0;
mrs 167 
mrs 168     List<ColumnIndex> initialToSaved = new LinkedList<ColumnIndex>();
mrs 169     //  This code would need to be somewhat revamped if we allowed
mrs 170     //  arbitrary, or user-selected column names.
mrs 171     try {
mrs 172       for(int i = 0; i<atm.getColumnCount(); i++) {
mrs 173         curColumnName = atm.getColumnName(i);
mrs 174         //noinspection StringContatenationInLoop
mrs 175         String colWidth = JConfig.queryDisplayProperty(prefix + '.' + curColumnName);
mrs 176         if(colWidth == null) {
mrs 177           colWidth = JConfig.queryDisplayProperty(curColumnName);
mrs 178         }
mrs 179         if(colWidth != null) {
mrs 180           int dotIndex = colWidth.indexOf('.');
mrs 181           if (dotIndex != -1) {
mrs 182             String colIndex = colWidth.substring(0, dotIndex);
mrs 183             colWidth = colWidth.substring(dotIndex + 1);
mrs 184             initialToSaved.add(new ColumnIndex(curColumnName, Integer.parseInt(colIndex)));
mrs 185           }
mrs 186           columnCount++;
mrs 187           makeNewColumn(curColumnName, colWidth);
mrs 188         }
mrs 189       }
mrs 190     } catch(Exception e) {
mrs 191       //  If we encountered any errors in earlier columns, don't try
mrs 192       //  to set later columns.
mrs 193       JConfig.log().handleException("In display configuration for table " + prefix +", column \"" + curColumnName + "\" has an invalid property.", e);
mrs 194       JConfig.log().logDebug("No longer loading column widths from configuration.");
mrs 195     }
mrs 196 
mrs 197     //  If there are less than 2 columns, freak out and refresh.
mrs 198     if(columnCount < 2) {
mrs 199       SuperQueue.getInstance().preQueue("NOTIFY Column data for '" + prefix + "' was corrupted; resetting to defaults", "Swing", System.currentTimeMillis() + Constants.ONE_SECOND * 12 + notify_delay);
mrs 200       JConfig.log().logMessage("Column data for '" + prefix + "' was corrupted; resetting to defaults");
mrs 201       notify_delay += 2 * Constants.ONE_SECOND;
mrs 202       for(String[] column : DEFAULT_COLUMNS) {
mrs 203         String column_name = column[0];
mrs 204         String column_width = column[1];
mrs 205 
mrs 206         if (column_name.equals("Time left") && prefix.equals("complete")) continue;
mrs 207         makeNewColumn(column_name, column_width);
mrs 208       }
mrs 209     }
mrs 210 
mrs 211     if(!initialToSaved.isEmpty()) {
mrs 212       Collections.sort(initialToSaved, new Comparator<ColumnIndex>() {
mrs 213         public int compare(ColumnIndex o1, ColumnIndex o2) {
mrs 214           if(o1.getLast() < o2.getLast()) return -1;
mrs 215           if(o1.getLast() > o2.getLast()) return 1;
mrs 216           return 0;
mrs 217         }
mrs 218       });
mrs 219       for (ColumnIndex pair : initialToSaved) {
mrs 220         int colFrom = getColumnModel().getColumnIndex(pair.getFirst());
mrs 221         int colTo = pair.getLast();
mrs 222         try {
mrs 223           moveColumn(colFrom, colTo);
mrs 224         } catch (IllegalArgumentException iae) {
mrs 225           //  Ignore it, and move on.
mrs 226         }
mrs 227       }
mrs 228     }
mrs 229   }
mrs 230 
mrs 231   private void makeNewColumn(String curColumnName, String colWidth) {
mrs 232     TableColumn tc = new TableColumn(TableColumnController.getInstance().getColumnNumber(curColumnName));
mrs 233     tc.setHeaderValue(curColumnName);
mrs 234     tc.setIdentifier(curColumnName);
mrs 235     addColumn(tc);
mrs 236     getColumn(curColumnName).setPreferredWidth(Integer.parseInt(colWidth));
mrs 237     getColumn(curColumnName).setWidth(Integer.parseInt(colWidth));
mrs 238   }
mrs 239 }

Check out the code: svn co jbidwatcher/trunk/src/com/jbidwatcher/ui/table/AuctionTable.java