1 /*
   2  * Copyright (c) 2007, 2017 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.jemmy.input;
  24 
  25 import java.util.ArrayList;
  26 import org.jemmy.JemmyException;
  27 import org.jemmy.Point;
  28 import org.jemmy.action.Action;
  29 import org.jemmy.control.Wrap;
  30 import org.jemmy.env.Environment;
  31 import org.jemmy.env.Timeout;
  32 import org.jemmy.interfaces.Caret;
  33 import org.jemmy.interfaces.CaretOwner;
  34 import org.jemmy.interfaces.Focusable;
  35 import org.jemmy.interfaces.Keyboard.KeyboardButton;
  36 import org.jemmy.interfaces.Keyboard.KeyboardModifier;
  37 import org.jemmy.interfaces.Mouse.MouseButtons;
  38 
  39 /**
  40  *
  41  * @author shura
  42  */
  43 public class CaretImpl implements Caret {
  44 
  45     /**
  46      * Time to sleep between scrolling actions
  47      */
  48     public static final Timeout SCROLL_TIMEOUT =
  49             new Timeout("ScrollerImpl.scroll", 1);
  50 
  51     static {
  52         Environment.getEnvironment().initTimeout(SCROLL_TIMEOUT);
  53     }
  54     private Wrap<?> wrap;
  55     private CaretOwner caretOwner;
  56     private ArrayList<ScrollAction> actions;
  57 
  58     /**
  59      *
  60      * @param wrap
  61      * @param caretOwner only position() is used
  62      */
  63     public CaretImpl(Wrap<?> wrap, CaretOwner caretOwner) {
  64         this.wrap = wrap;
  65         this.caretOwner = caretOwner;
  66         actions = new ArrayList<ScrollAction>();
  67     }
  68 
  69     /**
  70      *
  71      * @return
  72      */
  73     public Wrap<?> getWrap() {
  74         return wrap;
  75     }
  76 
  77     /**
  78      *
  79      * @param action
  80      */
  81     protected void addScrollAction(ScrollAction action) {
  82         actions.add(0, action);
  83     }
  84 
  85     public void to(final double value) {
  86         to(new DirectionToPosition(caretOwner, value));
  87     }
  88 
  89     public void to(final Caret.Direction direction) {
  90         wrap.getEnvironment().getExecutor().execute(wrap.getEnvironment(), false, new Action() {
  91 
  92             @Override
  93             public void run(Object... parameters) {
  94                 if (direction.to() == 0) {
  95                     return;
  96                 }
  97                 if (wrap.is(Focusable.class)) {
  98                     wrap.as(Focusable.class).focuser().focus();
  99                 }
 100                 int orig = direction.to();
 101                 if (orig == 0) {
 102                     return;
 103                 }
 104                 double prevPos = caretOwner.position();
 105                 double prevDist = Double.MAX_VALUE;
 106                 for (int i = 0; i < actions.size(); i++) {
 107                     while (!isInterrupted() && (direction.to() * orig) >= 0) {
 108                         actions.get(i).scrollTo(orig);
 109                         wrap.getEnvironment().getTimeout(SCROLL_TIMEOUT).sleep();
 110                         //if didn't move - use the smaller adjustment
 111                         //like, puching up when in the first line
 112                         if(caretOwner.position() == prevPos) {
 113                             //if did not move and there are more - move to next
 114                             if(i < actions.size() - 1) {
 115                                 break;
 116                             } else {
 117                                 //try more and finally fail by timeout
 118                                 //throw new JemmyException("Unable to scoll.", wrap);
 119                             }
 120                         }
 121                         prevPos = caretOwner.position();
 122                         if (direction.to() == 0) {
 123                             return;
 124                         }
 125                     }
 126                     orig = direction.to();
 127                 }
 128             }
 129 
 130             @Override
 131             public String toString() {
 132                 return "Scrolling to " + direction.toString() + " condition";
 133             }
 134         });
 135     }
 136 
 137     /**
 138      * @deprecated Use ApproximateCaretOwner.ToPosition or PreciseCaretOwner.ToPosition
 139      */
 140     public static class DirectionToPosition implements Direction {
 141 
 142         private double value;
 143         private CaretOwner caret;
 144         private double precision;
 145 
 146         /**
 147          *
 148          * @param caret
 149          * @param value
 150          */
 151         public DirectionToPosition(CaretOwner caret, double value, double precision) {
 152             this.value = value;
 153             this.caret = caret;
 154             this.precision = precision;
 155         }
 156 
 157         public DirectionToPosition(CaretOwner caret, double value) {
 158             this(caret, value, 0);
 159         }
 160 
 161         public int to() {
 162             double diff = position() - caret.position();
 163             return (diff == 0) ? 0 : ((diff > 0) ? 1 : -1);
 164         }
 165 
 166         /**
 167          *
 168          * @return
 169          */
 170         @Override
 171         public String toString() {
 172             return "value == " + position();
 173         }
 174 
 175         /**
 176          *
 177          * @return
 178          */
 179         protected double position() {
 180             return value;
 181         }
 182     }
 183 
 184     /**
 185      *
 186      */
 187     protected static interface ScrollAction {
 188 
 189         /**
 190          *
 191          * @param direction
 192          */
 193         public void scrollTo(int direction);
 194     }
 195 
 196     /**
 197      *
 198      */
 199     protected class MouseScrollAction implements ScrollAction {
 200 
 201         Point up, down;
 202         KeyboardModifier[] upMods, downMods;
 203 
 204         /**
 205          *
 206          * @param down
 207          * @param downMods
 208          * @param up
 209          * @param upMods
 210          */
 211         public MouseScrollAction(Point down, KeyboardModifier[] downMods, Point up, KeyboardModifier[] upMods) {
 212             this.up = up;
 213             this.down = down;
 214             this.upMods = upMods;
 215             this.downMods = downMods;
 216         }
 217 
 218         /**
 219          *
 220          * @param down
 221          * @param up
 222          */
 223         public MouseScrollAction(Point down, Point up) {
 224             this(up, new KeyboardModifier[0], up, new KeyboardModifier[0]);
 225         }
 226 
 227         /**
 228          *
 229          * @param direction
 230          */
 231         public void scrollTo(int direction) {
 232             wrap.mouse().click(1, (direction > 0) ? up : down, MouseButtons.BUTTON1,
 233                     (direction > 0) ? upMods : downMods);
 234         }
 235     }
 236 
 237     /**
 238      *
 239      */
 240     protected class KeyboardScrollAction implements ScrollAction {
 241 
 242         KeyboardButton down, up;
 243         KeyboardModifier[] downMods, upMods;
 244 
 245         /**
 246          *
 247          * @param down
 248          * @param downMods
 249          * @param up
 250          * @param upMods
 251          */
 252         public KeyboardScrollAction(KeyboardButton down, KeyboardModifier[] downMods, KeyboardButton up, KeyboardModifier[] upMods) {
 253             this.down = down;
 254             this.up = up;
 255             this.downMods = downMods;
 256             this.upMods = upMods;
 257         }
 258 
 259         /**
 260          *
 261          * @param down
 262          * @param up
 263          */
 264         public KeyboardScrollAction(KeyboardButton down, KeyboardButton up) {
 265             this(down, new KeyboardModifier[0], up, new KeyboardModifier[0]);
 266         }
 267 
 268         /**
 269          *
 270          * @param direction
 271          */
 272         public void scrollTo(int direction) {
 273             wrap.keyboard().pushKey((direction > 0) ? up : down,
 274                     (direction > 0) ? upMods : downMods);
 275         }
 276     }
 277 }