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 }