Index: NavigationView.java =================================================================== RCS file: /cvs/java/src/org/netbeans/modules/java/ui/NavigationView.java,v retrieving revision 1.20 diff -u -r1.20 NavigationView.java --- NavigationView.java 27 Feb 2003 23:38:23 -0000 1.20 +++ NavigationView.java 5 Feb 2004 13:58:36 -0000 @@ -19,12 +19,14 @@ import java.lang.ref.WeakReference; import java.lang.ref.Reference; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Proxy; import java.util.LinkedList; +import java.awt.*; -import javax.swing.ComboBoxModel; -import javax.swing.JComboBox; -import javax.swing.JEditorPane; +import javax.swing.*; import javax.swing.event.ListDataEvent; import org.openide.cookies.SourceCookie; @@ -147,6 +149,7 @@ return rootChangeListener = new RootL(); } + private JCBRender cellRender = null; /** * Finds the context where the view is actually used. */ @@ -171,6 +174,12 @@ topComponent.addPropertyChangeListener(rootChangeListener); } getExplorerManager().addPropertyChangeListener(rootChangeListener); + + if (cellRender == null && topComponent != null) { + //System.out.println("##create renderer for: " + (topComponent==null?"warmup":topComponent.getDisplayName())); + cellRender = new JCBRender(); + cellRender.addNotify(); + } } public void removeNotify() { @@ -494,5 +503,171 @@ Node n = (Node)k; return new Node[] { new FNode(n, new FChildren(n)) }; } + } + + private class JCBRender implements InvocationHandler { + + private final ListCellRenderer orig; + private boolean isHackable = true; + private boolean isThin = false; + private JPopupMenu popup; + private JList list; + private int height = 0; + private int width; + private Component comp; + private final JComboBox comboToHack; + private boolean debug = false; + + public JCBRender() { + this.comboToHack = NavigationView.this; + this.orig = comboToHack.getRenderer(); + this.width = comboToHack.getWidth(); + } + + public void addNotify() { + if (comboToHack.getRenderer() == orig) { + comboToHack.setRenderer(getProxy()); + this.width = comboToHack.getWidth(); + } + } + + public void removeNotify() { + comboToHack.setRenderer(orig); + } + + private int lastMaxWidth; + private boolean isReshaped = false; + private boolean isFirstRun = true; + public void processListCellRendererComponent(JList list, Object value, int index, Component comp) { + if (index < 0) return; + if (index == 0) { + popup = findPopupMenu(list); + this.list = list; + if (popup == null) { + //System.out.println("## is not hackable: " + this); + isHackable = false; + return; + } + height = 0; + if (isReshaped) { + this.lastMaxWidth = this.width; + isReshaped = false; + } + this.width = comboToHack.getWidth(); + if (lastMaxWidth < width) lastMaxWidth = width; + } + Dimension size = comp.getPreferredSize(); + height += size.height; + if (width < size.width) { + width = size.width; + this.comp = comp; + isThin = true; + } + //System.out.println("##process: idx:"+index+",last:" + lastMaxWidth + ",curr:" + width + ",size:"+size.width + ",isThin:" + isThin + ",model:"+(list.getModel().getSize() - 1)+", @" + System.identityHashCode(this)); + if (isThin && index == list.getModel().getSize() - 1 && width != lastMaxWidth || + isFirstRun && index == list.getModel().getSize() - 1) { + isReshaped = true; + isThin = false; + isFirstRun = false; + reshapePopup(); + height = 0; + } + } + + public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws Throwable { + Object res = null; + try { + res = method.invoke(orig, args); + if (isHackable && "getListCellRendererComponent".equals(method.getName())) { + Component c = (Component) res; + JList l = (JList) args[0]; + Object value = args[1]; + int index = ((Integer) args[2]).intValue(); + processListCellRendererComponent(l, value, index, c); + } + } catch (IllegalArgumentException ex) { + ex.printStackTrace(); + } catch (InvocationTargetException ex) { + Throwable t = ex.getCause(); + if (t != null) throw t; + else ex.printStackTrace(); + } + return res; + } + + + + void reshapePopup() { + int preferredWidth = Math.min(width + 20, getMaxWidth()); + int prefferedHeight = Math.min(height, getMaxHeight()); + + //System.out.println("revalidate: w: " + preferredWidth + ", h: " +prefferedHeight + ", @" + System.identityHashCode(this) + ", AWT: " +EventQueue.isDispatchThread()); + this.comp = null; + popup.removeAll(); + scroller.remove(list); + popup.setPopupSize(preferredWidth, prefferedHeight); + popup.add(createScroller()); + popup.revalidate(); + popup.repaint(); + } + + private JScrollPane scroller; + + private JPopupMenu findPopupMenu(Component c) { + Component parent = c; + Component previous = c; + while (parent != null && !(parent instanceof JPopupMenu)) { + previous = parent; + parent = parent.getParent(); + } + + if (previous instanceof JScrollPane) { + scroller = (JScrollPane) previous; + } else { + parent = null; // unsupported component hierarchy + } + return (JPopupMenu) parent; + } + /** + * Creates the scroll pane which houses the scrollable list. + */ + private JScrollPane createScroller() { + JScrollPane scroller = new JScrollPane(list, + ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + scroller.setFocusable(false); + scroller.getVerticalScrollBar().setFocusable(false); + scroller.setBorder(null); + return scroller; + } + + private int maxWidth = -1; + private int maxHeight = -1; + + private int getMaxWidth() { + TopComponent tc = NavigationView.this.findParentTopComponent(); + if (tc != null) { + maxWidth = tc.getX() + tc.getWidth() - comboToHack.getX(); + } else { + maxWidth = Toolkit.getDefaultToolkit().getScreenSize().width >> 1; + } + return maxWidth; + } + + private int getMaxHeight() { + TopComponent tc = NavigationView.this.findParentTopComponent(); + if (tc != null) { + maxHeight = tc.getY() + tc.getHeight() - comboToHack.getY() - comboToHack.getHeight(); + } else { + maxHeight = Toolkit.getDefaultToolkit().getScreenSize().height >> 1; + } + return maxHeight; + } + + public ListCellRenderer getProxy() { + return (ListCellRenderer) Proxy.newProxyInstance( + this.getClass().getClassLoader(), new Class[] {ListCellRenderer.class}, this); + } + } }