? nbbuild/tests ? openide/src/org/openide/awt/HtmlRenderer.java Index: core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/DefaultTabLayoutModel.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/DefaultTabLayoutModel.java,v retrieving revision 1.4 diff -c -r1.4 DefaultTabLayoutModel.java *** core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/DefaultTabLayoutModel.java 19 Jan 2004 19:30:01 -0000 1.4 --- core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/DefaultTabLayoutModel.java 9 Mar 2004 23:25:39 -0000 *************** *** 28,33 **** --- 28,34 ---- import javax.swing.event.*; import javax.swing.*; import org.openide.ErrorManager; + import org.openide.awt.HtmlRenderer; /** Default implementation of TabLayoutModel. Simply provides a series of rectangles * for each tab starting at 0 and ending at the last element, with the width set to the *************** *** 53,73 **** private Font getFont() { return renderTarget.getFont(); } ! ! private FontMetrics getFontMetrics() { ! return getGraphics().getFontMetrics(getFont()); ! } ! ! private static Graphics2D getGraphics() { ! /// Graphics2D result = (Graphics2D) renderTarget.getGraphics(); ! ! //Allow calculation of width before display ! // if (result == null) { ! Graphics2D result = (Graphics2D) TabTable.getOffscreenGraphics(); ! // } ! return result; ! } ! protected int iconWidth (int index) { Icon ic = model.getTab(index).getIcon (); int result; --- 54,60 ---- private Font getFont() { return renderTarget.getFont(); } ! protected int iconWidth (int index) { Icon ic = model.getTab(index).getIcon (); int result; *************** *** 78,118 **** } return result; } ! ! /** [dafe] commented out, is useless probably here */ ! /*protected int iconHeight (int index) { ! Icon ic = model.getTab(index).getIcon (); ! int result; ! if (ic != null) { ! result = ic.getIconHeight(); ! } else { ! result = 0; ! } ! return result; ! }*/ ! ! /** ! */ protected int textWidth (int index) { - //XXX eventually keep an array of widths and init insertions with - //-1, calculate value only if stored value is -1 try { ! String text = model.getTab(index).getText(); ! return textWidth (text); ! /* ! double wid = Utils.renderString (text, ! getGraphics(), 0, ! 0, Integer.MAX_VALUE, Integer.MAX_VALUE, getFont(), Color.BLACK, ! Utils.STYLE_TRUNCATE, false); ! return new Double(wid).intValue(); ! */ } catch (NullPointerException npe) { IllegalArgumentException iae = new IllegalArgumentException ("Error fetching width for tab " + //NOI18N index + " - model size is " + model.size() + " TabData is " + //NOI18N model.getTab(index) + " model contents: " + model); //NOI18N ErrorManager.getDefault().annotate(iae, npe); ! throw iae; } } private static Map widthMap = new HashMap (30); --- 65,82 ---- } return result; } ! protected int textWidth (int index) { try { ! return textWidth (model.getTab(index).getText()); } catch (NullPointerException npe) { IllegalArgumentException iae = new IllegalArgumentException ("Error fetching width for tab " + //NOI18N index + " - model size is " + model.size() + " TabData is " + //NOI18N model.getTab(index) + " model contents: " + model); //NOI18N ErrorManager.getDefault().annotate(iae, npe); ! ErrorManager.getDefault().notify(iae); } + return 16; } private static Map widthMap = new HashMap (30); *************** *** 122,132 **** //dump it if the font changes. Integer result = (Integer) widthMap.get(text); if (result == null) { ! double wid = Utils.renderString (text, ! getGraphics(), 0, ! 0, Integer.MAX_VALUE, Integer.MAX_VALUE, getFont(), Color.BLACK, ! Utils.STYLE_TRUNCATE, false); ! result = new Integer (Math.round(Math.round(wid))); widthMap.put (text, result); } return result.intValue(); --- 86,94 ---- //dump it if the font changes. Integer result = (Integer) widthMap.get(text); if (result == null) { ! HtmlRenderer ren = HtmlRenderer.sharedInstance (); ! ren.setText (text); ! result = new Integer(ren.getPreferredSize().width); widthMap.put (text, result); } return result.intValue(); *************** *** 135,146 **** protected int textHeight (int index) { if (textHeight == -1) { //No need to calculate for every string ! String testStr = "Zgj"; //NOI18N ! Font f = getFont(); ! textHeight = new Double (f.getStringBounds ! (testStr, ! getGraphics().getFontRenderContext() ! ).getWidth()).intValue() + 2; } return textHeight; } --- 97,104 ---- protected int textHeight (int index) { if (textHeight == -1) { //No need to calculate for every string ! textHeight = ! HtmlRenderer.sharedInstance().getGraphics().getFontMetrics(getFont()).getHeight() + 2; } return textHeight; } *************** *** 158,164 **** } public int getH(int index) { - //return Math.max (textHeight(index) + padY, model.getTab(index).getIcon().getIconHeight() + padY); Insets insets = renderTarget.getInsets(); return renderTarget.getHeight() - (insets.bottom + insets.top); } --- 116,121 ---- Index: core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/OSXViewTabUI.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/OSXViewTabUI.java,v retrieving revision 1.10 diff -c -r1.10 OSXViewTabUI.java *** core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/OSXViewTabUI.java 25 Feb 2004 01:15:10 -0000 1.10 --- core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/OSXViewTabUI.java 9 Mar 2004 23:25:40 -0000 *************** *** 36,41 **** --- 36,42 ---- import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; + import org.openide.awt.HtmlRenderer; import org.netbeans.core.windows.view.ui.tabcontrol.plaf.GenericGlowingChicklet; /** A quickie view tabs ui for OS-X adapted from the view tabs *************** *** 135,146 **** int gap = 2; int iconX = x + width - (iconWidth + gap); - if (index == getDataModel().size() - 1) { iconX -= 3; textW -= 3; } - g.setColor(isSelected(index) && isActive() ? new Color (80, 80, 123) : new Color (110,120,120)); iconY-=2; //Only if we're painting, not using a bitmap iconX-=2; //Only if we're painting, not using a bitmap --- 136,145 ---- *************** *** 163,178 **** int textY; int textX = x + 5; textW -= 5; ! if (textHeight > height) { textY = (-1 * ((textHeight - height) / 2)) + fm.getAscent() - 1; } else { textY = (height / 2) - (textHeight / 2) + fm.getAscent() - 1; } ! ! Utils.renderString(text, g, textX, textY, textW, height, getTxtFont(), UIManager.getColor("textText"), ! Utils.STYLE_TRUNCATE, true); } //private static final JButton jb = new JButton(); --- 162,176 ---- int textY; int textX = x + 5; textW -= 5; ! if (textHeight > height) { textY = (-1 * ((textHeight - height) / 2)) + fm.getAscent() - 1; } else { textY = (height / 2) - (textHeight / 2) + fm.getAscent() - 1; } ! HtmlRenderer.renderString(text, g, textX, textY, textW, height, getTxtFont(), UIManager.getColor("textText"), ! HtmlRenderer.STYLE_TRUNCATE, true); } //private static final JButton jb = new JButton(); *************** *** 243,249 **** getControl().repaint(); } } ! private boolean isContainsMouse() { return containsMouse; } --- 241,247 ---- getControl().repaint(); } } ! private boolean isContainsMouse() { return containsMouse; } Index: core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/TabTable.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/TabTable.java,v retrieving revision 1.17 diff -c -r1.17 TabTable.java *** core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/TabTable.java 3 Mar 2004 13:50:25 -0000 1.17 --- core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/TabTable.java 9 Mar 2004 23:25:41 -0000 *************** *** 14,27 **** package org.netbeans.core.windows.view.ui.tabcontrol; import java.awt.*; - import java.awt.Component; - import java.awt.Dimension; - import java.awt.Font; - import java.awt.FontMetrics; - import java.awt.Graphics; - import java.awt.Point; import java.awt.event.*; - import java.awt.image.BufferedImage; import java.beans.*; import java.lang.ref.Reference; import java.lang.ref.SoftReference; --- 14,20 ---- *************** *** 30,72 **** import javax.swing.border.*; import java.util.EventObject; import java.util.List; ! import org.openide.util.Utilities; /** * A panel that displays a drop down list of items, in several columns if needed, ! * associated with a JTabbedPane * * @author Tim Boudreau */ public final class TabTable extends JTable implements MouseMotionListener, MouseListener { ! private static TabTable defaultInstance=null; /** Reference to container for which we display quicklist */ private TabbedContainer pane = null; /** Creates a new instance of TabListPanel */ private TabTable() { super (new TabTableModel()); ! setBorder (BorderFactory.createLineBorder(getForeground())); ! setColors(); } /** Sets right colors of the control. Colors should imitate colors of * drop-down list of combo box. */ ! private void setColors() { ! Color color = ColorUtil.get("ComboBox.background", Color.white); setBackground (color); //setGridColor(color); ! color = ColorUtil.get("ComboBox.foreground", Color.black); setForeground(color); ! color = ColorUtil.get("ComboBox.selectionBackground", Color.blue); setSelectionBackground(color); ! color = ColorUtil.get("ComboBox.selectionForeground", Color.white); setSelectionForeground(color); ! Font font = (Font)UIManager.get("ComboBox.font"); if (font != null) { setFont(font); } --- 23,91 ---- import javax.swing.border.*; import java.util.EventObject; import java.util.List; ! import org.openide.awt.HtmlRenderer; /** * A panel that displays a drop down list of items, in several columns if needed, ! * associated with a TabControl * * @author Tim Boudreau */ public final class TabTable extends JTable implements MouseMotionListener, MouseListener { ! /** Reference to the focus owner when addNotify was called. This is the ! * component that received the mouse event, so it's what we need to listen ! * on to update the selected cell as the user drags the mouse */ ! private Component invokingComponent = null; ! /** Cached preferred size value */ ! private Dimension prefSize = null; ! /** Flag indicating that the fixed row height has not yet been calculated - ! * this is for fontsize support */ ! boolean needCalcRowHeight = true; ! /** The default instance - there's only ever one shared between all ! * tab controls */ private static TabTable defaultInstance=null; /** Reference to container for which we display quicklist */ private TabbedContainer pane = null; + + /** Reference to the popup object currently showing the default instance, + * if it is visible */ + private static Popup currentPopup=null; + /** AWTEventListener which is attached when the popup is shown to ensure + * that it is closed when it should be. It is removed after the popup + * has been hidden. */ + private static AWTEventListener blistener = null; + /** Reference to the default shared instance */ + private static Reference instance=null; + /** Time of invocation, used to determine if a mouse release is + * delayed long enough from a mouse press that it should close + * the popup, instead of assuming the user wants move-and-click + * behavior instead of drag-and-click behavior */ + long invocationTime = -1; /** Creates a new instance of TabListPanel */ private TabTable() { super (new TabTableModel()); ! //Set up a line border around the edges ! setBorder (BorderFactory.createLineBorder(getForeground())); //XXX for all look and feels? ! initColors(); } /** Sets right colors of the control. Colors should imitate colors of * drop-down list of combo box. */ ! private void initColors() { ! Color color = ColorUtil.get("ComboBox.background", Color.white); //NOI18N setBackground (color); //setGridColor(color); ! color = ColorUtil.get("ComboBox.foreground", Color.black); //NOI18N setForeground(color); ! color = ColorUtil.get("ComboBox.selectionBackground", Color.blue); //NOI18N setSelectionBackground(color); ! color = ColorUtil.get("ComboBox.selectionForeground", Color.white); //NOI18N setSelectionForeground(color); ! Font font = (Font)UIManager.get("ComboBox.font"); //NOI18N if (font != null) { setFont(font); } *************** *** 74,79 **** --- 93,99 ---- setShowHorizontalLines(false); } + /** Overridden to provide keyboard support */ public void processKeyEvent (KeyEvent ke) { //Apparently the code that changes selection on enter is //not an action in JTable's action map. Override it entirely. *************** *** 116,213 **** } } } ! ! public static TabTable getDefault() { ! if (defaultInstance == null) { ! defaultInstance = new TabTable(); ! } ! return defaultInstance; ! } ! public void updateUI () { needCalcRowHeight = true; super.updateUI(); } public void setFont (Font f) { needCalcRowHeight = true; super.setFont (f); } ! public Component prepareRenderer(TableCellRenderer renderer, int row, int column) { ! Component result = super.prepareRenderer (renderer, row, column); ! TabData value = (TabData) getValueAt (row, column); boolean isSelTab = value == pane.getSelection(); result.setFont (isSelTab ? getFont().deriveFont (Font.BOLD) : getFont()); boolean sel = row == getSelectedRow() && column == getSelectedColumn() && value != null; if (sel) { result.setBackground(getSelectionBackground()); result.setForeground(getSelectionForeground()); } else { ! result.setBackground(getBackground()); ! result.setForeground(getForeground()); } return result; ! } ! ! ! boolean needCalcRowHeight = true; /** Calculate the height of rows based on the current font. This is ! * done when the first paint occurs, to ensure that a valid Graphics ! * object is available. ! * @since 1.25 */ private void calcRowHeight(Graphics g) { Font f = getFont(); FontMetrics fm = g.getFontMetrics(f); //As icons are displayed use maximum from font and icon height ! int rowHeight = Math.max(fm.getHeight(),16) + 4; needCalcRowHeight = false; setRowHeight(rowHeight); } ! /** Utility field used by event firing mechanism. */ ! private javax.swing.event.EventListenerList listenerList = null; ! ! public void attach(TabbedContainer cont) { prefSize = null; pane = cont; //Calc row height here so that TableModel can adjust number of columns. ! calcRowHeight(getOffscreenGraphics()); getTTModel().setRowHeight(getRowHeight()); getTTModel().attach(cont); ! synchronizeColumns(getTTModel().getColumnCount()); getSelectionModel().clearSelection(); getSelectionModel().setAnchorSelectionIndex(-1); getSelectionModel().setLeadSelectionIndex(-1); } ! static SoftReference ctx = null; ! /** Provides an offscreen graphics context so that widths based on character ! * size can be calculated correctly before the component is shown */ ! public static Graphics getOffscreenGraphics() { ! BufferedImage result = null; ! //XXX multi-monitors w/ different resolution may have problems; ! //Better to call Toolkit to create a screen graphics ! if (ctx != null) { ! result = (BufferedImage) ctx.get(); ! } ! if (result == null) { ! result = new BufferedImage (10, 10, BufferedImage.TYPE_INT_RGB); ! ctx = new SoftReference(result); ! } ! return result.getGraphics(); } ! private void synchronizeColumns(int count) { TableColumnModel mdl = getColumnModel(); int currCt = mdl.getColumnCount(); if (currCt < count) { for (int i=currCt; i < count; i++) { ! mdl.addColumn(new TableColumn(i, 75, getTTModel(), null)); } } else if (currCt > count) { for (int i=currCt-1; i >= count; i--) { --- 136,272 ---- } } } ! ! /** Overridden to set a flag to recalculate the required row height in ! * case the font changed */ public void updateUI () { needCalcRowHeight = true; super.updateUI(); } + /** Overridden to set a flag to recalcualte the required rwo height if + * the font changes */ public void setFont (Font f) { needCalcRowHeight = true; super.setFont (f); } ! ! /** Uses HtmlRenderer.sharedInstance(), the generic HTML table/tree/list ! * cell renderer for everything */ ! public TableCellRenderer getDefaultRenderer(Class columnClass) { ! return HtmlRenderer.sharedInstance(); ! } ! public Component prepareRenderer(TableCellRenderer renderer, int row, int column) { ! //Will always use the default instance of HtmlRenderer ! HtmlRenderer result = (HtmlRenderer) ! super.prepareRenderer (renderer, row, column); + //Find our TabData object + Object value = getTTModel().getValueAt(row, column); + if (value == null) { + //it's a filler space, we're done + return result; + } + + //Set up font, selection, icon, colors, borders boolean isSelTab = value == pane.getSelection(); result.setFont (isSelTab ? getFont().deriveFont (Font.BOLD) : getFont()); + result.setSelected(row == getSelectedRow() && + column == getSelectedColumn()); + + Icon icon = ((TabData) value).getIcon(); + + //Use icon text gap and borders to align the rows of icons and captions + //TabData uses a dummy 0 width icon, not null + if (icon.getIconWidth() > 0) { + //Max annotated icon width is 24, so to have all the text and all + //the icons come out aligned, set the icon text gap to the difference + result.setIconTextGap (28 - icon.getIconWidth()); + //Add a 2 pixel border so it's not flush, with an extra two pixel + //margin at the top and bottom + result.setBorder (BorderFactory.createEmptyBorder( + row == 0 ? 2 : 0, + 2, + row == getRowCount() - 1 ? 2 : 0, + 2)); + boolean sel = row == getSelectedRow() && column == getSelectedColumn() && value != null; if (sel) { result.setBackground(getSelectionBackground()); result.setForeground(getSelectionForeground()); + } } else { ! //If the icon width is 0, fill the space using a border, and add in ! //the extra two pixels for the first and last rows and 28 (24 for ! //the icon + the two we add above in the border) pixels on ! //the left to align the text ! result.setBorder (BorderFactory.createEmptyBorder( ! row == 0 ? 2 : 0, ! 30, ! row == getRowCount() - 1 ? 2 : 0, ! 2)); } + + result.setText(((TabData) value).getText()); + result.setIcon(icon); + + //The table may not really have focus, but it should always use the focus + //color for the selection, not controlShadow + result.setPaintAsFocused(true); + return result; ! } ! /** Calculate the height of rows based on the current font. This is ! * done when the first paint occurs. This allows the table to have ! * fixed height rows but adjust the fixed row height based on the ! * height of the current font. ! * */ private void calcRowHeight(Graphics g) { Font f = getFont(); FontMetrics fm = g.getFontMetrics(f); //As icons are displayed use maximum from font and icon height ! int rowHeight = Math.max(fm.getHeight(), 16) + 4; needCalcRowHeight = false; setRowHeight(rowHeight); } ! /** Prepare the tab table's model with data from a tabbed container, ! * it is about to be displayed */ ! private void attach(TabbedContainer cont) { prefSize = null; pane = cont; //Calc row height here so that TableModel can adjust number of columns. ! calcRowHeight(HtmlRenderer.sharedInstance().getGraphics()); getTTModel().setRowHeight(getRowHeight()); getTTModel().attach(cont); ! setupColumns(getTTModel().getColumnCount()); getSelectionModel().clearSelection(); getSelectionModel().setAnchorSelectionIndex(-1); getSelectionModel().setLeadSelectionIndex(-1); } ! /** Called after the component is hidden to dispose of any references to ! * the tab control it was invoked on */ ! private void detach() { ! pane = null; ! getTTModel().detach(); } ! /** Update the number of columns the column model has to match the ! * optimal number of columns for the the number of tabs we're ! * currently configured for */ ! private void setupColumns(int count) { TableColumnModel mdl = getColumnModel(); int currCt = mdl.getColumnCount(); if (currCt < count) { for (int i=currCt; i < count; i++) { ! mdl.addColumn(new TableColumn(i, 75, ! HtmlRenderer.sharedInstance(), null)); } } else if (currCt > count) { for (int i=currCt-1; i >= count; i--) { *************** *** 216,267 **** } } ! private Dimension prefSize = null; public Dimension getPreferredSize() { if (prefSize == null) { ! Dimension d = new Dimension(); int cols = getColumnCount(); int rows = getRowCount(); ! for (int i=0; i 0) && (getColumnCount() > 0)) { changeSelection(-1,-1,false,false); } EventObject eo = EventQueue.getCurrentEvent(); if (eo != null) { if (eo.getSource() instanceof Component) { --- 275,333 ---- } } ! /** Overridden to calculate a preferred size based on the current optimal ! * number of columns, and set up the preferred width for each column based ! * on the maximum width tab name & icon displayed in it */ public Dimension getPreferredSize() { if (prefSize == null) { ! prefSize = new Dimension(); int cols = getColumnCount(); int rows = getRowCount(); ! ! //Iterate the columns ! for (int i=0; i < cols; i++) { ! int columnWidth = 0; ! //For each column, iterate the rows ! for (int j=0; j < rows; j++) { TableCellRenderer ren = getCellRenderer(j,i); ! Component c = prepareRenderer (ren, j, i); ! //find the widest cell ! columnWidth = Math.max (c.getPreferredSize().width, ! columnWidth); } ! //Add in the max width needed for this column to the total ! //width ! prefSize.width += columnWidth; ! //Store it in the column model so it will be displayed with ! //the right width ! getColumnModel().getColumn(i).setPreferredWidth(columnWidth); } ! //Rows will be fixed height, so just multiply it out ! prefSize.height = rows * getRowHeight(); } return prefSize; } + /** Convenience getter to cast the table model to its implementation class */ private final TabTableModel getTTModel () { return (TabTableModel) getModel(); } ! /** Overridden to set up mouse listeners to track mouse movements while the ! * popup is shown */ public void addNotify() { super.addNotify(); + //Start listening to ourselves, always a nice thing to do addMouseListener(this); addMouseMotionListener(this); //Set initial selection if there is any field in table if ((getRowCount() > 0) && (getColumnCount() > 0)) { changeSelection(-1,-1,false,false); } + + //Attach a mouse listener to whatever component caused us to open. + //That is what we will need to listen to to update the table selection + //as the mouse is moved. EventObject eo = EventQueue.getCurrentEvent(); if (eo != null) { if (eo.getSource() instanceof Component) { *************** *** 275,295 **** } invocationTime = System.currentTimeMillis(); } - Component invokingComponent = null; - long invocationTime = -1; public void removeNotify() { super.removeNotify(); removeMouseListener(this); removeMouseMotionListener(this); if (invokingComponent != null) { invokingComponent.removeMouseListener(this); invokingComponent.removeMouseMotionListener(this); invokingComponent = null; } - detach(); } public void paint (Graphics g) { if (needCalcRowHeight) { calcRowHeight(g); --- 341,363 ---- } invocationTime = System.currentTimeMillis(); } + /** Overridden to remove the mouse listeners that were attached during + * addNotify */ public void removeNotify() { super.removeNotify(); removeMouseListener(this); removeMouseMotionListener(this); + + //Detach from the invoker if (invokingComponent != null) { invokingComponent.removeMouseListener(this); invokingComponent.removeMouseMotionListener(this); invokingComponent = null; } } + /** Overridden to ensure the row height will accomodate the font */ public void paint (Graphics g) { if (needCalcRowHeight) { calcRowHeight(g); *************** *** 297,302 **** --- 365,372 ---- super.paint(g); } + /** Convert an index in rows and columns into an index of a tab + * in the control we're representing */ int convertIndex (int row, int column) { return column * getRowCount() + row; } *************** *** 320,325 **** --- 390,397 ---- } public void mouseReleased(MouseEvent e) { + //The user selected something (depending on how it was invoked and the + //platform/L&F, this may be on mouse pressed or released if (e.getSource() == invokingComponent) { Point p = SwingUtilities.convertPoint((Component) e.getSource(), e.getPoint(), this); *************** *** 342,347 **** --- 414,420 ---- //MouseMotionListener public void mouseDragged(MouseEvent e) { + //Handle the same as a mouse motion event mouseMoved(e); e.consume(); } *************** *** 353,358 **** --- 426,432 ---- p = SwingUtilities.convertPoint((Component) e.getSource(), p, this); } + //Set the selection or clear it if (this.contains(p)) { int row = rowAtPoint(p); int col = columnAtPoint(p); *************** *** 381,397 **** //show it currentPopup.show(); //Use an AWT listener to hide it in certain circumstances ! blistener = new BackupListener(tt); } /** Focus changes are occasionally missed if the editor has focus while * the tab table is active. This listener ensures that any mouse event * on it will indeed close the component. */ ! private static class BackupListener implements AWTEventListener { ! //XXX could consolidate the property change listener above into this ! //and just have one listener class. private TabTable tt; ! public BackupListener (TabTable tt) { this.tt = tt; Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.MOUSE_EVENT_MASK | AWTEvent.KEY_EVENT_MASK); --- 455,469 ---- //show it currentPopup.show(); //Use an AWT listener to hide it in certain circumstances ! blistener = new PopupCloser(tt); } /** Focus changes are occasionally missed if the editor has focus while * the tab table is active. This listener ensures that any mouse event * on it will indeed close the component. */ ! private static class PopupCloser implements AWTEventListener { private TabTable tt; ! public PopupCloser (TabTable tt) { this.tt = tt; Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.MOUSE_EVENT_MASK | AWTEvent.KEY_EVENT_MASK); *************** *** 440,459 **** } ! private static Popup currentPopup=null; ! private static PropertyChangeListener listener = null; ! private static BackupListener blistener = null; public synchronized static void hideCurrentPopup() { if (currentPopup != null) { currentPopup.hide(); currentPopup = null; } if (blistener != null) { Toolkit.getDefaultToolkit().removeAWTEventListener(blistener); } } ! private static Reference instance=null; private static TabTable sharedInstance() { TabTable result=null; if (instance != null) { --- 512,549 ---- } ! /** Hides the currently displayed tab popup, if any, clearing its references ! * to the outside world, and removing any remaining listeners */ public synchronized static void hideCurrentPopup() { if (currentPopup != null) { + //Close the popup currentPopup.hide(); + + //Actually look it up via the reference, on the off chance gc + //ran after currentPopup.hide() was called. No sense in creating + //a new instance just to call detach on it. + TabTable tt = null; + if (instance != null) { + tt = (TabTable) instance.get(); + } + if (tt != null) { + tt.detach(); + } + //Null out the current popup so the next call to invoke will + //open a new one currentPopup = null; } + //Make sure we get rid of our AWT event listener if (blistener != null) { Toolkit.getDefaultToolkit().removeAWTEventListener(blistener); + //Null it out, it's cheap and memory isn't + blistener = null; } } ! ! /** Fetch a shared instance of TabTable - only one is needed for any ! * number of tab controls. */ private static TabTable sharedInstance() { TabTable result=null; if (instance != null) { Index: core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/TabTableModel.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/TabTableModel.java,v retrieving revision 1.6 diff -c -r1.6 TabTableModel.java *** core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/TabTableModel.java 19 Jan 2004 19:30:02 -0000 1.6 --- core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/TabTableModel.java 9 Mar 2004 23:25:42 -0000 *************** *** 13,45 **** package org.netbeans.core.windows.view.ui.tabcontrol; - import java.awt.Color; - //import java.awt.*; - import java.awt.Component; - import java.awt.Dimension; - import java.awt.Font; - import java.awt.FontMetrics; - import java.awt.Graphics; - import java.awt.Graphics2D; - import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; import javax.swing.table.*; - import java.util.Arrays; import java.util.ArrayList; import java.util.Collections; - import java.util.HashSet; import java.util.List; - import java.util.Set; - import javax.swing.plaf.metal.MetalLookAndFeel; - - import org.openide.util.Utilities; ! /** Table model that wraps the tabs in a Tabbed control * * @author Tim Boudreau */ ! class TabTableModel implements TableModel, TableCellRenderer { private transient TabbedContainer pane = null; private transient ArrayList tableModelListenerList; /** Used to estimate number of cells fitting to given space. */ --- 13,33 ---- package org.netbeans.core.windows.view.ui.tabcontrol; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; import javax.swing.table.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; ! /** Table model that wraps the tabs in a Tabbed control. In particular, this ! * model will have a varying (but hopefully optimal) number of columns ! * depending on the number of tabs it is asked to display, so as to not ! * run offscreen. * * @author Tim Boudreau */ ! class TabTableModel implements TableModel { private transient TabbedContainer pane = null; private transient ArrayList tableModelListenerList; /** Used to estimate number of cells fitting to given space. */ *************** *** 71,76 **** --- 59,65 ---- } this.pane = c; int count = pane.getModel().size(); + //Calculate an optimal number of columns int colCount = calcColumns (count); int rowCount = count / colCount; //avoid div by 0 *************** *** 80,87 **** } else { setRowsAndColumns(0,0); } ! //Duplicate data model and sort it by text (TopComponent name) ! tabs.clear(); tabs.addAll(pane.getModel().getTabs()); Collections.sort(tabs); } --- 69,76 ---- } else { setRowsAndColumns(0,0); } ! //Duplicate data model and sort it by text (TopComponent name). TabData ! //implements Comparable tabs.addAll(pane.getModel().getTabs()); Collections.sort(tabs); } *************** *** 98,103 **** --- 87,93 ---- int result = (count / nRow) + 1; //Old code + //FYI, the code below reduces wasted space by using multiples of 2 or //3 columns depending on which the number of items we have to display //divides into with a smaller remainder. Not sure why it was commented *************** *** 116,122 **** result = 3; } }*/ - return result; } --- 106,111 ---- *************** *** 126,132 **** } public Class getColumnClass(int columnIndex) { ! return Integer.class; } public String getColumnName(int columnIndex) { --- 115,121 ---- } public Class getColumnClass(int columnIndex) { ! return TabData.class; } public String getColumnName(int columnIndex) { *************** *** 142,149 **** } public Object getValueAt(int rowIndex, int columnIndex) { ! if ((rowIndex == -1) || (columnIndex == -1)) { ! return new Integer (-1); } else { int mdlIdx = (columnIndex * getRowCount()) + rowIndex; if (mdlIdx < tabs.size()) { --- 131,138 ---- } public Object getValueAt(int rowIndex, int columnIndex) { ! if (rowIndex == -1 || columnIndex == -1) { ! return null; } else { int mdlIdx = (columnIndex * getRowCount()) + rowIndex; if (mdlIdx < tabs.size()) { *************** *** 199,204 **** --- 188,196 ---- public int getColumnCount() { return cols; } + /* + <<<<<<< TabTableModel.java + ======= public Component getTableCellRendererComponent (JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { *************** *** 308,313 **** --- 300,306 ---- return fh; } } + */ /** Utility field used by event firing mechanism. */ private javax.swing.event.EventListenerList listenerList = null; Index: core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/AbstractTabRenderer.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/AbstractTabRenderer.java,v retrieving revision 1.16 diff -c -r1.16 AbstractTabRenderer.java *** core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/AbstractTabRenderer.java 27 Feb 2004 00:38:56 -0000 1.16 --- core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/AbstractTabRenderer.java 9 Mar 2004 23:25:44 -0000 *************** *** 32,42 **** import java.awt.event.HierarchyListener; import java.awt.event.MouseEvent; import javax.swing.Icon; - import javax.swing.JComponent; - import javax.swing.JLabel; import javax.swing.UIManager; import org.netbeans.core.windows.view.ui.tabcontrol.*; /** Base class for tab renderers for the tab control. This is a support class * which will allow authors who want to provide a different look or behavior --- 32,41 ---- import java.awt.event.HierarchyListener; import java.awt.event.MouseEvent; import javax.swing.Icon; import javax.swing.UIManager; import org.netbeans.core.windows.view.ui.tabcontrol.*; + import org.openide.awt.HtmlRenderer; /** Base class for tab renderers for the tab control. This is a support class * which will allow authors who want to provide a different look or behavior *************** *** 49,55 **** * * @author Tim Boudreau */ ! public abstract class AbstractTabRenderer extends JLabel implements TabCellRenderer { private int state=TabsUI2.TABSTATE_NOT_ONSCREEN; Painter leftBorder; Painter rightBorder; --- 48,54 ---- * * @author Tim Boudreau */ ! public abstract class AbstractTabRenderer extends HtmlRenderer implements TabCellRenderer { private int state=TabsUI2.TABSTATE_NOT_ONSCREEN; Painter leftBorder; Painter rightBorder; *************** *** 134,140 **** /** Convenience getter to determine if the current state includes the * selected state (the tab this component is currently configured to * render is the selected tab in a container) */ ! protected final boolean isSelected() { return (state & TabsUI2.TABSTATE_SELECTED) != 0; } --- 133,139 ---- /** Convenience getter to determine if the current state includes the * selected state (the tab this component is currently configured to * render is the selected tab in a container) */ ! public final boolean isSelected() { return (state & TabsUI2.TABSTATE_SELECTED) != 0; } *************** *** 332,341 **** //do nothing - performance } - public void validate() { - //do nothing - performance - } - public void repaint (long tm) { //do nothing - performance } --- 331,336 ---- *************** *** 344,353 **** //do nothing - performance } - protected final void firePropertyChange (String s, Object a, Object b) { - //do nothing - performance - } - public final void addHierarchyBoundsListener(HierarchyBoundsListener hbl) { //do nothing } --- 339,344 ---- *************** *** 384,399 **** } } ! /** Overridden to paint the interior of the polygon if using Painter */ ! public void paintComponent(Graphics g) { g.setColor(getBackground()); if (getBorder() instanceof Painter) { ((Painter)getBorder()).paintInterior(g, this); } - //use our own HTML painting algorithm. Also ensures that tops of - //icons are not cut off - JLabel's icon baseline is that of the - //text, we want it centered in the available space. - paintIconAndText(g); } protected int getPaintYAdjust() { --- 375,385 ---- } } ! public void paintBackground (Graphics g) { g.setColor(getBackground()); if (getBorder() instanceof Painter) { ((Painter)getBorder()).paintInterior(g, this); } } protected int getPaintYAdjust() { *************** *** 412,419 **** return 0; } ! ! private void paintIconAndText(Graphics g) { g.setFont(getFont()); FontMetrics fm = g.getFontMetrics(getFont()); //Find out what height we need --- 398,406 ---- return 0; } ! protected void paintIconAndText(Graphics g) { ! //XXX eventually support "pre-truncation" directly in HtmlRenderer and ! //delete this method g.setFont(getFont()); FontMetrics fm = g.getFontMetrics(getFont()); //Find out what height we need *************** *** 444,453 **** } int iconX = ins.left + centeringToAdd; - iconY--; //XXX compensate for extra pixel vs. spec. fix the math above... - iconY += getIconYAdjust(); - icon.paintIcon(this, g, iconX, iconY); txtX = iconX + icon.getIconWidth() + getIconTextGap(); } else { --- 431,437 ---- *************** *** 468,474 **** if (isClipLeft()) { //fiddle with the string to get "...blah" String s = preTruncateString(getText(), g, txtW-4); //subtract 4 so it's not flush w/ tab edge ! Utils.renderString(s, g, txtX, txtY, txtW, txtH, getFont(), getForeground(), Utils.STYLE_CLIP, true); } else { String s; if (isClipRight()) { --- 452,459 ---- if (isClipLeft()) { //fiddle with the string to get "...blah" String s = preTruncateString(getText(), g, txtW-4); //subtract 4 so it's not flush w/ tab edge ! renderString(s, g, txtX, txtY, txtW, txtH, getFont(), ! getForeground(), HtmlRenderer.STYLE_CLIP, true); } else { String s; if (isClipRight()) { *************** *** 478,488 **** } else { s = getText(); } ! Utils.renderString(s, g, txtX, txtY, txtW, txtH, getFont(), getForeground(), Utils.STYLE_TRUNCATE, true); } } ! static String preTruncateString(String s, Graphics g, int availPixels) { if (s.length() < 3) { return s; } --- 463,477 ---- } else { s = getText(); } ! renderString(s, g, txtX, txtY, txtW, txtH, getFont(), ! getForeground(), HtmlRenderer.STYLE_TRUNCATE, true); } } ! static String preTruncateString(String s, Graphics g, int availPixels) { + //XXX if we want to really use HTML in tab titles, the method needs + //to preserve HTML tags prior to the point at which we start + //painting. The real way to do this is have the HTML renderer do it. if (s.length() < 3) { return s; } *************** *** 624,646 **** } return result; } ! ! /** Useful for logging */ ! private static final String polyToString(Polygon p) { ! //XXX delete me ! int[] x = p.xpoints; ! int[] y = p.ypoints; ! StringBuffer sb = new StringBuffer(); ! for (int i=0; i < p.npoints; i++) { ! sb.append('('); ! sb.append(x[i]); ! sb.append(','); ! sb.append(y[i]); ! sb.append(')'); ! } ! return sb.toString(); ! } ! public boolean eventOccuredInCloseButton(MouseEvent me, int tabState, Rectangle bounds) { return isInCloseButton (me.getPoint(), bounds); } --- 613,619 ---- } return result; } ! public boolean eventOccuredInCloseButton(MouseEvent me, int tabState, Rectangle bounds) { return isInCloseButton (me.getPoint(), bounds); } *************** *** 657,663 **** protected boolean inCloseButton() { return (getLastKnownState() & TabsUI2.TABSTATE_IN_CLOSEBUTTON) != 0; ! } /** Default implementation returns 0 */ public int getPixelsToAddToSelection() { return 0; --- 630,637 ---- protected boolean inCloseButton() { return (getLastKnownState() & TabsUI2.TABSTATE_IN_CLOSEBUTTON) != 0; ! } ! /** Default implementation returns 0 */ public int getPixelsToAddToSelection() { return 0; Index: core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/MetalTabsRenderer.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/MetalTabsRenderer.java,v retrieving revision 1.10 diff -c -r1.10 MetalTabsRenderer.java *** core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/MetalTabsRenderer.java 27 Feb 2004 17:17:54 -0000 1.10 --- core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/MetalTabsRenderer.java 9 Mar 2004 23:25:45 -0000 *************** *** 44,65 **** setBorder (metalborder); } ! /** Convenience repaint method - default implementation returns false */ protected boolean repaintOnMousePressed(MouseEvent me, int state) { return true;//!isSelected(); } ! /** Convenience repaint method - default implementation returns false */ protected boolean repaintOnMouseReleased(MouseEvent me, int state) { return true;//!isPressed(); } ! /** Convenience repaint method - default implementation returns false */ protected boolean repaintOnMouseEntered(MouseEvent me, int state) { return true; } ! /** Convenience repaint method - default implementation returns false */ protected boolean repaintOnMouseExited(MouseEvent me, int state) { return true; } --- 44,65 ---- setBorder (metalborder); } ! /** Convenience repaint method - default implementation returns true */ protected boolean repaintOnMousePressed(MouseEvent me, int state) { return true;//!isSelected(); } ! /** Convenience repaint method - default implementation returns true */ protected boolean repaintOnMouseReleased(MouseEvent me, int state) { return true;//!isPressed(); } ! /** Convenience repaint method - default implementation returns true */ protected boolean repaintOnMouseEntered(MouseEvent me, int state) { return true; } ! /** Convenience repaint method - default implementation returns true */ protected boolean repaintOnMouseExited(MouseEvent me, int state) { return true; } Index: core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/OSXTabCellRenderer.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/OSXTabCellRenderer.java,v retrieving revision 1.4 diff -c -r1.4 OSXTabCellRenderer.java *** core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/OSXTabCellRenderer.java 25 Feb 2004 01:15:11 -0000 1.4 --- core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/OSXTabCellRenderer.java 9 Mar 2004 23:25:47 -0000 *************** *** 49,55 **** import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.UIManager; ! import org.netbeans.core.windows.view.ui.tabcontrol.Utils; /** A tab cell renderer for OS-X. Basically does its work by subclassing * JButton and doing some tricks to use it as a cell renderer, so the Aqua --- 49,55 ---- import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.UIManager; ! import org.openide.awt.HtmlRenderer; /** A tab cell renderer for OS-X. Basically does its work by subclassing * JButton and doing some tricks to use it as a cell renderer, so the Aqua *************** *** 342,353 **** --- 342,382 ---- upper.lineTo (rightEnd, bottom); } + <<<<<<< OSXTabCellRenderer.java + if (selected() && isActive()) { + txtY += 1; + txtX += 2; + } + txtX -=5; + + //Get the available horizontal pixels for text + int txtW = getWidth() - (txtX + ins.right); + if (isClipLeft()) { + //fiddle with the string to get "...blah" + String s = AbstractTabRenderer.preTruncateString(getText(), g, txtW-4); //subtract 4 so it's not flush w/ tab edge + HtmlRenderer.renderString(s, g, txtX, txtY, txtW, txtH, getFont(), + getForeground(), HtmlRenderer.STYLE_CLIP, true); + } else { + String s; + if (isClipRight()) { + //Jano wants to always show a "..." for cases where a tab is truncated, + //even if we've really painted all the text. + s = getText() + "..."; //NOI18N + } else { + s = getText(); + ======= upper.lineTo (leftNotch, bottom); if (curveLeft) { if (leftClip) { upper.curveTo (backin, bottom, backin, upperStopLeft, 0, upperStopLeft); + >>>>>>> 1.4 } + <<<<<<< OSXTabCellRenderer.java + HtmlRenderer.renderString(s, g, txtX, txtY, txtW, txtH, getFont(), + getForeground(), HtmlRenderer.STYLE_TRUNCATE, true); + ======= upper.curveTo (leftNotch, upperStopLeft, 0, top, arcstart, top); + >>>>>>> 1.4 } upper.closePath(); Index: core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/OSXTabsUI.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/OSXTabsUI.java,v retrieving revision 1.9 diff -c -r1.9 OSXTabsUI.java *** core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/OSXTabsUI.java 1 Mar 2004 03:33:09 -0000 1.9 --- core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/OSXTabsUI.java 9 Mar 2004 23:25:48 -0000 *************** *** 55,60 **** --- 55,61 ---- import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.plaf.ComponentUI; + import org.openide.awt.HtmlRenderer; import org.netbeans.core.windows.view.ui.tabcontrol.ColorUtil; import org.netbeans.core.windows.view.ui.tabcontrol.DefaultTabLayoutModel; import org.netbeans.core.windows.view.ui.tabcontrol.TabData; *************** *** 117,123 **** int prefHeight=28; //Never call getGraphics() on the control, it resets in-process //painting on OS-X 1.4.1 and triggers gratuitous repaints ! Graphics g = TabTable.getOffscreenGraphics();//control.getGraphics(); if (g != null) { FontMetrics fm = g.getFontMetrics(control.getFont()); Insets ins = getTabAreaInsets(); --- 118,124 ---- int prefHeight=28; //Never call getGraphics() on the control, it resets in-process //painting on OS-X 1.4.1 and triggers gratuitous repaints ! Graphics g = HtmlRenderer.sharedInstance().getGraphics(); if (g != null) { FontMetrics fm = g.getFontMetrics(control.getFont()); Insets ins = getTabAreaInsets(); Index: core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/WinClassicTabRenderer.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/WinClassicTabRenderer.java,v retrieving revision 1.8 diff -c -r1.8 WinClassicTabRenderer.java *** core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/WinClassicTabRenderer.java 27 Feb 2004 00:38:56 -0000 1.8 --- core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/WinClassicTabRenderer.java 9 Mar 2004 23:25:49 -0000 *************** *** 49,55 **** public Color getSelectedForeground() { return ColorUtil.get("textText", Color.BLACK); //NOI18N } ! public Color getForeground() { return getSelectedForeground(); } --- 49,55 ---- public Color getSelectedForeground() { return ColorUtil.get("textText", Color.BLACK); //NOI18N } ! public Color getForeground() { return getSelectedForeground(); } Index: core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/WinClassicTabsUI.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/WinClassicTabsUI.java,v retrieving revision 1.8 diff -c -r1.8 WinClassicTabsUI.java *** core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/WinClassicTabsUI.java 27 Feb 2004 00:38:56 -0000 1.8 --- core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/WinClassicTabsUI.java 9 Mar 2004 23:25:49 -0000 *************** *** 32,37 **** --- 32,38 ---- import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComponent; + import javax.swing.UIManager; import org.netbeans.core.windows.view.ui.tabcontrol.ColorUtil; /** Windows classic impl of tabs ui Index: core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/WinXPTabsUI.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/WinXPTabsUI.java,v retrieving revision 1.12 diff -c -r1.12 WinXPTabsUI.java *** core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/WinXPTabsUI.java 27 Feb 2004 17:17:54 -0000 1.12 --- core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/plaf/WinXPTabsUI.java 9 Mar 2004 23:25:50 -0000 *************** *** 175,181 **** i <= scroll().getLastVisibleTab(tabsWidth) && !scroll().isLastTabClipped()) && i >= scroll().getFirstVisibleTab(tabsWidth)); ! g.setColor(ColorUtil.get("controlLtHighlight", Color.WHITE)); if (needSplitLine) { //Find the rectangle of the selection to skip it getTabRect(i, scratch5); --- 175,181 ---- i <= scroll().getLastVisibleTab(tabsWidth) && !scroll().isLastTabClipped()) && i >= scroll().getFirstVisibleTab(tabsWidth)); ! g.setColor(ColorUtil.get("controlLtHighlight", Color.WHITE)); //XXX don't hardcode colors if (needSplitLine) { //Find the rectangle of the selection to skip it getTabRect(i, scratch5); Index: openide/loaders/src/org/openide/loaders/DataNode.java =================================================================== RCS file: /cvs/openide/loaders/src/org/openide/loaders/DataNode.java,v retrieving revision 1.9 diff -c -r1.9 DataNode.java *** openide/loaders/src/org/openide/loaders/DataNode.java 27 Feb 2004 18:27:27 -0000 1.9 --- openide/loaders/src/org/openide/loaders/DataNode.java 9 Mar 2004 23:27:03 -0000 *************** *** 22,27 **** --- 22,28 ---- import org.openide.ErrorManager; import org.openide.filesystems.*; + import org.openide.filesystems.FileSystem.HtmlStatus; import org.openide.util.datatransfer.*; import org.openide.util.HelpCtx; import org.openide.util.RequestProcessor; *************** *** 149,154 **** --- 150,185 ---- return s; } + + + /** Get a display name formatted using the limited HTML subset supported + * by HtmlRenderer. If the underlying + * FileSystem.Status is an instance of HmlStatus, + * this method will return non-null if status information is added. + * + * @return a string containing compliant HTML markup or null + * @see org.openide.awt.HtmlRenderer + * @see org.openide.nodes.Node.getHtmlDisplayName */ + public String getHtmlDisplayName() { + try { + FileSystem.Status stat = + obj.getPrimaryFile().getFileSystem().getStatus(); + if (stat instanceof HtmlStatus) { + HtmlStatus hstat = (HtmlStatus) stat; + + String result = hstat.annotateNameHtml ( + super.getDisplayName(), new LazyFilesSet()); + + //Make sure the super string was really modified + if (!super.getDisplayName().equals(result)) { + return result; + } + } + } catch (FileStateInvalidException e) { + //do nothing and fall through + } + return super.getHtmlDisplayName(); + } /** Get the displayed icon for this node. * A filesystem may {@link org.openide.filesystems.FileSystem#getStatus specially alter} this. Index: openide/src/org/openide/explorer/propertysheet/ComboInplaceEditor.java =================================================================== RCS file: /cvs/openide/src/org/openide/explorer/propertysheet/ComboInplaceEditor.java,v retrieving revision 1.21 diff -c -r1.21 ComboInplaceEditor.java *** openide/src/org/openide/explorer/propertysheet/ComboInplaceEditor.java 1 Mar 2004 05:14:00 -0000 1.21 --- openide/src/org/openide/explorer/propertysheet/ComboInplaceEditor.java 9 Mar 2004 23:28:05 -0000 *************** *** 21,26 **** --- 21,27 ---- import javax.swing.plaf.ComboBoxUI; import javax.swing.plaf.metal.MetalLookAndFeel; import javax.swing.text.JTextComponent; + import org.openide.awt.HtmlRenderer; import org.openide.explorer.propertysheet.editors.EnhancedPropertyEditor; /** A combo box inplace editor. Does a couple of necessary things: *************** *** 60,66 **** * less borders & such */ public ComboInplaceEditor(boolean tableUI) { if (tableUI) { ! putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE); } if (Boolean.getBoolean("netbeans.ps.combohack")) { //NOI18N setLightWeightPopupEnabled(false); --- 61,67 ---- * less borders & such */ public ComboInplaceEditor(boolean tableUI) { if (tableUI) { ! putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE); //NOI18N } if (Boolean.getBoolean("netbeans.ps.combohack")) { //NOI18N setLightWeightPopupEnabled(false); *************** *** 73,78 **** --- 74,95 ---- updateUI(); } } + + /** Uses the shared instance of the fast HTML renderer */ + public ListCellRenderer getRenderer() { + ListCellRenderer result = HtmlRenderer.sharedInstance(); + ((JComponent) result).setBorder (BorderFactory.createEmptyBorder ( + 0, + PropUtils.getTextMargin(), + 0, + 0)); + //Popups should paint as the focused component even though the combo + //is what has focus + ((HtmlRenderer) result).setPaintAsFocused(true); + ((HtmlRenderer) result).setIcon(null); + ((HtmlRenderer) result).setOpaque(false); + return result; + } /** Overridden to add a listener to the editor if necessary, since the * UI won't do that for us without a focus listener */ Index: openide/src/org/openide/explorer/propertysheet/PropUtils.java =================================================================== RCS file: /cvs/openide/src/org/openide/explorer/propertysheet/PropUtils.java,v retrieving revision 1.33 diff -c -r1.33 PropUtils.java *** openide/src/org/openide/explorer/propertysheet/PropUtils.java 29 Feb 2004 03:56:21 -0000 1.33 --- openide/src/org/openide/explorer/propertysheet/PropUtils.java 9 Mar 2004 23:28:09 -0000 *************** *** 35,40 **** --- 35,41 ---- import org.netbeans.modules.openide.explorer.PsSettings; import org.openide.nodes.Node.*; import org.openide.*; + import org.openide.awt.HtmlRenderer; import org.openide.util.*; import org.openide.nodes.*; *************** *** 326,349 **** /** Get a scratch graphics object which can be used to calculate string * widths offscreen */ static Graphics getScratchGraphics(Component c) { ! //OS-X 1.4.1 calling getGraphics() can cause cyclic repaints ! //if called while painting. Safer to use an offscreen, cached ! //resource, probably everywhere. ! Graphics result = null; //c.getGraphics(); ! if (result == null) { ! //xxx this grabs the AWT tree lock ! /* ! result = ! GraphicsEnvironment.getLocalGraphicsEnvironment ! ().getDefaultScreenDevice().getDefaultConfiguration ! ().createCompatibleImage(1,1).getGraphics(); ! */ ! if (scratch == null) { ! scratch = new BufferedImage(1,1,BufferedImage.TYPE_INT_ARGB); ! } ! result = scratch.getGraphics(); ! } ! return result; } --- 327,333 ---- /** Get a scratch graphics object which can be used to calculate string * widths offscreen */ static Graphics getScratchGraphics(Component c) { ! return HtmlRenderer.sharedInstance().getGraphics(); } Index: openide/src/org/openide/explorer/propertysheet/RendererFactory.java =================================================================== RCS file: /cvs/openide/src/org/openide/explorer/propertysheet/RendererFactory.java,v retrieving revision 1.10 diff -c -r1.10 RendererFactory.java *** openide/src/org/openide/explorer/propertysheet/RendererFactory.java 25 Feb 2004 00:45:00 -0000 1.10 --- openide/src/org/openide/explorer/propertysheet/RendererFactory.java 9 Mar 2004 23:28:12 -0000 *************** *** 21,30 **** import java.awt.Color; import java.awt.Component; import java.awt.Dimension; - import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Image; - import java.awt.Insets; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; --- 21,28 ---- *************** *** 47,54 **** import javax.swing.border.BevelBorder; import javax.swing.border.Border; import javax.swing.event.ChangeListener; - import javax.swing.table.DefaultTableCellRenderer; import org.openide.ErrorManager; import org.openide.nodes.Node.Property; import org.openide.util.Utilities; --- 45,52 ---- import javax.swing.border.BevelBorder; import javax.swing.border.Border; import javax.swing.event.ChangeListener; import org.openide.ErrorManager; + import org.openide.awt.HtmlRenderer; import org.openide.nodes.Node.Property; import org.openide.util.Utilities; *************** *** 236,246 **** return lbl; } ! public JLabel getStringRenderer() { StringRenderer result = stringRenderer(); result.clear(); result.setEnabled(true); ! return (JLabel) result; } private JComponent prepareRadioButtons(PropertyEditor editor, PropertyEnv env) { --- 234,244 ---- return lbl; } ! public HtmlRenderer getStringRenderer() { StringRenderer result = stringRenderer(); result.clear(); result.setEnabled(true); ! return result; } private JComponent prepareRadioButtons(PropertyEditor editor, PropertyEnv env) { *************** *** 426,439 **** /** Overridden only fire those properties needed */ protected void firePropertyChange(String name, Object old, Object nue) { ! //gtk L&F needs these, although bg and fg don't work on it in 1.4.2 ! /* ! if ("foreground".equals(name) || ! "background".equals(name) || "font".equals(name) || ! "editable".equals(name) || "enabled".equals(name)) { //NOI18N ! super.firePropertyChange(name, old, nue); ! } ! */ super.firePropertyChange(name,old,nue); } --- 424,430 ---- /** Overridden only fire those properties needed */ protected void firePropertyChange(String name, Object old, Object nue) { ! //firing all changes for now - breaks text painting on OS-X super.firePropertyChange(name,old,nue); } *************** *** 495,501 **** /** A renderer for string properties, which can also delegate to the * property editor's paint()method if possible. */ ! private static final class StringRenderer extends DefaultTableCellRenderer implements InplaceEditor { private PropertyEditor editor=null; private PropertyEnv env=null; private boolean tableUI=false; --- 486,492 ---- /** A renderer for string properties, which can also delegate to the * property editor's paint()method if possible. */ ! private static final class StringRenderer extends HtmlRenderer implements InplaceEditor { private PropertyEditor editor=null; private PropertyEnv env=null; private boolean tableUI=false; *************** *** 516,548 **** public boolean isEnabled() { return enabled; } ! public Dimension getPreferredSize() { ! Graphics g = PropUtils.getScratchGraphics(this); ! FontMetrics fm = g.getFontMetrics(getFont()); ! int w; ! if (getText() != null) { ! //avoid NPE in sun.awt.font.FontDesignMetrics.stringWidth():281 ! w = fm.stringWidth(getText()) + 4; ! } else { ! w = PropUtils.getMinimumPropPanelWidth(); ! } ! int h = fm.getHeight() + 2; ! w = Math.max(w,PropUtils.getMinimumPropPanelWidth()); ! h = Math.max(h,PropUtils.getMinimumPropPanelHeight()); ! Dimension result = new Dimension(w,h); ! if (getIcon() != null) { ! result.height = Math.max (result.height, getIcon().getIconHeight()); ! result.width += getIcon().getIconHeight(); ! } ! if (getBorder() != null) { ! Insets i = getBorder().getBorderInsets(this); ! result.width += i.right+i.left; ! result.height += i.top+i.bottom; ! } return result; } ! public void paint (Graphics g) { if (editor != null) { setEnabled(PropUtils.checkEnabled (this, editor, env)); --- 507,524 ---- public boolean isEnabled() { return enabled; } ! public Dimension getPreferredSize() { ! Dimension result = super.getPreferredSize(); ! result.width = Math.max(result.width, ! PropUtils.getMinimumPropPanelWidth()); ! ! result.height = Math.max(result.height, ! PropUtils.getMinimumPropPanelHeight()); ! return result; } ! public void paint (Graphics g) { if (editor != null) { setEnabled(PropUtils.checkEnabled (this, editor, env)); *************** *** 557,562 **** --- 533,539 ---- } clear(); } + private void delegatedPaint (Graphics g) { Color c = g.getColor(); *************** *** 572,577 **** --- 549,556 ---- b.paintBorder(this, g, 0, 0, getWidth(), getHeight()); } Rectangle r = getBounds(); + //XXX May be the source of Rochelle's multiple rows of error + //marking misalignment problem...(I do not jest) r.x = getWidth() > 16 ? editor instanceof Boolean3WayEditor ? 0 : 3 : 0; //align text with other renderers r.width -= getWidth() > 16 ? editor instanceof Boolean3WayEditor ? 0 : 3 : 0; //align text with other renderers r.y = 0; *************** *** 584,607 **** public void clear() { editor = null; env = null; ! setText(""); //NOI18N setIcon(null); setOpaque(true); } public void setValue(Object o) { ! super.setValue(o); } - public void setText(String s) { - //XXX hotfix for the form editor until the HTML renderer can - //be put into trunk - per Trung's request - super.setText(stripHTML(s)); - } - public void connect(PropertyEditor p, PropertyEnv env) { editor = p; this.env = env; reset(); } --- 563,583 ---- public void clear() { editor = null; env = null; ! setText("", null); //NOI18N setIcon(null); setOpaque(true); } + private Object value = null; public void setValue(Object o) { ! value = o; ! setText (value instanceof String ? (String) value : value != null ? value.toString() : null); } public void connect(PropertyEditor p, PropertyEnv env) { editor = p; this.env = env; + setRenderStyle(STYLE_TRUNCATE); reset(); } *************** *** 664,670 **** } public boolean supportsTextEntry() { ! return true; } /** Overridden to do nothing */ --- 640,646 ---- } public boolean supportsTextEntry() { ! return false; } /** Overridden to do nothing */ *************** *** 675,712 **** protected void fireStateChanged() { } - /** Overridden to do nothing */ - protected void firePropertyChange(String name, Object old, Object nue) { - } - - /** Overridden to do nothing */ - public void firePropertyChange(String name, boolean old, boolean nue) { - } - - /** Overridden to do nothing */ - public void firePropertyChange(String name, int old, int nue) { - } - - /** Overridden to do nothing */ - public void firePropertyChange(String name, byte old, byte nue) { - } - - /** Overridden to do nothing */ - public void firePropertyChange(String name, char old, char nue) { - } - - /** Overridden to do nothing */ - public void firePropertyChange(String name, double old, double nue) { - } - - /** Overridden to do nothing */ - public void firePropertyChange(String name, float old, float nue) { - } - - /** Overridden to do nothing */ - public void firePropertyChange(String name, short old, short nue) { - } - public void addActionListener(ActionListener al) { //do nothing } --- 651,656 ---- *************** *** 1054,1060 **** } } ! public static boolean requiresSwingPainting (Component c) { if (!(c instanceof InplaceEditor)) { return true; --- 998,1004 ---- } } ! /* public static boolean requiresSwingPainting (Component c) { if (!(c instanceof InplaceEditor)) { return true; *************** *** 1115,1118 **** --- 1059,1063 ---- return s; } } + */ } Index: openide/src/org/openide/explorer/propertysheet/SheetCellRenderer.java =================================================================== RCS file: /cvs/openide/src/org/openide/explorer/propertysheet/SheetCellRenderer.java,v retrieving revision 1.8 diff -c -r1.8 SheetCellRenderer.java *** openide/src/org/openide/explorer/propertysheet/SheetCellRenderer.java 21 Jan 2004 20:36:53 -0000 1.8 --- openide/src/org/openide/explorer/propertysheet/SheetCellRenderer.java 9 Mar 2004 23:28:12 -0000 *************** *** 21,28 **** import java.beans.FeatureDescriptor; import javax.swing.*; import javax.swing.table.TableCellRenderer; ! import javax.swing.table.DefaultTableCellRenderer; import org.openide.nodes.Node.*; /** An implementation of SheetCellRenderer that wraps custom InplaceEditors * to efficiently render properties. * --- 21,29 ---- import java.beans.FeatureDescriptor; import javax.swing.*; import javax.swing.table.TableCellRenderer; ! import org.openide.awt.HtmlRenderer; import org.openide.nodes.Node.*; + /** An implementation of SheetCellRenderer that wraps custom InplaceEditors * to efficiently render properties. * *************** *** 69,81 **** if (fd instanceof PropertySet) { SetRenderer sr = getSetRenderer(); ! sr.setText(fd.getDisplayName()); sr.setExpanded(((SheetTable) table).getPropertySetModel().isExpanded(fd)); result = sr; } else { if (column == 0) { ! JLabel lbl = factory().getStringRenderer(); ! lbl.setText(fd.getDisplayName()); if (includeMargin) { lbl.setBorder(BorderFactory.createEmptyBorder( 0, PropUtils.getMarginWidth() + 2, 0, 1)); --- 70,100 ---- if (fd instanceof PropertySet) { SetRenderer sr = getSetRenderer(); ! String txt = ((PropertySet) fd).getHtmlDisplayName(); ! boolean isHtml = txt != null; ! if (!isHtml) { ! txt = fd.getDisplayName(); ! } ! ! ((HtmlRenderer)sr).setText (txt, isHtml ? Boolean.TRUE : ! Boolean.FALSE); ! sr.setExpanded(((SheetTable) table).getPropertySetModel().isExpanded(fd)); + result = sr; } else { if (column == 0) { ! ! ! String txt = ((Property) fd).getHtmlDisplayName(); ! boolean isHtml = txt != null; ! if (!isHtml) { ! txt = fd.getDisplayName(); ! } ! HtmlRenderer lbl = HtmlRenderer.sharedInstance(isHtml); ! ! lbl.setText (txt, isHtml ? Boolean.TRUE : Boolean.FALSE); ! if (includeMargin) { lbl.setBorder(BorderFactory.createEmptyBorder( 0, PropUtils.getMarginWidth() + 2, 0, 1)); *************** *** 98,111 **** result = lbl; } else { result = factory().getRenderer((Property) fd); - //Use a 2 pixel margin so it's not flush - /* - ((JComponent)result).setBorder(BorderFactory.createEmptyBorder(0, - PropUtils.getTextMargin(), 0, 0)); - */ } } - // result.setFont(table.getFont()); return result; } --- 117,124 ---- *************** *** 124,140 **** * and paint it across two columns after the rest of the paint cycle * is completed. */ ! static class SetRenderer extends DefaultTableCellRenderer { ! public boolean dontPaint = true; - int textY = -1; - int iconY = -1; /** Discard/recalc UI dependent values */ public void updateUI() { super.updateUI(); ! iconY=-1; ! textY=-1; } private boolean expanded; --- 137,150 ---- * and paint it across two columns after the rest of the paint cycle * is completed. */ ! static class SetRenderer extends HtmlRenderer { ! Insets insets; public boolean dontPaint = true; /** Discard/recalc UI dependent values */ public void updateUI() { super.updateUI(); ! insets = new Insets(0, PropUtils.getIconMargin(), 0, 0); } private boolean expanded; *************** *** 147,168 **** PropUtils.getCollapsedIcon(); } ! /** Calculate y position to center text and icon vertically */ ! private void calcYPos(FontMetrics fm) { ! int h = getHeight(); ! int ih = getIcon().getIconHeight(); ! int fh = fm.getHeight(); ! if (fh >= h) { ! textY = 0 + fm.getAscent(); ! } else { ! textY = ((h - fh) / 2) + fm.getAscent(); ! } ! ! if (ih >= h) { ! iconY = 0; ! } else { ! iconY = (h - ih) / 2; ! } } /** Paint the component, if the dontPaint field is --- 157,168 ---- PropUtils.getCollapsedIcon(); } ! public Insets getInsets() { ! return insets; ! } ! ! public int getIconTextGap() { ! return 2; //PropUtils.getMarginWidth() + 2; } /** Paint the component, if the dontPaint field is *************** *** 173,187 **** */ public void paint(Graphics g) { if (!dontPaint) { ! if (iconY == -1) calcYPos(g.getFontMetrics(getFont())); ! g.setFont(getFont()); ! g.setColor(getBackground()); ! g.fillRect(0,0,getWidth(),getHeight()); ! Icon ic = getIcon(); ! ic.paintIcon(this, g, PropUtils.getIconMargin(), iconY); ! g.setColor(getForeground()); ! g.drawString(getText(), PropUtils.getIconMargin()+ PropUtils.getMarginWidth() + 2, //XXX text icon gap ! textY); } } /** Overridden to do nothing */ --- 173,179 ---- */ public void paint(Graphics g) { if (!dontPaint) { ! super.paint(g); } } /** Overridden to do nothing */ *************** *** 190,199 **** /** Overridden to do nothing */ protected void fireStateChanged() { - } - - /** Overridden to do nothing */ - protected void firePropertyChange(String name, Object old, Object nue) { } } } --- 182,187 ---- Index: openide/src/org/openide/explorer/view/ListViewDropSupport.java =================================================================== RCS file: /cvs/openide/src/org/openide/explorer/view/ListViewDropSupport.java,v retrieving revision 1.16 diff -c -r1.16 ListViewDropSupport.java *** openide/src/org/openide/explorer/view/ListViewDropSupport.java 21 Nov 2003 09:05:25 -0000 1.16 --- openide/src/org/openide/explorer/view/ListViewDropSupport.java 9 Mar 2004 23:28:13 -0000 *************** *** 53,61 **** /** The component we are supporting with drop support */ protected JList list; - /** For managing visual appearance of JList cells. */ - protected NodeRenderer.List cellRenderer; - // Operations public ListViewDropSupport (ListView view, JList list) { this( view, list, true ); --- 53,58 ---- *************** *** 66,72 **** { this.view = view; this.list = list; - //cellRenderer = (NodeListCellRenderer)list.getCellRenderer(); this.dropTargetPopupAllowed = dropTargetPopupAllowed; } --- 63,68 ---- *************** *** 265,277 **** } return dropTarget; } - - /** Safe getter for the cell renderer of asociated list */ - NodeRenderer.List getCellRenderer () { - if (cellRenderer == null) - cellRenderer = (NodeRenderer.List)list.getCellRenderer(); - return cellRenderer; - } - - } --- 261,264 ---- Index: openide/src/org/openide/explorer/view/NodeRenderer.java =================================================================== RCS file: /cvs/openide/src/org/openide/explorer/view/NodeRenderer.java,v retrieving revision 1.28 diff -c -r1.28 NodeRenderer.java *** openide/src/org/openide/explorer/view/NodeRenderer.java 25 Feb 2004 01:08:43 -0000 1.28 --- openide/src/org/openide/explorer/view/NodeRenderer.java 9 Mar 2004 23:28:18 -0000 *************** *** 36,45 **** --- 36,47 ---- import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.WeakHashMap; + import javax.swing.Icon; import javax.swing.JRootPane; import javax.swing.SwingUtilities; import org.openide.ErrorManager; + import org.openide.awt.HtmlRenderer; import org.openide.nodes.Node; import org.openide.util.Utilities; *************** *** 49,55 **** * * @see org.openide.nodes.Node * ! * @author Jaroslav Tulach */ public class NodeRenderer extends Object implements TreeCellRenderer, ListCellRenderer { --- 51,57 ---- * * @see org.openide.nodes.Node * ! * @author Jaroslav Tulach, Tim Boudreau */ public class NodeRenderer extends Object implements TreeCellRenderer, ListCellRenderer { *************** *** 59,71 **** /** Flag indicating if to use big icons. */ private boolean bigIcons; - static Border emptyBorder = BorderFactory.createEmptyBorder (1, 1, 1, 1); - /** Creates default renderer. */ public NodeRenderer () { } - /** Creates renderer. * @param bigIcons use big icons if possible */ --- 61,70 ---- *************** *** 82,91 **** return sharedInstance; } - - // - // Rendering methods - // /** Finds the component that is capable of drawing the cell in a tree. * @param value value can be either Node --- 81,86 ---- *************** *** 97,489 **** boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus ) { ! return getTree().getTreeCellRendererComponent ( ! tree, value, sel, expanded, leaf, row, hasFocus ! ); } ! ! /** This is the only method defined by ListCellRenderer. We just * reconfigure the Jlabel each time we're called. */ public Component getListCellRendererComponent ( ! JList list, ! Object value, // value to display ! int index, // cell index ! boolean isSelected, // is the cell selected ! boolean cellHasFocus // the list and the cell have the focus ! ) { ! // accepting either Node or Visualizers ! VisualizerNode vis = (value instanceof Node) ? ! VisualizerNode.getVisualizer (null, (Node)value) ! : ! (VisualizerNode)value; ! if (vis == null) { ! vis = VisualizerNode.EMPTY; } - ListCellRenderer r = bigIcons ? (ListCellRenderer)getPane() : getList(); - - Component result = r.getListCellRendererComponent ( - list, vis, index, isSelected, cellHasFocus - ); - result.setFont(list.getFont()); return result; } ! ! // ******************** ! // Support for dragging ! // ******************** ! ! /** Value of the cell with 'drag under' visual feedback */ ! private static VisualizerNode draggedOver; ! ! ! /** DnD operation enters. Update look and feel to the 'drag under' state. ! * @param value the value of cell which should have 'drag under' visual feedback ! */ ! static void dragEnter (Object dragged) { ! draggedOver = (VisualizerNode)dragged; ! } ! ! /** DnD operation exits. Revert to the normal look and feel. */ ! static void dragExit () { ! draggedOver = null; ! } ! ! ! // ******************** ! // Cache for ImageIcons ! // ******************** ! ! /** default icon to use when none is present */ ! private static final String DEFAULT_ICON = "org/openide/resources/defaultNode.gif"; // NOI18N ! ! /** loaded default icon */ ! private static ImageIcon defaultIcon; ! ! /** of icons used (Image, IconImage)*/ ! private static final WeakHashMap map = new WeakHashMap (); ! ! /** Loades default icon if not loaded. */ ! static ImageIcon getDefaultIcon () { ! if (defaultIcon == null) { ! defaultIcon = new ImageIcon(Utilities.loadImage(DEFAULT_ICON)); ! } ! ! return defaultIcon; ! } ! ! /** Finds imager for given resource. ! * @param image image to get ! * @return icon for the image ! */ ! static ImageIcon getIcon (Image image) { ! Reference ref = (Reference)map.get (image); ! ! ImageIcon icon = ref == null ? null : (ImageIcon)ref.get (); ! if (icon != null) { ! return icon; ! } ! ! icon = new ImageIcon (image); ! map.put (image, new WeakReference (icon)); ! ! return icon; ! } ! ! // ! // Renderers ! // ! ! ! private static NodeRenderer.Tree tree = null; ! ! private synchronized static NodeRenderer.Tree getTree () { ! if (tree == null) ! tree = new NodeRenderer.Tree (); ! return tree; ! } ! ! private static NodeRenderer.Pane pane = null; ! ! private synchronized static NodeRenderer.Pane getPane() { ! if (pane == null) ! pane = new NodeRenderer.Pane (); ! return pane; ! } ! ! private static NodeRenderer.List list = null; ! ! private synchronized static NodeRenderer.List getList() { ! if (list == null) ! list = new NodeRenderer.List (); ! return list; ! } ! ! ! /** Tree cell renderer. Accepts only VisualizerNode values. */ ! final static class Tree extends DefaultTreeCellRenderer { ! /** generated Serialized Version UID */ ! static final long serialVersionUID = -183570483117501696L; ! ! /** @return Rendered cell component */ ! public Component getTreeCellRendererComponent( ! JTree tree, Object value, ! boolean sel, boolean expanded, ! boolean leaf, int row, boolean hasFocus ! ) { ! setEnabled(tree.isEnabled()); ! // accepts only VisualizerNode ! VisualizerNode vis = (VisualizerNode)value; ! ! Image iconImg; ! if (expanded) { ! iconImg = vis.node.getOpenedIcon(BeanInfo.ICON_COLOR_16x16); ! } else { ! iconImg = vis.node.getIcon(BeanInfo.ICON_COLOR_16x16); ! } - // bugfix #28515, check if getIcon contract isn't broken - if (iconImg == null) { - String method = expanded ? "getOpenedIcon" : "getIcon"; // NOI18N - ErrorManager.getDefault ().log (ErrorManager.WARNING, "Node \"" + vis.node.getName () + // NOI18N - "\" [" +vis.node.getClass().getName()+ "] cannot return null from " + method + "(). See Node." + method + " contract."); // NOI18N - } else { - ImageIcon nodeicon = NodeRenderer.getIcon(iconImg); - - setIconTextGap (4 - nodeicon.getIconWidth() - + ( nodeicon.getIconWidth() > 24 ? nodeicon.getIconWidth() : 24 ) ); - setIcon(nodeicon); - } - - setText(vis.getDisplayName ()); - - // provide "drag under" feedback if DnD operation is active // NOI18N - if (vis == draggedOver) { - sel = true; - } - - this.hasFocus = hasFocus; - selected = sel; - - if (sel) { //Find out who has focus Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager(). getPermanentFocusOwner(); ! ! boolean treeHasFocus = focusOwner == tree || ! tree.isAncestorOf(focusOwner) || focusOwner instanceof TreeTable; if (!treeHasFocus) { ! TreeTable tt = (TreeTable)SwingUtilities.getAncestorOfClass(TreeTable.class, focusOwner); if (tt != null) { ! treeHasFocus = focusOwner != null && ! tt.getDefaultRenderer(TreeTableModelAdapter.class) ! == tree; } } - - setBackgroundSelectionColor(treeHasFocus ? - UIManager.getColor("Tree.selectionBackground") : - getNoFocusSelectionBackground()); //NOI18N - - setForeground(treeHasFocus ? - UIManager.getColor("Tree.selectionForeground") : - getNoFocusSelectionForeground()); //NOI18N - } else { - setForeground(getTextNonSelectionColor()); - setBackground(tree.getBackground()); } ! ! return this; ! } ! ! protected void firePropertyChange(String name, Object old, Object nw) { ! // do really nothing! ! } ! ! } // End of class Tree. ! ! ! /** Implements a ListCellRenderer for rendering items ! * of a List containing Nodes. ! * It displays the node's 16x16 icon and its display name. ! * ! * @author Ian Formanek ! */ ! static final class List extends JLabel implements ListCellRenderer { ! /** generated Serialized Version UID */ ! static final long serialVersionUID = -8387317362588264203L; ! ! /** Focused Node border. */ ! protected static Border focusBorder = BorderFactory.createLineBorder ( ! UIManager.getColor ("List.focusCellHighlight") != null ? ! UIManager.getColor ("List.focusCellHighlight") : Color.blue); // NOI18N ! ! public List() { ! setOpaque(true); ! } ! ! /** This is the only method defined by ListCellRenderer. We just ! * reconfigure the Jlabel each time we're called. ! */ ! public Component getListCellRendererComponent ( ! JList list, ! Object value, // value to display ! int index, // cell index ! boolean isSelected, // is the cell selected ! boolean cellHasFocus) // the list and the cell have the focus ! { ! VisualizerNode vis = (VisualizerNode)value; ! ImageIcon nodeicon = NodeRenderer.getIcon(vis.node.getIcon(BeanInfo.ICON_COLOR_16x16)); ! setIcon(nodeicon); ! setText(vis.getDisplayName ()); ! if (isSelected) { ! Component focusOwner = ! KeyboardFocusManager.getCurrentKeyboardFocusManager(). ! getPermanentFocusOwner(); ! ! boolean hasFocus = focusOwner == list || list.isAncestorOf(focusOwner); ! ! setBackground(hasFocus ? list.getSelectionBackground() : ! getNoFocusSelectionBackground()); ! ! setForeground(hasFocus ? list.getSelectionForeground() : ! getNoFocusSelectionForeground()); ! } else { ! setBackground(list.getBackground()); ! setForeground(list.getForeground()); } - - setIconTextGap (4 - nodeicon.getIconWidth() - + ( nodeicon.getIconWidth() > 24 ? nodeicon.getIconWidth() : 24 ) ); - - - int delta = NodeListModel.findVisualizerDepth (list.getModel (), vis); - - Border border = (cellHasFocus || value == draggedOver) ? focusBorder : emptyBorder; - if (delta > 0) { - border = BorderFactory.createCompoundBorder ( - BorderFactory.createEmptyBorder (0, nodeicon.getIconWidth() * delta, 0, 0), - border - ); - } - setBorder(border); - - return this; } ! ! protected void firePropertyChange(String name, Object old, Object nw) { ! // do really nothing! ! } ! ! } // End of class List. ! ! ! /** List cell renderer which renders icon and display name from VisualizerNode. */ ! final static class Pane extends JLabel implements ListCellRenderer { ! /** generated Serialized Version UID */ ! static final long serialVersionUID = -5100925551665387243L; ! ! /** Focused Node border. */ ! static Border focusBorder = LineBorder.createBlackLineBorder(); ! ! /** Creates a new NetbeansListCellRenderer */ ! public Pane () { ! setOpaque(true); ! setVerticalTextPosition(JLabel.BOTTOM); ! setHorizontalAlignment(JLabel.CENTER); ! setHorizontalTextPosition(JLabel.CENTER); ! } ! ! /** This is the only method defined by ListCellRenderer. We just ! * reconfigure the Jlabel each time we're called. ! * @param list the JList ! * @param value the value returned by list.getModel().getElementAt(index) ! * @param index the cells index ! * @param isSelected true if the specified cell was selected ! * @param cellHasFocus true if the specified cell has the focus ! * @return a component whose paint() method will render the specified value ! */ ! public Component getListCellRendererComponent ( ! JList list, Object value, int index, ! boolean isSelected, boolean cellHasFocus ! ) { ! VisualizerNode vis = (VisualizerNode)value; ! ! setIcon(NodeRenderer.getIcon(vis.node.getIcon(BeanInfo.ICON_COLOR_32x32))); ! setText(vis.getDisplayName ()); ! Component focusOwner = ! KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); ! boolean hasFocus = focusOwner == list || ! list.isAncestorOf(focusOwner); ! ! if (isSelected){ ! setBackground(hasFocus ? list.getSelectionBackground() ! : getNoFocusSelectionBackground()); ! ! setForeground(hasFocus ? list.getSelectionForeground() : ! getNoFocusSelectionForeground()); } ! else { ! setBackground(list.getBackground()); ! setForeground(list.getForeground()); ! } ! ! setBorder(cellHasFocus ? focusBorder : emptyBorder); ! return this; ! } ! ! protected void firePropertyChange(String name, Object old, Object nw) { ! // do really nothing! ! } ! } // End of class Pane. ! private static Color noFocusSelectionBackground=null; ! static Color getNoFocusSelectionBackground() { ! if (noFocusSelectionBackground == null) { ! //allow theme/ui custom definition ! noFocusSelectionBackground = ! UIManager.getColor("nb.explorer.noFocusSelectionBackground"); //NOI18N ! if (noFocusSelectionBackground == null) { ! //try to get standard shadow color ! noFocusSelectionBackground = UIManager.getColor("controlShadow"); //NOI18N ! if (noFocusSelectionBackground == null) { ! //Okay, the look and feel doesn't suport it, punt ! noFocusSelectionBackground = Color.lightGray; ! } ! //Lighten it a bit because disabled text will use controlShadow/ ! //gray ! noFocusSelectionBackground = noFocusSelectionBackground.brighter(); ! } ! } ! return noFocusSelectionBackground; } ! ! private static Color noFocusSelectionForeground=null; ! static Color getNoFocusSelectionForeground() { ! if (noFocusSelectionForeground == null) { ! //allow theme/ui custom definition ! noFocusSelectionForeground = ! UIManager.getColor("nb.explorer.noFocusSelectionForeground"); //NOI18N ! if (noFocusSelectionForeground == null) { ! //try to get standard shadow color ! noFocusSelectionForeground = UIManager.getColor("textText"); //NOI18N ! if (noFocusSelectionForeground == null) { ! //Okay, the look and feel doesn't suport it, punt ! noFocusSelectionForeground = Color.BLACK; ! } ! } ! } ! return noFocusSelectionForeground; } ! ! } --- 92,261 ---- boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus ) { ! VisualizerNode vis = findVisualizerNode (value); ! ! String text = vis.getHtmlDisplayName(); ! boolean isHtml = text != null; ! if (!isHtml) { ! text = vis.getDisplayName(); ! } ! ! HtmlRenderer ren = HtmlRenderer.sharedInstance(isHtml); ! ! //Get our result value - really it is ren, but this call causes ! //it to configure itself with the passed values ! Component result = ren.getTreeCellRendererComponent( ! tree, text, sel, expanded, leaf, row, hasFocus); ! ! //Do our additional configuration - set up the icon and possibly ! //do some hacks to make it look focused for TreeTableView ! configureFrom (ren, value, tree, expanded, sel, vis); ! ! return result; } ! /** This is the only method defined by ListCellRenderer. We just * reconfigure the Jlabel each time we're called. */ public Component getListCellRendererComponent ( ! JList list, Object value, int index, boolean sel, ! boolean cellHasFocus) { ! ! VisualizerNode vis = findVisualizerNode(value); ! String text = vis.getHtmlDisplayName(); ! boolean isHtml = text != null; ! if (!isHtml) { ! text = vis.getDisplayName(); ! } ! ! HtmlRenderer ren = HtmlRenderer.sharedInstance(isHtml); ! ! //Get our result value - really it is ren, but this call causes ! //it to configure itself with the passed values ! Component result = ren.getListCellRendererComponent( ! list, text, index, sel, cellHasFocus || value == draggedOver); ! ! //Do our additional configuration - set up the icon and possibly ! //do some hacks to make it look focused for TreeTableView ! configureFrom (ren, value, list, false, sel, vis); ! ! if (bigIcons) { ! ren.setCentered(true); ! } else if (ren.getIcon() != null) { ! //copied from original - not quite sure how this does what it does, ! //but it does something ! int indent = ren.getIcon().getIconWidth() * ! NodeListModel.findVisualizerDepth (list.getModel (), vis); ! ! ren.setIndent (indent); } return result; } ! ! /** Utility method which performs configuration which is common to all of the renderer ! * implementations - sets the icon and focus properties on the renderer ! * from the VisualizerNode. */ ! private static void configureFrom (HtmlRenderer ren, Object value, Container ! target, boolean useOpenedIcon, boolean sel, VisualizerNode vis) { ! ! Icon icon = vis.getIcon(useOpenedIcon); ! ! if (icon.getIconWidth() > 0) { ! //Max annotated icon width is 24, so to have all the text and all ! //the icons come out aligned, set the icon text gap to the difference ! //plus a two pixel margin ! ren.setIconTextGap (26 - icon.getIconWidth()); ! //Add a 2 pixel border so it's not flush, with an extra two pixel ! //margin at the top and bottom ! } else { ! //If the icon width is 0, fill the space using a border, and add in ! //the extra two pixels so the node names are aligned (btw, this ! //does seem to waste a frightful amount of horizontal space in ! //a tree that can use all it can get) ! ren.setBorder (BorderFactory.createEmptyBorder( ! 0, ! 26, ! 0, ! 0)); ! } ! //Inscrutable original code ! /* ! if (icon.getIconWidth() > 24) { ! iconTextGap = 4 - icon.getIconWidth() + ! (icon.getIconWidth() > 24 ? icon.getIconWidth() : 24 ); ! } ! */ ! ! ren.setIcon (icon); ! ! //Do the kooky focus dance so the tree that is a renderer for ! //TreeTableView will paint as though focused even though it's ! //never onscreen. ! if (target instanceof JTree) { ! boolean treeHasFocus = false; if (sel) { //Find out who has focus Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager(). getPermanentFocusOwner(); ! ! treeHasFocus = focusOwner == target || ! target.isAncestorOf(focusOwner) || focusOwner ! instanceof TreeTable; if (!treeHasFocus) { ! TreeTable tt = (TreeTable)SwingUtilities.getAncestorOfClass( ! TreeTable.class, focusOwner); ! if (tt != null) { ! treeHasFocus = focusOwner != null && ! tt.getDefaultRenderer(TreeTableModelAdapter.class) ! == target; } } } ! if (treeHasFocus) { ! ren.setPaintAsFocused(true); } } ! ren.setFont(target.getFont()); ! } ! ! /** Utility method to find a visualizer node for the object passed to ! * any of the cell renderer methods as the value */ ! private static final VisualizerNode findVisualizerNode(Object value) { ! VisualizerNode vis = (value instanceof Node) ? ! VisualizerNode.getVisualizer (null, (Node) value) : ! (VisualizerNode)value; ! if (vis == null) { ! vis = VisualizerNode.EMPTY; } ! ! return vis; ! } ! ! // ******************** ! // Support for dragging ! // ******************** ! /** Value of the cell with 'drag under' visual feedback */ ! private static VisualizerNode draggedOver; ! /** DnD operation enters. Update look and feel to the 'drag under' state. ! * @param value the value of cell which should have 'drag under' visual feedback ! */ ! static void dragEnter (Object dragged) { ! draggedOver = (VisualizerNode)dragged; } ! ! /** DnD operation exits. Revert to the normal look and feel. */ ! static void dragExit () { ! draggedOver = null; } ! } Index: openide/src/org/openide/explorer/view/TableSheetCell.java =================================================================== RCS file: /cvs/openide/src/org/openide/explorer/view/TableSheetCell.java,v retrieving revision 1.18 diff -c -r1.18 TableSheetCell.java *** openide/src/org/openide/explorer/view/TableSheetCell.java 17 Feb 2004 16:47:49 -0000 1.18 --- openide/src/org/openide/explorer/view/TableSheetCell.java 9 Mar 2004 23:28:19 -0000 *************** *** 259,269 **** propPanel.setBackground(tableHasFocus ? table.getSelectionBackground() : ! NodeRenderer.getNoFocusSelectionBackground()); propPanel.setForeground(tableHasFocus ? table.getSelectionForeground() : ! NodeRenderer.getNoFocusSelectionForeground()); } else { propPanel.setBackground(table.getBackground()); --- 259,269 ---- propPanel.setBackground(tableHasFocus ? table.getSelectionBackground() : ! TreeTable.getUnfocusedSelectedBackground()); propPanel.setForeground(tableHasFocus ? table.getSelectionForeground() : ! TreeTable.getUnfocusedSelectedForeground()); } else { propPanel.setBackground(table.getBackground()); *************** *** 289,296 **** ((Container) focusOwner).isAncestorOf(table)); nullPanel.setBackground(tableHasFocus ? ! table.getSelectionBackground() : ! NodeRenderer.getNoFocusSelectionBackground()); //XXX may want to handle inverse theme here and use brighter if //below a threshold. Deferred to centralized color management //being implemented. --- 289,296 ---- ((Container) focusOwner).isAncestorOf(table)); nullPanel.setBackground(tableHasFocus ? ! table.getSelectionBackground() : ! TreeTable.getUnfocusedSelectedBackground()); //XXX may want to handle inverse theme here and use brighter if //below a threshold. Deferred to centralized color management //being implemented. Index: openide/src/org/openide/explorer/view/TreeTable.java =================================================================== RCS file: /cvs/openide/src/org/openide/explorer/view/TreeTable.java,v retrieving revision 1.46 diff -c -r1.46 TreeTable.java *** openide/src/org/openide/explorer/view/TreeTable.java 2 Mar 2004 19:06:13 -0000 1.46 --- openide/src/org/openide/explorer/view/TreeTable.java 9 Mar 2004 23:28:23 -0000 *************** *** 703,714 **** focusOwner == TreeTable.this || TreeTable.this.isAncestorOf(focusOwner); setBackground(tableHasFocus ? table.getSelectionBackground() : ! NodeRenderer.getNoFocusSelectionBackground()); setForeground(tableHasFocus ? table.getSelectionForeground() : ! NodeRenderer.getNoFocusSelectionForeground()); } else { setBackground(table.getBackground()); setForeground(table.getForeground()); --- 703,718 ---- focusOwner == TreeTable.this || TreeTable.this.isAncestorOf(focusOwner); + //TODO - it should be possible to simply set the correct + //color in prepareRenderer for the tree's cell renderer, + //rather than set it for the whole tree. Might fix a + //couple problems. -Tim setBackground(tableHasFocus ? table.getSelectionBackground() : ! getUnfocusedSelectedBackground()); setForeground(tableHasFocus ? table.getSelectionForeground() : ! getUnfocusedSelectedForeground()); } else { setBackground(table.getBackground()); setForeground(table.getForeground()); *************** *** 818,827 **** if (isEditing() && editorComp != null) { editorComp.setBackground(focused ? getSelectionBackground() : ! NodeRenderer.getNoFocusSelectionBackground()); editorComp.setForeground(focused ? getSelectionForeground() : ! NodeRenderer.getNoFocusSelectionForeground()); } } --- 822,831 ---- if (isEditing() && editorComp != null) { editorComp.setBackground(focused ? getSelectionBackground() : ! getUnfocusedSelectedBackground()); editorComp.setForeground(focused ? getSelectionForeground() : ! getUnfocusedSelectedForeground()); } } *************** *** 1611,1614 **** --- 1615,1660 ---- } } } + + private static Color unfocusedSelBg = null; + private static Color unfocusedSelFg = null; + + /** Get the system-wide unfocused selection background color */ + static Color getUnfocusedSelectedBackground() { + if (unfocusedSelBg == null) { + //allow theme/ui custom definition + unfocusedSelBg = + UIManager.getColor("nb.explorer.unfocusedSelBg"); //NOI18N + if (unfocusedSelBg == null) { + //try to get standard shadow color + unfocusedSelBg = UIManager.getColor("controlShadow"); //NOI18N + if (unfocusedSelBg == null) { + //Okay, the look and feel doesn't suport it, punt + unfocusedSelBg = Color.lightGray; + } + //Lighten it a bit because disabled text will use controlShadow/ + //gray + unfocusedSelBg = unfocusedSelBg.brighter(); + } + } + return unfocusedSelBg; + } + + /** Get the system-wide unfocused selection foreground color */ + static Color getUnfocusedSelectedForeground() { + if (unfocusedSelFg == null) { + //allow theme/ui custom definition + unfocusedSelFg = + UIManager.getColor("nb.explorer.unfocusedSelFg"); //NOI18N + if (unfocusedSelFg == null) { + //try to get standard shadow color + unfocusedSelFg = UIManager.getColor("textText"); //NOI18N + if (unfocusedSelFg == null) { + //Okay, the look and feel doesn't suport it, punt + unfocusedSelFg = Color.BLACK; + } + } + } + return unfocusedSelFg; + } } Index: openide/src/org/openide/explorer/view/TreeView.java =================================================================== RCS file: /cvs/openide/src/org/openide/explorer/view/TreeView.java,v retrieving revision 1.157 diff -c -r1.157 TreeView.java *** openide/src/org/openide/explorer/view/TreeView.java 8 Mar 2004 15:28:55 -0000 1.157 --- openide/src/org/openide/explorer/view/TreeView.java 9 Mar 2004 23:28:27 -0000 *************** *** 1091,1097 **** // note: dropTarget is activated in constructor } // lazy cell editor init ! tree.setCellEditor(new TreeViewCellEditor(tree, new NodeRenderer.Tree ())); tree.setEditable(true); } --- 1091,1097 ---- // note: dropTarget is activated in constructor } // lazy cell editor init ! tree.setCellEditor(new TreeViewCellEditor(tree)); tree.setEditable(true); } Index: openide/src/org/openide/explorer/view/TreeViewCellEditor.java =================================================================== RCS file: /cvs/openide/src/org/openide/explorer/view/TreeViewCellEditor.java,v retrieving revision 1.37 diff -c -r1.37 TreeViewCellEditor.java *** openide/src/org/openide/explorer/view/TreeViewCellEditor.java 9 Feb 2004 16:04:19 -0000 1.37 --- openide/src/org/openide/explorer/view/TreeViewCellEditor.java 9 Mar 2004 23:28:28 -0000 *************** *** 46,53 **** * @param tree the tree * @param renderer the renderer to use for the cell */ ! public TreeViewCellEditor(JTree tree, DefaultTreeCellRenderer renderer) { ! super(tree, renderer); // deal with selection if already exists if (tree.getSelectionCount() == 1) { lastPath = tree.getSelectionPath(); --- 46,57 ---- * @param tree the tree * @param renderer the renderer to use for the cell */ ! public TreeViewCellEditor(JTree tree) { ! //Use a dummy DefaultTreeCellEditor - we'll set up the correct ! //icon when we fetch the editor component (see EOF). Not sure ! //it's wildly vaulable to subclass DefaultTreeCellEditor here - ! //we override most everything ! super(tree, new DefaultTreeCellRenderer()); // deal with selection if already exists if (tree.getSelectionCount() == 1) { lastPath = tree.getSelectionPath(); *************** *** 154,160 **** } }; ! tf.registerKeyboardAction( this, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true), JComponent.WHEN_FOCUSED --- 158,164 ---- } }; ! tf.registerKeyboardAction( //TODO update to use inputMap/actionMap this, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true), JComponent.WHEN_FOCUSED *************** *** 259,264 **** --- 263,269 ---- protected void prepareForEditing () { tree.removeMouseMotionListener (this); + super.prepareForEditing (); } *************** *** 269,275 **** } /** Redefined default cell editor to convert nodes to name */ ! static class Ed extends DefaultCellEditor { /** generated Serialized Version UID */ static final long serialVersionUID = -6373058702842751408L; --- 274,280 ---- } /** Redefined default cell editor to convert nodes to name */ ! class Ed extends DefaultCellEditor { /** generated Serialized Version UID */ static final long serialVersionUID = -6373058702842751408L; *************** *** 289,294 **** --- 294,301 ---- else delegate.setValue(""); // NOI18N + editingIcon = ((VisualizerNode) value).getIcon(expanded); + ((JTextField) editorComponent).selectAll(); return editorComponent; } Index: openide/src/org/openide/explorer/view/VisualizerNode.java =================================================================== RCS file: /cvs/openide/src/org/openide/explorer/view/VisualizerNode.java,v retrieving revision 1.39 diff -c -r1.39 VisualizerNode.java *** openide/src/org/openide/explorer/view/VisualizerNode.java 20 Jan 2004 15:31:59 -0000 1.39 --- openide/src/org/openide/explorer/view/VisualizerNode.java 9 Mar 2004 23:28:32 -0000 *************** *** 13,21 **** --- 13,25 ---- package org.openide.explorer.view; + import java.awt.Image; + import java.beans.BeanInfo; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.*; + import javax.swing.Icon; + import javax.swing.ImageIcon; import javax.swing.SwingUtilities; import javax.swing.event.EventListenerList; import javax.swing.tree.TreeNode; *************** *** 23,36 **** import org.openide.ErrorManager; import org.openide.nodes.*; import org.openide.util.Mutex; import org.openide.util.enum.QueueEnumeration; /** Visual representation of one node. Holds necessary information about nodes * like icon, name, description and also list of its children. *

! * There is at most one VisualizerNode for one node. All of them are hold in a cache. *

! * The VisualizerNode level provides secure layer between Nodes and Swing AWT dispatch * thread. * * @author Jaroslav Tulach --- 27,41 ---- import org.openide.ErrorManager; import org.openide.nodes.*; import org.openide.util.Mutex; + import org.openide.util.Utilities; import org.openide.util.enum.QueueEnumeration; /** Visual representation of one node. Holds necessary information about nodes * like icon, name, description and also list of its children. *

! * There is at most one VisualizerNode for one node. All of them are held in a cache. *

! * The VisualizerNode level provides a thread-safe layer between Nodes and Swing AWT dispatch * thread. * * @author Jaroslav Tulach *************** *** 53,58 **** --- 58,68 ---- private static final QP QUEUE = new QP (); private static final ErrorManager err = ErrorManager.getDefault().getInstance("org.openide.explorer.view.VisualizerNode"); // NOI18N + + /** Cached icon - pre-html, there was a separate cache in NodeRenderer, but + * if we're keeping a weak cache of VisualizerNodes, there's no reason not + * to keep it here */ + private Icon icon = null; // bugfix #29435, getVisualizer is synchronized in place of be called only from EventQueue /** Finds VisualizerNode for given node. *************** *** 391,396 **** --- 401,452 ---- return getDisplayName (); } + public String getHtmlDisplayName() { + return node.getHtmlDisplayName(); + } + + Icon getIcon(boolean opened) { + //XXX - I have moved caching of icons out of NodeRenderer and put it + //here, since there wasn't much sense in having one weak cache of + //icons -> VisualizerNode, and another weak cache of + //VisualizerNode -> Node. However, we should measure this - it may be + //less expensive to create a new ImageIcon every time on the fly than + //to keep a copy of it here. + if (icon == null) { + Image image = opened ? node.getOpenedIcon( + BeanInfo.ICON_COLOR_16x16) : node.getIcon( + BeanInfo.ICON_COLOR_16x16); + + + // bugfix #28515, check if getIcon contract isn't broken + if (image == null) { + String method = opened ? "getOpenedIcon" : "getIcon"; // NOI18N + ErrorManager.getDefault ().log (ErrorManager.WARNING, "Node \"" + + node.getName () + + "\" [" + node.getClass().getName()+ + "] cannot return null from " + method + "(). See Node." + + method + " contract."); // NOI18N + + icon = defaultIcon; + } else { + icon = new ImageIcon (image); + } + } + return icon; + } + + /** loaded default icon */ + private static Icon defaultIcon; + /** default icon to use when none is present */ + private static final String DEFAULT_ICON = "org/openide/resources/defaultNode.gif"; // NOI18N + /** Loads default icon if not loaded. */ + private static Icon getDefaultIcon () { + if (defaultIcon == null) { + defaultIcon = new ImageIcon(Utilities.loadImage(DEFAULT_ICON)); + } + return defaultIcon; + } + /** Strong reference. */ private static final class StrongReference extends WeakReference { Index: openide/src/org/openide/filesystems/FileSystem.java =================================================================== RCS file: /cvs/openide/src/org/openide/filesystems/FileSystem.java,v retrieving revision 1.76 diff -c -r1.76 FileSystem.java *** openide/src/org/openide/filesystems/FileSystem.java 16 Jan 2004 11:16:31 -0000 1.76 --- openide/src/org/openide/filesystems/FileSystem.java 9 Mar 2004 23:28:34 -0000 *************** *** 684,690 **** */ public java.awt.Image annotateIcon (java.awt.Image icon, int iconType, java.util.Set files); } ! /** Empty status */ private static final Status STATUS_NONE = new Status () { public String annotateName (String name, java.util.Set files) { --- 684,717 ---- */ public java.awt.Image annotateIcon (java.awt.Image icon, int iconType, java.util.Set files); } ! ! /** Extension interface for Status provides HTML-formatted annotations. ! * Principally this is used to deëmphasize status text by presenting ! * it in a lighter color, by placing it inside ! * <font color=!controlShadow> tags. Note that it is preferable to ! * use logical colors (such as controlShadow) which are resolved by calling ! * UIManager.getColor(key) — this way they will always fit with the ! * look and feel. To use a logical color, prefix the color name with a ! * ! character. ! *

! * Please use only the limited markup subset of HTML supported by the ! * lightweight HTML renderer. ! * @see org.openide.awt.HtmlRenderer */ ! public static interface HtmlStatus extends Status { ! /** Annotate a name such that the returned value contains HTML markup. ! * The return value less the html content should typically be the same ! * as the return value from annotateName(). ! *

! * For consistency with Node.getHtmlDisplayName(), ! * filesystems that proxy other filesystems (and so must implement ! * this interface to supply HTML annotations) should return null if ! * the filesystem they proxy does not provide an implementation of ! * HTMLStatus. ! * ! * @see org.openide.awt.HtmlRenderer */ ! public String annotateNameHtml (String name, java.util.Set files); ! } ! /** Empty status */ private static final Status STATUS_NONE = new Status () { public String annotateName (String name, java.util.Set files) { Index: openide/src/org/openide/nodes/FilterNode.java =================================================================== RCS file: /cvs/openide/src/org/openide/nodes/FilterNode.java,v retrieving revision 1.88 diff -c -r1.88 FilterNode.java *** openide/src/org/openide/nodes/FilterNode.java 24 Feb 2004 13:34:06 -0000 1.88 --- openide/src/org/openide/nodes/FilterNode.java 9 Mar 2004 23:28:38 -0000 *************** *** 22,27 **** --- 22,28 ---- import java.util.*; import java.lang.ref.WeakReference; + import java.lang.reflect.Method; import org.openide.ErrorManager; import org.openide.util.datatransfer.NewType; *************** *** 643,648 **** --- 644,682 ---- } return retValue; + } + + /** Get a display name containing HTML markup. Note: If you subclass + * FilterNode and override getDisplayName(), this method will + * always return null unless you override it as well (assuming that if you're + * changing the display name, you don't want an HTML display name constructed + * from the original node's display name to be what shows up in views of + * this node. + * @see org.openide.nodes.Node.getHtmlDisplayName + * @return An HTML display name, if available, or null if no display name + * is available */ + public String getHtmlDisplayName() { + if (overridesGetDisplayName()) { + return null; + } else { + return delegating (DELEGATE_GET_DISPLAY_NAME) ? + original.getHtmlDisplayName() : super.getHtmlDisplayName(); + } + } + + private boolean overridesGetDisplayName() { + if (getClass() != FilterNode.class) { + try { + Method m = getClass().getMethod("getDisplayName", null); //NOI18N + return m.getDeclaringClass() != FilterNode.class; + } catch (NoSuchMethodException nsme) { + //can't happen + ErrorManager.getDefault().notify(nsme); + return true; + } + } else { + return false; + } } /* Index: openide/src/org/openide/nodes/Node.java =================================================================== RCS file: /cvs/openide/src/org/openide/nodes/Node.java,v retrieving revision 1.78 diff -c -r1.78 Node.java *** openide/src/org/openide/nodes/Node.java 13 Oct 2003 23:15:28 -0000 1.78 --- openide/src/org/openide/nodes/Node.java 9 Mar 2004 23:28:48 -0000 *************** *** 651,656 **** --- 651,674 ---- } } + /** Return a variant of the display name containing HTML markup + * conforming to the limited subset of font-markup HTML supported by + * the lightweight HTML renderer org.openide.awt.HtmlRenderer + * (font color, bold, italic and strikethrough supported; font + * colors can be UIManager color keys if they are prefixed with + * a ! character, i.e. <font color=&'controlShadow'>). + * Enclosing html tags are not needed. + *

This method should return either an HTML display name + * or null; it should not return the non-html display name. + * + * @see org.openide.awt.HtmlRenderer + * @return a String containing conformant, legal HTML markup which + * represents the display name, or null. The default implementation + * returns null. */ + public String getHtmlDisplayName() { + return null; + } + /** Register delegating lookup so it can always be found. */ final void registerDelegatingLookup (NodeLookup l) { *************** *** 1007,1012 **** --- 1025,1048 ---- public int hashCode () { return getName().hashCode (); } + + /** Return a variant of the display name containing HTML markup + * conforming to the limited subset of font-markup HTML supported by + * the lightweight HTML renderer org.openide.awt.HtmlRenderer + * (font color, bold, italic and strikethrough supported; font + * colors can be UIManager color keys if they are prefixed with + * a ! character, i.e. <font color=&'controlShadow'>). + * Enclosing html tags are not needed. + *

This method should return either an HTML display name + * or null; it should not return the non-html display name. + * + * @see org.openide.awt.HtmlRenderer + * @return a String containing conformant, legal HTML markup which + * represents the display name, or null. The default implementation + * returns null. */ + public String getHtmlDisplayName() { + return null; + } } /** Description of a Bean property on a node, and operations on it. *************** *** 1151,1156 **** --- 1187,1210 ---- return getName ().hashCode () * ( valueType == null ? 1 : valueType.hashCode () ); } + + /** Return a variant of the display name containing HTML markup + * conforming to the limited subset of font-markup HTML supported by + * the lightweight HTML renderer org.openide.awt.HtmlRenderer + * (font color, bold, italic and strikethrough supported; font + * colors can be UIManager color keys if they are prefixed with + * a ! character, i.e. <font color=&'controlShadow'>). + * Enclosing html tags are not needed. + *

This method should return either an HTML display name + * or null; it should not return the non-html display name. + * + * @see org.openide.awt.HtmlRenderer + * @return a String containing conformant, legal HTML markup which + * represents the display name, or null. The default implementation + * returns null. */ + public String getHtmlDisplayName() { + return null; + } } /** Description of an indexed property and operations on it. *************** *** 1289,1295 **** public String toString () { return super.toString () + "[Name="+getName ()+", displayName="+getDisplayName ()+"]"; // NOI18N } - /** template for changes in cookies */ private static final Lookup.Template TEMPL_COOKIE = new Lookup.Template (Node.Cookie.class); --- 1343,1348 ---- Index: treefs/src/org/netbeans/modules/treefs/TreeFS.java =================================================================== RCS file: /cvs/treefs/src/org/netbeans/modules/treefs/TreeFS.java,v retrieving revision 1.17 diff -c -r1.17 TreeFS.java *** treefs/src/org/netbeans/modules/treefs/TreeFS.java 5 Sep 2003 01:46:00 -0000 1.17 --- treefs/src/org/netbeans/modules/treefs/TreeFS.java 9 Mar 2004 23:29:12 -0000 *************** *** 48,54 **** * created is made upon an ExtensionList. */ public final class TreeFS extends MultiFileSystem ! implements FileSystem.Status, FileStatusListener, Runnable { // does not need to be necessarily final static final long serialVersionUID = -1173676775805432024L; --- 48,54 ---- * created is made upon an ExtensionList. */ public final class TreeFS extends MultiFileSystem ! implements FileSystem.Status, FileStatusListener, Runnable, FileSystem.HtmlStatus { // does not need to be necessarily final static final long serialVersionUID = -1173676775805432024L; *************** *** 431,434 **** --- 431,452 ---- org.openide.util.RequestProcessor.getDefault ().post (new Fire (), 0, Thread.MIN_PRIORITY); } + /** Proxies implementations of HtmlStatus, returns null if not found */ + public String annotateNameHtml(String name, java.util.Set files) { + if (files.isEmpty()) { + //probably the original filesystem was unmounted and this is a + //dead TreeFS. Just return the name and get out. + return name; + } + FileObject fo = (FileObject) files.iterator().next(); + //Get the real underlying filesystem + FileSystem fs = findSystem(fo); + if (fs.getStatus() instanceof FileSystem.HtmlStatus) { + FileSystem.HtmlStatus htmls = (FileSystem.HtmlStatus) fs.getStatus(); + return htmls.annotateNameHtml(name, translateFiles(fs, files)); + } else { + return null; + } + } + } Index: vcscore/src/org/netbeans/modules/vcscore/VcsFileSystem.java =================================================================== RCS file: /cvs/vcscore/src/org/netbeans/modules/vcscore/VcsFileSystem.java,v retrieving revision 1.250 diff -c -r1.250 VcsFileSystem.java *** vcscore/src/org/netbeans/modules/vcscore/VcsFileSystem.java 4 Mar 2004 15:23:20 -0000 1.250 --- vcscore/src/org/netbeans/modules/vcscore/VcsFileSystem.java 9 Mar 2004 23:29:49 -0000 *************** *** 103,109 **** AbstractFileSystem.Change, FileSystem.Status, CommandExecutionContext, CacheHandlerListener, FileObjectImportantness, FileObjectExistence, ! VcsOISActivator, Serializable { public static interface IgnoreListSupport { --- 103,110 ---- AbstractFileSystem.Change, FileSystem.Status, CommandExecutionContext, CacheHandlerListener, FileObjectImportantness, FileObjectExistence, ! VcsOISActivator, Serializable, ! FileSystem.HtmlStatus { public static interface IgnoreListSupport { *************** *** 4859,4864 **** --- 4860,4872 ---- private void D(String debug) { //System.out.println("VcsFileSystem(): "+debug); + } + + public String annotateNameHtml (String name, java.util.Set files) { + String result = annotateName (name, files); + result = Utilities.replaceString(result, name, + name + "") + ""; //NOI18N + return result; } //------------------------------------------- }