MoneyWiseTableSelect.java

/*
 * MoneyWise: Finance Application
 * Copyright 2012-2026. Tony Washer
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.  You may obtain a copy
 * of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package io.github.tonywasher.joceanus.moneywise.ui.base;

import java.util.Iterator;
import java.util.function.Predicate;

import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataItem;
import io.github.tonywasher.joceanus.tethys.api.table.TethysUITableManager;

/**
 * Table Selection control.
 *
 * @param <T> the item type
 */
public class MoneyWiseTableSelect<T extends PrometheusDataItem> {
    /**
     * The table.
     */
    private final TethysUITableManager<?, T> theTable;

    /**
     * The filter.
     */
    private final Predicate<T> theFilter;

    /**
     * The item panel.
     */
    private MoneyWiseItemPanel<T> theItemPanel;

    /**
     * The most recent selection.
     */
    private T theSelected;

    /**
     * Are we refreshing?
     */
    private boolean isRefreshing;

    /**
     * Constructor.
     *
     * @param pTable  the table
     * @param pFilter the filter
     */
    MoneyWiseTableSelect(final TethysUITableManager<?, T> pTable,
                         final Predicate<T> pFilter) {
        theTable = pTable;
        theFilter = pFilter;
    }

    /**
     * Declare item panel.
     *
     * @param pPanel the item panel
     */
    void declareItemPanel(final MoneyWiseItemPanel<T> pPanel) {
        theItemPanel = pPanel;
    }

    /**
     * Record the selected item.
     *
     * @param pItem the selected item
     */
    public void recordSelection(final T pItem) {
        /* Only handle if we are not refreshing */
        if (!isRefreshing && pItem != null) {
            /* Record the selection */
            theSelected = pItem;

            /* Notify the item panel */
            if (theItemPanel != null) {
                theItemPanel.setItem(pItem);
            }

            /* Scroll it into view */
            theTable.scrollSelectedToView();
        }
    }

    /**
     * Restore the selected item.
     */
    public void restoreSelected() {
        /* Determine the item to select */
        T myItem = theSelected;

        /* If we have a selected item */
        if (myItem != null) {
            /* Check whether the selected item was a new item that has now been discarded */
            final boolean isDiscarded = myItem.getOriginalValues().getVersion() > myItem.getList().getVersion();

            /* Check whether the item is now filtered out */
            final boolean isFiltered = theFilter != null && !theFilter.test(myItem);

            /* If it is no longer visible */
            if (isDiscarded || isFiltered) {
                /* locate the nearest item */
                myItem = findNearest();
            }

            /* Else nothing selected */
        } else {
            /* Find the first item */
            myItem = findFirst();
        }

        /* Select the item */
        theTable.selectRow(myItem);
    }

    /**
     * Update Table data.
     */
    public void updateTableData() {
        /* Update the table and restore the selected item */
        isRefreshing = true;
        theTable.fireTableDataChanged();
        isRefreshing = false;

        /* Restore the actual selection */
        restoreSelected();
    }

    /**
     * Find the first visible item.
     *
     * @return the first item
     */
    private T findFirst() {
        /* Access the view iterator */
        final Iterator<T> myIterator = theTable.viewIterator();
        while (myIterator.hasNext()) {
            final T myItem = myIterator.next();

            /* Return first non-header */
            if (!myItem.isHeader()) {
                return myItem;
            }
        }

        /* None found */
        return null;
    }

    /**
     * Find the nearest visible item.
     *
     * @return the nearest item
     */
    private T findNearest() {
        /* The most recent item */
        T myBest = null;

        /* Access the view iterator */
        final Iterator<T> myIterator = theTable.viewIterator();
        while (myIterator.hasNext()) {
            final T myItem = myIterator.next();

            /* Ignore header */
            if (myItem.isHeader()) {
                continue;
            }

            /* If we have zoomed past the selected item */
            if (myItem.compareTo(theSelected) > 0) {
                return myBest == null ? myItem : myBest;
            }

            /* Note the nearest item */
            myBest = myItem;
        }

        /* Return the best item */
        return myBest;
    }
}