All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.tentackle.fx.table.FxTableCell Maven / Gradle / Ivy

/*
 * Tentackle - https://tentackle.org.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

package org.tentackle.fx.table;

import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.control.TableCell;
import javafx.scene.control.TablePosition;
import javafx.scene.input.KeyEvent;

import org.tentackle.fx.FxComponent;
import org.tentackle.fx.FxFactory;
import org.tentackle.fx.ValueTranslator;

/**
 * Extended table cell.
 *
 * @param  type of the objects contained within the table's items list
 * @param  type of the content in all cells in this column
 * @author harald
 */
public class FxTableCell extends TableCell {

  private final TableColumnConfiguration columnConfiguration;      // column configuration

  private boolean toNext;                           // true if editor was committed with ENTER or TAB
  private boolean toPrevious;                       // true if editor was committed with Shift ENTER/TAB

  /**
   * Creates a table cell.
   *
   * @param columnConfiguration the column configuration
   */
  public FxTableCell(TableColumnConfiguration columnConfiguration) {
    this.columnConfiguration = columnConfiguration;
    setEditable(columnConfiguration.isEditable());
  }


  /**
   * Gets the column configuration.
   *
   * @return the configuration
   */
  public TableColumnConfiguration getColumnConfiguration() {
    return columnConfiguration;
  }


  @Override
  protected void updateItem(T item, boolean empty) {
    if (item == getItem()) {
      return;
    }
    super.updateItem(item, empty);

    if (item == null) {
      setText(null);
      setGraphic(null);
    }
    else if (item instanceof Node) {
      setText(null);
      setGraphic((Node) item);
    }
    else {
      getCellType().updateItem(this, item);
    }
  }


  /**
   * Gets the table cell type.
   *
   * @return the cell type, never null
   */
  protected TableCellType getCellType() {
    return columnConfiguration.getTableConfiguration().getCellType(columnConfiguration.getType());
  }


  @Override
  @SuppressWarnings({ "unchecked", "rawtypes" })
  public void startEdit() {
    if (isEditable() && getTableView().isEditable() && getTableColumn().isEditable()) {
      super.startEdit();
      if (isEditing()) {
        toNext = false;
        toPrevious = false;
        TableCellType cellType = getCellType();
        FxComponent editor = getEditor(cellType);
        editor.setTableCell(this);
        editor.setType(columnConfiguration.getType());
        ValueTranslator translator = FxFactory.getInstance().createValueTranslator(
                editor.getType(), cellType.getEditorType(), editor);
        editor.setViewObject(translator.toView(getItem()));

        setText(null);
        setGraphic((Node) editor);

        Node node = editor.getDelegate().getNode();   // effective node getting the keyboard events

        EventHandler handler = event -> {
          switch (event.getCode()) {
            case ESCAPE:
              cancelEdit();
              getTableView().refresh();
              event.consume();
              break;

            case TAB:
            case ENTER:
              if (event.isShiftDown()) {
                toPrevious = true;
              }
              else {
                toNext = true;
              }
              T value = (T) translator.toModel(editor.getViewObject());
              columnConfiguration.getBinding().setModelValue(value);
              commitEdit(value);
              getTableView().refresh();
              event.consume();
              break;
          }
        };

        node.setOnKeyPressed(handler);

        if (editor != node) {
          // for whatever reason, ESC is sometimes not forwarded to the inner component.
          // In such a case, catch the keystroke on the outer one (for ex. DatePicker).
          // @todo: check if still necessary in newer releases then Java 9
          ((Node) editor).setOnKeyPressed(handler);
        }

        // same sh*t as in Swing... hoping 3 loops in eventQ is sufficient, one isnt always :(
        Platform.runLater(() -> Platform.runLater(() -> Platform.runLater(node::requestFocus)));
      }
    }
  }


  /**
   * Gets the editor for this table cell.
   *
   * @param cellType the table cell type
   * @return the editor
   */
  protected FxComponent getEditor(TableCellType cellType) {
    FxComponent editor = columnConfiguration.getEditor();
    if (editor == null) {
      editor = cellType.getEditor();
    }
    return editor;
  }


  @Override
  public void commitEdit(T newValue) {
    super.commitEdit(newValue);
    Platform.runLater(() -> {
      switch(columnConfiguration.getTableConfiguration().getEditMode()) {
        case ROW:
          if (toNext) {
            TablePosition pos;
            do {
              getTableView().getSelectionModel().selectNext();
              pos = getPosition();
            }
            while (pos != null && !pos.getTableColumn().isEditable());
            editCell();
          }
          else if (toPrevious) {
            TablePosition pos;
            do {
              getTableView().getSelectionModel().selectPrevious();
              pos = getPosition();
            }
            while (pos != null && !pos.getTableColumn().isEditable());
            editCell();
          }
          break;

        case COLUMN:
          if (toNext) {
            getTableView().getSelectionModel().selectBelowCell();
            editCell();
          }
          else if (toPrevious) {
            getTableView().getSelectionModel().selectAboveCell();
            editCell();
          }
          break;
      }
      getTableView().requestFocus();
    });
  }


  /**
   * Gets the table position of the currently selected cell.
   *
   * @return the position, null if none selected
   */
  @SuppressWarnings("unchecked")
  protected TablePosition getPosition() {
    ObservableList cells = getTableView().getSelectionModel().getSelectedCells();
    return cells.isEmpty() ? null : cells.get(0);
  }

  /**
   * Edits the currently selected cell.
   */
  protected void editCell() {
    TablePosition pos = getPosition();
    if (pos != null) {
      Platform.runLater(() -> getTableView().edit(pos.getRow(), pos.getTableColumn()));
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy