~ubuntu-branches/ubuntu/quantal/netbeans/quantal

« back to all changes in this revision

Viewing changes to editor/src/org/netbeans/modules/editor/impl/SearchBar.java

  • Committer: Bazaar Package Importer
  • Author(s): Marek Slama
  • Date: 2008-01-29 14:11:22 UTC
  • Revision ID: james.westby@ubuntu.com-20080129141122-fnzjbo11ntghxfu7
Tags: upstream-6.0.1
ImportĀ upstreamĀ versionĀ 6.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 
3
 *
 
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 
5
 *
 
6
 * The contents of this file are subject to the terms of either the GNU
 
7
 * General Public License Version 2 only ("GPL") or the Common
 
8
 * Development and Distribution License("CDDL") (collectively, the
 
9
 * "License"). You may not use this file except in compliance with the
 
10
 * License. You can obtain a copy of the License at
 
11
 * http://www.netbeans.org/cddl-gplv2.html
 
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 
13
 * specific language governing permissions and limitations under the
 
14
 * License.  When distributing the software, include this License Header
 
15
 * Notice in each file and include the License file at
 
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
 
17
 * particular file as subject to the "Classpath" exception as provided
 
18
 * by Sun in the GPL Version 2 section of the License file that
 
19
 * accompanied this code. If applicable, add the following below the
 
20
 * License Header, with the fields enclosed by brackets [] replaced by
 
21
 * your own identifying information:
 
22
 * "Portions Copyrighted [year] [name of copyright owner]"
 
23
 *
 
24
 * Contributor(s):
 
25
 *
 
26
 * The Original Software is NetBeans. The Initial Developer of the Original
 
27
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
 
28
 * Microsystems, Inc. All Rights Reserved.
 
29
 *
 
30
 * If you wish your version of this file to be governed by only the CDDL
 
31
 * or only the GPL Version 2, indicate your decision by adding
 
32
 * "[Contributor] elects to include this software in this distribution
 
33
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 
34
 * single choice of license, a recipient has the option to distribute
 
35
 * your version of this file under either the CDDL, the GPL Version 2 or
 
36
 * to extend the choice of license to its licensees as provided above.
 
37
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 
38
 * Version 2 license, then the option applies only if the new code is
 
39
 * made subject to such option by the copyright holder.
 
40
 */
 
41
package org.netbeans.modules.editor.impl;
 
42
 
 
43
import java.awt.event.FocusEvent;
 
44
import javax.swing.event.PopupMenuEvent;
 
45
import javax.swing.text.BadLocationException;
 
46
import javax.swing.text.Document;
 
47
import javax.swing.text.Position;
 
48
import org.netbeans.editor.*;
 
49
import javax.swing.Action;
 
50
import org.openide.util.NbBundle;
 
51
 
 
52
import java.awt.Color;
 
53
import java.awt.Component;
 
54
import java.awt.Container;
 
55
import java.awt.Dimension;
 
56
import java.awt.Insets;
 
57
import java.awt.Toolkit;
 
58
import java.awt.event.ActionEvent;
 
59
import java.awt.event.ActionListener;
 
60
import java.awt.event.ComponentAdapter;
 
61
import java.awt.event.ComponentEvent;
 
62
import java.awt.event.FocusAdapter;
 
63
import java.awt.event.InputEvent;
 
64
import java.awt.event.KeyEvent;
 
65
import java.awt.event.MouseEvent;
 
66
import java.awt.event.MouseListener;
 
67
import java.util.ArrayList;
 
68
 
 
69
import java.util.HashMap;
 
70
import java.util.LinkedList;
 
71
import java.util.List;
 
72
import java.util.Map;
 
73
import java.util.logging.Logger;
 
74
import java.util.regex.Pattern;
 
75
import java.util.regex.PatternSyntaxException;
 
76
 
 
77
import javax.swing.AbstractAction;
 
78
import javax.swing.AbstractButton;
 
79
import javax.swing.BoxLayout;
 
80
import javax.swing.ImageIcon;
 
81
import javax.swing.InputMap;
 
82
import javax.swing.JButton;
 
83
import javax.swing.JCheckBox;
 
84
import javax.swing.JComboBox;
 
85
import javax.swing.JComponent;
 
86
import javax.swing.JLabel;
 
87
import javax.swing.JPanel;
 
88
import javax.swing.JPopupMenu;
 
89
import javax.swing.JTextField;
 
90
import javax.swing.JToolBar;
 
91
import javax.swing.KeyStroke;
 
92
import javax.swing.MutableComboBoxModel;
 
93
import javax.swing.event.DocumentEvent;
 
94
import javax.swing.event.DocumentListener;
 
95
import javax.swing.event.PopupMenuListener;
 
96
import javax.swing.text.JTextComponent;
 
97
import javax.swing.text.Keymap;
 
98
import org.netbeans.api.editor.EditorRegistry;
 
99
import org.openide.awt.Mnemonics;
 
100
import org.openide.util.Utilities;
 
101
 
 
102
 
 
103
/**
 
104
 * This is an implementation of a Firefox(TM) style Incremental Search Side Bar.
 
105
 *
 
106
 * @author Sandip V. Chitale (Sandip.Chitale@Sun.Com)
 
107
 */
 
