Logo Search packages:      
Sourcecode: cacao-oj6 version File versions

BP_FilteredOutSubpanel.java

/*
 * $Id$
 *
 * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */
package com.sun.javatest.exec;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.LinkedList;
import java.util.Vector;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.border.Border;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;

import com.sun.javatest.TestFilter;
import com.sun.javatest.TestResult;
import com.sun.javatest.TestResultTable;
import com.sun.javatest.tool.I18NUtils;
import com.sun.javatest.tool.UIFactory;
import com.sun.javatest.util.Debug;
import com.sun.javatest.util.I18NResourceBundle;

/**
 * This panel renders information about the tests which are "filtered out" in
 * the current node.  It shows the test name, as well as the reason that the
 * test was filtered out.  The tooltip are also upgraded beyond the standard
 * list to provide more filter information.
 * <p>
 * The background thread has a
 * two-stage commit process so that the iterator can run at full speed,
 * ignoring the MT-unsafe swing list model.  The changes are reflected in the
 * real list when the AWT event queue schedules the notification thread which
 * is created during the iteration.  This class also processes the list
 * click events and usually dispatches changes to the branch panel model.
 *
 * <p>
 * If you need to synchronize against both the vLock (for live data) and this
 * class' lock, then blocks should be synchronized against this outer class,
 * then the vLock.  The ordering is vital to avoiding deadlocks.
 */
