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

org.pushingpixels.radiance.component.internal.ui.common.BasicCommandButtonPanelUI Maven / Gradle / Ivy

/*
 * Copyright (c) 2005-2022 Radiance Kirill Grouchnikov. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  o Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  o Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 *  o Neither the name of the copyright holder nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.pushingpixels.radiance.component.internal.ui.common;

import org.pushingpixels.radiance.component.api.common.JCommandButton;
import org.pushingpixels.radiance.component.api.common.JCommandButtonPanel;
import org.pushingpixels.radiance.component.api.common.model.CommandPanelPresentationModel;
import org.pushingpixels.radiance.theming.api.RadianceThemingCortex;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.UIResource;
import java.awt.*;

/**
 * Basic UI for command button panel {@link JCommandButtonPanel}.
 *
 * @author Kirill Grouchnikov
 */
public abstract class BasicCommandButtonPanelUI extends CommandButtonPanelUI {
    /**
     * Client property to mark the command button panel to not draw the background fill
     */
    public static final String SKIP_BACKGROUND_FILL =
            "radiance.component.internal.commandButtonPanel.ui.skipBackgroundFill";

    /**
     * The associated command button panel.
     */
    protected JCommandButtonPanel buttonPanel;

    /**
     * Labels of the button panel groups.
     */
    protected JLabel[] groupLabels;

    /**
     * Bounds of button panel groups.
     */
    private Rectangle[] groupRects;

    private ChangeListener contentModelChangeListener;

    private ChangeListener presentationModelChangeListener;

    private CommandButtonPanelLayout layoutManager;

    /**
     * Default insets of button panel groups.
     */
    protected static final Insets GROUP_INSETS = new Insets(4, 4, 4, 4);

    @Override
    public void installUI(JComponent c) {
        this.buttonPanel = (JCommandButtonPanel) c;

        installDefaults();
        installComponents();
        installListeners();
    }

    /**
     * Installs defaults on the associated button panel.
     */
    protected void installDefaults() {
        this.updateLayoutManager();
        Font currFont = this.buttonPanel.getFont();
        if ((currFont == null) || (currFont instanceof UIResource)) {
            Font controlFont = RadianceThemingCortex.GlobalScope.getFontPolicy()
                    .getFontSet().getControlFont();
            this.buttonPanel.setFont(controlFont.deriveFont(Font.BOLD, controlFont.getSize() + 1));
        }
    }

    /**
     * Installs sub-components on the associated button panel.
     */
    protected void installComponents() {
        this.recomputeGroupHeaders();
    }

    /**
     * Installs listeners on the associated button panel.
     */
    protected void installListeners() {
        this.presentationModelChangeListener = (ChangeEvent event) ->
                SwingUtilities.invokeLater(() -> {
                    if (buttonPanel != null) {
                        recomputeGroupHeaders();

                        int groupCount = (groupLabels != null) ? groupLabels.length : 0;

                        CommandPanelPresentationModel presentationModel =
                                buttonPanel.getProjection().getPresentationModel();
                        for (int i = 0; i < groupCount; i++) {
                            for (JCommandButton button :
                                    buttonPanel.getGroupButtons(i)) {
                                button.setIconDimension(
                                        presentationModel.getCommandIconDimension());
                                button.setPresentationState(
                                        presentationModel.getCommandPresentationState());
                            }
                        }

                        updateLayoutManager();
                        buttonPanel.revalidate();
                        buttonPanel.doLayout();
                    }
                });
        this.buttonPanel.getProjection().getPresentationModel().addChangeListener(
                this.presentationModelChangeListener);

        this.contentModelChangeListener = changeEvent ->
                SwingUtilities.invokeLater(() -> {
                    if (buttonPanel != null) {
                        recomputeGroupHeaders();
                        buttonPanel.revalidate();
                        buttonPanel.doLayout();
                    }
                });
        this.buttonPanel.getProjection().getContentModel().addChangeListener(this.contentModelChangeListener);
    }

    @Override
    public void uninstallUI(JComponent c) {
        c.setLayout(null);

        uninstallListeners();
        uninstallComponents();
        uninstallDefaults();
        this.buttonPanel = null;
    }

    /**
     * Uninstalls defaults from the associated button panel.
     */
    protected void uninstallDefaults() {
    }

    /**
     * Uninstalls sub-components from the associated button panel.
     */
    protected void uninstallComponents() {
        if (this.groupLabels != null) {
            for (JLabel groupLabel : this.groupLabels) {
                this.buttonPanel.remove(groupLabel);
            }
        }
    }

    /**
     * Uninstalls listeners from the associated button panel.
     */
    protected void uninstallListeners() {
        this.buttonPanel.getProjection().getPresentationModel().removeChangeListener(
                this.presentationModelChangeListener);
        this.presentationModelChangeListener = null;

        this.buttonPanel.getProjection().getContentModel().removeChangeListener(
                this.contentModelChangeListener);
        this.contentModelChangeListener = null;
    }

    /**
     * Updates the layout manager for the associated button panel.
     */
    private void updateLayoutManager() {
        CommandPanelPresentationModel panelPresentationModel =
                this.buttonPanel.getProjection().getPresentationModel();
        if ((panelPresentationModel != null)
                && (panelPresentationModel.getLayoutKind() ==
                CommandPanelPresentationModel.LayoutKind.COLUMN_FILL)) {
            this.layoutManager = new ColumnFillLayout();
        } else {
            this.layoutManager = new RowFillLayout();
        }
        this.buttonPanel.setLayout(this.layoutManager);
    }

    @Override
    public void paint(Graphics g, JComponent c) {
        boolean skipBackgroundFill = Boolean.TRUE.equals(
                this.buttonPanel.getClientProperty(SKIP_BACKGROUND_FILL));

        if (!skipBackgroundFill) {
            Color bg = this.buttonPanel.getBackground();
            g.setColor(bg);
            g.fillRect(0, 0, c.getWidth(), c.getHeight());
        }

        int groupCount = (groupLabels != null) ? groupLabels.length : 0;

        for (int i = 0; i < groupCount; i++) {
            Rectangle groupRect = this.groupRects[i];
            if (groupRect == null) {
                continue;
            }
            if (!skipBackgroundFill) {
                this.paintGroupBackground(g, i, groupRect.x, groupRect.y, groupRect.width,
                        groupRect.height);
            }

            if (this.groupLabels[i].isVisible()) {
                Rectangle groupTitleBackground = this.groupLabels[i].getBounds();
                this.paintGroupTitleBackground(g, i, groupRect.x,
                        groupTitleBackground.y - getGroupInsets().top, groupRect.width,
                        groupTitleBackground.height + getGroupInsets().top + getLayoutGap());
            }
        }
    }

    /**
     * Paints the background of the specified button panel group.
     *
     * @param g          Graphics context.
     * @param groupIndex Group index.
     * @param x          X coordinate of the button group bounds.
     * @param y          Y coordinate of the button group bounds.
     * @param width      Width of the button group bounds.
     * @param height     Height of the button group bounds.
     */
    protected abstract void paintGroupBackground(Graphics g, int groupIndex, int x, int y,
            int width, int height);

    /**
     * Paints the background of the title of specified button panel group.
     *
     * @param g          Graphics context.
     * @param groupIndex Group index.
     * @param x          X coordinate of the button group title bounds.
     * @param y          Y coordinate of the button group title bounds.
     * @param width      Width of the button group title bounds.
     * @param height     Height of the button group title bounds.
     */
    protected abstract void paintGroupTitleBackground(Graphics g, int groupIndex, int x, int y,
            int width, int height);

    /**
     * Returns the height of the group title strip.
     *
     * @param groupIndex Group index.
     * @return The height of the title strip of the specified group.
     */
    protected abstract int getGroupTitleHeight(int groupIndex);

    /**
     * Returns the insets of button panel groups.
     *
     * @return The insets of button panel groups.
     */
    protected abstract Insets getGroupInsets();

    private abstract static class CommandButtonPanelLayout implements LayoutManager {
        protected int commandButtonGridRowCount = -1;
        protected int commandButtonGridColumnCount = -1;
        protected JCommandButton[][] commandButtonGrid;

        protected void setCommandButtonGridSize(int rowCount, int columnCount) {
            if ((this.commandButtonGrid == null) ||
                    (this.commandButtonGridRowCount != rowCount) ||
                    (this.commandButtonGridColumnCount != columnCount)) {
                this.commandButtonGridRowCount = rowCount;
                this.commandButtonGridColumnCount = columnCount;
                this.commandButtonGrid =
                        new JCommandButton[this.commandButtonGridRowCount][this.commandButtonGridColumnCount];
            }
        }
    }

    /**
     * Row-fill layout for the button panel.
     *
     * @author Kirill Grouchnikov
     */
    protected class RowFillLayout extends CommandButtonPanelLayout {
        @Override
        public void addLayoutComponent(String name, Component comp) {
        }

        @Override
        public void removeLayoutComponent(Component comp) {
        }

        @Override
        public void layoutContainer(Container parent) {
            if ((parent.getWidth() <= 0) || (parent.getHeight() <= 0)) {
                return;
            }

            Insets bInsets = parent.getInsets();
            Insets groupInsets = getGroupInsets();
            int left = bInsets.left;
            int right = bInsets.right;

            int y = bInsets.top;

            JCommandButtonPanel panel = (JCommandButtonPanel) parent;
            boolean ltr = panel.getComponentOrientation().isLeftToRight();

            // compute max width of buttons
            int maxButtonWidth = 0;
            int maxButtonHeight = 0;
            int groupCount = (groupLabels != null) ? groupLabels.length : 0;

            for (int i = 0; i < groupCount; i++) {
                for (JCommandButton button : panel.getGroupButtons(i)) {
                    maxButtonWidth = Math.max(maxButtonWidth, button.getPreferredSize().width);
                    maxButtonHeight = Math.max(maxButtonHeight, button.getPreferredSize().height);
                }
            }
            groupRects = new Rectangle[groupCount];

            int gap = getLayoutGap();
            int maxWidth = parent.getWidth() - bInsets.left - bInsets.right - groupInsets.left
                    - groupInsets.right;
            // for N buttons, there are N-1 gaps. Add the gap to the
            // available width and divide by the max button width + gap.
            int buttonsInRow = (maxButtonWidth == 0) ? 0
                    : (maxWidth + gap) / (maxButtonWidth + gap);
            int maxButtonColumnsToUse = panel.getProjection().getPresentationModel()
                    .getMaxColumns();
            if (maxButtonColumnsToUse > 0) {
                buttonsInRow = Math.min(buttonsInRow, maxButtonColumnsToUse);
            }
            // Never end up with zero buttons in row
            buttonsInRow = Math.max(buttonsInRow, 1);

            int totalRowCount = 0;
            for (int i = 0; i < groupCount; i++) {
                int buttonRows = (int) (Math.ceil((double) panel.getGroupButtons(i).size() / buttonsInRow));
                totalRowCount += buttonRows;
            }

            this.setCommandButtonGridSize(totalRowCount, buttonsInRow);

            if (totalRowCount == 0) {
                return;
            }

            int currRowIndex = 0;
            for (int i = 0; i < groupCount; i++) {
                int topGroupY = y;
                y += groupInsets.top;

                JLabel groupLabel = groupLabels[i];
                if (buttonPanel.getProjection().getPresentationModel().isToShowGroupLabels()) {
                    int labelWidth = groupLabel.getPreferredSize().width;
                    int labelHeight = getGroupTitleHeight(i);
                    if (groupLabel.getComponentOrientation().isLeftToRight()) {
                        groupLabel.setBounds(left + groupInsets.left, y, labelWidth, labelHeight);
                    } else {
                        groupLabel.setBounds(
                                parent.getWidth() - right - groupInsets.right - labelWidth, y,
                                labelWidth, labelHeight);
                    }
                    y += labelHeight + gap;
                }

                int buttonRows = (int) (Math.ceil((double) panel.getGroupButtons(i).size() / buttonsInRow));
                if (maxButtonColumnsToUse > 0) {
                    buttonsInRow = Math.min(buttonsInRow, maxButtonColumnsToUse);
                }

                // spread the buttons so that we don't have extra space
                // on the right
                int actualButtonWidth = (buttonRows > 1)
                        ? (maxWidth - (buttonsInRow - 1) * gap) / buttonsInRow
                        : maxButtonWidth;
                if (maxButtonColumnsToUse == 1) {
                    actualButtonWidth = maxWidth;
                }

                if (ltr) {
                    int currX = left + groupInsets.left;
                    int currColumnIndex = 0;
                    for (JCommandButton button : panel.getGroupButtons(i)) {
                        int endX = currX + actualButtonWidth;
                        if (endX > (parent.getWidth() - right - groupInsets.right)) {
                            currRowIndex++;
                            currColumnIndex = 0;
                            currX = left + groupInsets.left;
                            y += maxButtonHeight;
                            y += gap;
                        }
                        button.setBounds(currX, y, actualButtonWidth, maxButtonHeight);
                        this.commandButtonGrid[currRowIndex][currColumnIndex] = button;
                        currColumnIndex++;
                        currX += actualButtonWidth;
                        currX += gap;
                    }
                } else {
                    int currX = parent.getWidth() - right - groupInsets.right;
                    int currColumnIndex = buttonsInRow - 1;
                    for (JCommandButton button : panel.getGroupButtons(i)) {
                        int startX = currX - actualButtonWidth;
                        if (startX < (left + groupInsets.left)) {
                            currRowIndex++;
                            currColumnIndex = buttonsInRow - 1;
                            currX = parent.getWidth() - right - groupInsets.right;
                            y += maxButtonHeight;
                            y += gap;
                        }
                        button.setBounds(currX - actualButtonWidth, y, actualButtonWidth,
                                maxButtonHeight);
                        this.commandButtonGrid[currRowIndex][currColumnIndex] = button;
                        currColumnIndex--;
                        currX -= actualButtonWidth;
                        currX -= gap;
                    }
                }

                y += maxButtonHeight + groupInsets.bottom;
                currRowIndex++;
                int bottomGroupY = y;
                groupRects[i] = new Rectangle(left, topGroupY, (parent.getWidth() - left - right),
                        (bottomGroupY - topGroupY));
            }
        }

