001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2026 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 */ 035public final 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 treeTable TreeTable to perform updates on. 049 */ 050 /* package */ ListToTreeSelectionModelWrapper(TreeTable treeTable) { 051 this.treeTable = treeTable; 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 accordingly. 060 * 061 * @return the list selection model 062 */ 063 public ListSelectionModel getListSelectionModel() { 064 return listSelectionModel; 065 } 066 067 /** 068 * This is overridden to set {@code updatingListSelectionModel} 069 * and message super. This is the only place DefaultTreeSelectionModel 070 * alters the ListSelectionModel. 071 */ 072 @Override 073 public void resetRowSelection() { 074 if (!updatingListSelectionModel) { 075 updatingListSelectionModel = true; 076 try { 077 super.resetRowSelection(); 078 } 079 finally { 080 updatingListSelectionModel = false; 081 } 082 } 083 // Notice how we don't message super if 084 // updatingListSelectionModel is true. If 085 // updatingListSelectionModel is true, it implies the 086 // ListSelectionModel has already been updated and the 087 // paths are the only thing that needs to be updated. 088 } 089 090 /** 091 * If {@code updatingListSelectionModel} is false, this will 092 * reset the selected paths from the selected rows in the list selection model. 093 */ 094 private void updateSelectedPathsFromSelectedRows() { 095 if (!updatingListSelectionModel) { 096 updatingListSelectionModel = true; 097 try { 098 // This is way expensive, ListSelectionModel needs an 099 // enumerator for iterating. 100 final int min = listSelectionModel.getMinSelectionIndex(); 101 final int max = listSelectionModel.getMaxSelectionIndex(); 102 103 clearSelection(); 104 if (min != -1 && max != -1) { 105 for (int counter = min; counter <= max; counter++) { 106 updateSelectedPathIfRowIsSelected(counter); 107 } 108 } 109 } 110 finally { 111 updatingListSelectionModel = false; 112 } 113 } 114 } 115 116 /** 117 * If the row at given index is selected, selected paths are updated. 118 * 119 * @param counter number of row. 120 */ 121 private void updateSelectedPathIfRowIsSelected(int counter) { 122 if (listSelectionModel.isSelectedIndex(counter)) { 123 final TreePath selPath = treeTable.getTree().getPathForRow(counter); 124 125 if (selPath != null) { 126 addSelectionPath(selPath); 127 } 128 } 129 } 130 131}