108
public final class SearchBar extends JPanel {
 
109
    
 
110
    private static final Logger LOG = Logger.getLogger(SearchBar.class.getName());
 
111
 
 
112
    private static final Insets BUTTON_INSETS = new Insets(2, 1, 0, 1);
 
113
    private static final Color NOT_FOUND = Color.RED.darker();
 
114
    private static final Color INVALID_REGEXP = Color.red;
 
115
    
 
116
    /** Shared mouse listener used for setting the border painting property
 
117
     * of the toolbar buttons and for invoking the popup menu.
 
118
     */
 
119
    private static final MouseListener sharedMouseListener
 
120
        = new org.openide.awt.MouseUtils.PopupMouseAdapter() {
 
121
            public @Override void mouseEntered(MouseEvent evt) {
 
122
                Object src = evt.getSource();
 
123
                
 
124
                if (src instanceof JButton) { // ignore JMenuItem and JToggleButton
 
125
                    AbstractButton button = (AbstractButton)evt.getSource();
 
126
                    if (button.isEnabled()) {
 
127
                        button.setContentAreaFilled(true);
 
128
                        button.setBorderPainted(true);
 
129
                    }
 
130
                }
 
131
            }
 
132
            
 
133
            public @Override void mouseExited(MouseEvent evt) {
 
134
                Object src = evt.getSource();
 
135
                if (src instanceof JButton) { // ignore JMenuItem and JToggleButton
 
136
                    AbstractButton button = (AbstractButton)evt.getSource();
 
137
                    button.setContentAreaFilled(false);
 
138
                    button.setBorderPainted(false);
 
139
                }
 
140
            }
 
141
            
 
142
            protected void showPopup(MouseEvent evt) {
 
143
            }
 
144
        };
 
145
 
 
146
    private JTextComponent component;
 
147
    private JButton closeButton;
 
148
    private JButton expandButton;
 
149
    private JLabel findLabel;
 
150
    private JComboBox incrementalSearchComboBox;
 
151
    private JTextField incrementalSearchTextField;
 
152
    private DocumentListener incrementalSearchTextFieldListener;
 
153
    private JButton findNextButton;
 
154
    private JButton findPreviousButton;
 
155
    private JCheckBox matchCaseCheckBox;
 
156
    private JCheckBox wholeWordsCheckBox;
 
157
    private JCheckBox regexpCheckBox;
 
158
    private JCheckBox highlightCheckBox;
 
159
    private Map<Object, Object> findProps;
 
160
    private JPopupMenu expandPopup;
 
161
    private JPanel padding;
 
162
    /**
 
163
     * contains everything that is in Search bar and is possible to move to expand popup
 
164
     */
 
165
    private final List<Component> inBar = new ArrayList<Component>();
 
166
    /**
 
167
     * components moved to popup
 
168
     */
 
169
    private final LinkedList<Component> inPopup = new LinkedList<Component>();
 
170
    /**
 
171
     * defines index of all components in Search bar
 
172
     */
 
173
    private final List<Component> barOrder = new ArrayList<Component>();
 
174
    private boolean isPopupGoingToShow = false;
 
175
    private boolean isPopupShown = false;
 
176
    
 
177
    @SuppressWarnings("unchecked")
 
178
    public SearchBar(JTextComponent component) {
 
179
        this.component = component;
 
180
 
 
181
        setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
 
182
        Color bgColor = getBackground();
 
183
        bgColor = new Color( Math.max( 0, bgColor.getRed() - 20 ),
 
184
                             Math.max( 0, bgColor.getGreen() - 20 ),
 
185
                             Math.max( 0, bgColor.getBlue() - 20 ) );        
 
186
        setBackground(bgColor);
 
187
        
 
188
        //setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
 
189
        addMouseListener(sharedMouseListener);
 
190
 
 
191
        Keymap keymap = component.getKeymap();
 
192
        
 
193
        if (keymap instanceof MultiKeymap) {
 
194
            MultiKeymap multiKeymap = (MultiKeymap) keymap;
 
195
 
 
196
            Action[] actions = component.getActions();
 
197
            for(Action action:actions) {
 
198
                // Discover the keyStrokes for incremental-search-forward
 
199
                if (action.getValue(Action.NAME).equals(IncrementalSearchForwardAction.ACTION_NAME)) {
 
200
                    Action incrementalSearchForwardAction = action;
 
201
                    KeyStroke[] keyStrokes = multiKeymap.getKeyStrokesForAction(incrementalSearchForwardAction);
 
202
                    if (keyStrokes != null) {
 
203
                        InputMap inputMap = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
 
204
                        for(KeyStroke ks : keyStrokes) {
 
205
                            LOG.fine("found IncrementalSearchForwardAction, " + ks); //NOI18N
 
206
                            inputMap.put(ks, IncrementalSearchForwardAction.ACTION_NAME);
 
207
                        }
 
208
                        getActionMap().put(IncrementalSearchForwardAction.ACTION_NAME,
 
209
                            new AbstractAction() {
 
210
                                public void actionPerformed(ActionEvent e) {
 
211
                                    findNext();
 
212
                                }
 
213
                            });
 
214
                    }
 
215
                // Discover the keyStrokes for incremental-search-backward
 
216
                } else if (action.getValue(Action.NAME).equals(IncrementalSearchBackwardAction.ACTION_NAME)) {
 
217
                    Action incrementalSearchBackwardAction = action;
 
218
                    KeyStroke[] keyStrokes = multiKeymap.getKeyStrokesForAction(incrementalSearchBackwardAction);
 
219
                    if (keyStrokes != null) {
 
220
                        InputMap inputMap = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
 
221
                        for(KeyStroke ks : keyStrokes) {
 
222
                            LOG.fine("found IncrementalSearchBackwardAction, " + ks); //NOI18N
 
223
                            inputMap.put(ks, IncrementalSearchBackwardAction.ACTION_NAME);
 
224
                        }
 
225
                        getActionMap().put(IncrementalSearchBackwardAction.ACTION_NAME,
 
226
                            new AbstractAction() {
 
227
                                public void actionPerformed(ActionEvent e) {
 
228
                                    findPrevious();
 
229
                                }
 
230
                            });
 
231
                    }
 
232
                }
 
233
            }
 
234
        }
 
235
 
 
236
        // ESCAPE to put focus back in the editor
 
237
        getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
 
238
            .put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true),
 
239
            "loose-focus"); // NOI18N
 