        @Override
        public Dimension minimumLayoutSize(Container parent) {
            return new Dimension(20, 20);
        }

        @Override
        public Dimension preferredLayoutSize(Container parent) {
            JCommandButtonPanel panel = (JCommandButtonPanel) parent;

            int maxButtonColumnsToUse = panel.getProjection().getPresentationModel()
                    .getMaxColumns();

            Insets bInsets = parent.getInsets();
            Insets groupInsets = getGroupInsets();
            int insetsWidth = bInsets.left + groupInsets.left + bInsets.right + groupInsets.right;

            // compute max width of buttons
            int maxButtonWidth = 0;
            int maxButtonHeight = 0;
            int groupCount = (groupLabels != null) ? groupLabels.length : 0;
            for (int i = 0; i < groupCount; i++) {
                for (JCommandButton button : panel.getGroupButtons(i)) {
                    maxButtonWidth = Math.max(maxButtonWidth, button.getPreferredSize().width);
                    maxButtonHeight = Math.max(maxButtonHeight, button.getPreferredSize().height);
                }
            }

            // total height
            int gap = getLayoutGap();
            boolean usePanelWidth = (maxButtonColumnsToUse <= 0);
            int availableWidth = panel.getWidth();
            availableWidth -= insetsWidth;

            if (usePanelWidth) {
                // this hasn't been set. Compute using the available width
                maxButtonColumnsToUse = (availableWidth + gap) / (maxButtonWidth + gap);
                // Never end up with zero buttons in row
                maxButtonColumnsToUse = Math.max(1, maxButtonColumnsToUse);
            }
            int height = bInsets.top + bInsets.bottom;
            for (int i = 0; i < groupCount; i++) {
                if (groupLabels[i].isVisible()) {
                    height += (getGroupTitleHeight(i) + gap);
                }

                height += (groupInsets.top + groupInsets.bottom);

                int buttonRows = (int) (Math.ceil((double) panel.getGroupButtons(i).size()
                        / maxButtonColumnsToUse));
                height += buttonRows * maxButtonHeight + (buttonRows - 1) * gap;
            }
            int prefWidth = usePanelWidth ? availableWidth
                    : maxButtonColumnsToUse * maxButtonWidth + (maxButtonColumnsToUse - 1) * gap
                    + bInsets.left + bInsets.right + groupInsets.left + groupInsets.right;
            return new Dimension(Math.max(10, prefWidth), Math.max(10, height));
        }
    }

