Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (c) 2011, 2013, 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.
*/
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.sun.javafx.scene.control.skin;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javafx.animation.Animation.Status;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.WeakInvalidationListener;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableBooleanValue;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.geometry.HPos;
import javafx.geometry.Orientation;
import javafx.geometry.Side;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.CustomMenuItem;
import javafx.scene.control.RadioMenuItem;
import javafx.scene.control.SeparatorMenuItem;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Rectangle;
import javafx.util.Callback;
import javafx.util.Duration;
import javafx.css.CssMetaData;
import javafx.css.PseudoClass;
import java.util.Iterator;
import javafx.geometry.NodeOrientation;
import javafx.stage.Window;
import com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler;
import com.sun.javafx.scene.control.behavior.TwoLevelFocusPopupBehavior;
import javafx.beans.WeakInvalidationListener;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.css.Styleable;
/**
* This is a the SkinBase for PopupMenu based controls so that the CSS parts
* work right, because otherwise we would have to copy the Keys from there to here.
* {@link PopupControlSkin} should be changed to use SkinBase instead of Resizable
* for its content and popup content.
*/
public class ContextMenuContent extends Region {
private ContextMenu contextMenu;
/***************************************************************************
* UI subcomponents
**************************************************************************/
private double maxGraphicWidth = 0; // we keep this margin to left for graphic
private double maxRightWidth = 0;
private double maxLabelWidth = 0;
private double maxRowHeight = 0;
private double maxLeftWidth = 0;
private Rectangle clipRect;
MenuBox itemsContainer;
private ArrowMenuItem upArrow;
private ArrowMenuItem downArrow;
/*
* We maintain a current focused index which is used
* in keyboard navigation of menu items.
*/
private int currentFocusedIndex = -1;
private boolean itemsDirty = true;
private InvalidationListener popupShowingListener = new InvalidationListener() {
@Override public void invalidated(Observable arg0) {
updateItems();
}
};
private WeakInvalidationListener weakPopupShowingListener =
new WeakInvalidationListener(popupShowingListener);
/***************************************************************************
* Constructors
**************************************************************************/
public ContextMenuContent(final ContextMenu popupMenu) {
this.contextMenu = popupMenu;
clipRect = new Rectangle();
clipRect.setSmooth(false);
itemsContainer = new MenuBox();
// itemsContainer = new VBox();
itemsContainer.setClip(clipRect);
upArrow = new ArrowMenuItem(this);
upArrow.setUp(true);
upArrow.setFocusTraversable(false);
downArrow = new ArrowMenuItem(this);
downArrow.setUp(false);
downArrow.setFocusTraversable(false);
getChildren().add(itemsContainer);
getChildren().add(upArrow);
getChildren().add(downArrow);
computeInitialSize();
initialize();
setUpBinds();
// RT-20197 add menuitems only on first show.
popupMenu.showingProperty().addListener(weakPopupShowingListener);
/*
** only add this if we're on an embedded
** platform that supports 5-button navigation
*/
if (Utils.isTwoLevelFocus()) {
new TwoLevelFocusPopupBehavior(this);
}
}
//For access from controls
public VBox getItemsContainer() {
return itemsContainer;
}
//For testing purpose only
int getCurrentFocusIndex() {
return currentFocusedIndex;
}
//For testing purpose only
void setCurrentFocusedIndex(int index) {
if (index < itemsContainer.getChildren().size()) {
currentFocusedIndex = index;
}
}
private void updateItems() {
if (itemsDirty) {
updateVisualItems();
itemsDirty = false;
if (getScene() != null) {
impl_processCSS(true);
}
}
}
private void computeVisualMetrics() {
maxRightWidth = 0;
maxLabelWidth = 0;
maxRowHeight = 0;
maxGraphicWidth = 0;
maxLeftWidth = 0;
for (int i = 0; i < itemsContainer.getChildren().size(); i++) {
Node child = itemsContainer.getChildren().get(i);
if (child instanceof MenuItemContainer) {
final MenuItemContainer menuItemContainer = (MenuItemContainer)itemsContainer.getChildren().get(i);
if (! menuItemContainer.isVisible()) continue;
double alt = -1;
Node n = menuItemContainer.left;
if (n != null) {
if (n.getContentBias() == Orientation.VERTICAL) { // width depends on height
alt = snapSize(n.prefHeight(-1));
} else alt = -1;
maxLeftWidth = Math.max(maxLeftWidth, snapSize(n.prefWidth(alt)));
maxRowHeight = Math.max(maxRowHeight, n.prefHeight(-1));
}
n = menuItemContainer.graphic;
if (n != null) {
if (n.getContentBias() == Orientation.VERTICAL) { // width depends on height
alt = snapSize(n.prefHeight(-1));
} else alt = -1;
maxGraphicWidth = Math.max(maxGraphicWidth, snapSize(n.prefWidth(alt)));
maxRowHeight = Math.max(maxRowHeight, n.prefHeight(-1));
}
n = menuItemContainer.label;
if (n != null) {
if (n.getContentBias() == Orientation.VERTICAL) {
alt = snapSize(n.prefHeight(-1));
} else alt = -1;
maxLabelWidth = Math.max(maxLabelWidth, snapSize(n.prefWidth(alt)));
maxRowHeight = Math.max(maxRowHeight, n.prefHeight(-1));
}
n = menuItemContainer.right;
if (n != null) {
if (n.getContentBias() == Orientation.VERTICAL) { // width depends on height
alt = snapSize(n.prefHeight(-1));
} else alt = -1;
maxRightWidth = Math.max(maxRightWidth, snapSize(n.prefWidth(alt)));
maxRowHeight = Math.max(maxRowHeight, n.prefHeight(-1));
}
}
}
}
private void updateVisualItems() {
// clean up itemsContainer
ObservableList itemsContainerChilder = itemsContainer.getChildren();
for (int i = 0, max = itemsContainerChilder.size(); i < max; i++) {
Node n = itemsContainerChilder.get(i);
if (n instanceof MenuItemContainer) {
MenuItemContainer container = (MenuItemContainer) n;
container.visibleProperty().unbind();
container.dispose();
}
}
itemsContainerChilder.clear();
for (int row = 0; row < getItems().size(); row++) {
final MenuItem item = getItems().get(row);
if (item instanceof CustomMenuItem && ((CustomMenuItem) item).getContent() == null) {
continue;
}
if (item instanceof SeparatorMenuItem) {
// we don't want the hover highlight for separators, so for
// now this is the simplest approach - just remove the
// background entirely. This may cause issues if people
// intend to style the background differently.
Node node = ((CustomMenuItem) item).getContent();
itemsContainerChilder.add(node);
// Add the (separator) menu item to properties map of this node.
// Special casing this for separator :
// This allows associating this container with SeparatorMenuItem.
node.getProperties().put(MenuItem.class, item);
} else {
MenuItemContainer menuItemContainer = new MenuItemContainer(item);
menuItemContainer.visibleProperty().bind(item.visibleProperty());
itemsContainerChilder.add(menuItemContainer);
}
}
// Add the Menu to properties map of this skin. Used by QA for testing
// This enables associating a parent menu for this skin showing menu items.
if (getItems().size() > 0) {
final MenuItem item = getItems().get(0);
getProperties().put(Menu.class, item.getParentMenu());
}
}
@Override protected void layoutChildren() {
if (itemsContainer.getChildren().size() == 0) return;
final double x = snappedLeftInset();
final double y = snappedTopInset();
final double w = getWidth() - x - snappedRightInset();
final double h = getHeight() - y - snappedBottomInset();
final double contentHeight = snapSize(getContentHeight()); // itemsContainer.prefHeight(-1);
itemsContainer.resize(w,contentHeight);
itemsContainer.relocate(x, y);
if (isFirstShow && ty == 0) {
upArrow.setVisible(false);
isFirstShow = false;
} else {
upArrow.setVisible(ty < y && ty < 0);
}
downArrow.setVisible(ty + contentHeight > (y + h));
clipRect.setX(0);
clipRect.setY(0);
clipRect.setWidth(w);
clipRect.setHeight(h);
if (upArrow.isVisible()) {
final double prefHeight = snapSize(upArrow.prefHeight(-1));
clipRect.setHeight(snapSize(clipRect.getHeight() - prefHeight));
clipRect.setY(snapSize(clipRect.getY()) + prefHeight);
upArrow.resize(snapSize(upArrow.prefWidth(-1)), prefHeight);
positionInArea(upArrow, x, y, w, prefHeight, /*baseline ignored*/0,
HPos.CENTER, VPos.CENTER);
}
if (downArrow.isVisible()) {
final double prefHeight = snapSize(downArrow.prefHeight(-1));
clipRect.setHeight(snapSize(clipRect.getHeight()) - prefHeight);
downArrow.resize(snapSize(downArrow.prefWidth(-1)), prefHeight);
positionInArea(downArrow, x, (y + h - prefHeight), w, prefHeight, /*baseline ignored*/0,
HPos.CENTER, VPos.CENTER);
}
}
@Override protected double computePrefWidth(double height) {
computeVisualMetrics();
double prefWidth = 0;
if (itemsContainer.getChildren().size() == 0) return 0;
for (Node n : itemsContainer.getChildren()) {
if (! n.isVisible()) continue;
prefWidth = Math.max(prefWidth, snapSize(n.prefWidth(-1)));
}
return snappedLeftInset() + snapSize(prefWidth) + snappedRightInset();
}
@Override protected double computePrefHeight(double width) {
if (itemsContainer.getChildren().size() == 0) return 0;
final double screenHeight = getScreenHeight();
final double contentHeight = getContentHeight(); // itemsContainer.prefHeight(width);
double totalHeight = snappedTopInset() + snapSize(contentHeight) + snappedBottomInset();
// the pref height of this menu is the smaller value of the
// actual pref height and the height of the screens _visual_ bounds.
double prefHeight = (screenHeight <= 0) ? (totalHeight) : (Math.min(totalHeight, screenHeight));
return prefHeight;
}
@Override protected double computeMinHeight(double width) {
return 0.0;
}
@Override protected double computeMaxHeight(double height) {
return getScreenHeight();
}
private double getScreenHeight() {
if (contextMenu == null || contextMenu.getOwnerWindow() == null ||
contextMenu.getOwnerWindow().getScene() == null) {
return -1;
}
return snapSize(com.sun.javafx.Utils.getScreen(
contextMenu.getOwnerWindow().getScene().getRoot()).getVisualBounds().getHeight());
}
private double getContentHeight() {
double h = 0.0d;
for (Node i : itemsContainer.getChildren()) {
if (i.isVisible()) {
h += snapSize(i.prefHeight(-1));
}
}
return h;
}
// // FIXME: This handles shifting ty when doing keyboard navigation.
// // By no means is this the best way to do this, but it works for now.
// private Node focusedItem;
// public Node getFocusedItem() { return focusedItem; }
//
// private void setFocusedItem(Node node) {
// focusedItem = node;
// if (focusedItem != null) {
// // this is for moving down the menu
// if (focusedItem.getBoundsInParent().getMaxY() >= clipRect.getBoundsInParent().getMaxY()) {
// watchMouseHover = false;
// ty = ty - focusedItem.getBoundsInParent().getMaxY() + clipRect.getBoundsInParent().getMaxY();
// } else // this is for moving up the menu
// if (focusedItem.getBoundsInParent().getMinY() <= clipRect.getBoundsInParent().getMinY()) {
// watchMouseHover = false;
// ty = ty - focusedItem.getBoundsInParent().getMinY() + clipRect.getBoundsInParent().getMinY();
// }
// }
// }
protected ObservableList