Allow custom implementation of tab control.
diff --git a/o.n.swing.tabcontrol/manifest.mf b/o.n.swing.tabcontrol/manifest.mf
--- a/o.n.swing.tabcontrol/manifest.mf
+++ b/o.n.swing.tabcontrol/manifest.mf
@@ -1,6 +1,6 @@
Manifest-Version: 1.0
OpenIDE-Module-Localizing-Bundle: org/netbeans/swing/tabcontrol/Bundle.properties
OpenIDE-Module: org.netbeans.swing.tabcontrol
-OpenIDE-Module-Specification-Version: 1.33
+OpenIDE-Module-Specification-Version: 1.34
AutoUpdate-Essential-Module: true
diff --git a/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/TabDisplayerUI.java b/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/TabDisplayerUI.java
--- a/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/TabDisplayerUI.java
+++ b/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/TabDisplayerUI.java
@@ -49,16 +49,19 @@
package org.netbeans.swing.tabcontrol;
-import org.netbeans.swing.tabcontrol.event.TabActionEvent;
-
-import javax.swing.*;
-import javax.swing.plaf.ComponentUI;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Map;
+import javax.swing.Icon;
+import javax.swing.JComponent;
+import javax.swing.SingleSelectionModel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import org.netbeans.swing.tabcontrol.event.TabActionEvent;
import org.netbeans.swing.tabcontrol.plaf.TabControlButton;
import org.netbeans.swing.tabcontrol.plaf.TabControlButtonFactory;
+import org.openide.windows.TopComponent;
/**
* The basic UI of a tab displayer component. Defines the API of the UI for
@@ -225,6 +228,26 @@
}
/**
+ * Check if the given tab is busy and should be painted in a special way.
+ * @param tabIndex
+ * @return True if given tab is 'busy', false otherwise.
+ * @since 1.34
+ */
+ public final boolean isTabBusy( int tabIndex ) {
+ WinsysInfoForTabbedContainer winsysInfo = displayer.getContainerWinsysInfo();
+ if( null == winsysInfo )
+ return false;
+ TabDataModel model = displayer.getModel();
+ if( tabIndex < 0 || tabIndex >= model.size() )
+ return false;
+ TabData td = model.getTab( tabIndex );
+ if( td.getComponent() instanceof TopComponent ) {
+ return winsysInfo.isTopComponentBusy( (TopComponent)td.getComponent() );
+ }
+ return false;
+ }
+
+ /**
* Installs the selection model into the tab control via a package private
* method.
*/
@@ -247,8 +270,8 @@
protected abstract void requestAttention (int tab);
- protected abstract void cancelRequestAttention (int tab);
-
+ protected abstract void cancelRequestAttention (int tab);
+
/**
* @since 1.9
* @return An icon for various buttons displayed in tab control (close/pin/scroll left/right etc), see TabControlButton class.
diff --git a/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/WinsysInfoForTabbedContainer.java b/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/WinsysInfoForTabbedContainer.java
--- a/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/WinsysInfoForTabbedContainer.java
+++ b/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/WinsysInfoForTabbedContainer.java
@@ -132,10 +132,21 @@
return false;
}
+ /**
+ * Check if the header of given TopComponent should be painted in a special
+ * way to indicate that some process is running in that TopComponent.
+ * @param tc
+ * @return True to indicate process being run in the TopComponent, false otherwise.
+ * @since 1.34
+ */
+ public boolean isTopComponentBusy( TopComponent tc ) {
+ return false;
+ }
+
public static WinsysInfoForTabbedContainer getDefault( WinsysInfoForTabbed winsysInfo ) {
return new DefaultWinsysInfoForTabbedContainer( winsysInfo );
}
-
+
private static class DefaultWinsysInfoForTabbedContainer extends WinsysInfoForTabbedContainer {
private WinsysInfoForTabbed delegate;
diff --git a/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/customtabs/Tabbed.java b/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/customtabs/Tabbed.java
--- a/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/customtabs/Tabbed.java
+++ b/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/customtabs/Tabbed.java
@@ -141,6 +141,27 @@
public abstract boolean isTransparent();
public abstract void setTransparent( boolean transparent );
+
+ /**
+ * Notify user that given TopComponent is 'busy' (some lengthy process is
+ * running in it).
+ * @param tc
+ * @param busy True to make the TopComponent busy, false to cancel the notification.
+ * @since 1.34
+ */
+ public void makeBusy( TopComponent tc, boolean busy ) {
+ }
+
+ /**
+ * Check if given TopComponent is busy
+ * @param tc
+ * @return True if the TopComponent is busy and its header should be painted
+ * in a special way, false otherwise.
+ * @since 1.34
+ */
+ public boolean isBusy( TopComponent tc ) {
+ return false;
+ }
/**
* Visual containers that hold the tabbed components must implement this interface
diff --git a/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AbstractTabCellRenderer.java b/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AbstractTabCellRenderer.java
--- a/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AbstractTabCellRenderer.java
+++ b/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AbstractTabCellRenderer.java
@@ -268,6 +268,13 @@
return (state & TabState.AFTER_SELECTED) != 0;
}
+ /**
+ * @return True if the tab is busy.
+ */
+ protected final boolean isBusy() {
+ return (state & TabState.BUSY) != 0;
+ }
+
public Dimension getPadding() {
return new Dimension(padding);
}
diff --git a/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AquaEditorTabCellRenderer.java b/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AquaEditorTabCellRenderer.java
--- a/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AquaEditorTabCellRenderer.java
+++ b/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AquaEditorTabCellRenderer.java
@@ -92,6 +92,9 @@
@Override
protected void paintIconAndText(Graphics g) {
+ if( isBusy() ) {
+ setIcon( BusyTabsSupport.getDefault().getBusyIcon( isSelected() ) );
+ }
super.paintIconAndText(g);
}
diff --git a/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AquaViewTabDisplayerUI.java b/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AquaViewTabDisplayerUI.java
--- a/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AquaViewTabDisplayerUI.java
+++ b/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AquaViewTabDisplayerUI.java
@@ -64,6 +64,7 @@
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.UIManager;
+import org.netbeans.swing.tabcontrol.WinsysInfoForTabbedContainer;
/**
* A view tabs ui for OS-X adapted from the view tabs UI for Metal.
@@ -165,6 +166,17 @@
textY = (height / 2) - (textHeight / 2) + fm.getAscent();
}
+ boolean slidedOut = false;
+ WinsysInfoForTabbedContainer winsysInfo = displayer.getContainerWinsysInfo();
+ if( null != winsysInfo && winsysInfo.isSlidedOutContainer() )
+ slidedOut = false;
+ if( isTabBusy( index ) && !slidedOut ) {
+ Icon busyIcon = BusyTabsSupport.getDefault().getBusyIcon( isSelected( index ) );
+ textW -= busyIcon.getIconWidth() - 3 - TXT_X_PAD;
+ busyIcon.paintIcon( displayer, g, textX, y+(height-busyIcon.getIconHeight())/2);
+ textX += busyIcon.getIconWidth() + 3;
+ }
+
int realTextWidth = (int)HtmlRenderer.renderString(text, g, textX, textY, textW, height, getTxtFont(),
UIManager.getColor("textText"), //NOI18N
HtmlRenderer.STYLE_TRUNCATE, false);
diff --git a/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/BasicTabDisplayerUI.java b/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/BasicTabDisplayerUI.java
--- a/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/BasicTabDisplayerUI.java
+++ b/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/BasicTabDisplayerUI.java
@@ -70,6 +70,7 @@
import javax.swing.event.ListDataEvent;
import org.netbeans.swing.tabcontrol.TabData;
import org.netbeans.swing.tabcontrol.TabDisplayer;
+import org.netbeans.swing.tabcontrol.WinsysInfoForTabbedContainer;
import org.netbeans.swing.tabcontrol.event.ComplexListDataEvent;
import org.openide.windows.TopComponent;
@@ -427,6 +428,10 @@
TabCellRenderer ren = getTabCellRenderer(i);
TabData data = displayer.getModel().getTab(i);
+
+ if( isTabBusy( i ) ) {
+ state |= TabState.BUSY;
+ }
JComponent renderer = ren.getRendererComponent(
data, scratch, state);
diff --git a/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/BusyIcon.java b/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/BusyIcon.java
new file mode 100644
--- /dev/null
+++ b/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/BusyIcon.java
@@ -0,0 +1,210 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2012 Sun Microsystems, Inc.
+ */
+package org.netbeans.swing.tabcontrol.plaf;
+
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.geom.AffineTransform;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.Icon;
+import javax.swing.UIManager;
+import org.openide.util.ImageUtilities;
+import org.openide.util.Lookup;
+
+/**
+ * An animated icon to indicate that a tab is 'busy'.
+ * @see BusyTabsSupport
+ * @author S. Aubrecht
+ * @since 1.34
+ */
+abstract class BusyIcon implements Icon {
+
+ protected final int width;
+ protected final int height;
+
+ protected BusyIcon( int width, int height ) {
+ this.width = width;
+ this.height = height;
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * The implementation first checks UIManager
defaults and looks for Icon
+ * under keys "nb.tabcontrol.busy.icon.selected"
and "nb.tabcontrol.busy.icon.normal"
.
+ * If there is an Icon under those keys then the created instance will rotate
+ * that Icon to animate it.
+ *
+ * If there are no Icons in UIManager then there will be an attempt to create
+ * animated Icon based BusyPainter
in SwingX library. If swingx.jar
+ * is available on classpath then reflection is used to create BusyPainter
+ * instance and paint icon animations with it.
+ *
+ * If SwingX library isn't available then the default image
+ * "org/netbeans/swing/tabcontrol/resources/busy_icon.png"
+ * will be rotated.
+ *
+ *
+ * @param selectedTab Boolean to create icon for selected tab state, false
+ * to create icon for normal tab state.
+ * @return Animated icon.
+ */
+ public static BusyIcon create( boolean selectedTab ) {
+ BusyIcon res = null;
+ Icon img = UIManager.getIcon( "nb.tabcontrol.busy.icon." + (selectedTab ? ".selected" : ".normal") ); //NOI18N
+ if( null != img ) {
+ res = new ImageBusyIcon( ImageUtilities.icon2Image( img ) );
+ } else {
+ res = SwingXBusyIcon.create();
+ }
+ if( null == res )
+ res = new ImageBusyIcon( ImageUtilities.loadImage( "org/netbeans/swing/tabcontrol/resources/busy_icon.png") ); //NOI18N
+ return res;
+ }
+
+ abstract void tick();
+
+ @Override
+ public final int getIconWidth() {
+ return width;
+ }
+
+ @Override
+ public final int getIconHeight() {
+ return height;
+ }
+
+ private static class ImageBusyIcon extends BusyIcon {
+
+ private final Image img;
+ private int state = 0;
+ private AffineTransform at;
+ private static final int STEP = 15;
+
+ public ImageBusyIcon( Image img ) {
+ super( img.getWidth( null ), img.getHeight( null ) );
+ this.img = img;
+ }
+
+ @Override
+ void tick() {
+ state += STEP;
+ if( state >= 360 )
+ state = 0;
+ at = new AffineTransform();
+ at.rotate( state * Math.PI / 180.0, width/2, height/2 );
+ }
+
+ @Override
+ public void paintIcon( Component c, Graphics g, int x, int y ) {
+ if( g instanceof Graphics2D ) {
+ Graphics2D g2d = ( Graphics2D ) g;
+ g2d.translate( x, y );
+ g2d.drawImage( img, at, null );
+ g2d.translate( -x, -y );
+ }
+ }
+ }
+
+ private static class SwingXBusyIcon extends BusyIcon {
+
+ private final Object painter;
+ private final Method setFrameMethod;
+ private final Method paintMethod;
+ private int currentFrame = 0;
+ private static final int POINTS = 8;
+ private static final int HEIGHT = 14;
+
+ private SwingXBusyIcon( Object painter, Method paint, Method setFrame ) {
+ super( HEIGHT, HEIGHT );
+ this.painter = painter;
+ this.setFrameMethod = setFrame;
+ this.paintMethod = paint;
+ }
+
+ public static BusyIcon create() {
+ Object painter = null;
+ ClassLoader cl = Lookup.getDefault().lookup( ClassLoader.class );
+ try {
+ Class painterClass = cl.loadClass( "org.jdesktop.swingx.painter.BusyPainter" ); //NOI18N
+ Constructor ctor = painterClass.getConstructor( int.class );
+ painter = ctor.newInstance( HEIGHT );
+ Method setFrame = painterClass.getMethod( "setFrame", int.class ); //NOI18N
+ Method paint = painterClass.getMethod( "paint", Graphics2D.class, Object.class, int.class, int.class ); //NOI18N
+ Method m = painterClass.getMethod( "setPoints", int.class ); //NOI18N
+ m.invoke( painter, POINTS );
+ return new SwingXBusyIcon( painter, paint, setFrame );
+ } catch( Exception ex ) {
+ Logger.getLogger( BusyIcon.class.getName() ).log( Level.FINE, null, ex );
+ }
+ return null;
+ }
+
+ @Override
+ public void tick() {
+ currentFrame = (currentFrame + 1) % POINTS;
+ try {
+ setFrameMethod.invoke( painter, currentFrame );
+ } catch( Exception ex ) {
+ }
+ }
+
+ @Override
+ public void paintIcon( Component c, Graphics g, int x, int y ) {
+ if( g instanceof Graphics2D ) {
+ Graphics2D g2d = ( Graphics2D ) g;
+ try {
+ g2d.translate( x, y );
+ paintMethod.invoke( painter, g, c, x, y );
+ } catch( Exception ex ) {
+ Logger.getLogger( BusyIcon.class.getName() ).log( Level.FINE, null, ex );
+ }
+ g2d.translate( -x, -y );
+ }
+ }
+ }
+}
diff --git a/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/BusyTabsSupport.java b/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/BusyTabsSupport.java
new file mode 100644
--- /dev/null
+++ b/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/BusyTabsSupport.java
@@ -0,0 +1,277 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2012 Sun Microsystems, Inc.
+ */
+package org.netbeans.swing.tabcontrol.plaf;
+
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Set;
+import javax.swing.Icon;
+import javax.swing.Timer;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import org.netbeans.swing.tabcontrol.TabDataModel;
+import org.netbeans.swing.tabcontrol.customtabs.Tabbed;
+import org.openide.util.Lookup;
+import org.openide.util.WeakSet;
+import org.openide.util.lookup.ServiceProvider;
+import org.openide.windows.TopComponent;
+
+/**
+ * Support class to implement animation in tab headers to indicate some sort of
+ * 'busy' state.
install(Tabbed, TabDataModel)) and if any tab is marked as 'busy'
+ * (TopComponent.makeBusy(boolean)
) it forces repeated repaints of
+ * that tab to allow animation effects. The UI of the tab container then can use
+ * an icon this class provides (getBusyIcon(boolean)
)to indicate
+ * the busy state. The default implementation of this class ensures that the
+ * icon is properly animated in each repaint.
+ *
+ * @see TopComponent#makeBusy(boolean)
+ *
+ * @author S. Aubrecht
+ */
+public abstract class BusyTabsSupport {
+
+ private Timer animationTimer;
+
+ private final ChangeListener modelListener = new ChangeListener() {
+
+ @Override
+ public void stateChanged( ChangeEvent e ) {
+ checkBusyTabs();
+ }
+ };
+
+ private final Set containers = new WeakSet(10);
+ private final Set busyContainers = new WeakSet(10);
+
+ /**
+ * @return The default implementation registered in global Lookup.
+ * @see DefaultBusyTabsSupport
+ */
+ public static BusyTabsSupport getDefault() {
+ return Lookup.getDefault().lookup( BusyTabsSupport.class );
+ }
+
+ /**
+ * Track changes in given tab container to have busy tab headers repainted.
+ * @param tabContainer Tab container. This method should be typically called
+ * from container's addNotify()
.
+ * @param tabModel Container data model.
+ */
+ public final void install( Tabbed tabContainer, TabDataModel tabModel ) {
+ if( containers.contains( tabContainer ) )
+ return;
+ tabModel.addChangeListener( modelListener );
+ containers.add( tabContainer );
+ checkBusyTabs();
+ }
+
+ /**
+ * Unregister the given tab container. This method should be typically called
+ * from container's removeNotify()
.
+ *
+ * @param tabContainer
+ * @param tabModel
+ */
+ public final void uninstall( Tabbed tabContainer, TabDataModel tabModel ) {
+ if( busyContainers.remove( tabContainer ) )
+ repaintAll( tabContainer );
+ tabModel.removeChangeListener( modelListener );
+ containers.remove( tabContainer );
+ checkBusyTabs();
+ }
+
+ /**
+ * An icon that can be shown in busy tab's header to indicate some lengthy
+ * process is being run in it. The default implementation ensures the icon
+ * is properly animated.
+ * @param isTabSelected True to get icon for a selected tab, false to get
+ * icon for regular tab state.
+ * @return Busy-like icon.
+ */
+ public abstract Icon getBusyIcon( boolean isTabSelected );
+
+ /**
+ * Notification that busy state has changed for a given tab.
+ * @param tabContainer Tab container.
+ * @param tabIndex Index of the tab.
+ * @param busy
+ */
+ public final void makeTabBusy( Tabbed tabContainer, int tabIndex, boolean busy ) {
+ if( !busy ) {
+ Rectangle tabRect = tabContainer.getTabBounds( tabIndex );
+ tabContainer.getComponent().repaint( tabRect.x, tabRect.y, tabRect.width, tabRect.height );
+ }
+ checkBusyTabs();
+ }
+
+ /**
+ *
+ * @return The time in milliseconds between repaints of busy tabs. Value of
+ * zero means there are no repeated repaints.
+ */
+ protected abstract int getRepaintTimerIntervalMillis();
+
+ /**
+ * Invoked between each repaint events. The default implementation animates
+ * the busy icon in this method.
+ */
+ protected abstract void tick();
+
+ private void startAnimationTimer() {
+ if( null != animationTimer ) {
+ return;
+ }
+ int interval = getRepaintTimerIntervalMillis();
+ if( interval <= 0 )
+ return;
+ animationTimer = new Timer( interval, new ActionListener() {
+ @Override
+ public void actionPerformed( ActionEvent e ) {
+ checkBusyTabs();
+ repaintBusyTabs();
+ tick();
+ }
+ });
+ animationTimer.setRepeats( true );
+ animationTimer.start();
+ }
+
+ private void stopAnimationTimer() {
+ if( null == animationTimer ) {
+ return;
+ }
+ animationTimer.stop();
+ animationTimer = null;
+ repaintBusyTabs();
+ }
+
+ private void checkBusyTabs() {
+ busyContainers.clear();
+
+ for( Tabbed tc : containers ) {
+ if( hasBusyTabs( tc ) )
+ busyContainers.add( tc );
+ }
+
+ if( busyContainers.isEmpty() ) {
+ stopAnimationTimer();
+ } else {
+ startAnimationTimer();
+ }
+ }
+
+ private void repaintBusyTabs() {
+ for( Tabbed tc : busyContainers ) {
+ repaintBusy( tc );
+ }
+ }
+
+ private void repaintBusy( Tabbed tabbed ) {
+ for( int i=0; i#>hky!;IgqZ);iY8CLx|=d8WI@9eeL
z-g#?pFzXO4JxAarHK8!?B^b*7jtLm{20^?DyLQm5z>3e{G|r0#Jum>HuwzV`3GqBk
zY5tEv9gg5pCSfJxa0xd~d=JY|@q1uR-ry6KU_m8jUK}D%bfFKrq2}_l>O{pRyI>!B
zP3{g(61#s3=dj}2SaK7a9a{M16R8sVWm0$fY5nY>-9d`tZCH0@g#&f~7ckS#Ac`kp
zOa>cJR!80?{$)}m#LsZ3F3iEA47T9Pn?!>T)ojJKQ-yMCa1B*>je{W?aI7;>OYDBG
rXArLHj#RBdG->?fHa^*pZvh4Xx%ov%((@F#00000NkvXXu0mjf2hyg}
diff --git a/openide.windows/apichanges.xml b/openide.windows/apichanges.xml
--- a/openide.windows/apichanges.xml
+++ b/openide.windows/apichanges.xml
@@ -50,6 +50,21 @@
Window System API
+
+
+ Added method TopComponent.makeBusy(boolean).
+
+
+
+
+
+ The new method can be used to inform user that some (possibly lengthy)
+ process is being run in given TopComponent.
+
+
+
+
+
Added branding options to replace the custom TabbedContainer with
diff --git a/openide.windows/manifest.mf b/openide.windows/manifest.mf
--- a/openide.windows/manifest.mf
+++ b/openide.windows/manifest.mf
@@ -1,6 +1,6 @@
Manifest-Version: 1.0
OpenIDE-Module: org.openide.windows
-OpenIDE-Module-Specification-Version: 6.50
+OpenIDE-Module-Specification-Version: 6.51
OpenIDE-Module-Localizing-Bundle: org/openide/windows/Bundle.properties
AutoUpdate-Essential-Module: true
diff --git a/openide.windows/src/org/openide/windows/TopComponent.java b/openide.windows/src/org/openide/windows/TopComponent.java
--- a/openide.windows/src/org/openide/windows/TopComponent.java
+++ b/openide.windows/src/org/openide/windows/TopComponent.java
@@ -95,15 +95,7 @@
import org.openide.nodes.Node;
import org.openide.nodes.NodeAdapter;
import org.openide.nodes.NodeListener;
-import org.openide.util.ContextAwareAction;
-import org.openide.util.HelpCtx;
-import org.openide.util.ImageUtilities;
-import org.openide.util.Lookup;
-import org.openide.util.NbBundle;
-import org.openide.util.NbPreferences;
-import org.openide.util.Utilities;
-import org.openide.util.WeakListeners;
-import org.openide.util.WeakSet;
+import org.openide.util.*;
import org.openide.util.actions.NodeAction;
import org.openide.util.actions.SystemAction;
@@ -922,6 +914,25 @@
}
/**
+ * Notify the user that some (possibly lengthy) process is being run in this
+ * window.
+ * It is safe to call this method outside EDT.
+ *
+ * @param True to start 'busy' notification, 'false' to stop it.
+ *
+ * @see WindowManager#topComponentMakeBusy(org.openide.windows.TopComponent, boolean)
+ * @since 6.51
+ */
+ public final void makeBusy( final boolean busy ) {
+ Mutex.EVENT.readAccess( new Runnable() {
+ @Override
+ public void run() {
+ WindowManager.getDefault().topComponentMakeBusy( TopComponent.this, busy );
+ }
+ });
+ }
+
+ /**
* Cause this TopComponent's tab to stop flashing if it was flashing.
* @since 5.1
*/
diff --git a/openide.windows/src/org/openide/windows/WindowManager.java b/openide.windows/src/org/openide/windows/WindowManager.java
--- a/openide.windows/src/org/openide/windows/WindowManager.java
+++ b/openide.windows/src/org/openide/windows/WindowManager.java
@@ -488,6 +488,19 @@
}
/**
+ * Notifies the user that some process is running in the given TopComponent,
+ * for example by drawing an animated "wait" icon in TopComponent's header.
+ * The default implementation does nothing.
+ *
+ * @param tc
+ * @param busy True to start 'busy' notification, false to stop it.
+ *
+ * @since 6.51
+ */
+ protected void topComponentMakeBusy( TopComponent tc, boolean busy ) {
+ }
+
+ /**
* Attempts to bring the parent Window
of the given TopComponent
* to front of other windows.
* @see java.awt.Window#toFront()