240
        getActionMap().put("loose-focus", // NOI18N
 
241
            new AbstractAction() {
 
242
                public void actionPerformed(ActionEvent e) {
 
243
                    looseFocus();
 
244
                }
 
245
            });
 
246
 
 
247
        closeButton = new JButton(new ImageIcon(Utilities.loadImage("org/netbeans/modules/editor/resources/find_close.png"))); // NOI18N
 
248
        closeButton.addActionListener(new ActionListener() {
 
249
            public void actionPerformed(ActionEvent e) {
 
250
                looseFocus();
 
251
            }
 
252
        });
 
253
        closeButton.setToolTipText(NbBundle.getMessage(SearchBar.class, "TOOLTIP_CloseIncrementalSearchSidebar")); // NOI18N
 
254
        processButton(closeButton);
 
255
 
 
256
        expandButton = new JButton(new ImageIcon(Utilities.loadImage("org/netbeans/modules/editor/resources/find_expand.png"))); // NOI18N
 
257
        expandButton.setMnemonic(NbBundle.getMessage(SearchBar.class, "CTL_ExpandButton_Mnemonic").charAt(0)); // NOI18N
 
258
        processButton(expandButton);
 
259
        expandButton.addActionListener(new ActionListener() {
 
260
                public void actionPerformed(ActionEvent e) {
 
261
                    boolean state = !isPopupShown;
 
262
                    isPopupShown = state;
 
263
                    if (state) {
 
264
                        showExpandedMenu();
 
265
                    } else {
 
266
                        hideExpandedMenu();
 
267
                    }
 
268
                }
 
269
        });
 
270
        
 
271
        findLabel = new JLabel(); 
 
272
        Mnemonics.setLocalizedText( findLabel, NbBundle.getMessage(SearchBar.class, "CTL_Find")); // NOI18N
 
273
        
 
274
        // configure incremental search text field
 
275
        incrementalSearchComboBox = new JComboBox()
 
276
        {
 
277
            public @Override Dimension getMinimumSize() {
 
278
                return getPreferredSize();
 
279
            }
 
280
 
 
281
            public @Override Dimension getMaximumSize() {
 
282
                return getPreferredSize();
 
283
            }
 
284
        };
 
285
        incrementalSearchComboBox.setEditable(true);
 
286
        incrementalSearchTextField = (JTextField) incrementalSearchComboBox.getEditor().getEditorComponent();
 
287
        incrementalSearchTextField.setToolTipText(NbBundle.getMessage(SearchBar.class, "TOOLTIP_IncrementalSearchText")); // NOI18N
 
288
 
 
289
        // listen on text change
 
290
        incrementalSearchTextFieldListener = new DocumentListener() {
 
291
            public void changedUpdate(DocumentEvent e) {
 
292
            }
 
293
 
 
294
            public void insertUpdate(DocumentEvent e) {
 
295
                // text changed - attempt incremental search
 
296
                incrementalSearch();
 
297
            }
 
298
 
 
299
            public void removeUpdate(DocumentEvent e) {
 
300
                // text changed - attempt incremental search
 
301
                incrementalSearch();
 
302
            }
 
303
        };
 
304
        incrementalSearchTextField.getDocument().addDocumentListener(incrementalSearchTextFieldListener);
 
305
 
 
306
        incrementalSearchTextField.addFocusListener(new FocusAdapter() {
 
307
            public @Override void focusLost(FocusEvent e) {
 
308
                looseFocus();
 
309
            }
 
310
        });
 
311
        
 
312
        // ENTER to find next
 
313
        incrementalSearchTextField.addActionListener(new ActionListener() {
 
314
            public void actionPerformed(ActionEvent e) {
 
315
                findNext();
 
316
            }
 
317
        });
 
318
 
 
319
        // Shift+ENTER to find previous
 
320
        incrementalSearchTextField.getInputMap()
 
