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