00087 class BP_FilteredOutSubpanel extends BP_BranchSubpanel {
    BP_FilteredOutSubpanel(UIFactory uif, BP_Model bpm, TestTreeModel ttm) {
      super("fo", uif, bpm, ttm, "br.fo");

      init();

      cacheWatcher = new CacheObserver();
    }

    /**
     * Clear the table contents and perpare to receive new data.
     */
00099     void reset(TT_NodeCache cache) {
      synchronized (BP_FilteredOutSubpanel.this) {
          if (this.cache != null)
            this.cache.removeObserver(cacheWatcher);

          this.cache = cache;

          if (resyncThread != null) {
            resyncThread.halt();
          }

          if (mod != null)
            mod.reset();
      }   // sync

      validateEnableState();
      //table.clearSelection();
    }

00118     protected void invalidateFilters() {
      super.invalidateFilters();

      // if we didn't have one, we certainly don't need to disconnect,
      // and probably don't need to get a new one...
      if (cache != null) {
          cache.removeObserver(cacheWatcher);
      }

      if (subpanelNode != null) {
          cache = ttm.getNodeInfo(subpanelNode, false);
          validateEnableState();
      }

      updateInfoText();
    }

    /**
     * Only called when this panel is onscreen and needs to be kept up to date.
     */
00138     protected synchronized void updateSubpanel(TestResultTable.TreeNode currNode) {
      super.updateSubpanel(currNode);

      // only run if we change nodes
      if (lastNode != currNode || filtersInvalidated) {
          if (debug)
            Debug.println("updating FO table");

          if (resyncThread != null) {
            resyncThread.halt();
          }

          resyncThread = new TableSynchronizer();
          resyncThread.start();
          lastNode = currNode;

          filtersInvalidated = false;
          validateEnableState();
      }
    }

    private void updateInfoText() {
      if (infoTa == null)
          return;

      TestFilter f = model.getFilter();
      if (f != null)
          infoTa.setText(uif.getI18NString("br.fo.info.txt", f.getName()));
      else
          infoTa.setText(uif.getI18NString("br.fo.noFn.txt"));
    }

    /**
     * Enable or disable this panel as necessary.
     */
00173     private void validateEnableState() {
      if (cache.getRejectCount() > 0) {
          model.setEnabled(BP_FilteredOutSubpanel.this, true);
      }
      else if (cache.getRejectCount() == 0) {
          model.setEnabled(BP_FilteredOutSubpanel.this, false);
      }
      else { }
    }

    /**
     * A special thread to repopulate the test lists.
     */
00186     private class TableSynchronizer extends Thread {
      TableSynchronizer() {
          super("filtered-out list synchronizer");
          setPriority(Thread.MIN_PRIORITY + 2);
      }

      public void run() {
          // grab cache lock first because many other threads may alter that
          // object, causing a deadlock here.
          // sync. to hold observer traffic until re-sync is done
          synchronized (cache) {
            synchronized(BP_FilteredOutSubpanel.this) {
                // resync with this node cache
                Vector[] newData = cache.addObserver(cacheWatcher, true);

                // add tests into the list model - this doesn't make the data
                // live though
                for (int j = 0; j < newData[newData.length-1].size() - 1; j++) {
                  if (stopping)
                      break;

                  mod.addTest(newData[newData.length-1].elementAt(j), true);
                }   // for

                if (newData[newData.length-1].size() > 0 && !stopping) {
                  // final item with a notify
                  mod.addTest(newData[newData.length-1].lastElement(), false);
                }

                // to indicate completion
                resyncThread = null;
            }   // this sync
          } // cache sync

          validateEnableState();
      }   // run()

      public void halt() {
          stopping = true;
      }

      private volatile boolean stopping;
    }

    private void init() {
      mod = new TestTableModel(uif);
      renderer = new TestCellRenderer(uif);
      listener = new InputListener();

      table = uif.createTable("br.fo.tbl", mod);
      table.setOpaque(true);
      table.setRowSelectionAllowed(true);
      table.setColumnSelectionAllowed(false);
      table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
      table.getSelectionModel().addListSelectionListener(listener);

      // setup col 0
      TableColumn tc = table.getColumnModel().getColumn(0);
      tc.setCellRenderer(renderer);
      tc.setResizable(true);

      // setup col 1
      tc = table.getColumnModel().getColumn(1);
      tc.setCellRenderer(renderer);
      tc.setResizable(true);

      uif.setAccessibleInfo(this, "br.fo");

      setLayout(new GridBagLayout());

      GridBagConstraints gbc = new GridBagConstraints();
      gbc.fill = GridBagConstraints.HORIZONTAL;
      gbc.anchor = GridBagConstraints.NORTH;
      gbc.gridy = 0;
      gbc.ipady = 12;
      gbc.weightx = 1.0;
      gbc.weighty = 0.0;

      infoTa = uif.createMessageArea("br.fo.info");
      infoTa.setOpaque(false);
      add(infoTa, gbc);

      gbc.gridy = 1;
      gbc.weightx = 2.0;
      gbc.weighty = 1.0;
      gbc.fill = GridBagConstraints.BOTH;
      add(uif.createScrollPane(table, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
            JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED), gbc);

      updateInfoText();

      InputListener tableListener = new InputListener();
      table.addMouseListener(tableListener); 
      table.getSelectionModel().addListSelectionListener(tableListener);

      // to trigger test selection when enter is pressed
      table.getInputMap(JComponent.WHEN_FOCUSED).put(
                  KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false),
                  "gotoTest");
      table.getActionMap().put("gotoTest",
                new KbTableAction(uif.getI18NResourceBundle(),
                            "br.list.enter"));
      // this is necessary to make sure that the split pane can resize
      // this panel.  without setting the min., the panel seems to take
      // all it is given, and never gives it back.
      setMinimumSize(new Dimension(150,100));
    }

    /**
     * It is assumed that this will run on the event thread.
    private void setEmpty(boolean state) {
      if (state && list.getModel() != EmptyListModel.getInstance()) {
          list.setModel(EmptyListModel.getInstance());
          model.setEnabled(BP_TestListSubpanel.this, false);
          lastMsg = "";
      }
      else if (!state && list.getModel() == EmptyListModel.getInstance()) {
          list.setModel(mod);
          model.setEnabled(BP_TestListSubpanel.this, true);
      }
    }
     */

    // ------------- inner class -------------
    /**
     * Enumerates tree in background to populate the list.
     * If this thread is running, consider list data incomplete.
     * Swing cannot handle an updating model, so there is a two-stage absorbtion
     * of data.  This thread runs with no delay reading the TRT and placing that data
     * into an "offline" queue.  It periodically schedules an event on the GUI event
     * thread; when that thread run, it copies the data from the offline area to the
     * online area, which is what the ListModel presents.  This workaround assumes that
     * Swing will never dispatch more than one event at a time.
     */