321
                                  .put(KeyStroke.getKeyStroke(
 
322
                KeyEvent.VK_ENTER, InputEvent.SHIFT_MASK, true),
 
323
            "incremental-find-previous"); // NOI18N
 
324
        incrementalSearchTextField.getActionMap().put("incremental-find-previous", // NOI18N
 
325
            new AbstractAction() {
 
326
                public void actionPerformed(ActionEvent e) {
 
327
                    findPrevious();
 
328
                }});
 
329
 
 
330
        // configure find next button
 
331
        findNextButton = new JButton(
 
332
            new ImageIcon(Utilities.loadImage("org/netbeans/modules/editor/resources/find_next.png"))); // NOI18N
 
333
        Mnemonics.setLocalizedText( findNextButton, NbBundle.getMessage(SearchBar.class, "CTL_FindNext")); // NOI18N
 
334
        findNextButton.addActionListener(new ActionListener() {
 
335
                public void actionPerformed(ActionEvent e) {
 
336
                    findNext();
 
337
                }});
 
338
        processButton(findNextButton);
 
339
 
 
340
        // configure find previous button
 
341
        findPreviousButton = new JButton(
 
342
            new ImageIcon(Utilities.loadImage("org/netbeans/modules/editor/resources/find_previous.png"))); // NOI18N
 
343
        Mnemonics.setLocalizedText(findPreviousButton, NbBundle.getMessage(SearchBar.class, "CTL_FindPrevious")); // NOI18N
 
344
        findPreviousButton.addActionListener(new ActionListener() {
 
345
                public void actionPerformed(ActionEvent e) {
 
346
                    findPrevious();
 
347
                }
 
348
            });
 
349
        processButton(findPreviousButton);
 
350
 
 
351
        // configure match case check box
 
352
        matchCaseCheckBox = new JCheckBox();
 
353
        Mnemonics.setLocalizedText(matchCaseCheckBox, NbBundle.getMessage(SearchBar.class, "CTL_MatchCase")); // NOI18N
 
354
        matchCaseCheckBox.addActionListener(new ActionListener() {
 
355
            public void actionPerformed(ActionEvent e) {
 
356
                // Put focus back in the incremental search textField
 
357
                incrementalSearch();
 
358
                incrementalSearchTextField.requestFocusInWindow();
 
359
            }
 
360
        });
 
361
        processButton(matchCaseCheckBox);
 
362
 
 
363
        wholeWordsCheckBox = new JCheckBox();
 
364
        Mnemonics.setLocalizedText(wholeWordsCheckBox, NbBundle.getMessage(SearchBar.class, "CTL_WholeWords")); // NOI18N
 
365
        wholeWordsCheckBox.addActionListener(new ActionListener() {
 
366
            public void actionPerformed(ActionEvent e) {
 
367
                // Put focus back in the incremental search textField
 
368
                incrementalSearch();
 
369
                incrementalSearchTextField.requestFocusInWindow();
 
370
            }
 
371
        });
 
372
        processButton(wholeWordsCheckBox);
 
373
        
 
374
        regexpCheckBox = new JCheckBox();
 
375
        Mnemonics.setLocalizedText(regexpCheckBox, NbBundle.getMessage(SearchBar.class, "CTL_Regexp")); // NOI18N
 
376
        regexpCheckBox.addActionListener(new ActionListener() {
 
377
            public void actionPerformed(ActionEvent e) {
 
378
                // Switch other checkbozes on/off
 
379
                wholeWordsCheckBox.setEnabled(!regexpCheckBox.isSelected());
 
380
                // Put focus back in the incremental search textField
 
381
                incrementalSearch();
 
382
                incrementalSearchTextField.requestFocusInWindow();
 
383
            }
 
384
        });
 
385
        processButton(regexpCheckBox);
 
386
        
 
387
        highlightCheckBox = new JCheckBox();
 
388
        Mnemonics.setLocalizedText(highlightCheckBox, NbBundle.getMessage(SearchBar.class, "CTL_Highlight")); // NOI18N
 
389
        highlightCheckBox.addActionListener(new ActionListener() {
 
390
            public void actionPerformed(ActionEvent e) {
 
391
                // Put focus back in the incremental search textField
 
392
                incrementalSearch();
 
393
                incrementalSearchTextField.requestFocusInWindow();
 
394
            }
 
395
        });
 
396
        highlightCheckBox.setSelected(true);
 
397
        processButton(highlightCheckBox);
 
398
        
 
399
        expandPopup = new JPopupMenu();
 
400
        expandPopup.addPopupMenuListener(new PopupMenuListener() {
 
401
 
 
402
            public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
 
403
            }
 
404
 
 
405
            public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
 
406
            }
 
407
 
 
408
            public void popupMenuCanceled(PopupMenuEvent e) {
 
409
                // check if it was canceled by click on expand button
 
410
                if (expandButton.getMousePosition() == null) {
 
411
                    expandButton.setContentAreaFilled(false);
 
412
                    expandButton.setBorderPainted(false);
 
413
                    isPopupShown = false;
 
414
                }
 
415
            }
 
416
        });
 
417
 
 
418
        // configure find properties
 
