package de.duehl.swing.ui.elements;

/*
 * Copyright 2017 Christian Dühl. All rights reserved.
 *
 * This program is free software. You can redistribute it and/or
 * modify it under the same terms as perl:
 *
 * general:  http://dev.perl.org/licenses/
 * GPL:      http://dev.perl.org/licenses/gpl1.html
 * artistic: http://dev.perl.org/licenses/artistic.html
 */

import java.awt.Dimension;
import java.awt.Rectangle;

import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JViewport;
import javax.swing.Scrollable;

/**
 * Diese Klasse stellt ein JPanel dar, dessen Scrollverhalten beeinflussbar ist.
 *
 * @version 1.01     2017-07-04
 * @author Christian Dühl
 */

public class ScrollablePanel extends JPanel implements Scrollable {

    private static final long serialVersionUID = -7108399478783026020L;

    /** Wert an Einheiten, um den beim Scrollen gescrollt wird. */
    private int unitIncrement = 25;

    /** Wert an Blöcken, um den beim Scrollen gescrollt wird. */
    private int blockIncrement = 25;

    /** Gibt an, ob horizontal gescrollt werden darf. */
    private boolean scrollHorizontal = true;

    /** Gibt an, ob vertikal gescrollt werden darf. */
    private boolean scrollVertical = true;

    /** Getter für den Wert an Einheiten, um den beim Scrollen gescrollt wird. */
    public int getUnitIncrement() {
        return unitIncrement;
    }

    /** Setter für den Wert an Einheiten, um den beim Scrollen gescrollt wird. */
    public void setUnitIncrement(int unitIncrement) {
        this.unitIncrement = unitIncrement;
    }

    /** Getter für den Wert an Blöcken, um den beim Scrollen gescrollt wird. */
    public int getBlockIncrement() {
        return blockIncrement;
    }

    /** Setter für den Wert an Blöcken, um den beim Scrollen gescrollt wird. */
    public void setBlockIncrement(int blockIncrement) {
        this.blockIncrement = blockIncrement;
    }

    /** Gibt an, ob horizontal gescrollt werden darf. */
    public boolean isScrollHorizontal() {
        return scrollHorizontal;
    }

    /** Legt fest, ob horizontal gescrollt werden darf. */
    public void setScrollHorizontal(boolean scrollHorizontal) {
        this.scrollHorizontal = scrollHorizontal;
    }

    /** Gibt an, ob vertikal gescrollt werden darf. */
    public boolean isScrollVertical() {
        return scrollVertical;
    }

    /** Legt fest, ob vertikal gescrollt werden darf. */
    public void setScrollVertical(boolean scrollVertical) {
        this.scrollVertical = scrollVertical;
    }

    /**
     * Returns the preferred size of the viewport for a view component. For example, the preferred
     * size of a <code>JList</code> component is the size required to accommodate all of the cells
     * in its list. However, the value of <code>preferredScrollableViewportSize</code> is the size
     * required for <code>JList.getVisibleRowCount</code> rows. A component without any properties
     * that would affect the viewport size should just return <code>getPreferredSize</code> here.
     *
     * @return the preferredSize of a <code>JViewport</code> whose view is this
     *         <code>Scrollable</code>
     * @see JViewport#getPreferredSize
     */
    @Override
    public Dimension getPreferredScrollableViewportSize() {
        return getPreferredSize();
    }

    /**
     * Components that display logical rows or columns should compute the scroll increment that
     * will completely expose one new row or column, depending on the value of orientation.
     * Ideally, components should handle a partially exposed row or column by returning the
     * distance required to completely expose the item. <p>
     *
     * Scrolling containers, like JScrollPane, will use this method each time the user requests a
     * unit scroll.
     *
     * @param visibleRect
     *            The view area visible within the viewport
     * @param orientation
     *            Either SwingConstants.VERTICAL or SwingConstants.HORIZONTAL.
     * @param direction
     *            Less than zero to scroll up/left, greater than zero for down/right.
     * @return The "unit" increment for scrolling in the specified direction. This value should
     *         always be positive.
     * @see JScrollBar#setUnitIncrement
     */
    @Override
    public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
            int direction) {
        return unitIncrement;
    }

    /**
     * Components that display logical rows or columns should compute the scroll increment that
     * will completely expose one block of rows or columns, depending on the value of orientation. <p>
     *
     * Scrolling containers, like JScrollPane, will use this method each time the user requests a
     * block scroll.
     *
     * @param visibleRect
     *            The view area visible within the viewport
     * @param orientation
     *            Either SwingConstants.VERTICAL or SwingConstants.HORIZONTAL.
     * @param direction
     *            Less than zero to scroll up/left, greater than zero for down/right.
     * @return The "block" increment for scrolling in the specified direction. This value should
     *         always be positive.
     * @see JScrollBar#setBlockIncrement
     */
    @Override
    public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation,
            int direction) {
        return blockIncrement;
    }

    /**
     * Return true if a viewport should always force the width of this <code>Scrollable</code> to
     * match the width of the viewport. For example a normal text view that supported line wrapping
     * would return true here, since it would be undesirable for wrapped lines to disappear beyond
     * the right edge of the viewport. Note that returning true for a Scrollable whose ancestor is
     * a JScrollPane effectively disables horizontal scrolling. <p>
     *
     * Scrolling containers, like JViewport, will use this method each time they are validated.
     *
     * @return True if a viewport should force the Scrollables width to match its own.
     */
    @Override
    public boolean getScrollableTracksViewportWidth() {
        return !scrollHorizontal;
    }

    /**
     * Return true if a viewport should always force the height of this Scrollable to match the
     * height of the viewport. For example a columnar text view that flowed text in left to right
     * columns could effectively disable vertical scrolling by returning true here. <p>
     *
     * Scrolling containers, like JViewport, will use this method each time they are validated.
     *
     * @return True if a viewport should force the Scrollables height to match its own.
     */
    @Override
    public boolean getScrollableTracksViewportHeight() {
        return !scrollVertical;
    }

    /** Scrollt nach oben / vorne. */
    public void scrollToTop() {
        Rectangle topRect = new Rectangle(0, 0, 1, 1);
        this.scrollRectToVisible(topRect );
    }

}
