Java tutorial
/* * Copyright 2016 Alessandro Falappa. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.alexfalappa.nbspringboot.projects.customizer; import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.Comparator; import java.util.TreeSet; import java.util.prefs.Preferences; import javax.swing.DefaultListCellRenderer; import javax.swing.DefaultListModel; import javax.swing.JComponent; import javax.swing.JList; import javax.swing.JTextField; import javax.swing.KeyStroke; import javax.swing.UIManager; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.text.html.HTMLEditorKit; import org.openide.util.NbPreferences; import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; import com.github.alexfalappa.nbspringboot.PrefConstants; import com.github.alexfalappa.nbspringboot.Utils; import com.github.alexfalappa.nbspringboot.projects.service.api.SpringBootService; import static com.github.alexfalappa.nbspringboot.PrefConstants.PREF_DEPR_ERROR_SHOW; import static com.github.alexfalappa.nbspringboot.PrefConstants.PREF_DEPR_SORT_LAST; import static java.awt.event.MouseEvent.BUTTON1; /** * Dialog for choosing one Spring Boot configuration property in an ordered list of those available in the project. * <p> * Queries a {@link SpringBootService} for the configuration properties names. * * @author Alessandro Falappa */ public class CfgPropsDialog extends javax.swing.JDialog { private boolean okPressed = false; private final TreeSet<ConfigurationMetadataProperty> sortedProps; private final boolean bDeprErrorShow; /** Creates new form CfgPropsDialog */ public CfgPropsDialog(java.awt.Dialog parent) { super(parent, true); initComponents(); // retrieve some flads from prefs final Preferences prefs = NbPreferences.forModule(PrefConstants.class); final boolean bDeprLast = prefs.getBoolean(PREF_DEPR_SORT_LAST, true); bDeprErrorShow = prefs.getBoolean(PREF_DEPR_ERROR_SHOW, false); // setup props sorting this.sortedProps = new TreeSet<>(new ConfigurationMetadataComparator(bDeprLast)); // setup property list lCfgProps.getSelectionModel().addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { if (!e.getValueIsAdjusting()) { final ConfigurationMetadataProperty selectedValue = lCfgProps.getSelectedValue(); if (selectedValue != null) { tpDetails.setText(Utils.cfgPropDetailsHtml(selectedValue)); tpDetails.setCaretPosition(0); } } } }); // set default button rootPane.setDefaultButton(bOk); // close dialog with ESC key final ActionListener escAction = new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { CfgPropsDialog.this.setVisible(false); } }; rootPane.registerKeyboardAction(escAction, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW); } public void loadCfgProps(SpringBootService bootService) { if (bootService == null) { return; } sortedProps.addAll(bootService.queryPropertyMetadata(null)); filterProps(null); // setup filtering new ListFilterer(txFilter); pack(); } public boolean okPressed() { return okPressed; } public String getSelectedPropName() { return lCfgProps.getSelectedValue().getId(); } /** This method is called from within the constructor to initialize the form. WARNING: Do NOT modify this code. The content of this * method is always regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { bCancel = new javax.swing.JButton(); bOk = new javax.swing.JButton(); splitter = new javax.swing.JSplitPane(); scroller1 = new javax.swing.JScrollPane(); lCfgProps = new javax.swing.JList<>(); scroller2 = new javax.swing.JScrollPane(); tpDetails = new javax.swing.JTextPane(); txFilter = new javax.swing.JTextField(); lFilter = new javax.swing.JLabel(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); setTitle(org.openide.util.NbBundle.getBundle(CfgPropsDialog.class).getString("CfgPropsDialog.title")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(bCancel, org.openide.util.NbBundle.getBundle(CfgPropsDialog.class).getString("CfgPropsDialog.bCancel.text")); // NOI18N bCancel.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { bCancelActionPerformed(evt); } }); org.openide.awt.Mnemonics.setLocalizedText(bOk, org.openide.util.NbBundle.getBundle(CfgPropsDialog.class).getString("CfgPropsDialog.bOk.text")); // NOI18N bOk.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { bOkActionPerformed(evt); } }); splitter.setBorder(null); splitter.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT); splitter.setContinuousLayout(true); lCfgProps.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); lCfgProps.setCellRenderer(new ConfigurationMetadataCellRenderer()); lCfgProps.setVisibleRowCount(16); lCfgProps.addMouseListener(new java.awt.event.MouseAdapter() { public void mouseClicked(java.awt.event.MouseEvent evt) { lCfgPropsMouseClicked(evt); } }); scroller1.setViewportView(lCfgProps); splitter.setTopComponent(scroller1); tpDetails.setEditorKit(new HTMLEditorKit()); tpDetails.setCursor(new java.awt.Cursor(java.awt.Cursor.TEXT_CURSOR)); tpDetails.setMinimumSize(new java.awt.Dimension(42, 63)); tpDetails.setPreferredSize(new java.awt.Dimension(42, 63)); scroller2.setViewportView(tpDetails); splitter.setRightComponent(scroller2); txFilter.setColumns(25); txFilter.setText( org.openide.util.NbBundle.getMessage(CfgPropsDialog.class, "CfgPropsDialog.txFilter.text")); // NOI18N txFilter.setToolTipText( org.openide.util.NbBundle.getMessage(CfgPropsDialog.class, "CfgPropsDialog.txFilter.toolTipText")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(lFilter, org.openide.util.NbBundle.getMessage(CfgPropsDialog.class, "CfgPropsDialog.lFilter.text")); // NOI18N javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup().addContainerGap().addGroup(layout .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(splitter, javax.swing.GroupLayout.Alignment.TRAILING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup().addGap(0, 0, Short.MAX_VALUE).addComponent(bCancel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(bOk)) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup().addComponent(lFilter) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(txFilter))) .addContainerGap())); layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] { bCancel, bOk }); layout.setVerticalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup().addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(txFilter, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(lFilter)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(splitter) .addGap(18, 18, 18) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(bOk).addComponent(bCancel)) .addContainerGap())); pack(); }// </editor-fold>//GEN-END:initComponents private void bOkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bOkActionPerformed okPressed = true; bCancelActionPerformed(null); }//GEN-LAST:event_bOkActionPerformed private void bCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bCancelActionPerformed setVisible(false); }//GEN-LAST:event_bCancelActionPerformed private void lCfgPropsMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_lCfgPropsMouseClicked if (evt.getButton() == BUTTON1 && evt.getClickCount() > 1) { bOkActionPerformed(null); } }//GEN-LAST:event_lCfgPropsMouseClicked // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton bCancel; private javax.swing.JButton bOk; private javax.swing.JList<ConfigurationMetadataProperty> lCfgProps; private javax.swing.JLabel lFilter; private javax.swing.JScrollPane scroller1; private javax.swing.JScrollPane scroller2; private javax.swing.JSplitPane splitter; private javax.swing.JTextPane tpDetails; private javax.swing.JTextField txFilter; // End of variables declaration//GEN-END:variables private void filterProps(String filter) { DefaultListModel<ConfigurationMetadataProperty> dlmCfgProps = new DefaultListModel<>(); for (ConfigurationMetadataProperty item : sortedProps) { if (filter == null || item.getId().contains(filter)) { if (Utils.isErrorDeprecated(item)) { if (bDeprErrorShow) { dlmCfgProps.addElement(item); } } else { dlmCfgProps.addElement(item); } } } lCfgProps.setModel(dlmCfgProps); if (!dlmCfgProps.isEmpty()) { lCfgProps.setSelectedIndex(0); } } private static class ConfigurationMetadataCellRenderer extends DefaultListCellRenderer { @Override public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) { super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (value instanceof ConfigurationMetadataProperty) { ConfigurationMetadataProperty prop = (ConfigurationMetadataProperty) value; if (prop.isDeprecated()) { setText(String.format("<html><s>%s", prop.getId())); if (Utils.isErrorDeprecated(prop)) { setForeground(UIManager.getColor("nb.errorForeground")); } } else { setText(prop.getId()); } } return this; } } private static class ConfigurationMetadataComparator implements Comparator<ConfigurationMetadataProperty> { private final boolean sortDeprLast; public ConfigurationMetadataComparator(boolean sortDeprLast) { this.sortDeprLast = sortDeprLast; } @Override public int compare(ConfigurationMetadataProperty p1, ConfigurationMetadataProperty p2) { if (!sortDeprLast) { return p1.getId().compareTo(p2.getId()); } else { boolean d1 = p1.isDeprecated(); boolean d2 = p2.isDeprecated(); if (d1 && !d2) { return 1; } else if (d2 && !d1) { return -1; } else { return p1.getId().compareTo(p2.getId()); } } } } private class ListFilterer extends KeyAdapter implements DocumentListener { private final JTextField txtField; public ListFilterer(JTextField txtField) { this.txtField = txtField; txtField.getDocument().addDocumentListener(this); txtField.addKeyListener(this); } @Override public void changedUpdate(DocumentEvent e) { doFilter(); } @Override public void insertUpdate(DocumentEvent e) { doFilter(); } @Override public void removeUpdate(DocumentEvent e) { doFilter(); } @Override public void keyPressed(KeyEvent e) { final int keyCode = e.getKeyCode(); if (keyCode == KeyEvent.VK_ESCAPE) { final String filter = txtField.getText(); if (filter == null || filter.isEmpty()) { // close dialog if filter textfield is empty setVisible(false); } else { // clear textfield txtField.setText(null); e.consume(); } } else { final int size = lCfgProps.getModel().getSize(); // short circuit if empty size if (size == 0) { return; } int selIdx = lCfgProps.getSelectedIndex(); final int pageStep = lCfgProps.getVisibleRowCount(); // calculate new index switch (keyCode) { case KeyEvent.VK_PAGE_UP: selIdx -= pageStep; // clamp to [0 : size-1] range if (selIdx < 0) { selIdx = 0; } else if (selIdx >= size) { selIdx = size - 1; } break; case KeyEvent.VK_UP: selIdx -= 1; // wrap if needed if (selIdx < 0) { selIdx += size; } else if (selIdx >= size) { selIdx -= size; } break; case KeyEvent.VK_DOWN: selIdx += 1; // wrap if needed if (selIdx < 0) { selIdx += size; } else if (selIdx >= size) { selIdx -= size; } break; case KeyEvent.VK_PAGE_DOWN: selIdx += pageStep; // clamp to [0 : size-1] range if (selIdx < 0) { selIdx = 0; } else if (selIdx >= size) { selIdx = size - 1; } break; } // move properties list selection and scroll to it lCfgProps.setSelectedIndex(selIdx); lCfgProps.scrollRectToVisible(lCfgProps.getCellBounds(selIdx, selIdx)); } } private void doFilter() { String text = txFilter.getText().toLowerCase(); if (!text.isEmpty()) { filterProps(text); } else { filterProps(null); } } } }