419
        findProps = new HashMap<Object, Object>();
 
420
        findProps.put(SettingsNames.FIND_HIGHLIGHT_SEARCH, true);
 
421
        findProps.put(SettingsNames.FIND_WHOLE_WORDS, false);
 
422
        findProps.put(SettingsNames.FIND_WRAP_SEARCH, true);
 
423
        findProps.put(SettingsNames.FIND_BLOCK_SEARCH, false);
 
424
        findProps.put(SettingsNames.FIND_BLOCK_SEARCH_START, null);
 
425
        findProps.put(SettingsNames.FIND_BLOCK_SEARCH_END, null);
 
426
        // XXX take from preferences
 
427
        findProps.put(SettingsNames.FIND_MATCH_CASE, false);
 
428
        findProps.put(SettingsNames.FIND_REG_EXP, false);
 
429
        findProps.put(SettingsNames.FIND_HIGHLIGHT_SEARCH, true);
 
430
        
 
431
        // padding at the end of the toolbar
 
432
        JPanel spacer = new JPanel();
 
433
        spacer.setSize(4, 4);
 
434
        spacer.setMaximumSize(new Dimension(4,4));
 
435
        spacer.setOpaque(false);
 
436
        add(spacer);
 
437
        
 
438
        add(findLabel);
 
439
        add(incrementalSearchComboBox);
 
440
        add(new JToolBar.Separator());
 
441
        add(findPreviousButton);
 
442
        add(findNextButton);
 
443
        add(new JToolBar.Separator());
 
444
        add(matchCaseCheckBox);
 
445
        add(wholeWordsCheckBox);
 
446
        add(regexpCheckBox);
 
447
        add(highlightCheckBox);
 
448
        add(expandButton);
 
449
        // padding at the end of the toolbar
 
450
        padding = new JPanel();
 
451
        padding.setOpaque(false);
 
452
        add(padding);
 
453
        add(closeButton);
 
454
        
 
455
        makeBarExpandable();
 
456
        
 
457
        // initially not visible
 
458
        setVisible(false);
 
459
        
 
460
        this.addComponentListener(new ComponentAdapter() {
 
461
            @Override
 
462
            public void componentResized(ComponentEvent evt) {
 
463
                computeLayout();
 
464
            }
 
465
        });
 
466
    }
 
467
    
 
468
    private void makeBarExpandable() {
 
469
//        inBar.add(findPreviousButton);
 
470
//        inBar.add(findNextButton);
 
471
        inBar.add(matchCaseCheckBox);
 
472
        inBar.add(wholeWordsCheckBox);
 
473
        inBar.add(regexpCheckBox);
 
474
        inBar.add(highlightCheckBox);
 
475
        for (Component c : this.getComponents()) {
 
476
            barOrder.add(c);
 
477
        }
 
478
        remove(expandButton);
 
479
    }
 
480
    
 
481
    private void computeLayout() {
 
482
        Container parent = this.getParent();
 
483
        int parentWidth = parent.getWidth();
 
484
        int totalWidth = 0;
 
485
        
 
486
        boolean change = false;
 
487
        
 
488
        for (Component c : this.getComponents()) {
 
489
            if (c != padding) {
 
490
                totalWidth += c.getWidth();
 
491
            }
 
492
        }
 
493
        
 
494
        if (totalWidth <= parentWidth) {
 
495
            // enough space try to clear expand popup
 
496
            while (!inPopup.isEmpty()) {
 
497
                Component c = inPopup.getFirst();
 
498
                totalWidth += c.getWidth();
 
499
                
 
500
                if (totalWidth > parentWidth) {
 
501
                    break;
 
502
                }
 
503
                inPopup.removeFirst();
 
504
                inBar.add(c);
 
505
                expandPopup.remove(c);
 
506
                add(c, barOrder.indexOf(c));
 
507
                change = true;
 
508
            }
 
509
        } else {
 
510
            // lack of space
 
511
            while (totalWidth > parentWidth && !inBar.isEmpty()) {
 
512
                Component c = inBar.remove(inBar.size() - 1);
 
513
                inPopup.addFirst(c);
 
514
                remove(c);
 
515
                expandPopup.add(c, 0);
 
516
                totalWidth -= c.getWidth();
 
517
                change = true;
 
518
            }
 
519
        }
 
520
        
 
521
        if (change) {
 
522
            if (inPopup.isEmpty()) {
 
523
                remove(expandButton);
 
524
            } else if (getComponentIndex(expandButton) < 0) {
 
525
                add(expandButton, getComponentIndex(padding));
 
526
            }
 
527
            this.revalidate();
 
528
            expandPopup.invalidate();
 
529
            expandPopup.validate();
 
530
        }
 
531
    }
 
532
 
 
533
    private int getComponentIndex(Component c) {
 
534
        Component[] comps = getComponents();
 
535
        for (int i = 0; i < comps.length; i++) {
 
536
            if (c == comps[i]) {
 
537
                return i;
 
538
            }
 
539
        }
 
540
        return -1;
 
541
    }
 
