Skip to content

Commit d2fe76d

Browse files
committed
Heap Viewer - preselect object opened in new tab, each tab remembers its last focus owner
1 parent c3b3c37 commit d2fe76d

File tree

5 files changed

+132
-38
lines changed

5 files changed

+132
-38
lines changed

visualvm/heapviewer/src/com/sun/tools/visualvm/heapviewer/swing/ProfilerTabbedView.java

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,23 @@
2626
package com.sun.tools.visualvm.heapviewer.swing;
2727

2828
import java.awt.BorderLayout;
29-
import java.awt.Color;
29+
import java.awt.Component;
3030
import java.awt.Image;
3131
import java.awt.Insets;
32+
import java.awt.KeyboardFocusManager;
3233
import java.awt.Rectangle;
3334
import java.awt.Toolkit;
3435
import java.awt.event.ActionEvent;
3536
import java.awt.event.ActionListener;
37+
import java.awt.event.HierarchyEvent;
38+
import java.awt.event.HierarchyListener;
3639
import java.awt.image.FilteredImageSource;
3740
import java.awt.image.ImageProducer;
3841
import java.awt.image.RGBImageFilter;
3942
import java.beans.PropertyChangeEvent;
4043
import java.beans.PropertyChangeListener;
44+
import java.lang.ref.Reference;
45+
import java.lang.ref.WeakReference;
4146
import java.util.HashMap;
4247
import java.util.Map;
4348
import java.util.Objects;
@@ -47,6 +52,7 @@
4752
import javax.swing.JComponent;
4853
import javax.swing.JPanel;
4954
import javax.swing.JTabbedPane;
55+
import javax.swing.SwingUtilities;
5056
import javax.swing.Timer;
5157
import javax.swing.UIManager;
5258
import javax.swing.event.ChangeEvent;
@@ -130,6 +136,9 @@ private static ProfilerTabbedView create(int tabPlacement, boolean minimizeInner
130136
public abstract void highlightView(JComponent view);
131137

132138

139+
public abstract void setFocusMaster(Component focusMaster);
140+
141+
133142
protected ProfilerTabbedView() {}
134143

135144

@@ -161,6 +170,8 @@ public static class Impl extends ProfilerTabbedView {
161170

162171
private JTabbedPane tabs;
163172

173+
private Component focusMaster;
174+
164175

165176
protected Impl(int tabPlacement, int tabLayoutPolicy, boolean minimizeOuterMargin,
166177
boolean minimizeInnerMargin, ChangeListener listener) {
@@ -183,11 +194,18 @@ protected Impl(int tabPlacement, int tabLayoutPolicy, boolean minimizeOuterMargi
183194
public void actionPerformed(ActionEvent e) { selectNextView(); }
184195
});
185196

197+
setFocusMaster(null);
198+
186199
// tabs = createTabs(component, tabPlacement, minimizeOuterMargin);
187200
// component.add(tabs, BorderLayout.CENTER);
188201
}
189202

190203

204+
public void setFocusMaster(Component focusMaster) {
205+
this.focusMaster = focusMaster == null ? component : focusMaster;
206+
}
207+
208+
191209
public JComponent getComponent() {
192210
return component;
193211
}
@@ -285,7 +303,7 @@ public void removeView(JComponent view) {
285303
firstClosable = singleViewport.isClosable();
286304
firstView = singleViewport.disposeView();
287305
component.remove(tabs);
288-
component.add(firstView);
306+
component.add(firstView, BorderLayout.CENTER);
289307
tabs = null;
290308
}
291309
} else if (firstView == view) {
@@ -380,6 +398,7 @@ protected final void fireChanged() {
380398

381399
protected final TabbedPaneViewport createViewport(JComponent view, boolean closable) {
382400
return new TabbedPaneViewport(view, closable) {
401+
Component getFocusMaster() { return focusMaster; }
383402
int getTabPlacement() { return tabPlacement; }
384403
boolean minimizeInnerMargin() { return minimizeInnerMargin; }
385404
};
@@ -526,16 +545,62 @@ private static abstract class TabbedPaneViewport extends JPanel {
526545

527546
private final JComponent content;
528547

548+
private Reference<Component> lastFocusOwner;
549+
529550
TabbedPaneViewport(JComponent view, boolean closable) {
530551
super(new BorderLayout());
531552

532553
content = view;
533554

534555
setOpaque(false);
556+
setFocusable(false);
535557
// setBackground(Color.YELLOW);
536558
add(view, BorderLayout.CENTER);
537559
if (!closable) putClientProperty(TabbedPaneFactory.NO_CLOSE_BUTTON, Boolean.TRUE);
538560
view.putClientProperty("TabbedPaneViewport", this); // NOI18N
561+
562+
final PropertyChangeListener focusListener = new PropertyChangeListener() {
563+
public void propertyChange(PropertyChangeEvent evt) {
564+
Component c = evt.getNewValue() instanceof Component ?
565+
(Component)evt.getNewValue() : null;
566+
processFocusedComponent(c);
567+
}
568+
private void processFocusedComponent(Component c) {
569+
Component cc = c;
570+
while (c != null) {
571+
if (c == getFocusMaster()) {
572+
lastFocusOwner = new WeakReference(cc);
573+
return;
574+
}
575+
c = c.getParent();
576+
}
577+
}
578+
};
579+
580+
addHierarchyListener(new HierarchyListener() {
581+
public void hierarchyChanged(HierarchyEvent e) {
582+
if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) {
583+
if (isShowing()) {
584+
final Component lastFocus = lastFocusOwner == null ? null : lastFocusOwner.get();
585+
if (lastFocus != null) lastFocus.requestFocusInWindow();
586+
else content.requestFocusInWindow();
587+
588+
SwingUtilities.invokeLater(new Runnable() {
589+
public void run() {
590+
if (lastFocus != null) lastFocus.requestFocusInWindow();
591+
else content.requestFocusInWindow();
592+
}
593+
});
594+
595+
KeyboardFocusManager.getCurrentKeyboardFocusManager().
596+
addPropertyChangeListener("focusOwner", focusListener); // NOI18N
597+
} else {
598+
KeyboardFocusManager.getCurrentKeyboardFocusManager().
599+
removePropertyChangeListener("focusOwner", focusListener); // NOI18N
600+
}
601+
}
602+
}
603+
});
539604
}
540605

541606

@@ -569,6 +634,8 @@ static TabbedPaneViewport fromView(JComponent view) {
569634
}
570635

571636

637+
abstract Component getFocusMaster();
638+
572639
abstract int getTabPlacement();
573640

574641
abstract boolean minimizeInnerMargin();

visualvm/heapviewer/src/com/sun/tools/visualvm/heapviewer/ui/HeapViewerComponent.java

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,10 @@ private void initComponents() {
178178
public void stateChanged(ChangeEvent e) {
179179
final JComponent newComponent = views.getSelectedView();
180180
viewSelected(getView(newComponent));
181-
newComponent.requestFocusInWindow();
182-
SwingUtilities.invokeLater(new Runnable() {
183-
public void run() { newComponent.requestFocusInWindow(); }
184-
});
185181
}
186182
});
187183
add(views.getComponent(), BorderLayout.CENTER);
184+
views.setFocusMaster(this);
188185

189186
// Create the main view
190187
mainView = new MainView();
@@ -246,27 +243,6 @@ private void updateViewTab(HeapView view) {
246243
}
247244

248245

249-
private void updateFocus() {
250-
// SwingUtilities.invokeLater(new Runnable() {
251-
// public void run() {
252-
// Component focused = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
253-
//
254-
// // Do not change focus for these special cases:
255-
// if (focused != null) {
256-
// if (profilePopupVisible) return; // focus in the Profile popup
257-
// if (ProfilerPopup.isInPopup(focused)) return; // focus in a profiler popup
258-
// if (ProfilerWindow.this.isAncestorOf(focused)) return; // focus inside the ProfilerWindow
259-
// }
260-
//
261-
// final Component foc = configure.isVisible() ? configure : start;
262-
// SwingUtilities.invokeLater(new Runnable() {
263-
// public void run() { foc.requestFocusInWindow(); }
264-
// });
265-
// }
266-
// });
267-
}
268-
269-
270246
private class MainView extends HeapView {
271247

272248
private PopupButton featureChooser;
@@ -371,12 +347,31 @@ void selectFeature(HeapContext context, HeapViewerFeature feature) {
371347
}
372348

373349

374-
private void displayPopupImpl(JComponent invoker) {
350+
// private void updateFocus(final JComponent invoker) {
351+
// SwingUtilities.invokeLater(new Runnable() {
352+
// public void run() {
353+
// Component focused = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
354+
//
355+
// // Do not change focus for these special cases:
356+
// if (focused != null) {
357+
// // if (profilePopupVisible) return; // focus in the Profile popup
358+
// if (ProfilerPopup.isInPopup(focused)) return; // focus in a profiler popup
359+
// if (HeapViewerComponent.this.isAncestorOf(focused)) return; // focus inside the ProfilerWindow
360+
// }
361+
//
362+
// SwingUtilities.invokeLater(new Runnable() {
363+
// public void run() { invoker.requestFocusInWindow(); }
364+
// });
365+
// }
366+
// });
367+
// }
368+
369+
private void displayPopupImpl(final JComponent invoker) {
375370
final ProfilerPopupMenu popup = new StayOpenPopupMenu() {
376-
public void setVisible(boolean visible) {
377-
super.setVisible(visible);
378-
if (!visible) updateFocus();
379-
}
371+
// public void setVisible(boolean visible) {
372+
// super.setVisible(visible);
373+
// if (!visible) updateFocus(invoker);
374+
// }
380375
};
381376
popup.setLayout(new GridBagLayout());
382377
if (!UIUtils.isAquaLookAndFeel()) {
@@ -507,7 +502,12 @@ private void initUI() {
507502
};
508503
toolbar.add(featureChooser);
509504

510-
component = new JPanel(new BorderLayout());
505+
component = new JPanel(new BorderLayout()) {
506+
public boolean requestFocusInWindow() {
507+
if (getComponentCount() == 0) return super.requestFocusInWindow();
508+
else return getComponent(0).requestFocusInWindow();
509+
}
510+
};
511511
component.setOpaque(false);
512512

513513
selectFeature(selectedContext, selectedFeature);

visualvm/heapviewer/src/com/sun/tools/visualvm/heapviewer/ui/NodeObjectsView.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import com.sun.tools.visualvm.heapviewer.model.HeapViewerNodeFilter;
4545
import com.sun.tools.visualvm.heapviewer.model.Progress;
4646
import com.sun.tools.visualvm.heapviewer.model.RootNode;
47+
import javax.swing.SwingUtilities;
4748
import org.openide.util.RequestProcessor;
4849

4950
/**
@@ -139,6 +140,13 @@ private void initUI() {
139140
}
140141

141142
component = new ViewContainer(objectsView.getComponent(), viewNode);
143+
144+
SwingUtilities.invokeLater(new Runnable() {
145+
public void run() {
146+
objectsView.selectNode(viewNode);
147+
objectsView.getComponent().requestFocusInWindow();
148+
}
149+
});
142150
}
143151

144152

@@ -150,9 +158,15 @@ private static class ViewContainer extends JPanel {
150158
super(new BorderLayout());
151159
node = viewNode;
152160
setOpaque(false);
161+
setFocusable(false);
153162
add(view, BorderLayout.CENTER);
154163
}
155164

165+
public boolean requestFocusInWindow() {
166+
if (getComponentCount() == 0) return super.requestFocusInWindow();
167+
else return getComponent(0).requestFocusInWindow();
168+
}
169+
156170
public boolean equals(Object o) {
157171
if (o == this) return true;
158172
if (!(o instanceof ViewContainer)) return false;

visualvm/heapviewer/src/com/sun/tools/visualvm/heapviewer/ui/PluggableTreeTableView.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,15 @@ protected void nodeSelected(HeapViewerNode node, boolean adjusting) {
9999
}
100100

101101
protected JComponent createComponent() {
102-
JComponent comp = super.createComponent();
102+
final JComponent comp = super.createComponent();
103103

104104
if (toolbar == null) init();
105105

106-
JExtendedSplitPane contentSplit = new JExtendedSplitPane(JExtendedSplitPane.VERTICAL_SPLIT, true, comp, pluginsComponent);
106+
JExtendedSplitPane contentSplit = new JExtendedSplitPane(JExtendedSplitPane.VERTICAL_SPLIT, true, comp, pluginsComponent) {
107+
public boolean requestFocusInWindow() {
108+
return comp.requestFocusInWindow();
109+
}
110+
};
107111
BasicSplitPaneDivider contentDivider = ((BasicSplitPaneUI)contentSplit.getUI()).getDivider();
108112
contentDivider.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, SEPARATOR_COLOR));
109113
contentDivider.setDividerSize(5);

visualvm/heapviewer/src/com/sun/tools/visualvm/heapviewer/ui/TreeTableView.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ public int compare(TreeTableViewColumn column1, TreeTableViewColumn column2) {
161161

162162
if (useBreadCrumbs) navigator = new BreadCrumbsNavigator() {
163163
void nodeClicked(HeapViewerNode node) {
164-
TreeTableView.this.selectNode(node);
164+
TreeTableView.this.selectExistingNode(node);
165165
}
166166
void nodePinned(HeapViewerNode node) {
167167
TreeTableView.this.pinNode(node);
@@ -224,6 +224,11 @@ public void reloadView() {
224224
}
225225

226226

227+
public void selectNode(HeapViewerNode node) {
228+
// TODO: implement correctly for lazy model
229+
treeTable.selectPath(HeapViewerNode.fromNode(node, currentRoot), true);
230+
}
231+
227232
public void expandNode(HeapViewerNode node) {
228233
if (treeTable == null) return;
229234
treeTable.expandPath(HeapViewerNode.fromNode(node));
@@ -282,7 +287,7 @@ private void setRoot(HeapViewerNode newRoot) {
282287
// if (node instanceof RootContainerNode) node = node.getNChildren() == 0 ? null : node.getChild(0);
283288
}
284289

285-
void selectNode(HeapViewerNode node) {
290+
void selectExistingNode(HeapViewerNode node) {
286291
// HeapViewerNode sel = (HeapViewerNode)treeTable.getSelectedValue(0);
287292

288293
if (node == null) {
@@ -447,7 +452,11 @@ public void mouseClicked(MouseEvent e) {
447452
}
448453
});
449454

450-
JComponent comp = new JPanel(new BorderLayout());
455+
JComponent comp = new JPanel(new BorderLayout()) {
456+
public boolean requestFocusInWindow() {
457+
return treeTable.requestFocusInWindow();
458+
}
459+
};
451460
comp.add(new ProfilerTableContainer(treeTable, false, null), BorderLayout.CENTER);
452461

453462
JComponent toolsContainer = new JPanel(new GridBagLayout());

0 commit comments

Comments
 (0)