001package jmri.jmrix.can.cbus.swing; 002 003import java.awt.Color; 004import java.awt.GridLayout; 005import javax.annotation.Nonnull; 006import javax.swing.*; 007import javax.swing.event.ChangeEvent; 008import javax.swing.plaf.basic.BasicToggleButtonUI; 009import javax.swing.text.DefaultFormatter; 010import jmri.jmrit.catalog.NamedIcon; 011import jmri.jmrix.can.cbus.CbusFilterType; 012import jmri.util.ThreadingUtil; 013 014// import org.slf4j.Logger; 015// import org.slf4j.LoggerFactory; 016 017/** 018 * Creates Panel for displaying a single filter 019 * @author Steve Young Copyright (C) 2018, 2020 020 */ 021public class CbusFilterPanel extends JPanel { 022 023 private final CbusFilterType _fType; 024 private final int _index; 025 private final CbusFilterFrame _filterFrame; 026 private String _textLabel; 027 private int _countFilter=0; 028 private int _countPass=0; 029 private JLabel fLabel; 030 private JPanel evPane; 031 private boolean _available; 032 private String _buttonText = Bundle.getMessage("ButtonPass"); 033 034 private final static Color greenish = new Color(110, 235, 131); 035 private final static Color redish = new Color(255, 132, 84); 036 private final static Color amberish = new Color(228, 255, 26); 037 038 // Buttons to enable/disable filters 039 private JToggleButton enableButton; 040 private JToggleButton catButton; 041 042 /** 043 * Create a new CbusFilterPanel for filtering any Nodes which are heard 044 * This is created at Frame startup, though #initComponents 045 * is not called until the Node is actually set by #setNode 046 * 047 * @param filterFrame parent Frame 048 * @param index Filter Index 049 */ 050 public CbusFilterPanel(CbusFilterFrame filterFrame, int index) { 051 super(); 052 _index = index; 053 _filterFrame = filterFrame; 054 _fType = CbusFilterType.CFNODES; 055 _textLabel = Bundle.getMessage("CbusNodes"); 056 _available = false; 057 setVisible(false); 058 } 059 060 /** 061 * Create a new CbusFilterPanel for filtering any CbusFilterType. 062 * This is created at Frame startup, #initComponents 063 * is called straight away. 064 * 065 * @param filterFrame parent Frame 066 * @param fType Filter to display 067 */ 068 public CbusFilterPanel(CbusFilterFrame filterFrame, CbusFilterType fType){ 069 super(); 070 _index = fType.ordinal(); 071 _filterFrame = filterFrame; 072 _fType = fType; 073 _textLabel = _fType.getName(); 074 _available=true; 075 initComponents(); 076 } 077 078 private static double _iconScale = 0.25; 079 080 private NamedIcon getCollapsed() { 081 NamedIcon collapsed = new NamedIcon("resources/icons/decorations/ArrowStyle2.png", "resources/icons/decorations/ArrowStyle2.png"); 082 collapsed.scale(_iconScale, this); 083 return collapsed; 084 } 085 086 private NamedIcon getShowing() { 087 NamedIcon showing = new NamedIcon("resources/icons/decorations/ArrowStyle2.png", "resources/icons/decorations/ArrowStyle2.png"); 088 showing.scale(_iconScale,this); 089 showing.setRotation(3, this); 090 return showing; 091 } 092 093 /** 094 * Initialise the Pane. 095 */ 096 protected final void initComponents() { 097 098 this.setLayout(new GridLayout(1,1,30,30)); // row, col, hgap, vgap 099 100 // Pane to hold Event 101 evPane = new JPanel(); 102 evPane.setBackground(Color.white); 103 104 catButton = new JToggleButton(); 105 fLabel = new JLabel(_textLabel,SwingConstants.RIGHT); 106 fLabel.setToolTipText(getFilterType().getToolTip()); 107 if (getFilterType().isCategoryHead()){ 108 evPane.setLayout(new GridLayout(1,2,0,10)); 109 configCatButton(catButton); 110 evPane.add(catButton); 111 } 112 else { 113 evPane.setLayout(new GridLayout(1,2,40,10)); 114 115 if ( _fType.showSpinners() ) { 116 evPane.add(getNewSpinnerAndButton(_fType)); 117 } else { 118 evPane.add(fLabel); 119 } 120 } 121 enableButton = getNewEnableButton(); 122 123 evPane.add(enableButton); 124 evPane.setVisible(true); 125 this.add(evPane); 126 this.setVisible( getFilterType().alwaysDisplay() ); 127 } 128 129 private void configCatButton(@Nonnull JToggleButton button){ 130 131 button.setBackground(Color.white); 132 button.setText(_textLabel); 133 button.setIcon(getCollapsed()); 134 button.setHorizontalAlignment(SwingConstants.RIGHT); 135 button.setHorizontalTextPosition(JLabel.LEFT); 136 button.setFocusPainted(false); 137 button.setBorderPainted(false); 138 button.addActionListener((java.awt.event.ActionEvent e) -> { 139 if (button.isSelected()) { 140 // log.warn("selected category index {}",_index); 141 button.setIcon(getShowing()); 142 } else { 143 // log.warn("deselected category index {} ",_index); 144 button.setIcon(getCollapsed()); 145 } 146 ThreadingUtil.runOnGUIEventually( ()->{ 147 _filterFrame.showFiltersChanged(_index,button.isSelected(),getFilterType()); 148 }); 149 }); 150 } 151 152 private JPanel getNewSpinnerAndButton(@Nonnull CbusFilterType fType){ 153 154 JPanel spinnerAndButton = new JPanel(); 155 spinnerAndButton.setLayout(new GridLayout(1,2,0,0)); 156 JSpinner spinner = new JSpinner(new SpinnerNumberModel(0, 0, 65535, 1)); 157 158 JSpinner.NumberEditor editor = new JSpinner.NumberEditor(spinner, "#"); 159 spinner.setEditor(editor); 160 161 JFormattedTextField field = (JFormattedTextField) editor.getComponent(0); 162 DefaultFormatter formatter = (DefaultFormatter) field.getFormatter(); 163 formatter.setCommitsOnValidEdit(true); 164 spinner.addChangeListener((ChangeEvent e) -> { 165 _filterFrame.setMinMax(fType,(Integer) spinner.getValue() ); 166 }); 167 168 spinnerAndButton.setBackground(Color.white); 169 spinnerAndButton.add(spinner); 170 spinnerAndButton.add(fLabel); 171 172 return spinnerAndButton; 173 } 174 175 private JToggleButton getNewEnableButton(){ 176 JToggleButton newButton = new JToggleButton(); 177 newButton.setUI(new BasicToggleButtonUI()); //Removes selectColor 178 newButton.setText( newTextString() ); 179 newButton.setBackground(greenish); 180 newButton.setFocusPainted(false); 181 newButton.setToolTipText(Bundle.getMessage("ButtonPassTip")); 182 183 // connect actions to buttons 184 newButton.addActionListener((java.awt.event.ActionEvent e) -> { 185 resetenableButton(); 186 ThreadingUtil.runOnGUIEventually( ()->{ 187 _filterFrame.checkBoxChanged(_index,enableButton.isSelected(),getFilterType()); 188 StringBuilder txt = new StringBuilder(); 189 txt.append(_textLabel) 190 .append(": ") 191 .append(_buttonText); 192 if (getFilterType().isCategoryHead()){ 193 txt.append(" ") 194 .append(Bundle.getMessage("All")); 195 } 196 _filterFrame.updateListeners(txt.toString()); 197 }); 198 }); 199 return newButton; 200 } 201 202 /** 203 * Reset the Filter Button. 204 */ 205 private void resetenableButton(){ 206 if (enableButton.isSelected()) { 207 _buttonText = Bundle.getMessage("ButtonFilter"); 208 enableButton.setText( newTextString() ); 209 enableButton.setToolTipText(Bundle.getMessage("ButtonFilterTip")); 210 enableButton.setBackground(redish); 211 } else { 212 _buttonText = Bundle.getMessage("ButtonPass"); 213 enableButton.setText( newTextString() ); 214 enableButton.setToolTipText(Bundle.getMessage("ButtonPassTip")); 215 enableButton.setBackground(greenish); 216 } 217 } 218 219 /** 220 * If panel is displaying a parent category, some children categories may 221 * be both Active and Inactive. 222 */ 223 protected void setMixed() { 224 _buttonText = Bundle.getMessage("ButtonMixed"); 225 enableButton.setText( newTextString() ); 226 enableButton.setToolTipText(Bundle.getMessage("ButtonMixedTip")); 227 enableButton.setBackground(amberish); 228 enableButton.setSelected(false); 229 } 230 231 /** 232 * Get the Filter Index, value for main Filter. 233 * @return Filter Index. 234 */ 235 protected final int getIndex() { 236 return _index; 237 } 238 239 /** 240 * Show or Hide the Filter Panel 241 * @param showornot true to show, false to hide. 242 */ 243 protected void visibleFilter(boolean showornot) { 244 this.setVisible(showornot); 245 } 246 247 /** 248 * Get the Filter Type in use by the panel. 249 * @return Filter Type ENUM 250 */ 251 @Nonnull 252 protected final CbusFilterType getFilterType(){ 253 return _fType; 254 } 255 256 /** 257 * Increment the number of Filtered Frames. 258 * Updates text label with counts. 259 */ 260 protected void incrementFilter() { 261 _countFilter++; 262 enableButton.setText(newTextString()); 263 } 264 265 /** 266 * Increment the number of allowed Frames. 267 * Updates text label with counts. 268 */ 269 protected void incrementPass() { 270 _countPass++; 271 enableButton.setText(newTextString()); 272 } 273 274 /** 275 * Get the button text string, with text label and filter / pass counts. 276 * @return Full text string 277 */ 278 @Nonnull 279 private String newTextString() { 280 StringBuilder t = new StringBuilder(); 281 t.append(_buttonText) 282 .append(" ( ") 283 .append(_countPass) 284 .append(" / ") 285 .append(_countFilter) 286 .append(" ) "); 287 return t.toString(); 288 } 289 290 /** 291 * Get if the button is currently set to filter. 292 * @return true if filtering, else false. 293 */ 294 protected boolean getButton() { 295 return enableButton.isSelected(); 296 } 297 298 /** 299 * Get if the Panel is visible 300 * @return true if visible, else false. 301 */ 302 protected boolean getVisible() { 303 return this.isVisible(); 304 } 305 306 /** 307 * Get if the Panel is in use. 308 * @return true if in use by a Filter / Node, else false. 309 */ 310 protected boolean getAvailable() { 311 return _available; 312 } 313 314 /** 315 * Set the Panel for a Node. 316 * @param node Node Number 317 * @param filter true to Start filter active, false to pass. 318 * @param show true to display, false to hide. 319 */ 320 protected void setNode( int node, boolean filter, boolean show ) { 321 initComponents(); 322 _available=true; 323 _textLabel=Bundle.getMessage("CbusNode") + Integer.toString(node); 324 fLabel.setText(_textLabel); 325 this.setVisible(show); 326 setPass(filter); 327 } 328 329 /** 330 * Set the panel button to display Pass / Filter. 331 * @param trueorfalse true to pass, false to filter. 332 */ 333 protected void setPass(boolean trueorfalse) { 334 enableButton.setSelected(trueorfalse); 335 resetenableButton(); 336 } 337 338 // private final static Logger log = LoggerFactory.getLogger(CbusFilterPanel.class); 339}