Index: core/swing/tabcontrol/demosrc/org/netbeans/swing/tabcontrol/demo/TestFrame.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/demosrc/org/netbeans/swing/tabcontrol/demo/TestFrame.java,v
--- core/swing/tabcontrol/demosrc/org/netbeans/swing/tabcontrol/demo/TestFrame.java 10 Aug 2004 13:56:37 -0000 1.5
+++ core/swing/tabcontrol/demosrc/org/netbeans/swing/tabcontrol/demo/TestFrame.java 10 Sep 2004 18:54:30 -0000
@@ -63,10 +63,16 @@
*/
try {
- //UIManager.setLookAndFeel(new javax.swing.plaf.metal.MetalLookAndFeel());
+// UIManager.setLookAndFeel(new javax.swing.plaf.metal.MetalLookAndFeel());
} catch (Exception e) {
}
+// UIManager.put ("EditorTabDisplayerUI", "org.netbeans.swing.tabcontrol.plaf.WinClassicEditorTabDisplayerUI");
+// UIManager.put ("ViewTabDisplayerUI", "org.netbeans.swing.tabcontrol.plaf.WinClassicViewTabDisplayerUI");
+// UIManager.put ("EditorTabDisplayerUI", "org.netbeans.swing.tabcontrol.plaf.WinXPEditorTabDisplayerUI");
+// UIManager.put ("ViewTabDisplayerUI", "org.netbeans.swing.tabcontrol.plaf.WinXPViewTabDisplayerUI");
+
+
JLabel jb1 = new JLabel("Label 1");
final JButton jb2 = new JButton("Button 2 - Update UI");
JButton jb3 = new JButton("Click me to remove this tab");
@@ -90,6 +96,14 @@
JTA.setColumns(80);
JTA.setLineWrap(true);
+ JButton jb7 = new JButton ("Remove non-contiguous tabs");
+ JButton jb8 = new JButton ("Discontig add");
+
+ JButton jb9 = new JButton ("Contig add");
+ JButton jb10 = new JButton ("Contig remove");
+
+ JButton jb11 = new JButton ("Discontig add and remove");
+
TabData tab0 = new TabData(jtr, myIcon, "0 JTree", "0");
TabData tab1 = new TabData(jb1, myIcon, "1 Tab 1", "1");
@@ -107,17 +121,17 @@
"7");
TabData tab8 = new TabData(new JLabel("gioo"), myIcon,
"8 something", "8");
- TabData tab9 = new TabData(new JButton("foo"), myIcon, "9 foob",
+ TabData tab9 = new TabData(jb7, myIcon, "9 Discontig remove",
"9");
TabData tab10 = new TabData(new JLabel("gioo"), myIcon, "10 wiggle",
"10");
- TabData tab11 = new TabData(new JButton("foo"), myIcon, "11 bumble",
+ TabData tab11 = new TabData(jb8, myIcon, "11 Discontig add",
"11");
- TabData tab12 = new TabData(new JLabel("mooble"), myIcon,
- "12 poodle", "12");
- TabData tab13 = new TabData(new JButton("fooble"), myIcon,
- "13 hoover", "13");
- TabData tab14 = new TabData(new JLabel("gooble"), myIcon, "14 snip",
+ TabData tab12 = new TabData(jb9, myIcon,
+ "12 contig add", "12");
+ TabData tab13 = new TabData(jb10, myIcon,
+ "13 contig remove", "13");
+ TabData tab14 = new TabData(jb11, myIcon, "14 MUNGE",
"14");
TabDataModel mdl = new DefaultTabDataModel(new TabData[]{
@@ -130,8 +144,10 @@
);
*/
- final TabbedContainer tab = new TabbedContainer(mdl, TabbedContainer.TYPE_EDITOR);
- tab.setActive(true);
+ final TabbedContainer tab = new TabbedContainer(mdl, TabbedContainer.TYPE_VIEW);
+ tab.setActive(true);
+ tab.requestAttention(5);
+ tab.requestAttention(3);
jb6.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
@@ -161,6 +177,72 @@
tab.updateUI();
}
});
+
+ jb7.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent ae) {
+ tab.getModel().removeTabs(new int[] {1, 3, 7, 8});
+ }
+ });
+
+ jb8.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent ae) {
+ int[] idxs = new int [] { 1, 3, 6, 8};
+ TabData[] td = new TabData[] {
+ new TabData(new JButton("inserted 1"), myIcon, "I-1", "tip"),
+ new TabData(new JButton("inserted 3"), myIcon, "I-3", "tip"),
+ new TabData(new JButton("inserted 6"), myIcon, "I-6", "tip"),
+ new TabData(new JButton("inserted 8"), myIcon, "I-8", "tip"),
+
+ };
+ tab.getModel().addTabs(idxs, td);
+ }
+ });
+
+ jb9.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent ae) {
+ int[] idxs = new int [] { 1, 3, 6, 8};
+ TabData[] td = new TabData[] {
+ new TabData(new JButton("inserted c 1"), myIcon, "Ic-1", "tip"),
+ new TabData(new JButton("inserted c 2"), myIcon, "Ic-2", "tip"),
+ new TabData(new JButton("inserted c 3"), myIcon, "Ic-3", "tip"),
+ new TabData(new JButton("inserted c 4"), myIcon, "Ic-4", "tip"),
+
+ };
+ tab.getModel().addTabs(1, td);
+ }
+ });
+
+ jb10.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent ae) {
+ tab.getModel().removeTabs(1, 4);
+ }
+ });
+
+ jb11.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent ae) {
+ TabData[] data = (TabData[]) tab.getModel().getTabs().toArray(new TabData[0]);
+
+ TabData[] newData = new TabData [ data.length - 3];
+ int ct = 0;
+
+ //strip out some tabs
+ for (int i=0; i < data.length; i++) {
+ newData[ct] = data[i];
+ if (i != 2 && i != 3 && i != 7) {
+ ct++;
+ }
+ }
+
+ //replace one tab
+ newData[1] = new TabData (new JLabel ("Hi there"), myIcon, "New tab", "foo");
+ //swap some tabs
+ TabData td = newData[8];
+ newData[8] = newData[7];
+ newData[7] = td;
+
+ tab.getModel().setTabs(newData);
+ }
+ });
tab.setActive(true);
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/DefaultTabDataModel.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/DefaultTabDataModel.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/DefaultTabDataModel.java 22 Apr 2004 13:09:06 -0000 1.2
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/DefaultTabDataModel.java 10 Sep 2004 18:54:30 -0000
@@ -327,9 +327,17 @@
fireIntervalRemoved(lde);
}
+ /**
+ * Remove a range of tabs from start
up to and including
+ * finish
.
+ */
public void removeTabs(int start, int end) {
java.util.List affected = list.subList(start, end);
- list.removeRange(start, end);
+ if (start == end) {
+ list.remove(start);
+ } else {
+ list.removeRange(start, end + 1);
+ }
ComplexListDataEvent lde = new ComplexListDataEvent(this,
ListDataEvent.INTERVAL_REMOVED,
start, end);
@@ -343,7 +351,6 @@
m.put(new Integer(indices[i]), data[i]);
}
Arrays.sort(indices);
-// for (int i=indices.length-1; i >= 0; i--) {
for (int i = 0; i < indices.length; i++) {
Integer key = new Integer(indices[i]);
TabData currData = (TabData) m.get(key);
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/SlidingButton.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/SlidingButton.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/SlidingButton.java 7 Jul 2004 07:31:19 -0000 1.4
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/SlidingButton.java 10 Sep 2004 18:54:30 -0000
@@ -12,10 +12,12 @@
*/
package org.netbeans.swing.tabcontrol;
+import java.awt.Color;
import java.awt.Component;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import javax.swing.Timer;
import javax.swing.JToggleButton;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
@@ -42,6 +44,8 @@
private int orientation;
/** Asociated component which actually slides from this button */
private Component component;
+
+ private TabData data;
/** Create a new button representing TabData from the model.
*
@@ -66,7 +70,7 @@
setBorderPainted(false);
// setHorizontalTextPosition(SwingConstants.);
// setVerticalTextPosition(SwingConstants.CENTER);
-
+ this.data = buttonData;
// note, updateUI() is called from superclass constructor
}
@@ -77,6 +81,7 @@
public void removeNotify() {
super.removeNotify();
+ setBlinking(false);
//XXX register with tooltip manager
}
@@ -100,6 +105,53 @@
/** Returns orinetation of this button */
public int getOrientation() {
return orientation;
+ }
+
+ public boolean isBlinking() {
+ return blinkState;
+ }
+
+ private Timer blinkTimer = null;
+ private boolean blinkState = false;
+ public void setBlinking (boolean val) {
+ if (!val && blinkTimer != null) {
+ blinkTimer.stop();
+ blinkTimer = null;
+ boolean wasBlinkState = blinkState;
+ blinkState = false;
+ if (wasBlinkState) {
+ repaint();
+ }
+ } else if (val && blinkTimer == null) {
+ blinkTimer = new Timer(700, new BlinkListener());
+ blinkState = true;
+ blinkTimer.start();
+ repaint();
+ }
+ }
+
+ private class BlinkListener implements ActionListener {
+ public void actionPerformed (ActionEvent ae) {
+ blinkState = !blinkState;
+ repaint();
+ }
+ }
+
+ /**
+ * Used by the UI to determine whether to use the blink
+ * color or the regular color
+ */
+ public final boolean isBlinkState() {
+ return blinkState;
+ }
+
+ public final Color getBackground() {
+ return isBlinkState() ?
+ new Color(252, 250, 244) : super.getBackground();
+ }
+
+ public TabData getData() {
+ return data;
}
}
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/SlidingButtonUI.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/SlidingButtonUI.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/SlidingButtonUI.java 2 Jun 2004 12:31:30 -0000 1.1
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/SlidingButtonUI.java 10 Sep 2004 18:54:31 -0000
@@ -210,7 +210,7 @@
Insets margin = b.getMargin();
g.fillRect(insets.left - margin.left,
- insets.top - margin.top,
+ insets.top - margin.top,
size.width - (insets.left-margin.left) - (insets.right - margin.right),
size.height - (insets.top-margin.top) - (insets.bottom - margin.bottom));
}
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/TabDisplayer.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/TabDisplayer.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/TabDisplayer.java 23 Aug 2004 12:22:15 -0000 1.8
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/TabDisplayer.java 10 Sep 2004 18:54:31 -0000
@@ -333,6 +333,31 @@
public final Dimension getMinimumSize() {
return getUI().getMinimumSize(this);
}
+
+ /**
+ * Cause the specified tab to flash or otherwise call attention to itself
+ * without changing selection or focus. Supported by VIEW and EDITOR type
+ * UIs.
+ */
+ public final void requestAttention (int tab) {
+ getUI().requestAttention(tab);
+ }
+
+ /**
+ * Cause a tab, if blinking, to stop.
+ */
+ public final void cancelRequestAttention (int tab) {
+ getUI().cancelRequestAttention (tab);
+ }
+
+ public final boolean requestAttention (TabData data) {
+ int idx = getModel().indexOf(data);
+ boolean result = idx >= 0;
+ if (result) {
+ requestAttention (idx);
+ }
+ return result;
+ }
/**
* Accessor only for TabDisplayerUI when installing the UI
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/TabDisplayerUI.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/TabDisplayerUI.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/TabDisplayerUI.java 10 Aug 2004 13:56:37 -0000 1.4
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/TabDisplayerUI.java 10 Sep 2004 18:54:32 -0000
@@ -184,5 +184,9 @@
public abstract void registerShortcuts (JComponent comp);
public abstract void unregisterShortcuts (JComponent comp);
+
+ protected abstract void requestAttention (int tab);
+
+ protected abstract void cancelRequestAttention (int tab);
}
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/TabbedContainer.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/TabbedContainer.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/TabbedContainer.java 11 Aug 2004 13:43:22 -0000 1.9
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/TabbedContainer.java 10 Sep 2004 18:54:33 -0000
@@ -556,6 +556,38 @@
firePropertyChange(PROP_ACTIVE, !active, active);
}
}
+
+ /**
+ * Cause the tab at the specified index to blink or otherwise suggest that
+ * the user should click it.
+ */
+ public final void requestAttention (int tab) {
+ getUI().requestAttention(tab);
+ }
+
+ public final void cancelRequestAttention (int tab) {
+ getUI().cancelRequestAttention(tab);
+ }
+
+ /**
+ * Cause the specified tab to blink or otherwisse suggest that the user should
+ * click it.
+ */
+ public final boolean requestAttention (TabData data) {
+ int idx = getModel().indexOf(data);
+ boolean result = idx >= 0;
+ if (result) {
+ requestAttention (idx);
+ }
+ return result;
+ }
+
+ public final void cancelRequestAttention (TabData data) {
+ int idx = getModel().indexOf(data);
+ if (idx != -1) {
+ cancelRequestAttention(idx);
+ }
+ }
/**
* Determine if this component thinks it is "active", which
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/TabbedContainerUI.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/TabbedContainerUI.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/TabbedContainerUI.java 10 Aug 2004 13:56:37 -0000 1.4
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/TabbedContainerUI.java 10 Sep 2004 18:54:33 -0000
@@ -199,4 +199,8 @@
*/
public abstract int dropIndexOfPoint(Point p);
+ protected abstract void requestAttention (int tab);
+
+ protected abstract void cancelRequestAttention (int tab);
+
}
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AbstractTabCellRenderer.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AbstractTabCellRenderer.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AbstractTabCellRenderer.java 8 Sep 2004 22:48:58 -0000 1.4
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AbstractTabCellRenderer.java 10 Sep 2004 18:54:33 -0000
@@ -190,6 +190,10 @@
protected final boolean isRightmost() {
return (state & TabState.RIGHTMOST) != 0;
}
+
+ protected final boolean isAttention() {
+ return (state & TabState.ATTENTION) != 0;
+ }
/**
* Convenience getter to determine if the current state indicates
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AbstractViewTabDisplayerUI.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AbstractViewTabDisplayerUI.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AbstractViewTabDisplayerUI.java 2 Sep 2004 13:43:09 -0000 1.15
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AbstractViewTabDisplayerUI.java 10 Sep 2004 18:54:34 -0000
@@ -13,6 +13,7 @@
package org.netbeans.swing.tabcontrol.plaf;
+import javax.swing.event.ListDataEvent;
import org.netbeans.swing.tabcontrol.TabData;
import org.netbeans.swing.tabcontrol.TabDataModel;
import org.netbeans.swing.tabcontrol.TabDisplayer;
@@ -38,6 +39,8 @@
import java.util.Map;
import org.netbeans.swing.tabcontrol.LocationInformer;
import org.netbeans.swing.tabcontrol.TabbedContainer;
+import org.netbeans.swing.tabcontrol.event.ComplexListDataEvent;
+import org.netbeans.swing.tabcontrol.event.ComplexListDataListener;
/**
* Basic UI class for view tabs - non scrollable tabbed displayer, which shows all
@@ -83,6 +86,7 @@
dataModel = displayer.getModel();
layoutModel = new ViewTabLayoutModel(dataModel, displayer);
dataModel.addChangeListener (controller);
+ dataModel.addComplexListDataListener(controller);
displayer.addPropertyChangeListener (controller);
selectionModel.addChangeListener (controller);
displayer.addMouseListener(controller);
@@ -106,6 +110,7 @@
ToolTipManager.sharedInstance().unregisterComponent(displayer);
displayer.removePropertyChangeListener (controller);
dataModel.removeChangeListener(controller);
+ dataModel.removeComplexListDataListener(controller);
selectionModel.removeChangeListener(controller);
displayer.removeMouseListener(controller);
displayer.removeMouseMotionListener(controller);
@@ -503,9 +508,12 @@
}
public Rectangle getTabRect(int index, Rectangle destination) {
+ if (destination == null) {
+ destination = new Rectangle();
+ }
if (index < 0 || index > displayer.getModel().size()) {
- throw new ArrayIndexOutOfBoundsException("Tab index out of " +
- "bounds: " + index);
+ destination.setBounds (0,0,0,0);
+ return destination;
}
destination.x = layoutModel.getX(index);
destination.width = layoutModel.getW(index);
@@ -532,14 +540,64 @@
return -1;
}
+
+ protected int createRepaintPolicy () {
+ return TabState.REPAINT_SELECTION_ON_ACTIVATION_CHANGE
+ | TabState.REPAINT_ON_SELECTION_CHANGE
+ | TabState.REPAINT_ON_MOUSE_ENTER_TAB
+ | TabState.REPAINT_ON_MOUSE_ENTER_CLOSE_BUTTON
+ | TabState.REPAINT_ON_MOUSE_PRESSED;
+ }
+
+ protected final TabState tabState = new ViewTabState();
+
+ private class ViewTabState extends TabState {
+ public int getRepaintPolicy(int tab) {
+ return createRepaintPolicy();
+ }
+
+ public void repaintAllTabs() {
+ displayer.repaint();
+ }
+
+ public void repaintTab (int tab) {
+ if (tab < 0 || tab > displayer.getModel().size()) {
+ //This can happen because we can be notified
+ //of a change on a tab that has just been removed
+ //from the model
+ return;
+ }
+ Rectangle r = getTabRect(tab, null);
+ displayer.repaint(r);
+ }
+ }
+
+ /**
+ * Determine if the tab should be flashing
+ */
+ protected boolean isAttention (int tab) {
+ return (tabState.getState(tab) & TabState.ATTENTION) != 0;
+ }
+
+
+ protected void requestAttention (int tab) {
+ tabState.addAlarmTab(tab);
+ }
+
+ protected void cancelRequestAttention (int tab) {
+ tabState.removeAlarmTab(tab);
+ }
/**
* Listen to mouse events and handles selection behaviour and close icon
* button behaviour.
*/
abstract class Controller extends MouseAdapter
- implements MouseMotionListener, ChangeListener, PropertyChangeListener, ActionListener {
+ implements MouseMotionListener, ChangeListener, PropertyChangeListener, ActionListener, ComplexListDataListener {
+ //XXX should be able to replace most of this class with
+ //tabState - we're already using it to manage the blinking state
+
/**
* index of tab whose close icon currently pressed, -1 otherwise
*/
@@ -596,11 +654,13 @@
public void mousePressed(MouseEvent e) {
Point p = e.getPoint();
int i = getLayoutModel().indexOfPoint(p.x, p.y);
+ tabState.setPressed(i);
SingleSelectionModel sel = getSelectionModel();
selectionChanged = i != sel.getSelectedIndex();
// invoke possible selection change
if ((i != -1) || !selectionChanged) {
getSelectionModel().setSelectedIndex(i);
+ tabState.setSelected(i);
}
// update pressed state
if (shouldReact(e) && !selectionChanged) {
@@ -632,6 +692,7 @@
public void mouseReleased(MouseEvent e) {
// close button must not be active when selection change was
// triggered by mouse press
+ tabState.setPressed(-1);
if (shouldReact(e) && !selectionChanged) {
setClosePressed(-1);
Point point = e.getPoint();
@@ -721,6 +782,7 @@
// sync of indexes
int oldValue = mouseInCloseButton;
mouseInCloseButton = isNow;
+ tabState.setCloseButtonContainsMouse(isNow);
if (isNow == -1) {
// exit from close area
TabLayoutModel tlm = getLayoutModel();
@@ -745,7 +807,44 @@
public void actionPerformed(ActionEvent e) {
performPinAction();
}
+
+ public void indicesAdded(ComplexListDataEvent e) {
+ tabState.indicesAdded(e);
+ }
+
+ /**
+ * Elements have been removed at the indices specified by the event's
+ * getIndices() value
+ *
+ * @param e The event
+ */
+ public void indicesRemoved(ComplexListDataEvent e) {
+ tabState.indicesRemoved(e);
+ }
+
+ /**
+ * Elements have been changed at the indices specified by the event's
+ * getIndices() value. If the changed data can affect display width (such
+ * as a text change or a change in icon size), the event's
+ * isTextChanged()
method will return true.
+ *
+ * @param e The event
+ */
+ public void indicesChanged(ComplexListDataEvent e) {
+ tabState.indicesChanged(e);
+ }
+ public void intervalAdded (ListDataEvent evt) {
+ tabState.intervalAdded(evt);
+ }
+
+ public void intervalRemoved (ListDataEvent evt) {
+ tabState.intervalRemoved(evt);
+ }
+
+ public void contentsChanged(ListDataEvent evt) {
+ tabState.contentsChanged(evt);
+ }
} // end of Controller
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AquaEditorTabCellRenderer.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AquaEditorTabCellRenderer.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AquaEditorTabCellRenderer.java 15 Jun 2004 19:58:32 -0000 1.3
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AquaEditorTabCellRenderer.java 10 Sep 2004 18:54:35 -0000
@@ -140,6 +140,8 @@
boolean leftmost = ((AquaEditorTabCellRenderer) c).isLeftmost();
boolean closing = pressed
&& ((AquaEditorTabCellRenderer) c).inCloseButton();
+ boolean attention = !pressed && !closing
+ && ((AquaEditorTabCellRenderer) c).isAttention();
//add in a pixel for rightmost/leftmost so we don't clip off
//antialiasing of the curve
@@ -161,6 +163,9 @@
}
if (closing) {
state |= GenericGlowingChiclet.STATE_CLOSING;
+ }
+ if (attention) {
+ state |= GenericGlowingChiclet.STATE_ATTENTION;
}
chiclet.setArcs(leftarc, rightarc, leftarc, rightarc);
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AquaSlidingButtonUI.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AquaSlidingButtonUI.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AquaSlidingButtonUI.java 15 Jun 2004 21:35:42 -0000 1.3
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AquaSlidingButtonUI.java 10 Sep 2004 18:54:35 -0000
@@ -27,6 +27,7 @@
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.text.View;
+import org.netbeans.swing.tabcontrol.SlidingButton;
import org.netbeans.swing.tabcontrol.SlidingButtonUI;
import org.netbeans.swing.tabcontrol.plaf.GenericGlowingChiclet;
@@ -73,7 +74,7 @@
protected void paintBackground(Graphics2D graph, AbstractButton b) {
chic.setNotch(false, false);
- chic.setState(0);
+ chic.setState(((SlidingButton) b).isBlinkState() ? GenericGlowingChiclet.STATE_ATTENTION : 0);
chic.setArcs(0.5f, 0.5f, 0.5f, 0.5f);
chic.setBounds(0, 1, b.getWidth() - 2, b.getHeight() - 2);
chic.setAllowVertical(true);
@@ -87,6 +88,7 @@
state |= b.getModel().isSelected() ? GenericGlowingChiclet.STATE_SELECTED : 0;
state |= b.getModel().isPressed() ? GenericGlowingChiclet.STATE_PRESSED : 0;
state |= b.getModel().isRollover() ? GenericGlowingChiclet.STATE_ACTIVE : 0;
+ state |= ((SlidingButton) b).isBlinkState() ? GenericGlowingChiclet.STATE_ATTENTION : 0;
chic.setState(state);
chic.setArcs(0.5f, 0.5f, 0.5f, 0.5f);
chic.setBounds(0, 1, b.getWidth() - 2, b.getHeight() - 2);
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AquaViewTabDisplayerUI.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AquaViewTabDisplayerUI.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AquaViewTabDisplayerUI.java 1 Sep 2004 15:06:05 -0000 1.9
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AquaViewTabDisplayerUI.java 10 Sep 2004 18:54:35 -0000
@@ -205,6 +205,9 @@
if (isSelected(index)) {
state |= GenericGlowingChiclet.STATE_SELECTED;
}
+ if (isAttention(index)) {
+ state |= GenericGlowingChiclet.STATE_ATTENTION;
+ }
chiclet.setState(state);
chiclet.setBounds(x, y, width, height);
@@ -299,9 +302,13 @@
Point p = e.getPoint();
int i = getLayoutModel().indexOfPoint(p.x, p.y);
int closeRectIdx = inCloseIconRect(p);
+ tabState.setPressed(i);
+ tabState.setCloseButtonContainsMouse(closeRectIdx);
+ tabState.setMousePressedInCloseButton(closeRectIdx);
// invoke possible selection change
if ((i != -1) && closeRectIdx == -1) {
getSelectionModel().setSelectedIndex(i);
+ tabState.setSelected(i);
}
if (shouldReact(e) && closeRectIdx != -1) {
setClosePressed(closeRectIdx);
@@ -317,6 +324,8 @@
}
public void mouseReleased(MouseEvent e) {
+ tabState.setMousePressedInCloseButton(-1);
+ tabState.setPressed(-1);
// close button must not be active when selection change was
// triggered by mouse press
if (shouldReact(e)) {
@@ -338,10 +347,12 @@
public void mouseEntered(MouseEvent me) {
setContainsMouse(true);
+ tabState.setMouseInTabsArea(true);
}
public void mouseExited(MouseEvent me) {
setContainsMouse(false);
+ tabState.setMouseInTabsArea(false);
}
} // end of OwnController
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/BasicSlidingTabDisplayerUI.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/BasicSlidingTabDisplayerUI.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/BasicSlidingTabDisplayerUI.java 22 Apr 2004 13:09:11 -0000 1.2
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/BasicSlidingTabDisplayerUI.java 10 Sep 2004 18:54:36 -0000
@@ -127,6 +127,14 @@
return new MouseAdapter() {}; //XXX
}
+ public void requestAttention (int tab) {
+ //not implemented
+ }
+
+ public void cancelRequestAttention (int tab) {
+ //not implemented
+ }
+
protected ChangeListener createSelectionListener() {
return new ChangeListener() {
private int lastKnownSelection = -1;
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/BasicTabDisplayerUI.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/BasicTabDisplayerUI.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/BasicTabDisplayerUI.java 2 Sep 2004 13:43:09 -0000 1.5
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/BasicTabDisplayerUI.java 10 Sep 2004 18:54:37 -0000
@@ -12,6 +12,7 @@
*/
package org.netbeans.swing.tabcontrol.plaf;
+import javax.swing.event.ListDataEvent;
import org.netbeans.swing.tabcontrol.TabData;
import org.netbeans.swing.tabcontrol.TabDisplayer;
import org.netbeans.swing.tabcontrol.TabbedContainer;
@@ -24,6 +25,7 @@
import java.awt.geom.Area;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeListener;
+import org.netbeans.swing.tabcontrol.event.ComplexListDataEvent;
/**
* Base class for tab displayer UIs which use cell renderers to display tabs.
@@ -117,6 +119,11 @@
defaultRenderer = null;
super.uninstall();
}
+
+ /** Used by unit tests */
+ TabState getTabState() {
+ return tabState;
+ }
/**
* Create a TabState instance. TabState manages the state of tabs - that is, which one
@@ -218,6 +225,10 @@
if (rect == null) {
rect = new Rectangle();
}
+ if (idx < 0 || idx >= displayer.getModel().size()) {
+ rect.x = rect.y = rect.width = rect.height = 0;
+ return rect;
+ }
rect.x = layoutModel.getX(idx);
rect.y = layoutModel.getY(idx);
rect.width = layoutModel.getW(idx);
@@ -449,6 +460,15 @@
protected void processMouseWheelEvent(MouseWheelEvent e) {
//do nothing
}
+
+ protected final void requestAttention (int tab) {
+ tabState.addAlarmTab(tab);
+ }
+
+ protected final void cancelRequestAttention (int tab) {
+ tabState.removeAlarmTab(tab);
+ }
+
protected void modelChanged() {
tabState.clearTransientStates();
@@ -457,6 +477,7 @@
//sync
int idx = selectionModel.getSelectedIndex();
tabState.setSelected(idx);
+ tabState.pruneAlarmTabs(displayer.getModel().size());
super.modelChanged();
}
@@ -501,16 +522,10 @@
}
protected void repaintAllTabs() {
- getTabsVisibleArea(scratch);
- if (scratch.height < displayer.getHeight()) {
- //Ensure any gap at the bottom is repainted
- scratch.y = 0;
- scratch.height = displayer.getHeight();
- }
-/* displayer.repaint(scratch.x, scratch.y, scratch.width,
- scratch.height);
- */
- //XXX optimize this
+ //XXX would be nicer to just repaint the tabs area,
+ //but we also need to repaint below all the tabs in the
+ //event of activated/deactivated. No actual reason to
+ //repaint the buttons here.
displayer.repaint();
}
@@ -530,6 +545,10 @@
scratch.height);
}
}
+
+ protected ModelListener createModelListener() {
+ return new BasicModelListener();
+ }
private class BasicDisplayerPropertyChangeListener
extends DisplayerPropertyChangeListener {
@@ -722,10 +741,43 @@
assert e.getSource() == selectionModel : "Unknown event source: "
+ e.getSource();
int idx = selectionModel.getSelectedIndex();
- tabState.setSelected(idx);
- if (idx != -1) {
+ tabState.setSelected(idx >= 0 ? idx : -1);
+ if (idx >= 0) {
makeTabVisible (selectionModel.getSelectedIndex());
}
}
}
+
+ /**
+ * Listener on data model which will pass modified indices to the
+ * TabState object, so it can update which tab indices are flashing in
+ * "attention" mode, if any.
+ */
+ protected class BasicModelListener extends ModelListener {
+ public void contentsChanged(ListDataEvent e) {
+ super.contentsChanged(e);
+ tabState.contentsChanged(e);
+ }
+
+ public void indicesAdded(ComplexListDataEvent e) {
+ super.indicesAdded(e);
+ tabState.indicesAdded(e);
+ }
+
+ public void indicesChanged(ComplexListDataEvent e) {
+ tabState.indicesChanged(e);
+ }
+
+ public void indicesRemoved(ComplexListDataEvent e) {
+ tabState.indicesRemoved(e);
+ }
+
+ public void intervalAdded(ListDataEvent e) {
+ tabState.intervalAdded(e);
+ }
+
+ public void intervalRemoved(ListDataEvent e) {
+ tabState.intervalRemoved(e);
+ }
+ }
}
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/DefaultTabSelectionModel.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/DefaultTabSelectionModel.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/DefaultTabSelectionModel.java 22 Apr 2004 13:09:13 -0000 1.2
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/DefaultTabSelectionModel.java 10 Sep 2004 18:54:37 -0000
@@ -88,7 +88,7 @@
}
private void adjustSelectionForEvent(ListDataEvent e) {
- if (e.getType() == e.CONTENTS_CHANGED) {
+ if (e.getType() == e.CONTENTS_CHANGED || sel == -1) {
return;
}
int start = e.getIndex0();
@@ -140,6 +140,7 @@
}
public void indicesAdded(ComplexListDataEvent e) {
+ if (sel < 0) return;
int[] indices = e.getIndices();
java.util.Arrays.sort(indices);
int offset = 0;
@@ -157,6 +158,7 @@
}
public void indicesRemoved(ComplexListDataEvent e) {
+ if (sel < 0) return;
int[] indices = e.getIndices();
java.util.Arrays.sort(indices);
int offset = -1;
@@ -176,12 +178,13 @@
sel = -1;
fireStateChanged();
} else if (offset != 0) {
- sel += offset;
+ sel = Math.max( -1, Math.min (sel + offset, -1));
fireStateChanged();
}
}
public void indicesChanged(ComplexListDataEvent e) {
+ if (sel < 0) return;
if (e instanceof VeryComplexListDataEvent) { //it always will be
ArrayDiff dif = ((VeryComplexListDataEvent) e).getDiff();
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/DefaultTabbedContainerUI.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/DefaultTabbedContainerUI.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/DefaultTabbedContainerUI.java 11 Aug 2004 17:09:17 -0000 1.12
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/DefaultTabbedContainerUI.java 10 Sep 2004 18:54:39 -0000
@@ -182,8 +182,11 @@
//#41681, #44278, etc. FOCUS related. the UI needs to be the first listener to be notified
// that the selection has changed. Otherwise strange focus effects kick in, eg. when the winsys snapshot gets undated beforehand..
tabDisplayer.getSelectionModel().addChangeListener(selectionListener);
-
-
+ }
+
+ /** Used by unit tests */
+ TabDisplayer getTabDisplayer() {
+ return tabDisplayer;
}
/** This method is final. Subclasses which need to provide additional initialization should override
@@ -604,6 +607,14 @@
r.x += p.x;
r.y += p.y;
return r;
+ }
+
+ protected void requestAttention (int tab) {
+ tabDisplayer.requestAttention (tab);
+ }
+
+ protected void cancelRequestAttention (int tab) {
+ tabDisplayer.cancelRequestAttention(tab);
}
/**
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/GenericGlowingChiclet.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/GenericGlowingChiclet.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/GenericGlowingChiclet.java 22 Apr 2004 13:09:13 -0000 1.2
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/GenericGlowingChiclet.java 10 Sep 2004 18:54:40 -0000
@@ -34,6 +34,7 @@
public static final int STATE_SELECTED = 2;
public static final int STATE_ACTIVE = 4;
public static final int STATE_CLOSING = 8;
+ public static final int STATE_ATTENTION = 16;
//Basic apple colors. Package access so that GtkChiclet can install its own
//defaults if the GTK classes it proxies for colors are not available or
@@ -62,6 +63,10 @@
new Color(255, 238, 220), new Color(238, 137, 109),
new Color(255, 50, 50), new Color(255, 237, 40)};
+ static Color[] attention = new Color[]{
+ new Color(255, 255, 220), new Color(238, 237, 109),
+ new Color(255, 255, 50), new Color(255, 237, 40)};
+
private Color upperTop = selectedActive[0];
private Color upperBottom = selectedActive[1];
private Color lowerTop = selectedActive[2];
@@ -124,6 +129,8 @@
Color[] nue;
if ((state & STATE_CLOSING) != 0) {
nue = closing;
+ } else if ((state & STATE_ATTENTION) != 0) {
+ nue = attention;
} else {
switch (state) {
case STATE_PRESSED | STATE_ACTIVE:
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/MetalEditorTabCellRenderer.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/MetalEditorTabCellRenderer.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/MetalEditorTabCellRenderer.java 22 Apr 2004 13:09:15 -0000 1.2
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/MetalEditorTabCellRenderer.java 10 Sep 2004 18:54:40 -0000
@@ -31,6 +31,7 @@
private static final MetalRightClippedTabPainter rightBorder = new MetalRightClippedTabPainter();
private static final MetalLeftClippedTabPainter leftBorder = new MetalLeftClippedTabPainter();
+ static final Color ATTENTION_COLOR = new Color(255, 238, 120);
/**
* Creates a new instance of MetalEditorTabCellRenderer
*/
@@ -129,6 +130,10 @@
public void paintInterior(Graphics g, Component c) {
MetalEditorTabCellRenderer mtr = (MetalEditorTabCellRenderer) c;
+ if (mtr.isAttention()) {
+ g.setColor(ATTENTION_COLOR);
+ }
+
Polygon p = getInteriorPolygon(c);
g.fillPolygon(p);
Rectangle r = new Rectangle();
@@ -246,6 +251,11 @@
public void paintInterior(Graphics g, Component c) {
Polygon p = getInteriorPolygon(c);
+ MetalEditorTabCellRenderer mtr = (MetalEditorTabCellRenderer) c;
+ if (mtr.isAttention()) {
+ g.setColor(ATTENTION_COLOR);
+ }
+
g.fillPolygon(p);
}
@@ -340,6 +350,10 @@
public void paintInterior(Graphics g, Component c) {
Polygon p = getInteriorPolygon(c);
+ MetalEditorTabCellRenderer mtr = (MetalEditorTabCellRenderer) c;
+ if (mtr.isAttention()) {
+ g.setColor(ATTENTION_COLOR);
+ }
g.fillPolygon(p);
}
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/MetalSlidingButtonUI.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/MetalSlidingButtonUI.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/MetalSlidingButtonUI.java 2 Sep 2004 13:43:09 -0000 1.3
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/MetalSlidingButtonUI.java 10 Sep 2004 18:54:40 -0000
@@ -101,10 +101,12 @@
}
protected void paintBackground (Graphics2D g, AbstractButton button) {
+ hiddenToggle.setBackground (button.getBackground());
hiddenToggle.paint(g);
}
protected void paintButtonPressed(Graphics g, AbstractButton b) {
+ hiddenToggle.setBackground (b.getBackground());
hiddenToggle.paint(g);
}
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/MetalViewTabDisplayerUI.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/MetalViewTabDisplayerUI.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/MetalViewTabDisplayerUI.java 1 Sep 2004 15:06:05 -0000 1.9
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/MetalViewTabDisplayerUI.java 10 Sep 2004 18:54:41 -0000
@@ -196,8 +196,12 @@
int width, int height) {
boolean selected = isSelected(index);
boolean highlighted = selected && isActive();
- if (highlighted) {
+ boolean attention = isAttention (index);
+ if (highlighted && !attention) {
g.setColor(getActBgColor());
+ g.fillRect(x, y, width, height - 3);
+ } else if (attention) {
+ g.setColor(MetalEditorTabCellRenderer.ATTENTION_COLOR);
g.fillRect(x, y, width, height - 3);
} else {
g.setColor(getInactBgColor());
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/TabState.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/TabState.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/TabState.java 9 Sep 2004 00:08:45 -0000 1.3
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/TabState.java 10 Sep 2004 18:54:42 -0000
@@ -12,6 +12,22 @@
*/
package org.netbeans.swing.tabcontrol.plaf;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import javax.swing.Timer;
+import javax.swing.event.ListDataEvent;
+import org.netbeans.swing.tabcontrol.TabData;
+import org.netbeans.swing.tabcontrol.event.ArrayDiff;
+import org.netbeans.swing.tabcontrol.event.ComplexListDataEvent;
+import org.netbeans.swing.tabcontrol.event.VeryComplexListDataEvent;
+import org.openide.util.Utilities;
+
/**
* Used by BasicTabDisplayerUI and its subclasses.
* Tracks and manages the state of tabs, mainly which one currently contains the
@@ -142,7 +158,18 @@
*/
public static final int MOUSE_IN_TABS_AREA = 4096;
+ /**
+ * Bitmask indicating that the mouse is inside the close button and has
+ * been pressed.
+ */
public static final int MOUSE_PRESSED_IN_CLOSE_BUTTON = 8192;
+
+ /**
+ * Bitmask indicating that the tab is in "attention" mode - blinking or
+ * flashing to get the user's attention.
+ */
+ public static final int ATTENTION = 16384;
+
/**
* Indicates the last constant defined - renderers that wish to add their
* own bitmasks should use multiples of this number
@@ -224,6 +251,9 @@
if (tab == selectedIndex - 1) {
result |= BEFORE_SELECTED;
}
+ if (isAlarmTab(tab)) {
+ result |= ATTENTION;
+ }
return result;
}
@@ -328,6 +358,7 @@
prev = selectedIndex;
selectedIndex = i;
curr = i;
+ removeAlarmTab(i);
possibleChange(prev, curr, SELECTED);
return prev;
}
@@ -356,8 +387,304 @@
boolean prev = active;
active = b;
possibleChange(prev, b, ACTIVE);
+ removeAlarmTab(selectedIndex);
return prev;
}
+
+ private boolean isAlarmTab (int tab) {
+ return attentionToggle && alarmTabs.contains(new Integer(tab));
+ }
+
+ private final HashSet alarmTabs = new HashSet(6);
+
+ /** Add a tab to the list of those which should "flash" or otherwise give
+ * some notification to the user to get their attention */
+ public final void addAlarmTab (int alarmTab) {
+ Integer in = new Integer(alarmTab);
+ boolean added = alarmTabs.contains(in);
+ boolean wasEmpty = alarmTabs.isEmpty();
+ if (!added) {
+ alarmTabs.add (new Integer(alarmTab));
+ repaintTab (alarmTab);
+ }
+ if (wasEmpty) {
+ startAlarmTimer();
+ attentionToggle = true;
+ repaintTab (alarmTab);
+ }
+ }
+
+ /** Remove a tab to the list of those which should "flash" or otherwise give
+ * some notification to the user to get their attention */
+ public final void removeAlarmTab (int alarmTab) {
+ Integer in = new Integer(alarmTab);
+ boolean contained = alarmTabs.contains(in);
+ if (contained) {
+ alarmTabs.remove(in);
+ boolean empty = alarmTabs.isEmpty();
+ boolean wasAttentionToggled = attentionToggle;
+ if (alarmTabs.isEmpty()) {
+ stopAlarmTimer();
+ }
+ if (wasAttentionToggled) {
+ repaintTab(alarmTab);
+ }
+ }
+ }
+
+ private Timer alarmTimer = null;
+ private boolean attentionToggle = false;
+ private final void startAlarmTimer() {
+ if (alarmTimer == null) {
+ ActionListener al = new ActionListener() {
+ public void actionPerformed (ActionEvent ae) {
+ attentionToggle = !attentionToggle;
+ Timer timer = (Timer) ae.getSource();
+ for (Iterator i=alarmTabs.iterator(); i.hasNext();) {
+ repaintTab (((Integer) i.next()).intValue());
+ }
+ }
+ };
+ alarmTimer = new Timer (700, al);
+ alarmTimer.setRepeats(true);
+ }
+ alarmTimer.start();
+ }
+
+ private final void stopAlarmTimer() {
+ if (alarmTimer != null && alarmTimer.isRunning()) {
+ alarmTimer.stop();
+ attentionToggle = false;
+ repaintAllTabs(); //XXX optimize
+ }
+ }
+
+ boolean hasAlarmTabs() {
+ return alarmTabs != null && !alarmTabs.isEmpty();
+ }
+
+ void pruneAlarmTabs(int max) {
+ if (!hasAlarmTabs()) {
+ return;
+ }
+ for (Iterator i=alarmTabs.iterator(); i.hasNext();) {
+ if (((Integer) i.next()).intValue() >= max) {
+ i.remove();
+ }
+ }
+ if (alarmTabs.isEmpty()) {
+ stopAlarmTimer();
+ }
+ }
+
+ int[] getAlarmTabs() {
+ int[] alarms = (int[]) Utilities.toPrimitiveArray((Integer[]) alarmTabs.toArray(new Integer[0]));
+ Arrays.sort(alarms);
+ return alarms;
+ }
+
+ //Handling of insertions/deletions where we'll need to update the
+ //list of blinking tabs here.
+ void intervalAdded (ListDataEvent evt) {
+ if (!hasAlarmTabs()) return;
+ int start = evt.getIndex0();
+ int end = evt.getIndex1();
+ int[] alarms = (int[]) Utilities.toPrimitiveArray((Integer[]) alarmTabs.toArray(new Integer[0]));
+ boolean changed = false;
+ for (int i=0; i < alarms.length; i++) {
+ if (alarms[i] >= start) {
+ alarms[i] += (end - start) + 1;
+ changed = true;
+ }
+ }
+ if (changed) {
+ alarmTabs.clear();
+ for (int i=0; i < alarms.length; i++) {
+ addAlarmTab(alarms[i]);
+ }
+ }
+ }
+
+ void intervalRemoved (ListDataEvent evt) {
+ if (!hasAlarmTabs()) return;
+ int start = evt.getIndex0();
+ int end = evt.getIndex1();
+
+ int[] alarms = (int[]) Utilities.toPrimitiveArray((Integer[]) alarmTabs.toArray(new Integer[0]));
+ Arrays.sort(alarms);
+
+ if (end == start) {
+ //Faster to handle this case separately
+ boolean changed = true;
+ for (int i=0; i < alarms.length; i++) {
+ if (alarms[i] > end) {
+ alarms[i]--;
+ } else if (alarms[i] == end) {
+ alarms[i] = -1;
+ }
+ }
+ if (changed) {
+ alarmTabs.clear();
+ boolean added = false;
+ for (int i=0; i < alarms.length; i++) {
+ if (alarms[i] != -1) {
+ addAlarmTab(alarms[i]);
+ added = true;
+ }
+ }
+ if (!added) {
+ stopAlarmTimer();
+ }
+ }
+ return;
+ }
+
+ boolean changed = false;
+ for (int i=0; i < alarms.length; i++) {
+ if (alarms[i] >= start && alarms[i] <= end) {
+ alarms[i] = -1;
+ changed = true;
+ }
+ }
+ for (int i=0; i < alarms.length; i++) {
+ if (alarms[i] > end) {
+ alarms[i] -= (end - start) + 1;
+ changed = true;
+ }
+ }
+ if (changed) {
+ alarmTabs.clear();
+ boolean added = false;
+ for (int i=0; i < alarms.length; i++) {
+ if (alarms[i] != -1) {
+ addAlarmTab(alarms[i]);
+ added = true;
+ }
+ }
+ if (!added) {
+ stopAlarmTimer();
+ }
+ }
+ }
+
+ void indicesAdded (ComplexListDataEvent e) {
+ if (!hasAlarmTabs()) return;
+ int[] alarms = (int[]) Utilities.toPrimitiveArray((Integer[]) alarmTabs.toArray(new Integer[0]));
+ java.util.Arrays.sort(alarms);
+
+ int[] indices = e.getIndices();
+ java.util.Arrays.sort(indices);
+
+ boolean changed = false;
+ for (int i=0; i < indices.length; i++) {
+ for (int j=0; j < alarms.length; j++) {
+ if (alarms[j] >= indices[i]) {
+ alarms[j]++;
+ changed = true;
+ }
+ }
+ }
+ if (changed) {
+ alarmTabs.clear();
+ for (int i=0; i < alarms.length; i++) {
+ if (alarms[i] != -1) {
+ addAlarmTab(alarms[i]);
+ }
+ }
+ }
+ }
+
+ void indicesRemoved (ComplexListDataEvent e) {
+ if (!hasAlarmTabs()) return;
+ int[] indices = e.getIndices();
+ java.util.Arrays.sort(indices);
+
+ int[] alarms = (int[]) Utilities.toPrimitiveArray((Integer[]) alarmTabs.toArray(new Integer[0]));
+ java.util.Arrays.sort(alarms);
+
+ if (alarms[alarms.length -1] < indices[0]) {
+ //Some tab removed after the last blinking tab, don't care
+ return;
+ }
+
+ boolean changed = false;
+ for (int i=0; i < alarms.length; i++) {
+ //First weed out all deleted alarm tabs
+ for (int j=0; j < indices.length; j++) {
+ if (alarms[i] == indices[j]) {
+ alarms[i] = -1;
+ changed = true;
+ }
+ }
+ }
+ for (int i=0; i < alarms.length; i++) {
+ //Now decrement those that remain that are affected
+ int alarm = alarms[i];
+ for (int j=0; j < indices.length; j++) {
+ if (alarm > indices[j]) {
+ alarms[i]--;
+ changed = true;
+ }
+ }
+ }
+
+ if (changed) {
+ alarmTabs.clear();
+ boolean addedSome = false;
+ for (int i=0; i < alarms.length; i++) {
+ if (alarms[i] >= 0) {
+ addAlarmTab(alarms[i]);
+ addedSome = true;
+ }
+ }
+ if (!addedSome) {
+ stopAlarmTimer();
+ }
+ }
+
+ repaintAllTabs();
+ }
+
+ void indicesChanged (ComplexListDataEvent e) {
+ if (!hasAlarmTabs()) return;
+ if (e instanceof VeryComplexListDataEvent) { //it always will be
+ VeryComplexListDataEvent ve = (VeryComplexListDataEvent) e;
+
+ ArrayDiff dif = ((VeryComplexListDataEvent) e).getDiff();
+
+ List old = Arrays.asList(dif.getOldData());
+ List nue = Arrays.asList(dif.getNewData());
+
+ int[] alarms = (int[]) Utilities.toPrimitiveArray((Integer[]) alarmTabs.toArray(new Integer[0]));
+
+ boolean changed = false;
+ for (int i=0; i < alarms.length; i++) {
+ Object o = old.get(alarms[i]);
+ int idx = nue.indexOf(o);
+ changed |= idx != alarms[i];
+ alarms[i] = nue.indexOf(o);
+ }
+ if (changed) {
+ alarmTabs.clear();
+ boolean addedSome = false;
+ for (int i=0; i < alarms.length; i++) {
+ if (alarms[i] >= 0) {
+ addAlarmTab(alarms[i]);
+ addedSome = true;
+ }
+ }
+ if (!addedSome) {
+ stopAlarmTimer();
+ }
+ }
+ }
+ }
+
+
+ void contentsChanged(ListDataEvent evt) {
+ if (!hasAlarmTabs()) return;
+ //Do nothing, just means some text or icons changed
+ }
//Change types
/** Change type indicating no change happened (i.e. calling setSelected() with the same value it was previously
@@ -508,6 +835,9 @@
type = ALL_TABS;
go = true;
}
+ break;
+ case ATTENTION:
+ go = true;
}
if (go) {
if (type == ALL_TABS) {
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/ToolbarTabDisplayerUI.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/ToolbarTabDisplayerUI.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/ToolbarTabDisplayerUI.java 15 Jun 2004 00:37:46 -0000 1.1
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/ToolbarTabDisplayerUI.java 10 Sep 2004 18:54:42 -0000
@@ -102,7 +102,15 @@
}
}
return null;
- }
+ }
+
+ public void requestAttention (int tab) {
+ //not implemented
+ }
+
+ public void cancelRequestAttention (int tab) {
+ //not implemented
+ }
protected ChangeListener createSelectionListener() {
return new ChangeListener() {
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WinClassicEditorTabCellRenderer.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WinClassicEditorTabCellRenderer.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WinClassicEditorTabCellRenderer.java 5 May 2004 18:50:27 -0000 1.3
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WinClassicEditorTabCellRenderer.java 10 Sep 2004 18:54:43 -0000
@@ -31,6 +31,8 @@
private static final TabPainter rightClip = new WinClassicRightClipPainter();
private static final TabPainter normal = new WinClassicPainter();
+ static final Color ATTENTION_COLOR = new Color(255, 238, 120);
+
private static boolean isGenericUI = !"Windows".equals(
UIManager.getLookAndFeel().getID());
@@ -135,9 +137,13 @@
((Graphics2D) g).setPaint(ColorUtil.getGradientPaint(0, 0, getSelGradientColor(), ren.getWidth(), 0, UIManager.getColor(
"TabbedPane.background")));//NOI18N
} else {
- g.setColor(ren.isSelected() ?
- UIManager.getColor("TabbedPane.background") :
- UIManager.getColor("tab_unsel_fill")); //NOI18N
+ if (!ren.isAttention()) {
+ g.setColor(ren.isSelected() ?
+ UIManager.getColor("TabbedPane.background") :
+ UIManager.getColor("tab_unsel_fill")); //NOI18N
+ } else {
+ g.setColor(ATTENTION_COLOR);
+ }
}
Polygon p = getInteriorPolygon(c);
g.fillPolygon(p);
@@ -260,9 +266,13 @@
((Graphics2D) g).setPaint(ColorUtil.getGradientPaint(0, 0, getSelGradientColor(), ren.getWidth(), 0, UIManager.getColor(
"TabbedPane.background")));//NOI18N
} else {
- g.setColor(ren.isSelected() ?
+ if (!ren.isAttention()) {
+ g.setColor(ren.isSelected() ?
UIManager.getColor("TabbedPane.background") :
UIManager.getColor("tab_unsel_fill")); //NOI18N
+ } else {
+ g.setColor(ATTENTION_COLOR);
+ }
}
Polygon p = getInteriorPolygon(c);
g.fillPolygon(p);
@@ -353,9 +363,13 @@
((Graphics2D) g).setPaint(ColorUtil.getGradientPaint(0, 0, getSelGradientColor(), ren.getWidth(), 0, UIManager.getColor(
"TabbedPane.background")));//NOI18N
} else {
- g.setColor(ren.isSelected() ?
- UIManager.getColor("TabbedPane.background") :
+ if (!ren.isAttention()) {
+ g.setColor(ren.isSelected() ?
+ UIManager.getColor("TabbedPane.background") : //NOI18N
UIManager.getColor("tab_unsel_fill")); //NOI18N
+ } else {
+ g.setColor(ATTENTION_COLOR);
+ }
}
Polygon p = getInteriorPolygon(c);
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WinClassicViewTabDisplayerUI.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WinClassicViewTabDisplayerUI.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WinClassicViewTabDisplayerUI.java 2 Sep 2004 13:43:09 -0000 1.10
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WinClassicViewTabDisplayerUI.java 10 Sep 2004 18:54:43 -0000
@@ -228,16 +228,20 @@
// background body, colored according to state
boolean selected = isSelected(index);
boolean focused = isFocused(index);
+ boolean attention = isAttention(index);
+
Paint result = null;
- if (focused) {
+ if (focused && !attention) {
result =
ColorUtil.getGradientPaint(x, y, getSelGradientColor(),
//NOI18N
x + width, y,
UIManager.getColor(
"TabbedPane.background"));
- } else if (selected) {
+ } else if (selected && !attention) {
result = UIManager.getColor("TabbedPane.background"); //NOI18N
+ } else if (attention) {
+ result = WinClassicEditorTabCellRenderer.ATTENTION_COLOR;
} else {
result = UIManager.getColor("tab_unsel_fill");
}
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WinXPEditorTabCellRenderer.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WinXPEditorTabCellRenderer.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WinXPEditorTabCellRenderer.java 22 Apr 2004 13:09:18 -0000 1.2
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WinXPEditorTabCellRenderer.java 10 Sep 2004 18:54:44 -0000
@@ -252,7 +252,13 @@
private static final Paint getPaint(int xTop, int yTop, int xBot, int yBot,
WinXPEditorTabCellRenderer ren) {
- if (ren.isSelected() || (ren.isPressed() && !ren.inCloseButton())) {
+
+ if (!ren.isSelected() && !ren.isPressed() && ren.isAttention()) {
+ Color a = new Color (255, 255, 128);
+ Color b = new Color (230, 200, 64);
+ return ColorUtil.getGradientPaint(xTop, yTop, a, xBot,
+ yBot, b);
+ } else if (ren.isSelected() || (ren.isPressed() && !ren.inCloseButton())) {
if (ren.isActive()) {
return ColorUtil.getGradientPaint(xTop, yTop,
getTopActiveSelectedColor(),
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WinXPSlidingButtonUI.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WinXPSlidingButtonUI.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WinXPSlidingButtonUI.java 2 Sep 2004 13:43:10 -0000 1.2
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WinXPSlidingButtonUI.java 10 Sep 2004 18:54:44 -0000
@@ -39,6 +39,7 @@
import javax.swing.border.CompoundBorder;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicGraphicsUtils;
+import org.netbeans.swing.tabcontrol.SlidingButton;
import org.netbeans.swing.tabcontrol.SlidingButtonUI;
/**
@@ -108,12 +109,17 @@
super.paint(g, c);
}
- protected void paintBackground (Graphics2D g, AbstractButton button) {
- hiddenToggle.paint(g);
+ protected void paintBackground(Graphics2D g, AbstractButton b) {
+ if (((SlidingButton) b).isBlinkState()) {
+ g.setColor(WinClassicEditorTabCellRenderer.ATTENTION_COLOR);
+ g.fillRect (0, 0, b.getWidth(), b.getHeight());
+ hiddenToggle.setFont(b.getFont());
+ } else {
+ hiddenToggle.paint(g);
+ }
}
protected void paintButtonPressed(Graphics g, AbstractButton b) {
hiddenToggle.paint(g);
}
-
}
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WinXPViewTabDisplayerUI.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WinXPViewTabDisplayerUI.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WinXPViewTabDisplayerUI.java 1 Sep 2004 15:06:05 -0000 1.8
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WinXPViewTabDisplayerUI.java 10 Sep 2004 18:54:45 -0000
@@ -190,12 +190,18 @@
// background body, colored according to state
boolean selected = isSelected(index);
boolean focused = selected && isActive();
- if (focused) {
+ boolean attention = isAttention(index);
+ if (focused && !attention) {
ColorUtil.xpFillRectGradient((Graphics2D) g, x, y, width, height,
focusFillBrightC, focusFillDarkC);
- } else if (selected && isMoreThanOne()) {
+ } else if (selected && isMoreThanOne() && !attention) {
g.setColor(selFillC);
g.fillRect(x, y, width, height);
+ } else if (attention) {
+ Color a = new Color (255, 255, 128);
+ Color b = new Color (230, 200, 64);
+ ColorUtil.xpFillRectGradient((Graphics2D) g, x, y, width, height,
+ a, b);
} else {
ColorUtil.xpFillRectGradient((Graphics2D) g, x, y, width, height,
unselFillBrightC, unselFillDarkC);
@@ -465,11 +471,13 @@
public Icon getIcon() {
- if (displayer.isActive()) {
- return iconCache.obtainIcon((String)focusedNormal.get(getOrientation()));
+ Icon result = null;
+ if (displayer != null && displayer.isActive()) {
+ result = iconCache.obtainIcon((String)focusedNormal.get(getOrientation()));
} else {
- return super.getIcon();
+ result = super.getIcon();
}
+ return result;
}
public Icon getRolloverIcon() {
Index: core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WindowsSlidingButtonUI.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WindowsSlidingButtonUI.java,v
--- core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WindowsSlidingButtonUI.java 2 Jun 2004 12:31:31 -0000 1.1
+++ core/swing/tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/WindowsSlidingButtonUI.java 10 Sep 2004 18:54:45 -0000
@@ -37,6 +37,7 @@
import javax.swing.border.CompoundBorder;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicGraphicsUtils;
+import org.netbeans.swing.tabcontrol.SlidingButton;
import org.netbeans.swing.tabcontrol.SlidingButtonUI;
/**
@@ -77,10 +78,15 @@
public void installDefaults (AbstractButton b) {
super.installDefaults(b);
if(!defaults_initialized) {
- dashedRectGapX = ((Integer)UIManager.get("Button.dashedRectGapX")).intValue();
- dashedRectGapY = ((Integer)UIManager.get("Button.dashedRectGapY")).intValue();
- dashedRectGapWidth = ((Integer)UIManager.get("Button.dashedRectGapWidth")).intValue();
- dashedRectGapHeight = ((Integer)UIManager.get("Button.dashedRectGapHeight")).intValue();
+ //Null checks so this can be tested on other platforms
+ Integer in = ((Integer)UIManager.get("Button.dashedRectGapX"));
+ dashedRectGapX = in == null ? 3 : in.intValue();
+ in = ((Integer)UIManager.get("Button.dashedRectGapY"));
+ dashedRectGapY = in == null ? 3 : in.intValue();
+ in = ((Integer)UIManager.get("Button.dashedRectGapWidth"));
+ dashedRectGapWidth = in == null ? 3 : in.intValue();
+ in = ((Integer)UIManager.get("Button.dashedRectGapHeight"));
+ dashedRectGapHeight = in == null ? 3 : in.intValue();
focusColor = UIManager.getColor(getPropertyPrefix() + "focus");
defaults_initialized = true;
@@ -92,15 +98,24 @@
defaults_initialized = false;
}
- protected void paintBackground (Graphics2D g, AbstractButton button) {
- super.paintBackground(g, button);
- }
-
+ protected void paintBackground(Graphics2D g, AbstractButton b) {
+ if (((SlidingButton) b).isBlinkState()) {
+ g.setColor(WinClassicEditorTabCellRenderer.ATTENTION_COLOR);
+ g.fillRect (0, 0, b.getWidth(), b.getHeight());
+ } else {
+ super.paintBackground(g, b);
+ }
+ }
protected void paintButtonPressed(Graphics g, AbstractButton b) {
// This is a special case in which the toggle button in the
// Rollover JToolBar will render the button in a pressed state
Color oldColor = g.getColor();
+
+ if (((SlidingButton) b).isBlinkState()) {
+ g.setColor(WinClassicEditorTabCellRenderer.ATTENTION_COLOR);
+ g.fillRect (0, 0, b.getWidth(), b.getHeight());
+ }
int w = b.getWidth();
int h = b.getHeight();
Index: core/swing/tabcontrol/test/unit/src/org/netbeans/swing/tabcontrol/DataModelTest.java
===================================================================
RCS file: /cvs/core/swing/tabcontrol/test/unit/src/org/netbeans/swing/tabcontrol/DataModelTest.java,v
--- core/swing/tabcontrol/test/unit/src/org/netbeans/swing/tabcontrol/DataModelTest.java 22 Apr 2004 13:09:27 -0000 1.2
+++ core/swing/tabcontrol/test/unit/src/org/netbeans/swing/tabcontrol/DataModelTest.java 10 Sep 2004 18:54:46 -0000
@@ -200,8 +200,8 @@
public void doTestRemoveContiguous() {
System.err.println("testRemoveContiguous");
int formerSize = mdl.size();
- mdl.removeTabs (10, 20);
- int expectedSize=formerSize-10;
+ mdl.removeTabs (10, 19);
+ int expectedSize=formerSize - 10;
assertPravda (mdl.size() == expectedSize, "Model size should be " + expectedSize + " after removing 10 items, but is " + mdl.size());
try {
_testContentsValid();
Index: core/swing/tabcontrol/test/unit/src/org/netbeans/swing/tabcontrol/plaf/AttentionAndModelChangesTest.java
===================================================================
RCS file: core/swing/tabcontrol/test/unit/src/org/netbeans/swing/tabcontrol/plaf/AttentionAndModelChangesTest.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ core/swing/tabcontrol/test/unit/src/org/netbeans/swing/tabcontrol/plaf/AttentionAndModelChangesTest.java 10 Sep 2004 18:54:46 -0000
@@ -0,0 +1,554 @@
+/*
+ * Sun Public License Notice
+ *
+ * The contents of this file are subject to the Sun Public License
+ * Version 1.0 (the "License"). You may not use this file except in
+ * compliance with the License. A copy of the License is available at
+ * http://www.sun.com/
+ *
+ * The Original Code is NetBeans. The Initial Developer of the Original
+ * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun
+ * Microsystems, Inc. All Rights Reserved.
+ */
+
+package org.netbeans.swing.tabcontrol.plaf;
+
+import java.awt.BorderLayout;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.SwingUtilities;
+import junit.framework.*;
+import org.netbeans.swing.tabcontrol.DefaultTabDataModel;
+import org.netbeans.swing.tabcontrol.TabData;
+import org.netbeans.swing.tabcontrol.TabDataModel;
+import org.netbeans.swing.tabcontrol.TabDisplayer;
+import org.netbeans.swing.tabcontrol.TabbedContainer;
+
+/**
+ * TabState stores a list of tabs which are blinking for attention.
+ * They are stored by index. This test ensures that when tabs are added
+ * or removed from the model, that the indices are updated correctly.
+ *
+ * This test also exercises the data model heavily. + * + * @author Tim Boudreau + */ +public class AttentionAndModelChangesTest extends TestCase { + + public AttentionAndModelChangesTest(java.lang.String testName) { + super(testName); + } + + public static Test suite() { + TestSuite suite = new TestSuite(AttentionAndModelChangesTest.class); + return suite; + } + + TabDataModel mdl = null; + TabData[] origData = null; + TabState state = null; + TabDisplayer displayer = null; + TabbedContainer tab = null; + public void setUp() { + origData = new TabData[13]; + for (int i=0; i < origData.length; i++) { + origData[i] = new TabData(new JLabel("TD " + i), null, "TD " + i, "tip"); + } + + mdl = new DefaultTabDataModel(origData); + + tab = new TabbedContainer(mdl, TabbedContainer.TYPE_EDITOR); + tab.setActive(true); + JFrame jf = new JFrame(); + jf.getContentPane().setLayout (new BorderLayout()); + jf.getContentPane().add (tab, BorderLayout.CENTER); + jf.setBounds (20, 20, 400, 200); + jf.show(); + jf.toFront(); + while (!jf.isShowing()) { + try { + Thread.currentThread().sleep(50); + } catch (InterruptedException e) { + fail (e.toString()); + } + } + displayer = ((DefaultTabbedContainerUI) tab.getUI()).getTabDisplayer(); + BasicTabDisplayerUI ui = (BasicTabDisplayerUI) displayer.getUI(); + state = ui.getTabState(); + } + + private void requestAttention (final int i) throws Exception { + SwingUtilities.invokeAndWait (new Runnable() { + public void run() { + tab.requestAttention(i); + } + }); + } + + private void assertAtt (final int[] i) throws Exception { + Arrays.sort (i); + int[] att = state.getAlarmTabs(); + assertTrue ("Expected attention tabs to be " + + a2s(i) + " but they are " + a2s(att), + Arrays.equals(i, att)); + } + + private void assertNotAtt (final int[] i) throws Exception { + SwingUtilities.invokeAndWait (new Runnable() { + public void run() { + Arrays.sort (i); + int[] att = state.getAlarmTabs(); + assertFalse ("Expected attention tabs NOT to be " + + a2s(i) + " but they are " + a2s(att), + Arrays.equals(i, att)); + } + }); + } + + private static String a2s (int[] ints) { + if (ints == null) { + return "null"; + } + if (ints.length == 0) { + return "[]"; + } + StringBuffer sb = new StringBuffer(); + for (int i=0; i < ints.length; i++) { + sb.append (ints[i]); + if (i != ints.length -1) { + sb.append(','); + } + } + return sb.toString(); + } + + public void testRequestAttention () throws Exception { + System.out.println("testRequestAttention"); + requestAttention (3); + requestAttention (5); + assertAtt(new int[] {3, 5}); + } + + public void testRemoveSingleInBetween() throws Exception { + System.out.println("testRemoveSingleInBetween"); + requestAttention(3); + requestAttention(5); + mdl.removeTab(4); + assertAtt(new int[] {3, 4}); + } + + public void testRemoveSingleBefore() throws Exception { + System.out.println("testRemoveSingleBefore"); + requestAttention(3); + requestAttention(5); + mdl.removeTab(2); + assertAtt(new int[] {2, 4}); + } + + + public void testRemoveSingleAfter() throws Exception { + System.out.println("testRemoveSingleAfter"); + requestAttention(3); + requestAttention(5); + mdl.removeTab(6); + assertAtt(new int[] {3, 5}); + mdl.removeTab(10); + assertAtt(new int[] {3, 5}); + } + + public void testRemoveSingleFirst() throws Exception { + System.out.println("testRemoveSingleFirst"); + requestAttention(3); + requestAttention(5); + mdl.removeTab(3); + assertAtt(new int[] {4}); + } + + + public void testRemoveSingleSecond() throws Exception { + System.out.println("testRemoveSingleSecond"); + requestAttention(3); + requestAttention(5); + mdl.removeTab(5); + assertAtt(new int[] {3}); + } + + public void testAddSingleBefore() throws Exception { + System.out.println("testAddSingleBefore"); + requestAttention(3); + requestAttention(5); + TabData td = new TabData (new JLabel("Added"), null, "Added", "tip"); + mdl.addTab(1, td); + assertAtt(new int[] {4, 6}); + } + + public void testAddSingleBetween() throws Exception { + System.out.println("testAddSingleBetween"); + requestAttention(3); + requestAttention(5); + TabData td = new TabData (new JLabel("Added"), null, "Added", "tip"); + mdl.addTab(4, td); + assertAtt(new int[] {3, 6}); + } + + public void testAddSingleAt() throws Exception { + System.out.println("testAddSingleAt"); + requestAttention(3); + requestAttention(5); + TabData td = new TabData (new JLabel("Added"), null, "Added", "tip"); + mdl.addTab(3, td); + assertAtt(new int[] {4, 6}); + } + + public void testAddSingleAfter() throws Exception { + System.out.println("testAddSingleAfter"); + requestAttention(3); + requestAttention(5); + TabData td = new TabData (new JLabel("Added"), null, "Added", "tip"); + mdl.addTab(7, td); + assertAtt(new int[] {3, 5}); + } + + public void testAddSingleAtEnd() throws Exception { + System.out.println("testAddSingleAtEnd"); + requestAttention(3); + requestAttention(5); + TabData td = new TabData (new JLabel("Added"), null, "Added", "tip"); + mdl.addTab(5, td); + assertAtt(new int[] {3, 6}); + } + + public void testAddMultipleBefore() throws Exception { + System.out.println("testAddMultipleBefore"); + requestAttention(3); + requestAttention(5); + TabData att1 = mdl.getTab(3); + TabData att2 = mdl.getTab(5); + + TabData td1 = new TabData (new JLabel("Added 1"), null, "Added 1", "tip"); + TabData td2 = new TabData (new JLabel("Added 2"), null, "Added 2", "tip"); + mdl.addTabs(1, new TabData[] {td1, td2}); + assertAtt(new int[] {5, 7}); + + int[] tabs = state.getAlarmTabs(); + assertSame ("First attention tab should be " + att1 + " not " + mdl.getTab(tabs[0]), att1, mdl.getTab(tabs[0])); + assertSame ("Second attention tab should be " + att2 + " not " + mdl.getTab(tabs[1]), att2, mdl.getTab(tabs[1])); + } + + + public void testAddMultipleAfter() throws Exception { + System.out.println("testAddMultipleAfter"); + requestAttention(3); + requestAttention(5); + TabData td1 = new TabData (new JLabel("Added 1"), null, "Added 1", "tip"); + TabData td2 = new TabData (new JLabel("Added 2"), null, "Added 2", "tip"); + mdl.addTabs(7, new TabData[] {td1, td2}); + assertAtt(new int[] {3, 5}); + } + + public void testAddMultipleBetween() throws Exception { + System.out.println("testAddMultipleBetween"); + requestAttention(3); + requestAttention(5); + TabData att1 = mdl.getTab(3); + TabData att2 = mdl.getTab(5); + + TabData td1 = new TabData (new JLabel("Added 1"), null, "Added 1", "tip"); + TabData td2 = new TabData (new JLabel("Added 2"), null, "Added 2", "tip"); + mdl.addTabs(4, new TabData[] {td1, td2}); + assertAtt(new int[] {3, 7}); + + int[] tabs = state.getAlarmTabs(); + assertSame ("First attention tab should be " + att1 + " not " + mdl.getTab(tabs[0]), att1, mdl.getTab(tabs[0])); + assertSame ("Second attention tab should be " + att2 + " not " + mdl.getTab(tabs[1]), att2, mdl.getTab(tabs[1])); + } + + + public void testRemoveMultipleBetween() throws Exception { + System.out.println("testRemoveMultipleBetween"); + requestAttention(3); + requestAttention(6); + TabData att1 = mdl.getTab(3); + TabData att2 = mdl.getTab(6); + + int sz = mdl.size(); + mdl.removeTabs(4, 5); + assertTrue ("After removing tabs 4 and 5, model size should be 2 smaller - was " + sz + " is " + mdl.size(), mdl.size() == sz-2); + + assertAtt (new int[] {3, 4}); + + int[] tabs = state.getAlarmTabs(); + System.out.println("Alarm tabs are " + tabs[0] + "," + tabs[1]); + System.err.println("First attention tab " + mdl.getTab(tabs[0])); + System.err.println("Second attention tab " + mdl.getTab(tabs[1])); + assertSame ("First attention tab should be " + att1 + " not " + mdl.getTab(tabs[0]), att1, mdl.getTab(tabs[0])); + assertSame ("Second attention tab should be " + att2 + " not " + mdl.getTab(tabs[1]), att2, mdl.getTab(tabs[1])); + } + + public void testRemoveMultipleBefore() throws Exception { + System.out.println("testRemoveMultipleBefore"); + requestAttention(3); + requestAttention(6); + TabData att1 = mdl.getTab(3); + TabData att2 = mdl.getTab(6); + int sz = mdl.size(); + + mdl.removeTabs(1, 2); + + assertTrue ("After removing tabs 1 and 2, model size should be 2 smaller - was " + sz + " is " + mdl.size(), mdl.size() == sz-2); + assertAtt (new int[] {1, 4}); + + int[] tabs = state.getAlarmTabs(); + assertSame ("First attention tab should be " + att1 + " not " + mdl.getTab(tabs[0]), att1, mdl.getTab(tabs[0])); + assertSame ("Second attention tab should be " + att2 + " not " + mdl.getTab(tabs[1]), att2, mdl.getTab(tabs[1])); + } + + public void testRemoveMultipleAfter() throws Exception { + System.out.println("testRemoveMultipleAfter"); + requestAttention(3); + requestAttention(6); + mdl.removeTabs(7, 10); + assertAtt (new int[] {3, 6}); + } + + public void testRemoveMultipleIncluding() throws Exception { + System.out.println("testRemoveMultipleIncluding"); + requestAttention(3); + requestAttention(6); + mdl.removeTabs(6, 10); + assertAtt (new int[] {3}); + } + + public void testRemoveMultipleInclusive() throws Exception { + System.out.println("testRemoveMultipleInclusive"); + requestAttention(3); + requestAttention(6); + mdl.removeTabs(2, 7); + assertAtt (new int[0]); + } + + public void testAddDiscontiguousBefore() throws Exception { + System.out.println("testAddDiscontiguousBefore"); + requestAttention(7); + requestAttention(9); + TabData td1 = new TabData (new JLabel("Added 1"), null, "Added 1", "tip"); + TabData td2 = new TabData (new JLabel("Added 2"), null, "Added 2", "tip"); + TabData td3 = new TabData (new JLabel("Added 3"), null, "Added 3", "tip"); + mdl.addTabs (new int[] { 1, 3, 5}, new TabData[] {td1, td2, td3}); + assertAtt (new int[] {10, 12}); + } + + public void testRemoveDiscontiguousBefore() throws Exception { + System.out.println("testRemoveDiscontiguousBefore"); + requestAttention(7); + requestAttention(9); + mdl.removeTabs (new int[] {1,3,5}); + assertAtt (new int[] {4, 6}); + } + + public void testRemoveDiscontiguousAfter() throws Exception { + System.out.println("testRemoveDiscontiguousAfter"); + requestAttention(5); + requestAttention(7); + mdl.removeTabs (new int[] {8,10,11}); + assertAtt (new int[] {5, 7}); + } + + + public void testAddDiscontiguousBeforeAndBetween() throws Exception { + System.out.println("testAddDiscontiguousBeforeAndBetween"); + requestAttention(7); + requestAttention(9); + TabData att1 = mdl.getTab(7); + TabData att2 = mdl.getTab(9); + + TabData td1 = new TabData (new JLabel("Added 1"), null, "Added 1", "tip"); + TabData td2 = new TabData (new JLabel("Added 2"), null, "Added 2", "tip"); + TabData td3 = new TabData (new JLabel("Added 3"), null, "Added 3", "tip"); + mdl.addTabs (new int[] {1, 3, 8}, new TabData[] {td1, td2, td3}); + assertAtt (new int[] {10, 12}); + + int[] tabs = state.getAlarmTabs(); + assertSame ("First attention tab should be " + att1 + " not " + mdl.getTab(tabs[0]), att1, mdl.getTab(tabs[0])); + assertSame ("Second attention tab should be " + att2 + " not " + mdl.getTab(tabs[1]), att2, mdl.getTab(tabs[1])); + } + + public void testRemoveDiscontiguousBeforeAndBetween() throws Exception { + System.out.println("testRemoveDiscontiguousBeforeAndBetween"); + requestAttention(7); + requestAttention(9); + TabData att1 = mdl.getTab(7); + TabData att2 = mdl.getTab(9); + mdl.removeTabs (new int[] {1,3,8}); + assertAtt (new int[] {5, 6}); + int[] tabs = state.getAlarmTabs(); + assertSame ("First attention tab should be " + att1 + " not " + mdl.getTab(tabs[0]), att1, mdl.getTab(tabs[0])); + assertSame ("Second attention tab should be " + att2 + " not " + mdl.getTab(tabs[1]), att2, mdl.getTab(tabs[1])); + } + + public void testRemoveDiscontiguousBeforeAndBetweenIncludingOne() throws Exception { + System.out.println("testRemoveDiscontiguousBeforeAndBetweenIncludingOne"); + requestAttention(7); + requestAttention(9); + TabData att1 = mdl.getTab(7); + TabData att2 = mdl.getTab(9); + mdl.removeTabs (new int[] {1,3,7}); + assertAtt (new int[] {6}); + } + + public void testReorderComplex() throws Exception { + System.out.println("testReorderComplex"); + requestAttention(7); + requestAttention(9); + TabData att1 = mdl.getTab(7); + TabData att2 = mdl.getTab(9); + + TabData[] data = (TabData[]) mdl.getTabs().toArray(new TabData[mdl.size()]); + TabData[] nue = new TabData[data.length]; + for (int i=data.length-1; i >=0; i--) { + nue [data.length-(i+1)] = data[i]; + } + mdl.setTabs(nue); + int[] tabs = state.getAlarmTabs(); + Arrays.sort(tabs); + assertSame ("First attention tab should be " + att2 + " not " + mdl.getTab(tabs[0]), att2, mdl.getTab(tabs[0])); + assertSame ("Second attention tab should be " + att1 + " not " + mdl.getTab(tabs[1]), att1, mdl.getTab(tabs[1])); + } + + public void testReplaceComplex() throws Exception { + System.out.println("testReplaceComplex"); + requestAttention(7); + requestAttention(9); + + TabData[] data = (TabData[]) mdl.getTabs().toArray(new TabData[mdl.size()]); + data[7] = new TabData (new JLabel("foo"), null, "foo", "foo"); + mdl.setTabs(data); + + assertAtt(new int[] {9}); + + data[9] = new TabData (new JLabel("goo"), null, "goo", "goo"); + mdl.setTabs(data); + assertAtt(new int[0]); + } + + public void testRemoveComplex() throws Exception { + System.out.println("testRemoveComplex"); + requestAttention(7); + requestAttention(9); + TabData att1 = mdl.getTab(7); + TabData att2 = mdl.getTab(9); + + TabData[] data = (TabData[]) mdl.getTabs().toArray(new TabData[mdl.size()]); + TabData[] nue = new TabData[data.length-1]; + int ct = 0; + for (int i=0; i < data.length; i++) { + nue [ct] = data[i]; + if (i != 7) { + ct++; + } + } + mdl.setTabs(nue); + int[] tabs = state.getAlarmTabs(); + + assertTrue (tabs.length ==1); + assertTrue (tabs[0] == 8); + assertSame (mdl.getTab(tabs[0]), att2); + } + + + public void testAddComplex() throws Exception { + System.out.println("testAddComplex"); + requestAttention(7); + requestAttention(9); + TabData att1 = mdl.getTab(7); + TabData att2 = mdl.getTab(9); + + TabData[] data = (TabData[]) mdl.getTabs().toArray(new TabData[mdl.size()]); + + ArrayList al = new ArrayList (Arrays.asList(data)); + al.add (8, new TabData(new JLabel("boo"), null, "boo", "boo")); + al.add (2, new TabData(new JLabel("moo"), null, "moo", "moo")); + + TabData[] nue = (TabData[]) al.toArray(new TabData[0]); + + mdl.setTabs(nue); + int[] tabs = state.getAlarmTabs(); + Arrays.sort(tabs); + assertSame ("First attention tab should be " + att1 + " not " + mdl.getTab(tabs[0]), att1, mdl.getTab(tabs[0])); + assertSame ("Second attention tab should be " + att2 + " not " + mdl.getTab(tabs[1]), att2, mdl.getTab(tabs[1])); + } + + //Make sure (non)exceptional case of only 1 tab works + + private void makeSingle() throws Exception { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + TabData[] td = new TabData[] { + new TabData (new JLabel("Single"), null, "single", "single") + }; + mdl.setTabs(td); + state.addAlarmTab(0); + assertTrue (state.getAlarmTabs().length == 1); + } + }); + Thread.currentThread().sleep(50); + } + + public void testHandleSingle1() throws Exception { + System.out.println("testHandleSingle1"); + makeSingle(); + + mdl.removeTab(0); + assertAtt (new int[0]); + } + + public void testHandleSingle2() throws Exception { + System.out.println("testHandleSingle2"); + makeSingle(); + mdl.removeTabs(0, 0); + assertAtt (new int[0]); + } + + public void testHandleSingle3() throws Exception { + System.out.println("testHandleSingle3"); + makeSingle(); + mdl.removeTabs(new int[] {0}); + assertAtt (new int[0]); + } + + public void testAddSingle1() throws Exception { + System.out.println("testAddSingle1"); + makeSingle(); + mdl.addTab(0, new TabData(new JLabel("foo"), null, "foo", "foo")); + assertTrue ("Size should be 2", mdl.size() == 2); + assertAtt(new int[] {1}); + } + + public void testAddSingle2() throws Exception { + System.out.println("testAddSingle2"); + makeSingle(); + mdl.addTabs(0, new TabData[] {new TabData(new JLabel("foo"), null, "foo", "foo")}); + assertTrue ("Size should be 2", mdl.size() == 2); + assertAtt(new int[] {1}); + } + + public void testAddSingle3() throws Exception { + System.out.println("testAddSingle3"); + makeSingle(); + mdl.addTabs(new int[] {0}, new TabData[] {new TabData(new JLabel("foo"), null, "foo", "foo")}); + assertTrue ("Size should be 2", mdl.size() == 2); + assertAtt(new int[] {1}); + } + + public void testAddSingle4() throws Exception { + System.out.println("testAddSingle4"); + makeSingle(); + mdl.addTabs(new int[] {1}, new TabData[] {new TabData(new JLabel("foo"), null, "foo", "foo")}); + assertTrue ("Size should be 2", mdl.size() == 2); + assertAtt(new int[] {0}); + } +} Index: core/swing/tabcontrol/test/unit/src/org/netbeans/swing/tabcontrol/plaf/TabStateTest.java =================================================================== RCS file: /cvs/core/swing/tabcontrol/test/unit/src/org/netbeans/swing/tabcontrol/plaf/TabStateTest.java,v --- core/swing/tabcontrol/test/unit/src/org/netbeans/swing/tabcontrol/plaf/TabStateTest.java 22 Apr 2004 13:09:28 -0000 1.2 +++ core/swing/tabcontrol/test/unit/src/org/netbeans/swing/tabcontrol/plaf/TabStateTest.java 10 Sep 2004 18:54:47 -0000 @@ -323,11 +323,4 @@ } private int policy = 0; - - - // TODO add test methods here, they have to start with 'test' name. - // for example: - // public void testHello() {} - - } Index: core/windows/src/org/netbeans/core/windows/Central.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/Central.java,v --- core/windows/src/org/netbeans/core/windows/Central.java 3 Sep 2004 09:55:43 -0000 1.25 +++ core/windows/src/org/netbeans/core/windows/Central.java 10 Sep 2004 18:54:50 -0000 @@ -48,6 +48,18 @@ } + public void topComponentRequestAttention (ModeImpl mode, TopComponent tc) { + String modeName = getModeName(mode); + viewRequestor.scheduleRequest ( + new ViewRequest(modeName, View.TOPCOMPONENT_REQUEST_ATTENTION, tc, tc)); + } + + public void topComponentCancelRequestAttention (ModeImpl mode, TopComponent tc) { + String modeName = getModeName(mode); + viewRequestor.scheduleRequest ( + new ViewRequest(modeName, View.TOPCOMPONENT_CANCEL_REQUEST_ATTENTION, tc, tc)); + } + ///////////////////// // Mutators >> /** Sets visible or invisible window system and requests view accordingly. */ Index: core/windows/src/org/netbeans/core/windows/ViewRequest.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/ViewRequest.java,v --- core/windows/src/org/netbeans/core/windows/ViewRequest.java 28 May 2004 10:59:42 -0000 1.3 +++ core/windows/src/org/netbeans/core/windows/ViewRequest.java 10 Sep 2004 18:54:50 -0000 @@ -118,6 +118,12 @@ case View.CHANGE_PROJECT_NAME : tp = "CHANGE_PROJECT_NAME"; //NOI18N break; + case View.TOPCOMPONENT_REQUEST_ATTENTION : + tp = "TOPCOMPONENT_REQUEST_ATTENTION"; //NOI18N + break; + case View.TOPCOMPONENT_CANCEL_REQUEST_ATTENTION : + tp = "TOPCOMPONENT_CANCEL_REQUEST_ATTENTION"; //NOI18N + break; default : tp = "UNKNOWN"; break; Index: core/windows/src/org/netbeans/core/windows/ViewRequestor.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/ViewRequestor.java,v --- core/windows/src/org/netbeans/core/windows/ViewRequestor.java 28 May 2004 10:59:42 -0000 1.10 +++ core/windows/src/org/netbeans/core/windows/ViewRequestor.java 10 Sep 2004 18:54:51 -0000 @@ -117,7 +117,9 @@ || type == View.CHANGE_TOPCOMPONENT_ACTIVATED || type == View.CHANGE_DND_PERFORMED || type == View.CHANGE_UI_UPDATE - || type == View.CHANGE_PROJECT_NAME; + || type == View.CHANGE_PROJECT_NAME + || type == View.TOPCOMPONENT_CANCEL_REQUEST_ATTENTION + || type == View.TOPCOMPONENT_REQUEST_ATTENTION; synchronized(requests) { Object oldValue = null; Index: core/windows/src/org/netbeans/core/windows/WindowManagerImpl.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/WindowManagerImpl.java,v --- core/windows/src/org/netbeans/core/windows/WindowManagerImpl.java 9 Sep 2004 20:50:37 -0000 1.31 +++ core/windows/src/org/netbeans/core/windows/WindowManagerImpl.java 10 Sep 2004 18:54:52 -0000 @@ -100,7 +100,18 @@ } return (WindowManagerImpl)Lookup.getDefault().lookup(WindowManager.class); } + + public void topComponentRequestAttention(TopComponent tc) { + ModeImpl mode = (ModeImpl) findMode(tc); + + central.topComponentRequestAttention(mode, tc); + } + public void topComponentCancelRequestAttention(TopComponent tc) { + ModeImpl mode = (ModeImpl) findMode(tc); + + central.topComponentCancelRequestAttention(mode, tc); + } ///////////////////////// // API impelementation >> Index: core/windows/src/org/netbeans/core/windows/view/DefaultView.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/DefaultView.java,v --- core/windows/src/org/netbeans/core/windows/view/DefaultView.java 31 Aug 2004 13:35:00 -0000 1.36 +++ core/windows/src/org/netbeans/core/windows/view/DefaultView.java 10 Sep 2004 18:54:53 -0000 @@ -120,7 +120,8 @@ type != CHANGE_TOPCOMPONENT_DISPLAY_NAME_ANNOTATION_CHANGED && type != CHANGE_TOPCOMPONENT_TOOLTIP_CHANGED && type != CHANGE_TOPCOMPONENT_ICON_CHANGED && - type != CHANGE_PROJECT_NAME) { + type != CHANGE_PROJECT_NAME && + type != TOPCOMPONENT_REQUEST_ATTENTION) { isDangerous = true; break; } @@ -449,6 +450,37 @@ hierarchy.setMaximizedModeView(hierarchy.getModeViewForAccessor(wsa.getMaximizedModeAccessor())); hierarchy.updateDesktop(wsa); hierarchy.activateMode(wsa.getActiveModeAccessor()); + } else if (changeType == View.TOPCOMPONENT_REQUEST_ATTENTION) { + if (DEBUG) { + debugLog("Top component request attention"); + } + ModeView modeView = hierarchy.getModeViewForAccessor(wsa.findModeAccessor((String)viewEvent.getSource())); // XXX + if (modeView != null) { + TopComponent tc = (TopComponent) viewEvent.getNewValue(); + if (tc == null) { + throw new NullPointerException ("Top component is null for attention request"); //NOI18N + } + modeView.requestAttention (tc); + } else { + ErrorManager.getDefault().log(ErrorManager.INFORMATIONAL, + "Could not find mode " + viewEvent.getSource()); + } + } else if (changeType == View.TOPCOMPONENT_CANCEL_REQUEST_ATTENTION) { + if (DEBUG) { + debugLog("Top component cancel request attention"); //NOI18N + } + ModeView modeView = hierarchy.getModeViewForAccessor(wsa.findModeAccessor((String)viewEvent.getSource())); // XXX + if (modeView != null) { + TopComponent tc = (TopComponent) viewEvent.getNewValue(); + if (tc == null) { + throw new NullPointerException ("Top component is null for attention cancellation request"); //NOI18N + } + modeView.cancelRequestAttention (tc); + } else { + ErrorManager.getDefault().log(ErrorManager.INFORMATIONAL, + "Could not find mode " + viewEvent.getSource()); + } + } } Index: core/windows/src/org/netbeans/core/windows/view/ModeContainer.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/ModeContainer.java,v --- core/windows/src/org/netbeans/core/windows/view/ModeContainer.java 22 Apr 2004 13:09:47 -0000 1.3 +++ core/windows/src/org/netbeans/core/windows/view/ModeContainer.java 10 Sep 2004 18:54:54 -0000 @@ -52,5 +52,9 @@ public void updateToolTip(TopComponent tc); public void updateIcon(TopComponent tc); + + public void requestAttention(TopComponent tc); + + public void cancelRequestAttention(TopComponent tc); } Index: core/windows/src/org/netbeans/core/windows/view/ModeView.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/ModeView.java,v --- core/windows/src/org/netbeans/core/windows/view/ModeView.java 11 Jun 2004 08:44:00 -0000 1.11 +++ core/windows/src/org/netbeans/core/windows/view/ModeView.java 10 Sep 2004 18:54:54 -0000 @@ -144,6 +144,14 @@ public void updateIcon(TopComponent tc) { container.updateIcon(tc); } + + public void requestAttention (TopComponent tc) { + container.requestAttention(tc); + } + + public void cancelRequestAttention (TopComponent tc) { + container.cancelRequestAttention(tc); + } // XXX public void updateFrameState() { Index: core/windows/src/org/netbeans/core/windows/view/View.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/View.java,v --- core/windows/src/org/netbeans/core/windows/view/View.java 19 May 2004 18:56:41 -0000 1.5 +++ core/windows/src/org/netbeans/core/windows/view/View.java 10 Sep 2004 18:54:54 -0000 @@ -74,6 +74,9 @@ public int CHANGE_UI_UPDATE = 61; public int CHANGE_PROJECT_NAME = 62; + public int TOPCOMPONENT_REQUEST_ATTENTION = 63; + public int TOPCOMPONENT_CANCEL_REQUEST_ATTENTION = 64; + /** Provides GUI changes to manifest model changes to user. */ public void changeGUI(ViewEvent[] viewEvents, WindowSystemSnapshot snapshot); Index: core/windows/src/org/netbeans/core/windows/view/ViewEvent.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/ViewEvent.java,v --- core/windows/src/org/netbeans/core/windows/view/ViewEvent.java 13 Feb 2004 09:59:35 -0000 1.3 +++ core/windows/src/org/netbeans/core/windows/view/ViewEvent.java 10 Sep 2004 18:54:54 -0000 @@ -97,6 +97,8 @@ case View.CHANGE_TOPCOMPONENT_TOOLTIP_CHANGED : typeStr = "CHANGE_TOPCOMPONENT_TOOLTIP_CHANGED"; break; //NOI18N case View.CHANGE_UI_UPDATE : typeStr = "CHANGE_UI_UPDATE"; break; //NOI18N case View.CHANGE_VISIBILITY_CHANGED : typeStr = "CHANGE_VISIBILITY_CHANGED"; break; //NOI18N + case View.TOPCOMPONENT_REQUEST_ATTENTION : typeStr = "TOPCOMPONENT_REQUEST_ATTENTION"; break; //NOI18N + case View.TOPCOMPONENT_CANCEL_REQUEST_ATTENTION : typeStr = "TOPCOMPONENT_CANCEL_REQUEST_ATTENTION"; break; //NOI18N } buf.append(typeStr); buf.append("\nnewValue="); //NOI18N Index: core/windows/src/org/netbeans/core/windows/view/ui/DefaultSeparateContainer.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/ui/DefaultSeparateContainer.java,v --- core/windows/src/org/netbeans/core/windows/view/ui/DefaultSeparateContainer.java 24 Aug 2004 21:35:51 -0000 1.7 +++ core/windows/src/org/netbeans/core/windows/view/ui/DefaultSeparateContainer.java 10 Sep 2004 18:54:55 -0000 @@ -104,6 +104,14 @@ } }); } + + public void requestAttention (TopComponent tc) { + //not implemented + } + + public void cancelRequestAttention (TopComponent tc) { + //not implemented + } /** */ protected Component getModeComponent() { Index: core/windows/src/org/netbeans/core/windows/view/ui/DefaultSplitContainer.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/ui/DefaultSplitContainer.java,v --- core/windows/src/org/netbeans/core/windows/view/ui/DefaultSplitContainer.java 22 Apr 2004 13:09:51 -0000 1.5 +++ core/windows/src/org/netbeans/core/windows/view/ui/DefaultSplitContainer.java 10 Sep 2004 18:54:55 -0000 @@ -50,6 +50,14 @@ // To be able to move split dividers. panel.setMinimumSize(new Dimension(1, 1)); } + + public void requestAttention (TopComponent tc) { + tabbedHandler.requestAttention(tc); + } + + public void cancelRequestAttention (TopComponent tc) { + tabbedHandler.cancelRequestAttention(tc); + } /** */ protected Component getModeComponent() { Index: core/windows/src/org/netbeans/core/windows/view/ui/Tabbed.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/ui/Tabbed.java,v --- core/windows/src/org/netbeans/core/windows/view/ui/Tabbed.java 3 Jun 2004 12:07:29 -0000 1.8 +++ core/windows/src/org/netbeans/core/windows/view/ui/Tabbed.java 10 Sep 2004 18:54:55 -0000 @@ -31,6 +31,10 @@ */ public interface Tabbed { + public void requestAttention(TopComponent tc); + + public void cancelRequestAttention(TopComponent tc); + public void addTopComponent(String name, Icon icon, TopComponent tc, String toolTip); public void insertComponent(String name, Icon icon, Component comp, String toolTip, int position); Index: core/windows/src/org/netbeans/core/windows/view/ui/TabbedHandler.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/ui/TabbedHandler.java,v --- core/windows/src/org/netbeans/core/windows/view/ui/TabbedHandler.java 7 Sep 2004 13:10:14 -0000 1.13 +++ core/windows/src/org/netbeans/core/windows/view/ui/TabbedHandler.java 10 Sep 2004 18:54:55 -0000 @@ -102,7 +102,14 @@ return tabbed; } + + public void requestAttention (TopComponent tc) { + tabbed.requestAttention(tc); + } + public void cancelRequestAttention (TopComponent tc) { + tabbed.cancelRequestAttention(tc); + } public Component getComponent() { return tabbed.getComponent(); Index: core/windows/src/org/netbeans/core/windows/view/ui/slides/SlideBar.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/ui/slides/SlideBar.java,v --- core/windows/src/org/netbeans/core/windows/view/ui/slides/SlideBar.java 6 Sep 2004 13:46:57 -0000 1.14 +++ core/windows/src/org/netbeans/core/windows/view/ui/slides/SlideBar.java 10 Sep 2004 18:54:56 -0000 @@ -23,8 +23,10 @@ import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.DefaultSingleSelectionModel; @@ -219,10 +221,39 @@ int index = getButtonIndex(clickedButton); commandMgr.showPopup(mouseEvent, index); } + + private SlidingButton buttonFor (TopComponent tc) { + int idx = 0; + for (Iterator i=dataModel.getTabs().iterator(); i.hasNext();) { + TabData td = (TabData) i.next(); + if (td.getComponent() == tc) { + break; + } + if (!i.hasNext()) { + idx = -1; + } else { + idx++; + } + } + if (idx >= 0 && idx < dataModel.size()) { + return getButton(idx); + } else { + return null; + } + } + + public void setBlinking (TopComponent tc, boolean val) { + SlidingButton button = buttonFor (tc); + if (button != null) { + button.setBlinking(val); + } + } /** Triggers slide operation by changing selected index */ public void userClickedSlidingButton(Component clickedButton) { int index = getButtonIndex(clickedButton); + SlidingButton button = (SlidingButton) buttons.get(index); + button.setBlinking(false); if (index != selModel.getSelectedIndex() || !isActive()) { TopComponent tc = (TopComponent)dataModel.getTab(index).getComponent(); @@ -240,6 +271,8 @@ if (index < 0) { return false; } + SlidingButton button = (SlidingButton) buttons.get(index); + button.setBlinking(false); TopComponent tc = (TopComponent)dataModel.getTab(index).getComponent(); if (tc == null) { return false; @@ -340,9 +373,16 @@ private void syncWithModel () { assert SwingUtilities.isEventDispatchThread(); - + Set blinks = null; for (Iterator iter = buttons.iterator(); iter.hasNext(); ) { - gestureRecognizer.detachButton((SlidingButton)iter.next()); + SlidingButton curr = (SlidingButton) iter.next(); + if (curr.isBlinking()) { + if (blinks == null) { + blinks = new HashSet(); + } + blinks.add (curr.getData()); + } + gestureRecognizer.detachButton(curr); } removeAll(); buttons.clear(); @@ -350,7 +390,11 @@ List dataList = dataModel.getTabs(); SlidingButton curButton; for (Iterator iter = dataList.iterator(); iter.hasNext(); ) { - curButton = new SlidingButton((TabData)iter.next(), dataModel.getOrientation()); + TabData td = (TabData) iter.next(); + curButton = new SlidingButton(td, dataModel.getOrientation()); + if (blinks != null && blinks.contains(td)) { + curButton.setBlinking(true); + } gestureRecognizer.attachButton(curButton); buttons.add(curButton); add(curButton); Index: core/windows/src/org/netbeans/core/windows/view/ui/slides/SlideBarContainer.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/ui/slides/SlideBarContainer.java,v --- core/windows/src/org/netbeans/core/windows/view/ui/slides/SlideBarContainer.java 3 Jun 2004 12:07:30 -0000 1.3 +++ core/windows/src/org/netbeans/core/windows/view/ui/slides/SlideBarContainer.java 10 Sep 2004 18:54:56 -0000 @@ -48,6 +48,14 @@ panel.add(this.tabbedHandler.getComponent(), BorderLayout.CENTER); } + public void requestAttention (TopComponent tc) { + tabbedHandler.requestAttention(tc); + } + + public void cancelRequestAttention (TopComponent tc) { + tabbedHandler.cancelRequestAttention (tc); + } + public void setTopComponents(TopComponent[] tcs, TopComponent selected) { super.setTopComponents(tcs, selected); } Index: core/windows/src/org/netbeans/core/windows/view/ui/slides/TabbedSlideAdapter.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/ui/slides/TabbedSlideAdapter.java,v --- core/windows/src/org/netbeans/core/windows/view/ui/slides/TabbedSlideAdapter.java 3 Jun 2004 12:07:31 -0000 1.4 +++ core/windows/src/org/netbeans/core/windows/view/ui/slides/TabbedSlideAdapter.java 10 Sep 2004 18:54:56 -0000 @@ -69,6 +69,15 @@ selModel = new DefaultSingleSelectionModel(); slideBar = new SlideBar(this, (SlideBarDataModel)dataModel, selModel); } + + public void requestAttention (TopComponent tc) { + slideBar.setBlinking(tc, true); + } + + public void cancelRequestAttention (TopComponent tc) { + slideBar.setBlinking(tc, false); + } + private void setSide (String side) { int orientation = SlideBarDataModel.WEST; @@ -235,6 +244,11 @@ int newIndex = indexOf(comp); if (selModel.getSelectedIndex() != newIndex) { selModel.setSelectedIndex(newIndex); + } + if (comp instanceof TopComponent) { + //Inelegant to do this here, but it guarantees blinking stops + TopComponent tc = (TopComponent) comp; + tc.cancelRequestAttention(); } } Index: core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/TabbedAdapter.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/TabbedAdapter.java,v --- core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/TabbedAdapter.java 1 Sep 2004 12:56:30 -0000 1.22 +++ core/windows/src/org/netbeans/core/windows/view/ui/tabcontrol/TabbedAdapter.java 10 Sep 2004 18:54:57 -0000 @@ -83,6 +83,26 @@ int i = getSelectionModel().getSelectedIndex(); return i == -1 ? null : getTopComponentAt(i); } + + public void requestAttention (TopComponent tc) { + int idx = indexOf(tc); + if (idx >= 0) { + requestAttention(idx); + } else { + ErrorManager.getDefault().log(ErrorManager.INFORMATIONAL, + "RequestAttention on component unknown to container: " + tc); //NOI18N + } + } + + public void cancelRequestAttention (TopComponent tc) { + int idx = indexOf(tc); + if (idx >= 0) { + cancelRequestAttention(idx); + } else { + throw new IllegalArgumentException ("TopComponent " + tc + + " is not a child of this container"); //NOI18N + } + } public void insertComponent(String name, javax.swing.Icon icon, Component comp, String toolTip, int position) { TabData td = new TabData (comp, icon, name, toolTip); Index: openide/src/org/openide/windows/TopComponent.java =================================================================== RCS file: /cvs/openide/src/org/openide/windows/TopComponent.java,v --- openide/src/org/openide/windows/TopComponent.java 12 Aug 2004 07:38:38 -0000 1.132 +++ openide/src/org/openide/windows/TopComponent.java 10 Sep 2004 18:55:47 -0000 @@ -16,6 +16,7 @@ import java.awt.Image; import java.awt.Toolkit; import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.util.*; import java.beans.*; @@ -40,10 +41,12 @@ import javax.swing.JComponent; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; +import javax.swing.Timer; import javax.swing.text.Keymap; import org.openide.ErrorManager; import org.openide.awt.UndoRedo; +import org.openide.util.Mutex; import org.openide.util.actions.SystemAction; import org.openide.nodes.*; import org.openide.util.ContextAwareAction; @@ -550,6 +553,96 @@ public void requestVisible () { WindowManager.getDefault().topComponentRequestVisible(this); } + + /** + * Cause this TopComponent's tab to flash or otherwise draw attention to + * itself. This method is thread-safe. + *
+ * It will remain flashing until either cancelRequestAttention
+ * is called, the component becomes selected or its activated state changes,
+ * unless the brief
argument is true, in which case it will stop
+ * after a few second.
+ * @param brief True if the tab should blink a few times and stop
+ * @since 4.45
+ */
+ public final void requestAttention(final boolean brief) {
+ //Reentrancy issues - always invoke later
+ SwingUtilities.invokeLater (new Runnable() {
+ public void run() {
+ if (attentionGetter != null && !brief) {
+ attentionGetter.kill();
+ } else if (!brief) {
+ WindowManager.getDefault().topComponentRequestAttention(
+ TopComponent.this);
+ } else if (attentionGetter != null) {
+ attentionGetter.reset();
+ } else {
+ attentionGetter = new AttentionGetter();
+ }
+ }
+ });
+ }
+
+ /**
+ * Cause this TopComponent's tab to stop flashing if it was flashing.
+ * @since 4.45
+ */
+ public final void cancelRequestAttention() {
+ //Reentrancy issues - always invoke later
+ SwingUtilities.invokeLater (new Runnable() {
+ public void run() {
+ if (attentionGetter != null) {
+ attentionGetter.stop();
+ } else {
+ WindowManager.getDefault().topComponentCancelRequestAttention(
+ TopComponent.this);
+ }
+ }
+ });
+ }
+
+ private AttentionGetter attentionGetter = null;
+ private class AttentionGetter implements ActionListener {
+ Timer timer = null;
+ public AttentionGetter () {
+ reset();
+ }
+
+ public void reset() {
+ assert SwingUtilities.isEventDispatchThread();
+ if (timer != null) {
+ timer.stop();
+ }
+ start();
+ timer = new Timer (3500, this);
+ timer.setRepeats(false);
+ timer.start();
+ }
+
+ private void start() {
+ WindowManager.getDefault().topComponentRequestAttention(
+ TopComponent.this);
+ }
+
+ public void kill() {
+ timer.stop();
+ attentionGetter = null;
+ }
+
+ private void stop() {
+ if (timer != null) {
+ timer.stop();
+ }
+ attentionGetter = null;
+ WindowManager.getDefault().topComponentCancelRequestAttention(
+ TopComponent.this);
+ attentionGetter = null;
+ }
+
+ public void actionPerformed (ActionEvent ae) {
+ stop();
+ }
+ }
/** Set the name of this top component.
* The default implementation just notifies the window manager.
Index: openide/src/org/openide/windows/WindowManager.java
===================================================================
RCS file: /cvs/openide/src/org/openide/windows/WindowManager.java,v
--- openide/src/org/openide/windows/WindowManager.java 3 Dec 2003 13:48:46 -0000 1.44
+++ openide/src/org/openide/windows/WindowManager.java 10 Sep 2004 18:55:48 -0000
@@ -365,6 +365,20 @@
* @since 4.13 */
protected abstract String topComponentID(TopComponent tc, String preferredID);
+ /**
+ * Cause this TopComponent's tab to flash or otherwise draw the users' attention
+ * to it.
+ * @param tc A TopComponent
+ * @since 4.45 */
+ protected abstract void topComponentRequestAttention(TopComponent tc);
+
+ /**
+ * Stop this TopComponent's tab from flashing if it is flashing.
+ *
+ * @param tc A TopComponent
+ * @since 4.45 */
+ protected abstract void topComponentCancelRequestAttention(TopComponent tc);
+
/** Returns unique ID for specified TopComponent
.
* @param tc TopComponent
the component for which is ID returned
* @return unique TopComponent
ID