542
    
 
543
    private void showExpandedMenu() {
 
544
        if (!inPopup.isEmpty() && !expandPopup.isVisible()) {
 
545
            isPopupGoingToShow = true;
 
546
            Insets ins = expandPopup.getInsets();
 
547
            // compute popup height since JPopupMenu.getHeight does not work
 
548
            expandPopup.show(expandButton, 0, -(matchCaseCheckBox.getHeight() * inPopup.size() + ins.top + ins.bottom));
 
549
        }
 
550
    }
 
551
    
 
552
    private void hideExpandedMenu() {
 
553
        if (expandPopup.isVisible()) {
 
554
            expandPopup.setVisible(false);
 
555
            incrementalSearch();
 
556
            incrementalSearchTextField.requestFocusInWindow();
 
557
        }
 
558
    }
 
559
 
 
560
    public @Override String getName() {
 
561
        //Required for Aqua L&F toolbar UI
 
562
        return "editorSearchBar"; // NOI18N
 
563
    }
 
564
    
 
565
    private void gainFocus() {
 
566
        if (isVisible()) {
 
567
            incrementalSearchTextField.requestFocusInWindow();
 
568
            return;
 
569
        }
 
570
        computeLayout();
 
571
        isPopupShown = false;
 
572
        
 
573
        setVisible(true);
 
574
        initBlockSearch();
 
575
        incrementalSearchTextField.requestFocusInWindow();
 
576
 
 
577
        if (incrementalSearchTextField.getText().length() > 0) {
 
578
            // preselect the text in incremental search text field
 
579
            incrementalSearchTextField.selectAll();
 
580
            findPreviousButton.setEnabled(true);
 
581
            findNextButton.setEnabled(true);
 
582
        }
 
583
        else {
 
584
            findPreviousButton.setEnabled(false);
 
585
            findNextButton.setEnabled(false);
 
586
        }
 
587
    }
 
588
 
 
589
    private void looseFocus() {
 
590
        if (isPopupGoingToShow) {
 
591
            isPopupGoingToShow = false;
 
592
            return;
 
593
        }
 
594
        FindSupport.getFindSupport().setBlockSearchHighlight(0, 0);
 
595
        FindSupport.getFindSupport().incSearchReset();
 
596
        setVisible(false);
 
597
 
 
598
        if (component.isEnabled()) {
 
599
            component.requestFocusInWindow();
 
600
        }
 
601
    }
 
602
 
 
603
    private void incrementalSearch() {
 
604
        String incrementalSearchText = incrementalSearchTextField.getText();
 
605
        boolean empty = incrementalSearchText.length() <= 0;
 
606
    
 
607
        // Enable/disable the pre/next buttons
 
608
        findPreviousButton.setEnabled(!empty);
 
609
        findNextButton.setEnabled(!empty);
 
610
        
 
611
        // configure find properties
 
612
        FindSupport findSupport = FindSupport.getFindSupport();
 
613
 
 
614
        findProps.put(SettingsNames.FIND_WHAT, incrementalSearchText);
 
615
        findProps.put(SettingsNames.FIND_MATCH_CASE, matchCaseCheckBox.isSelected());
 
616
        findProps.put(SettingsNames.FIND_WHOLE_WORDS, wholeWordsCheckBox.isSelected());
 
617
        findProps.put(SettingsNames.FIND_REG_EXP, regexpCheckBox.isSelected());
 
618
        findProps.put(SettingsNames.FIND_HIGHLIGHT_SEARCH, !empty && highlightCheckBox.isSelected());
 
619
        
 
620
        findProps.put(SettingsNames.FIND_BACKWARD_SEARCH, false);        
 
621
        findProps.put(SettingsNames.FIND_INC_SEARCH, true);
 
622
        
 
623
        findSupport.putFindProperties(findProps);
 
624
        
 
625
        // search starting at current caret position
 
626
        int caretPosition = component.getCaretPosition();
 
627
 
 
628
        if (regexpCheckBox.isSelected()) {
 
629
            Pattern pattern;
 
630
            try {
 
631
                pattern = Pattern.compile(incrementalSearchText);
 
632
            } catch (PatternSyntaxException e) {
 
633
                pattern = null;
 
634
            }
 
635
            if (pattern != null) {
 
636
                // valid regexp
 
637
                incrementalSearchTextField.setBackground(null);
 
638
                incrementalSearchTextField.setForeground(Color.BLACK);
 
639
            } else {
 
640
                // invalid regexp
 
641
                incrementalSearchTextField.setBackground(null);
 
642
                incrementalSearchTextField.setForeground(INVALID_REGEXP);
 
643
            }
 
644
        } else {
 
645
            if (findSupport.incSearch(findProps, caretPosition) || empty) {
 
646
                // text found - reset incremental search text field's foreground
 
647
                incrementalSearchTextField.setBackground(null);
 
648
                incrementalSearchTextField.setForeground(Color.BLACK);
 
649
            } else {
 
650
                // text not found - indicate error in incremental search
 
651
                // text field with red foreground
 
652
                incrementalSearchTextField.setBackground(null);
 
653
                incrementalSearchTextField.setForeground(NOT_FOUND);
 
654
                Toolkit.getDefaultToolkit().beep();
 
655
            }
 
656
        }
 
657
    }
 
