1 /*
   2  * Copyright (c) 2010, 2014, 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 
  26 package com.sun.javafx.scene.control.skin;
  27 
  28 import static org.junit.Assert.assertEquals;
  29 import static org.junit.Assert.assertFalse;
  30 import static org.junit.Assert.assertNotNull;
  31 import static org.junit.Assert.assertNull;
  32 import static org.junit.Assert.assertSame;
  33 import static org.junit.Assert.assertTrue;
  34 
  35 import java.util.Iterator;
  36 import java.util.LinkedList;
  37 
  38 import javafx.beans.InvalidationListener;
  39 import javafx.event.Event;
  40 import javafx.scene.control.IndexedCell;
  41 import javafx.scene.control.SkinStub;
  42 import javafx.scene.input.ScrollEvent;
  43 
  44 import org.junit.Before;
  45 import org.junit.Ignore;
  46 import org.junit.Test;
  47 
  48 import com.sun.javafx.scene.CssFlags;
  49 import com.sun.javafx.scene.control.skin.VirtualFlow.ArrayLinkedList;
  50 import java.util.List;
  51 import javafx.util.Callback;
  52 
  53 /**
  54  * Tests for the VirtualFlow class. VirtualFlow is the guts of the ListView,
  55  * TreeView, and TableView implementations.
  56  */
  57 public class VirtualFlowTest {
  58     // The following 4 vars are used when testing the
  59     private ArrayLinkedList<CellStub> list;
  60     private CellStub a;
  61     private CellStub b;
  62     private CellStub c;
  63 
  64     // The VirtualFlow we are going to test. By default, there are 100 cells
  65     // and each cell is 100 wide and 25 tall, except for the 30th cell, which
  66     // is 200 wide and 100 tall.
  67     private VirtualFlow<IndexedCell> flow;
  68 //    private Scene scene;
  69 
  70     @Before public void setUp() {
  71         list = new ArrayLinkedList<CellStub>();
  72         a = new CellStub(flow, "A");
  73         b = new CellStub(flow, "B");
  74         c = new CellStub(flow, "C");
  75 
  76         flow = new VirtualFlow();
  77 //        flow.setManaged(false);
  78         flow.setVertical(true);
  79         flow.setCreateCell(p -> new CellStub(flow) {
  80             @Override protected double computeMinWidth(double height) { return computePrefWidth(height); }
  81             @Override protected double computeMaxWidth(double height) { return computePrefWidth(height); }
  82             @Override protected double computePrefWidth(double height) {
  83                 return flow.isVertical() ? (c.getIndex() == 29 ? 200 : 100) : (c.getIndex() == 29 ? 100 : 25);
  84             }
  85 
  86             @Override protected double computeMinHeight(double width) { return computePrefHeight(width); }
  87             @Override protected double computeMaxHeight(double width) { return computePrefHeight(width); }
  88             @Override protected double computePrefHeight(double width) {
  89                 return flow.isVertical() ? (c.getIndex() == 29 ? 100 : 25) : (c.getIndex() == 29 ? 200 : 100);
  90             }
  91         });
  92         flow.setCellCount(100);
  93         flow.resize(300, 300);
  94         pulse();
  95     }
  96 
  97     private void pulse() {
  98 //        flow.impl_processCSS(true);
  99         flow.layout();
 100     }
 101 
 102     /**
 103      * Asserts that the items in the control LinkedList and the ones in the
 104      * list are exactly the same.
 105      */
 106     private void assertMatch(List<IndexedCell> control, ArrayLinkedList<IndexedCell> list) {
 107         assertEquals("The control and list did not have the same sizes. " +
 108                      "Expected " + control.size() + " but was " + list.size(),
 109                      control.size(), list.size());
 110         int index = 0;
 111         Iterator<IndexedCell> itr = control.iterator();
 112         while (itr.hasNext()) {
 113             IndexedCell cell = itr.next();
 114             IndexedCell cell2 = list.get(index);
 115             assertSame("The control and list did not have the same item at " +
 116                        "index " + index + ". Expected " + cell + " but was " + cell2,
 117                        cell, cell2);
 118             index++;
 119         }
 120     }
 121 
 122     /**
 123      * Asserts that only the minimal number of cells are used.
 124      */
 125     public <T extends IndexedCell> void assertMinimalNumberOfCellsAreUsed(VirtualFlow<T> flow) {
 126         pulse();
 127         IndexedCell firstCell = flow.cells.getFirst();
 128         IndexedCell lastCell = flow.cells.getLast();
 129         if (flow.isVertical()) {
 130             // First make sure that enough cells were created
 131             assertTrue("There is a gap between the top of the viewport and the first cell",
 132                        firstCell.getLayoutY() <= 0);
 133             assertTrue("There is a gap between the bottom of the last cell and the bottom of the viewport",
 134                        lastCell.getLayoutY() + lastCell.getHeight() >= flow.getViewportLength());
 135 
 136             // Now make sure that no extra cells were created.
 137             if (flow.cells.size() > 3) {
 138                 IndexedCell secondLastCell = flow.cells.get(flow.cells.size() - 2);
 139                 IndexedCell secondCell = flow.cells.get(1);
 140                 assertFalse("There are more cells created before the start of " +
 141                             "the flow than necessary",
 142                             secondCell.getLayoutY() <= 0);
 143                 assertFalse("There are more cells created after the end of the " +
 144                             "flow than necessary",
 145                             secondLastCell.getLayoutY() + secondLastCell.getHeight() >= flow.getViewportLength());
 146             }
 147         } else {
 148             // First make sure that enough cells were created
 149             assertTrue("There is a gap between the left of the viewport and the first cell",
 150                        firstCell.getLayoutX() <= 0);
 151             assertTrue("There is a gap between the right of the last cell and the right of the viewport",
 152                        lastCell.getLayoutX() + lastCell.getWidth() >= flow.getViewportLength());
 153 
 154             // Now make sure that no extra cells were created.
 155             if (flow.cells.size() > 3) {
 156                 IndexedCell secondLastCell = flow.cells.get(flow.cells.size() - 2);
 157                 IndexedCell secondCell = flow.cells.get(1);
 158                 assertFalse("There are more cells created before the start of " +
 159                             "the flow than necessary",
 160                             secondCell.getLayoutX() <= 0);
 161                 assertFalse("There are more cells created after the end of the " +
 162                             "flow than necessary",
 163                             secondLastCell.getLayoutX() + secondLastCell.getWidth() >= flow.getViewportLength());
 164             }
 165         }
 166     }
 167 
 168 
 169     /***************************************************************************
 170      *                          Tests for VirtualFlow                          *
 171      *                                                                         *
 172      *  These tests are broken out into several broad categories:              *
 173      *      - general layout (position of scroll bars, viewport, etc)          *
 174      *      - cell layout                                                      *
 175      *      - cell life cycle (creation, configuration, reuse, etc)            *
 176      *      - position (stable view, adjusts when necessary, etc)              *
 177      *      - pixel scrolling (cells are reused, position updated, etc)        *
 178      *                                                                         *
 179      *  - Test that the preferred width of a vertical flow takes into account  *
 180      *    the preferred width of the cells that are visible                    *
 181      *  - Test the same for horizontal when working with a horizontal flow     *
 182      *  - Test that cells are laid out as expected in a vertical flow          *
 183      *  - Test the same for a horizontal flow                                  *
 184      *  - Test that the width of cells in a vertical flow adjusts based on the *
 185      *    width of the flow's content area                                     *
 186      *  - Test the same for the height of cells in a horizontal flow           *
 187      *  - Test that changing the number of cells (up and down) also adjusts    *
 188      *    the position such that it is "stable", unless that is not possible   *
 189      *  - Test that after changing the cell factory, things are rebuilt        *
 190      *  - Test that after changing the cell config function, things are        *
 191      *    reconfigured.                                                        *
 192      *  - Test that functions which add to the pile and so forth work as       *
 193      *    expected.                                                            *
 194      *  - Test the layout of the scroll bars in various combinations, along    *
 195      *    with the corner region and so forth.                                 *
 196      *                                                                         *
 197      **************************************************************************/
 198 
 199     ////////////////////////////////////////////////////////////////////////////
 200     //
 201     //  General Layout
 202     //
 203     ////////////////////////////////////////////////////////////////////////////
 204 
 205     /**
 206      * In this test there are no cells. The VirtualFlow should be laid out such
 207      * that the scroll bars and corner are not visible, and the clip view fills
 208      * the entire width/height of the VirtualFlow.
 209      */
 210     @Test public void testGeneralLayout_NoCells() {
 211         flow.setCellCount(0);
 212         pulse();
 213         assertFalse("The hbar should have been invisible", flow.getHbar().isVisible());
 214         assertFalse("The vbar should have been invisible", flow.getVbar().isVisible());
 215         assertFalse("The corner should have been invisible", flow.corner.isVisible());
 216         assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0);
 217         assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0);
 218         assertMinimalNumberOfCellsAreUsed(flow);
 219 
 220         flow.setVertical(false);
 221         pulse();
 222         assertFalse("The hbar should have been invisible", flow.getHbar().isVisible());
 223         assertFalse("The vbar should have been invisible", flow.getVbar().isVisible());
 224         assertFalse("The corner should have been invisible", flow.corner.isVisible());
 225         assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0);
 226         assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0);
 227         assertMinimalNumberOfCellsAreUsed(flow);
 228     }
 229 
 230     /**
 231      * When we have a few cells, not enough to fill the viewport, then we need
 232      * to make sure there is no virtual scroll bar. In this test case the cells
 233      * are not wider than the viewport so no horizontal bar either.
 234      */
 235     @Test public void testGeneralLayout_FewCells() {
 236         flow.setCellCount(3);
 237         pulse();
 238         assertFalse("The hbar should have been invisible", flow.getHbar().isVisible());
 239         assertFalse("The vbar should have been invisible", flow.getVbar().isVisible());
 240         assertFalse("The corner should have been invisible", flow.corner.isVisible());
 241         assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0);
 242         assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0);
 243         assertEquals(12, flow.cells.size()); // we stil have 12 cells (300px / 25px), even if only three filled cells exist
 244         assertMinimalNumberOfCellsAreUsed(flow);
 245 
 246         flow.setVertical(false);
 247         pulse();
 248         assertFalse("The hbar should have been invisible", flow.getHbar().isVisible());
 249         assertFalse("The vbar should have been invisible", flow.getVbar().isVisible());
 250         assertFalse("The corner should have been invisible", flow.corner.isVisible());
 251         assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0);
 252         assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0);
 253 //        assertEquals(3, flow.cells.size());
 254         assertMinimalNumberOfCellsAreUsed(flow);
 255     }
 256 
 257     /**
 258      * Tests the case of a few cells (so not requiring a vertical scroll bar),
 259      * but the cells are wider than the viewport so a horizontal scroll bar is
 260      * required.
 261      */
 262     @Test public void testGeneralLayout_FewCellsButWide() {
 263         flow.setCellCount(3);
 264         flow.resize(50, flow.getHeight());
 265         pulse();
 266         assertTrue("The hbar should have been visible", flow.getHbar().isVisible());
 267         assertFalse("The vbar should have been invisible", flow.getVbar().isVisible());
 268         assertFalse("The corner should have been invisible", flow.corner.isVisible());
 269         assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0);
 270         assertEquals(flow.getHeight(), flow.clipView.getHeight() + flow.getHbar().getHeight(), 0.0);
 271         assertEquals(flow.getHbar().getLayoutY(), flow.getHeight() - flow.getHbar().getHeight(), 0.0);
 272         assertMinimalNumberOfCellsAreUsed(flow);
 273 
 274         flow.setVertical(false);
 275         flow.resize(300, 50);
 276         pulse();
 277         assertFalse("The hbar should have been invisible", flow.getHbar().isVisible());
 278         assertTrue("The vbar should have been visible", flow.getVbar().isVisible());
 279         assertFalse("The corner should have been invisible", flow.corner.isVisible());
 280         assertEquals(flow.getWidth(), flow.clipView.getWidth() + flow.getVbar().getWidth(), 0.0);
 281         assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0);
 282         assertEquals(flow.getVbar().getLayoutX(), flow.getWidth() - flow.getVbar().getWidth(), 0.0);
 283         assertMinimalNumberOfCellsAreUsed(flow);
 284     }
 285 
 286     /**
 287      * Tests that having a situation where the hbar in a vertical flow is
 288      * necessary (due to wide cells) will end up hiding the hbar if the flow
 289      * becomes wide enough that the hbar is no longer necessary.
 290      */
 291     @Test public void testGeneralLayout_FewCellsButWide_ThenNarrow() {
 292         flow.setCellCount(3);
 293         flow.resize(50, flow.getHeight());
 294         pulse();
 295         flow.resize(300, flow.getHeight());
 296         pulse();
 297         assertFalse("The hbar should have been invisible", flow.getHbar().isVisible());
 298         assertFalse("The vbar should have been invisible", flow.getVbar().isVisible());
 299         assertFalse("The corner should have been invisible", flow.corner.isVisible());
 300         assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0);
 301         assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0);
 302         assertMinimalNumberOfCellsAreUsed(flow);
 303 
 304         flow.setVertical(false);
 305         flow.resize(300, 50);
 306         pulse();
 307         flow.resize(flow.getWidth(), 300);
 308         pulse();
 309         assertFalse("The hbar should have been invisible", flow.getHbar().isVisible());
 310         assertFalse("The vbar should have been invisible", flow.getVbar().isVisible());
 311         assertFalse("The corner should have been invisible", flow.corner.isVisible());
 312         assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0);
 313         assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0);
 314         assertMinimalNumberOfCellsAreUsed(flow);
 315     }
 316 
 317     /**
 318      * Tests that when there are many cells then the vbar in a vertical flow
 319      * is used.
 320      * <p>
 321      * Note, this test uncovered a bug where the bottom of the last cell was
 322      * exactly on the bottom edge of the flow and the vbar was not made visible.
 323      * Be sure to test for this explicitly some time!
 324      */
 325     @Test public void testGeneralLayout_ManyCells() {
 326         assertFalse("The hbar should have been invisible", flow.getHbar().isVisible());
 327         assertTrue("The vbar should have been visible", flow.getVbar().isVisible());
 328         assertFalse("The corner should have been invisible", flow.corner.isVisible());
 329         assertEquals(flow.getWidth(), flow.clipView.getWidth() + flow.getVbar().getWidth(), 0.0);
 330         assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0);
 331         assertEquals(flow.getVbar().getLayoutX(), flow.getWidth() - flow.getVbar().getWidth(), 0.0);
 332         assertMinimalNumberOfCellsAreUsed(flow);
 333 
 334         flow.setVertical(false);
 335         pulse();
 336         assertTrue("The hbar should have been visible", flow.getHbar().isVisible());
 337         assertFalse("The vbar should have been invisible", flow.getVbar().isVisible());
 338         assertFalse("The corner should have been invisible", flow.corner.isVisible());
 339         assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0);
 340         assertEquals(flow.getHeight(), flow.clipView.getHeight() + flow.getHbar().getHeight(), 0.0);
 341         assertEquals(flow.getHbar().getLayoutY(), flow.getHeight() - flow.getHbar().getHeight(), 0.0);
 342         assertMinimalNumberOfCellsAreUsed(flow);
 343     }
 344 
 345     /**
 346      * Test that after having only a few cells, if I then have many cells, that
 347      * the vbar is shown appropriately.
 348      */
 349     @Test public void testGeneralLayout_FewCells_ThenMany() {
 350         flow.setCellCount(3);
 351         pulse();
 352         flow.setCellCount(100);
 353         pulse();
 354         assertFalse("The hbar should have been invisible", flow.getHbar().isVisible());
 355         assertTrue("The vbar should have been visible", flow.getVbar().isVisible());
 356         assertFalse("The corner should have been invisible", flow.corner.isVisible());
 357         assertEquals(flow.getWidth(), flow.clipView.getWidth() + flow.getVbar().getWidth(), 0.0);
 358         assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0);
 359         assertEquals(flow.getVbar().getLayoutX(), flow.getWidth() - flow.getVbar().getWidth(), 0.0);
 360         assertMinimalNumberOfCellsAreUsed(flow);
 361 
 362         flow.setVertical(false);
 363         flow.setCellCount(3);
 364         pulse();
 365         flow.setCellCount(100);
 366         pulse();
 367         assertTrue("The hbar should have been visible", flow.getHbar().isVisible());
 368         assertFalse("The vbar should have been invisible", flow.getVbar().isVisible());
 369         assertFalse("The corner should have been invisible", flow.corner.isVisible());
 370         assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0);
 371         assertEquals(flow.getHeight(), flow.clipView.getHeight() + flow.getHbar().getHeight(), 0.0);
 372         assertEquals(flow.getHbar().getLayoutY(), flow.getHeight() - flow.getHbar().getHeight(), 0.0);
 373         assertMinimalNumberOfCellsAreUsed(flow);
 374     }
 375 
 376     /**
 377      * Test the case where there are many cells and they are wider than the
 378      * viewport. We should have the hbar, vbar, and corner region in this case.
 379      */
 380     @Test public void testGeneralLayout_ManyCellsAndWide() {
 381         flow.resize(50, flow.getHeight());
 382         pulse();
 383         assertTrue("The hbar should have been visible", flow.getHbar().isVisible());
 384         assertTrue("The vbar should have been visible", flow.getVbar().isVisible());
 385         assertTrue("The corner should have been visible", flow.corner.isVisible());
 386         assertEquals(flow.getWidth(), flow.clipView.getWidth() + flow.getVbar().getWidth(), 0.0);
 387         assertEquals(flow.getHeight(), flow.clipView.getHeight() + flow.getHbar().getHeight(), 0.0);
 388         assertEquals(flow.getVbar().getLayoutX(), flow.getWidth() - flow.getVbar().getWidth(), 0.0);
 389         assertEquals(flow.getVbar().getWidth(), flow.corner.getWidth(), 0.0);
 390         assertEquals(flow.getHbar().getHeight(), flow.corner.getHeight(), 0.0);
 391         assertEquals(flow.getHbar().getWidth(), flow.getWidth() - flow.corner.getWidth(), 0.0);
 392         assertEquals(flow.getVbar().getHeight(), flow.getHeight() - flow.corner.getHeight(), 0.0);
 393         assertEquals(flow.corner.getLayoutX(), flow.getWidth() - flow.corner.getWidth(), 0.0);
 394         assertEquals(flow.corner.getLayoutY(), flow.getHeight() - flow.corner.getHeight(), 0.0);
 395         assertMinimalNumberOfCellsAreUsed(flow);
 396 
 397         flow.setVertical(false);
 398         flow.resize(300, 50);
 399         pulse();
 400         assertTrue("The hbar should have been visible", flow.getHbar().isVisible());
 401         assertTrue("The vbar should have been visible", flow.getVbar().isVisible());
 402         assertTrue("The corner should have been visible", flow.corner.isVisible());
 403         assertEquals(flow.getWidth(), flow.clipView.getWidth() + flow.getVbar().getWidth(), 0.0);
 404         assertEquals(flow.getHeight(), flow.clipView.getHeight() + flow.getHbar().getHeight(), 0.0);
 405         assertEquals(flow.getVbar().getLayoutX(), flow.getWidth() - flow.getVbar().getWidth(), 0.0);
 406         assertEquals(flow.getVbar().getWidth(), flow.corner.getWidth(), 0.0);
 407         assertEquals(flow.getHbar().getHeight(), flow.corner.getHeight(), 0.0);
 408         assertEquals(flow.getHbar().getWidth(), flow.getWidth() - flow.corner.getWidth(), 0.0);
 409         assertEquals(flow.getVbar().getHeight(), flow.getHeight() - flow.corner.getHeight(), 0.0);
 410         assertEquals(flow.corner.getLayoutX(), flow.getWidth() - flow.corner.getWidth(), 0.0);
 411         assertEquals(flow.corner.getLayoutY(), flow.getHeight() - flow.corner.getHeight(), 0.0);
 412         assertMinimalNumberOfCellsAreUsed(flow);
 413     }
 414 
 415     /**
 416      * Tests that when the vertical flag changes, it results in layout
 417      */
 418     @Test public void testGeneralLayout_VerticalChangeResultsInNeedsLayout() {
 419         assertFalse(flow.isNeedsLayout());
 420         flow.setVertical(false);
 421         assertTrue(flow.isNeedsLayout());
 422     }
 423 
 424     /**
 425      * Tests that the range of the non-virtual scroll bar is valid
 426      */
 427     @Test public void testGeneralLayout_NonVirtualScrollBarRange() {
 428         flow.resize(50, flow.getHeight());
 429         pulse();
 430         assertEquals(0, flow.getHbar().getMin(), 0.0);
 431         assertEquals(flow.getMaxPrefBreadth() - flow.clipView.getWidth(), flow.getHbar().getMax(), 0.0);
 432         assertEquals((flow.clipView.getWidth()/flow.getMaxPrefBreadth()) * flow.getHbar().getMax(), flow.getHbar().getVisibleAmount(), 0.0);
 433         flow.setPosition(.28f);
 434         pulse();
 435         assertEquals(0, flow.getHbar().getMin(), 0.0);
 436         assertEquals(flow.getMaxPrefBreadth() - flow.clipView.getWidth(), flow.getHbar().getMax(), 0.0);
 437         assertEquals((flow.clipView.getWidth()/flow.getMaxPrefBreadth()) * flow.getHbar().getMax(), flow.getHbar().getVisibleAmount(), 0.0);
 438 
 439         flow.setVertical(false);
 440         flow.setPosition(0);
 441         flow.resize(300, 50);
 442         pulse();
 443         assertEquals(0, flow.getVbar().getMin(), 0.0);
 444         assertEquals(flow.getMaxPrefBreadth() - flow.clipView.getHeight(), flow.getVbar().getMax(), 0.0);
 445         assertEquals((flow.clipView.getHeight()/flow.getMaxPrefBreadth()) * flow.getVbar().getMax(), flow.getVbar().getVisibleAmount(), 0.0);
 446         flow.setPosition(.28);
 447         pulse();
 448         assertEquals(0, flow.getVbar().getMin(), 0.0);
 449         assertEquals(flow.getMaxPrefBreadth() - flow.clipView.getHeight(), flow.getVbar().getMax(), 0.0);
 450         assertEquals((flow.clipView.getHeight()/flow.getMaxPrefBreadth()) * flow.getVbar().getMax(), flow.getVbar().getVisibleAmount(), 0.0);
 451     }
 452 
 453     /**
 454      * Tests that the maxPrefBreadth is computed correctly for the first page of cells.
 455      * In our test case, the first page of cells have a uniform pref.
 456      */
 457     @Test public void testGeneralLayout_maxPrefBreadth() {
 458         assertEquals(100, flow.getMaxPrefBreadth(), 0.0);
 459     }
 460 
 461     /**
 462      * Tests that even after the first computation of max pref, that it is
 463      * updated when we encounter a new cell (while scrolling for example) that
 464      * has a larger pref.
 465      */
 466     @Ignore
 467     @Test public void testGeneralLayout_maxPrefBreadthUpdatedWhenEncounterLargerPref() {
 468         flow.setPosition(.28);
 469         pulse();
 470         assertEquals(200, flow.getMaxPrefBreadth(), 0.0);
 471     }
 472 
 473     /**
 474      * Tests that if we encounter cells or pages of cells with smaller prefs
 475      * than the max pref that we will keep the max pref the same.
 476      */
 477     @Ignore
 478     @Test public void testGeneralLayout_maxPrefBreadthRemainsSameWhenEncounterSmallerPref() {
 479         flow.setPosition(.28);
 480         pulse();
 481         flow.setPosition(.8);
 482         pulse();
 483         assertEquals(200, flow.getMaxPrefBreadth(), 0.0);
 484     }
 485 
 486     /**
 487      * Tests that changes to the vertical property will clear the maxPrefBreadth
 488      */
 489     @Test public void testGeneralLayout_VerticalChangeClearsmaxPrefBreadth() {
 490         flow.setVertical(false);
 491         assertEquals(-1, flow.getMaxPrefBreadth(), 0.0);
 492     }
 493 
 494     /**
 495      * Tests that changes to the cell count will not affect maxPrefBreadth.
 496      */
 497     @Ignore
 498     @Test public void testGeneralLayout_maxPrefBreadthUnaffectedByCellCountChanges() {
 499         flow.setCellCount(10);
 500         pulse();
 501         assertEquals(100, flow.getMaxPrefBreadth(), 0.0);
 502         flow.setCellCount(100);
 503         pulse();
 504         flow.setPosition(.28);
 505         pulse();
 506         assertEquals(200, flow.getMaxPrefBreadth(), 0.0);
 507         flow.setCellCount(10);
 508         pulse();
 509         assertEquals(200, flow.getMaxPrefBreadth(), 0.0);
 510     }
 511 
 512     /**
 513      * Tests that as we scroll, if the non-virtual scroll bar is visible, then
 514      * as we update maxPrefBreadth it will not affect the non-virtual scroll bar's
 515      * value <b>unless</b> the value is such that the scroll bar is scrolled
 516      * all the way to the end, in which case it will remain scrolled to the
 517      * end.
 518      */
 519     @Test public void testGeneralLayout_maxPrefBreadthScrollBarValueInteraction() {
 520         flow.resize(50, flow.getHeight());
 521         flow.getHbar().setValue(30);
 522         pulse();
 523         flow.setPosition(.28);
 524         pulse();
 525         assertEquals(30, flow.getHbar().getValue(), 0.0);
 526 
 527         // Reset the test and this time check what happens when we are scrolled
 528         // to the very right
 529         flow.setPosition(0);
 530         flow.setVertical(false);
 531         flow.setVertical(true);
 532         pulse();
 533         assertEquals(100, flow.getMaxPrefBreadth(), 0.0);
 534         flow.getHbar().setValue(flow.getHbar().getMax()); // scroll to the end
 535         flow.setPosition(.28);
 536         pulse();
 537         assertEquals(flow.getHbar().getMax(), flow.getHbar().getValue(), 0.0);
 538 
 539         flow.setVertical(false);
 540         flow.setPosition(0);
 541         flow.getHbar().setValue(0);
 542         flow.resize(300, 50);
 543         pulse();
 544         flow.getVbar().setValue(30);
 545         pulse();
 546         flow.setPosition(.28);
 547         pulse();
 548         assertEquals(30, flow.getVbar().getValue(), 0.0);
 549 
 550         // Reset the test and this time check what happens when we are scrolled
 551         // to the very right
 552         flow.setPosition(0);
 553         flow.setVertical(true);
 554         flow.setVertical(false);
 555         pulse();
 556         assertEquals(100, flow.getMaxPrefBreadth(), 0.0);
 557         flow.getVbar().setValue(flow.getVbar().getMax()); // scroll to the end
 558         flow.setPosition(.28);
 559         pulse();
 560         assertEquals(flow.getVbar().getMax(), flow.getVbar().getValue(), 0.0);
 561     }
 562 
 563     @Test public void testGeneralLayout_ScrollToEndOfVirtual_BarStillVisible() {
 564         assertTrue("The vbar was expected to be visible", flow.getVbar().isVisible());
 565         flow.setPosition(1);
 566         pulse();
 567         assertTrue("The vbar was expected to be visible", flow.getVbar().isVisible());
 568 
 569         flow.setPosition(0);
 570         flow.setVertical(false);
 571         pulse();
 572         assertTrue("The hbar was expected to be visible", flow.getHbar().isVisible());
 573         flow.setPosition(1);
 574         pulse();
 575         assertTrue("The hbar was expected to be visible", flow.getHbar().isVisible());
 576     }
 577 
 578     // Need to test all the resize operations and make sure the position of
 579     // nodes is as expected, that they don't get shifted etc.
 580 
 581     // Test: Scroll to the bottom, expand size out, then make smaller. The
 582     // thumb/scroll is not consistent right now.
 583 
 584     // TODO figure out and deal with what happens when orientation changes
 585     // to the hbar.value and vbar.value. Do they just switch? Probably not?
 586     // Probably reset the non-virtual direction and swap the virtual one over.
 587     // So if vbar was .5 and hbar was 30, when we set vertical = false, then
 588     // we change the hbar to .5 and the vbar to 0. However this has to be done
 589     // at the same time that the "virtual" property of the scroll bars is
 590     // changed
 591 
 592     ////////////////////////////////////////////////////////////////////////////
 593     //
 594     //  Cell Layout
 595     //
 596     ////////////////////////////////////////////////////////////////////////////
 597 
 598     /**
 599      * Test to make sure that we are virtual -- that all cells are not being
 600      * created.
 601      */
 602     @Test public void testCellLayout_NotAllCellsAreCreated() {
 603         // due to the initial size of the VirtualFlow and the number of cells
 604         // and their heights, we should have more cells than we have space to
 605         // fit them and so only enough cells should be created to meet our
 606         // needs and not any more than that
 607         assertTrue("All of the cells were created", flow.cells.size() < flow.getCellCount());
 608         assertMinimalNumberOfCellsAreUsed(flow);
 609     }
 610 
 611     /**
 612      * Tests the size and position of all the cells to make sure they were
 613      * laid out properly.
 614      */
 615     @Test public void testCellLayout_CellSizes_AfterLayout() {
 616         double offset = 0.0;
 617         for (int i = 0; i < flow.cells.size(); i++) {
 618             IndexedCell cell = flow.cells.get(i);
 619             assertEquals(25, cell.getHeight(), 0.0);
 620             assertEquals(offset, cell.getLayoutY(), 0.0);
 621             offset += cell.getHeight();
 622         }
 623 
 624         offset = 0.0;
 625         flow.setVertical(false);
 626         pulse();
 627         for (int i = 0; i < flow.cells.size(); i++) {
 628             IndexedCell cell = flow.cells.get(i);
 629             assertEquals(25, cell.getWidth(), 0.0);
 630             assertEquals(offset, cell.getLayoutX(), 0.0);
 631             offset += cell.getWidth();
 632         }
 633     }
 634 
 635     /**
 636      * Test the widths of the cells when the viewport is wider than the
 637      * max pref width/height. They should be uniform, and should be the
 638      * width of the viewport.
 639      */
 640     @Test public void testCellLayout_ViewportWiderThanmaxPrefBreadth() {
 641         // Note that the pref width of everything is 100, but the actual
 642         // available width is much larger (300 - hbar.width or
 643         // 300 - vbar.height) and so the non-virtual dimension of the cell
 644         // should be larger than the max pref
 645         double expected = flow.clipView.getWidth();
 646         for (int i = 0; i < flow.cells.size(); i++) {
 647             IndexedCell cell = flow.cells.get(i);
 648             assertEquals(expected, cell.getWidth(), 0.0);
 649         }
 650 
 651         flow.setVertical(false);
 652         pulse();
 653         expected = flow.clipView.getHeight();
 654         for (int i = 0; i < flow.cells.size(); i++) {
 655             IndexedCell cell = flow.cells.get(i);
 656             assertEquals(expected, cell.getHeight(), 0.0);
 657         }
 658     }
 659 
 660     /**
 661      * Test the widths of the cells when the viewport is shorter than the
 662      * max pref width/height. They should be uniform, and should be the max
 663      * pref.
 664      */
 665     @Test public void testCellLayout_ViewportShorterThanmaxPrefBreadth() {
 666         flow.resize(50, flow.getHeight());
 667         pulse();
 668         assertEquals(100, flow.getMaxPrefBreadth(), 0.0);
 669         for (int i = 0; i < flow.cells.size(); i++) {
 670             IndexedCell cell = flow.cells.get(i);
 671             assertEquals(flow.getMaxPrefBreadth(), cell.getWidth(), 0.0);
 672         }
 673 
 674         flow.setVertical(false);
 675         flow.resize(flow.getWidth(), 50);
 676         pulse();
 677         assertEquals(100, flow.getMaxPrefBreadth(), 0.0);
 678         for (int i = 0; i < flow.cells.size(); i++) {
 679             IndexedCell cell = flow.cells.get(i);
 680             assertEquals(flow.getMaxPrefBreadth(), cell.getHeight(), 0.0);
 681         }
 682     }
 683 
 684     /**
 685      * Test that when we scroll and encounter a cell which has a larger pref
 686      * than we have previously encountered (happens in this test when we visit
 687      * the cell for item #29), then the max pref is updated and the cells are
 688      * all resized to match.
 689      */
 690     @Ignore
 691     @Test public void testCellLayout_ScrollingFindsCellWithLargemaxPrefBreadth() {
 692         flow.resize(50, flow.getHeight());
 693         flow.setPosition(.28); // happens to position such that #29 is visible
 694         pulse();
 695         assertEquals(200, flow.getMaxPrefBreadth(), 0.0);
 696         for (int i = 0; i < flow.cells.size(); i++) {
 697             IndexedCell cell = flow.cells.get(i);
 698             assertEquals(flow.getMaxPrefBreadth(), cell.getWidth(), 0.0);
 699         }
 700 
 701         flow.setVertical(false);
 702         flow.resize(flow.getWidth(), 50);
 703         // NOTE Run this test without the pulse and it fails!
 704         pulse();
 705         flow.setPosition(.28);
 706         pulse();
 707         assertEquals(200, flow.getMaxPrefBreadth(), 0.0);
 708         for (int i = 0; i < flow.cells.size(); i++) {
 709             IndexedCell cell = flow.cells.get(i);
 710             assertEquals(flow.getMaxPrefBreadth(), cell.getHeight(), 0.0);
 711         }
 712     }
 713 
 714     /**
 715      * Checks that the initial set of cells (the first page of cells) are
 716      * indexed starting with cell #0 and working up from there.
 717      */
 718     @Test public void testCellLayout_CellIndexes_FirstPage() {
 719         for (int i = 0; i < flow.cells.size(); i++) {
 720             assertEquals(i, flow.cells.get(i).getIndex());
 721         }
 722     }
 723 
 724     /**
 725      * The bug here is that if layout() is called on the flow numerous times,
 726      * but nothing substantially has changed, we should reuse the cells in the
 727      * same order they were before. We had a bug where when you clicked on the
 728      * ListView, the click wouldn't register. This was because by clicking we
 729      * were giving focus to the ListView, which caused a layout(), and then the
 730      * cells location was shuffled. It didn't look like it to the user, but that
 731      * is what happened. As a result, when the mouse release took place, the
 732      * event was delivered to a different cell than expected and misbehavior
 733      * took place.
 734      */
 735     @Test public void testCellLayout_LayoutWithoutChangingThingsUsesCellsInSameOrderAsBefore() {
 736         List<IndexedCell> cells = new LinkedList<IndexedCell>();
 737         for (int i = 0; i < flow.cells.size(); i++) {
 738             cells.add(flow.cells.get(i));
 739         }
 740         assertMatch(cells, flow.cells); // sanity check
 741         flow.requestLayout();
 742         pulse();
 743         assertMatch(cells, flow.cells);
 744         flow.setPosition(1);
 745         pulse();
 746         cells.clear();
 747         for (int i = 0; i < flow.cells.size(); i++) {
 748             cells.add(flow.cells.get(i));
 749         }
 750         flow.requestLayout();
 751         pulse();
 752         assertMatch(cells, flow.cells);
 753     }
 754 
 755 
 756     @Test
 757     public void testCellLayout_BiasedCellAndLengthBar() {
 758         flow.setCreateCell(param -> new CellStub(flow) {
 759             @Override protected double computeMinWidth(double height) { return 0; }
 760             @Override protected double computeMaxWidth(double height) { return Double.MAX_VALUE; }
 761             @Override protected double computePrefWidth(double height) {
 762                 return 200;
 763             }
 764 
 765             @Override protected double computeMinHeight(double width) { return 0; }
 766             @Override protected double computeMaxHeight(double width) { return Double.MAX_VALUE; }
 767             @Override protected double computePrefHeight(double width) {
 768                 return getIndex() == 0 ? 100 - 5 *(Math.floorDiv((int)width - 200, 10)) : 100;
 769             }
 770         });
 771         flow.setCellCount(3);
 772         flow.recreateCells(); // This help to override layoutChildren() in flow.setCellCount()
 773         flow.getVbar().setPrefWidth(20); // Since Skins are not initialized, we set the pref width explicitly
 774         flow.requestLayout();
 775         pulse();
 776         assertEquals(300, flow.cells.get(0).getWidth(), 1e-100);
 777         assertEquals(50, flow.cells.get(0).getHeight(), 1e-100);
 778 
 779         flow.resize(200, 300);
 780 
 781         flow.requestLayout();
 782         pulse();
 783         assertEquals(200, flow.cells.get(0).getWidth(), 1e-100);
 784         assertEquals(100, flow.cells.get(0).getHeight(), 1e-100);
 785 
 786     }
 787 
 788     ////////////////////////////////////////////////////////////////////////////
 789     //
 790     //  Cell Life Cycle
 791     //
 792     ////////////////////////////////////////////////////////////////////////////
 793 
 794     @Test public void testCellLifeCycle_CellsAreCreatedOnLayout() {
 795         // when the flow was first created in setUp we do a layout()
 796         assertTrue("The cells didn't get created", flow.cells.size() > 0);
 797     }
 798 
 799 //    /**
 800 //     * During layout the order and contents of cells will change. We need
 801 //     * to make sure that CSS for cells is applied at this time. To test this,
 802 //     * I just set the position and perform a new pulse. Since layout happens
 803 //     * after the CSS updates are applied, if the test fails, then there will
 804 //     * be cells left in a state where they need their CSS applied.
 805 //     */
 806 //    @Test public void testCellLifeCycle_CSSUpdatesHappenDuringLayout() {
 807 //        flow.setPosition(.35);
 808 //        pulse();
 809 //        for (int i = 0; i < flow.cells.size(); i++) {
 810 //            IndexedCell cell = flow.cells.get(i);
 811 //            assertEquals(CssFlags.CLEAN, cell.impl_getCSSFlags());
 812 //        }
 813 //    }
 814 
 815     ////////////////////////////////////////////////////////////////////////////
 816     //
 817     //  Position
 818     //
 819     ////////////////////////////////////////////////////////////////////////////
 820 
 821     ////////////////////////////////////////////////////////////////////////////
 822     //
 823     //  Pixel Scrolling
 824     //
 825     ////////////////////////////////////////////////////////////////////////////
 826 
 827     ////////////////////////////////////////////////////////////////////////////
 828     //
 829     //  Cell Count Changes
 830     //
 831     ////////////////////////////////////////////////////////////////////////////
 832 
 833     // want to test that the view remains stable when the cell count changes
 834 
 835     // LIST VIEW: test that using a bunch of nodes as items to a ListView works
 836     // LIST VIEW: test that inserting an item moves the selected index to keep in sync
 837     // test that dynamically changing all of the contents causes them to refresh
 838 
 839     // test that when the number of cells change, that things are laid out
 840 //    @Test public void testCellCountChanges_FirstRowIsRemoved() {
 841 //
 842 //    }
 843 //
 844 //    @Test public void testCellCountChanges_MiddleRowIsRemoved() {
 845 //
 846 //    }
 847 //
 848 //    @Test public void testCellCountChanges_LastRowIsRemoved() {
 849 //
 850 //    }
 851 //
 852 ////    @Test public void testCellCountChanges_SelectedRowRemoved() {
 853 ////
 854 ////    }
 855 ////
 856 ////    @Test public void testCellCountChanges_NonSelectedRowRemoved() {
 857 ////
 858 ////    }
 859 //
 860 //    @Test public void testCellCountChanges_FirstRowIsAdded() {
 861 //
 862 //    }
 863 //
 864 //    @Test public void testCellCountChanges_MiddleRowIsAdded() {
 865 //
 866 //    }
 867 //
 868 //    @Test public void testCellCountChanges_LastRowIsAdded() {
 869 //
 870 //    }
 871 //
 872 ////    @Test public void testCellCountChanges_RowIsAddedBeforeSelectedRow() {
 873 ////
 874 ////    }
 875 ////
 876 ////    @Test public void testCellCountChanges_RowIsAddedAfterSelectedRow() {
 877 ////
 878 ////    }
 879 
 880     ////////////////////////////////////////////////////////////////////////////
 881     //
 882     //  VirtualFlow State Changes
 883     //
 884     ////////////////////////////////////////////////////////////////////////////
 885 
 886     /**
 887      * Tests that when the createCell method changes, it results in layout
 888      */
 889     @Test public void testCreateCellFunctionChangesResultInNeedsLayoutAndNoCellsAndNoAccumCell() {
 890         assertFalse(flow.isNeedsLayout());
 891         flow.getCellLength(49); // forces accum cell to be created
 892         assertNotNull("Accum cell was null", flow.accumCell);
 893         flow.setCreateCell(p -> new CellStub(flow));
 894         assertTrue(flow.isNeedsLayout());
 895         assertNull("accumCell didn't get cleared", flow.accumCell);
 896     }
 897 
 898 
 899     ////////////////////////////////////////////////////////////////////////////
 900     //
 901     //  Tests on specific functions
 902     //
 903     ////////////////////////////////////////////////////////////////////////////
 904 
 905     @Test public void test_getCellLength() {
 906         assertEquals(100, flow.getCellCount());
 907         for (int i = 0; i < 50; i++) {
 908             if (i != 29) assertEquals(25, flow.getCellLength(i), 0.0);
 909         }
 910         flow.setVertical(false);
 911         flow.requestLayout();
 912         pulse();
 913         assertEquals(100, flow.getCellCount());
 914         for (int i = 0; i < 50; i++) {
 915             if (i != 29) assertEquals("Bad index: " + i, 25, flow.getCellLength(i), 0.0);
 916         }
 917     }
 918 
 919     /*
 920     ** if we scroll the flow by a number of LINES,
 921     ** without having done anything to select a cell
 922     ** the flow should scroll.
 923     */ 
 924     @Test public void testInitialScrollEventActuallyScrolls() {
 925         /*
 926         ** re-initialize this, as it must be the first
 927         ** interaction with the flow
 928         */
 929         flow = new VirtualFlow();
 930         flow.setVertical(true);
 931         flow.setCreateCell(p -> new CellStub(flow) {
 932             @Override protected double computeMinWidth(double height) { return computePrefWidth(height); }
 933             @Override protected double computeMaxWidth(double height) { return computePrefWidth(height); }
 934             @Override protected double computePrefWidth(double height) {
 935                 return flow.isVertical() ? (c.getIndex() == 29 ? 200 : 100) : (c.getIndex() == 29 ? 100 : 25);
 936             }
 937 
 938             @Override protected double computeMinHeight(double width) { return computePrefHeight(width); }
 939             @Override protected double computeMaxHeight(double width) { return computePrefHeight(width); }
 940             @Override protected double computePrefHeight(double width) {
 941                 return flow.isVertical() ? (c.getIndex() == 29 ? 100 : 25) : (c.getIndex() == 29 ? 200 : 100);
 942             }
 943         });
 944         
 945         flow.setCellCount(100);
 946         flow.resize(300, 300);
 947         pulse();
 948        
 949         double originalValue = flow.getPosition();
 950 
 951         Event.fireEvent(flow, 
 952               new ScrollEvent(ScrollEvent.SCROLL,
 953                           0.0, -10.0, 0.0, -10.0,
 954                           false, false, false, false, true, false,
 955                           0, 0,
 956                           0, 0,
 957                           ScrollEvent.HorizontalTextScrollUnits.NONE, 0.0,
 958                           ScrollEvent.VerticalTextScrollUnits.LINES, -1.0,
 959                           0, null));
 960 
 961         assertTrue(originalValue != flow.getPosition());
 962     }
 963 
 964     @Test
 965     public void test_RT_36507() {
 966         flow = new VirtualFlow();
 967         flow.setVertical(true);
 968         // Worst case scenario is that the cells have height = 0.
 969         // The code should prevent creating more than 100 of these zero height cells
 970         // (since viewportLength is 100).
 971         // An "INFO: index exceeds maxCellCount" message should print out.
 972         flow.setCreateCell(p -> new CellStub(flow) {
 973             @Override
 974             protected double computeMaxHeight(double width) { return 0; }
 975             @Override
 976             protected double computePrefHeight(double width) { return 0; }
 977             @Override
 978             protected double computeMinHeight(double width) { return 0; }
 979 
 980         });
 981         flow.setCellCount(10);
 982         flow.setViewportLength(100);
 983         flow.addLeadingCells(1, 0);
 984         flow.sheetChildren.addListener((InvalidationListener) (o) -> {
 985             int count = ((List) o).size();
 986             assertTrue(Integer.toString(count), count <= 100);
 987         });
 988         flow.addTrailingCells(true);
 989     }
 990 
 991     private int rt36556_instanceCount;
 992     @Test
 993     public void test_rt36556() {
 994         rt36556_instanceCount = 0;
 995         flow = new VirtualFlow();
 996         flow.setVertical(true);
 997         flow.setCreateCell(p -> {
 998             rt36556_instanceCount++;
 999             return new CellStub(flow);
1000         });
1001         flow.setCellCount(100);
1002         flow.resize(300, 300);
1003         pulse();
1004         final int cellCountAtStart = rt36556_instanceCount;
1005         flow.adjustPixels(10000);
1006         pulse();
1007         assertEquals(cellCountAtStart, rt36556_instanceCount);
1008         assertNull(flow.getVisibleCell(0));
1009         assertMinimalNumberOfCellsAreUsed(flow);
1010     }
1011 
1012     @Test
1013     public void test_rt36556_scrollto() {
1014         rt36556_instanceCount = 0;
1015         flow = new VirtualFlow();
1016         flow.setVertical(true);
1017         flow.setCreateCell(p -> {
1018             rt36556_instanceCount++;
1019             return new CellStub(flow);
1020         });
1021         flow.setCellCount(100);
1022         flow.resize(300, 300);
1023         pulse();
1024         final int cellCountAtStart = rt36556_instanceCount;
1025         flow.scrollTo(80);
1026         pulse();
1027         assertEquals(cellCountAtStart, rt36556_instanceCount);
1028         assertNull(flow.getVisibleCell(0));
1029         assertMinimalNumberOfCellsAreUsed(flow);
1030     }
1031     
1032     @Test
1033     public void test_RT39035() {
1034         flow.adjustPixels(250);
1035         pulse();
1036         flow.adjustPixels(500);
1037         pulse();
1038         assertTrue(flow.getPosition() < 1.0);
1039         assertMinimalNumberOfCellsAreUsed(flow);
1040     }
1041 
1042     @Test
1043     public void test_RT37421() {
1044         flow.setPosition(0.98);
1045         pulse();
1046         flow.adjustPixels(100);
1047         pulse();
1048         assertEquals(1.0, flow.getPosition(), 0.0);
1049         assertMinimalNumberOfCellsAreUsed(flow);
1050     }
1051 }
1052 
1053 class CellStub extends IndexedCell {
1054     String s;
1055     VirtualFlow flow;
1056 
1057     public CellStub(VirtualFlow flow) { init(flow); }
1058     public CellStub(VirtualFlow flow, String s) { init(flow); this.s = s; }
1059     
1060     private void init(VirtualFlow flow) {
1061         this.flow = flow;
1062         setSkin(new SkinStub<CellStub>(this));
1063         updateItem(this, false);
1064     }
1065 
1066     @Override
1067     public void updateIndex(int i) {
1068         super.updateIndex(i);
1069         
1070         s = "Item " + getIndex();
1071 //        updateItem(getIndex(), getIndex() >= flow.getCellCount());
1072     }
1073 }