diff --git a/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TabPaneBehavior.java b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TabPaneBehavior.java --- a/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TabPaneBehavior.java +++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TabPaneBehavior.java @@ -29,16 +29,17 @@ import javafx.collections.ObservableList; import javafx.event.Event; +import javafx.scene.Node; import javafx.scene.Parent; -import javafx.scene.Node; +import javafx.scene.control.SingleSelectionModel; import javafx.scene.control.Tab; +import javafx.scene.control.TabEvent; import javafx.scene.control.TabPane; import javafx.scene.input.KeyCode; import javafx.scene.input.MouseEvent; import com.sun.javafx.scene.control.skin.TabPaneSkin; import com.sun.javafx.scene.traversal.Direction; -import javafx.scene.control.SingleSelectionModel; public class TabPaneBehavior extends BehaviorBase { @@ -270,7 +271,6 @@ return firstPopulatedEngine; } - /*************************************************************************** * * * Mouse event handling * @@ -295,6 +295,12 @@ getControl().getSelectionModel().select(tab); } + public boolean canCloseTab(Tab tab) { + TabEvent event = new TabEvent(tab,TabEvent.TAB_CLOSE_REQUEST); + Event.fireEvent(tab, event); + return ! event.isConsumed(); + } + public void closeTab(Tab tab) { TabPane tabPane = getControl(); // only switch to another tab if the selected tab is the one we're closing diff --git a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TabPaneSkin.java b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TabPaneSkin.java --- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TabPaneSkin.java +++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TabPaneSkin.java @@ -912,9 +912,13 @@ closeBtn.getStyleClass().setAll("tab-close-button"); closeBtn.setOnMousePressed(new EventHandler() { @Override public void handle(MouseEvent me) { - removeListeners(getTab()); - getBehavior().closeTab(getTab()); - setOnMousePressed(null); + Tab tab = getTab(); + TabPaneBehavior behavior = getBehavior(); + if( behavior.canCloseTab(tab) ) { + removeListeners(tab); + behavior.closeTab(tab); + setOnMousePressed(null); + } } }); @@ -1083,8 +1087,12 @@ } if (me.getButton().equals(MouseButton.MIDDLE)) { if (showCloseButton()) { - removeListeners(getTab()); - getBehavior().closeTab(getTab()); + Tab tab = getTab(); + TabPaneBehavior behavior = getBehavior(); + if( behavior.canCloseTab(tab) ) { + removeListeners(tab); + behavior.closeTab(tab); + } } } else if (me.getButton().equals(MouseButton.PRIMARY)) { getBehavior().selectTab(getTab()); diff --git a/javafx-ui-controls/src/javafx/scene/control/Tab.java b/javafx-ui-controls/src/javafx/scene/control/Tab.java --- a/javafx-ui-controls/src/javafx/scene/control/Tab.java +++ b/javafx-ui-controls/src/javafx/scene/control/Tab.java @@ -24,17 +24,26 @@ */ package javafx.scene.control; -import com.sun.javafx.css.Styleable; -import com.sun.javafx.css.StyleableProperty; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +import javafx.beans.DefaultProperty; import javafx.beans.property.BooleanProperty; +import javafx.beans.property.BooleanPropertyBase; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectPropertyBase; +import javafx.beans.property.ReadOnlyBooleanProperty; +import javafx.beans.property.ReadOnlyBooleanWrapper; +import javafx.beans.property.ReadOnlyObjectProperty; +import javafx.beans.property.ReadOnlyObjectWrapper; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import javafx.collections.ObservableMap; import javafx.event.Event; import javafx.event.EventDispatchChain; import javafx.event.EventHandler; @@ -42,13 +51,9 @@ import javafx.event.EventType; import javafx.scene.Node; +import com.sun.javafx.css.Styleable; +import com.sun.javafx.css.StyleableProperty; import com.sun.javafx.event.EventHandlerManager; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import javafx.beans.DefaultProperty; -import javafx.beans.property.*; -import javafx.collections.ObservableMap; /** *

Tabs are placed within a {@link TabPane}, where each tab represents a single @@ -587,11 +592,50 @@ } return disabled; } - + private void updateDisabled() { setDisabled(isDisable() || (getTabPane() != null && getTabPane().isDisabled())); } + /** + * Called when there is an external request to close this {@code Tab}. + * The installed event handler can prevent tab closing by consuming the + * received event. + */ + private ObjectProperty> onCloseRequest; + public final ObjectProperty> onCloseRequestProperty() { + if( onCloseRequest == null ) { + onCloseRequest = new ObjectPropertyBase>() { + @Override protected void invalidated() { + setEventHandler(TabEvent.TAB_CLOSE_REQUEST, get()); + } + + @Override + public Object getBean() { + return Tab.this; + } + + @Override + public String getName() { + return "onCloseRequest"; + } + }; + } + return onCloseRequest; + } + + public EventHandler getOnCloseRequest() { + if( onCloseRequest == null ) { + return null; + } + return onCloseRequest.get(); + } + + public void setOnCloseRequests(EventHandler value) { + onCloseRequestProperty().set(value); + } + + // --- Properties private static final Object USER_DATA_KEY = new Object(); diff --git a/javafx-ui-controls/src/javafx/scene/control/TabEvent.java b/javafx-ui-controls/src/javafx/scene/control/TabEvent.java new file mode 100644 --- /dev/null +++ b/javafx-ui-controls/src/javafx/scene/control/TabEvent.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javafx.scene.control; + +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.event.Event; +import javafx.event.EventTarget; +import javafx.event.EventType; +import javafx.stage.WindowEvent; + +/** + * Event related to tab + */ +public class TabEvent extends Event { + + private static final long serialVersionUID = 20121206L; + + /** + * Common supertype for all window event types. + */ + public static final EventType ANY = new EventType(Event.ANY, "TAB"); + + public static final EventType TAB_CLOSE_REQUEST = new EventType(TabEvent.ANY, "TAB_CLOSE_REQUEST"); + + private BooleanProperty vetoOnClose; + + /** + * Construct a new {@code Event} with the specified event source, target + * and type. If the source or target is set to {@code null}, it is replaced + * by the {@code NULL_SOURCE_TARGET} value. + * @param tab the event source which sent the event + * @param eventType the event type + */ + public TabEvent(Tab tab, EventType eventType) { + super(tab, tab, eventType); + } + + /** + * Returns a string representation of this {@code WindowEvent} object. + * @return a string representation of this {@code WindowEvent} object. + */ + @Override public String toString() { + final StringBuilder sb = new StringBuilder("TabEvent ["); + + sb.append("source = ").append(getSource()); + sb.append(", target = ").append(getTarget()); + sb.append(", eventType = ").append(getEventType()); + sb.append(", consumed = ").append(isConsumed()); + + return sb.append("]").toString(); + } + + @Override + public TabEvent copyFor(Object newSource, EventTarget newTarget) { + return (TabEvent) super.copyFor(newSource, newTarget); + } + + /** + * Creates a copy of the given event with the given fields substituted. + * @param source the new source of the copied event + * @param target the new target of the copied event + * @param eventType the new eventType + * @return the event copy with the fields substituted + */ + public TabEvent copyFor(Object newSource, EventTarget newTarget, EventType type) { + TabEvent e = copyFor(newSource, newTarget); + e.eventType = type; + return e; + } + + @Override + public EventType getEventType() { + return (EventType) super.getEventType(); + } +}