658
 
 
659
    private void findNext() {
 
660
        find(true);
 
661
    }
 
662
 
 
663
    private void findPrevious() {
 
664
        find(false);
 
665
    }
 
666
 
 
667
    private void find(boolean next) {
 
668
        String incrementalSearchText = incrementalSearchTextField.getText();
 
669
        boolean empty = incrementalSearchText.length() <= 0;
 
670
 
 
671
        incrementalSearchTextField.getDocument().removeDocumentListener(incrementalSearchTextFieldListener);
 
672
        // Add the text to the top of the list
 
673
        for(int i = incrementalSearchComboBox.getItemCount() - 1; i >= 0; i--) {
 
674
            String item = (String) incrementalSearchComboBox.getItemAt(i);
 
675
            if (item.equals(incrementalSearchText)) {
 
676
                incrementalSearchComboBox.removeItemAt(i);
 
677
            }
 
678
        }
 
679
        ((MutableComboBoxModel) incrementalSearchComboBox.getModel()).insertElementAt(incrementalSearchText, 0);
 
680
        incrementalSearchComboBox.setSelectedIndex(0);
 
681
        incrementalSearchTextField.getDocument().addDocumentListener(incrementalSearchTextFieldListener);
 
682
        
 
683
        // configure find properties
 
684
        FindSupport findSupport = FindSupport.getFindSupport();
 
685
 
 
686
        findProps.put(SettingsNames.FIND_WHAT, incrementalSearchText);
 
687
        findProps.put(SettingsNames.FIND_MATCH_CASE, matchCaseCheckBox.isSelected());
 
688
        findProps.put(SettingsNames.FIND_WHOLE_WORDS, wholeWordsCheckBox.isSelected());
 
689
        findProps.put(SettingsNames.FIND_REG_EXP, regexpCheckBox.isSelected());
 
690
        findProps.put(SettingsNames.FIND_BACKWARD_SEARCH, Boolean.FALSE);
 
691
        findProps.put(SettingsNames.FIND_INC_SEARCH, Boolean.TRUE);
 
692
        findProps.put(SettingsNames.FIND_HIGHLIGHT_SEARCH, !empty && highlightCheckBox.isSelected());
 
693
 
 
694
        findSupport.putFindProperties(findProps);
 
695
        
 
696
        if (findSupport.find(findProps, !next) || empty) {
 
697
            // text found - reset incremental search text field's foreground
 
698
            incrementalSearchTextField.setBackground(null);
 
699
            incrementalSearchTextField.setForeground(Color.BLACK);
 
700
        } else {
 
701
            // text not found - indicate error in incremental search text field with red foreground
 
702
            incrementalSearchTextField.setBackground(null);
 
703
            incrementalSearchTextField.setForeground(NOT_FOUND);
 
704
            Toolkit.getDefaultToolkit().beep();
 
705
        }
 
706
    }
 
707
 
 
708
    private void processButton(AbstractButton button) {
 
709
        button.setContentAreaFilled(false);
 
710
        button.setBorderPainted(false);
 
711
        button.setMargin(BUTTON_INSETS);
 
712
        if (button instanceof JButton) {
 
713
            button.addMouseListener(sharedMouseListener);
 
714
        }
 
715
        button.setFocusable(false);
 
716
    }
 
717
    
 
718
    @SuppressWarnings("unchecked")
 
719
    private void initBlockSearch() {
 
720
        JTextComponent c = EditorRegistry.lastFocusedComponent();
 
721
        String selText = null;
 
722
        int startSelection = 0;
 
723
        int endSelection = 0;
 
724
        boolean blockSearchVisible = false;
 
725
 
 
726
        if (c != null) {
 
727
            startSelection = c.getSelectionStart();
 
728
            endSelection = c.getSelectionEnd();
 
729
 
 
730
            Document doc = c.getDocument();
 
731
            if (doc instanceof BaseDocument){
 
732
                BaseDocument bdoc = (BaseDocument) doc;
 
733
                try{
 
734
                    int startLine = org.netbeans.editor.Utilities.getLineOffset(bdoc, startSelection);
 
735
                    int endLine = org.netbeans.editor.Utilities.getLineOffset(bdoc, endSelection);
 
736
                    if (endLine > startLine) {
 
737
                        blockSearchVisible = true;
 
738
                    }
 
739
                } catch (BadLocationException ble){
 
740
                }
 
741
            }
 
742
 
 
743
            // caretPosition = bwdSearch.isSelected() ? c.getSelectionEnd() : c.getSelectionStart();
 
744
 
 
745
            if (!blockSearchVisible){
 
746
                selText = c.getSelectedText();
 
747
                if (selText != null && selText.length() > 0) {
 
748
                    int n = selText.indexOf( '\n' );
 
749
                    if (n >= 0 ) selText = selText.substring(0, n);
 
750
                    incrementalSearchTextField.setText(selText);
 
751
                    // findWhat.getEditor().setItem(selText);
 
752
                    // changeFindWhat(true);
 
753
                } else {
 
754
                    String findWhat = (String) FindSupport.getFindSupport().getFindProperty(SettingsNames.FIND_WHAT);
 
755
                    if (findWhat != null && findWhat.length() > 0) {
 
756
                        incrementalSearchTextField.setText(findWhat);
 
757
                    }
 
758
                }
 
759
            }
 
760
 
 
761
            int blockSearchStartPos = blockSearchVisible ? startSelection : 0;
 
762
            int blockSearchEndPos = blockSearchVisible ? endSelection : 0;
 
763
 
 
764
            try{                
 
765
                findProps.put(SettingsNames.FIND_BLOCK_SEARCH, blockSearchVisible);
 
766
                findProps.put(SettingsNames.FIND_BLOCK_SEARCH_START, blockSearchStartPos);
 
767
                int be = getBlockEndOffset();
 
768
                if (be < 0){
 
769
                    findProps.put(SettingsNames.FIND_BLOCK_SEARCH_END, doc.createPosition(blockSearchEndPos));
 
770
                }else{
 
771
                    blockSearchEndPos = be;
 
772
                }
 
773
                FindSupport.getFindSupport().setBlockSearchHighlight(blockSearchStartPos, blockSearchEndPos);
 
774
            } catch(BadLocationException ble){
 
775
                findProps.put(SettingsNames.FIND_BLOCK_SEARCH, Boolean.FALSE);
 
776
                findProps.put(SettingsNames.FIND_BLOCK_SEARCH_START, null);
 
777
            }
 
778
            
 
779
            FindSupport.getFindSupport().putFindProperties(findProps);
 
780
        }
 
781
    }
 
