001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: * The Original Software is NetBeans. The Initial Developer of the Original
026: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
027: * Microsystems, Inc. All Rights Reserved.
028: *
029: * If you wish your version of this file to be governed by only the CDDL
030: * or only the GPL Version 2, indicate your decision by adding
031: * "[Contributor] elects to include this software in this distribution
032: * under the [CDDL or GPL Version 2] license." If you do not indicate a
033: * single choice of license, a recipient has the option to distribute
034: * your version of this file under either the CDDL, the GPL Version 2 or
035: * to extend the choice of license to its licensees as provided above.
036: * However, if you add GPL Version 2 code and therefore, elected the GPL
037: * Version 2 license, then the option applies only if the new code is
038: * made subject to such option by the copyright holder.
039: */
040:
041: package org.netbeans.modules.profiler.heapwalk.ui;
042:
043: import org.netbeans.lib.profiler.heap.Instance;
044: import org.netbeans.lib.profiler.heap.JavaClass;
045: import org.netbeans.lib.profiler.ui.UIConstants;
046: import org.netbeans.lib.profiler.ui.UIUtils;
047: import org.netbeans.lib.profiler.ui.components.JTitledPanel;
048: import org.netbeans.lib.profiler.ui.components.JTreeTable;
049: import org.netbeans.lib.profiler.ui.components.table.LabelTableCellRenderer;
050: import org.netbeans.lib.profiler.ui.components.treetable.AbstractTreeTableModel;
051: import org.netbeans.lib.profiler.ui.components.treetable.ExtendedTreeTableModel;
052: import org.netbeans.lib.profiler.ui.components.treetable.JTreeTablePanel;
053: import org.netbeans.lib.profiler.ui.components.treetable.TreeTableModel;
054: import org.netbeans.modules.profiler.heapwalk.InstancesListController;
055: import org.netbeans.modules.profiler.heapwalk.model.HeapWalkerNode;
056: import org.openide.util.NbBundle;
057: import org.openide.util.Utilities;
058: import java.awt.BorderLayout;
059: import java.awt.Dimension;
060: import java.awt.Insets;
061: import java.awt.KeyboardFocusManager;
062: import java.awt.Rectangle;
063: import java.awt.event.InputEvent;
064: import java.awt.event.KeyAdapter;
065: import java.awt.event.KeyEvent;
066: import java.awt.event.MouseAdapter;
067: import java.awt.event.MouseEvent;
068: import java.util.HashSet;
069: import java.util.Set;
070: import javax.swing.ImageIcon;
071: import javax.swing.JButton;
072: import javax.swing.JCheckBoxMenuItem;
073: import javax.swing.JLabel;
074: import javax.swing.JPanel;
075: import javax.swing.JPopupMenu;
076: import javax.swing.JScrollPane;
077: import javax.swing.KeyStroke;
078: import javax.swing.ListSelectionModel;
079: import javax.swing.SwingUtilities;
080: import javax.swing.event.ListSelectionEvent;
081: import javax.swing.event.ListSelectionListener;
082: import javax.swing.table.TableCellRenderer;
083: import javax.swing.table.TableColumnModel;
084: import javax.swing.tree.TreePath;
085:
086: /**
087: *
088: * @author Jiri Sedlacek
089: */
090: public class InstancesListControllerUI extends JTitledPanel {
091: //~ Inner Classes ------------------------------------------------------------------------------------------------------------
092:
093: // --- TreeTable model -------------------------------------------------------
094: private class InstancesListTreeTableModel extends
095: AbstractTreeTableModel {
096: //~ Constructors ---------------------------------------------------------------------------------------------------------
097:
098: private InstancesListTreeTableModel() {
099: super (InstancesListController.EMPTY_INSTANCE_NODE, true,
100: sortingColumn, sortingOrder);
101: }
102:
103: //~ Methods --------------------------------------------------------------------------------------------------------------
104:
105: public boolean isCellEditable(int rowIndex, int columnIndex) {
106: return false;
107: }
108:
109: public Class getColumnClass(int column) {
110: if (column == 0) {
111: return TreeTableModel.class;
112: } else {
113: return Object.class;
114: }
115: }
116:
117: public int getColumnCount() {
118: return columnCount;
119: }
120:
121: public String getColumnName(int columnIndex) {
122: return columnNames[columnIndex];
123: }
124:
125: public String getColumnToolTipText(int col) {
126: return columnToolTips[col];
127: }
128:
129: public boolean getInitialSorting(int column) {
130: switch (column) {
131: case 0:
132: return true;
133: default:
134: return false;
135: }
136: }
137:
138: public boolean isLeaf(Object node) {
139: return ((HeapWalkerNode) node).isLeaf();
140: }
141:
142: public Object getValueAt(Object object, int columnIndex) {
143: if (object instanceof InstancesListController.InstancesListNode) {
144: InstancesListController.InstancesListNode node = (InstancesListController.InstancesListNode) object;
145:
146: switch (columnIndex) {
147: case 0:
148: return node;
149: case 1:
150: return node.getSize();
151:
152: // TODO: uncomment once retained & reachable size implemented
153: // case 2: return node.getRetainedSize();
154: // case 3: return node.getReachableSize();
155: default:
156: return null;
157: }
158: } else {
159: HeapWalkerNode node = (HeapWalkerNode) object;
160:
161: switch (columnIndex) {
162: case 0:
163: return node;
164: default:
165: return ""; // NOI18N
166: }
167: }
168: }
169:
170: public void sortByColumn(int column, boolean order) {
171: sortingColumn = column;
172: sortingOrder = order;
173:
174: Instance selectedInstance = instancesListController
175: .getSelectedInstance();
176:
177: if (selectedInstance != null) {
178: instancesListController
179: .scheduleInstanceSelection(selectedInstance);
180: } else if (instancesListTable != null) {
181: int selectedRow = instancesListTable.getSelectedRow();
182:
183: if (selectedRow != -1) {
184: HeapWalkerNode selectedNode = (HeapWalkerNode) instancesListTable
185: .getTree().getPathForRow(selectedRow)
186: .getLastPathComponent();
187:
188: if (selectedNode instanceof InstancesListController.InstancesListContainerNode) {
189: instancesListController
190: .scheduleContainerSelection(selectedNode
191: .getParent().getIndexOfChild(
192: selectedNode));
193: }
194: }
195: }
196:
197: if (isShowing()) {
198: sorting = true;
199: }
200:
201: update(true);
202: }
203: }
204:
205: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
206:
207: // -----
208: // I18N String constants
209: private static final String VIEW_CAPTION = NbBundle.getMessage(
210: InstancesListControllerUI.class,
211: "InstancesListControllerUI_ViewCaption"); // NOI18N
212: private static final String SHOW_HIDE_COLUMNS_STRING = NbBundle
213: .getMessage(InstancesListControllerUI.class,
214: "InstancesListControllerUI_ShowHideColumnsString"); // NOI18N
215: private static final String INSTANCE_COLUMN_NAME = NbBundle
216: .getMessage(InstancesListControllerUI.class,
217: "InstancesListControllerUI_InstanceColumnName"); // NOI18N
218: private static final String INSTANCE_COLUMN_DESCR = NbBundle
219: .getMessage(InstancesListControllerUI.class,
220: "InstancesListControllerUI_InstanceColumnDescr"); // NOI18N
221: private static final String SIZE_COLUMN_NAME = NbBundle.getMessage(
222: InstancesListControllerUI.class,
223: "InstancesListControllerUI_SizeColumnName"); // NOI18N
224: private static final String SIZE_COLUMN_DESCR = NbBundle
225: .getMessage(InstancesListControllerUI.class,
226: "InstancesListControllerUI_SizeColumnDescr"); // NOI18N
227: private static final String RETAINED_SIZE_COLUMN_NAME = NbBundle
228: .getMessage(InstancesListControllerUI.class,
229: "InstancesListControllerUI_RetainedSizeColumnName"); // NOI18N
230: private static final String RETAINED_SIZE_COLUMN_DESCR = NbBundle
231: .getMessage(InstancesListControllerUI.class,
232: "InstancesListControllerUI_RetainedSizeColumnDescr"); // NOI18N
233: private static final String REACHABLE_SIZE_COLUMN_NAME = NbBundle
234: .getMessage(InstancesListControllerUI.class,
235: "InstancesListControllerUI_ReachableSizeColumnName"); // NOI18N
236: private static final String REACHABLE_SIZE_COLUMN_DESCR = NbBundle
237: .getMessage(InstancesListControllerUI.class,
238: "InstancesListControllerUI_ReachableSizeColumnDescr"); // NOI18N
239: // -----
240: private static ImageIcon ICON_INSTANCES = new ImageIcon(
241: Utilities
242: .loadImage("org/netbeans/modules/profiler/heapwalk/ui/resources/instances.png")); // NOI18N
243: private static final int columnCount = 2; // TODO: restore back to 4 once retained & reachable size implemented
244:
245: //~ Instance fields ----------------------------------------------------------------------------------------------------------
246:
247: private ExtendedTreeTableModel instancesListTableModel;
248: private FieldTreeCellRenderer treeCellRenderer = new FieldTreeCellRenderer();
249: private Instance instanceToSelect = null;
250: private InstancesListController instancesListController;
251: private InstancesListTreeTableModel realInstancesListModel;
252:
253: // --- UI definition ---------------------------------------------------------
254: private JPanel dataPanel;
255: private JPopupMenu cornerPopup;
256: private JTreeTable instancesListTable;
257: private String filterValue = ""; // NOI18N
258: private String selectedRowContents;
259: private String[] columnNames;
260: private javax.swing.table.TableCellRenderer[] columnRenderers;
261: private String[] columnToolTips;
262: private int[] columnWidths;
263: private boolean internalCornerButtonClick = false; // flag for closing columns popup by pressing cornerButton
264:
265: // --- Selection utils -------------------------------------------------------
266: private boolean selectionSaved = false;
267: private boolean sorting = false;
268: private boolean sortingOrder = false;
269: private int selectedRow;
270: private int sortingColumn = 1;
271:
272: //~ Constructors -------------------------------------------------------------------------------------------------------------
273:
274: // --- Constructors ----------------------------------------------------------
275: public InstancesListControllerUI(
276: InstancesListController instancesListController) {
277: super (VIEW_CAPTION, ICON_INSTANCES, true);
278:
279: this .instancesListController = instancesListController;
280:
281: realInstancesListModel = new InstancesListTreeTableModel();
282: instancesListTableModel = new ExtendedTreeTableModel(
283: realInstancesListModel);
284:
285: initColumnsData();
286: initData();
287: initComponents();
288: }
289:
290: //~ Methods ------------------------------------------------------------------------------------------------------------------
291:
292: public void initColumns() {
293: if (instancesListTable != null) {
294: SwingUtilities.invokeLater(new Runnable() {
295: public void run() {
296: JavaClass selectedClass = instancesListController
297: .getInstancesController()
298: .getSelectedClass();
299:
300: if ((selectedClass != null)
301: && selectedClass.isArray()) {
302: if (!instancesListTableModel
303: .isRealColumnVisible(1)) {
304: toggleColumnVisibility(1, false);
305: }
306: } else {
307: if (instancesListTableModel
308: .isRealColumnVisible(1)) {
309: toggleColumnVisibility(1, false);
310: }
311: }
312: }
313: });
314: }
315: }
316:
317: // --- Public interface ------------------------------------------------------
318: public void makeVisible() {
319: if (!isShowing()) {
320: setVisible(true);
321: }
322: }
323:
324: // --- Internal interface ----------------------------------------------------
325: public void refreshView() {
326: // Used for refreshing treetable after lazy-populating the model
327: if (instancesListTable != null) {
328: HeapWalkerNode root = (HeapWalkerNode) instancesListTableModel
329: .getRoot();
330: instancesListTable
331: .getTree()
332: .setShowsRootHandles(
333: root instanceof InstancesListController.InstancesListClassNode
334: && root.getChild(0) instanceof InstancesListController.InstancesListContainerNode);
335: instancesListTable.updateTreeTable();
336: }
337: }
338:
339: public void selectInstance(Instance instance) {
340: if (displaysFlatInstances()) {
341: selectFlatInstance(instance);
342: } else if (displaysCollapsedInstances()) {
343: selectCollapsedInstance(instance);
344: }
345: }
346:
347: public void selectPath(TreePath pathToSelect) {
348: if (instancesListTable == null) {
349: return;
350: }
351:
352: final TreePath pathToSelectFinal = pathToSelect;
353: SwingUtilities.invokeLater(new Runnable() {
354: public void run() {
355: if (!isShowing()) {
356: setVisible(true);
357: }
358:
359: instancesListTable.getTree().setSelectionPath(
360: pathToSelectFinal);
361:
362: Rectangle pathToSelectBounds = instancesListTable
363: .getTree().getPathBounds(pathToSelectFinal);
364:
365: if (pathToSelectBounds != null) {
366: instancesListTable
367: .scrollRectToVisible(pathToSelectBounds); // Fix for Issue 105299, pathToSelectBounds can be null
368: }
369:
370: if (sorting) {
371: sorting = false;
372: }
373: }
374: });
375: }
376:
377: public void update() {
378: update(false);
379: }
380:
381: protected void initColumnSelectorItems() {
382: cornerPopup.removeAll();
383:
384: JCheckBoxMenuItem menuItem;
385:
386: for (int i = 0; i < realInstancesListModel.getColumnCount(); i++) {
387: menuItem = new JCheckBoxMenuItem(realInstancesListModel
388: .getColumnName(i));
389: menuItem.setActionCommand(new Integer(i).toString());
390: addMenuItemListener(menuItem);
391:
392: if (instancesListTable != null) {
393: menuItem.setState(instancesListTableModel
394: .isRealColumnVisible(i));
395:
396: if (i == 0) {
397: menuItem.setEnabled(false);
398: }
399: } else {
400: menuItem.setState(true);
401: }
402:
403: cornerPopup.add(menuItem);
404: }
405:
406: cornerPopup.pack();
407: }
408:
409: private void setColumnsData() {
410: TableColumnModel colModel = instancesListTable.getColumnModel();
411:
412: instancesListTable.setTreeCellRenderer(treeCellRenderer);
413:
414: for (int i = 0; i < instancesListTableModel.getColumnCount(); i++) {
415: int index = instancesListTableModel.getRealColumn(i);
416:
417: if (index != 0) {
418: colModel.getColumn(i).setPreferredWidth(
419: columnWidths[index - 1]);
420: colModel.getColumn(i).setCellRenderer(
421: columnRenderers[index]);
422: }
423: }
424: }
425:
426: private void addMenuItemListener(JCheckBoxMenuItem menuItem) {
427: menuItem.addActionListener(new java.awt.event.ActionListener() {
428: public void actionPerformed(java.awt.event.ActionEvent e) {
429: toggleColumnVisibility(Integer.parseInt(e
430: .getActionCommand()), true);
431: }
432: });
433: }
434:
435: private JButton createHeaderPopupCornerButton(
436: final JPopupMenu headerPopup) {
437: final JButton cornerButton = new JButton(
438: new ImageIcon(
439: Utilities
440: .loadImage("org/netbeans/lib/profiler/ui/resources/hideColumn.png"))); // NOI18N
441: cornerButton.setToolTipText(SHOW_HIDE_COLUMNS_STRING);
442: cornerButton.setDefaultCapable(false);
443:
444: if (UIUtils.isWindowsClassicLookAndFeel()) {
445: cornerButton.setMargin(new Insets(0, 0, 2, 2));
446: } else if (UIUtils.isWindowsXPLookAndFeel()) {
447: cornerButton.setMargin(new Insets(0, 0, 0, 1));
448: } else if (UIUtils.isMetalLookAndFeel()) {
449: cornerButton.setMargin(new Insets(0, 0, 2, 1));
450: }
451:
452: cornerButton.addKeyListener(new KeyAdapter() {
453: public void keyPressed(final KeyEvent evt) {
454: if (evt.getKeyCode() == KeyEvent.VK_SPACE) {
455: showColumnSelectionPopup(headerPopup, cornerButton);
456: }
457: }
458: });
459:
460: cornerButton.addMouseListener(new MouseAdapter() {
461: public void mousePressed(MouseEvent mouseEvent) {
462: if (headerPopup.isVisible()) {
463: internalCornerButtonClick = true;
464: cornerButton.getModel().setArmed(false);
465: } else {
466: internalCornerButtonClick = false;
467:
468: if (mouseEvent.getModifiers() == InputEvent.BUTTON3_MASK) {
469: showColumnSelectionPopup(headerPopup,
470: cornerButton);
471: }
472: }
473: }
474:
475: public void mouseClicked(MouseEvent mouseEvent) {
476: if ((mouseEvent.getModifiers() == InputEvent.BUTTON1_MASK)
477: && (!internalCornerButtonClick)) {
478: showColumnSelectionPopup(headerPopup, cornerButton);
479: }
480: }
481: });
482:
483: return cornerButton;
484: }
485:
486: private boolean displaysCollapsedInstances() {
487: HeapWalkerNode root = (HeapWalkerNode) instancesListTableModel
488: .getRoot();
489:
490: return ((root != null) && (root.getNChildren() > 0) && root
491: .getChild(0) instanceof InstancesListController.InstancesListContainerNode);
492: }
493:
494: private boolean displaysFlatInstances() {
495: HeapWalkerNode root = (HeapWalkerNode) instancesListTableModel
496: .getRoot();
497:
498: return ((root != null) && (root.getNChildren() > 0) && root
499: .getChild(0) instanceof InstancesListController.InstancesListInstanceNode);
500: }
501:
502: private void initColumnsData() {
503: columnWidths = new int[columnCount - 1]; // Width of the first column fits to width
504: columnNames = new String[columnCount];
505: columnToolTips = new String[columnCount];
506: columnRenderers = new TableCellRenderer[columnCount];
507:
508: columnNames[0] = INSTANCE_COLUMN_NAME;
509: columnToolTips[0] = INSTANCE_COLUMN_DESCR;
510:
511: columnNames[1] = SIZE_COLUMN_NAME;
512: columnToolTips[1] = SIZE_COLUMN_DESCR;
513:
514: // TODO: uncomment once retained & reachable size implemented
515: // columnNames[2] = RETAINED_SIZE_COLUMN_NAME;
516: // columnToolTips[2] = RETAINED_SIZE_COLUMN_DESCR;
517: //
518: // columnNames[3] = REACHABLE_SIZE_COLUMN_NAME;
519: // columnToolTips[3] = REACHABLE_SIZE_COLUMN_DESCR;
520: int maxWidth = getFontMetrics(getFont()).charWidth('W') * 7; // NOI18N // initial width of data columns
521:
522: FieldTreeCellRenderer treeCellRenderer = new FieldTreeCellRenderer();
523: treeCellRenderer.setLeafIcon(null);
524: treeCellRenderer.setClosedIcon(null);
525: treeCellRenderer.setOpenIcon(null);
526:
527: LabelTableCellRenderer dataCellRenderer = new LabelTableCellRenderer(
528: JLabel.TRAILING);
529:
530: // method / class / package name
531: columnRenderers[0] = null;
532:
533: columnWidths[1 - 1] = maxWidth;
534: columnRenderers[1] = dataCellRenderer;
535:
536: // TODO: uncomment once retained & reachable size implemented
537: // columnWidths[2 - 1] = maxWidth;
538: // columnRenderers[2] = dataCellRenderer;
539: //
540: // columnWidths[3 - 1] = maxWidth;
541: // columnRenderers[3] = dataCellRenderer;
542: }
543:
544: private void initComponents() {
545: treeCellRenderer.setLeafIcon(null);
546: treeCellRenderer.setClosedIcon(null);
547: treeCellRenderer.setOpenIcon(null);
548:
549: // TODO: uncomment once retained & reachable size implemented
550: // instancesListTableModel.setRealColumnVisibility(2, false);
551: // instancesListTableModel.setRealColumnVisibility(3, false);
552: instancesListTable = new JTreeTable(instancesListTableModel) {
553: public void doLayout() {
554: int columnsWidthsSum = 0;
555: int realFirstColumn = -1;
556:
557: TableColumnModel colModel = getColumnModel();
558:
559: for (int i = 0; i < instancesListTableModel
560: .getColumnCount(); i++) {
561: if (instancesListTableModel.getRealColumn(i) == 0) {
562: realFirstColumn = i;
563: } else {
564: columnsWidthsSum += colModel.getColumn(i)
565: .getPreferredWidth();
566: }
567: }
568:
569: if (realFirstColumn != -1) {
570: colModel.getColumn(realFirstColumn)
571: .setPreferredWidth(
572: getWidth() - columnsWidthsSum);
573: }
574:
575: super .doLayout();
576: };
577: };
578: instancesListTable.getTree().setRootVisible(false);
579: instancesListTable
580: .setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
581: instancesListTable
582: .setGridColor(UIConstants.TABLE_VERTICAL_GRID_COLOR);
583: instancesListTable
584: .setSelectionBackground(UIConstants.TABLE_SELECTION_BACKGROUND_COLOR);
585: instancesListTable
586: .setSelectionForeground(UIConstants.TABLE_SELECTION_FOREGROUND_COLOR);
587: instancesListTable
588: .setShowHorizontalLines(UIConstants.SHOW_TABLE_HORIZONTAL_GRID);
589: instancesListTable
590: .setShowVerticalLines(UIConstants.SHOW_TABLE_VERTICAL_GRID);
591: instancesListTable.setRowMargin(UIConstants.TABLE_ROW_MARGIN);
592: instancesListTable
593: .setRowHeight(UIUtils.getDefaultRowHeight() + 2);
594:
595: // Disable traversing table cells using TAB and Shift+TAB
596: Set keys = new HashSet(
597: instancesListTable
598: .getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS));
599: keys.add(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0));
600: instancesListTable.setFocusTraversalKeys(
601: KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, keys);
602:
603: keys = new HashSet(
604: instancesListTable
605: .getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS));
606: keys.add(KeyStroke.getKeyStroke(KeyEvent.VK_TAB,
607: InputEvent.SHIFT_MASK));
608: instancesListTable.setFocusTraversalKeys(
609: KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, keys);
610:
611: setColumnsData();
612:
613: // tablePopup = createTablePopup();
614: cornerPopup = new JPopupMenu();
615:
616: JButton cornerButton = createHeaderPopupCornerButton(cornerPopup);
617:
618: JTreeTablePanel tablePanel = new JTreeTablePanel(
619: instancesListTable);
620: tablePanel.setCorner(JScrollPane.UPPER_RIGHT_CORNER,
621: createHeaderPopupCornerButton(cornerPopup));
622:
623: setLayout(new BorderLayout());
624: add(tablePanel, BorderLayout.CENTER);
625:
626: instancesListTable.getSelectionModel()
627: .addListSelectionListener(new ListSelectionListener() {
628: public void valueChanged(ListSelectionEvent e) {
629: if (sorting || e.getValueIsAdjusting()) {
630: return;
631: }
632:
633: Instance selectedInstance = null;
634: int selectedRow = instancesListTable
635: .getSelectedRow();
636:
637: if (selectedRow != -1) {
638: HeapWalkerNode selectedNode = (HeapWalkerNode) instancesListTable
639: .getTree().getPathForRow(
640: selectedRow)
641: .getLastPathComponent();
642:
643: if (selectedNode instanceof InstancesListController.InstancesListInstanceNode) {
644: selectedInstance = ((InstancesListController.InstancesListInstanceNode) selectedNode)
645: .getInstance();
646: }
647: }
648:
649: instancesListController
650: .instanceSelected(selectedInstance);
651: }
652: });
653:
654: setPreferredSize(new Dimension(225, 500));
655: }
656:
657: // --- Private implementation ------------------------------------------------
658: private void initData() {
659: instancesListTableModel.setRoot(instancesListController
660: .getFilteredSortedInstances(filterValue, sortingColumn,
661: sortingOrder));
662: refreshView();
663: }
664:
665: private void restoreSelection() {
666: if (selectedRowContents != null) {
667: instancesListTable.selectRowByContents(selectedRowContents,
668: 0, true);
669: }
670:
671: selectionSaved = false;
672: }
673:
674: private void saveSelection() {
675: if (selectionSaved) {
676: return;
677: }
678:
679: selectedRow = (instancesListTable == null) ? (-1)
680: : instancesListTable.getSelectedRow();
681: selectedRowContents = null;
682:
683: if (selectedRow != -1) {
684: selectedRowContents = instancesListTable.getValueAt(
685: selectedRow, 0).toString();
686: }
687:
688: selectionSaved = true;
689: }
690:
691: // Selects instance displayed in container node
692: private void selectCollapsedInstance(Instance instance) {
693: HeapWalkerNode root = (HeapWalkerNode) instancesListTableModel
694: .getRoot();
695:
696: if (root instanceof InstancesListController.InstancesListNode) {
697: InstancesListController.InstancesListNode instancesListRoot = (InstancesListController.InstancesListNode) root;
698: TreePath instancePath = instancesListRoot
699: .getInstancePath(instance);
700:
701: if (instancePath != null) {
702: // instance node already created
703: selectPath(instancePath);
704: } else {
705: // instance node collapsed and not yet created
706: HeapWalkerNode instanceContainer = instancesListController
707: .getInstanceContainer(
708: instance,
709: (InstancesListController.InstancesListClassNode) root);
710:
711: if (instanceContainer != null) {
712: instancesListController
713: .scheduleInstanceSelection(instance);
714: instanceContainer.getChildren(); // lazily computes children and invokes instance selection
715: }
716: }
717: }
718: }
719:
720: // Selects instance when no containers are displayed
721: private void selectFlatInstance(Instance instance) {
722: HeapWalkerNode root = (HeapWalkerNode) instancesListTableModel
723: .getRoot();
724:
725: if (root instanceof InstancesListController.InstancesListNode) {
726: InstancesListController.InstancesListNode instancesListRoot = (InstancesListController.InstancesListNode) root;
727: TreePath instancePath = instancesListRoot
728: .getInstancePath(instance);
729:
730: if (instancePath != null) {
731: selectPath(instancePath);
732: }
733: }
734: }
735:
736: private void showColumnSelectionPopup(final JPopupMenu headerPopup,
737: final JButton cornerButton) {
738: initColumnSelectorItems();
739: headerPopup.show(cornerButton, cornerButton.getWidth()
740: - headerPopup.getPreferredSize().width, cornerButton
741: .getHeight());
742: }
743:
744: private void toggleColumnVisibility(int column, boolean reSort) {
745: boolean sortResults = false;
746: int currentSortingColumn = instancesListTable
747: .getSortingColumn();
748: int realSortingColumn = instancesListTableModel
749: .getRealColumn(currentSortingColumn);
750: boolean isColumnVisible = instancesListTableModel
751: .isRealColumnVisible(column);
752:
753: // Current sorting column is going to be hidden
754: if ((isColumnVisible) && (column == realSortingColumn)) {
755: // Try to set next column as a currentSortingColumn. If currentSortingColumn is the last column,
756: // set previous column as a sorting Column (one column is always visible).
757: currentSortingColumn = ((currentSortingColumn + 1) == instancesListTableModel
758: .getColumnCount()) ? (currentSortingColumn - 1)
759: : (currentSortingColumn + 1);
760: realSortingColumn = instancesListTableModel
761: .getRealColumn(currentSortingColumn);
762: sortResults = true;
763: }
764:
765: instancesListTableModel.setRealColumnVisibility(column,
766: !isColumnVisible);
767: instancesListTable.createDefaultColumnsFromModel();
768: instancesListTable.updateTreeTableHeader(); // required to restore table header renderer
769: currentSortingColumn = instancesListTableModel
770: .getVirtualColumn(realSortingColumn);
771:
772: if (sortResults) {
773: if (reSort) {
774: instancesListTableModel
775: .sortByColumn(
776: currentSortingColumn,
777: instancesListTableModel
778: .getInitialSorting(currentSortingColumn));
779: } else {
780: sortingOrder = instancesListTableModel
781: .getInitialSorting(currentSortingColumn);
782: sortingColumn = currentSortingColumn;
783: }
784:
785: instancesListTable.updateTreeTable();
786: }
787:
788: instancesListTable.setSortingColumn(currentSortingColumn);
789: instancesListTable.setSortingOrder(sortingOrder);
790: instancesListTable.getTableHeader().repaint();
791: setColumnsData();
792: }
793:
794: private void update(boolean fromSorting) {
795: makeVisible();
796: initData();
797:
798: if (!fromSorting && (instancesListTable != null)) {
799: instancesListTable.resetTreeCellOffsetX(); // Ideally should be invoked directly on the component when root node changes
800: }
801: }
802: }
|