Autopsy  4.5.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
DataResultViewerTable.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-2017 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.corecomponents;
20 
21 import java.awt.Color;
22 import java.awt.Component;
23 import java.awt.Cursor;
24 import java.awt.FontMetrics;
25 import java.awt.Graphics;
26 import java.awt.dnd.DnDConstants;
27 import java.awt.event.MouseAdapter;
28 import java.awt.event.MouseEvent;
29 import java.beans.PropertyVetoException;
30 import java.lang.reflect.InvocationTargetException;
31 import java.util.ArrayList;
32 import java.util.Comparator;
33 import java.util.HashMap;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.TreeMap;
37 import java.util.TreeSet;
38 import java.util.logging.Level;
39 import java.util.prefs.Preferences;
40 import javax.swing.JTable;
41 import javax.swing.ListSelectionModel;
42 import javax.swing.SwingUtilities;
43 import javax.swing.event.ChangeEvent;
44 import javax.swing.event.ListSelectionEvent;
45 import javax.swing.event.TableColumnModelEvent;
46 import javax.swing.event.TableColumnModelListener;
47 import javax.swing.table.TableCellRenderer;
48 import javax.swing.table.TableColumn;
49 import javax.swing.table.TableColumnModel;
50 import org.netbeans.swing.etable.ETableColumn;
51 import org.netbeans.swing.etable.ETableColumnModel;
52 import org.netbeans.swing.outline.DefaultOutlineCellRenderer;
53 import org.netbeans.swing.outline.DefaultOutlineModel;
54 import org.netbeans.swing.outline.Outline;
55 import org.openide.explorer.ExplorerManager;
56 import org.openide.explorer.view.OutlineView;
57 import org.openide.nodes.AbstractNode;
58 import org.openide.nodes.Children;
59 import org.openide.nodes.Node;
60 import org.openide.nodes.Node.Property;
61 import org.openide.util.NbBundle;
62 import org.openide.util.NbPreferences;
67 
75 //@ServiceProvider(service = DataResultViewer.class)
76 public class DataResultViewerTable extends AbstractDataResultViewer {
77 
78  private static final long serialVersionUID = 1L;
79  private static final Logger logger = Logger.getLogger(DataResultViewerTable.class.getName());
80  @NbBundle.Messages("DataResultViewerTable.firstColLbl=Name")
81  static private final String FIRST_COLUMN_LABEL = Bundle.DataResultViewerTable_firstColLbl();
82  private static final Color TAGGED_COLOR = new Color(255, 255, 195);
83 
84  private final String title;
85 
95  private final Map<Integer, Property<?>> propertiesMap = new TreeMap<>();
96 
102  private final Map<String, ETableColumn> columnMap = new HashMap<>();
103 
104  private Node currentRoot;
105 
106  /*
107  * Convience reference to internal Outline.
108  */
109  private Outline outline;
110 
115 
122  public DataResultViewerTable(ExplorerManager explorerManager) {
123  this(explorerManager, Bundle.DataResultViewerTable_title());
124  }
125 
133  public DataResultViewerTable(ExplorerManager explorerManager, String title) {
134  super(explorerManager);
135  this.title = title;
136 
137  initComponents();
138 
139  outlineView.setAllowedDragActions(DnDConstants.ACTION_NONE);
140  outline = outlineView.getOutline();
141  outline.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
142  outline.setRootVisible(false); // don't show the root node
143  outline.setDragEnabled(false);
144  outline.setDefaultRenderer(Object.class, new ColorTagCustomRenderer());
145  // add a listener so that when columns are moved, the new order is stored
146  tableListener = new TableListener();
147  outline.getColumnModel().addColumnModelListener(tableListener);
148  // the listener also moves columns back if user tries to move the first column out of place
149  outline.getTableHeader().addMouseListener(tableListener);
150  }
151 
157  this(new ExplorerManager(),Bundle.DataResultViewerTable_title());
158  }
159 
160 
166  @Override
167  public void expandNode(Node n) {
168  super.expandNode(n);
169 
170  outlineView.expandNode(n);
171  }
172 
178  @SuppressWarnings("unchecked")
179  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
180  private void initComponents() {
181 
183 
184  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
185  this.setLayout(layout);
186  layout.setHorizontalGroup(
187  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
188  .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 691, Short.MAX_VALUE)
189  );
190  layout.setVerticalGroup(
191  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
192  .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 366, Short.MAX_VALUE)
193  );
194  }// </editor-fold>//GEN-END:initComponents
195  // Variables declaration - do not modify//GEN-BEGIN:variables
196  private org.openide.explorer.view.OutlineView outlineView;
197  // End of variables declaration//GEN-END:variables
198 
199  @Override
200  public boolean isSupported(Node selectedNode) {
201  return true;
202  }
203 
204  @Override
206  public void setNode(Node selectedNode) {
207 
208  /*
209  * The quick filter must be reset because when determining column width,
210  * ETable.getRowCount is called, and the documentation states that quick
211  * filters must be unset for the method to work "If the quick-filter is
212  * applied the number of rows do not match the number of rows in the
213  * model."
214  */
215  outline.unsetQuickFilter();
216  // change the cursor to "waiting cursor" for this operation
217  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
218  try {
219  boolean hasChildren = false;
220  if (selectedNode != null) {
221  // @@@ This just did a DB round trip to get the count and the results were not saved...
222  hasChildren = selectedNode.getChildren().getNodesCount() > 0;
223  }
224 
225  if (hasChildren) {
226  currentRoot = selectedNode;
227  em.setRootContext(currentRoot);
228  setupTable();
229  } else {
230  Node emptyNode = new AbstractNode(Children.LEAF);
231  em.setRootContext(emptyNode); // make empty node
232  outline.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
233 
234  /*
235  * Since we are modifying the columns, we don't want to listen
236  * to added/removed events as un-hide/hide.
237  */
238  tableListener.listenToVisibilityChanges(false);
239  outlineView.setPropertyColumns(); // set the empty property header
240  }
241  } finally {
242  this.setCursor(null);
243  }
244  }
245 
250  private void setupTable() {
251  /*
252  * Since we are modifying the columns, we don't want to listen to
253  * added/removed events as un-hide/hide, until the table setup is done.
254  */
255  tableListener.listenToVisibilityChanges(false);
256 
268  List<Node.Property<?>> props = loadColumnOrder();
269  boolean propsExist = props.isEmpty() == false;
270  Node.Property<?> firstProp = null;
271  if (propsExist) {
272  firstProp = props.remove(0);
273  }
274 
275  /*
276  * show the horizontal scroll panel and show all the content & header If
277  * there is only one column (which was removed from props above) Just
278  * let the table resize itself.
279  */
280  outline.setAutoResizeMode((props.isEmpty()) ? JTable.AUTO_RESIZE_ALL_COLUMNS : JTable.AUTO_RESIZE_OFF);
281 
282  assignColumns(props); // assign columns to match the properties
283  if (firstProp != null) {
284  ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(firstProp.getDisplayName());
285  }
286 
287  setColumnWidths();
288 
289  //Load column sorting information from preferences file and apply it to columns.
291 
292  /*
293  * Save references to columns before we deal with their visibility. This
294  * has to happen after the sorting is applied, because that actually
295  * causes the columns to be recreated. It has to happen before
296  * loadColumnVisibility so we have referenecs to the columns to pass to
297  * setColumnHidden.
298  */
300 
301  //Load column visibility information from preferences file and apply it to columns.
303 
304  /*
305  * If one of the child nodes of the root node is to be selected, select
306  * it.
307  */
308  SwingUtilities.invokeLater(() -> {
309  if (currentRoot instanceof TableFilterNode) {
310  NodeSelectionInfo selectedChildInfo = ((TableFilterNode) currentRoot).getChildNodeSelectionInfo();
311  if (null != selectedChildInfo) {
312  Node[] childNodes = currentRoot.getChildren().getNodes(true);
313  for (int i = 0; i < childNodes.length; ++i) {
314  Node childNode = childNodes[i];
315  if (selectedChildInfo.matches(childNode)) {
316  try {
317  em.setSelectedNodes(new Node[]{childNode});
318  } catch (PropertyVetoException ex) {
319  logger.log(Level.SEVERE, "Failed to select node specified by selected child info", ex);
320  }
321  break;
322  }
323  }
324  ((TableFilterNode) currentRoot).setChildNodeSelectionInfo(null);
325  }
326  }
327  });
328 
329  //the table setup is done, so any added/removed events can now be treated as un-hide/hide.
330  tableListener.listenToVisibilityChanges(true);
331  }
332 
333  /*
334  * Populate the map with references to the column objects for use when
335  * loading/storing the visibility info.
336  */
337  private void populateColumnMap() {
338  columnMap.clear();
339  TableColumnModel columnModel = outline.getColumnModel();
340  int columnCount = columnModel.getColumnCount();
341  //for each property get a reference to the column object from the column model.
342  for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
343  final String propName = entry.getValue().getName();
344  if (entry.getKey() < columnCount) {
345  final ETableColumn column = (ETableColumn) columnModel.getColumn(entry.getKey());
346  columnMap.put(propName, column);
347  }
348  }
349  }
350 
351  private void setColumnWidths() {
352  if (currentRoot.getChildren().getNodesCount() != 0) {
353  final Graphics graphics = outlineView.getGraphics();
354  if (graphics != null) {
355  final FontMetrics metrics = graphics.getFontMetrics();
356 
357  int margin = 4;
358  int padding = 8;
359 
360  for (int column = 0; column < outline.getModel().getColumnCount(); column++) {
361  int firstColumnPadding = (column == 0) ? 32 : 0;
362  int columnWidthLimit = (column == 0) ? 350 : 300;
363  int valuesWidth = 0;
364 
365  // find the maximum width needed to fit the values for the first 100 rows, at most
366  for (int row = 0; row < Math.min(100, outline.getRowCount()); row++) {
367  TableCellRenderer renderer = outline.getCellRenderer(row, column);
368  Component comp = outline.prepareRenderer(renderer, row, column);
369  valuesWidth = Math.max(comp.getPreferredSize().width, valuesWidth);
370  }
371 
372  int headerWidth = metrics.stringWidth(outline.getColumnName(column));
373  valuesWidth += firstColumnPadding; // add extra padding for first column
374 
375  int columnWidth = Math.max(valuesWidth, headerWidth);
376  columnWidth += 2 * margin + padding; // add margin and regular padding
377  columnWidth = Math.min(columnWidth, columnWidthLimit);
378 
379  outline.getColumnModel().getColumn(column).setPreferredWidth(columnWidth);
380  }
381  }
382  } else {
383  // if there's no content just auto resize all columns
384  outline.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
385  }
386  }
387 
388  synchronized private void assignColumns(List<Property<?>> props) {
389  // Get the columns setup with respect to names and sortability
390  String[] propStrings = new String[props.size() * 2];
391  for (int i = 0; i < props.size(); i++) {
392  final Property<?> prop = props.get(i);
393  prop.setValue("ComparableColumnTTV", Boolean.TRUE); //NON-NLS
394  //First property column is sorted initially
395  if (i == 0) {
396  prop.setValue("TreeColumnTTV", Boolean.TRUE); // Identifies special property representing first (tree) column. NON-NLS
397  prop.setValue("SortingColumnTTV", Boolean.TRUE); // TreeTableView should be initially sorted by this property column. NON-NLS
398  }
399  propStrings[2 * i] = prop.getName();
400  propStrings[2 * i + 1] = prop.getDisplayName();
401  }
402 
403  outlineView.setPropertyColumns(propStrings);
404  }
405 
409  private synchronized void storeColumnVisibility() {
410  if (currentRoot == null || propertiesMap.isEmpty()) {
411  return;
412  }
413  if (currentRoot instanceof TableFilterNode) {
414  TableFilterNode tfn = (TableFilterNode) currentRoot;
415  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
416  final ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
417 
418  //store hidden state
419  for (Map.Entry<String, ETableColumn> entry : columnMap.entrySet()) {
420 
421  String columnName = entry.getKey();
422  final String columnHiddenKey = ResultViewerPersistence.getColumnHiddenKey(tfn, columnName);
423  final TableColumn column = entry.getValue();
424 
425  boolean columnHidden = columnModel.isColumnHidden(column);
426  if (columnHidden) {
427  preferences.putBoolean(columnHiddenKey, true);
428  } else {
429  preferences.remove(columnHiddenKey);
430  }
431  }
432  }
433  }
434 
438  private synchronized void storeColumnOrder() {
439  if (currentRoot == null || propertiesMap.isEmpty()) {
440  return;
441  }
442  if (currentRoot instanceof TableFilterNode) {
443  TableFilterNode tfn = (TableFilterNode) currentRoot;
444  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
445 
446  // Store the current order of the columns into settings
447  for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
448  preferences.putInt(ResultViewerPersistence.getColumnPositionKey(tfn, entry.getValue().getName()), entry.getKey());
449  }
450  }
451  }
452 
456  private synchronized void storeColumnSorting() {
457  if (currentRoot == null || propertiesMap.isEmpty()) {
458  return;
459  }
460  if (currentRoot instanceof TableFilterNode) {
461  final TableFilterNode tfn = ((TableFilterNode) currentRoot);
462  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
463  ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
464  for (Map.Entry<String, ETableColumn> entry : columnMap.entrySet()) {
465  ETableColumn etc = entry.getValue();
466  String columnName = entry.getKey();
467 
468  //store sort rank and order
469  final String columnSortOrderKey = ResultViewerPersistence.getColumnSortOrderKey(tfn, columnName);
470  final String columnSortRankKey = ResultViewerPersistence.getColumnSortRankKey(tfn, columnName);
471  if (etc.isSorted() && (columnModel.isColumnHidden(etc) == false)) {
472  preferences.putBoolean(columnSortOrderKey, etc.isAscending());
473  preferences.putInt(columnSortRankKey, etc.getSortRank());
474  } else {
475  columnModel.setColumnSorted(etc, true, 0);
476  preferences.remove(columnSortOrderKey);
477  preferences.remove(columnSortRankKey);
478  }
479  }
480  }
481  }
482 
489  private synchronized void loadColumnSorting() {
490  if (currentRoot == null || propertiesMap.isEmpty()) {
491  return;
492  }
493 
494  if (currentRoot instanceof TableFilterNode) {
495  final TableFilterNode tfn = (TableFilterNode) currentRoot;
496 
497  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
498  //organize property sorting information, sorted by rank
499  TreeSet<ColumnSortInfo> sortInfos = new TreeSet<>(Comparator.comparing(ColumnSortInfo::getRank));
500  propertiesMap.entrySet().stream().forEach(entry -> {
501  final String propName = entry.getValue().getName();
502  //if the sort rank is undefined, it will be defaulted to 0 => unsorted.
503 
504  Integer sortRank = preferences.getInt(ResultViewerPersistence.getColumnSortRankKey(tfn, propName), 0);
505  //default to true => ascending
506  Boolean sortOrder = preferences.getBoolean(ResultViewerPersistence.getColumnSortOrderKey(tfn, propName), true);
507 
508  sortInfos.add(new ColumnSortInfo(entry.getKey(), sortRank, sortOrder));
509  });
510 
511  //apply sort information in rank order.
512  sortInfos.forEach(sortInfo -> outline.setColumnSorted(sortInfo.modelIndex, sortInfo.order, sortInfo.rank));
513  }
514  }
515 
516  private synchronized void loadColumnVisibility() {
517  if (currentRoot == null || propertiesMap.isEmpty()) {
518  return;
519  }
520 
521  if (currentRoot instanceof TableFilterNode) {
522 
523  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
524 
525  final TableFilterNode tfn = ((TableFilterNode) currentRoot);
526  ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
527  for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
528  final String propName = entry.getValue().getName();
529  boolean hidden = preferences.getBoolean(ResultViewerPersistence.getColumnHiddenKey(tfn, propName), false);
530  final TableColumn column = columnMap.get(propName);
531  columnModel.setColumnHidden(column, hidden);
532  }
533  }
534  }
535 
544  private synchronized List<Node.Property<?>> loadColumnOrder() {
545 
546  List<Property<?>> props = ResultViewerPersistence.getAllChildProperties(currentRoot, 100);
547 
548  // If node is not table filter node, use default order for columns
549  if (!(currentRoot instanceof TableFilterNode)) {
550  return props;
551  }
552 
553  final TableFilterNode tfn = ((TableFilterNode) currentRoot);
554  propertiesMap.clear();
555 
556  /*
557  * We load column index values into the properties map. If a property's
558  * index is outside the range of the number of properties or the index
559  * has already appeared as the position of another property, we put that
560  * property at the end.
561  */
562  int offset = props.size();
563  boolean noPreviousSettings = true;
564 
565  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
566 
567  for (Property<?> prop : props) {
568  Integer value = preferences.getInt(ResultViewerPersistence.getColumnPositionKey(tfn, prop.getName()), -1);
569  if (value >= 0 && value < offset && !propertiesMap.containsKey(value)) {
570  propertiesMap.put(value, prop);
571  noPreviousSettings = false;
572  } else {
573  propertiesMap.put(offset, prop);
574  offset++;
575  }
576  }
577 
578  // If none of the properties had previous settings, we should decrement
579  // each value by the number of properties to make the values 0-indexed.
580  if (noPreviousSettings) {
581  ArrayList<Integer> keys = new ArrayList<>(propertiesMap.keySet());
582  for (int key : keys) {
583  propertiesMap.put(key - props.size(), propertiesMap.remove(key));
584  }
585  }
586 
587  return new ArrayList<>(propertiesMap.values());
588  }
589 
590  @Override
591  @NbBundle.Messages("DataResultViewerTable.title=Table")
592  public String getTitle() {
593  return title;
594  }
595 
596  @Override
598  return new DataResultViewerTable();
599  }
600 
601  @Override
602  public void clearComponent() {
603  this.outlineView.removeAll();
604  this.outlineView = null;
605 
606  super.clearComponent();
607 
608  }
609 
613  static private final class ColumnSortInfo {
614 
615  private final int modelIndex;
616  private final int rank;
617  private final boolean order;
618 
619  private ColumnSortInfo(int modelIndex, int rank, boolean order) {
620  this.modelIndex = modelIndex;
621  this.rank = rank;
622  this.order = order;
623  }
624 
625  private int getRank() {
626  return rank;
627  }
628  }
629 
634  private class TableListener extends MouseAdapter implements TableColumnModelListener {
635 
636  // When a column in the table is moved, these two variables keep track of where
637  // the column started and where it ended up.
638  private int startColumnIndex = -1;
639  private int endColumnIndex = -1;
640  private boolean listenToVisibilitEvents;
641 
642  @Override
643  public void columnMoved(TableColumnModelEvent e) {
644  int fromIndex = e.getFromIndex();
645  int toIndex = e.getToIndex();
646  if (fromIndex == toIndex) {
647  return;
648  }
649 
650  /*
651  * Because a column may be dragged to several different positions
652  * before the mouse is released (thus causing multiple
653  * TableColumnModelEvents to be fired), we want to keep track of the
654  * starting column index in this potential series of movements.
655  * Therefore we only keep track of the original fromIndex in
656  * startColumnIndex, but we always update endColumnIndex to know the
657  * final position of the moved column. See the MouseListener
658  * mouseReleased method.
659  */
660  if (startColumnIndex == -1) {
661  startColumnIndex = fromIndex;
662  }
663  endColumnIndex = toIndex;
664 
665  // This list contains the keys of propertiesMap in order
666  ArrayList<Integer> indicesList = new ArrayList<>(propertiesMap.keySet());
667  int leftIndex = Math.min(fromIndex, toIndex);
668  int rightIndex = Math.max(fromIndex, toIndex);
669  // Now we can copy the range of keys that have been affected by
670  // the column movement
671  List<Integer> range = indicesList.subList(leftIndex, rightIndex + 1);
672  int rangeSize = range.size();
673 
674  if (fromIndex < toIndex) {
675  // column moved right, shift all properties left, put in moved
676  // property at the rightmost index
677  Property<?> movedProp = propertiesMap.get(range.get(0));
678  for (int i = 0; i < rangeSize - 1; i++) {
679  propertiesMap.put(range.get(i), propertiesMap.get(range.get(i + 1)));
680  }
681  propertiesMap.put(range.get(rangeSize - 1), movedProp);
682  } else {
683  // column moved left, shift all properties right, put in moved
684  // property at the leftmost index
685  Property<?> movedProp = propertiesMap.get(range.get(rangeSize - 1));
686  for (int i = rangeSize - 1; i > 0; i--) {
687  propertiesMap.put(range.get(i), propertiesMap.get(range.get(i - 1)));
688  }
689  propertiesMap.put(range.get(0), movedProp);
690  }
691 
693  }
694 
695  @Override
696  public void mouseReleased(MouseEvent e) {
697  /*
698  * If the startColumnIndex is not -1 (which is the reset value),
699  * that means columns have been moved around. We then check to see
700  * if either the starting or end position is 0 (the first column),
701  * and then swap them back if that is the case because we don't want
702  * to allow movement of the first column. We then reset
703  * startColumnIndex to -1, the reset value. We check if
704  * startColumnIndex is at reset or not because it is possible for
705  * the mouse to be released and a MouseEvent to be fired without
706  * having moved any columns.
707  */
708  if (startColumnIndex != -1 && (startColumnIndex == 0 || endColumnIndex == 0)) {
709  outline.moveColumn(endColumnIndex, startColumnIndex);
710  }
711  startColumnIndex = -1;
712  }
713 
714  @Override
715  public void mouseClicked(MouseEvent e) {
716  //the user clicked a column header
718  }
719 
720  @Override
721  public void columnAdded(TableColumnModelEvent e) {
723  }
724 
725  @Override
726  public void columnRemoved(TableColumnModelEvent e) {
728  }
729 
735  private void columnAddedOrRemoved() {
736  if (listenToVisibilitEvents) {
737  SwingUtilities.invokeLater(DataResultViewerTable.this::storeColumnVisibility);
738 
739  }
740  }
741 
742  @Override
743  public void columnMarginChanged(ChangeEvent e) {
744  }
745 
746  @Override
747  public void columnSelectionChanged(ListSelectionEvent e) {
748  }
749 
759  private void listenToVisibilityChanges(boolean b) {
760  this.listenToVisibilitEvents = b;
761  }
762  }
763 
769  private class ColorTagCustomRenderer extends DefaultOutlineCellRenderer {
770 
771  private static final long serialVersionUID = 1L;
772 
773  @Override
774  public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {
775 
776  Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
777  // only override the color if a node is not selected
778  if (currentRoot != null && !isSelected) {
779  Node node = currentRoot.getChildren().getNodeAt(table.convertRowIndexToModel(row));
780  boolean tagFound = false;
781  if (node != null) {
782  Node.PropertySet[] propSets = node.getPropertySets();
783  if (propSets.length != 0) {
784  // currently, a node has only one property set, named Sheet.PROPERTIES ("properties")
785  Node.Property<?>[] props = propSets[0].getProperties();
786  for (Property<?> prop : props) {
787  if ("Tags".equals(prop.getName())) {//NON-NLS
788  try {
789  tagFound = !prop.getValue().equals("");
790  } catch (IllegalAccessException | InvocationTargetException ignore) {
791  }
792  break;
793  }
794  }
795  }
796  }
797  //if the node does have associated tags, set its background color
798  if (tagFound) {
799  component.setBackground(TAGGED_COLOR);
800  }
801  }
802  return component;
803  }
804  }
805 }
synchronized void assignColumns(List< Property<?>> props)
Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
DataResultViewerTable(ExplorerManager explorerManager, String title)

Copyright © 2012-2016 Basis Technology. Generated on: Tue Feb 20 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.