001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2025 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 java.io.Serial; 023 024import javax.swing.ListSelectionModel; 025import javax.swing.tree.DefaultTreeSelectionModel; 026import javax.swing.tree.TreePath; 027 028/** 029 * ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel 030 * to listen for changes in the ListSelectionModel it maintains. Once 031 * a change in the ListSelectionModel happens, the paths are updated 032 * in the DefaultTreeSelectionModel. 033 * 034 */ 035final class ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel { 036 037 /** A unique serial version identifier. */ 038 @Serial 039 private static final long serialVersionUID = 2267930983939339510L; 040 /** TreeTable to perform updates on. */ 041 private final TreeTable treeTable; 042 /** Set to true when we are updating the ListSelectionModel. */ 043 private boolean updatingListSelectionModel; 044 045 /** 046 * Constructor to initialise treeTable. 047 * 048 * @param jTreeTable TreeTable to perform updates on. 049 */ 050 /* package */ ListToTreeSelectionModelWrapper(TreeTable jTreeTable) { 051 treeTable = jTreeTable; 052 getListSelectionModel().addListSelectionListener(event -> { 053 updateSelectedPathsFromSelectedRows(); 054 }); 055 } 056 057 /** 058 * Returns the list selection model. ListToTreeSelectionModelWrapper 059 * listens for changes to this model and updates the selected paths 060 * accordingly. 061 * 062 * @return the list selection model 063 */ 064 public ListSelectionModel getListSelectionModel() { 065 return listSelectionModel; 066 } 067 068 /** 069 * This is overridden to set {@code updatingListSelectionModel} 070 * and message super. This is the only place DefaultTreeSelectionModel 071 * alters the ListSelectionModel. 072 */ 073 @Override 074 public void resetRowSelection() { 075 if (!updatingListSelectionModel) { 076 updatingListSelectionModel = true; 077 try { 078 super.resetRowSelection(); 079 } 080 finally { 081 updatingListSelectionModel = false; 082 } 083 } 084 // Notice how we don't message super if 085 // updatingListSelectionModel is true. If 086 // updatingListSelectionModel is true, it implies the 087 // ListSelectionModel has already been updated and the 088 // paths are the only thing that needs to be updated. 089 } 090 091 /** 092 * If {@code updatingListSelectionModel} is false, this will 093 * reset the selected paths from the selected rows in the list 094 * selection model. 095 */ 096 private void updateSelectedPathsFromSelectedRows() { 097 if (!updatingListSelectionModel) { 098 updatingListSelectionModel = true; 099 try { 100 // This is way expensive, ListSelectionModel needs an 101 // enumerator for iterating. 102 final int min = listSelectionModel.getMinSelectionIndex(); 103 final int max = listSelectionModel.getMaxSelectionIndex(); 104 105 clearSelection(); 106 if (min != -1 && max != -1) { 107 for (int counter = min; counter <= max; counter++) { 108 updateSelectedPathIfRowIsSelected(counter); 109 } 110 } 111 } 112 finally { 113 updatingListSelectionModel = false; 114 } 115 } 116 } 117 118 /** 119 * If the row at given index is selected, selected paths are updated. 120 * 121 * @param counter number of row. 122 */ 123 private void updateSelectedPathIfRowIsSelected(int counter) { 124 if (listSelectionModel.isSelectedIndex(counter)) { 125 final TreePath selPath = treeTable.getTree().getPathForRow(counter); 126 127 if (selPath != null) { 128 addSelectionPath(selPath); 129 } 130 } 131 } 132 133}