Autopsy  4.6.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 2012-2018 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;
63 import org.openide.util.lookup.ServiceProvider;
68 
79 @ServiceProvider(service = DataResultViewer.class)
80 public final class DataResultViewerTable extends AbstractDataResultViewer {
81 
82  private static final long serialVersionUID = 1L;
83  private static final Logger LOGGER = Logger.getLogger(DataResultViewerTable.class.getName());
84  @NbBundle.Messages("DataResultViewerTable.firstColLbl=Name")
85  static private final String FIRST_COLUMN_LABEL = Bundle.DataResultViewerTable_firstColLbl();
86  static private final Color TAGGED_ROW_COLOR = new Color(255, 255, 195);
87  private final String title;
88  private final Map<String, ETableColumn> columnMap;
89  private final Map<Integer, Property<?>> propertiesMap;
90  private final Outline outline;
92  private Node rootNode;
93 
102  this(null, Bundle.DataResultViewerTable_title());
103  }
104 
114  public DataResultViewerTable(ExplorerManager explorerManager) {
115  this(explorerManager, Bundle.DataResultViewerTable_title());
116  }
117 
128  public DataResultViewerTable(ExplorerManager explorerManager, String title) {
129  super(explorerManager);
130  this.title = title;
131  this.columnMap = new HashMap<>();
132  this.propertiesMap = new TreeMap<>();
133 
134  /*
135  * Execute the code generated by the GUI builder.
136  */
137  initComponents();
138 
139  /*
140  * Configure the child OutlineView (explorer view) component.
141  */
142  outlineView.setAllowedDragActions(DnDConstants.ACTION_NONE);
143 
144  outline = outlineView.getOutline();
145  outline.setRowSelectionAllowed(true);
146  outline.setColumnSelectionAllowed(true);
147  outline.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
148  outline.setRootVisible(false);
149  outline.setDragEnabled(false);
150  outline.setDefaultRenderer(Object.class, new ColorTagCustomRenderer());
151 
152  /*
153  * Add a table listener to the child OutlineView (explorer view) to
154  * persist the order of the table columns when a column is moved.
155  */
156  outlineViewListener = new TableListener();
157  outline.getColumnModel().addColumnModelListener(outlineViewListener);
158 
159  /*
160  * Add a mouse listener to the child OutlineView (explorer view) to make
161  * sure the first column of the table is kept in place.
162  */
163  outline.getTableHeader().addMouseListener(outlineViewListener);
164  }
165 
175  @Override
177  return new DataResultViewerTable();
178  }
179 
183  @Override
184  @NbBundle.Messages("DataResultViewerTable.title=Table")
185  public String getTitle() {
186  return title;
187  }
188 
197  @Override
198  public boolean isSupported(Node candidateRootNode) {
199  return true;
200  }
201 
207  @Override
209  public void setNode(Node rootNode) {
210  /*
211  * The quick filter must be reset because when determining column width,
212  * ETable.getRowCount is called, and the documentation states that quick
213  * filters must be unset for the method to work "If the quick-filter is
214  * applied the number of rows do not match the number of rows in the
215  * model."
216  */
217  outline.unsetQuickFilter();
218 
219  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
220  try {
221  /*
222  * If the given node is not null and has children, set it as the
223  * root context of the child OutlineView, otherwise make an
224  * "empty"node the root context.
225  *
226  * IMPORTANT NOTE: This is the first of many times where a
227  * getChildren call on the current root node causes all of the
228  * children of the root node to be created and defeats lazy child
229  * node creation, if it is enabled. It also likely leads to many
230  * case database round trips.
231  */
232  if (rootNode != null && rootNode.getChildren().getNodesCount() > 0) {
233  this.rootNode = rootNode;
234  this.getExplorerManager().setRootContext(this.rootNode);
235  setupTable();
236  } else {
237  Node emptyNode = new AbstractNode(Children.LEAF);
238  this.getExplorerManager().setRootContext(emptyNode);
239  outline.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
240  outlineViewListener.listenToVisibilityChanges(false);
241  outlineView.setPropertyColumns();
242  }
243  } finally {
244  this.setCursor(null);
245  }
246  }
247 
253  private void setupTable() {
254  /*
255  * Since we are modifying the columns, we don't want to listen to
256  * added/removed events as un-hide/hide, until the table setup is done.
257  */
258  outlineViewListener.listenToVisibilityChanges(false);
259  /*
260  * OutlineView makes the first column be the result of
261  * node.getDisplayName with the icon. This duplicates our first column,
262  * which is the file name, etc. So, pop that property off the list, but
263  * use its display name as the header for the column so that the header
264  * can change depending on the type of data being displayed.
265  *
266  * NOTE: This assumes that the first property is always the one that
267  * duplicates getDisplayName(). The current implementation does not
268  * allow the first property column to be moved.
269  */
270  List<Node.Property<?>> props = loadColumnOrder();
271  boolean propsExist = props.isEmpty() == false;
272  Node.Property<?> firstProp = null;
273  if (propsExist) {
274  firstProp = props.remove(0);
275  }
276 
277  /*
278  * show the horizontal scroll panel and show all the content & header If
279  * there is only one column (which was removed from props above) Just
280  * let the table resize itself.
281  */
282  outline.setAutoResizeMode((props.isEmpty()) ? JTable.AUTO_RESIZE_ALL_COLUMNS : JTable.AUTO_RESIZE_OFF);
283 
284  assignColumns(props); // assign columns to match the properties
285  if (firstProp != null) {
286  ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(firstProp.getDisplayName());
287  }
288 
289  setColumnWidths();
290 
291  /*
292  * Load column sorting information from preferences file and apply it to
293  * columns.
294  */
295  loadColumnSorting();
296 
297  /*
298  * Save references to columns before we deal with their visibility. This
299  * has to happen after the sorting is applied, because that actually
300  * causes the columns to be recreated. It has to happen before
301  * loadColumnVisibility so we have referenecs to the columns to pass to
302  * setColumnHidden.
303  */
304  populateColumnMap();
305 
306  /*
307  * Load column visibility information from preferences file and apply it
308  * to columns.
309  */
310  loadColumnVisibility();
311 
312  /*
313  * If one of the child nodes of the root node is to be selected, select
314  * it.
315  */
316  SwingUtilities.invokeLater(() -> {
317  if (rootNode instanceof TableFilterNode) {
318  NodeSelectionInfo selectedChildInfo = ((TableFilterNode) rootNode).getChildNodeSelectionInfo();
319  if (null != selectedChildInfo) {
320  Node[] childNodes = rootNode.getChildren().getNodes(true);
321  for (int i = 0; i < childNodes.length; ++i) {
322  Node childNode = childNodes[i];
323  if (selectedChildInfo.matches(childNode)) {
324  try {
325  this.getExplorerManager().setSelectedNodes(new Node[]{childNode});
326  } catch (PropertyVetoException ex) {
327  LOGGER.log(Level.SEVERE, "Failed to select node specified by selected child info", ex);
328  }
329  break;
330  }
331  }
332  ((TableFilterNode) rootNode).setChildNodeSelectionInfo(null);
333  }
334  }
335  });
336 
337  /*
338  * The table setup is done, so any added/removed events can now be
339  * treated as un-hide/hide.
340  */
341  outlineViewListener.listenToVisibilityChanges(true);
342  }
343 
344  /*
345  * Populates the column map for the child OutlineView of this tabular
346  * result viewer with references to the column objects for use when
347  * loading/storing the visibility info.
348  */
349  private void populateColumnMap() {
350  columnMap.clear();
351  TableColumnModel columnModel = outline.getColumnModel();
352  int columnCount = columnModel.getColumnCount();
353  //for each property get a reference to the column object from the column model.
354  for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
355  final String propName = entry.getValue().getName();
356  if (entry.getKey() < columnCount) {
357  final ETableColumn column = (ETableColumn) columnModel.getColumn(entry.getKey());
358  columnMap.put(propName, column);
359  }
360  }
361  }
362 
363  /*
364  * Sets the column widths for the child OutlineView of this tabular results
365  * viewer.
366  */
367  private void setColumnWidths() {
368  if (rootNode.getChildren().getNodesCount() != 0) {
369  final Graphics graphics = outlineView.getGraphics();
370  if (graphics != null) {
371  final FontMetrics metrics = graphics.getFontMetrics();
372 
373  int margin = 4;
374  int padding = 8;
375 
376  for (int column = 0; column < outline.getModel().getColumnCount(); column++) {
377  int firstColumnPadding = (column == 0) ? 32 : 0;
378  int columnWidthLimit = (column == 0) ? 350 : 300;
379  int valuesWidth = 0;
380 
381  // find the maximum width needed to fit the values for the first 100 rows, at most
382  for (int row = 0; row < Math.min(100, outline.getRowCount()); row++) {
383  TableCellRenderer renderer = outline.getCellRenderer(row, column);
384  Component comp = outline.prepareRenderer(renderer, row, column);
385  valuesWidth = Math.max(comp.getPreferredSize().width, valuesWidth);
386  }
387 
388  int headerWidth = metrics.stringWidth(outline.getColumnName(column));
389  valuesWidth += firstColumnPadding; // add extra padding for first column
390 
391  int columnWidth = Math.max(valuesWidth, headerWidth);
392  columnWidth += 2 * margin + padding; // add margin and regular padding
393  columnWidth = Math.min(columnWidth, columnWidthLimit);
394 
395  outline.getColumnModel().getColumn(column).setPreferredWidth(columnWidth);
396  }
397  }
398  } else {
399  // if there's no content just auto resize all columns
400  outline.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
401  }
402  }
403 
404  /*
405  * Sets up the columns for the child OutlineView of this tabular results
406  * viewer with respect to column names and visisbility.
407  */
408  synchronized private void assignColumns(List<Property<?>> props) {
409  String[] propStrings = new String[props.size() * 2];
410  for (int i = 0; i < props.size(); i++) {
411  final Property<?> prop = props.get(i);
412  prop.setValue("ComparableColumnTTV", Boolean.TRUE); //NON-NLS
413  //First property column is sorted initially
414  if (i == 0) {
415  prop.setValue("TreeColumnTTV", Boolean.TRUE); // Identifies special property representing first (tree) column. NON-NLS
416  prop.setValue("SortingColumnTTV", Boolean.TRUE); // TreeTableView should be initially sorted by this property column. NON-NLS
417  }
418  propStrings[2 * i] = prop.getName();
419  propStrings[2 * i + 1] = prop.getDisplayName();
420  }
421  outlineView.setPropertyColumns(propStrings);
422  }
423 
428  private synchronized void storeColumnVisibility() {
429  if (rootNode == null || propertiesMap.isEmpty()) {
430  return;
431  }
432  if (rootNode instanceof TableFilterNode) {
433  TableFilterNode tfn = (TableFilterNode) rootNode;
434  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
435  final ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
436  for (Map.Entry<String, ETableColumn> entry : columnMap.entrySet()) {
437  String columnName = entry.getKey();
438  final String columnHiddenKey = ResultViewerPersistence.getColumnHiddenKey(tfn, columnName);
439  final TableColumn column = entry.getValue();
440  boolean columnHidden = columnModel.isColumnHidden(column);
441  if (columnHidden) {
442  preferences.putBoolean(columnHiddenKey, true);
443  } else {
444  preferences.remove(columnHiddenKey);
445  }
446  }
447  }
448  }
449 
454  private synchronized void storeColumnOrder() {
455  if (rootNode == null || propertiesMap.isEmpty()) {
456  return;
457  }
458  if (rootNode instanceof TableFilterNode) {
459  TableFilterNode tfn = (TableFilterNode) rootNode;
460  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
461  // Store the current order of the columns into settings
462  for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
463  preferences.putInt(ResultViewerPersistence.getColumnPositionKey(tfn, entry.getValue().getName()), entry.getKey());
464  }
465  }
466  }
467 
471  private synchronized void storeColumnSorting() {
472  if (rootNode == null || propertiesMap.isEmpty()) {
473  return;
474  }
475  if (rootNode instanceof TableFilterNode) {
476  final TableFilterNode tfn = ((TableFilterNode) rootNode);
477  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
478  ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
479  for (Map.Entry<String, ETableColumn> entry : columnMap.entrySet()) {
480  ETableColumn etc = entry.getValue();
481  String columnName = entry.getKey();
482  //store sort rank and order
483  final String columnSortOrderKey = ResultViewerPersistence.getColumnSortOrderKey(tfn, columnName);
484  final String columnSortRankKey = ResultViewerPersistence.getColumnSortRankKey(tfn, columnName);
485  if (etc.isSorted() && (columnModel.isColumnHidden(etc) == false)) {
486  preferences.putBoolean(columnSortOrderKey, etc.isAscending());
487  preferences.putInt(columnSortRankKey, etc.getSortRank());
488  } else {
489  columnModel.setColumnSorted(etc, true, 0);
490  preferences.remove(columnSortOrderKey);
491  preferences.remove(columnSortRankKey);
492  }
493  }
494  }
495  }
496 
503  private synchronized void loadColumnSorting() {
504  if (rootNode == null || propertiesMap.isEmpty()) {
505  return;
506  }
507  if (rootNode instanceof TableFilterNode) {
508  final TableFilterNode tfn = (TableFilterNode) rootNode;
509  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
510  //organize property sorting information, sorted by rank
511  TreeSet<ColumnSortInfo> sortInfos = new TreeSet<>(Comparator.comparing(ColumnSortInfo::getRank));
512  propertiesMap.entrySet().stream().forEach(entry -> {
513  final String propName = entry.getValue().getName();
514  //if the sort rank is undefined, it will be defaulted to 0 => unsorted.
515  Integer sortRank = preferences.getInt(ResultViewerPersistence.getColumnSortRankKey(tfn, propName), 0);
516  //default to true => ascending
517  Boolean sortOrder = preferences.getBoolean(ResultViewerPersistence.getColumnSortOrderKey(tfn, propName), true);
518  sortInfos.add(new ColumnSortInfo(entry.getKey(), sortRank, sortOrder));
519  });
520  //apply sort information in rank order.
521  sortInfos.forEach(sortInfo -> outline.setColumnSorted(sortInfo.modelIndex, sortInfo.order, sortInfo.rank));
522  }
523  }
524 
529  private synchronized void loadColumnVisibility() {
530  if (rootNode == null || propertiesMap.isEmpty()) {
531  return;
532  }
533  if (rootNode instanceof TableFilterNode) {
534  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
535  final TableFilterNode tfn = ((TableFilterNode) rootNode);
536  ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
537  for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
538  final String propName = entry.getValue().getName();
539  boolean hidden = preferences.getBoolean(ResultViewerPersistence.getColumnHiddenKey(tfn, propName), false);
540  final TableColumn column = columnMap.get(propName);
541  columnModel.setColumnHidden(column, hidden);
542  }
543  }
544  }
545 
554  private synchronized List<Node.Property<?>> loadColumnOrder() {
555 
556  List<Property<?>> props = ResultViewerPersistence.getAllChildProperties(rootNode, 100);
557 
558  // If node is not table filter node, use default order for columns
559  if (!(rootNode instanceof TableFilterNode)) {
560  return props;
561  }
562 
563  final TableFilterNode tfn = ((TableFilterNode) rootNode);
564  propertiesMap.clear();
565 
566  /*
567  * We load column index values into the properties map. If a property's
568  * index is outside the range of the number of properties or the index
569  * has already appeared as the position of another property, we put that
570  * property at the end.
571  */
572  int offset = props.size();
573  boolean noPreviousSettings = true;
574 
575  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
576 
577  for (Property<?> prop : props) {
578  Integer value = preferences.getInt(ResultViewerPersistence.getColumnPositionKey(tfn, prop.getName()), -1);
579  if (value >= 0 && value < offset && !propertiesMap.containsKey(value)) {
580  propertiesMap.put(value, prop);
581  noPreviousSettings = false;
582  } else {
583  propertiesMap.put(offset, prop);
584  offset++;
585  }
586  }
587 
588  // If none of the properties had previous settings, we should decrement
589  // each value by the number of properties to make the values 0-indexed.
590  if (noPreviousSettings) {
591  ArrayList<Integer> keys = new ArrayList<>(propertiesMap.keySet());
592  for (int key : keys) {
593  propertiesMap.put(key - props.size(), propertiesMap.remove(key));
594  }
595  }
596 
597  return new ArrayList<>(propertiesMap.values());
598  }
599 
604  @Override
605  public void clearComponent() {
606  this.outlineView.removeAll();
607  this.outlineView = null;
608  super.clearComponent();
609  }
610 
614  static private final class ColumnSortInfo {
615 
616  private final int modelIndex;
617  private final int rank;
618  private final boolean order;
619 
620  private ColumnSortInfo(int modelIndex, int rank, boolean order) {
621  this.modelIndex = modelIndex;
622  this.rank = rank;
623  this.order = order;
624  }
625 
626  private int getRank() {
627  return rank;
628  }
629  }
630 
635  private class TableListener extends MouseAdapter implements TableColumnModelListener {
636 
637  // When a column in the table is moved, these two variables keep track of where
638  // the column started and where it ended up.
639  private int startColumnIndex = -1;
640  private int endColumnIndex = -1;
641  private boolean listenToVisibilitEvents;
642 
643  @Override
644  public void columnMoved(TableColumnModelEvent e) {
645  int fromIndex = e.getFromIndex();
646  int toIndex = e.getToIndex();
647  if (fromIndex == toIndex) {
648  return;
649  }
650 
651  /*
652  * Because a column may be dragged to several different positions
653  * before the mouse is released (thus causing multiple
654  * TableColumnModelEvents to be fired), we want to keep track of the
655  * starting column index in this potential series of movements.
656  * Therefore we only keep track of the original fromIndex in
657  * startColumnIndex, but we always update endColumnIndex to know the
658  * final position of the moved column. See the MouseListener
659  * mouseReleased method.
660  */
661  if (startColumnIndex == -1) {
662  startColumnIndex = fromIndex;
663  }
664  endColumnIndex = toIndex;
665 
666  // This list contains the keys of propertiesMap in order
667  ArrayList<Integer> indicesList = new ArrayList<>(propertiesMap.keySet());
668  int leftIndex = Math.min(fromIndex, toIndex);
669  int rightIndex = Math.max(fromIndex, toIndex);
670  // Now we can copy the range of keys that have been affected by
671  // the column movement
672  List<Integer> range = indicesList.subList(leftIndex, rightIndex + 1);
673  int rangeSize = range.size();
674 
675  if (fromIndex < toIndex) {
676  // column moved right, shift all properties left, put in moved
677  // property at the rightmost index
678  Property<?> movedProp = propertiesMap.get(range.get(0));
679  for (int i = 0; i < rangeSize - 1; i++) {
680  propertiesMap.put(range.get(i), propertiesMap.get(range.get(i + 1)));
681  }
682  propertiesMap.put(range.get(rangeSize - 1), movedProp);
683  } else {
684  // column moved left, shift all properties right, put in moved
685  // property at the leftmost index
686  Property<?> movedProp = propertiesMap.get(range.get(rangeSize - 1));
687  for (int i = rangeSize - 1; i > 0; i--) {
688  propertiesMap.put(range.get(i), propertiesMap.get(range.get(i - 1)));
689  }
690  propertiesMap.put(range.get(0), movedProp);
691  }
692 
693  storeColumnOrder();
694  }
695 
696  @Override
697  public void mouseReleased(MouseEvent e) {
698  /*
699  * If the startColumnIndex is not -1 (which is the reset value),
700  * that means columns have been moved around. We then check to see
701  * if either the starting or end position is 0 (the first column),
702  * and then swap them back if that is the case because we don't want
703  * to allow movement of the first column. We then reset
704  * startColumnIndex to -1, the reset value. We check if
705  * startColumnIndex is at reset or not because it is possible for
706  * the mouse to be released and a MouseEvent to be fired without
707  * having moved any columns.
708  */
709  if (startColumnIndex != -1 && (startColumnIndex == 0 || endColumnIndex == 0)) {
710  outline.moveColumn(endColumnIndex, startColumnIndex);
711  }
712  startColumnIndex = -1;
713  }
714 
715  @Override
716  public void mouseClicked(MouseEvent e) {
717  //the user clicked a column header
718  storeColumnSorting();
719  }
720 
721  @Override
722  public void columnAdded(TableColumnModelEvent e) {
723  columnAddedOrRemoved();
724  }
725 
726  @Override
727  public void columnRemoved(TableColumnModelEvent e) {
728  columnAddedOrRemoved();
729  }
730 
736  private void columnAddedOrRemoved() {
737  if (listenToVisibilitEvents) {
738  SwingUtilities.invokeLater(DataResultViewerTable.this::storeColumnVisibility);
739 
740  }
741  }
742 
743  @Override
744  public void columnMarginChanged(ChangeEvent e) {
745  }
746 
747  @Override
748  public void columnSelectionChanged(ListSelectionEvent e) {
749  }
750 
760  private void listenToVisibilityChanges(boolean b) {
761  this.listenToVisibilitEvents = b;
762  }
763  }
764 
770  private class ColorTagCustomRenderer extends DefaultOutlineCellRenderer {
771 
772  private static final long serialVersionUID = 1L;
773 
774  @Override
775  public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {
776 
777  Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
778  // only override the color if a node is not selected
779  if (rootNode != null && !isSelected) {
780  Node node = rootNode.getChildren().getNodeAt(table.convertRowIndexToModel(row));
781  boolean tagFound = false;
782  if (node != null) {
783  Node.PropertySet[] propSets = node.getPropertySets();
784  if (propSets.length != 0) {
785  // currently, a node has only one property set, named Sheet.PROPERTIES ("properties")
786  Node.Property<?>[] props = propSets[0].getProperties();
787  for (Property<?> prop : props) {
788  if ("Tags".equals(prop.getName())) {//NON-NLS
789  try {
790  tagFound = !prop.getValue().equals("");
791  } catch (IllegalAccessException | InvocationTargetException ignore) {
792  }
793  break;
794  }
795  }
796  }
797  }
798  //if the node does have associated tags, set its background color
799  if (tagFound) {
800  component.setBackground(TAGGED_ROW_COLOR);
801  }
802  }
803  return component;
804  }
805  }
806 
812  @SuppressWarnings("unchecked")
813  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
814  private void initComponents() {
815 
816  outlineView = new OutlineView(DataResultViewerTable.FIRST_COLUMN_LABEL);
817 
818  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
819  this.setLayout(layout);
820  layout.setHorizontalGroup(
821  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
822  .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 691, Short.MAX_VALUE)
823  );
824  layout.setVerticalGroup(
825  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
826  .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 366, Short.MAX_VALUE)
827  );
828  }// </editor-fold>//GEN-END:initComponents
829  // Variables declaration - do not modify//GEN-BEGIN:variables
830  private org.openide.explorer.view.OutlineView outlineView;
831  // End of variables declaration//GEN-END:variables
832 
833 }
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: Mon May 7 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.