00320     private class TestTableModel extends AbstractTableModel {
      TestTableModel(UIFactory uif) {
          super();

          colNames = new String[] {
            uif.getI18NString("br.fo.col0.txt"),
            uif.getI18NString("br.fo.col1.txt")
          };

          if (debug) {
            Debug.println("TableModel constructed: ");
            Debug.println("   -> " + this);
          }

          init();
      }

      // ---------- TableModel interface ----------
      public int getRowCount() {
          synchronized (liveData) {
            return liveData.size();
          }
      }

      public int getColumnCount() {
          return COLUMN_COUNT;
      }

      public String getColumnName(int columnIndex) {
          if (columnIndex >= colNames.length)
            throw new IndexOutOfBoundsException();
          else
            return colNames[columnIndex];
      }

      public Object getValueAt(int row, int column) {
          if (column == 0) {
            synchronized (liveData) {
                return liveData.get(row);
            }
          }
          else if (column == 1) {
            synchronized (liveData) {
                Object tst = (liveData.get(row));
                Object r = null;

                if (cache != null && r == null) {
                  r = cache.getRejectReason((TestResult)tst);
                }

                if (r == null)
                  r = uif.getI18NString("br.fo.noFi.txt");

                return r;
            }
          }
          else
            throw new IndexOutOfBoundsException(
                "Index into filtered out table is out of range: " +
                row + ", " + column);
      }

      public boolean isCellEditable(int rowIndex, int colIndex) {
          return false;
      }

      // ---------- Custom methods for this model ----------
      /**
       * @param suppressNotify Actively request that no update be scheduled.
       */
00390       void addTest(Object tr, boolean suppressNotify) {
          synchronized (vLock) {
            // make sure this item is not already in the list
            if (!inQueue.contains(tr)) {
                inQueue.addElement(tr);
            }
          } // sync

          // try not to saturate the GUI event thread
          if (!suppressNotify && !isUpdateScheduled) {
            TableNotifier tn = new TableNotifier(subpanelNode, this);
            pendingEvents.addElement(tn);
            EventQueue.invokeLater(tn);
          }
      }

      /**
       * Remove the given test from the list.
       * Ignored if the test is not in the list.
       */
00410       void removeTest(Object tr) {
          synchronized (vLock) {
            rmQueue.addElement(tr);

            // try not to saturate the GUI event thread
            if (!isUpdateScheduled) {
                TableNotifier tn = new TableNotifier(subpanelNode, this);
                pendingEvents.addElement(tn);
                EventQueue.invokeLater(tn);
            }
          } // sync

      }

      void reset() {
          synchronized (vLock) {
            init();
          }

          // force GUI to update the now empty list
          notifyDone();
      }

      // ------------ private --------------

      private void init() {
          // discard all pending events
          // this is necessary to ensure that update events which haven't
          // been processed are not processed after the model has changed
          // arguably, this should be solved by putting this init() onto
          // the event thread
          synchronized (pendingEvents) {
            for (int i = 0; i < pendingEvents.size(); i++) {
                TableNotifier tn = (TableNotifier)(pendingEvents.get(i));
                tn.cancel();
            }   // for
          }

          inQueue = new Vector();
          rmQueue = new Vector();
          liveData = new LinkedList();

          isUpdateScheduled = false;
      }

      /**
       * Transfer data from the internal queue to the live data queue.
       * This is part of the Swing threading workaround.  This method immediately
       * exits if there is no work to do.  It also dispatches model update events
       * if necessary.
       * This method always runs on the event dispatch thread.
       */
00462       private void goLive() {
          int firstNew, lastNew = 0;
          if (debug)
            Debug.println("BP_TL.TLM - goLive() starting.");

          // this is sync. against the outer class because we may change the
          // list model object during execution of this block
          synchronized (BP_FilteredOutSubpanel.this) {
            synchronized (vLock) {
                if (inQueue.size() == 0 && rmQueue.size() == 0) {
                  if (debug)
                      Debug.println("BP_TT.TTM - goLive() nothing to do, returning");
                  return;
                }

                processRemoveQueue();
                //preprocessAddQueue();

                // now add the new items
                if (inQueue.size() != 0) {
                  synchronized (liveData) {
                      firstNew = liveData.size();
                      if (inQueue.size() < BATCH_SIZE) {
                        liveData.addAll(inQueue);
                        lastNew = liveData.size()-1;
                        inQueue.setSize(0);
                      }
                      else {      // only add some of the new items
                            for (int i = 0; i < BATCH_SIZE; i++) {
                              liveData.add(inQueue.remove(0));
                            } // for

                        // schedule a future update
                        if (!isUpdateScheduled) {
                            TableNotifier tn = new TableNotifier(
                                          subpanelNode, this);
                            pendingEvents.addElement(tn);
                            EventQueue.invokeLater(tn);
                        }

                        lastNew = liveData.size()-1;
                      }
                  }     // sync

                  // dispatch update range event to Swing
                  if (listenerList.getListenerCount() > 0) {
                      TableModelEvent e =
                        new TableModelEvent(this, firstNew, lastNew,
                                  TableModelEvent.ALL_COLUMNS,
                                  TableModelEvent.INSERT);
                      TableNotifier tn = new TableNotifier(e, mod);
                      pendingEvents.addElement(tn);
                      EventQueue.invokeLater(tn);
                  }
                }

                // enable this tab now that it has data
                /*
                if (liveData.size() > 0) {
                  // switch back from an empty list
                  setEmpty(false);
                }
                else {
                  setEmpty(true);
                }
                */

                // this clears the "please wait" message if needed
                if (table.getSelectedRow() == -1 && inQueue.size() == 0)
                  showMessage("");
            }   // sync
          }

          if (debug)
            Debug.println("BP_TL.LT - goLive() finished");
      }

      /**
       * Remove tests in the removal queue from the live data or the incoming data.
       * vLock should be locked when you call this method
       */
00543       private void processRemoveQueue() {
          if (rmQueue.size() == 0)
            return;

          while (rmQueue.size() > 0) {
            TestResult target = (TestResult)(rmQueue.remove(0));
            int targetIndex = liveData.indexOf(target);
            if (targetIndex != -1) {
                synchronized (liveData) {
                  // necessary for proper synchronization
                  // should not be a problem really, based on how other
                  // locking is done, all work on liveData occurs in goLive()
                  targetIndex = liveData.indexOf(target);

                  // only should happen if the item disappears
                  if (targetIndex == -1)
                      continue;

                  liveData.remove(targetIndex);

                  // WARNING: since we are continually changing the contents of
                  // the data, you must notify the observers sychronously to get
                  // proper results
                  notifyRemoved(target, targetIndex);
                } // sync
            }
          } // while
      }

      /**
       * Remove duplicates in the add queue.
       * vLock should be locked when you call this method
       */
00576       private void preprocessAddQueue() {
          // make sure this list does not contain dups
          for (int i = 0; i < inQueue.size(); i++) {
            if (liveData.contains(inQueue.elementAt(i))) {
                inQueue.remove(i);
                i--;
            }
            else {
            }
          } // for
      }

      // --------- event utility methods -----------
      /**
       * Notify observers that the given index was added
       */
00592       private void notifyAdded(TestResult what, int index) {
          if (listenerList.getListenerCount() > 0) {
            // may want to buffer these messages for performance
            TableModelEvent e = new TableModelEvent (this, index, index,
                                  TableModelEvent.ALL_COLUMNS,
                                  TableModelEvent.INSERT);

            if (EventQueue.isDispatchThread()) {
                // XXX try without this to see perf. impact
                // dispatch synchronously
                mod.fireTableChanged(e);
            }
            else {
                // switch event onto AWT event thread
                TableNotifier tn = new TableNotifier(e, mod);
                pendingEvents.addElement(tn);
                EventQueue.invokeLater(tn);
            }
          }
      }

      private void notifyRemoved(TestResult what, int index) {
          if (listenerList.getListenerCount() > 0) {
            // may want to buffer these messages
            TableModelEvent e = new TableModelEvent(this, index, index,
                              TableModelEvent.ALL_COLUMNS,
                              TableModelEvent.DELETE);

            if (EventQueue.isDispatchThread()) {
                // XXX try without this to see perf. impact
                // dispatch synchronously
                mod.fireTableChanged(e);
            }
            else {
                // switch event onto AWT event thread
                TableNotifier tn = new TableNotifier(e, mod);
                pendingEvents.addElement(tn);
                EventQueue.invokeLater(tn);
            }
          }
      }

      private void notifyDone() {
          if (listenerList.getListenerCount() > 0) {
            // may want to buffer these messages
            TableModelEvent e = new TableModelEvent(this);
            // switch event onto AWT event thread
            TableNotifier tn = new TableNotifier(e, mod);
            pendingEvents.addElement(tn);
            EventQueue.invokeLater(tn);
          }
      }

      private String[] colNames;

      // must sync. on vLock anytime you access inQueue or liveData
      private final Object vLock = new Object();      // lock for inQueue & rmQueue
      private Vector inQueue;     // queue of items to be added to live data
      private Vector rmQueue;     // queue of items to be removed from live data
      private LinkedList liveData;  // to allow manual synchronization
      Vector pendingEvents = new Vector();

      volatile boolean isUpdateScheduled;  // are updates waiting in inQueue or rmQueue

      private static final int BATCH_SIZE = 100;
      private static final int COLUMN_COUNT = 2;
    }

    private class CacheObserver extends TT_NodeCache.TT_NodeCacheObserver {
      CacheObserver() {
          super();
          // configure our interest list
          interestList[MSGS_FILTERED] = true;
      }

      public void testAdded(int msgType, TestResultTable.TreeNode[] path,
                        TestResult what, int index) {
          synchronized(BP_FilteredOutSubpanel.this) {
            mod.addTest(what, false);
          }
      }

      public void testRemoved(int msgType, TestResultTable.TreeNode[] path,
                        TestResult what, int index) {
          synchronized(BP_FilteredOutSubpanel.this) {
            mod.removeTest(what);
          }
      }

      public void statsUpdated(int[] stats) {
          // ignore
      }
    }

    /**
     * This is a double duty class; it commits changes the model and also dispatches
     * given events.  If instance var. lt is null, it dispatches events, otherwise it
     * triggers a commit on the list thread data using (<tt>goLive()</tt>).
     * This class is critical because it is the task which gets scheduled on
     * the event thread.
     */
