1 /* 2 * Copyright (c) 2009, 2012, 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 java.util.Collections; 28 import java.util.LinkedList; 29 import java.util.List; 30 import javafx.geometry.Bounds; 31 import javafx.geometry.Orientation; 32 import javafx.scene.Node; 33 import javafx.scene.Scene; 34 import javafx.scene.control.Control; 35 import javafx.scene.control.ListCell; 36 import javafx.scene.control.ListView; 37 import javafx.util.Callback; 38 import org.jemmy.Point; 39 import org.jemmy.Rectangle; 40 import org.jemmy.action.GetAction; 41 import org.jemmy.control.*; 42 import org.jemmy.dock.DockInfo; 43 import org.jemmy.fx.Utils; 44 import org.jemmy.fx.WindowElement; 45 import static org.jemmy.fx.control.TableUtils.getClickPoint; 46 import org.jemmy.input.AbstractScroll; 47 import org.jemmy.interfaces.EditableCellOwner.CellEditor; 48 import org.jemmy.interfaces.EditableCellOwner.EditableCell; 49 import org.jemmy.interfaces.*; 50 import org.jemmy.lookup.LookupCriteria; 51 52 /** 53 * A list could be used as a parent for objects, which are stored in it. 54 * 55 * @param DATA 56 * @author KAM, shura 57 * @see ItemWrap 58 * @see ListItemDock 59 */ 60 @ControlType(Object.class) 61 @ControlInterfaces(value = {WindowElement.class, EditableCell.class, Showable.class}, 62 encapsulates = {ListView.class}) 63 @DockInfo(name = "org.jemmy.fx.control.ListItemDock", multipleCriteria = false, generateSubtypeLookups = true) 64 public class ListItemWrap<DATA extends Object> extends ItemWrap<DATA> implements Showable { 65 66 private final ListViewWrap<? extends ListView> listViewWrap; 67 private final WindowElement<ListView> wElement; 68 69 /** 70 * 71 * @param env 72 * @param data 73 * @param listViewWrap 74 */ 75 public ListItemWrap(DATA data, int index, ListViewWrap<? extends ListView> listViewWrap, CellEditor<? super DATA> editor) { 76 this(null, data, index, listViewWrap, editor); 77 } 78 79 public ListItemWrap(Class<DATA> dataClass, DATA data, int index, ListViewWrap<? extends ListView> listViewWrap, CellEditor<? super DATA> editor) { 80 super(dataClass, index, data, listViewWrap, editor); 81 this.listViewWrap = listViewWrap; 82 wElement = new ViewElement<>(ListView.class, listViewWrap.getControl()); 83 } 84 85 /** 86 * Index of this item within the list. 87 * 88 * @return 89 */ 90 @Property(ITEM_PROP_NAME) 91 public int getIndex() { 92 return (Integer) super.getItem(); 93 } 94 95 @Override 96 public Wrap<? extends ListCell> cellWrap() { 97 return listViewWrap.as(Parent.class, Node.class).lookup(ListCell.class, 98 new ListItemByObjectLookup<>(getControl())).wrap(0); 99 } 100 101 @Override 102 public Point getClickPoint() { 103 return TableUtils.getClickPoint(listViewWrap, this); 104 } 105 106 @Override 107 public Rectangle getScreenBounds() { 108 return cellWrap().getScreenBounds(); 109 } 110 111 @Override 112 public Show shower() { 113 return this; 114 } 115 116 @Override 117 public void show() { 118 final List<DATA> items = listViewWrap.getItems(); 119 120 AbstractScroll scroll1 = Utils.getContainerScroll((Parent <Node>)listViewWrap.as(Parent.class, Node.class), listViewWrap.getControl().getOrientation() == Orientation.VERTICAL); 121 if (scroll1 != null) { 122 TableUtils.scrollToInSingleDimension((Wrap<? extends Control>) viewWrap, ListCell.class, p -> items.indexOf(p.getItem()), listViewWrap.getItems().indexOf(getControl()), 123 scroll1.caret(), listViewWrap.vertical()); 124 } 125 AbstractScroll scroll2 = Utils.getContainerScroll((Parent <Node>)listViewWrap.as(Parent.class, Node.class), listViewWrap.getControl().getOrientation() != Orientation.VERTICAL); 126 Utils.makeCenterVisible(getClippedContainerWrap(), this, scroll2); 127 128 } 129 /** 130 * @return wrap of parent container that contains TableCells 131 */ 132 private Wrap<? extends javafx.scene.Parent> clippedContainer; 133 134 private Wrap<? extends javafx.scene.Parent> getClippedContainerWrap() { 135 if (clippedContainer == null) { 136 clippedContainer = ((Parent<Node>) listViewWrap.as(Parent.class, Node.class)).lookup(javafx.scene.Parent.class, control -> control.getClass().getName().endsWith("VirtualFlow$ClippedContainer")).wrap(); 137 } 138 return clippedContainer; 139 } 140 141 /** 142 * Identifies which elements are shown in the list currently. 143 * 144 * @return list of indices of all elements that are fully visible in the 145 * list. 146 */ 147 List<Long> shown() { 148 final ListView lv = (ListView) listViewWrap.getControl(); 149 final Bounds viewArea = lv.getBoundsInLocal(); 150 151 List<Long> res = new GetAction<List<Long>>() { 152 153 @Override 154 @SuppressWarnings("unchecked") 155 public void run(Object... parameters) { 156 final List<Long> res = new LinkedList<>(); 157 158 listViewWrap.as(Parent.class, Node.class).lookup( 159 ListCell.class, new LookupCriteria<ListCell>() { 160 161 @Override 162 public boolean check(ListCell control) { 163 if (control.isVisible() && control.getOpacity() == 1.0) { 164 Bounds controlBounds = lv.sceneToLocal( 165 control.localToScene(control.getBoundsInLocal())); 166 if (viewArea.contains(controlBounds)) { 167 Long index = new Long(control.getIndex()); 168 res.add(index); 169 return false; 170 } 171 } 172 return false; 173 } 174 }).size(); 175 176 setResult(res); 177 } 178 }.dispatch(getEnvironment()); 179 180 Collections.sort(res); 181 return res; 182 } 183 184 @Override 185 public <INTERFACE extends ControlInterface> boolean is(Class<INTERFACE> interfaceClass) { 186 if (WindowElement.class.equals(interfaceClass)) { 187 return true; 188 } 189 return super.is(interfaceClass); 190 } 191 192 @Override 193 @SuppressWarnings("unchecked") 194 public <TYPE, INTERFACE extends TypeControlInterface<TYPE>> boolean is(Class<INTERFACE> interfaceClass, Class<TYPE> type) { 195 if (WindowElement.class.equals(interfaceClass) && Scene.class.equals(type)) { 196 return true; 197 } 198 if (Parent.class.equals(interfaceClass) && Node.class.equals(type)) { 199 return cellWrap().is(interfaceClass, type); 200 } 201 return super.is(interfaceClass, type); 202 } 203 204 /** 205 * To get the list view where the item resides. 206 * 207 * @return 208 */ 209 @As 210 public WindowElement<ListView> asWindowElement() { 211 return wElement; 212 } 213 214 /** 215 * @deprecated @param <ITEM> 216 */ 217 public static class ListItemByObjectLookup<ITEM> implements LookupCriteria<ListCell> { 218 219 private final ITEM item; 220 221 public ListItemByObjectLookup(ITEM item) { 222 this.item = item; 223 } 224 225 @Override 226 public boolean check(ListCell control) { 227 if (control.isVisible() && control.getOpacity() == 1.0) { 228 if ((control.getItem() != null) && control.getItem().equals(item)) { 229 return true; 230 } 231 } 232 return false; 233 } 234 235 @Override 236 public String toString() { 237 return "Looking for a visible listCell with the value '" + item + "'"; 238 } 239 } 240 }