/*
* Copyright (c) 2012-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.
*/
package org.jemmy.fx.control;
import javafx.scene.control.skin.VirtualFlow;
import java.util.ArrayList;
import java.util.List;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.IndexedCell;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.TableColumnBase;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTablePosition;
import javafx.scene.control.TreeTableRow;
import javafx.scene.control.TreeTableView;
import org.jemmy.Point;
import org.jemmy.action.GetAction;
import org.jemmy.control.As;
import org.jemmy.control.ControlInterfaces;
import org.jemmy.control.ControlType;
import org.jemmy.control.Property;
import org.jemmy.dock.Shortcut;
import org.jemmy.env.Environment;
import org.jemmy.fx.control.Scrollable2DImpl.ScrollsLookupCriteria;
import org.jemmy.interfaces.Caret;
import org.jemmy.interfaces.EditableCellOwner;
import org.jemmy.interfaces.Focusable;
import org.jemmy.interfaces.Parent;
import org.jemmy.interfaces.Scroll;
import org.jemmy.interfaces.Scrollable2D;
import org.jemmy.interfaces.Scroller;
import org.jemmy.interfaces.Selectable;
import org.jemmy.interfaces.Selector;
import org.jemmy.interfaces.Table;
import org.jemmy.interfaces.Tree;
/**
* TreeTable support in JemmyFX is provided through a few different control
* interfaces. Namely, these are
* Table
,
* Tree
,
* Parent
and
* Selectable
. A tree could be considered a parent/selectable for
* two types of objects:
* javafx.scene.control.TreeItem
in which case TreeItems are
* themselves the elements of the hierarchy/list or the underlying data held
* within the tree items.
*
* @see #asTreeTableItemParent()
* @see #asTreeTableItemSelectable()
* @see #asSelectable(java.lang.Class)
* @see #asItemParent(java.lang.Class)
* @author Alexander Kirov
* @param
*/
@ControlType({TreeTableView.class})
@ControlInterfaces(value = {Selectable.class, EditableCellOwner.class, EditableCellOwner.class, Tree.class, Table.class, Scroll.class, Scrollable2D.class},
encapsulates = {TreeItem.class, Object.class, Object.class, Object.class, Object.class},
name = {"asTreeItemSelectable", "asTableItemParent", "asTreeItemParent"})
public class TreeTableViewWrap extends ControlWrap implements Scroll, Selectable, Focusable {
public static final String SELECTED_INDEX_PROP_NAME = "tree.selected.index";
public static final String SELECTED_ITEM_PROP_NAME = "tree.selected.item";
public static final String SHOW_ROOT_PROP_NAME = "show.root";
public static final String ROOT_PROP_NAME = "root.item";
public static final String SELECTED_ITEMS_PROP_NAME = "selected.items";
public final static String SELECTED_CELLS_PROP_NAME = "selected.cells";
public static final String SELECTED_INDICES_PROP_NAME = "selected.indices";
public static final String ITEMS_COUNT_PROP_NAME = "item.count";
public static final String ITEMS_PROP_NAME = "items";
public static final String DATA_COLUMNS_PROP_NAME = "data.columns";
public static final String COLUMNS_PROP_NAME = "columns";
private Tree tree;
private Table table;
private TreeTableCellParent cellParent;
private TreeTableItemParent treeTableItemParent;
private TreeTableNodeParent nodeParent;
private Selectable selectable;
private Selectable itemSelectable;
private TableTreeScroll scroll;
private Scrollable2D scrollable2D;
public TreeTableViewWrap(Environment env, CONTROL nd) {
super(env, nd);
}
/**
* Jemmy table control interface introduces multiple selections mechanism.
*
* @param
* @param type
* @return
*/
@As(Object.class)
public Table asTable(Class type) {
if (table == null || !table.getType().equals(type)) {
table = asTreeTableCellItemParent(type);
}
return table;
}
/**
* Allows selections of paths. Notice that in the tree implementation, tree
* root is not looked up. That is, fist lookup criterion of the criteria
* passed for tree selection will be used for finding a child of the root
* node whether or not the root node is shown.
*
* @param
* @param type
* @return
*/
@As(Object.class)
public Tree asTree(Class type) {
if (tree == null || !tree.getType().equals(type)) {
asTreeItemParent(TreeItem.class);
tree = new TreeTableImpl(type, this, treeTableItemParent);
}
return tree;
}
/**
* You could find items within treetable and operate with them just like with
* any other UI elements.
*
* @param
* @param type
* @return
* @see TableCellItemWrap
*/
@As(Object.class)
public EditableCellOwner asTableItemParent(Class type) {
return asTreeTableCellItemParent(type);
}
/**
* Allows to perform lookup for data objects - the objects which are
* accessible through
* javafx.scene.control.TreeItem.getValue()
. All objects within
* the treeTable are elements of this hierarchy: whether visible or not. Notice
* though that the implementation does not expand nodes, so if a tree is
* loaded dynamically on node expand, those dynamically added nodes will not
* be a part of hierarchy.
*
* @param type of data supported by the tree. If should be consistent
* with the data present in the tree, because the tree data may get casted
* to this type parameter during lookup operations. That, this must be a
* super type for all types present in the tree.
* @param type
* @return
* @see #asTableItemParent()
*/
@As(Object.class)
public EditableCellOwner asTreeItemParent(Class type) {
if (treeTableItemParent == null || !treeTableItemParent.getType().equals(type)) {
treeTableItemParent = new TreeTableItemParent(this, getRoot(), type);
}
return treeTableItemParent;
}
/**
* Allows to perform lookup for
* javafx.scene.control.TreeItem
s. All objects within the tree
* are elements of this hierarchy: whether visible or not. Notice though
* that the implementation does not expand nodes, so if a tree is loaded
* dynamically on node expand, those dynamically added nodes will not be a
* part of hierarchy.
*
* @param
* @param type
* @return
* @see #asTreeItemParent()
*/
@As(TreeItem.class)
public EditableCellOwner asItemParent(Class type) {
if (nodeParent == null) {
nodeParent = new TreeTableNodeParent(this, type);
}
return nodeParent;
}
/**
* Allows to work with treeTable as with a list on selectable data objects - the
* objects which are accessible through
* javafx.scene.control.TreeItem.getValue()
. Notice that only
* expanded tree items get into the list. A set of items in the hierarchy is
* also limited by the type parameter - objects of other types do not make
* it to the list.
*
* @param
* @param type
* @return
* @see #asTreeItemSelectable()
*/
@As(Object.class)
public Selectable asSelectable(Class type) {
if (selectable == null || !selectable.getType().equals(type)) {
selectable = new TreeTableViewSelectable(type);
}
return selectable;
}
/**
* Allows to work with treeTable as with a list on selectable items. Notice
* that only expanded tree items get into the list.
*
* @return
* @see #asSelectable(java.lang.Class)
*/
@As(TreeItem.class)
public Selectable asTreeItemSelectable() {
if (itemSelectable == null) {
itemSelectable = new TreeTableViewSelectable();
}
return itemSelectable;
}
/**
* Returns implementation of Scrollable2D interface.
*/
@As
public Scrollable2D asScrollable2D() {
if (scrollable2D == null) {
scrollable2D = new Scrollable2DImpl(this, new ScrollsLookupCriteria() {
@Override
public boolean checkFor(ScrollBar scrollBar) {
return ((scrollBar.getParent() instanceof VirtualFlow)
&& (scrollBar.getParent().getParent() instanceof TreeTableView));
}
});
}
return scrollable2D;
}
/**
* Returns implementation of vertical scroll.
*/
@As
public Scroll asScroll() {
checkScroll();
return scroll.asScroll();
}
@Property(ROOT_PROP_NAME)
public TreeItem getRoot() {
return new GetAction() {
@Override
public void run(Object... os) throws Exception {
setResult(getControl().getRoot());
}
}.dispatch(getEnvironment());
}
/**
* Returns selected row index.
*
* @return
*/
@Property(SELECTED_INDEX_PROP_NAME)
public int getSelectedIndex() {
return new GetAction() {
@Override
public void run(Object... parameters) {
setResult(Integer.valueOf(getControl().getSelectionModel().getSelectedIndex()));
}
}.dispatch(getEnvironment());
}
@Property(SELECTED_ITEM_PROP_NAME)
public TreeItem getSelectedItem() {
return new GetAction() {
@Override
public void run(Object... parameters) {
setResult((TreeItem) getControl().getSelectionModel().getSelectedItem());
}
}.dispatch(getEnvironment());
}
@Property(SHOW_ROOT_PROP_NAME)
public boolean isShowRoot() {
return new GetAction() {
@Override
public void run(Object... parameters) throws Exception {
setResult(getControl().isShowRoot());
}
}.dispatch(getEnvironment());
}
@Override
public TreeItem getState() {
return asTreeItemSelectable().getState();
}
@Override
public List getStates() {
return asTreeItemSelectable().getStates();
}
@Override
public Class getType() {
return TreeItem.class;
}
@Override
public Selector selector() {
return asTreeItemSelectable().selector();
}
@Property(VALUE_PROP_NAME)
@Override
public double position() {
checkScroll();
return scroll.position();
}
@Property(MINIMUM_PROP_NAME)
@Override
public double minimum() {
checkScroll();
return scroll.minimum();
}
@Property(MAXIMUM_PROP_NAME)
@Override
public double maximum() {
checkScroll();
return scroll.maximum();
}
@Override
@Deprecated
public double value() {
checkScroll();
return position();
}
@Deprecated
@Override
public Scroller scroller() {
checkScroll();
return scroll.scroller();
}
@Override
public Caret caret() {
checkScroll();
return scroll.caret();
}
@Override
public void to(double position) {
checkScroll();
scroll.to(position);
}
private void checkScroll() {
if (scroll == null) {
scroll = new TableTreeScroll(this);
}
}
@Property(COLUMNS_PROP_NAME)
public List getColumns() {
return new GetAction>() {
@Override
public void run(Object... parameters) throws Exception {
setResult(getControl().getColumns());
}
}.dispatch(getEnvironment());
}
/**
* @return List of columns, which are on the last level (leaf columns) of
* tree table header. In the case of nested columns, some columns are
* parents, and some are children. This method returns the list of columns,
* which have no children. Those columns correspond to the realy shown
* columns of data in TreeTableView.
*/
@Property(DATA_COLUMNS_PROP_NAME)
public List getDataColumns() {
return new GetAction>() {
@Override
public void run(Object... parameters) throws Exception {
ArrayList fillList = new ArrayList();
TableViewWrap.getLastLevelColumns((java.util.List extends TableColumnBase>) getControl().getColumns(), fillList);
setResult((java.util.List) fillList);
}
}.dispatch(getEnvironment());
}
/**
* Gives a size of a list of objects (rows) displayed in the tree table.
*
* @return
*/
@Property(ITEMS_PROP_NAME)
public ObservableList getItems() {
return new GetAction() {
@Override
public void run(Object... parameters) throws Exception {
ObservableList listForItems = FXCollections.observableArrayList();
if (getControl().isShowRoot()) {
if (getControl().getRoot() != null) {
listForItems.add(getControl().getRoot());
}
}
getItemsRec(getControl().getRoot(), listForItems);
setResult(listForItems);
}
private void getItemsRec(TreeItem rootItem, ObservableList listForItems) {
if (rootItem.isExpanded()) {
for (Object item : rootItem.getChildren()) {
listForItems.add(item);
getItemsRec((TreeItem) item, listForItems);
}
}
}
}.dispatch(getEnvironment());
}
public TreeTableItemWrap getRootWrap() {
TreeItem root = getRoot();
if (root == null) {
return null;
} else {
return new TreeTableItemWrap(Object.class, root, this, null);
}
}
/**
* Gives a list of objects (rows) displayed in the tree table.
*
* @return
*/
@Property(ITEMS_COUNT_PROP_NAME)
public int getSize() {
return new GetAction() {
@Override
public void run(Object... parameters) throws Exception {
setResult(getControl().getExpandedItemCount());
}
}.dispatch(getEnvironment());
}
/**
* @return List, containing list of selected indices.
*/
@Property(SELECTED_INDICES_PROP_NAME)
public java.util.List getSelectedIndices() {
return new GetAction>() {
@Override
public void run(Object... parameters) throws Exception {
setResult((java.util.List) getControl().getSelectionModel().getSelectedIndices());
}
}.dispatch(getEnvironment());
}
@Property(SELECTED_ITEMS_PROP_NAME)
public List getSelectedItems() {
return new GetAction() {
@Override
public void run(Object... parameters) throws Exception {
setResult(getControl().getSelectionModel().getSelectedItems());
}
}.dispatch(getEnvironment());
}
@Property(SELECTED_CELLS_PROP_NAME)
public List getSelectedCells() {
return new GetAction>() {
@Override
public void run(Object... parameters) throws Exception {
java.util.List res = new ArrayList();
for (TreeTablePosition tp : (java.util.List) getControl().getSelectionModel().getSelectedCells()) {
res.add(new Point(tp.getColumn(), tp.getRow()));
}
setResult(res);
}
}.dispatch(getEnvironment());
}
@Shortcut
public void scrollTo(int row, int column) {
if (scroll == null) {
scroll = new TableTreeScroll(this);
}
scroll.checkScrolls();
TableUtils.scrollTo(getEnvironment(), this,
scroll.hScroll, scroll.vScroll,
row, column,
new TableUtils.TreeTableViewIndexInfoProvider(this), TreeTableCell.class);
}
Object getRow(final int index) {
return getItems().get(index);
}
TreeTableColumn getColumn(final int index) {
return (TreeTableColumn) getDataColumns().get(index);
}
protected int getRowIndex(IndexedCell tableCell) {
return ((TreeTableCell) tableCell).getTreeTableRow().getIndex();
}
protected int getColumnIndex(IndexedCell tableCell) {
return ((TreeTableCell) tableCell).getTreeTableView().getVisibleLeafIndex(((TreeTableCell) tableCell).getTableColumn());
}
public int getRow(TreeItem item) {
return getItems().indexOf(item);
}
public IndexedCell getTreeCell(TreeItem item) {
return (TreeTableRow) new TreeTableNodeWrap<>(item, new TreeTableItemWrap<>(Object.class, item, this, null), this, null).getNode().getControl();
}
protected class TreeTableViewSelectable extends TreeSelectable {
TreeTableViewSelectable(Class type) {
super(type);
}
TreeTableViewSelectable() {
super();
}
@Override
protected TreeItem getRoot() {
return TreeTableViewWrap.this.getRoot();
}
@Override
protected boolean isShowRoot() {
return TreeTableViewWrap.this.isShowRoot();
}
@Override
protected TreeItem getSelectedItem() {
return TreeTableViewWrap.this.getSelectedItem();
}
@Override
protected Parent asTreeItemParent() {
return TreeTableViewWrap.this.asItemParent(TreeItem.class);
}
@Override
protected Environment getEnvironment() {
return TreeTableViewWrap.this.getEnvironment();
}
}
private TreeTableCellParent asTreeTableCellItemParent(Class type) {
if (cellParent == null || !cellParent.getType().equals(type)) {
cellParent = new TreeTableCellParent(this, type);
}
return cellParent;
}
}