00693     class TableNotifier implements Runnable {
      TableNotifier(TestResultTable.TreeNode n, TestTableModel m) {
          node = n;
          tm = m;
          tm.isUpdateScheduled = true;
      }

      TableNotifier(TableModelEvent e, TestTableModel m) {
          tm = m;
          tme = e;
      }

      public void run() {
          tm.pendingEvents.remove(this);

          // this message has been cancelled
          if (!isValid)
            return;

          if (tme == null) {
            // consume the update event
            tm.isUpdateScheduled = false;
            tm.goLive();
          }
          else {
            tm.fireTableChanged(tme);
          }
      }

      public void cancel() {
          isValid = false;
      }

      // used to validate the event at dispatch time
      TestResultTable.TreeNode node;

      // go live data, no event dispatch
      TestTableModel tm;

      // event dispatch items
      private TableModelEvent tme;
      private boolean isValid = true;
    }   // list notifier

    /**
     * One of these listeners is associated with each of the test lists.
     */
00740     class InputListener extends MouseAdapter implements ListSelectionListener {
      // Mouse Adapter
      public void mouseClicked(MouseEvent e) {
          JTable tbl = (JTable)(e.getComponent());
          int col = table.columnAtPoint(e.getPoint());
          int row = table.rowAtPoint(e.getPoint());
          TableModel tm = table.getModel();

          // an empty table can't do anything
          if (tm.getRowCount() < 1) {
            // clear the message field
            showMessage("");
            return;
          }

          // always use col 1, which is where the TestResult is
          // we only really care which row was clicked on
          TestResult tr = (TestResult)(tm.getValueAt(row, 0));

          if (e.getClickCount() == 1) {
            // show vital stats only
            showMessage(I18NUtils.getStatusMessage(tr.getStatus()));
          }
          else if (e.getClickCount() == 2) {
            // construct the path required by the model
            TestResultTable.TreeNode[] path = TestResultTable.getObjectPath(tr);

            // sanity check, could happen in exceptional cases (out of memory)
            if (path == null || path.length == 0)
                return;

            Object[] fp = new Object[path.length + 1];
            System.arraycopy(path, 0, fp, 0, path.length);
            fp[fp.length-1] = tr;

            model.showTest(tr, fp);
          }
      }

      // ListSelectionListener
      public void valueChanged(ListSelectionEvent e) {
          int index = e.getLastIndex();

          if (mod.getRowCount() == 0 || index >= mod.getRowCount()) {
            // swing seems to generate re-selection events when the
            // list model is 'replaced' with a new one (completely
            // invalidated).  for some reason it changes the selection
            // index without doing bounds checking
            return;         // ignore invalid event
          }

          if (index != lastIndex) {
            TestResult tr =
                (TestResult)(mod.getValueAt(index, 0));

            // show vital stats only
            showMessage(I18NUtils.getStatusMessage(tr.getStatus()));
            lastIndex = index;
          }
      }

      private int lastIndex = -2;
    }

    /**
     * Action to handle user pressing enter to select a test.
     */
