private void installListeners() {
    treeTableView
        .getSelectionModel()
        .selectedItemProperty()
        .addListener(
            (observable, oldValue, newValue) -> {
              if (newValue != null) {
                selectedAccountProperty.setValue(newValue.getValue());
              } else {
                selectedAccountProperty.setValue(null);
              }
            });

    for (final TreeTableColumn<?, ?> treeTableColumn : treeTableView.getColumns()) {
      treeTableColumn
          .visibleProperty()
          .addListener((observable, oldValue, newValue) -> saveColumnVisibility());
    }
  }
  private TreeTableView<ITreeNode> createTreeTableView(ProfileContainer container) {

    final List<Profile> profiles = container.getProfiles();
    final MyTreeNode rootNode = new MyTreeNode(null);
    for (Profile p : profiles) {
      final MyTreeNode profileNode = new MyTreeNode(p);
      rootNode.addChild(profileNode);

      profileNode.addChild(createTreeNodes(p.getTopLevelMethod()));
    }

    final MyTreeModel model = new MyTreeModel(rootNode, new MethodStatsHelper(container));

    final TreeItem<ITreeNode> root = model.toTreeItems();
    root.setExpanded(true);

    final TreeTableView<ITreeNode> treeTableView = new TreeTableView<>(root);

    for (int i = 0; i < model.getColumnCount(); i++) {
      final int columnNo = i;
      final TreeTableColumn<ITreeNode, String> newColumn =
          new TreeTableColumn<>(model.getColumnName(i));

      newColumn.setCellValueFactory(
          new Callback<
              TreeTableColumn.CellDataFeatures<ITreeNode, String>, ObservableValue<String>>() {

            @Override
            public ObservableValue<String> call(CellDataFeatures<ITreeNode, String> param) {
              return new ReadOnlyStringWrapper(
                  model.getValue(param.getValue().getValue(), columnNo));
            }
          });
      treeTableView.getColumns().add(newColumn);
    }

    treeTableView.setShowRoot(false);
    return treeTableView;
  }
  public Map<IPropertyName, Object> getChangeMap() {
    final Map<IPropertyName, Object> result = new HashMap<>();

    if (MathUtils.equals(treeTableColumn.getMinWidth(), originalSizing.getMinWidth()) == false) {
      result.put(minWidthName, treeTableColumn.getMinWidth());
    }
    if (MathUtils.equals(treeTableColumn.getPrefWidth(), originalSizing.getPrefWidth()) == false) {
      result.put(prefWidthName, treeTableColumn.getPrefWidth());
    }
    if (MathUtils.equals(treeTableColumn.getMaxWidth(), originalSizing.getMaxWidth()) == false) {
      result.put(maxWidthName, treeTableColumn.getMaxWidth());
    }
    return result;
  }
  @SuppressWarnings("unchecked")
  private void initializeTreeTableView() {
    treeTableView.setShowRoot(false); // don't show the root
    treeTableView.setEditable(true); // required for editable columns
    treeTableView.setTableMenuButtonVisible(true);

    // force resize policy for better default appearance
    treeTableView.setColumnResizePolicy(TreeTableView.CONSTRAINED_RESIZE_POLICY);

    final TreeTableColumn<Account, String> nameColumn =
        new TreeTableColumn<>(resources.getString("Column.Account"));
    nameColumn.setCellValueFactory(
        param -> new ReadOnlyStringWrapper(param.getValue().getValue().getName()));

    final TreeTableColumn<Account, Integer> entriesColumn =
        new TreeTableColumn<>(resources.getString("Column.Entries"));
    entriesColumn.setCellValueFactory(
        param ->
            new SimpleIntegerProperty(param.getValue().getValue().getTransactionCount())
                .asObject());

    final TreeTableColumn<Account, BigDecimal> balanceColumn =
        new TreeTableColumn<>(resources.getString("Column.Balance"));
    balanceColumn.setCellValueFactory(
        param ->
            new ReadOnlyObjectWrapper<>(
                AccountBalanceDisplayManager.convertToSelectedBalanceMode(
                    param.getValue().getValue().getAccountType(),
                    param.getValue().getValue().getTreeBalance())));
    balanceColumn.setCellFactory(cell -> new AccountCommodityFormatTreeTableCell());

    final TreeTableColumn<Account, BigDecimal> reconciledBalanceColumn =
        new TreeTableColumn<>(resources.getString("Column.ReconciledBalance"));
    reconciledBalanceColumn.setCellValueFactory(
        param ->
            new ReadOnlyObjectWrapper<>(
                AccountBalanceDisplayManager.convertToSelectedBalanceMode(
                    param.getValue().getValue().getAccountType(),
                    param.getValue().getValue().getReconciledTreeBalance())));
    reconciledBalanceColumn.setCellFactory(cell -> new AccountCommodityFormatTreeTableCell());

    final TreeTableColumn<Account, String> currencyColumn =
        new TreeTableColumn<>(resources.getString("Column.Currency"));
    currencyColumn.setCellValueFactory(
        param ->
            new ReadOnlyStringWrapper(param.getValue().getValue().getCurrencyNode().getSymbol()));

    final TreeTableColumn<Account, String> typeColumn =
        new TreeTableColumn<>(resources.getString("Column.Type"));
    typeColumn.setCellValueFactory(
        param ->
            new ReadOnlyStringWrapper(param.getValue().getValue().getAccountType().toString()));

    final TreeTableColumn<Account, Integer> codeColumn =
        new TreeTableColumn<>(resources.getString("Column.Code"));
    codeColumn.setEditable(true);
    codeColumn.setCellValueFactory(
        param ->
            new SimpleIntegerProperty(param.getValue().getValue().getAccountCode()).asObject());
    codeColumn.setCellFactory(param -> new IntegerEditingTreeTableCell());
    codeColumn.setOnEditCommit(
        event -> updateAccountCode(event.getRowValue().getValue(), event.getNewValue()));

    treeTableView
        .getColumns()
        .addAll(
            nameColumn,
            codeColumn,
            entriesColumn,
            balanceColumn,
            reconciledBalanceColumn,
            currencyColumn,
            typeColumn);

    restoreColumnVisibility();

    installListeners();
  }
  public TreeTableColumnResizer(TreeTableColumn<?, ?> treeTableColumn) {
    assert treeTableColumn != null;
    assert treeTableColumn.getTreeTableView() != null;

    this.treeTableColumn = treeTableColumn;
    this.originalSizing = new ColumnSizing(this.treeTableColumn);

    final List<?> columns;
    if (this.treeTableColumn.getParentColumn() != null) {
      columns = this.treeTableColumn.getParentColumn().getColumns();
    } else {
      columns = this.treeTableColumn.getTreeTableView().getColumns();
    }
    final int columnIndex = columns.indexOf(this.treeTableColumn);
    if (columnIndex + 1 < columns.size()) {
      this.treeTableColumnNext = (TreeTableColumn<?, ?>) columns.get(columnIndex + 1);
      this.originalSizingNext = new ColumnSizing(this.treeTableColumnNext);
    } else {
      this.treeTableColumnNext = null;
      this.originalSizingNext = null;
    }

    //
    //  Case #1 : treeTableColumnNext != null
    //
    //         x1                x2                       x3
    //       --+-----------------+------------------------+--
    //         |      col n      |         col n+1        |
    //         |                 |                        |
    //
    //       Range for moving x2 is [x1, x3]
    //
    //
    //  Case #2 : treeTableColumnNext == null
    //
    //      Case #2.1: treeTableColumn.getParentColumn() != null
    //
    //         x1                x2                       x3
    //       --+-----------------+                        |
    //         |      col n      |                        |
    //         |                 |                        |
    //                                               parentColumn maxX
    //
    //
    //      Case #2.2: treeTableColumn.getParentColumn() == null
    //
    //         x1                x2                       x3
    //       --+-----------------+                        |
    //         |      col n      |                        |
    //         |                 |                        |
    //                                               treeTableView maxX
    //
    //       Range for moving x2 is [x1, x3]
    //
    //

    final TreeTableViewDesignInfoX di = new TreeTableViewDesignInfoX();
    final Bounds columnBounds = di.getColumnBounds(treeTableColumn);
    x1 = columnBounds.getMinX();
    x2 = columnBounds.getMaxX();
    if (treeTableColumnNext != null) {
      final Bounds nextBounds = di.getColumnBounds(treeTableColumnNext);
      x3 = nextBounds.getMaxX();
    } else {
      if (treeTableColumn.getParentColumn() != null) {
        final TableColumnBase<?, ?> parentColumn =
            (TableColumnBase<?, ?>) this.treeTableColumn.getParentColumn();
        assert parentColumn instanceof TreeTableColumn<?, ?>;
        final Bounds parentBounds = di.getColumnBounds((TreeTableColumn<?, ?>) parentColumn);
        x3 = parentBounds.getMaxX();
      } else {
        final Bounds layoutBounds = treeTableColumn.getTreeTableView().getLayoutBounds();
        x3 = layoutBounds.getMaxX();
      }
    }
  }
 public void applyTo(TreeTableColumn<?, ?> tc) {
   tc.setMinWidth(minWidth);
   tc.setMaxWidth(maxWidth);
   tc.setPrefWidth(prefWidth);
 }
 public ColumnSizing(TreeTableColumn<?, ?> tc) {
   this.minWidth = tc.getMinWidth();
   this.maxWidth = tc.getMaxWidth();
   this.prefWidth = tc.getPrefWidth();
 }
  public void updateWidth(double dx) {

    // Clamp x2 + dx in [x1, x3]
    final double newX2 = Math.max(x1, Math.min(x3, x2 + dx));
    final double newWidth = newX2 - x1;
    final double newWidthNext = x3 - newX2;

    //        assert (newCellWidth+newNextWidth) ==
    // (downColWidths[colIndex]+downColWidths[colIndex+1]) :
    //                "newCellWidth+newNextWidth=" +  (newCellWidth+newNextWidth) + ", " +
    //                "downColWidths[colIndex]+downColWidths[colIndex+1]=" +
    //                (downColWidths[colIndex]+downColWidths[colIndex+1]);

    // Updates width of treeTableColumn
    treeTableColumn.setPrefWidth(newWidth);
    if (treeTableColumn.getMinWidth() == Region.USE_COMPUTED_SIZE) {
      treeTableColumn.setMinWidth(newWidth);
    } else {
      treeTableColumn.setMinWidth(Math.min(newWidth, treeTableColumn.getMinWidth()));
    }
    if (treeTableColumn.getMaxWidth() == Region.USE_COMPUTED_SIZE) {
      treeTableColumn.setMaxWidth(newWidth);
    } else {
      treeTableColumn.setMaxWidth(Math.max(newWidth, treeTableColumn.getMaxWidth()));
    }

    // Updates with of treeTableColumnNext
    if (treeTableColumnNext != null) {
      treeTableColumnNext.setPrefWidth(newWidthNext);
      if (treeTableColumnNext.getMinWidth() == Region.USE_COMPUTED_SIZE) {
        treeTableColumnNext.setMinWidth(newWidthNext);
      } else {
        treeTableColumnNext.setMinWidth(Math.min(newWidthNext, treeTableColumnNext.getMinWidth()));
      }
      if (treeTableColumnNext.getMaxWidth() == Region.USE_COMPUTED_SIZE) {
        treeTableColumnNext.setMaxWidth(newWidthNext);
      } else {
        treeTableColumnNext.setMaxWidth(Math.max(newWidthNext, treeTableColumnNext.getMaxWidth()));
      }
    }
  }