001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2024 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018///////////////////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.gui;
021
022import javax.swing.ListSelectionModel;
023import javax.swing.tree.DefaultTreeSelectionModel;
024import javax.swing.tree.TreePath;
025
026/**
027 * ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel
028 * to listen for changes in the ListSelectionModel it maintains. Once
029 * a change in the ListSelectionModel happens, the paths are updated
030 * in the DefaultTreeSelectionModel.
031 *
032 */
033final class ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel {
034
035    /** A unique serial version identifier. */
036    private static final long serialVersionUID = 2267930983939339510L;
037    /** TreeTable to perform updates on. */
038    private final TreeTable treeTable;
039    /** Set to true when we are updating the ListSelectionModel. */
040    private boolean updatingListSelectionModel;
041
042    /**
043     * Constructor to initialise treeTable.
044     *
045     * @param jTreeTable TreeTable to perform updates on.
046     */
047    /* package */ ListToTreeSelectionModelWrapper(TreeTable jTreeTable) {
048        treeTable = jTreeTable;
049        getListSelectionModel().addListSelectionListener(event -> {
050            updateSelectedPathsFromSelectedRows();
051        });
052    }
053
054    /**
055     * Returns the list selection model. ListToTreeSelectionModelWrapper
056     * listens for changes to this model and updates the selected paths
057     * accordingly.
058     *
059     * @return the list selection model
060     */
061    public ListSelectionModel getListSelectionModel() {
062        return listSelectionModel;
063    }
064
065    /**
066     * This is overridden to set {@code updatingListSelectionModel}
067     * and message super. This is the only place DefaultTreeSelectionModel
068     * alters the ListSelectionModel.
069     */
070    @Override
071    public void resetRowSelection() {
072        if (!updatingListSelectionModel) {
073            updatingListSelectionModel = true;
074            try {
075                super.resetRowSelection();
076            }
077            finally {
078                updatingListSelectionModel = false;
079            }
080        }
081        // Notice how we don't message super if
082        // updatingListSelectionModel is true. If
083        // updatingListSelectionModel is true, it implies the
084        // ListSelectionModel has already been updated and the
085        // paths are the only thing that needs to be updated.
086    }
087
088    /**
089     * If {@code updatingListSelectionModel} is false, this will
090     * reset the selected paths from the selected rows in the list
091     * selection model.
092     */
093    private void updateSelectedPathsFromSelectedRows() {
094        if (!updatingListSelectionModel) {
095            updatingListSelectionModel = true;
096            try {
097                // This is way expensive, ListSelectionModel needs an
098                // enumerator for iterating.
099                final int min = listSelectionModel.getMinSelectionIndex();
100                final int max = listSelectionModel.getMaxSelectionIndex();
101
102                clearSelection();
103                if (min != -1 && max != -1) {
104                    for (int counter = min; counter <= max; counter++) {
105                        updateSelectedPathIfRowIsSelected(counter);
106                    }
107                }
108            }
109            finally {
110                updatingListSelectionModel = false;
111            }
112        }
113    }
114
115    /**
116     * If the row at given index is selected, selected paths are updated.
117     *
118     * @param counter number of row.
119     */
120    private void updateSelectedPathIfRowIsSelected(int counter) {
121        if (listSelectionModel.isSelectedIndex(counter)) {
122            final TreePath selPath = treeTable.getTree().getPathForRow(counter);
123
124            if (selPath != null) {
125                addSelectionPath(selPath);
126            }
127        }
128    }
129
130}