    /**
     * Column-fill layout for the button panel.
     *
     * @author Kirill Grouchnikov
     */
    protected class ColumnFillLayout extends CommandButtonPanelLayout {
        @Override
        public void addLayoutComponent(String name, Component comp) {
        }

        @Override
        public void removeLayoutComponent(Component comp) {
        }

        @Override
        public void layoutContainer(Container parent) {
            Insets bInsets = parent.getInsets();
            Insets groupInsets = getGroupInsets();
            int top = bInsets.top;
            int bottom = bInsets.bottom;

            JCommandButtonPanel panel = (JCommandButtonPanel) parent;
            boolean ltr = panel.getComponentOrientation().isLeftToRight();

            // compute max width of buttons
            int maxButtonWidth = 0;
            int maxButtonHeight = 0;
            int groupCount = panel.getGroupCount();
            for (int i = 0; i < groupCount; i++) {
                for (JCommandButton button : panel.getGroupButtons(i)) {
                    maxButtonWidth = Math.max(maxButtonWidth, button.getPreferredSize().width);
                    maxButtonHeight = Math.max(maxButtonHeight, button.getPreferredSize().height);
                }
            }
            groupRects = new Rectangle[groupCount];

            int gap = getLayoutGap();
            int maxHeight = parent.getHeight() - bInsets.top - bInsets.bottom - groupInsets.top
                    - groupInsets.bottom;
            // for N buttons, there are N-1 gaps. Add the gap to the
            // available width and divide by the max button width + gap.
            int buttonsInColumn = (maxButtonHeight == 0) ? 0
                    : (maxHeight + gap) / (maxButtonHeight + gap);

            int totalColumnCount = 0;
            for (int i = 0; i < groupCount; i++) {
                int buttonColumns = (buttonsInColumn == 0) ? 0
                        : (int) (Math.ceil((double) panel.getGroupButtons(i).size()
                        / buttonsInColumn));
                totalColumnCount += buttonColumns;
            }

            this.setCommandButtonGridSize(buttonsInColumn, totalColumnCount);

            if (totalColumnCount == 0) {
                return;
            }

            if (ltr) {
                int x = bInsets.left + groupInsets.left;
                int currColumnIndex = 0;
                for (int i = 0; i < groupCount; i++) {
                    int leftGroupX = x;
                    x += groupInsets.left;
                    int currY = top + groupInsets.top;

                    int buttonColumns = (buttonsInColumn == 0) ? 0
                            : (int) (Math.ceil((double) panel.getGroupButtons(i).size()
                            / buttonsInColumn));
                    // spread the buttons so that we don't have extra space
                    // on the bottom
                    int actualButtonHeight = (buttonColumns > 1)
                            ? (maxHeight - (buttonsInColumn - 1) * gap) / buttonsInColumn
                            : maxButtonWidth;

                    int currRowIndex = 0;
                    for (JCommandButton button : panel.getGroupButtons(i)) {
                        int endY = currY + actualButtonHeight;
                        if (endY > (parent.getHeight() - bottom - groupInsets.bottom)) {
                            currY = top + groupInsets.top;
                            currRowIndex = 0;
                            currColumnIndex++;
                            x += maxButtonWidth;
                            x += gap;
                        }
                        button.setBounds(x, currY, maxButtonWidth, actualButtonHeight);
                        this.commandButtonGrid[currRowIndex][currColumnIndex] = button;
                        currRowIndex++;
                        currY += actualButtonHeight;
                        currY += gap;
                    }
                    x += maxButtonWidth + groupInsets.bottom;
                    currColumnIndex++;
                    int rightGroupX = x;
                    groupRects[i] = new Rectangle(leftGroupX, top, (rightGroupX - leftGroupX),
                            (parent.getHeight() - top - bottom));
                }
            } else {
                int x = panel.getWidth() - bInsets.right - groupInsets.right;
                int currColumnIndex = this.commandButtonGridColumnCount - 1;
                for (int i = 0; i < groupCount; i++) {
                    int rightGroupX = x;
                    x -= groupInsets.left;
                    int currY = top + groupInsets.top;

                    int buttonColumns = (buttonsInColumn == 0) ? 0
                            : (int) (Math.ceil((double) panel.getGroupButtons(i).size()
                            / buttonsInColumn));
                    // spread the buttons so that we don't have extra space
                    // on the bottom
                    int actualButtonHeight = (buttonColumns > 1)
                            ? (maxHeight - (buttonsInColumn - 1) * gap) / buttonsInColumn
                            : maxButtonWidth;

                    int currRowIndex = 0;
                    for (JCommandButton button : panel.getGroupButtons(i)) {
                        int endY = currY + actualButtonHeight;
                        if (endY > (parent.getHeight() - bottom - groupInsets.bottom)) {
                            currY = top + groupInsets.top;
                            currRowIndex = 0;
                            currColumnIndex--;
                            x -= maxButtonWidth;
                            x -= gap;
                        }
                        button.setBounds(x - maxButtonWidth, currY, maxButtonWidth,
                                actualButtonHeight);
                        this.commandButtonGrid[currRowIndex][currColumnIndex] = button;
                        currRowIndex++;
                        currY += actualButtonHeight;
                        currY += gap;
                    }
                    x -= (maxButtonWidth + groupInsets.bottom);
                    currColumnIndex--;
                    int leftGroupX = x;
                    groupRects[i] = new Rectangle(leftGroupX, top, (rightGroupX - leftGroupX),
                            (parent.getHeight() - top - bottom));
                }
            }
        }