00807     private class KbTableAction extends AbstractAction {
      KbTableAction(I18NResourceBundle bund, String key) {
          desc = bund.getString(key + ".desc");
          name = bund.getString(key + ".act");
      }

      public void actionPerformed(ActionEvent e) {
          int row = table.getSelectedRow();

          // nothing selected, ignore event
          if (row < 0)
            return;

          Object target = table.getModel().getValueAt(row, 0);

          // this shouldn't be the case...
          if (!(target instanceof TestResult))
            return;

          TestResult tr = (TestResult)target;
          TestResultTable.TreeNode[] path = TestResultTable.getObjectPath(tr);

          // sanity check, could happen in exceptional cases (out of memory)
          if (path == null || path.length == 0)
            return;

          Object[] fp = new Object[path.length + 1];
          System.arraycopy(path, 0, fp, 0, path.length);
          fp[fp.length-1] = tr;

          model.showTest(tr, fp);
      }

      public Object getValue(String key) {
          if (key == null)
            throw new NullPointerException();

          if (key.equals(NAME))
            return name;
          else if (key.equals(SHORT_DESCRIPTION))
            return desc;
          else 
            return null;
      }

      private String name;
      private String desc;
    }

    class TestCellRenderer extends DefaultTableCellRenderer {
       public TestCellRenderer(UIFactory uif) {
           setOpaque(false);
       }

      public Component getTableCellRendererComponent(JTable table, Object value,
                  boolean isSelected, boolean hasFocus, int row,
                  int column) {
          if (value == null)  // very strange...
            return this;

          if (value instanceof TestResult) {
            TestResult tr = (TestResult)value;
            setText(tr.getTestName());
            setToolTipText(I18NUtils.getStatusMessage(tr.getStatus()));

          } else if (value instanceof TestFilter) {
            TestFilter tf = (TestFilter)value;
            setText(tf.getReason());
            setToolTipText(tf.getDescription());
          }
          else {  // this will run for the reason column (1)
            setText(value.toString());
          }

          setBorder(spacerBorder);
          setFont(getFont().deriveFont(Font.PLAIN));

          if (isSelected) {
            setOpaque(true);
            //setBackground(MetalLookAndFeel.getTextHighlightColor());
            setBackground(table.getSelectionBackground());
            setForeground(table.getSelectionForeground());
          }
          else {
            //setForeground(MetalLookAndFeel.getPrimaryControlDarkShadow());
            setForeground(Color.black);
            setOpaque(false);
          }

          // would like to find a better way to do this
          // seems like we can't do this properly until the first item
          // is rendered though
          if (!rowHeightSet) {
            table.setRowHeight(getFontMetrics(getFont()).getHeight() +
                           ROW_HEIGHT_PADDING);
            rowHeightSet = true;
          }

          return this;
      }

      // border to pad left and right
      private Border spacerBorder = BorderFactory.createEmptyBorder(3,3,3,3);
    }


    private JTable table;
    private TestTableModel mod;           // JTable model
    private TT_NodeCache cache;
    private TestResultTable.TreeNode lastNode;      // last node updated against
    private CacheObserver cacheWatcher;
    private volatile TableSynchronizer resyncThread;
    private TableCellRenderer renderer;
    private InputListener listener;
    private JTextArea infoTa;
    private boolean rowHeightSet;
    private static final int ROW_HEIGHT_PADDING = 3;

    private boolean debug = Debug.getBoolean(BP_FilteredOutSubpanel.class);
}

Generated by  Doxygen 1.6.0   Back to index