782
    
 
783
    private int getBlockEndOffset(){
 
784
        Position pos = (Position) FindSupport.getFindSupport().getFindProperties().get(SettingsNames.FIND_BLOCK_SEARCH_END);
 
785
        return (pos != null) ? pos.getOffset() : -1;
 
786
    }
 
787
    
 
788
    /**
 
789
     * Factory for creating the incremental search sidebar
 
790
     */
 
791
    public static final class Factory implements SideBarFactory {
 
792
        public JComponent createSideBar(JTextComponent target) {
 
793
            return new SearchBar(target);
 
794
        }
 
795
    }
 
796
 
 
797
    public static class IncrementalSearchForwardAction extends BaseAction {
 
798
        
 
799
        public static final String ACTION_NAME = "incremental-search-forward"; // NOI18N
 
800
    
 
801
        static final long serialVersionUID = -1;
 
802
        
 
803
        public IncrementalSearchForwardAction() {
 
804
            super(ACTION_NAME, CLEAR_STATUS_TEXT);
 
805
            putValue(SHORT_DESCRIPTION, NbBundle.getMessage(IncrementalSearchForwardAction.class, ACTION_NAME));
 
806
        }
 
807
        
 
808
        public void actionPerformed(ActionEvent evt, JTextComponent target) {
 
809
            if (target != null) {
 
810
                EditorUI eui = org.netbeans.editor.Utilities.getEditorUI(target);
 
811
                if (eui != null) {
 
812
                    JComponent comp = eui.getExtComponent();
 
813
                    if (comp != null) {
 
814
                        SearchBar issb = findComponent(comp,SearchBar.class, 5);
 
815
                        if (issb != null) {
 
816
                            issb.gainFocus();
 
817
                        }
 
818
                    }
 
819
                }
 
820
            }
 
821
        }
 
822
    }
 
823
    
 
824
    public static class IncrementalSearchBackwardAction extends BaseAction {
 
825
        
 
826
        public static final String ACTION_NAME = "incremental-search-backward"; // NOI18N
 
827
 
 
828
        static final long serialVersionUID = -1;
 
829
        
 
830
        public IncrementalSearchBackwardAction() {
 
831
            super(ACTION_NAME, CLEAR_STATUS_TEXT);
 
832
            putValue(SHORT_DESCRIPTION, NbBundle.getMessage(IncrementalSearchBackwardAction.class, ACTION_NAME));
 
833
        }
 
834
        
 
835
        public void actionPerformed(ActionEvent evt, JTextComponent target) {
 
836
            if (target != null) {
 
837
                EditorUI eui = org.netbeans.editor.Utilities.getEditorUI(target);
 
838
                if (eui != null) {
 
839
                    JComponent comp = eui.getExtComponent();
 
840
                    if (comp != null) {
 
841
                        SearchBar issb = findComponent(comp,SearchBar.class, 5);
 
842
                        if (issb != null) {
 
843
                            issb.gainFocus();
 
844
                        }
 
845
                    }
 
846
                }
 
847
            }
 
848
        }
 
849
    }
 
850
 
 
851
    private static <T> T findComponent(Container container, Class<T> componentClass, int depth) {
 
852
        if (depth > 0) {
 
853
            for(Component c : container.getComponents()) {
 
854
                if (componentClass.isAssignableFrom(c.getClass())) {
 
855
                    @SuppressWarnings("unchecked")
 
856
                    T target = (T) c;
 
857
                    return target;
 
858
                } else if (c instanceof Container) {
 
859
                    T target = findComponent((Container) c, componentClass, depth - 1);
 
860
                    if (target != null) {
 
861
                        return target;
 
862
                    }
 
863
                }
 
864
            }
 
865
        }
 
866
        return null;
 
867
    }
 
868
}