        @Override
        public Dimension minimumLayoutSize(Container parent) {
            return new Dimension(20, 20);
        }

        @Override
        public Dimension preferredLayoutSize(Container parent) {
            JCommandButtonPanel panel = (JCommandButtonPanel) parent;

            int maxButtonRowsToUse = panel.getProjection().getPresentationModel().getMaxRows();

            Insets bInsets = parent.getInsets();
            Insets groupInsets = getGroupInsets();
            int insetsHeight = bInsets.top + groupInsets.top + bInsets.bottom + groupInsets.bottom;

            // compute max width of buttons
            int maxButtonWidth = 0;
            int maxButtonHeight = 0;
            int groupCount = panel.getGroupCount();
            for (int i = 0; i < groupCount; i++) {
                for (JCommandButton button : panel.getGroupButtons(i)) {
                    maxButtonWidth = Math.max(maxButtonWidth, button.getPreferredSize().width);
                    maxButtonHeight = Math.max(maxButtonHeight, button.getPreferredSize().height);
                }
            }

            // total width
            int gap = getLayoutGap();
            boolean usePanelHeight = (maxButtonRowsToUse <= 0);

            int availableHeight = panel.getHeight();
            availableHeight -= insetsHeight;
            if (usePanelHeight) {
                // this hasn't been set. Compute using the available
                // height
                maxButtonRowsToUse = (availableHeight + gap) / (maxButtonHeight + gap);
            }
            // go over all groups and see how many columns each one needs
            int width = bInsets.left + bInsets.right;
            for (int i = 0; i < groupCount; i++) {
                width += (groupInsets.left + groupInsets.right);

                int buttonColumns = (int) (Math.ceil((double) panel.getGroupButtons(i).size()
                        / maxButtonRowsToUse));
                width += buttonColumns * maxButtonWidth + (buttonColumns - 1) * gap;
            }
            int prefHeight = usePanelHeight ? availableHeight
                    : maxButtonRowsToUse * maxButtonWidth + (maxButtonRowsToUse - 1) * gap
                    + bInsets.top + bInsets.bottom + groupInsets.top + groupInsets.bottom;
            return new Dimension(Math.max(10, width), Math.max(10, prefHeight));
        }
    }

