/**
  * Adds columns.
  *
  * @param index The index to insert the columns.
  * @param cols A list of all columns to add.
  * @param names The names of the new columns.
  * @param initWidth The initial width for the columns.
  * @param widths The actual widths of the columns.
  * @param timing The animation timing.
  */
 public synchronized void addColumns(
     final int index,
     final List<List<T>> cols,
     final List<String> names,
     final double initWidth,
     final List<Double> widths,
     final AnimationTiming timing) {
   ensureChangeAllowed();
   Objects.requireNonNull(timing);
   // add columns
   final int rows = rows();
   final int colsToAdd = cols.size();
   final List<List<T>> toAdd = new ArrayList<>(rows);
   for (int i = 0; i < rows; ++i) {
     toAdd.add(new ArrayList<T>(colsToAdd));
   }
   for (final List<T> l : cols) {
     int row = 0;
     for (final T el : l) {
       final List<T> list = toAdd.get(row);
       list.add(el);
       ++row;
     }
     if (row != rows) throw new IllegalArgumentException(row + " != " + rows);
   }
   for (final List<T> l : toAdd) {
     if (l.size() != colsToAdd) throw new IllegalArgumentException(l.size() + " != " + colsToAdd);
   }
   // add widths
   final AnimationList al = animator != null ? animator.getAnimationList() : null;
   final List<AnimatedDouble> newWidths = new ArrayList<>(widths.size());
   for (final Double w : widths) {
     final AnimatedDouble d = new AnimatedDouble(initWidth);
     if (al != null) {
       al.addAnimated(d);
     }
     d.startAnimationTo(Objects.requireNonNull(w), timing);
     newWidths.add(d);
   }
   if (newWidths.size() != colsToAdd)
     throw new IllegalArgumentException(newWidths.size() + " != " + colsToAdd);
   // add names
   final List<String> newNames = new ArrayList<>(names.size());
   for (final String n : names) {
     newNames.add(Objects.requireNonNull(n));
   }
   if (newNames.size() != colsToAdd)
     throw new IllegalArgumentException(newNames.size() + " != " + colsToAdd);
   // add on end
   int i = 0;
   for (final List<T> m : matrix) {
     m.addAll(index, toAdd.get(i));
     ++i;
   }
   this.cols += colsToAdd;
   this.widths.addAll(index, newWidths);
   colNames.addAll(index, names);
   refreshAll();
 }
 /**
  * Adds rows.
  *
  * @param index The index to insert the rows.
  * @param rows A list of all rows to add.
  * @param names The names of the new rows.
  * @param initHeight The initial height for the rows.
  * @param heights The actual heights of the rows.
  * @param timing The animation timing.
  */
 public synchronized void addRows(
     final int index,
     final List<List<T>> rows,
     final List<String> names,
     final double initHeight,
     final List<Double> heights,
     final AnimationTiming timing) {
   ensureChangeAllowed();
   Objects.requireNonNull(timing);
   // add rows
   final List<List<T>> toAdd = new ArrayList<>(rows.size());
   for (final List<T> l : rows) {
     final ArrayList<T> add = new ArrayList<>(l);
     if (add.size() != cols) throw new IllegalArgumentException(add.size() + " != " + cols);
     toAdd.add(add);
   }
   // add heights
   final AnimationList al = animator != null ? animator.getAnimationList() : null;
   final List<AnimatedDouble> newHeights = new ArrayList<>(heights.size());
   for (final Double h : heights) {
     final AnimatedDouble d = new AnimatedDouble(initHeight);
     if (al != null) {
       al.addAnimated(d);
     }
     d.startAnimationTo(Objects.requireNonNull(h), timing);
     newHeights.add(d);
   }
   if (newHeights.size() != toAdd.size())
     throw new IllegalArgumentException(newHeights.size() + " != " + toAdd.size());
   // add names
   final List<String> newNames = new ArrayList<>(names.size());
   for (final String n : names) {
     newNames.add(Objects.requireNonNull(n));
   }
   if (newNames.size() != toAdd.size())
     throw new IllegalArgumentException(newNames.size() + " != " + toAdd.size());
   // add on end
   matrix.addAll(index, toAdd);
   this.heights.addAll(index, newHeights);
   rowNames.addAll(index, names);
   refreshAll();
 }
  /**
   * Removes rows. Note that it is not allowed to remove all rows.
   *
   * @param from The starting index inclusive.
   * @param to The end index exclusive.
   * @param timing The animation timing.
   */
  public synchronized void removeRows(final int from, final int to, final AnimationTiming timing) {
    if (to - from >= rows()) throw new IllegalArgumentException("cannot remove all rows");
    ensureChangeAllowed();
    Objects.requireNonNull(timing);
    noChange = true;
    for (final AnimatedDouble d : heights.subList(from, to)) {
      d.startAnimationTo(0.0, timing);
    }
    // TODO #43 -- Java 8 simplification
    animator
        .getAnimationList()
        .scheduleAction(
            new AnimationAction() {

              @Override
              public void animationFinished() {
                noChange = false;
                removeRows(from, to);
              }
            },
            timing);
  }