// --- Constructors ----------------------------------------------------------
  public ReferencesBrowserControllerUI(ReferencesBrowserController referencesBrowserController) {
    super(Bundle.ReferencesBrowserControllerUI_ViewTitleReferences(), ICON_FIELDS, true);

    this.referencesBrowserController = referencesBrowserController;

    retainedSizeSupported =
        referencesBrowserController
                .getReferencesControllerHandler()
                .getHeapFragmentWalker()
                .getRetainedSizesStatus()
            != HeapFragmentWalker.RETAINED_SIZES_UNSUPPORTED;
    columnCount = retainedSizeSupported ? 6 : 5;

    realFieldsListTableModel = new FieldsListTreeTableModel();
    fieldsListTableModel = new ExtendedTreeTableModel(realFieldsListTableModel);

    addHierarchyListener(
        new HierarchyListener() {
          public void hierarchyChanged(HierarchyEvent e) {
            if (((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0)
                && isShowing()
                && needsUpdate) {
              update();
            }
          }
        });

    initColumnsData();
    initData();
    initComponents();

    referencesBrowserController
        .getReferencesControllerHandler()
        .getHeapFragmentWalker()
        .addStateListener(
            new HeapFragmentWalker.StateListener() {
              public void stateChanged(StateEvent e) {
                if (e.getRetainedSizesStatus() == HeapFragmentWalker.RETAINED_SIZES_COMPUTED
                    && e.isMasterChange()) {
                  SwingUtilities.invokeLater(
                      new Runnable() {
                        public void run() {
                          fieldsListTableModel.setRealColumnVisibility(5, true);
                          fieldsListTable.createDefaultColumnsFromModel();
                          fieldsListTable.updateTreeTableHeader();
                          setColumnsData();
                        }
                      });
                }
              }
            });
  }
  private JButton createHeaderPopupCornerButton(final JPopupMenu headerPopup) {
    final JButton cornerButton = new JButton(Icons.getIcon(GeneralIcons.HIDE_COLUMN));
    cornerButton.setToolTipText(Bundle.ReferencesBrowserControllerUI_ShowHideColumnsString());
    cornerButton.setDefaultCapable(false);

    if (UIUtils.isWindowsClassicLookAndFeel()) {
      cornerButton.setMargin(new Insets(0, 0, 2, 2));
    } else if (UIUtils.isWindowsXPLookAndFeel()) {
      cornerButton.setMargin(new Insets(0, 0, 0, 1));
    } else if (UIUtils.isMetalLookAndFeel()) {
      cornerButton.setMargin(new Insets(0, 0, 2, 1));
    }

    cornerButton.addKeyListener(
        new KeyAdapter() {
          public void keyPressed(final KeyEvent evt) {
            if (evt.getKeyCode() == KeyEvent.VK_SPACE) {
              showColumnSelectionPopup(headerPopup, cornerButton);
            }
          }
        });

    cornerButton.addMouseListener(
        new MouseAdapter() {
          public void mousePressed(MouseEvent mouseEvent) {
            if (headerPopup.isVisible()) {
              internalCornerButtonClick = true;
              cornerButton.getModel().setArmed(false);
            } else {
              internalCornerButtonClick = false;

              if (mouseEvent.getModifiers() == InputEvent.BUTTON3_MASK) {
                showColumnSelectionPopup(headerPopup, cornerButton);
              }
            }
          }

          public void mouseClicked(MouseEvent mouseEvent) {
            if ((mouseEvent.getModifiers() == InputEvent.BUTTON1_MASK)
                && (!internalCornerButtonClick)) {
              showColumnSelectionPopup(headerPopup, cornerButton);
            }
          }
        });

    return cornerButton;
  }
  private void initComponents() {
    treeCellRenderer.setLeafIcon(null);
    treeCellRenderer.setClosedIcon(null);
    treeCellRenderer.setOpenIcon(null);

    fieldsListTableModel.setRealColumnVisibility(2, false);
    fieldsListTableModel.setRealColumnVisibility(4, false);

    if (retainedSizeSupported)
      fieldsListTableModel.setRealColumnVisibility(
          5,
          referencesBrowserController
                  .getReferencesControllerHandler()
                  .getHeapFragmentWalker()
                  .getRetainedSizesStatus()
              == HeapFragmentWalker.RETAINED_SIZES_COMPUTED);

    fieldsListTable =
        new JTreeTable(fieldsListTableModel) {
          public void doLayout() {
            int columnsWidthsSum = 0;
            int realFirstColumn = -1;

            TableColumnModel colModel = getColumnModel();

            for (int i = 0; i < fieldsListTableModel.getColumnCount(); i++) {
              if (fieldsListTableModel.getRealColumn(i) == 0) {
                realFirstColumn = i;
              } else {
                columnsWidthsSum += colModel.getColumn(i).getPreferredWidth();
              }
            }

            if (realFirstColumn != -1) {
              colModel.getColumn(realFirstColumn).setPreferredWidth(getWidth() - columnsWidthsSum);
            }

            super.doLayout();
          }
        };
    fieldsListTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    fieldsListTable.setGridColor(UIConstants.TABLE_VERTICAL_GRID_COLOR);
    fieldsListTable.setSelectionBackground(UIConstants.TABLE_SELECTION_BACKGROUND_COLOR);
    fieldsListTable.setSelectionForeground(UIConstants.TABLE_SELECTION_FOREGROUND_COLOR);
    fieldsListTable.setShowHorizontalLines(UIConstants.SHOW_TABLE_HORIZONTAL_GRID);
    fieldsListTable.setShowVerticalLines(UIConstants.SHOW_TABLE_VERTICAL_GRID);
    fieldsListTable.setRowMargin(UIConstants.TABLE_ROW_MARGIN);
    fieldsListTable.setRowHeight(UIUtils.getDefaultRowHeight() + 2);
    fieldsListTable.getTree().setLargeModel(true);
    fieldsListTable.getTree().setToggleClickCount(0);
    fieldsListTable.getColumnModel().getColumn(0).setMinWidth(150);
    fieldsListTable
        .getInputMap(JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
        .put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "DEFAULT_ACTION"); // NOI18N
    fieldsListTable
        .getActionMap()
        .put(
            "DEFAULT_ACTION", // NOI18N
            new AbstractAction() {
              public void actionPerformed(ActionEvent e) {
                performDefaultAction();
              }
            });

    // Disable traversing table cells using TAB and Shift+TAB
    Set keys =
        new HashSet(
            fieldsListTable.getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS));
    keys.add(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0));
    fieldsListTable.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, keys);

    keys =
        new HashSet(
            fieldsListTable.getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS));
    keys.add(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_MASK));
    fieldsListTable.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, keys);

    setColumnsData();

    tablePopup = createTablePopup();

    cornerPopup = new JPopupMenu();

    JTreeTablePanel tablePanel = new JTreeTablePanel(fieldsListTable);
    tablePanel.setCorner(
        JScrollPane.UPPER_RIGHT_CORNER, createHeaderPopupCornerButton(cornerPopup));

    dataPanel = new JPanel(new BorderLayout());
    dataPanel.add(tablePanel, BorderLayout.CENTER);

    noDataPanel = new JPanel(new BorderLayout());
    noDataPanel.setBorder(BorderFactory.createLoweredBevelBorder());

    HTMLTextArea hintArea = new HTMLTextArea();
    hintArea.setBorder(BorderFactory.createEmptyBorder(10, 8, 8, 8));

    String instancesRes = Icons.getResource(HeapWalkerIcons.INSTANCES);
    String hintText =
        Bundle.ReferencesBrowserControllerUI_NoInstanceSelectedMsg(
            "<img border='0' align='bottom' src='nbresloc:/" + instancesRes + "'>"); // NOI18N

    hintArea.setText(hintText);
    noDataPanel.add(hintArea, BorderLayout.CENTER);

    contents = new CardLayout();
    setLayout(contents);
    add(noDataPanel, NO_DATA);
    add(dataPanel, DATA);

    fieldsListTable.addMouseListener(new FieldsListTableMouseListener());
    fieldsListTable.addKeyListener(new FieldsListTableKeyListener());
  }
  private void initColumnsData() {
    columnWidths = new int[columnCount - 1]; // Width of the first column fits to width
    columnNames = new String[columnCount];
    columnToolTips = new String[columnCount];
    columnRenderers = new TableCellRenderer[columnCount];

    columnNames[0] = Bundle.ReferencesBrowserControllerUI_FieldColumnName();
    columnToolTips[0] = Bundle.ReferencesBrowserControllerUI_FieldColumnDescr();

    columnNames[1] = Bundle.ReferencesBrowserControllerUI_TypeColumnName();
    columnToolTips[1] = Bundle.ReferencesBrowserControllerUI_TypeColumnDescr();

    columnNames[2] = Bundle.ReferencesBrowserControllerUI_FullTypeColumnName();
    columnToolTips[2] = Bundle.ReferencesBrowserControllerUI_FullTypeColumnDescr();

    columnNames[3] = Bundle.ReferencesBrowserControllerUI_ValueColumnName();
    columnToolTips[3] = Bundle.ReferencesBrowserControllerUI_ValueColumnDescr();

    columnNames[4] = Bundle.ReferencesBrowserControllerUI_SizeColumnName();
    columnToolTips[4] = Bundle.ReferencesBrowserControllerUI_SizeColumnDescr();

    if (retainedSizeSupported) {
      columnNames[5] = Bundle.ReferencesBrowserControllerUI_RetainedSizeColumnName();
      columnToolTips[5] = Bundle.ReferencesBrowserControllerUI_RetainedSizeColumnDescr();
    }

    int unitWidth =
        getFontMetrics(getFont()).charWidth('W'); // NOI18N // initial width of data columns

    FieldTreeCellRenderer treeCellRenderer = new FieldTreeCellRenderer();
    treeCellRenderer.setLeafIcon(null);
    treeCellRenderer.setClosedIcon(null);
    treeCellRenderer.setOpenIcon(null);

    LabelTableCellRenderer dataCellRendererL = new LabelTableCellRenderer(JLabel.LEADING);
    LabelTableCellRenderer dataCellRendererT = new LabelTableCellRenderer(JLabel.TRAILING);

    // method / class / package name
    columnRenderers[0] = null;

    columnWidths[1 - 1] = unitWidth * 18;
    columnRenderers[1] = dataCellRendererL;

    columnWidths[2 - 1] = unitWidth * 28;
    columnRenderers[2] = dataCellRendererL;

    columnWidths[3 - 1] = unitWidth * 14;
    columnRenderers[3] = dataCellRendererT;

    columnWidths[4 - 1] = unitWidth * 7;
    columnRenderers[4] = dataCellRendererT;

    if (retainedSizeSupported) {
      columnWidths[5 - 1] = unitWidth * 7;
      columnRenderers[5] = dataCellRendererT;
    }
  }
  private JPopupMenu createTablePopup() {
    JPopupMenu popup = new JPopupMenu();

    showInstanceItem = new JMenuItem(Bundle.ReferencesBrowserControllerUI_ShowInstanceItemText());
    showInstanceItem.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            performDefaultAction();
          }
        });
    showInstanceItem.setFont(popup.getFont().deriveFont(Font.BOLD));

    //        showClassItem = new JMenuItem(SHOW_IN_CLASSES_ITEM_TEXT);
    //        showClassItem.addActionListener(new ActionListener() {
    //                public void actionPerformed(ActionEvent e) {
    //                    int row = fieldsListTable.getSelectedRow();
    //
    //                    if (row != -1) {
    //                        HeapWalkerNode node = (HeapWalkerNode)
    // fieldsListTable.getTree().getPathForRow(row).getLastPathComponent();
    //
    //                        if (node instanceof HeapWalkerInstanceNode) {
    //                            HeapWalkerInstanceNode instanceNode = (HeapWalkerInstanceNode)
    // node;
    //
    //                            if (instanceNode instanceof ObjectFieldNode && ((ObjectFieldNode)
    // instanceNode).isStatic()) {
    //                                referencesBrowserController.navigateToClass(((ObjectFieldNode)
    // instanceNode).getFieldValue()
    //
    // .getField().getDeclaringClass());
    //                            } else if (instanceNode.hasInstance()) {
    //
    // referencesBrowserController.navigateToClass(instanceNode.getInstance().getJavaClass());
    //                            }
    //                        }
    //                    }
    //                }
    //            });

    showGcRootItem = new JMenuItem(Bundle.ReferencesBrowserControllerUI_ShowGcRootItemText());
    showGcRootItem.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            int row = fieldsListTable.getSelectedRow();

            if (row != -1) {
              HeapWalkerNode node =
                  (HeapWalkerNode)
                      fieldsListTable.getTree().getPathForRow(row).getLastPathComponent();

              if (node instanceof InstanceNode) {
                InstanceNode instanceNode = (InstanceNode) node;

                if (instanceNode.hasInstance()) {
                  referencesBrowserController.navigateToNearestGCRoot(instanceNode);
                }
              }
            }
          }
        });

    showLoopOriginItem = new JMenuItem(Bundle.ReferencesBrowserControllerUI_ShowLoopItemText());
    showLoopOriginItem.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            int row = fieldsListTable.getSelectedRow();

            if (row != -1) {
              HeapWalkerNode node =
                  (HeapWalkerNode)
                      fieldsListTable.getTree().getPathForRow(row).getLastPathComponent();

              if (node instanceof HeapWalkerInstanceNode
                  && ((HeapWalkerInstanceNode) node).isLoop()) {
                selectNode(((HeapWalkerInstanceNode) node).getLoopTo());
              }
            }
          }
        });

    // Copy Path From Root
    copyPathFromRootItem = new JMenuItem(Bundle.ReferencesBrowserControllerUI_CopyPathFromRoot());
    copyPathFromRootItem.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            int row = fieldsListTable.getSelectedRow();
            if (row != -1) {
              TreePath path = fieldsListTable.getTree().getPathForRow(row);
              BrowserUtils.copyPathFromRoot(path);
            }
          };
        });

    if (GoToSource.isAvailable()) {
      showSourceItem = new JMenuItem(Bundle.ReferencesBrowserControllerUI_GoToSourceItemText());
      showSourceItem.addActionListener(
          new ActionListener() {
            public void actionPerformed(ActionEvent e) {
              int row = fieldsListTable.getSelectedRow();

              if (row != -1) {
                HeapWalkerNode node =
                    (HeapWalkerNode)
                        fieldsListTable.getTree().getPathForRow(row).getLastPathComponent();
                String className = node.getType();

                while (className.endsWith("[]")) {
                  className = className.substring(0, className.length() - 2); // NOI18N
                }
                Lookup.Provider p =
                    referencesBrowserController
                        .getReferencesControllerHandler()
                        .getHeapFragmentWalker()
                        .getHeapDumpProject();
                GoToSource.openSource(p, className, null, null);
              }
            }
          });
    }

    showInThreadsItem = new JMenuItem(Bundle.ReferencesBrowserControllerUI_ShowInThreadsItemText());
    showInThreadsItem.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            int row = fieldsListTable.getSelectedRow();

            if (row != -1) {
              HeapWalkerNode node =
                  (HeapWalkerNode)
                      fieldsListTable.getTree().getPathForRow(row).getLastPathComponent();
              if (node instanceof HeapWalkerInstanceNode) {
                Instance instance = ((HeapWalkerInstanceNode) node).getInstance();
                referencesBrowserController.showInThreads(instance);
              }
            }
          }
        });

    popup.add(showInstanceItem);
    //        popup.add(showClassItem);
    popup.add(showGcRootItem);
    popup.add(showInThreadsItem);
    popup.addSeparator();
    popup.add(copyPathFromRootItem);
    popup.addSeparator();
    popup.add(showLoopOriginItem);
    if (showSourceItem != null) popup.add(showSourceItem);

    return popup;
  }