    /**
     * Returns the layout gap for button panel components.
     *
     * @return The layout gap for button panel components.
     */
    protected int getLayoutGap() {
        return 4;
    }

    /**
     * Recomputes the components for button group headers.
     */
    private void recomputeGroupHeaders() {
        if (this.groupLabels != null) {
            for (JLabel groupLabel : this.groupLabels) {
                this.buttonPanel.remove(groupLabel);
            }
        }

        int groupCount = this.buttonPanel.getGroupCount();
        this.groupLabels = new JLabel[groupCount];
        for (int i = 0; i < groupCount; i++) {
            this.groupLabels[i] = new JLabel(this.buttonPanel.getGroupTitleAt(i));
            this.groupLabels[i].setComponentOrientation(this.buttonPanel.getComponentOrientation());

            this.buttonPanel.add(this.groupLabels[i]);

            this.groupLabels[i].setVisible(
                    this.buttonPanel.getProjection().getPresentationModel().isToShowGroupLabels());
        }
    }

    /**
     * Returns the preferred size of the associated button panel for the specified parameters.
     *
     * @param buttonVisibleRows Target number of visible button rows.
     * @param titleVisibleRows  Target number of visible group title rows.
     * @return The preferred size of the associated button panel for the specified parameters.
     */
    public int getPreferredHeight(int buttonVisibleRows, int titleVisibleRows) {
        Insets bInsets = this.buttonPanel.getInsets();
        Insets groupInsets = getGroupInsets();
        int maxButtonHeight = 0;
        int groupCount = this.buttonPanel.getGroupCount();
        for (int i = 0; i < groupCount; i++) {
            for (JCommandButton button : this.buttonPanel.getGroupButtons(i)) {
                maxButtonHeight = Math.max(maxButtonHeight, button.getPreferredSize().height);
            }
        }

        // total height
        int gap = getLayoutGap();

        // panel insets
        int totalHeight = bInsets.top + bInsets.bottom;
        // height of icon rows
        totalHeight += buttonVisibleRows * maxButtonHeight;
        // gaps between icon rows
        totalHeight += (buttonVisibleRows - 1) * gap;
        // title height
        totalHeight += titleVisibleRows * getGroupTitleHeight(0);
        // title insets
        totalHeight += (titleVisibleRows - 1) * (groupInsets.top + groupInsets.bottom);

        return totalHeight;
    }

    private JCommandButton findFirstFocusableRight(int row, int column) {
        int currColumn = column + 1;
        while (currColumn < this.layoutManager.commandButtonGridColumnCount) {
            JCommandButton currButton =
                    this.layoutManager.commandButtonGrid[row][currColumn];
            if ((currButton != null) && currButton.isFocusable()) {
                return currButton;
            }
            currColumn++;
        }
        return null;
    }

    private JCommandButton findLastFocusableLeft(int row, int column) {
        int currColumn = column - 1;
        while (currColumn >= 0) {
            JCommandButton currButton =
                    this.layoutManager.commandButtonGrid[row][currColumn];
            if ((currButton != null) && currButton.isFocusable()) {
                return currButton;
            }
            currColumn--;
        }
        return null;
    }

    private JCommandButton findLastFocusableUp(int row, int column) {
        int currRow = row - 1;
        while (currRow >= 0) {
            JCommandButton currButton =
                    this.layoutManager.commandButtonGrid[currRow][column];
            if ((currButton != null) && currButton.isFocusable()) {
                return currButton;
            }
            currRow--;
        }
        return null;
    }

