org.netbeans.modules.jumpto.symbol.GoToPanelImpl Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.netbeans.modules.jumpto.symbol;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.logging.Level;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.ButtonModel;
import javax.swing.Icon;
import javax.swing.InputMap;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.ListCellRenderer;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.AbstractDocument;
import javax.swing.text.BadLocationException;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.modules.jumpto.SearchHistory;
import org.netbeans.modules.jumpto.common.UiUtils;
import org.netbeans.modules.jumpto.type.UiOptions;
import org.netbeans.spi.jumpto.symbol.SymbolDescriptor;
import org.openide.awt.Mnemonics;
import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle;
import org.openide.util.Pair;
/**
*
* @author Petr Hrebejk
*/
class GoToPanelImpl extends javax.swing.JPanel implements GoToPanel {
private static Icon WAIT_ICON = ImageUtilities.loadImageIcon("org/netbeans/modules/jumpto/resources/wait.gif", false); // NOI18N
private static Icon WARN_ICON = ImageUtilities.loadImageIcon("org/netbeans/modules/jumpto/resources/warning.png", false); // NOI18N
private static final int BRIGHTER_COLOR_COMPONENT = 10;
private ContentProvider contentProvider;
private boolean containsScrollPane;
private JLabel messageLabel;
private SymbolDescriptor selectedSymbol;
// Time when the serach stared (for debugging purposes)
private long time = -1;
private final SearchHistory searchHistory;
// handling http://netbeans.org/bugzilla/show_bug.cgi?id=203528
// if the whole search argument (in the name JTextField) is selected and something is pasted in it's place,
// notify the DocumentListener because it will first call removeUpdate() and then inserteUpdate().
// When removeUpdate() is called we should not call update() because it messes the messageLabel's text.
private boolean pastedFromClipboard = false;
/** Creates new form GoToPanel */
public GoToPanelImpl( ContentProvider contentProvider ) throws IOException {
this.contentProvider = contentProvider;
initComponents();
containsScrollPane = true;
((AbstractDocument)nameField.getDocument()).setDocumentFilter(UiUtils.newUserInputFilter());
matchesList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
matchesList.addListSelectionListener(null);
Color bgColorBrighter = new Color(
Math.min(getBackground().getRed() + BRIGHTER_COLOR_COMPONENT, 255),
Math.min(getBackground().getGreen() + BRIGHTER_COLOR_COMPONENT, 255),
Math.min(getBackground().getBlue() + BRIGHTER_COLOR_COMPONENT, 255)
);
messageLabel = new JLabel();
messageLabel.setBackground(bgColorBrighter);
messageLabel.setHorizontalAlignment(SwingConstants.CENTER);
messageLabel.setEnabled(true);
messageLabel.setText(NbBundle.getMessage(GoToPanelImpl.class, "TXT_NoSymbolsFound")); // NOI18N
messageLabel.setFont(matchesList.getFont());
// matchesList.setBackground( bgColorBrighter );
// matchesScrollPane1.setBackground( bgColorBrighter );
matchesList.setCellRenderer(
contentProvider.getListCellRenderer(
matchesList,
caseSensitive.getModel()));
contentProvider.setListModel( this, null );
PatternListener pl = new PatternListener( this );
nameField.getDocument().addDocumentListener(pl);
matchesList.addListSelectionListener(pl);
caseSensitive.setSelected(UiOptions.GoToSymbolDialog.getCaseSensitive());
caseSensitive.addItemListener(pl);
searchHistory = new SearchHistory(GoToPanelImpl.class, nameField);
}
@Override
public void removeNotify() {
searchHistory.saveHistory();
super.removeNotify();
}
@Override
public boolean isCaseSensitive () {
return this.caseSensitive.isSelected();
}
@Override
public long getStartTime() {
return this.time;
}
/** Sets the model.
* Threading: Requires EDT.
* @param the model to set
* @param finished true for final update
* @return true if model has changed
*/
@Override
public boolean setModel(
@NonNull final ListModel model,
final boolean finished) {
assert SwingUtilities.isEventDispatchThread();
matchesList.setModel(model);
if (model.getSize() > 0 || getText() == null || getText().trim().length() == 0 ) {
matchesList.setSelectedIndex(0);
setListPanelContent(null,false);
if ( time != -1 ) {
GoToSymbolAction.LOGGER.log(
Level.FINE,
"Real search time {0} ms.", //NOI18N
(System.currentTimeMillis() - time));
time = -1;
}
return true;
} else if (finished) {
setListPanelContent(NbBundle.getMessage(GoToPanelImpl.class, "TXT_NoSymbolsFound") ,false );
return false;
} else {
return false;
}
}
/**
* Revalidates the model
* @param finished true for final update
* @return true if model has changed
*/
@Override
public boolean revalidateModel (final boolean finished) {
return setModel(matchesList.getModel(), finished);
}
/** Sets the initial text to find in case the user did not start typing yet. */
public void setInitialText( final String text ) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
String textInField = nameField.getText();
if ( textInField == null || textInField.trim().length() == 0 ) {
nameField.setText(text);
nameField.setCaretPosition(text.length());
nameField.setSelectionStart(0);
nameField.setSelectionEnd(text.length());
}
}
});
}
public void setSelectedSymbol() {
selectedSymbol = ((SymbolDescriptor) matchesList.getSelectedValue());
}
public SymbolDescriptor getSelectedSymbol() {
return selectedSymbol;
}
@Override
public void setWarning(String warningMessage) {
if (warningMessage != null) {
jLabelWarning.setIcon(WARN_ICON);
jLabelWarning.setBorder(BorderFactory.createEmptyBorder(3, 1, 1, 1));
} else {
jLabelWarning.setIcon(null);
jLabelWarning.setBorder(null);
}
jLabelWarning.setText(warningMessage);
}
/** 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("deprecation")
// //GEN-BEGIN:initComponents
private void initComponents() {
GridBagConstraints gridBagConstraints;
jLabelText = new JLabel();
nameField = new JTextField();
jLabelList = new JLabel();
listPanel = new JPanel();
matchesScrollPane1 = new JScrollPane();
matchesList = new JList();
jLabelWarning = new JLabel();
caseSensitive = new JCheckBox();
jLabelLocation = new JLabel();
jTextFieldLocation = new JTextField();
setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
setFocusable(false);
setNextFocusableComponent(nameField);
setLayout(new GridBagLayout());
jLabelText.setLabelFor(nameField);
Mnemonics.setLocalizedText(jLabelText, NbBundle.getMessage(GoToPanelImpl.class, "TXT_GoToSymbol_TypeName_Label")); // NOI18N
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridwidth = GridBagConstraints.REMAINDER;
gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = GridBagConstraints.SOUTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new Insets(0, 0, 4, 0);
add(jLabelText, gridBagConstraints);
nameField.setFont(new Font("Monospaced", 0, getFontSize()));
nameField.setBorder(BorderFactory.createEtchedBorder());
nameField.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
nameFieldActionPerformed(evt);
}
});
nameField.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent evt) {
nameFieldKeyPressed(evt);
}
public void keyReleased(KeyEvent evt) {
nameFieldKeyReleased(evt);
}
public void keyTyped(KeyEvent evt) {
nameFieldKeyTyped(evt);
}
});
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridwidth = GridBagConstraints.REMAINDER;
gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
gridBagConstraints.insets = new Insets(0, 0, 8, 0);
add(nameField, gridBagConstraints);
nameField.getAccessibleContext().setAccessibleName("Symbol &Name (prefix, camel case: \"AA\" or \"AbcAb\", wildcards: \"?\" \"*\", exact match: end with space):");
nameField.getAccessibleContext().setAccessibleDescription("Symbol Name (prefix, camel case: \"AA\" or \"AbcAb\", wildcards: \"?\" \"*\", exact match: end with space)");
jLabelList.setLabelFor(matchesList);
Mnemonics.setLocalizedText(jLabelList, NbBundle.getMessage(GoToPanelImpl.class, "TXT_GoToSymbol_MatchesList_Label")); // NOI18N
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridwidth = GridBagConstraints.REMAINDER;
gridBagConstraints.anchor = GridBagConstraints.SOUTHWEST;
gridBagConstraints.insets = new Insets(0, 0, 4, 0);
add(jLabelList, gridBagConstraints);
listPanel.setBorder(BorderFactory.createEtchedBorder());
listPanel.setName("dataPanel"); // NOI18N
listPanel.setLayout(new BorderLayout());
matchesScrollPane1.setBorder(null);
matchesScrollPane1.setFocusable(false);
matchesList.setFont(new Font("Monospaced", 0, getFontSize()));
matchesList.setVisibleRowCount(15);
matchesList.addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent evt) {
matchesListMouseReleased(evt);
}
});
matchesScrollPane1.setViewportView(matchesList);
matchesList.getAccessibleContext().setAccessibleName("Symbols &Found :");
matchesList.getAccessibleContext().setAccessibleDescription("Symbols Found");
listPanel.add(matchesScrollPane1, BorderLayout.CENTER);
jLabelWarning.setFocusable(false);
listPanel.add(jLabelWarning, BorderLayout.PAGE_END);
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridwidth = GridBagConstraints.REMAINDER;
gridBagConstraints.fill = GridBagConstraints.BOTH;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new Insets(0, 0, 8, 0);
add(listPanel, gridBagConstraints);
Mnemonics.setLocalizedText(caseSensitive, NbBundle.getMessage(GoToPanelImpl.class, "CTL_CaseSensitive")); // NOI18N
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridwidth = GridBagConstraints.REMAINDER;
gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = GridBagConstraints.WEST;
gridBagConstraints.insets = new Insets(0, 0, 8, 0);
add(caseSensitive, gridBagConstraints);
caseSensitive.getAccessibleContext().setAccessibleDescription(NbBundle.getMessage(GoToPanelImpl.class, "AD_CaseSensitive")); // NOI18N
jLabelLocation.setText(NbBundle.getMessage(GoToPanelImpl.class, "LBL_GoToSymbol_LocationJLabel")); // NOI18N
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.anchor = GridBagConstraints.WEST;
gridBagConstraints.insets = new Insets(0, 0, 4, 0);
add(jLabelLocation, gridBagConstraints);
jTextFieldLocation.setEditable(false);
jTextFieldLocation.setFocusable(false);
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridwidth = GridBagConstraints.REMAINDER;
gridBagConstraints.gridheight = GridBagConstraints.REMAINDER;
gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
add(jTextFieldLocation, gridBagConstraints);
}// //GEN-END:initComponents
private void matchesListMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_matchesListMouseReleased
if ( evt.getClickCount() == 2 ) {
nameFieldActionPerformed( null );
}
}//GEN-LAST:event_matchesListMouseReleased
private void nameFieldKeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_nameFieldKeyTyped
if (boundScrollingKey(evt)) {
delegateScrollingKey(evt);
}
}//GEN-LAST:event_nameFieldKeyTyped
private void nameFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_nameFieldKeyReleased
if (boundScrollingKey(evt)) {
delegateScrollingKey(evt);
}
}//GEN-LAST:event_nameFieldKeyReleased
private void nameFieldKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_nameFieldKeyPressed
if (boundScrollingKey(evt)) {
delegateScrollingKey(evt);
} else {
//handling http://netbeans.org/bugzilla/show_bug.cgi?id=203528
Object o = nameField.getInputMap().get(KeyStroke.getKeyStrokeForEvent(evt));
if (o instanceof String) {
String action = (String) o;
if ("paste-from-clipboard".equals(action)) {
String selectedTxt = nameField.getSelectedText();
String txt = nameField.getText();
if (selectedTxt != null && txt != null) {
if (selectedTxt.length() == txt.length()) {
pastedFromClipboard = true;
}
}
}
}
}
}//GEN-LAST:event_nameFieldKeyPressed
private void nameFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nameFieldActionPerformed
if (contentProvider.hasValidContent()) {
contentProvider.closeDialog();
setSelectedSymbol();
}
}//GEN-LAST:event_nameFieldActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private JCheckBox caseSensitive;
private JLabel jLabelList;
private JLabel jLabelLocation;
private JLabel jLabelText;
private JLabel jLabelWarning;
private JTextField jTextFieldLocation;
private JPanel listPanel;
private JList matchesList;
private JScrollPane matchesScrollPane1;
private JTextField nameField;
// End of variables declaration//GEN-END:variables
private String getText() {
try {
String text = nameField.getDocument().getText(0, nameField.getDocument().getLength());
return text;
}
catch( BadLocationException ex ) {
return null;
}
}
private int getFontSize () {
return this.jLabelList.getFont().getSize();
}
void setListPanelContent( String message ,boolean waitIcon ) {
if ( message == null && !containsScrollPane ) {
listPanel.remove( messageLabel );
listPanel.add( matchesScrollPane1 );
containsScrollPane = true;
revalidate();
repaint();
}
else if ( message != null ) {
jTextFieldLocation.setText("");
messageLabel.setText(message);
messageLabel.setIcon( waitIcon ? WAIT_ICON : null);
if ( containsScrollPane ) {
listPanel.remove( matchesScrollPane1 );
listPanel.add( messageLabel );
containsScrollPane = false;
}
revalidate();
repaint();
}
}
@CheckForNull
private Pair listActionFor(KeyEvent ev) {
InputMap map = matchesList.getInputMap();
Object o = map.get(KeyStroke.getKeyStrokeForEvent(ev));
if (o instanceof String) {
return Pair.of((String)o, matchesList);
}
map = matchesScrollPane1.getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
o = map.get(KeyStroke.getKeyStrokeForEvent(ev));
if (o instanceof String) {
return Pair.of((String)o, matchesScrollPane1);
}
return null;
}
private boolean boundScrollingKey(KeyEvent ev) {
final Pair p = listActionFor(ev);
if (p == null) {
return false;
}
final String action = p.first();
// See BasicListUI, MetalLookAndFeel:
return "selectPreviousRow".equals(action) || // NOI18N
"selectNextRow".equals(action) || // NOI18N
// "selectFirstRow".equals(action) || // NOI18N
// "selectLastRow".equals(action) || // NOI18N
"scrollUp".equals(action) || // NOI18N
"scrollDown".equals(action); // NOI18N
}
private void delegateScrollingKey(KeyEvent ev) {
final Pair p = listActionFor(ev);
if (p == null) {
return;
}
final String action = p.first();
final JComponent target = p.second();
// Wrap around
if ( "selectNextRow".equals(action) &&
matchesList.getSelectedIndex() == matchesList.getModel().getSize() -1 ) {
matchesList.setSelectedIndex(0);
matchesList.ensureIndexIsVisible(0);
return;
}
else if ( "selectPreviousRow".equals(action) &&
matchesList.getSelectedIndex() == 0 ) {
int last = matchesList.getModel().getSize() - 1;
matchesList.setSelectedIndex(last);
matchesList.ensureIndexIsVisible(last);
return;
}
// Plain delegation
final Action a = target.getActionMap().get(action);
if (a != null) {
a.actionPerformed(new ActionEvent(target, 0, action));
}
}
private static class PatternListener implements DocumentListener, ListSelectionListener, ItemListener {
private final GoToPanelImpl dialog;
PatternListener( GoToPanelImpl dialog ) {
this.dialog = dialog;
}
PatternListener( DocumentEvent e, GoToPanelImpl dialog ) {
this.dialog = dialog;
}
// DocumentListener ----------------------------------------------------
public void changedUpdate( DocumentEvent e ) {
update();
}
public void removeUpdate( DocumentEvent e ) {
// handling http://netbeans.org/bugzilla/show_bug.cgi?id=203528
if (dialog.pastedFromClipboard) {
dialog.pastedFromClipboard = false;
} else {
update();
}
}
public void insertUpdate( DocumentEvent e ) {
update();
}
public void itemStateChanged(ItemEvent e) {
UiOptions.GoToSymbolDialog.setCaseSensitive(dialog.isCaseSensitive());
update();
}
// ListSelectionListener -----------------------------------------------
@Override
public void valueChanged(@NonNull final ListSelectionEvent ev) {
// got "Not computed yet" text sometimes
final Object obj = dialog.matchesList.getSelectedValue();
if (obj instanceof SymbolDescriptor) {
final SymbolDescriptor selectedValue = ((SymbolDescriptor) obj);
final String fileName = selectedValue.getFileDisplayPath();
dialog.jTextFieldLocation.setText(fileName);
} else {
dialog.jTextFieldLocation.setText(""); //NOI18N
}
}
private void update() {
dialog.time = System.currentTimeMillis();
String text = dialog.getText();
if (dialog.contentProvider.setListModel(dialog,text)) {
dialog.setListPanelContent(NbBundle.getMessage(GoToPanelImpl.class, "TXT_Searching"),true); // NOI18N
}
}
}
public static interface ContentProvider {
public ListCellRenderer getListCellRenderer(JList list, ButtonModel caseSensitive);
public boolean setListModel( GoToPanel panel, String text );
public void closeDialog();
public boolean hasValidContent ();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy