Autopsy  4.4.1
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(200, 210, 220);
92  private final Map<Integer, Property<?>> propertiesMap = new TreeMap<>();
93 
99  private final Map<String, ETableColumn> columnMap = new HashMap<>();
100 
101  private Node currentRoot;
102 
103  /*
104  * Convience reference to internal Outline.
105  */
106  private Outline outline;
107 
112 
119  public DataResultViewerTable(ExplorerManager explorerManager) {
120  super(explorerManager);
121  initialize();
122  }
123 
129  initialize();
130  }
131 
132  private void initialize() {
133  initComponents();
134 
135  outlineView.setAllowedDragActions(DnDConstants.ACTION_NONE);
136 
137  outline = outlineView.getOutline();
138  outline.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
139  outline.setRootVisible(false); // don't show the root node
140  outline.setDragEnabled(false);
141  outline.setDefaultRenderer(Object.class, new ColorTagCustomRenderer());
142 
143  // add a listener so that when columns are moved, the new order is stored
144  tableListener = new TableListener();
145  outline.getColumnModel().addColumnModelListener(tableListener);
146  // the listener also moves columns back if user tries to move the first column out of place
147  outline.getTableHeader().addMouseListener(tableListener);
148  }
149 
155  @Override
156  public void expandNode(Node n) {
157  super.expandNode(n);
158 
159  outlineView.expandNode(n);
160  }
161 
167  @SuppressWarnings("unchecked")
168  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
169  private void initComponents() {
170 
172 
173  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
174  this.setLayout(layout);
175  layout.setHorizontalGroup(
176  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
177  .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 691, Short.MAX_VALUE)
178  );
179  layout.setVerticalGroup(
180  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
181  .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 366, Short.MAX_VALUE)
182  );
183  }// </editor-fold>//GEN-END:initComponents
184  // Variables declaration - do not modify//GEN-BEGIN:variables
185  private org.openide.explorer.view.OutlineView outlineView;
186  // End of variables declaration//GEN-END:variables
187 
188  @Override
189  public boolean isSupported(Node selectedNode) {
190  return true;
191  }
192 
193  @Override
195  public void setNode(Node selectedNode) {
196 
197  /*
198  * The quick filter must be reset because when determining column width,
199  * ETable.getRowCount is called, and the documentation states that quick
200  * filters must be unset for the method to work "If the quick-filter is
201  * applied the number of rows do not match the number of rows in the
202  * model."
203  */
204  outline.unsetQuickFilter();
205  // change the cursor to "waiting cursor" for this operation
206  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
207  try {
208  boolean hasChildren = false;
209  if (selectedNode != null) {
210  // @@@ This just did a DB round trip to get the count and the results were not saved...
211  hasChildren = selectedNode.getChildren().getNodesCount() > 0;
212  }
213 
214  if (hasChildren) {
215  currentRoot = selectedNode;
216  em.setRootContext(currentRoot);
217  setupTable();
218  } else {
219  Node emptyNode = new AbstractNode(Children.LEAF);
220  em.setRootContext(emptyNode); // make empty node
221  outline.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
222 
223  /*
224  * Since we are modifying the columns, we don't want to listen
225  * to added/removed events as un-hide/hide.
226  */
227  tableListener.listenToVisibilityChanges(false);
228  outlineView.setPropertyColumns(); // set the empty property header
229  }
230  } finally {
231  this.setCursor(null);
232  }
233  }
234 
239  private void setupTable() {
240  /*
241  * Since we are modifying the columns, we don't want to listen to
242  * added/removed events as un-hide/hide, until the table setup is done.
243  */
244  tableListener.listenToVisibilityChanges(false);
245 
257  List<Node.Property<?>> props = loadColumnOrder();
258  boolean propsExist = props.isEmpty() == false;
259  Node.Property<?> firstProp = null;
260  if (propsExist) {
261  firstProp = props.remove(0);
262  }
263 
264  /*
265  * show the horizontal scroll panel and show all the content & header If
266  * there is only one column (which was removed from props above) Just
267  * let the table resize itself.
268  */
269  outline.setAutoResizeMode((props.isEmpty()) ? JTable.AUTO_RESIZE_ALL_COLUMNS : JTable.AUTO_RESIZE_OFF);
270 
271  assignColumns(props); // assign columns to match the properties
272  if (firstProp != null) {
273  ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(firstProp.getDisplayName());
274  }
275 
276  setColumnWidths();
277 
278  //Load column sorting information from preferences file and apply it to columns.
280 
281  /*
282  * Save references to columns before we deal with their visibility. This
283  * has to happen after the sorting is applied, because that actually
284  * causes the columns to be recreated. It has to happen before
285  * loadColumnVisibility so we have referenecs to the columns to pass to
286  * setColumnHidden.
287  */
289 
290  //Load column visibility information from preferences file and apply it to columns.
292 
293  /*
294  * If one of the child nodes of the root node is to be selected, select
295  * it.
296  */
297  SwingUtilities.invokeLater(() -> {
298  if (currentRoot instanceof TableFilterNode) {
299  NodeSelectionInfo selectedChildInfo = ((TableFilterNode) currentRoot).getChildNodeSelectionInfo();
300  if (null != selectedChildInfo) {
301  Node[] childNodes = currentRoot.getChildren().getNodes(true);
302  for (int i = 0; i < childNodes.length; ++i) {
303  Node childNode = childNodes[i];
304  if (selectedChildInfo.matches(childNode)) {
305  try {
306  em.setSelectedNodes(new Node[]{childNode});
307  } catch (PropertyVetoException ex) {
308  logger.log(Level.SEVERE, "Failed to select node specified by selected child info", ex);
309  }
310  break;
311  }
312  }
313  ((TableFilterNode) currentRoot).setChildNodeSelectionInfo(null);
314  }
315  }
316  });
317 
318  //the table setup is done, so any added/removed events can now be treated as un-hide/hide.
319  tableListener.listenToVisibilityChanges(true);
320  }
321 
322  /*
323  * Populate the map with references to the column objects for use when
324  * loading/storing the visibility info.
325  */
326  private void populateColumnMap() {
327  columnMap.clear();
328  TableColumnModel columnModel = outline.getColumnModel();
329  int columnCount = columnModel.getColumnCount();
330  //for each property get a reference to the column object from the column model.
331  for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
332  final String propName = entry.getValue().getName();
333  if (entry.getKey() < columnCount) {
334  final ETableColumn column = (ETableColumn) columnModel.getColumn(entry.getKey());
335  columnMap.put(propName, column);
336  }
337  }
338  }
339 
340  private void setColumnWidths() {
341  if (currentRoot.getChildren().getNodesCount() != 0) {
342  final Graphics graphics = outlineView.getGraphics();
343  if (graphics != null) {
344  final FontMetrics metrics = graphics.getFontMetrics();
345 
346  int margin = 4;
347  int padding = 8;
348 
349  for (int column = 0; column < outline.getModel().getColumnCount(); column++) {
350  int firstColumnPadding = (column == 0) ? 32 : 0;
351  int columnWidthLimit = (column == 0) ? 350 : 300;
352  int valuesWidth = 0;
353 
354  // find the maximum width needed to fit the values for the first 100 rows, at most
355  for (int row = 0; row < Math.min(100, outline.getRowCount()); row++) {
356  TableCellRenderer renderer = outline.getCellRenderer(row, column);
357  Component comp = outline.prepareRenderer(renderer, row, column);
358  valuesWidth = Math.max(comp.getPreferredSize().width, valuesWidth);
359  }
360 
361  int headerWidth = metrics.stringWidth(outline.getColumnName(column));
362  valuesWidth += firstColumnPadding; // add extra padding for first column
363 
364  int columnWidth = Math.max(valuesWidth, headerWidth);
365  columnWidth += 2 * margin + padding; // add margin and regular padding
366  columnWidth = Math.min(columnWidth, columnWidthLimit);
367 
368  outline.getColumnModel().getColumn(column).setPreferredWidth(columnWidth);
369  }
370  }
371  } else {
372  // if there's no content just auto resize all columns
373  outline.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
374  }
375  }
376 
377  synchronized private void assignColumns(List<Property<?>> props) {
378  // Get the columns setup with respect to names and sortability
379  String[] propStrings = new String[props.size() * 2];
380  for (int i = 0; i < props.size(); i++) {
381  final Property<?> prop = props.get(i);
382  prop.setValue("ComparableColumnTTV", Boolean.TRUE); //NON-NLS
383  //First property column is sorted initially
384  if (i == 0) {
385  prop.setValue("TreeColumnTTV", Boolean.TRUE); // Identifies special property representing first (tree) column. NON-NLS
386  prop.setValue("SortingColumnTTV", Boolean.TRUE); // TreeTableView should be initially sorted by this property column. NON-NLS
387  }
388  propStrings[2 * i] = prop.getName();
389  propStrings[2 * i + 1] = prop.getDisplayName();
390  }
391 
392  outlineView.setPropertyColumns(propStrings);
393  }
394 
398  private synchronized void storeColumnVisibility() {
399  if (currentRoot == null || propertiesMap.isEmpty()) {
400  return;
401  }
402  if (currentRoot instanceof TableFilterNode) {
403  TableFilterNode tfn = (TableFilterNode) currentRoot;
404  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
405  final ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
406 
407  //store hidden state
408  for (Map.Entry<String, ETableColumn> entry : columnMap.entrySet()) {
409 
410  String columnName = entry.getKey();
411  final String columnHiddenKey = ResultViewerPersistence.getColumnHiddenKey(tfn, columnName);
412  final TableColumn column = entry.getValue();
413 
414  boolean columnHidden = columnModel.isColumnHidden(column);
415  if (columnHidden) {
416  preferences.putBoolean(columnHiddenKey, true);
417  } else {
418  preferences.remove(columnHiddenKey);
419  }
420  }
421  }
422  }
423 
427  private synchronized void storeColumnOrder() {
428  if (currentRoot == null || propertiesMap.isEmpty()) {
429  return;
430  }
431  if (currentRoot instanceof TableFilterNode) {
432  TableFilterNode tfn = (TableFilterNode) currentRoot;
433  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
434 
435  // Store the current order of the columns into settings
436  for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
437  preferences.putInt(ResultViewerPersistence.getColumnPositionKey(tfn, entry.getValue().getName()), entry.getKey());
438  }
439  }
440  }
441 
445  private synchronized void storeColumnSorting() {
446  if (currentRoot == null || propertiesMap.isEmpty()) {
447  return;
448  }
449  if (currentRoot instanceof TableFilterNode) {
450  final TableFilterNode tfn = ((TableFilterNode) currentRoot);
451  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
452  ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
453  for (Map.Entry<String, ETableColumn> entry : columnMap.entrySet()) {
454  ETableColumn etc = entry.getValue();
455  String columnName = entry.getKey();
456 
457  //store sort rank and order
458  final String columnSortOrderKey = ResultViewerPersistence.getColumnSortOrderKey(tfn, columnName);
459  final String columnSortRankKey = ResultViewerPersistence.getColumnSortRankKey(tfn, columnName);
460  if (etc.isSorted() && (columnModel.isColumnHidden(etc) == false)) {
461  preferences.putBoolean(columnSortOrderKey, etc.isAscending());
462  preferences.putInt(columnSortRankKey, etc.getSortRank());
463  } else {
464  columnModel.setColumnSorted(etc, true, 0);
465  preferences.remove(columnSortOrderKey);
466  preferences.remove(columnSortRankKey);
467  }
468  }
469  }
470  }
471 
478  private synchronized void loadColumnSorting() {
479  if (currentRoot == null || propertiesMap.isEmpty()) {
480  return;
481  }
482 
483  if (currentRoot instanceof TableFilterNode) {
484  final TableFilterNode tfn = (TableFilterNode) currentRoot;
485 
486  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
487  //organize property sorting information, sorted by rank
488  TreeSet<ColumnSortInfo> sortInfos = new TreeSet<>(Comparator.comparing(ColumnSortInfo::getRank));
489  propertiesMap.entrySet().stream().forEach(entry -> {
490  final String propName = entry.getValue().getName();
491  //if the sort rank is undefined, it will be defaulted to 0 => unsorted.
492 
493  Integer sortRank = preferences.getInt(ResultViewerPersistence.getColumnSortRankKey(tfn, propName), 0);
494  //default to true => ascending
495  Boolean sortOrder = preferences.getBoolean(ResultViewerPersistence.getColumnSortOrderKey(tfn, propName), true);
496 
497  sortInfos.add(new ColumnSortInfo(entry.getKey(), sortRank, sortOrder));
498  });
499 
500  //apply sort information in rank order.
501  sortInfos.forEach(sortInfo -> outline.setColumnSorted(sortInfo.modelIndex, sortInfo.order, sortInfo.rank));
502  }
503  }
504 
505  private synchronized void loadColumnVisibility() {
506  if (currentRoot == null || propertiesMap.isEmpty()) {
507  return;
508  }
509 
510  if (currentRoot instanceof TableFilterNode) {
511 
512  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
513 
514  final TableFilterNode tfn = ((TableFilterNode) currentRoot);
515  ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
516  for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
517  final String propName = entry.getValue().getName();
518  boolean hidden = preferences.getBoolean(ResultViewerPersistence.getColumnHiddenKey(tfn, propName), false);
519  final TableColumn column = columnMap.get(propName);
520  columnModel.setColumnHidden(column, hidden);
521  }
522  }
523  }
524 
533  private synchronized List<Node.Property<?>> loadColumnOrder() {
534 
535  List<Property<?>> props = ResultViewerPersistence.getAllChildProperties(currentRoot, 100);
536 
537  // If node is not table filter node, use default order for columns
538  if (!(currentRoot instanceof TableFilterNode)) {
539  return props;
540  }
541 
542  final TableFilterNode tfn = ((TableFilterNode) currentRoot);
543  propertiesMap.clear();
544 
545  /*
546  * We load column index values into the properties map. If a property's
547  * index is outside the range of the number of properties or the index
548  * has already appeared as the position of another property, we put that
549  * property at the end.
550  */
551  int offset = props.size();
552  boolean noPreviousSettings = true;
553 
554  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
555 
556  for (Property<?> prop : props) {
557  Integer value = preferences.getInt(ResultViewerPersistence.getColumnPositionKey(tfn, prop.getName()), -1);
558  if (value >= 0 && value < offset && !propertiesMap.containsKey(value)) {
559  propertiesMap.put(value, prop);
560  noPreviousSettings = false;
561  } else {
562  propertiesMap.put(offset, prop);
563  offset++;
564  }
565  }
566 
567  // If none of the properties had previous settings, we should decrement
568  // each value by the number of properties to make the values 0-indexed.
569  if (noPreviousSettings) {
570  ArrayList<Integer> keys = new ArrayList<>(propertiesMap.keySet());
571  for (int key : keys) {
572  propertiesMap.put(key - props.size(), propertiesMap.remove(key));
573  }
574  }
575 
576  return new ArrayList<>(propertiesMap.values());
577  }
578 
579  @Override
580  @NbBundle.Messages("DataResultViewerTable.title=Table")
581  public String getTitle() {
582  return Bundle.DataResultViewerTable_title();
583  }
584 
585  @Override
587  return new DataResultViewerTable();
588  }
589 
590  @Override
591  public void clearComponent() {
592  this.outlineView.removeAll();
593  this.outlineView = null;
594 
595  super.clearComponent();
596 
597  }
598 
602  static private final class ColumnSortInfo {
603 
604  private final int modelIndex;
605  private final int rank;
606  private final boolean order;
607 
608  private ColumnSortInfo(int modelIndex, int rank, boolean order) {
609  this.modelIndex = modelIndex;
610  this.rank = rank;
611  this.order = order;
612  }
613 
614  private int getRank() {
615  return rank;
616  }
617  }
618 
623  private class TableListener extends MouseAdapter implements TableColumnModelListener {
624 
625  // When a column in the table is moved, these two variables keep track of where
626  // the column started and where it ended up.
627  private int startColumnIndex = -1;
628  private int endColumnIndex = -1;
629  private boolean listenToVisibilitEvents;
630 
631  @Override
632  public void columnMoved(TableColumnModelEvent e) {
633  int fromIndex = e.getFromIndex();
634  int toIndex = e.getToIndex();
635  if (fromIndex == toIndex) {
636  return;
637  }
638 
639  /*
640  * Because a column may be dragged to several different positions
641  * before the mouse is released (thus causing multiple
642  * TableColumnModelEvents to be fired), we want to keep track of the
643  * starting column index in this potential series of movements.
644  * Therefore we only keep track of the original fromIndex in
645  * startColumnIndex, but we always update endColumnIndex to know the
646  * final position of the moved column. See the MouseListener
647  * mouseReleased method.
648  */
649  if (startColumnIndex == -1) {
650  startColumnIndex = fromIndex;
651  }
652  endColumnIndex = toIndex;
653 
654  // This list contains the keys of propertiesMap in order
655  ArrayList<Integer> indicesList = new ArrayList<>(propertiesMap.keySet());
656  int leftIndex = Math.min(fromIndex, toIndex);
657  int rightIndex = Math.max(fromIndex, toIndex);
658  // Now we can copy the range of keys that have been affected by
659  // the column movement
660  List<Integer> range = indicesList.subList(leftIndex, rightIndex + 1);
661  int rangeSize = range.size();
662 
663  if (fromIndex < toIndex) {
664  // column moved right, shift all properties left, put in moved
665  // property at the rightmost index
666  Property<?> movedProp = propertiesMap.get(range.get(0));
667  for (int i = 0; i < rangeSize - 1; i++) {
668  propertiesMap.put(range.get(i), propertiesMap.get(range.get(i + 1)));
669  }
670  propertiesMap.put(range.get(rangeSize - 1), movedProp);
671  } else {
672  // column moved left, shift all properties right, put in moved
673  // property at the leftmost index
674  Property<?> movedProp = propertiesMap.get(range.get(rangeSize - 1));
675  for (int i = rangeSize - 1; i > 0; i--) {
676  propertiesMap.put(range.get(i), propertiesMap.get(range.get(i - 1)));
677  }
678  propertiesMap.put(range.get(0), movedProp);
679  }
680 
682  }
683 
684  @Override
685  public void mouseReleased(MouseEvent e) {
686  /*
687  * If the startColumnIndex is not -1 (which is the reset value),
688  * that means columns have been moved around. We then check to see
689  * if either the starting or end position is 0 (the first column),
690  * and then swap them back if that is the case because we don't want
691  * to allow movement of the first column. We then reset
692  * startColumnIndex to -1, the reset value. We check if
693  * startColumnIndex is at reset or not because it is possible for
694  * the mouse to be released and a MouseEvent to be fired without
695  * having moved any columns.
696  */
697  if (startColumnIndex != -1 && (startColumnIndex == 0 || endColumnIndex == 0)) {
698  outline.moveColumn(endColumnIndex, startColumnIndex);
699  }
700  startColumnIndex = -1;
701  }
702 
703  @Override
704  public void mouseClicked(MouseEvent e) {
705  //the user clicked a column header
707  }
708 
709  @Override
710  public void columnAdded(TableColumnModelEvent e) {
712  }
713 
714  @Override
715  public void columnRemoved(TableColumnModelEvent e) {
717  }
718 
724  private void columnAddedOrRemoved() {
725  if (listenToVisibilitEvents) {
726  SwingUtilities.invokeLater(DataResultViewerTable.this::storeColumnVisibility);
727 
728  }
729  }
730 
731  @Override
732  public void columnMarginChanged(ChangeEvent e) {
733  }
734 
735  @Override
736  public void columnSelectionChanged(ListSelectionEvent e) {
737  }
738 
748  private void listenToVisibilityChanges(boolean b) {
749  this.listenToVisibilitEvents = b;
750  }
751  }
752 
758  private class ColorTagCustomRenderer extends DefaultOutlineCellRenderer {
759 
760  private static final long serialVersionUID = 1L;
761 
762  @Override
763  public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {
764 
765  Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
766  // only override the color if a node is not selected
767  if (!isSelected) {
768  Node node = currentRoot.getChildren().getNodeAt(table.convertRowIndexToModel(row));
769  boolean tagFound = false;
770  if (node != null) {
771  Node.PropertySet[] propSets = node.getPropertySets();
772  if (propSets.length != 0) {
773  // currently, a node has only one property set, named Sheet.PROPERTIES ("properties")
774  Node.Property<?>[] props = propSets[0].getProperties();
775  for (Property<?> prop : props) {
776  if ("Tags".equals(prop.getName())) {//NON-NLS
777  try {
778  tagFound = !prop.getValue().equals("");
779  } catch (IllegalAccessException | InvocationTargetException ignore) {
780  }
781  break;
782  }
783  }
784  }
785  }
786  //if the node does have associated tags, set its background color
787  if (tagFound) {
788  component.setBackground(TAGGED_COLOR);
789  }
790  }
791  return component;
792  }
793  }
794 }
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:161

Copyright © 2012-2016 Basis Technology. Generated on: Fri Sep 29 2017
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.