    private JCommandButton findFirstFocusableDown(int row, int column) {
        int currRow = row + 1;
        while (currRow < this.layoutManager.commandButtonGridRowCount) {
            JCommandButton currButton =
                    this.layoutManager.commandButtonGrid[currRow][column];
            if ((currButton != null) && currButton.isFocusable()) {
                return currButton;
            }
            currRow++;
        }
        return null;
    }

    @Override
    public boolean focusFirst() {
        JCommandButton first = this.findFirstFocusableRight(0, -1);
        if (first != null) {
            first.requestFocus();
            this.buttonPanel.scrollRectToVisible(first.getBounds());
            return true;
        }
        return false;
    }

    @Override
    public boolean focusLast() {
        JCommandButton last = this.findLastFocusableLeft(
                this.layoutManager.commandButtonGridRowCount - 1,
                this.layoutManager.commandButtonGridColumnCount);
        if (last != null) {
            last.requestFocus();
            this.buttonPanel.scrollRectToVisible(last.getBounds());
            return true;
        }
        return false;
    }

    private enum FocusMoveDirection {
        UP, DOWN, LEFT, RIGHT
    }

    @Override
    public boolean hasFocus() {
        for (int row = 0; row < this.layoutManager.commandButtonGridRowCount; row++) {
            for (int column = 0; column < this.layoutManager.commandButtonGridColumnCount; column++) {
                JCommandButton button = this.layoutManager.commandButtonGrid[row][column];
                if ((button != null) && button.hasFocus()) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean focusMove(FocusMoveDirection focusMoveDirection) {
        int focusedRow = -1;
        int focusedColumn = -1;
        for (int row = 0; row < this.layoutManager.commandButtonGridRowCount; row++) {
            for (int column = 0; column < this.layoutManager.commandButtonGridColumnCount; column++) {
                JCommandButton button = this.layoutManager.commandButtonGrid[row][column];
                if ((button != null) && button.hasFocus()) {
                    focusedRow = row;
                    focusedColumn = column;
                }
            }
        }

        if (focusedRow < 0) {
            return false;
//            switch (focusMoveDirection) {
//                case DOWN:
//                    return this.focusFirst();
//                case UP:
//                    return this.focusLast();
//                default:
//                    return false;
//            }
        }

        switch (focusMoveDirection) {
            case DOWN:
                JCommandButton nextDown = findFirstFocusableDown(focusedRow, focusedColumn);
                if ((nextDown != null) && nextDown.isFocusable()) {
                    nextDown.requestFocus();
                    this.buttonPanel.scrollRectToVisible(nextDown.getBounds());
                    return true;
                } else {
                    return false;
                }
            case UP:
                JCommandButton nextUp = findLastFocusableUp(focusedRow, focusedColumn);
                if ((nextUp != null) && nextUp.isFocusable()) {
                    nextUp.requestFocus();
                    this.buttonPanel.scrollRectToVisible(nextUp.getBounds());
                    return true;
                } else {
                    return false;
                }
            case RIGHT:
                JCommandButton nextRight = findFirstFocusableRight(focusedRow, focusedColumn);
                if ((nextRight != null) && nextRight.isFocusable()) {
                    nextRight.requestFocus();
                    this.buttonPanel.scrollRectToVisible(nextRight.getBounds());
                    return true;
                } else {
                    return false;
                }
            case LEFT:
                JCommandButton nextLeft = findLastFocusableLeft(focusedRow, focusedColumn);
                if ((nextLeft != null) && nextLeft.isFocusable()) {
                    nextLeft.requestFocus();
                    this.buttonPanel.scrollRectToVisible(nextLeft.getBounds());
                    return true;
                } else {
                    return false;
                }
        }

        return false;
    }

    @Override
    public boolean focusUp() {
        return focusMove(FocusMoveDirection.UP);
    }

    @Override
    public boolean focusDown() {
        return focusMove(FocusMoveDirection.DOWN);
    }

    @Override
    public boolean focusRight() {
        return focusMove(FocusMoveDirection.RIGHT);
    }

    @Override
    public boolean focusLeft() {
        return focusMove(FocusMoveDirection.LEFT);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy