1 /* 2 * Copyright (c) 2009-2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package org.jemmy.fx.control; 26 27 import com.sun.javafx.scene.control.skin.VirtualFlow; 28 import javafx.scene.Node; 29 import javafx.scene.control.ScrollBar; 30 import javafx.scene.control.TreeCell; 31 import javafx.scene.control.TreeItem; 32 import javafx.scene.control.TreeView; 33 import org.jemmy.action.FutureAction; 34 import org.jemmy.control.As; 35 import org.jemmy.control.ControlInterfaces; 36 import org.jemmy.control.ControlType; 37 import org.jemmy.control.Property; 38 import org.jemmy.env.Environment; 39 import org.jemmy.fx.control.Scrollable2DImpl.ScrollsLookupCriteria; 40 import org.jemmy.interfaces.*; 41 import org.jemmy.lookup.ByStringLookup; 42 import org.jemmy.resources.StringComparePolicy; 43 import org.jemmy.timing.DescriptiveLookupCriteria; 44 45 import java.util.List; 46 47 /** 48 * Tree support in JemmyFX is provided through a few different control 49 * interfaces. Namely, these are 50 * <code>Tree</code>, 51 * <code>Parent</code> and 52 * <code>Selectable</code>. A tree could be considered a parent/selectable for 53 * two types of objects: 54 * <code>javafx.scene.control.TreeItem</code> in which case TreeItems are 55 * themselves the elements of the hierarchy/list or the underlying data held 56 * within the tree items. 57 * 58 * @param <CONTROL> 59 * @author shura 60 * @see #asTreeItemParent() 61 * @see #asTreeItemSelectable() 62 * @see #asSelectable(java.lang.Class) 63 * @see #asItemParent(java.lang.Class) 64 * @see TreeViewDock 65 */ 66 @ControlType({TreeView.class}) 67 @ControlInterfaces(value = {Selectable.class, EditableCellOwner.class, Tree.class, Selectable.class, Scroll.class, Scrollable2D.class}, 68 encapsulates = {TreeItem.class, Object.class, Object.class, Object.class}, 69 name = {"asTreeItemSelectable", "asItemParent"}) 70 public class TreeViewWrap<CONTROL extends TreeView> extends ControlWrap<CONTROL> implements Scroll, Selectable<TreeItem>, Focusable { 71 72 public static final String SELECTED_INDEX_PROP_NAME = "tree.selected.index"; 73 public static final String SELECTED_ITEM_PROP_NAME = "tree.selected.item"; 74 public static final String SHOW_ROOT_PROP_NAME = "show.root"; 75 public static final String ROOT_PROP_NAME = "root.item"; 76 77 private TableTreeScroll scroll; 78 private Scrollable2D scrollable2D; 79 private Selectable selectable; 80 private Selectable<TreeItem> itemSelectable; 81 private TreeItemParent parent; 82 private TreeNodeParent itemParent; 83 private Tree tree; 84 85 /** 86 * @param env 87 * @param scene 88 * @param nd 89 */ 90 @SuppressWarnings("unchecked") 91 public TreeViewWrap(Environment env, CONTROL nd) { 92 super(env, nd); 93 } 94 95 /** 96 * This method finds TreeCell for the selected item. Should be invoked only 97 * using FX.deferAction() That can be needed for cases like obtaining 98 * screenBounds for corresponding TreeCell. 99 * 100 * @return TreeCell, null if it is not visible 101 */ 102 @SuppressWarnings("unchecked") 103 TreeCell getTreeCell(final TreeItem item) { 104 return (TreeCell) as(Parent.class, Node.class).lookup(TreeCell.class, 105 new DescriptiveLookupCriteria<TreeCell>(cell -> { 106 if (cell.isVisible() && cell.getOpacity() == 1.0) { 107 if (cell.getTreeItem() == item) { 108 return true; 109 } 110 } 111 return false; 112 }, () -> "Looking for a visible treeCell with the value '" + item + "'")).get(0); 113 } 114 115 int getRow(final TreeItem item) { 116 return new FutureAction<>(getEnvironment(), () -> getControl().getRow(item)).get(); 117 } 118 119 /** 120 * Allows to work with tree as with a list on selectable data objects - the 121 * objects which are accessible through 122 * <code>javafx.scene.control.TreeItem.getValue()</code>. Notice that only 123 * expanded tree items get into the list. A set of items in the hierarchy is 124 * also limited by the type parameter - objects of other types do not make 125 * it to the list. 126 * 127 * @param <T> 128 * @param type 129 * @return 130 * @see #asTreeItemSelectable() 131 */ 132 @As(Object.class) 133 public <T> Selectable<T> asSelectable(Class<T> type) { 134 if (selectable == null || !selectable.getType().equals(type)) { 135 selectable = new TreeViewSelectable<>(type); 136 } 137 return selectable; 138 } 139 140 /** 141 * Allows to work with tree as with a list on selectable items. Notice that 142 * only expanded tree items get into the list. 143 * 144 * @return 145 * @see #asSelectable(java.lang.Class) 146 */ 147 @As(TreeItem.class) 148 public Selectable<TreeItem> asTreeItemSelectable() { 149 if (itemSelectable == null) { 150 itemSelectable = new TreeViewSelectable(); 151 } 152 return itemSelectable; 153 } 154 155 /** 156 * Allows to perform lookup for 157 * <code>javafx.scene.control.TreeItem</code>s. All objects within the tree 158 * are elements of this hierarchy: whether visible or not. Notice though 159 * that the implementation does not expand nodes, so if a tree is loaded 160 * dynamically on node expand, those dynamically added nodes will not be a 161 * part of hierarchy. 162 * 163 * @param <T> 164 * @param type 165 * @return 166 * @see #asTreeItemParent() 167 */ 168 @As(TreeItem.class) 169 public <T extends TreeItem> EditableCellOwner<T> asTreeItemParent(Class<T> type) { 170 if (itemParent == null) { 171 itemParent = new TreeNodeParent<>(this, type); 172 } 173 return itemParent; 174 } 175 176 /** 177 * Allows to perform lookup for data objects - the objects which are 178 * accessible through 179 * <code>javafx.scene.control.TreeItem.getValue()</code>. All objects within 180 * the tree are elements of this hierarchy: whether visible or not. Notice 181 * though that the implementation does not expand nodes, so if a tree is 182 * loaded dynamically on node expand, those dynamically added nodes will not 183 * be a part of hierarchy. 184 * 185 * @param <T> type of data supported by the tree. If should be consistent 186 * with the data present in the tree, because the tree data may get casted 187 * to this type parameter during lookup operations. That, this must be a 188 * super type for all types present in the tree. 189 * @param type 190 * @return 191 * @see #asItemParent() 192 */ 193 @As(Object.class) 194 public <T> EditableCellOwner<T> asItemParent(Class<T> type) { 195 if (parent == null || !parent.getType().equals(type)) { 196 parent = new TreeItemParent<>(this, type); 197 } 198 return parent; 199 } 200 201 /** 202 * Allows selections of paths. Notice that in the tree implementation, tree 203 * root is not looked up. That is, fist lookup criterion of the criteria 204 * passed for tree selection will be used for finding a child of the root 205 * node whether or not the root node is shown. 206 * 207 * @param <T> 208 * @param type 209 * @return 210 */ 211 @As(Object.class) 212 public <T> Tree<T> asTree(Class<T> type) { 213 if (tree == null || !tree.getType().equals(type)) { 214 asItemParent(type); 215 tree = new TreeImpl<>(type, this, parent); 216 } 217 return tree; 218 } 219 220 /** 221 * Returns selected row index. 222 * 223 * @return 224 */ 225 @Property(SELECTED_INDEX_PROP_NAME) 226 public int getSelectedIndex() { 227 return new FutureAction<>(getEnvironment(), () -> getControl().getSelectionModel().getSelectedIndex()).get(); 228 } 229 230 @Property(SELECTED_ITEM_PROP_NAME) 231 public TreeItem getSelectedItem() { 232 return new FutureAction<>(getEnvironment(), () -> (TreeItem) getControl().getSelectionModel().getSelectedItem()).get(); 233 } 234 235 @Override 236 public List<TreeItem> getStates() { 237 return asTreeItemSelectable().getStates(); 238 } 239 240 @Override 241 public TreeItem getState() { 242 return asTreeItemSelectable().getState(); 243 } 244 245 @Override 246 public Selector<TreeItem> selector() { 247 return asTreeItemSelectable().selector(); 248 } 249 250 @Override 251 public Class<TreeItem> getType() { 252 return TreeItem.class; 253 } 254 255 @Property(SHOW_ROOT_PROP_NAME) 256 public boolean isShowRoot() { 257 return new FutureAction<>(getEnvironment(), () -> getControl().isShowRoot()).get(); 258 } 259 260 @Property(ROOT_PROP_NAME) 261 public TreeItem getRoot() { 262 return new FutureAction<>(getEnvironment(), () -> getControl().getRoot()).get(); 263 } 264 265 @As 266 public Scrollable2D asScrollable2D() { 267 if (scrollable2D == null) { 268 scrollable2D = new Scrollable2DImpl(this, new ScrollsLookupCriteria() { 269 @Override 270 public boolean checkFor(ScrollBar scrollBar) { 271 return ((scrollBar.getParent() instanceof VirtualFlow) 272 && (scrollBar.getParent().getParent() instanceof TreeView)); 273 } 274 }); 275 } 276 return scrollable2D; 277 } 278 279 @As 280 public Scroll asScroll() { 281 checkScroll(); 282 return scroll.asScroll(); 283 } 284 285 @Property(VALUE_PROP_NAME) 286 public double position() { 287 checkScroll(); 288 return scroll.position(); 289 } 290 291 @Property(MINIMUM_PROP_NAME) 292 public double minimum() { 293 checkScroll(); 294 return scroll.minimum(); 295 } 296 297 @Property(MAXIMUM_PROP_NAME) 298 public double maximum() { 299 checkScroll(); 300 return scroll.maximum(); 301 } 302 303 @Override 304 @Deprecated 305 public double value() { 306 checkScroll(); 307 return position(); 308 } 309 310 @Deprecated 311 public Scroller scroller() { 312 checkScroll(); 313 return scroll.scroller(); 314 } 315 316 public Caret caret() { 317 checkScroll(); 318 return scroll.caret(); 319 } 320 321 public void to(double position) { 322 checkScroll(); 323 scroll.to(position); 324 } 325 326 private void checkScroll() { 327 if (scroll == null) { 328 scroll = new TableTreeScroll(this); 329 } 330 } 331 332 public static class ByTestTreeItem<T extends TreeItem> extends ByStringLookup<T> { 333 334 public ByTestTreeItem(String text, StringComparePolicy policy) { 335 super(text, policy); 336 } 337 338 @Override 339 public String getText(T t) { 340 return t.getValue().toString(); 341 } 342 } 343 344 protected class TreeViewSelectable<T> extends TreeSelectable<T> { 345 346 private TreeNodeParent treeNodeParent; 347 348 TreeViewSelectable(Class<T> type) { 349 super(type); 350 } 351 352 TreeViewSelectable() { 353 super(); 354 } 355 356 @Override 357 protected TreeItem getRoot() { 358 return TreeViewWrap.this.getRoot(); 359 } 360 361 @Override 362 protected boolean isShowRoot() { 363 return TreeViewWrap.this.isShowRoot(); 364 } 365 366 @Override 367 protected TreeItem getSelectedItem() { 368 return TreeViewWrap.this.getSelectedItem(); 369 } 370 371 @Override 372 protected Parent asTreeItemParent() { 373 if (treeNodeParent == null) { 374 treeNodeParent = new TreeNodeParent(TreeViewWrap.this, TreeItem.class); 375 } 376 return treeNodeParent; 377 } 378 379 @Override 380 protected Environment getEnvironment() { 381 return TreeViewWrap.this.getEnvironment(); 382 } 383 } 384 }